X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Frun-program.c;h=367dd77bc497042a89c8ae77a3adeb714a9da753;hb=dd54f9e004a0a83d1328e94648f48dcc27e0be5b;hp=76f39b9015649bd792d373573c68fafd79a72f90;hpb=568daf6b160280428701670b921f419aabd9eba0;p=sbcl.git diff --git a/src/runtime/run-program.c b/src/runtime/run-program.c index 76f39b9..367dd77 100644 --- a/src/runtime/run-program.c +++ b/src/runtime/run-program.c @@ -25,10 +25,15 @@ #include #include #include - +#include #include #include +#include +#ifdef LISP_FEATURE_OPENBSD +/* FIXME: there has to be a better way to avoid ./util.h here */ +#include +#endif /* borrowed from detachtty's detachtty.c, in turn borrowed from APUE * example code found at @@ -54,72 +59,171 @@ int set_noecho(int fd) return 1; } -int spawn(char *program, char *argv[], char *envp[], char *pty_name, - int stdin, int stdout, int stderr) +#if defined(LISP_FEATURE_OPENBSD) + +int +set_pty(char *pty_name) +{ + int fd; + + if ((fd = open(pty_name, O_RDWR, 0)) == -1 || + login_tty(fd) == -1) + return (0); + return (set_noecho(STDIN_FILENO)); +} + +#else /* !LISP_FEATURE_OPENBSD */ + +int +set_pty(char *pty_name) +{ + int fd; + +#if !defined(LISP_FEATURE_HPUX) && !defined(SVR4) + fd = open("/dev/tty", O_RDWR, 0); + if (fd >= 0) { + ioctl(fd, TIOCNOTTY, 0); + close(fd); + } +#endif + if ((fd = open(pty_name, O_RDWR, 0)) == -1) + return (-1); + dup2(fd, 0); + set_noecho(0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + return (0); +} + +#endif /* !LISP_FEATURE_OPENBSD */ + +extern char **environ; +int spawn(char *program, char *argv[], int sin, int sout, int serr, + int search, char *envp[], char *pty_name, int wait) { - int pid = fork(); + pid_t pid; int fd; + int channel[2]; sigset_t sset; - if (pid != 0) - return pid; + channel[0] = -1; + channel[1] = -1; + if (!pipe(channel)) { + if (-1==fcntl(channel[1], F_SETFD, FD_CLOEXEC)) { + close(channel[1]); + channel[1] = -1; + } + } - /* Put us in our own process group. */ -#if defined(hpux) - setsid(); + pid = fork(); + if (pid) { + if ((-1 != pid) && (-1 != channel[1])) { + int child_errno = 0; + int bytes = sizeof(int); + int n; + char *p = (char*)&child_errno; + close(channel[1]); + /* Try to read child errno from channel. */ + while ((bytes > 0) && + (n = read(channel[0], p, bytes))) { + if (-1 == n) { + if (EINTR == errno) { + continue; + } else { + break; + } + } else { + bytes -= n; + p += n; + } + } + close(channel[0]); + if (child_errno) { + waitpid(pid, NULL, 0); + /* Our convention to tell Lisp that it was the exec that + * failed, not the fork. */ + pid = -2; + errno = child_errno; + } + } + return pid; + } + close (channel[0]); + + /* Put us in our own process group, but only if we need not + * share stdin with our parent. In the latter case we claim + * control of the terminal. */ + if (sin >= 0) { +#if defined(LISP_FEATURE_HPUX) || defined(LISP_FEATURE_OPENBSD) + setsid(); +#elif defined(LISP_FEATURE_DARWIN) + setpgid(0, getpid()); #elif defined(SVR4) || defined(__linux__) || defined(__osf__) - setpgrp(); + setpgrp(); #else - setpgrp(0, getpid()); + setpgrp(0, getpid()); #endif + } else { + tcsetpgrp(0, getpgrp()); + } /* unblock signals */ sigemptyset(&sset); sigprocmask(SIG_SETMASK, &sset, NULL); /* If we are supposed to be part of some other pty, go for it. */ - if (pty_name) { -#if !defined(hpux) && !defined(SVR4) - fd = open("/dev/tty", O_RDWR, 0); - if (fd >= 0) { - ioctl(fd, TIOCNOTTY, 0); - close(fd); - } -#endif - fd = open(pty_name, O_RDWR, 0); - dup2(fd, 0); - set_noecho(0); - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } else{ + if (pty_name) + set_pty(pty_name); + else { /* Set up stdin, stdout, and stderr */ - if (stdin >= 0) - dup2(stdin, 0); - if (stdout >= 0) - dup2(stdout, 1); - if (stderr >= 0) - dup2(stderr, 2); + if (sin >= 0) + dup2(sin, 0); + if (sout >= 0) + dup2(sout, 1); + if (serr >= 0) + dup2(serr, 2); } /* Close all other fds. */ #ifdef SVR4 for (fd = sysconf(_SC_OPEN_MAX)-1; fd >= 3; fd--) - close(fd); + if (fd != channel[1]) close(fd); #else for (fd = getdtablesize()-1; fd >= 3; fd--) - close(fd); + if (fd != channel[1]) close(fd); #endif + if (envp) { + environ = envp; + } /* Exec the program. */ - execve(program, argv, envp); - - /* It didn't work, so try /bin/sh. */ - argv[0] = program; - argv[-1] = "sh"; - execve("/bin/sh", argv-1, envp); - - /* The exec didn't work, flame out. */ - exit(1); + if (search) + execvp(program, argv); + else + execv(program, argv); + + /* When exec fails and channel is available, send the errno value. */ + if (-1 != channel[1]) { + int our_errno = errno; + int bytes = sizeof(int); + int n; + char *p = (char*)&our_errno; + while ((bytes > 0) && + (n = write(channel[1], p, bytes))) { + if (-1 == n) { + if (EINTR == errno) { + continue; + } else { + break; + } + } else { + bytes -= n; + p += n; + } + } + close(channel[1]); + } + _exit(1); } #else /* !LISP_FEATURE_WIN32 */ @@ -147,33 +251,38 @@ HANDLE spawn ( int in, int out, int err, + int search, + char *envp, + char *ptyname, int wait ) { - int fdOut, fdIn, fdErr, fdInPipe[2], fdOutPipe[2], fdErrPipe[2], wait_mode; + int stdout_backup, stdin_backup, stderr_backup, wait_mode; HANDLE hProcess; - - /* Make pipes to be passed to the spawned process as in/out/err */ - if ( _pipe ( fdOutPipe, 512, O_TEXT | O_NOINHERIT ) == -1 ) return (HANDLE)-1; - if ( _pipe ( fdInPipe, 512, O_TEXT | O_NOINHERIT ) == -1 ) return (HANDLE)-1; - if ( _pipe ( fdErrPipe, 512, O_TEXT | O_NOINHERIT ) == -1 ) return (HANDLE)-1; - - /* Duplicate and save original in/out/err handles */ - fdOut = _dup ( out ); - fdIn = _dup ( in ); - fdErr = _dup ( err ); - - /* Duplicate write end of new pipes to current out/err handles, - * read to in */ - if ( _dup2 ( fdOutPipe[WRITE_HANDLE], out ) != 0 ) return (HANDLE)-1; - if ( _dup2 ( fdInPipe[READ_HANDLE], in ) != 0 ) return (HANDLE)-1; - if ( _dup2 ( fdErrPipe[WRITE_HANDLE], err ) != 0 ) return (HANDLE)-1; - - - /* Close the duplicated handles to the new pipes */ - close ( fdOutPipe[WRITE_HANDLE] ); - close ( fdInPipe[READ_HANDLE] ); - close ( fdErrPipe[WRITE_HANDLE] ); + HANDLE hReturn; + + /* Duplicate and save the original stdin/out/err handles. */ + stdout_backup = _dup ( _fileno ( stdout ) ); + stdin_backup = _dup ( _fileno ( stdin ) ); + stderr_backup = _dup ( _fileno ( stderr ) ); + + /* If we are not using stdin/out/err + * then duplicate the new pipes to current stdin/out/err handles. + * + * Default std fds are used if in, out or err parameters + * are -1. */ + + hReturn = (HANDLE)-1; + hProcess = (HANDLE)-1; + if ( ( out >= 0 ) && ( out != _fileno ( stdout ) ) ) { + if ( _dup2 ( out, _fileno ( stdout ) ) != 0 ) goto error_exit; + } + if ( ( in >= 0 ) && ( in != _fileno ( stdin ) ) ) { + if ( _dup2 ( in, _fileno ( stdin ) ) != 0 ) goto error_exit_out; + } + if ( ( err >= 0 ) && ( err != _fileno ( stderr ) ) ) { + if ( _dup2 ( err, _fileno ( stderr ) ) != 0 ) goto error_exit_in; + } /* Set the wait mode. */ if ( 0 == wait ) { @@ -183,20 +292,29 @@ HANDLE spawn ( } /* Spawn process given on the command line*/ - hProcess = (HANDLE) spawnvp ( wait_mode, program, argv ); + if (search) + hProcess = (HANDLE) spawnvp ( wait_mode, program, argv ); + else + hProcess = (HANDLE) spawnv ( wait_mode, program, argv ); /* Now that the process is launched, replace the original - * in/out/err handles */ - if ( _dup2 ( fdOut, out ) != 0 ) return (HANDLE)-1; - if ( _dup2 ( fdIn, in ) != 0 ) return (HANDLE)-1; - if ( _dup2 ( fdErr, err ) != 0 ) return (HANDLE)-1; + * in/out/err handles and close the backups. */ + + if ( _dup2 ( stderr_backup, _fileno ( stderr ) ) != 0 ) goto error_exit; + error_exit_in: + if ( _dup2 ( stdin_backup, _fileno ( stdin ) ) != 0 ) goto error_exit; + error_exit_out: + if ( _dup2 ( stdout_backup, _fileno ( stdout ) ) != 0 ) goto error_exit; + + hReturn = hProcess; + + error_exit: + close ( stdout_backup ); + close ( stdin_backup ); + close ( stderr_backup ); - /* Close duplicates */ - close(fdOut); - close(fdIn); - close(fdErr); + return hReturn; - return ( hProcess ); }