diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2018-01-23 16:54:55 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2018-01-23 16:54:55 (GMT) |
commit | 5bf617b1e9347322b8b16419fd5e74107e8b1c26 (patch) | |
tree | e5bbb785b82b35bc62df41bebca8b540078aaf76 /xpa/xlaunch.c | |
parent | 51e1f85047b34f095ed69a3024d696997d2667c8 (diff) | |
parent | 0ebcd152c10d2eae6b62f16e7138aa187a8a1bdd (diff) | |
download | blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.zip blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.tar.gz blt-5bf617b1e9347322b8b16419fd5e74107e8b1c26.tar.bz2 |
Merge commit '0ebcd152c10d2eae6b62f16e7138aa187a8a1bdd' as 'xpa'
Diffstat (limited to 'xpa/xlaunch.c')
-rw-r--r-- | xpa/xlaunch.c | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/xpa/xlaunch.c b/xpa/xlaunch.c new file mode 100644 index 0000000..9f064d1 --- /dev/null +++ b/xpa/xlaunch.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 1999-2007 Smithsonian Astrophysical Observatory + */ + +#include <xlaunch.h> + +/* + *---------------------------------------------------------------------------- + * + * + * Private Routines and Data + * + * + *---------------------------------------------------------------------------- + */ + +static pid_t pid=0; + +#if HAVE_MINGW32==0 + +/* wait for child process to start, using waitpid() */ +#ifdef ANSI_FUNC +static int launch_pipes(int *pipes, int flag) +#else +static int launch_pipes(pipes, flag) + int *pipes; + int flag; +#endif +{ + int i; + char tbuf[SZ_LINE]; + if( pipes ){ + for(i=0; i<4; i++){ + pipes[i] = -1; + } + if( (pipe(&pipes[0]) < 0) || (pipe(&pipes[2]) < 0) ) return -1; + if( flag ){ +#if HAVE_SETENV + snprintf(tbuf, SZ_LINE-1, "%d,%d,%d,%d", + pipes[0], pipes[1], pipes[2], pipes[3]); + setenv("LAUNCH_PIPES", tbuf, 1); +#else + snprintf(tbuf, SZ_LINE-1, "LAUNCH_PIPES=%d,%d,%d,%d", + pipes[0], pipes[1], pipes[2], pipes[3]); + putenv(xstrdup(tbuf)); +#endif + } + } + return 0; +} + +#ifdef ANSI_FUNC +static int cleanup_pipes(int *pipes) +#else +static int cleanup_pipes(pipes) + int *pipes; +#endif +{ + if( pipes ){ + /* close child pipes */ + close(pipes[1]); + close(pipes[2]); + /* move parent write into slot 1 */ + pipes[1] = pipes[3]; + /* set unused pipes to impossible value */ + pipes[2] = -1; + pipes[3] = -1; + } + return 0; +} + +#if LAUNCH_USE_WAITPID +/* wait for child process to start, using waitpid() */ +#ifdef ANSI_FUNC +static int launch_waitstart(pid_t pid) +#else +static int launch_waitstart(pid) + pid_t pid; +#endif +{ + int i, got; + int status=0; + struct timeval tv; + /* wait up to LAUNCH_WAIT_TRIES millisec to make sure the child started, + but if we get an error, we can exit immediately */ + for(i=0; i<LAUNCH_WAIT_TRIES; i++){ + errno = 0; + got=waitpid(pid, &status, WNOHANG); + /* look for error termination */ + if( (got < 0) || ((got == 0) && xerrno) ){ + got = -1; + /* make sure status shows error */ + if( status == 0 ) + status = -1; + break; + } + /* look for normal termination */ + else if( got > 0 ){ + break; + } + /* no termination, sleep and wait some more */ + else{ + tv.tv_sec = 0; + tv.tv_usec = LAUNCH_WAIT_MSEC; + xselect(1, NULL, NULL, NULL, &tv); + } + } + /* no termination means the child is still running */ + if( got == 0 ) status = 0; + /* return the news */ + return status; +} +#endif + +/* + * standard unix version of launch: + * adapted from the system() code in: + * W. Richard Stevens + * "Advanced Programming in the Unix Environment" + * Addison-Wesley Publishing Co, 1992 + * p. 314 + */ +#ifdef ANSI_FUNC +static int launch_fork_exec(char *cmdstring, int attach, + char **stdfiles, int *pipes) +#else + static int launch_fork_exec(cmdstring, attach, stdfiles, pipes) + char *cmdstring; + int attach; + char **stdfiles; + int *pipes; +#endif +{ + int status; + int tpipes[4]; + struct sigaction ignore, saveintr, savequit; + sigset_t chldmask, savemask; +#if LAUNCH_USE_PIPE + int fd[2]; +#endif + + /* return false if no command is specified */ + if( !cmdstring || !*cmdstring ) return -1; + + ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */ + sigemptyset(&ignore.sa_mask); + ignore.sa_flags = 0; + if (sigaction(SIGINT, &ignore, &saveintr) < 0) + return -1; + if (sigaction(SIGQUIT, &ignore, &savequit) < 0) + return -1; + + sigemptyset(&chldmask); /* now block SIGCHLD */ + sigaddset(&chldmask, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0) + return -1; + +#if LAUNCH_USE_PIPE + /* open a pipe so parent can hear if the child fails to exec */ + if( !attach ){ + if( pipe(fd) < 0 ) + return -1; + xfcntl(fd[0], F_SETFD, FD_CLOEXEC); + xfcntl(fd[1], F_SETFD, FD_CLOEXEC); + } +#endif + + /* create temp ipc pipes if necessary */ + if( pipes ){ + if( launch_pipes(tpipes, 0) < 0 ) return -1; + } + + /* start new process */ + if( (pid = fork()) < 0 ){ +#if LAUNCH_USE_PIPE + if( !attach ){ + close(fd[0]); + close(fd[1]); + } +#endif + if( pipes ){ + close(tpipes[0]); + close(tpipes[1]); + close(tpipes[2]); + close(tpipes[3]); + } + status = -1; /* ERROR: probably out of processes */ + } else if( pid == 0 ){ /* child */ + int i, j, len; + char *argv[LAUNCH_ARGS+1]; + char *path=NULL; + char *s=NULL, *t=NULL; + + /* reset pipes, if necessary */ + if( pipes ){ + /* close parent's read/write pipes */ + close(tpipes[0]); + close(tpipes[3]); + /* change child's stdin/stdout to use the passed pipes to parent */ + dup2(tpipes[2], 0); close(tpipes[2]); + dup2(tpipes[1], 1); close(tpipes[1]); + } + + /* close and reopen stdio files, if necessary */ + if( stdfiles ){ + for(i=0; i<3; i++){ + if( stdfiles[i] ){ + close(i); + switch(i){ + case 0: + if( open(stdfiles[i], O_RDONLY) < 0){ + _exit(-1); + } + break; + case 1: + if( open(stdfiles[i], O_CREAT|O_WRONLY|O_TRUNC, 0600) < 0){ + _exit(-1); + } + break; + case 2: + /* if stderr is the same as stdout, just dup */ + if( stdfiles[1] && !strcmp(stdfiles[1], stdfiles[i]) ){ + dup(1); + } + else{ + if( open(stdfiles[i], O_CREAT|O_WRONLY|O_TRUNC, 0600) < 0){ + _exit(-1); + } + } + break; + } + } + } + } + + /* restore previous signal actions & reset signal mask, but only if + parent is waiting for completion (i.e., we are "attached") */ + if( attach ){ + sigaction(SIGINT, &saveintr, NULL); + sigaction(SIGQUIT, &savequit, NULL); + sigprocmask(SIG_SETMASK, &savemask, NULL); + } +#if LAUNCH_USE_PIPE + /* child closes reader -- only writes status */ + else{ + close(fd[0]); + } +#endif + + /* package up the arguments for new process */ + t = (char *)xstrdup(cmdstring); + for(i=0, s=(char *)strtok(t, " \t"); s; + i++, s=(char *)strtok(NULL," \t")){ + if( i < LAUNCH_ARGS ){ + /* save argument */ + argv[i] = xstrdup(s); + /* change back special char to spaces, if necessary */ + len = strlen(argv[i]); + for(j=0; j<len; j++){ + if( argv[i][j] == LAUNCH_SPACE){ + argv[i][j] = ' '; + } + } + argv[i+1] = NULL; + /* save program name */ + if( i == 0 ) path = argv[i]; + } + } + if( t ) xfree(t); +#ifndef HAVE_CYGWIN + /* this call is broken in cygwin */ + /* for unattached processes, start a new session (with new process id), + so that we do not inherit signals from parent (particularly SIGTERM) */ + if( !attach ) + setsid(); +#endif + /* start up the new program */ + if( execvp(path, argv) ){ + status = 127; +#if LAUNCH_USE_PIPE + if( !attach ){ + write(fd[1], &status, 4); + close(fd[1]); + } +#endif + _exit(status); /* exec error */ + } + } else { /* parent */ + /* wait for program termination from attached process */ + if( attach ){ + while( waitpid(pid, &status, 0) < 0 ){ + if( xerrno != EINTR ){ + status = -1; /* error other than EINTR from waitpid() */ + break; + } + } + } + else{ +#if LAUNCH_USE_WAITPID + status = launch_waitstart(pid); +#endif +#if LAUNCH_USE_PIPE + close(fd[1]); + if( read(fd[0], &status, 4) == 0 ){ + status = 0; + } + close(fd[0]); +#endif + } + } + + /* cleanup temp ipc pipes and move into user space */ + if( pipes ){ + cleanup_pipes(tpipes); + pipes[0] = tpipes[0]; + pipes[1] = tpipes[1]; + } + + /* restore previous signal actions & reset signal mask */ + if( sigaction(SIGINT, &saveintr, NULL) < 0 ) return -1; + if( sigaction(SIGQUIT, &savequit, NULL) < 0 ) return -1; + if( sigprocmask(SIG_SETMASK, &savemask, NULL) < 0 ) return -1; + + /* return the news */ + return status; +} + +#endif + +#if HAVE_POSIX_SPAWN + +#if defined(HAVE__NSGETENVIRON) && defined(HAVE_CRT_EXTERNS_H) +#include <crt_externs.h> +#define environ (*_NSGetEnviron()) +#else +extern char **environ; +#endif + +/* spawn calls POSIX posix_spawn */ +#ifdef ANSI_FUNC +static int launch_posix_spawn(char *cmdstring, int attach, + char **stdfiles, int *pipes) +#else + static int launch_posix_spawn(cmdstring, attach, stdfiles, pipes) + char *cmdstring; + int attach; + char **stdfiles; + int *pipes; +#endif +{ + int i, j, len; + int status=0; + int got=0; + int tpipes[4]; + char *argv[LAUNCH_ARGS+1]; + char *path=NULL; + char *s=NULL, *t=NULL; + posix_spawn_file_actions_t act; + posix_spawn_file_actions_t *pact=NULL; + + /* return false if no command is specified */ + if( !cmdstring || !*cmdstring ) + return -1; + + /* create temp ipc pipes if necessary */ + if( pipes ){ + if( launch_pipes(tpipes, 1) < 0 ) return -1; + } + + /* package up the arguments for new process */ + t = (char *)xstrdup(cmdstring); + for(i=0, s=(char *)strtok(t, " \t"); s; + i++, s=(char *)strtok(NULL," \t")){ + if( i < LAUNCH_ARGS ){ + /* save argument */ + argv[i] = xstrdup(s); + /* change back special char to spaces, if necessary */ + len = strlen(argv[i]); + for(j=0; j<len; j++){ + if( argv[i][j] == LAUNCH_SPACE){ + argv[i][j] = ' '; + } + } + /* last arg is always a NULL */ + argv[i+1] = NULL; + /* save program name */ + if( i == 0 ) path = argv[i]; + /* inc arg count */ + got++; + } + } + if( t ) xfree(t); + /* arrange stdfiles files, if necessary */ + if( stdfiles ){ + if( posix_spawn_file_actions_init(&act) != 0) + return -1; + /* stdin */ + if(stdfiles[0] && + posix_spawn_file_actions_addopen(&act, 0, stdfiles[0], O_RDONLY, 0)) + return -1; + /* stdout */ + if(stdfiles[1] && + posix_spawn_file_actions_addopen(&act, 1, stdfiles[1], O_CREAT|O_WRONLY|O_TRUNC, 0600)) + return -1; + /* stderr */ + if(stdfiles[2] && + posix_spawn_file_actions_addopen(&act, 2, stdfiles[2], O_CREAT|O_WRONLY|O_TRUNC, 0600)) + return -1; + pact = &act; + } + /* start the new process */ + if( (status = posix_spawnp(&pid, path, pact, NULL, argv, environ)) ) + return status; + /* wait for program termination from attached process */ + if( attach ){ + while( waitpid(pid, &status, 0) < 0 ){ + if( xerrno != EINTR ){ + status = -1; /* error other than EINTR from waitpid() */ + break; + } + } + } +#if BIG_DELAY_WHEN_USING_THIS + /* wait for child process to start */ + else{ + status = launch_waitstart(pid); + } +#endif + /* clean up */ + if( stdfiles ) posix_spawn_file_actions_destroy(&act); + /* cleanup temp ipc pipes and move into user space */ + if( pipes ){ + cleanup_pipes(tpipes); + pipes[0] = tpipes[0]; + pipes[1] = tpipes[1]; + } + for(i=0; i<got; i++){ + if( argv[i] ) xfree((char *)argv[i]); + } + /* return status */ + return status; +} + +#endif + +#if HAVE_SPAWNVP + +#ifdef ANSI_FUNC +static int launch_spawnvp(char *cmdstring, int attach, + char **stdfiles, int *pipes) +#else + static int launch_spawnvp(cmdstring, attach, stdfiles, pipes) + char *cmdstring; + int attach; + char **stdfiles; + int *pipes; +#endif +{ + int i, j; + int len; + int got; + int status; + int tpipes[4]; + char *argv[LAUNCH_ARGS+1]; + char *path=NULL; + char *s=NULL, *t=NULL; + struct timeval tv; + + /* return false if no command is specified */ + if( !cmdstring || !*cmdstring ) return -1; + + /* for now, we can't support stdfiles */ + if( stdfiles ) return -1; + + /* create temp ipc pipes if necessary */ + if( pipes ){ +#if HAVE_MINGW32==0 + if( launch_pipes(tpipes, 1) < 0 ) return -1; +#else + fprintf(stderr, "ERROR: launch_pipes() not available in mingw\n"); + exit(1); +#endif + } + + + /* package up the arguments for new process */ + t = (char *)xstrdup(cmdstring); + for(i=0, got=0, s=(char *)strtok(t, " \t"); s; + i++, s=(char *)strtok(NULL," \t")){ + if( i < LAUNCH_ARGS ){ + /* save argument */ + argv[i] = xstrdup(s); + /* change back special char to spaces, if necessary */ + len = strlen(argv[i]); + for(j=0; j<len; j++){ + if( argv[i][j] == LAUNCH_SPACE){ + argv[i][j] = ' '; + } + } + /* last arg is always a NULL */ + argv[i+1] = NULL; + /* save program name */ + if( i == 0 ) path = (char *)argv[i]; + /* inc arg count */ + got++; + } + } + if( t ) xfree(t); + if( attach ) + i = _P_WAIT; + else + i = _P_NOWAIT; + if((status = spawnvp(i, path, (void *)argv)) != -1){ + status = 0; + /* wait for child to start */ + tv.tv_sec = 0; + tv.tv_usec = LAUNCH_WAIT_MSEC; + xselect(1, NULL, NULL, NULL, &tv); + } + /* clean up */ + for(i=0; i<got; i++){ + if( argv[i] ) xfree((char *)argv[i]); + } + /* cleanup temp ipc pipes and move into user space */ + if( pipes ){ +#if HAVE_MINGW32==0 + cleanup_pipes(tpipes); + pipes[0] = tpipes[0]; + pipes[1] = tpipes[1]; +#else + fprintf(stderr, "ERROR: launch_pipes() not available in mingw\n"); + exit(1); +#endif + } + return status; +} + +#endif + +/* + *---------------------------------------------------------------------------- + * + * + * Public Routines and Data + * + * + *---------------------------------------------------------------------------- + */ + +/* + * + * LaunchPid() -- return pid of last launched process + * + */ +#ifdef ANSI_FUNC +pid_t LaunchPid(void) +#else +pid_t LaunchPid() +#endif +{ + return pid; +} + +#ifdef ANSI_FUNC +int Launch(char *cmdstring, int attach, char **stdfiles, int *pipes) +#else +int Launch(cmdstring, attach, stdfiles, piles) + char *cmdstring; + int attach; + char **stdfiles; + int *pipes; +#endif +{ + static int which_launch=0; + static int which_debug=0; + char *s=NULL; + + /* return false if no command is specified */ + if( !cmdstring || !*cmdstring ) return -1; + + /* sanity check: don't specify stdfiles and pipes simultaneously */ + if( stdfiles && pipes ){ + fprintf(stderr, + "ERROR: stdfiles and pipes are mutually exclusive in Launch()\n"); + return -1; + } + + /* if pipes are specified, we don't attach */ + if( pipes ) attach = 0; + + /* determine launch method */ + if( !which_launch ){ + which_launch = LAUNCH_DEFAULT_WHICH; + if( (s=getenv("LAUNCH_ROUTINE")) ){ + /* fork_exec */ + if( !strncasecmp(s, "f", 1) ){ + which_launch = 1; + if( *s == 'F' ) which_debug = 1; + } + /* posix_spawn */ + else if( !strncasecmp(s, "p", 1) ){ + which_launch = 2; + if( *s == 'P' ) which_debug = 1; + } + /* spawnvp */ + else if( !strncasecmp(s, "s", 1) ){ + which_launch = 3; + if( *s == 'S' ) which_debug = 1; + } + else if( *s == 'V' ) { + which_debug = 1; + } + } + } + /* call the correct launch method */ + switch(which_launch){ + case 1: +#if HAVE_MINGW32==0 + if( which_debug ) fprintf(stderr, "launch_fork_exec: %s\n", cmdstring); + return launch_fork_exec(cmdstring, attach, stdfiles, pipes); +#else + fprintf(stderr, "ERROR: fork_exec() not available on this host\n"); + exit(1); +#endif + break; + case 2: +#if HAVE_POSIX_SPAWN + if( which_debug ) fprintf(stderr, "launch_posix_spawn: %s\n", cmdstring); + return launch_posix_spawn(cmdstring, attach, stdfiles, pipes); +#else + fprintf(stderr, "ERROR: posix_spawn() not available on this host\n"); + exit(1); +#endif + break; + case 3: +#if HAVE_SPAWNVP + if( which_debug ) fprintf(stderr, "launch_spawnvp: %s\n", cmdstring); + return launch_spawnvp(cmdstring, attach, stdfiles, pipes); +#else + fprintf(stderr, "ERROR: spawnvp() not available on this host\n"); + exit(1); +#endif + break; + default: +#if HAVE_MINGW32==0 + if( which_debug ) fprintf(stderr, "launch_fork_exec: %s\n", cmdstring); + return launch_fork_exec(cmdstring, attach, stdfiles, pipes); +#else + fprintf(stderr, "ERROR: no launch techniques available on this host\n"); + exit(1); +#endif + break; + } + /* can't happen */ + return -1; +} + |