Take pointer, not word count, as upper limit in verify_space()
[sbcl.git] / src / runtime / run-program.c
blobb88ae51ed4c3e0bbd5c254d70d006e00f8019756
1 /*
2 * support for the Lisp function RUN-PROGRAM and friends
3 */
5 /*
6 * This software is part of the SBCL system. See the README file for
7 * more information.
9 * This software is derived from the CMU CL system, which was
10 * written at Carnegie Mellon University and released into the
11 * public domain. The software is in the public domain and is
12 * provided with absolutely no warranty. See the COPYING and CREDITS
13 * files for more information.
16 #include "sbcl.h"
18 #ifndef LISP_FEATURE_WIN32
20 #include <stdlib.h>
21 #include <sys/file.h>
22 #include <sys/types.h>
23 #include <signal.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28 #include <sys/wait.h>
29 #include <sys/ioctl.h>
30 #include <termios.h>
31 #include <errno.h>
33 #ifdef LISP_FEATURE_OPENBSD
34 #include <util.h>
35 #endif
37 /* borrowed from detachtty's detachtty.c, in turn borrowed from APUE
38 * example code found at
39 * http://www.yendor.com/programming/unix/apue/pty/main.c
41 -brkint
45 int set_noecho(int fd)
47 struct termios stermios;
49 if (tcgetattr(fd, &stermios) < 0) return 0;
51 stermios.c_lflag &= ~( ECHO | /* ECHOE | ECHOK | */ ECHONL);
52 stermios.c_oflag |= (ONLCR);
53 stermios.c_iflag &= ~(BRKINT);
54 stermios.c_iflag |= (ICANON|ICRNL);
56 stermios.c_cc[VERASE]=0177;
57 if (tcsetattr(fd, TCSANOW, &stermios) < 0) return 0;
58 return 1;
61 #if defined(LISP_FEATURE_OPENBSD)
63 int
64 set_pty(char *pty_name)
66 int fd;
68 if ((fd = open(pty_name, O_RDWR, 0)) == -1 ||
69 login_tty(fd) == -1)
70 return (0);
71 return (set_noecho(STDIN_FILENO));
74 #else /* !LISP_FEATURE_OPENBSD */
76 int
77 set_pty(char *pty_name)
79 int fd;
81 #if !defined(LISP_FEATURE_HPUX) && !defined(SVR4)
82 fd = open("/dev/tty", O_RDWR, 0);
83 if (fd >= 0) {
84 ioctl(fd, TIOCNOTTY, 0);
85 close(fd);
87 #endif
88 if ((fd = open(pty_name, O_RDWR, 0)) == -1)
89 return (-1);
90 dup2(fd, 0);
91 set_noecho(0);
92 dup2(fd, 1);
93 dup2(fd, 2);
94 close(fd);
95 return (0);
98 #endif /* !LISP_FEATURE_OPENBSD */
100 extern char **environ;
101 int spawn(char *program, char *argv[], int sin, int sout, int serr,
102 int search, char *envp[], char *pty_name, int wait, char *pwd)
104 pid_t pid;
105 int fd;
106 int channel[2];
107 sigset_t sset;
108 int failure_code = 2;
110 channel[0] = -1;
111 channel[1] = -1;
112 if (!pipe(channel)) {
113 if (-1==fcntl(channel[1], F_SETFD, FD_CLOEXEC)) {
114 close(channel[1]);
115 channel[1] = -1;
119 pid = fork();
120 if (pid) {
121 if ((-1 != pid) && (-1 != channel[1])) {
122 int child_errno = 0;
123 int bytes = sizeof(int);
124 int n;
125 char *p = (char*)&child_errno;
126 close(channel[1]);
127 /* Try to read child errno from channel. */
128 while ((bytes > 0) &&
129 (n = read(channel[0], p, bytes))) {
130 if (-1 == n) {
131 if (EINTR == errno) {
132 continue;
133 } else {
134 break;
136 } else {
137 bytes -= n;
138 p += n;
141 close(channel[0]);
142 if (child_errno) {
143 int status;
144 waitpid(pid, &status, 0);
145 /* Our convention to tell Lisp that it was the exec or
146 chdir that failed, not the fork. */
147 /* FIXME: there are other values waitpid(2) can return. */
148 if (WIFEXITED(status)) {
149 pid = -WEXITSTATUS(status);
151 errno = child_errno;
154 return pid;
156 close (channel[0]);
158 /* Put us in our own process group, but only if we need not
159 * share stdin with our parent. In the latter case we claim
160 * control of the terminal. */
161 if (sin >= 0) {
162 #if defined(LISP_FEATURE_HPUX) || defined(LISP_FEATURE_OPENBSD)
163 setsid();
164 #elif defined(LISP_FEATURE_DARWIN)
165 setpgid(0, getpid());
166 #elif defined(SVR4) || defined(__linux__) || defined(__osf__) || defined(__GLIBC__)
167 setpgrp();
168 #else
169 setpgrp(0, getpid());
170 #endif
171 } else {
172 tcsetpgrp(0, getpgrp());
175 /* unblock signals */
176 sigemptyset(&sset);
177 sigprocmask(SIG_SETMASK, &sset, NULL);
179 /* If we are supposed to be part of some other pty, go for it. */
180 if (pty_name)
181 set_pty(pty_name);
182 else {
183 /* Set up stdin, stdout, and stderr */
184 if (sin >= 0)
185 dup2(sin, 0);
186 if (sout >= 0)
187 dup2(sout, 1);
188 if (serr >= 0)
189 dup2(serr, 2);
191 /* Close all other fds. */
192 #ifdef SVR4
193 for (fd = sysconf(_SC_OPEN_MAX)-1; fd >= 3; fd--)
194 if (fd != channel[1]) close(fd);
195 #else
196 for (fd = getdtablesize()-1; fd >= 3; fd--)
197 if (fd != channel[1]) close(fd);
198 #endif
200 if (pwd && chdir(pwd) < 0) {
201 failure_code = 3;
202 } else {
203 if (envp) {
204 environ = envp;
206 /* Exec the program. */
207 if (search)
208 execvp(program, argv);
209 else
210 execv(program, argv);
213 /* When exec or chdir fails and channel is available, send the errno value. */
214 if (-1 != channel[1]) {
215 int our_errno = errno;
216 int bytes = sizeof(int);
217 int n;
218 char *p = (char*)&our_errno;
219 while ((bytes > 0) &&
220 (n = write(channel[1], p, bytes))) {
221 if (-1 == n) {
222 if (EINTR == errno) {
223 continue;
224 } else {
225 break;
227 } else {
228 bytes -= n;
229 p += n;
232 close(channel[1]);
234 _exit(failure_code);
236 #else /* !LISP_FEATURE_WIN32 */
238 # include <windows.h>
239 # include <process.h>
240 # include <stdio.h>
241 # include <stdlib.h>
242 # include <fcntl.h>
243 # include <io.h>
245 #define READ_HANDLE 0
246 #define WRITE_HANDLE 1
248 /* These functions do not attempt to deal with wchar_t variations. */
250 /* Get the value of _environ maintained by MSVCRT */
251 char **msvcrt_environ ( void ) {
252 return ( _environ );
255 /* Set up in, out, err pipes and spawn a program, waiting or otherwise. */
256 HANDLE spawn (
257 const char *program,
258 const char *const *argv,
259 int in,
260 int out,
261 int err,
262 int search,
263 char *envp,
264 char *ptyname,
265 int wait,
266 char *pwd
269 int stdout_backup, stdin_backup, stderr_backup, wait_mode;
270 HANDLE hProcess;
271 HANDLE hReturn;
273 /* Duplicate and save the original stdin/out/err handles. */
274 stdout_backup = _dup ( _fileno ( stdout ) );
275 stdin_backup = _dup ( _fileno ( stdin ) );
276 stderr_backup = _dup ( _fileno ( stderr ) );
278 /* If we are not using stdin/out/err
279 * then duplicate the new pipes to current stdin/out/err handles.
281 * Default std fds are used if in, out or err parameters
282 * are -1. */
284 hReturn = (HANDLE)-1;
285 hProcess = (HANDLE)-1;
286 if ( ( out >= 0 ) && ( out != _fileno ( stdout ) ) ) {
287 if ( _dup2 ( out, _fileno ( stdout ) ) != 0 ) goto error_exit;
289 if ( ( in >= 0 ) && ( in != _fileno ( stdin ) ) ) {
290 if ( _dup2 ( in, _fileno ( stdin ) ) != 0 ) goto error_exit_out;
292 if ( ( err >= 0 ) && ( err != _fileno ( stderr ) ) ) {
293 if ( _dup2 ( err, _fileno ( stderr ) ) != 0 ) goto error_exit_in;
296 /* Set the wait mode. */
297 if ( 0 == wait ) {
298 wait_mode = P_NOWAIT;
299 } else {
300 wait_mode = P_WAIT;
303 /* Change working directory if supplied. */
304 if (pwd) {
305 if (chdir(pwd) < 0) {
306 goto error_exit;
310 /* Spawn process given on the command line*/
311 if (search)
312 hProcess = (HANDLE) spawnvp ( wait_mode, program, (char* const* )argv );
313 else
314 hProcess = (HANDLE) spawnv ( wait_mode, program, (char* const* )argv );
316 /* Now that the process is launched, replace the original
317 * in/out/err handles and close the backups. */
319 if ( _dup2 ( stderr_backup, _fileno ( stderr ) ) != 0 ) goto error_exit;
320 error_exit_in:
321 if ( _dup2 ( stdin_backup, _fileno ( stdin ) ) != 0 ) goto error_exit;
322 error_exit_out:
323 if ( _dup2 ( stdout_backup, _fileno ( stdout ) ) != 0 ) goto error_exit;
325 hReturn = hProcess;
327 error_exit:
328 close ( stdout_backup );
329 close ( stdin_backup );
330 close ( stderr_backup );
332 return hReturn;
337 #endif /* !LISP_FEATURE_WIN32 */