summaryrefslogtreecommitdiffstats
path: root/xpa/xlaunch.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2017-10-26 16:45:12 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2017-10-26 16:45:12 (GMT)
commitdd1df8d8538f275b17e0d9d50dca19f4d44595fd (patch)
tree624bc07023b33668147eaa9097279c6fc92b4e48 /xpa/xlaunch.c
parent79d64f400391ce81b4eda73977cb40099256b348 (diff)
parent6054f36bb658916bd231b9002efb94444e2871c8 (diff)
downloadblt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.zip
blt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.tar.gz
blt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.tar.bz2
Merge commit '6054f36bb658916bd231b9002efb94444e2871c8' as 'xpa'
Diffstat (limited to 'xpa/xlaunch.c')
-rw-r--r--xpa/xlaunch.c657
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;
+}
+