Implement subprocess.wait
[lua-subprocess:lua-subprocess.git] / subprocess.c
1 /* Copyright (c) 2010 Joshua Phillips
2  * 
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  * 
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  * 
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21
22 #if !defined(OS_WINDOWS) && !defined(OS_POSIX)
23 #error None of these are defined: OS_WINDOWS, OS_POSIX
24 #else
25
26 #define LUA_LIB
27 #include "lua.h"
28 #include "lualib.h"
29 #include "lauxlib.h"
30 #include "stdlib.h"
31 #include "stdio.h"
32 #include "string.h"
33 #include "unistd.h"
34 #include "fcntl.h"
35 #include "errno.h"
36 #include "assert.h"
37 #include "liolib-copy.h"
38 #if defined(OS_POSIX)
39 #include "sys/wait.h"
40 #include "sys/stat.h"
41 typedef int filedes_t;
42
43 /* return 1 if the named directory exists and is a directory */
44 static int direxists(const char *fname)
45 {
46     struct stat statbuf;
47     if (stat(fname, &statbuf)){
48         return 0;
49     }
50     return !!S_ISDIR(statbuf.st_mode);
51 }
52
53 #elif defined(OS_WINDOWS)
54 #include "windows.h"
55 typedef HANDLE filedes_t;
56
57 /* return 1 if the named directory exists and is a directory */
58 static int direxists(const char *fname)
59 {
60     DWORD result;
61     result = GetFileAttributes(fname);
62     if (result == INVALID_FILE_ATTRIBUTES) return 0;
63     return !!(result & FILE_ATTRIBUTE_DIRECTORY);
64 }
65
66 #endif
67
68 /* This is the proc object, which is stored as Lua userdata */
69 struct proc {
70 #if defined(OS_POSIX)
71     pid_t pid;
72 #elif defined(OS_WINDOWS)
73     DWORD pid;
74     HANDLE hProcess;
75 #endif
76     unsigned char done; /* set to 1 when child has finished and closed */
77     int exitcode;
78 };
79
80 /* Lua registry key for proc metatable */
81 #define SP_PROC_META "subprocess_proc*"
82
83 /* Environment keys */
84 #define SP_LIST 1                                    /* table of [pid]=proc */
85
86 /* Function to count number of keys in a table.
87    Table must be at top of stack. */
88 static int countkeys(lua_State *L)
89 {
90     int i = 0;
91     lua_checkstack(L, 3);
92     lua_pushnil(L);
93     while (lua_next(L, -2)){
94         ++i;
95         lua_pop(L, 1);
96     }
97     return i;
98 }
99
100 /* Check to see if object at the given index is a proc object.
101    Return pointer to proc object, or NULL if it isn't. */
102 static struct proc *toproc(lua_State *L, int index)
103 {
104     int eq;
105     if (lua_type(L, index) != LUA_TUSERDATA) return NULL;
106     lua_getmetatable(L, index);
107     luaL_getmetatable(L, SP_PROC_META);
108     eq = lua_equal(L, -2, -1);
109     lua_pop(L, 2);
110     if (!eq) return NULL;
111     return lua_touserdata(L, index);
112 }
113
114 /* Same but raise an error instead of returning NULL */
115 #define checkproc(L, index) ((struct proc *) luaL_checkudata((L), (index), SP_PROC_META))
116
117 /* Create and return a new proc object */
118 static struct proc *newproc(lua_State *L)
119 {
120     struct proc *proc = lua_newuserdata(L, sizeof *proc);
121     proc->done = 1;
122     proc->pid = 0;
123     luaL_getmetatable(L, SP_PROC_META);
124     lua_setmetatable(L, -2);
125     lua_newtable(L);
126     lua_setfenv(L, -2);
127     return proc;
128 }
129
130 /* Mark a process (at index) as done */
131 static void doneproc(lua_State *L, int index)
132 {
133     struct proc *proc = toproc(L, index);
134     if (!proc){
135         fputs("subprocess.c: doneproc: not a proc\n", stderr);
136     } else {
137         proc->done = 1;
138         /* remove proc from SP_LIST */
139         lua_checkstack(L, 4);
140         lua_pushvalue(L, index);    /* stack: proc */
141         lua_rawgeti(L, LUA_ENVIRONINDEX, SP_LIST);
142         /* stack: proc list */
143         if (lua_isnil(L, -1)){
144             fputs("subprocess.c: XXX: SP_LIST IS NIL\n", stderr);
145         } else {
146             lua_pushinteger(L, proc->pid);      /* stack: proc list pid */
147             lua_pushvalue(L, -1);               /* stack: proc list pid pid */
148             lua_gettable(L, -3);                /* stack: proc list pid proc2 */
149             if (!lua_equal(L, -4, -1)){
150                 /* lookup by pid didn't work */
151                 fputs("subprocess.c: doneproc: XXX: pid lookup in SP_LIST failed\n", stderr);
152                 lua_pop(L, 2);                  /* stack: proc list */
153             } else {
154                 lua_pop(L, 1);                  /* stack: proc list pid */
155                 lua_pushnil(L);                 /* stack: proc list pid nil */
156                 lua_settable(L, -3);            /* stack: proc list */
157             }
158             /* stack: proc list */
159         }
160         lua_pop(L, 2);
161     }
162 }
163
164 /* Special constants for popen arguments. */
165 static char PIPE, STDOUT;
166
167 /* Names of standard file handles. */
168 static const char *fd_names[3] = {"stdin", "stdout", "stderr"};
169
170 /* Information about what to do for a standard file handle.
171    This is constructed from popen arguments. */
172 struct fdinfo {
173     enum {
174         FDMODE_INHERIT = 0,  /* fd is inherited from parent */
175         FDMODE_FILENAME,     /* open named file */
176         FDMODE_FILEDES,      /* use a file descriptor */
177         FDMODE_FILEOBJ,      /* use FILE* */
178         FDMODE_PIPE,         /* create and use pipe */
179         FDMODE_STDOUT        /* redirect to stdout (only for stderr) */
180     } mode;
181     union {
182         char *filename;
183         filedes_t filedes;
184         FILE *fileobj;
185     } info;
186 };
187
188 /* Close multiple file descriptors */
189 static void closefds(filedes_t *fds, int n)
190 {
191     int i;
192     for (i=0; i<n; ++i){
193 #if defined(OS_POSIX)
194         if (fds[i] != -1)
195             close(fds[i]);
196 #elif defined(OS_WINDOWS)
197         if (fds[i] != INVALID_HANDLE_VALUE)
198             CloseHandle(fds[i]);
199 #endif
200     }
201 }
202
203 /* Close multiple C files */
204 static void closefiles(FILE **files, int n)
205 {
206     int i;
207     for (i=0; i<n; ++i)
208         if (files[i] != NULL)
209             fclose(files[i]);
210 }
211
212 /* Free multiple strings */
213 static void freestrings(char **strs, int n)
214 {
215     int i;
216     for (i=0; i<n; ++i)
217         if (strs[i] != NULL)
218             free(strs[i]);
219 }
220
221 #ifdef OS_WINDOWS
222 /* Copy a Windows error into a buffer */
223 static void copy_w32error(char errmsg_out[], size_t errmsg_len, DWORD error)
224 {
225     if (FormatMessage(
226         FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0,
227         (void *) errmsg_out, errmsg_len, NULL) == 0)
228     {
229         strncpy(errmsg_out, "failed to get error message", errmsg_len + 1);
230     }
231 }
232
233 /* Push a Windows error onto a Lua stack */
234 static void push_w32error(lua_State *L, DWORD error)
235 {
236     LPTSTR buf;
237     if (FormatMessage(
238         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
239         NULL, error, 0, (void *) &buf, 1, NULL) == 0)
240     {
241         lua_pushliteral(L, "failed to get error message");
242     } else {
243         lua_pushstring(L, buf);
244         LocalFree(buf);
245     }
246 }
247
248 /* n is 0, 1 or 2
249    return handle for standard input/output/error */
250 static HANDLE getstdhandle(int n)
251 {
252     DWORD n2;
253     switch (n){
254         case 0: n2 = STD_INPUT_HANDLE; break;
255         case 1: n2 = STD_OUTPUT_HANDLE; break;
256         case 2: n2 = STD_ERROR_HANDLE; break;
257         default: return INVALID_HANDLE_VALUE;
258     }
259     return GetStdHandle(n2);
260 }
261
262 struct str {
263     char *data;
264     size_t len;
265     size_t size; /* size allocated */
266 };
267
268 static void str_init(struct str *s)
269 {
270     s->data = NULL;
271     s->len = 0;
272     s->size = 0;
273 }
274
275 /* Append n chars from s2 */
276 static int str_appendlstr(struct str *s, char *s2, size_t n)
277 {
278     void *newp;
279     if (s->size < s->len + n){
280         if (s->size < 16) s->size = 16;
281         while (s->size < s->len + n)
282             s->size = (s->size * 3) / 2;
283         newp = realloc(s->data, s->size + 1);
284         if (newp == NULL){
285             free(s->data);
286             return 0;
287         }
288         s->data = newp;
289     }
290     memcpy(s->data + s->len, s2, n);
291     s->len += n;
292     s->data[s->len] = '\0';
293     return 1;
294 }
295
296 static int str_appendc(struct str *s, char ch)
297 {
298     return str_appendlstr(s, &ch, 1);
299 }
300
301 /* Compiles command line for CreateProcess. Returns malloc'd string. */
302 static char *compile_cmdline(char *const *args)
303 {
304     /*  "      --> \"
305         \"     --> \\\"
306         \<NUL> --> \\    */
307     struct str str;
308     char *arg;
309     str_init(&str);
310     while (*args != NULL){
311         arg = *args++;
312         if (!str_appendc(&str, '"')) return NULL;
313         while (arg[0]){
314             if (arg[0] == '"'){
315                 if (!str_appendlstr(&str, "\\\"", 2)) return NULL;
316             } else if (arg[0] == '\\'){
317                 if (arg[1] == '"' || arg[1] == '\0'){
318                     if (!str_appendlstr(&str, "\\\\", 2)) return NULL;
319                 } else {
320                     if (!str_appendc(&str, '\\')) return NULL;
321                 }
322             } else {
323                 if (!str_appendc(&str, arg[0])) return NULL;
324             }
325             arg++;
326         }
327         if (!str_appendlstr(&str, "\" ", 2)) return NULL;
328     }
329     str.data[str.len - 1] = '\0';
330     return str.data;
331 }
332 #endif
333
334 /* Function for opening subprocesses. Returns 0 on success and -1 on failure.
335    On failure, errmsg_out shall contain a '\0'-terminated error message. */
336 static int dopopen(char *const *args,        /* program arguments with NULL sentinel */
337                    const char *executable,   /* actual executable */
338                    struct fdinfo fdinfo[3],  /* info for stdin/stdout/stderr */
339                    int close_fds,            /* 1 to close all fds */
340                    const char *cwd,          /* working directory for program */
341                    struct proc *proc,        /* populated on success! */
342                    FILE *pipe_ends_out[3],   /* pipe ends are put here */
343                    char errmsg_out[],        /* written to on failure */
344                    size_t errmsg_len         /* length of errmsg_out (EXCLUDING sentinel) */
345                   )
346 #if defined(OS_POSIX)
347 {
348     int fds[3];
349     int i;
350     struct fdinfo *fdi;
351     int piperw[2];
352     int errpipe[2]; /* pipe for returning error status */
353     int flags;
354     int en; /* saved errno */
355     int count;
356     pid_t pid;
357
358     errmsg_out[errmsg_len] = '\0';
359
360     for (i=0; i<3; ++i)
361         pipe_ends_out[i] = NULL;
362
363     /* Manage stdin/stdout/stderr */
364     for (i=0; i<3; ++i){
365         fdi = &fdinfo[i];
366         switch (fdi->mode){
367             case FDMODE_INHERIT:
368 inherit:
369                 fds[i] = dup(i);
370                 if (fds[i] == -1){
371 fd_failure:
372                     strncpy(errmsg_out, strerror(errno), errmsg_len + 1);
373                     closefds(fds, i);
374                     closefiles(pipe_ends_out, i);
375                     return -1;
376                 }
377                 break;
378             case FDMODE_FILENAME:
379                 if (i == STDIN_FILENO){
380                     if ((fds[i] = creat(fdi->info.filename, 0666)) == -1) goto fd_failure;
381                 } else {
382                     if ((fds[i] = open(fdi->info.filename, O_RDONLY)) == -1) goto fd_failure;
383                 }
384                 break;
385             case FDMODE_FILEDES:
386                 if ((fds[i] = dup(fdi->info.filedes)) == -1) goto fd_failure;
387                 break;
388             case FDMODE_FILEOBJ:
389                 if ((fds[i] = dup(fileno(fdi->info.fileobj))) == -1) goto fd_failure;
390                 break;
391             case FDMODE_PIPE:
392                 if (pipe(piperw) == -1) goto fd_failure;
393                 if (i == STDIN_FILENO){
394                     fds[i] = piperw[0]; /* give read end to process */
395                     if ((pipe_ends_out[i] = fdopen(piperw[1], "w")) == NULL) goto fd_failure;
396                 } else {
397                     fds[i] = piperw[1]; /* give write end to process */
398                     if ((pipe_ends_out[i] = fdopen(piperw[0], "r")) == NULL) goto fd_failure;
399                 }
400                 break;
401             case FDMODE_STDOUT:
402                 if (i == STDERR_FILENO){
403                     if ((fds[STDERR_FILENO] = dup(fds[STDOUT_FILENO])) == -1) goto fd_failure;
404                 } else goto inherit;
405                 break;
406         }
407     }
408     
409     /* Find executable name */
410     if (!executable){
411         /* use first arg */
412         executable = args[0];
413     }
414     assert(executable != NULL);
415
416     /* Create a pipe for returning error status */
417     if (pipe(errpipe) == -1){
418         strncpy(errmsg_out, strerror(errno), errmsg_len + 1);
419         closefds(fds, 3);
420         closefiles(pipe_ends_out, 3);
421         return -1;
422     }
423     /* Make write end close on exec */
424     flags = fcntl(errpipe[1], F_GETFD);
425     if (flags == -1){
426 pipe_failure:
427         strncpy(errmsg_out, strerror(errno), errmsg_len + 1);
428         closefds(errpipe, 2);
429         closefds(fds, 3);
430         closefiles(pipe_ends_out, 3);
431         return -1;
432     }
433     if (fcntl(errpipe[1], F_SETFD, flags | FD_CLOEXEC) == -1) goto pipe_failure;
434
435     /* Do the fork/exec (TODO: use vfork somehow?) */
436     pid = fork();
437     if (pid == -1) goto pipe_failure;
438     else if (pid == 0){
439         /* child */
440         close(errpipe[0]);
441         
442         /* dup file descriptors */
443         for (i=0; i<3; ++i){
444             if (dup2(fds[i], i) == -1) goto child_failure;
445         }
446
447         /* close other fds */
448         if (close_fds){
449             for (i=3; i<sysconf(_SC_OPEN_MAX); ++i){
450                 if (i != errpipe[1])
451                     close(i);
452             }
453         }
454
455         /* change directory */
456         if (cwd && chdir(cwd)) goto child_failure;
457
458         /* exec! Farewell, subprocess.c! */
459         execvp(executable, args);
460
461         /* Oh dear, we're still here. */
462 child_failure:
463         en = errno;
464         write(errpipe[1], &en, sizeof en);
465         _exit(1);
466     }
467
468     /* parent */
469     /* close unneeded fds */
470     closefds(fds, 3);
471     close(errpipe[1]);
472     
473     /* read errno from child */
474     while ((count = read(errpipe[0], &en, sizeof en)) == -1)
475         if (errno != EAGAIN && errno != EINTR) break;
476     if (count > 0){
477         /* exec failed */
478         close(errpipe[0]);
479         strncpy(errmsg_out, strerror(en), errmsg_len + 1);
480         return -1;
481     }
482     close(errpipe[0]);
483
484     /* Child is now running */
485     proc->done = 0;
486     proc->pid = pid;
487     return 0;
488 }
489 #elif defined(OS_WINDOWS)
490 {
491     HANDLE hfiles[3], piper, pipew, hfile;
492     int i, fd;
493     struct fdinfo *fdi;
494     SECURITY_ATTRIBUTES secattr;
495     STARTUPINFO si;
496     PROCESS_INFORMATION pi;
497     char *cmdline;
498
499     errmsg_out[errmsg_len] = '\0';
500
501     /* Create a SECURITY_ATTRIBUTES for inheritable handles */
502     secattr.nLength = sizeof secattr;
503     secattr.lpSecurityDescriptor = NULL;
504     secattr.bInheritHandle = TRUE;
505
506     for (i=0; i<3; ++i)
507         pipe_ends_out[i] = NULL;
508
509     /* Manage stdin/stdout/stderr */
510     for (i=0; i<3; ++i){
511         fdi = &fdinfo[i];
512         switch (fdi->mode){
513             case FDMODE_INHERIT:
514 inherit:
515                 /* XXX: duplicated file handles share the
516                    same object (and thus file cursor, etc.).
517                    CreateFile might be a better idea. */
518                 hfile = getstdhandle(i);
519                 if (hfile == INVALID_HANDLE_VALUE){
520 fd_failure:
521                     copy_w32error(errmsg_out, errmsg_len, GetLastError());
522                     closefds(hfiles, i);
523                     closefiles(pipe_ends_out, i);
524                     return -1;
525                 }
526 dup_hfile:
527                 if (DuplicateHandle(GetCurrentProcess(), hfile,
528                     GetCurrentProcess(), &hfiles[i], 0, TRUE,
529                     DUPLICATE_SAME_ACCESS) == 0)
530                 {
531                     goto fd_failure;
532                 }
533                 break;
534             case FDMODE_FILENAME:
535                 if (i == STDIN_FILENO){
536                     hfiles[i] = CreateFile(
537                         fdi->info.filename,
538                         GENERIC_READ,
539                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
540                         &secattr,
541                         OPEN_EXISTING,
542                         FILE_ATTRIBUTE_NORMAL,
543                         NULL);
544                 } else {
545                     hfiles[i] = CreateFile(
546                         fdi->info.filename,
547                         GENERIC_WRITE,
548                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
549                         &secattr,
550                         CREATE_ALWAYS,
551                         FILE_ATTRIBUTE_NORMAL,
552                         NULL);
553                 }
554                 if (hfiles[i] == INVALID_HANDLE_VALUE){
555                     goto fd_failure;
556                 }
557                 break;
558             case FDMODE_FILEDES:
559                 if (DuplicateHandle(GetCurrentProcess(), fdi->info.filedes,
560                     GetCurrentProcess(), &hfiles[i], 0, TRUE,
561                     DUPLICATE_SAME_ACCESS) == 0)
562                 {
563                     goto fd_failure;
564                 }
565                 break;
566             case FDMODE_FILEOBJ:
567                 fd = _fileno(fdi->info.fileobj);
568                 if (fd == -1){
569 get_osf_failure:
570                     strncpy(errmsg_out, strerror(errno), errmsg_len + 1);
571 failure:
572                     closefds(hfiles, i);
573                     closefiles(pipe_ends_out, i);
574                     return -1;
575                 }
576                 hfile = (HANDLE) _get_osfhandle(fd);
577                 if (hfile == INVALID_HANDLE_VALUE) goto get_osf_failure;
578                 goto dup_hfile;
579             case FDMODE_PIPE:
580                 if (CreatePipe(&piper, &pipew, &secattr, 0) == 0)
581                     goto fd_failure;
582                 if (i == STDIN_FILENO){
583                     hfiles[i] = piper;
584                     fd = _open_osfhandle((intptr_t) pipew, 0);
585                     if (fd == -1){
586                         strncpy(errmsg_out, "_open_osfhandle failed", errmsg_len + 1);
587                         goto failure;
588                     }
589                     pipe_ends_out[i] = _fdopen(fd, "w");
590                     if (pipe_ends_out[i] == 0){
591                         strncpy(errmsg_out, "_fdopen failed", errmsg_len + 1);
592                         goto failure;
593                     }
594                 } else {
595                     hfiles[i] = pipew;
596                     fd = _open_osfhandle((intptr_t) piper, _O_RDONLY);
597                     if (fd == -1){
598                         strncpy(errmsg_out, "_open_osfhandle failed", errmsg_len + 1);
599                         goto failure;
600                     }
601                     pipe_ends_out[i] = _fdopen(fd, "r");
602                     if (pipe_ends_out[i] == 0){
603                         strncpy(errmsg_out, "_fdopen failed", errmsg_len + 1);
604                         goto failure;
605                     }
606                 }
607                 break;
608             case FDMODE_STDOUT:
609                 if (i == STDERR_FILENO){
610                     hfile = hfiles[STDOUT_FILENO];
611                     goto dup_hfile;
612                 } else goto inherit;
613         }
614     }
615
616     /* Find executable name */
617     if (!executable){
618         /* use first arg */
619         executable = args[0];
620     }
621
622     /* Compile command line into string. Yuck. */
623     cmdline = compile_cmdline(args);
624     if (!cmdline){
625         strncpy(errmsg_out, "memory full", errmsg_len + 1);
626         closefds(hfiles, 3);
627         closefiles(pipe_ends_out, 3);
628         return -1;
629     }
630
631     si.cb = sizeof si;
632     si.lpReserved = NULL;
633     si.lpDesktop = NULL;
634     si.lpTitle = NULL;
635     si.dwFlags = STARTF_USESTDHANDLES;
636     si.cbReserved2 = 0;
637     si.lpReserved2 = NULL;
638     si.hStdInput = hfiles[0];
639     si.hStdOutput = hfiles[1];
640     si.hStdError = hfiles[2];
641
642     if (CreateProcess(
643         executable, /* lpApplicationName */
644         cmdline,    /* lpCommandLine */
645         NULL,       /* lpProcessAttributes */
646         NULL,       /* lpThreadAttributes */
647         FALSE,      /* bInheritHandles */
648         0,          /* dwCreationFlags */
649         NULL,       /* lpEnvironment */
650         cwd,        /* lpCurrentDirectory */
651         &si,        /* lpStartupInfo */
652         &pi)        /* lpProcessInformation */
653     == 0){
654         copy_w32error(errmsg_out, errmsg_len, GetLastError());
655         free(cmdline);
656         closefds(hfiles, 3);
657         closefiles(pipe_ends_out, 3);
658         return -1;
659     }
660     CloseHandle(pi.hThread); /* Don't want this handle */
661     free(cmdline);
662     closefds(hfiles, 3); /* XXX: is this correct? */
663     ci_out->done = 0;
664     ci_out->pid = pi.dwProcessId;
665     ci_out->hProcess = pi.hProcess;
666     return 0;
667 }
668 #endif
669
670 /* popen {arg0, arg1, arg2, ..., [executable=...]} */
671 static int superpopen(lua_State *L)
672 {
673     struct proc *proc = NULL;
674
675     /* List of arguments (malloc'd NULL-terminated array of malloc'd C strings) */
676     int nargs = 0;
677     char **args = NULL;
678     /* Command to run (malloc'd) */
679     char *executable = NULL;
680     /* Directory to run it in */
681     char *cwd = NULL;
682     /* File options */
683     struct fdinfo fdinfo[3];
684     /* Close fds? */
685     int close_fds = 0;
686
687     FILE *pipe_ends[3] = {NULL, NULL, NULL};
688     int i, j, result;
689     FILE *f;
690     const char *s;
691
692     char errmsg_buf[256];
693
694     luaL_checktype(L, 1, LUA_TTABLE);
695     lua_settop(L, 1);
696
697     proc = newproc(L);
698     
699     /* get arguments */
700     nargs = lua_objlen(L, 1);
701     if (nargs == 0) return luaL_error(L, "no arguments specified");
702     args = malloc((nargs + 1) * sizeof *args);
703     if (!args) return luaL_error(L, "memory full");
704     for (i=0; i<=nargs; ++i) args[i] = NULL;
705     for (i=1; i<=nargs; ++i){
706         lua_rawgeti(L, 1, i);
707         s = lua_tostring(L, -1);
708         if (!s){
709             freestrings(args, nargs);
710             free(args);
711             return luaL_error(L, "popen argument %d not a string", (int) i);
712
713         }
714         args[i-1] = strdup(s);
715         if (args[i-1] == NULL){
716 strings_failure:
717             freestrings(args, nargs);
718             free(args);
719             return luaL_error(L, "memory full");
720         }
721         lua_pop(L, 1);
722     }
723     
724     /* get executable string */
725     lua_getfield(L, 1, "executable");
726     s = lua_tostring(L, -1);
727     if (s){
728         executable = strdup(s);
729         if (executable == NULL) goto strings_failure;
730     }
731     lua_pop(L, 1); /* to match lua_getfield */
732
733     /* get directory name */
734     lua_getfield(L, 1, "cwd");
735     if (lua_isstring(L, -1)){
736         cwd = strdup(lua_tostring(L, -1));
737         if (!cwd){
738             free(executable);
739             freestrings(args, nargs);
740             free(args);
741             return luaL_error(L, "memory full");
742         }
743         /* make sure the cwd exists */
744         if (!direxists(cwd)){
745             free(executable);
746             freestrings(args, nargs);
747             free(args);
748             return luaL_error(L, "directory `%s' does not exist", cwd);
749         }
750     }
751     lua_pop(L, 1);
752
753     /* close_fds */
754     lua_getfield(L, 1, "close_fds");
755     close_fds = lua_toboolean(L, -1);
756     lua_pop(L, 1);
757
758     /* handle stdin/stdout/stderr */
759     for (i=0; i<3; ++i){
760         lua_getfield(L, 1, fd_names[i]);
761         if (lua_isnil(L, -1)){
762             fdinfo[i].mode = FDMODE_INHERIT;
763         } else if (lua_touserdata(L, -1) == &PIPE){
764             fdinfo[i].mode = FDMODE_PIPE;
765         } else if (lua_touserdata(L, -1) == &STDOUT){
766             if (i == STDERR_FILENO && fdinfo[STDOUT_FILENO].mode == FDMODE_PIPE){
767                 fdinfo[i].mode = FDMODE_STDOUT;
768             } else {
769                 lua_pushliteral(L, "STDOUT must be used only for stderr when stdout is set to PIPE");
770 files_failure:
771                 for (j=0; j<i; ++j){
772                     if (fdinfo[j].mode == FDMODE_FILENAME)
773                         free(fdinfo[j].info.filename);
774                 }
775                 free(executable);
776                 freestrings(args, nargs);
777                 free(args);
778                 return lua_error(L);
779             }
780         } else if (lua_isstring(L, -1)){
781             /* open a file */
782             fdinfo[i].mode = FDMODE_FILENAME;
783             if ((fdinfo[i].info.filename = strdup(lua_tostring(L, -1))) == NULL){
784                 lua_pushliteral(L, "out of memory");
785                 goto files_failure;
786             }
787         } else if (lua_isnumber(L, -1)){
788             /* use this fd */
789             fdinfo[i].mode = FDMODE_FILEDES;
790             fdinfo[i].info.filedes = (filedes_t) lua_tointeger(L, -1);
791         } else {
792             f = liolib_copy_tofile(L, -1);
793             if (f){
794                 fdinfo[i].mode = FDMODE_FILEOBJ;
795                 fdinfo[i].info.fileobj = f;
796             } else {
797                 /* huh? */
798                 lua_pushfstring(L, "unexpected value for %s", fd_names[i]);
799                 goto files_failure;
800             }
801         }
802         lua_pop(L, 1);
803     }
804
805     result = dopopen(args, executable, fdinfo, close_fds, cwd, proc, pipe_ends, errmsg_buf, 255);
806     for (i=0; i<3; ++i)
807         if (fdinfo[i].mode == FDMODE_FILENAME)
808             free(fdinfo[i].info.filename);
809     free(executable);
810     freestrings(args, nargs);
811     free(args);
812     if (result == -1){
813         /* failed */
814         return luaL_error(L, "popen failed: %s", errmsg_buf);
815     }
816
817     /* Put pipe object's in proc userdata's environment */
818     lua_getfenv(L, 2);
819     for (i=0; i<3; ++i){
820         if (pipe_ends[i]){
821             *liolib_copy_newfile(L) = pipe_ends[i];
822             lua_setfield(L, -2, fd_names[i]);
823         }
824     }
825     lua_pop(L, 1);
826
827     /* Put proc object in SP_LIST table */
828     lua_rawgeti(L, LUA_ENVIRONINDEX, SP_LIST);
829     if (lua_isnil(L, -1)){
830         fputs("subprocess.c: XXX: SP_LIST IS NIL\n", stderr);
831     } else {
832         lua_pushinteger(L, proc->pid); /* stack: proc list pid */
833         lua_pushvalue(L, -3);          /* stack: proc list pid proc */
834         lua_settable(L, -3);           /* stack: proc list */
835     }
836     lua_pop(L, 1);
837         
838     /* Return the proc */
839     return 1;
840 }
841
842 /* __gc */
843 static int proc_gc(lua_State *L)
844 {
845     struct proc *proc = checkproc(L, 1);
846     if (!proc->done){
847 #if defined(OS_POSIX)
848         /* Try to wait for process to avoid leaving zombie.
849            If the process hasn't finished yet, we'll end up leaving a zombie. */
850         int stat;
851         waitpid(proc->pid, &stat, WNOHANG);
852 #elif defined(OS_WINDOWS)
853         CloseHandle(proc->hProcess);
854 #endif
855         doneproc(L, 1);
856     }
857     return 0;
858 }
859
860 /* __index */
861 static int proc_index(lua_State *L)
862 {
863     struct proc *proc;
864     const char *s;
865     lua_settop(L, 2);
866     proc = checkproc(L, 1);
867     /* first check environment table */
868     lua_getfenv(L, 1);
869     lua_pushvalue(L, 2);
870     lua_gettable(L, 3);
871     if (!lua_isnil(L, 4)) return 1;
872     lua_pop(L, 2);
873     /* next check metatable */
874     lua_getmetatable(L, 1);
875     lua_pushvalue(L, 2);
876     lua_gettable(L, 3);
877     if (!lua_isnil(L, 4)) return 1;
878     lua_pop(L, 2);
879     /* lastly, fixed fields */
880     s = lua_tostring(L, 2);
881     if (!strcmp(s, "pid")){
882         lua_pushinteger(L, proc->pid);
883         return 1;
884     } else if (!strcmp(s, "exitcode") && proc->done){
885         lua_pushinteger(L, proc->exitcode);
886         return 1;
887     } else {
888         return 0;
889     }
890 }
891
892 /* Push string representation of process on stack */
893 static int proc_tostring(lua_State *L)
894 {
895     struct proc *proc = checkproc(L, 1);
896     if (proc->done)
897         lua_pushliteral(L, "(finished process)");
898     else
899         lua_pushfstring(L, "process (%d)", (int) proc->pid);
900     return 1;
901 }
902
903 #if defined(OS_POSIX)
904 /* Get exitcode from wait's 'stat' value */
905 static int getexitcode(int stat)
906 {
907     if (WIFEXITED(stat))
908         return WEXITSTATUS(stat);
909     else if (WIFSIGNALED(stat))
910         return -WTERMSIG(stat);
911     else if (WIFSTOPPED(stat))
912         return -WSTOPSIG(stat);
913     else {
914         fputs("child disappeared into black hole\n", stderr);
915         return -1;
916     }
917 }
918 #endif
919
920 /* Wait for, or poll, a process */
921 static int do_waitpid(lua_State *L, struct proc *proc, int wait)
922 #if defined(OS_POSIX)
923 {
924     int stat, options;
925
926     if (proc->done){
927         lua_pushinteger(L, proc->exitcode);
928         return 1;
929     }
930
931     if (wait) options = 0;
932     else options = WNOHANG;
933     switch (waitpid(proc->pid, &stat, options)){
934         case -1:
935             return luaL_error(L, strerror(errno));
936         case 0:
937             /* child still running */
938             lua_pushnil(L);
939             return 1;
940         default:
941             proc->exitcode = getexitcode(stat);
942             doneproc(L, 1);
943             lua_pushinteger(L, proc->exitcode);
944             return 1;
945     }
946 }
947 #elif defined(OS_WINDOWS)
948 {
949     DWORD dwMilliseconds, retval, exitcode;
950
951     if (proc->done){
952         lua_pushinteger(L, proc->exitcode);
953         return 1;
954     }
955     if (wait) dwMilliseconds = INFINITE;
956     else dwMilliseconds = 0;
957     retval = WaitForSingleObject(proc->hProcess, dwMilliseconds);
958     switch (retval){
959         case WAIT_FAILED:
960 failure:
961             push_w32error(L, GetLastError());
962             return lua_error(L);
963         case WAIT_OBJECT_0:
964             /* child finished */
965             if (GetExitCodeProcess(proc->hProcess, &exitcode) == 0){
966                 goto failure;
967             }
968             CloseHandle(proc->hProcess);
969             proc->exitcode = exitcode;
970             doneproc(L, 1);
971             lua_pushinteger(L, proc->exitcode);
972             return 1;
973         case WAIT_TIMEOUT:
974         default:
975             /* child still running */
976             lua_pushnil(L);
977             return 1;
978     }
979 }
980 #endif
981
982 static int proc_poll(lua_State *L)
983 {
984     return do_waitpid(L, checkproc(L, 1), 0);
985 }
986
987 static int proc_wait(lua_State *L)
988 {
989     return do_waitpid(L, checkproc(L, 1), 1);
990 }
991
992 #if defined(OS_POSIX)
993 static int proc_send_signal(lua_State *L)
994 {
995     struct proc *proc = checkproc(L, 1);
996     int sig = luaL_checkint(L, 2);
997     if (!proc->done){
998         if (kill(proc->pid, sig)){
999             return luaL_error(L, "kill: %s", strerror(errno));
1000         }
1001         proc->exitcode = -sig;
1002         doneproc(L, 1);
1003     }
1004     return 0;
1005 }
1006
1007 static int proc_terminate(lua_State *L)
1008 {
1009     lua_settop(L, 1);
1010     lua_pushinteger(L, SIGTERM);
1011     return proc_send_signal(L);
1012 }
1013
1014 static int proc_kill(lua_State *L)
1015 {
1016     lua_settop(L, 1);
1017     lua_pushinteger(L, SIGKILL);
1018     return proc_send_signal(L);
1019 }
1020 #elif defined(OS_WINDOWS)
1021 static int sp_terminate(lua_State *L)
1022 {
1023     struct proc *proc = checkproc(L, 1);
1024     if (!proc->done){
1025         if (TerminateProcess(proc->hProcess, -9) == 0){
1026             push_w32error(L, GetLastError());
1027             return lua_error(L);
1028         }
1029         CloseHandle(proc->hProcess);
1030         proc->exitcode = -9;
1031         doneproc(L, 1);
1032     }
1033     return 0;
1034 }
1035 #endif
1036
1037 static const luaL_Reg proc_meta[] = {
1038     {"__tostring", proc_tostring},
1039     {"__gc", proc_gc},
1040     {"__index", proc_index},
1041     {"poll", proc_poll},
1042     {"wait", proc_wait},
1043 #if defined(OS_POSIX)
1044     {"send_signal", proc_send_signal},
1045     {"terminate", proc_terminate},
1046     {"kill", proc_kill},
1047 #elif defined(OS_WINDOWS)
1048     {"terminate", proc_terminate},
1049     {"kill", proc_terminate},
1050 #endif
1051     {NULL, NULL}
1052 };
1053
1054 /* convenience functions */
1055 static int call(lua_State *L)
1056 {
1057     int r = superpopen(L);
1058     if (r != 1){
1059         return r;
1060     }
1061     return proc_wait(L);
1062 }
1063
1064 static int call_capture(lua_State *L)
1065 {
1066     int r;
1067     lua_settop(L, 1);
1068     luaL_checktype(L, 1, LUA_TTABLE);
1069     lua_getfield(L, 1, "stdout");
1070     lua_pushlightuserdata(L, &PIPE);
1071     lua_setfield(L, 1, "stdout");
1072     r = superpopen(L);
1073     if (r != 1) return r;
1074     /* stack: args oldstdout sp */
1075     /* restore old stdout value in table */
1076     lua_pushvalue(L, 2);
1077     lua_setfield(L, 1, "stdout");
1078     lua_replace(L, 1);
1079     lua_settop(L, 1);
1080     /* stack: sp */
1081     lua_getfield(L, 1, "stdout");
1082     lua_getfield(L, 2, "read");
1083     lua_pushvalue(L, 2);
1084     lua_pushliteral(L, "*a");
1085     lua_call(L, 2, 2);
1086     /* stack: sp stdout a b */
1087     /* close stdout, rather than relying on GC */
1088     lua_getfield(L, 2, "close");
1089     lua_pushvalue(L, 2);
1090     lua_call(L, 1, 0);
1091     /* wait for child (to avoid leaving a zombie) */
1092     lua_getfield(L, 1, "wait");
1093     lua_pushvalue(L, 1);
1094     lua_call(L, 1, 1);
1095     /* return exitcode, content */
1096     lua_pushvalue(L, 3);
1097     return 2;
1098 }
1099
1100 /* Miscellaneous */
1101
1102 static int superwait(lua_State *L)
1103 {
1104     int stat;
1105     int exitcode;
1106     struct proc *proc;
1107 #if defined(OS_POSIX)
1108     pid_t pid;
1109 #elif defined(OS_WINDOWS)
1110     HANDLE *handles, hProcess;
1111     int i, nprocs;
1112     DWORD retval;
1113     DWORD exitcode;
1114 #endif
1115
1116     lua_rawgeti(L, LUA_ENVIRONINDEX, SP_LIST);
1117     if (lua_isnil(L, -1))
1118         return luaL_error(L, "SP_LIST is nil");
1119 #if defined(OS_POSIX)
1120     pid = wait(&stat);
1121     if (pid == -1){
1122         lua_pushnil(L);
1123         lua_pushstring(L, strerror(errno));
1124         return 2;
1125     }
1126     exitcode = getexitcode(stat);
1127     /* find proc object corresponding to pid */
1128     lua_pushinteger(L, pid);
1129     lua_pushvalue(L, -1);    /* stack: list pid pid */
1130     lua_gettable(L, -3);     /* stack: list pid proc */
1131     if (lua_isnil(L, -1)){
1132         fprintf(stderr, "subprocess.c: XXX: cannot find proc object for pid %d\n", (int) pid);
1133     }
1134     lua_replace(L, -3);     /* stack: proc pid */
1135     lua_pop(L, 1);          /* stack: proc */
1136     /* update proc object */
1137     proc = toproc(L, -1);
1138     if (!proc){
1139         fputs("subprocess.c: XXX: proc list entry is wrong type\n", stderr);
1140     } else {
1141         proc->exitcode = exitcode;
1142         doneproc(L, -1);
1143     }
1144     lua_pushinteger(L, exitcode);
1145     lua_pushinteger(L, pid);
1146     /* stack: proc exitcode pid */
1147     return 3;
1148 #elif defined(OS_WINDOWS)
1149     /* count number of procs there are */
1150     nprocs = countkeys(L);
1151     /* stack: list */
1152     if (nprocs > 0){
1153         handles = malloc(nprocs * sizeof *handles);
1154         if (!handles)
1155             return luaL_error(L, "memory full");
1156         i = 0;
1157         lua_pushnil(L);
1158         while (lua_next(L, -2)){
1159             proc = toproc(L, -1);
1160             if (proc && !proc->done && i < nprocs){
1161                 handles[i++] = proc->hProcess;
1162             } else if (proc && !proc->done){
1163                 fputs("subprocess.c: XXX: handles array allocated too small\n", stderr);
1164             } else if (!proc){
1165                 fputs("foreign object in SP_LIST\n", stderr);
1166             }
1167             lua_pop(L, 1);
1168         }
1169     } else i = 0;
1170     if (i > 0){
1171         if (i > MAXIMUM_WAIT_OBJECTS){
1172             free(handles);
1173             return luaL_error("too many wait objects: %d", i);
1174         }
1175         retval = WaitForMultipleObjects(i, handles, FALSE, INFINITE);
1176         if (retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + i){
1177             hProcess = handles[retval - WAIT_OBJECT_0];
1178             free(handles);
1179             /* find this process again in the table */
1180             lua_pushnil(L);
1181             while (lua_next(L, -2)){
1182                 proc = toproc(L, -1);
1183                 if (proc && !proc->done && proc->hProcess == hProcess){
1184                     /* it's this one */
1185                     if (GetExitCodeProcess(proc->hProcess, &exitcode) == 0){
1186                         {
1187                             char buf[256];
1188                             copy_w32error(buf, 255, GetLastError());
1189                             fprintf(stderr, "GetExitCodeProcess failed: %s\n", buf);
1190                         }
1191                         proc->exitcode = -1; /*  :-\  */
1192                     } else {
1193                         proc->exitcode = exitcode;
1194                     }
1195                     CloseHandle(proc->hProcess);
1196                     doneproc(L, -1);
1197                     lua_pushinteger(L, exitcode);
1198                     lua_pushinteger(L, proc->pid); /* stack: list key proc exitcode pid */
1199                     return 3;
1200                 }
1201                 lua_pop(L, 1);
1202             }
1203             fputs("proc has mysteriously disappeared from table!\n", stderr);
1204             return 0;
1205         } else if (retval == WAIT_FAILED){
1206             free(handles);
1207             push_w32error(L, GetLastError());
1208             return lua_error(L);
1209         } else {
1210             free(handles);
1211             return luaL_error("WaitForMultipleObjects failed unexpectedly");
1212         }
1213     } else {
1214         free(handles);
1215         lua_pushnil(L);
1216         lua_pushliteral(L, "no processes to wait for");
1217         return 2;
1218     }
1219 #endif
1220 }
1221
1222 static const luaL_Reg subprocess[] = {
1223     /* {"pipe", superpipe}, */
1224     {"popen", superpopen},
1225     {"call", call},
1226     {"call_capture", call_capture},
1227     {"wait", superwait},
1228     {NULL, NULL}
1229 };
1230
1231 LUALIB_API int luaopen_subprocess(lua_State *L)
1232 {
1233     /* create environment table for C functions */
1234     lua_createtable(L, 1, 0);
1235     lua_newtable(L);   /* table for all proc objects */
1236     lua_rawseti(L, -2, SP_LIST);
1237     lua_replace(L, LUA_ENVIRONINDEX);
1238
1239     luaL_register(L, "subprocess", subprocess);
1240
1241     /* export PIPE and STDOUT constants */
1242     lua_pushlightuserdata(L, &PIPE);
1243     lua_setfield(L, -2, "PIPE");
1244     lua_pushlightuserdata(L, &STDOUT);
1245     lua_setfield(L, -2, "STDOUT");
1246
1247     /* create metatable for proc objects */
1248     luaL_newmetatable(L, SP_PROC_META);
1249     luaL_register(L, NULL, proc_meta);
1250     lua_pushboolean(L, 0);
1251     lua_setfield(L, -2, "__metatable");
1252     lua_pop(L, 1);
1253
1254     return 1;
1255 }
1256
1257 #endif