* Unix support for the Lisp function RUN-PROGRAM and friends
*/
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
#include "sbcl.h"
#include <stdlib.h>
#include <sys/file.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <errno.h>
#include <dirent.h>
#include "interr.h"
#ifdef LISP_FEATURE_OPENBSD
#include <util.h>
#endif
* example code found at
* http://www.yendor.com/programming/unix/apue/pty/main.c
-brkint
*/
int set_noecho(int fd)
{
struct termios stermios;
if (tcgetattr(fd, &stermios) < 0) return 0;
stermios.c_lflag &= ~( ECHO | ECHONL);
stermios.c_oflag |= (ONLCR);
stermios.c_iflag &= ~(BRKINT);
stermios.c_iflag |= (ICANON|ICRNL);
stermios.c_cc[VERASE]=0177;
if (tcsetattr(fd, TCSANOW, &stermios) < 0) return 0;
return 1;
}
#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
int
set_pty(char *pty_name)
{
int fd;
#if !defined(SVR4) && !defined(__HAIKU__)
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
void closefrom_fallback(int lowfd)
{
int fd, maxfd;
#ifdef SVR4
maxfd = sysconf(_SC_OPEN_MAX)-1;
#else
maxfd = getdtablesize()-1;
#endif
for (fd = maxfd; fd >= lowfd; fd--)
close(fd);
}
int closefrom_fddir(char *dir, int lowfd)
{
DIR *d;
struct dirent *ent;
int fd;
* a chroot that does not include /proc, or potentially
* on old kernel versions. */
d = opendir(dir);
if (!d) return -1;
for (ent = readdir(d); ent; ent = readdir(d)) {
* prevent us from closing anything we care about. */
fd = atoi(ent->d_name);
if (fd >= 0 && fd >= lowfd)
close(fd);
}
closedir(d);
return 0;
}
void closefds_from(int lowfd, int* dont_close)
{
if (dont_close) {
uword_t length = fixnum_value(((uword_t*)dont_close)[-1]);
uword_t i;
for (i = 0; i < length; i++)
{
int fd = dont_close[i];
int close_fd;
for (close_fd = lowfd; close_fd < fd; close_fd++)
{
close(close_fd);
}
lowfd = fd+1;
}
}
#if defined(LISP_FEATURE_OPENBSD) || defined(LISP_FEATURE_NETBSD) \
|| defined(LISP_FEATURE_DRAGONFLY) || defined(LISP_FEATURE_FREEBSD) \
|| defined(LISP_FEATURE_SUNOS)
closefrom(lowfd);
#else
int fds_closed = 0;
#ifdef LISP_FEATURE_LINUX
if (!fds_closed)
fds_closed = !closefrom_fddir("/proc/self/fd/", lowfd);
#endif
#ifdef LISP_FEATURE_DARWIN
if (!fds_closed)
fds_closed = !closefrom_fddir("/dev/fd/", lowfd);
#endif
*/
if (!fds_closed)
closefrom_fallback(lowfd);
#endif
}
int wait_for_exec(int pid, int channel[2]) {
if ((-1 != pid) && (-1 != channel[1])) {
int child_errno = 0;
int bytes = sizeof(int);
int n;
char *p = (char*)&child_errno;
close(channel[1]);
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) {
int status;
waitpid(pid, &status, 0);
chdir that failed, not the fork. */
if (WIFEXITED(status)) {
pid = -WEXITSTATUS(status);
}
errno = child_errno;
}
}
return pid;
}
extern char **environ;
int spawn(char *program, char *argv[], int sin, int sout, int serr,
int search, char *envp[], char *pty_name,
int channel[2],
char *pwd, int* dont_close)
{
pid_t pid;
sigset_t sset;
int failure_code = 2;
channel[0] = -1;
channel[1] = -1;
if (pipe(channel)) lose("can't run-program");
pid = fork();
if (pid) {
return pid;
}
close (channel[0]);
* share stdin with our parent. In the latter case we claim
* control of the terminal. */
if (sin >= 0) {
#ifdef LISP_FEATURE_OPENBSD
setsid();
#elif defined(LISP_FEATURE_DARWIN)
setpgid(0, getpid());
#elif defined SVR4 || defined __linux__ || defined __osf__ || defined __GLIBC__ || defined __HAIKU__
setpgrp();
#else
setpgrp(0, getpid());
#endif
} else {
tcsetpgrp(0, getpgrp());
}
sigemptyset(&sset);
sigprocmask(SIG_SETMASK, &sset, NULL);
if (pty_name)
set_pty(pty_name);
else {
if (sin >= 0)
dup2(sin, 0);
if (sout >= 0)
dup2(sout, 1);
if (serr >= 0)
dup2(serr, 2);
}
* lowest free fd, then close every open fd above that. */
channel[1] = dup2(channel[1], 3);
closefds_from(4, dont_close);
if (-1 != channel[1]) {
if (-1==fcntl(channel[1], F_SETFD, FD_CLOEXEC)) {
close(channel[1]);
channel[1] = -1;
}
}
if (pwd && chdir(pwd) < 0) {
failure_code = 3;
} else {
if (envp) {
environ = envp;
}
if (search)
execvp(program, argv);
else
execv(program, argv);
}
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(failure_code);
}