1 /*  $Id$
2 
3     Part of SWI-Prolog
4 
5     Author:        Jan Wielemaker
6     E-mail:        J.Wielemaker@uva.nl
7     WWW:           http://www.swi-prolog.org
8     Copyright (C): 2008-2009, University of Amsterdam
9 
10     This library is free software; you can redistribute it and/or
11     modify it under the terms of the GNU Lesser General Public
12     License as published by the Free Software Foundation; either
13     version 2.1 of the License, or (at your option) any later version.
14 
15     This library is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18     Lesser General Public License for more details.
19 
20     You should have received a copy of the GNU Lesser General Public
21     License along with this library; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 /*#define O_DEBUG 1*/
30 #include <SWI-Stream.h>
31 #include <SWI-Prolog.h>
32 #include "error.h"
33 #include <stdio.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <errno.h>
37 #ifdef HAVE_SYS_TYPES_H
38 #include <sys/types.h>
39 #endif
40 #ifdef HAVE_SYS_WAIT_H
41 #include <sys/wait.h>
42 #endif
43 #ifdef HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #ifdef HAVE_FCNTL_H
47 #include <fcntl.h>
48 #endif
49 
50 static atom_t ATOM_stdin;
51 static atom_t ATOM_stdout;
52 static atom_t ATOM_stderr;
53 static atom_t ATOM_std;
54 static atom_t ATOM_null;
55 static atom_t ATOM_process;
56 static atom_t ATOM_detached;
57 static atom_t ATOM_cwd;
58 static atom_t ATOM_env;
59 static atom_t ATOM_window;
60 static atom_t ATOM_timeout;
61 static atom_t ATOM_release;
62 static atom_t ATOM_infinite;
63 static functor_t FUNCTOR_error2;
64 static functor_t FUNCTOR_type_error2;
65 static functor_t FUNCTOR_domain_error2;
66 static functor_t FUNCTOR_resource_error1;
67 static functor_t FUNCTOR_process_error2;
68 static functor_t FUNCTOR_system_error2;
69 static functor_t FUNCTOR_pipe1;
70 static functor_t FUNCTOR_exit1;
71 static functor_t FUNCTOR_killed1;
72 static functor_t FUNCTOR_eq2;		/* =/2 */
73 
74 #define MAYBE 2
75 
76 #if O_DEBUG
77 #define DEBUG(g) g
78 #else
79 #define DEBUG(g) (void)0
80 #endif
81 
82 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
83 ISSUES:
84 	- Deal with child errors (no cwd, cannot execute, etc.)
85 	- Windows version
86 	- Complete test suite
87 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
88 
89 
90 		 /*******************************
91 		 *	      ERRORS		*
92 		 *******************************/
93 
94 static int
type_error(term_t actual,const char * expected)95 type_error(term_t actual, const char *expected)
96 { term_t ex;
97 
98   if ( (ex=PL_new_term_ref()) &&
99        PL_unify_term(ex,
100 		     PL_FUNCTOR, FUNCTOR_error2,
101 		       PL_FUNCTOR, FUNCTOR_type_error2,
102 		         PL_CHARS, expected,
103 		         PL_TERM, actual,
104 		       PL_VARIABLE) )
105     return PL_raise_exception(ex);
106 
107   return FALSE;
108 }
109 
110 
111 static int
domain_error(term_t actual,const char * expected)112 domain_error(term_t actual, const char *expected)
113 { term_t ex;
114 
115   if ( (ex=PL_new_term_ref()) &&
116        PL_unify_term(ex,
117 		     PL_FUNCTOR, FUNCTOR_error2,
118 		       PL_FUNCTOR, FUNCTOR_domain_error2,
119 		         PL_CHARS, expected,
120 		         PL_TERM, actual,
121 		       PL_VARIABLE) )
122     return PL_raise_exception(ex);
123 
124   return FALSE;
125 }
126 
127 
128 static int
resource_error(const char * resource)129 resource_error(const char *resource)
130 { term_t ex;
131 
132   if ( (ex=PL_new_term_ref()) &&
133        PL_unify_term(ex,
134 		     PL_FUNCTOR, FUNCTOR_error2,
135 		       PL_FUNCTOR, FUNCTOR_resource_error1,
136 		         PL_CHARS, resource,
137 		       PL_VARIABLE) )
138     return PL_raise_exception(ex);
139 
140   return FALSE;
141 }
142 
143 
144 		 /*******************************
145 		 *	       ADMIN		*
146 		 *******************************/
147 
148 #ifdef __WINDOWS__
149 #include <windows.h>
150 #include <stdio.h>
151 #include <fcntl.h>
152 #include <io.h>
153 #ifndef __MINGW32__
154 typedef DWORD  pid_t;
155 #endif
156 typedef wchar_t echar;			/* environment character */
157 #else
158 typedef char echar;
159 #endif
160 
161 typedef enum std_type
162 { std_std,
163   std_null,
164   std_pipe
165 } std_type;
166 
167 
168 typedef struct p_stream
169 { term_t   term;			/* P in pipe(P) */
170   std_type type;			/* type of stream */
171 #ifdef __WINDOWS__
172   HANDLE   fd[2];			/* pipe handles */
173 #else
174   int      fd[2];			/* pipe handles */
175 #endif
176 } p_stream;
177 
178 
179 typedef struct ecbuf
180 { echar *buffer;
181   size_t size;
182   size_t allocated;
183 } ecbuf;
184 
185 
186 typedef struct p_options
187 { atom_t exe_name;			/* exe as atom */
188 #ifdef __WINDOWS__
189   wchar_t *exe;				/* Executable */
190   wchar_t *cmdline;			/* Command line */
191   wchar_t *cwd;				/* CWD of new process */
192 #else
193   char *exe;				/* Executable */
194   char **argv;				/* argument vector */
195   char *cwd;				/* CWD of new process */
196   char **envp;				/* New environment */
197 #endif
198   ecbuf  envbuf;			/* environment buffer */
199   term_t pid;				/* process(PID) */
200   int pipes;				/* #pipes found */
201   p_stream streams[3];
202   int   detached;			/* create as detached */
203   int   window;				/* Show a window? */
204 } p_options;
205 
206 
207 typedef struct wait_options
208 { double timeout;
209   int	 has_timeout;
210   int	 release;
211 } wait_options;
212 
213 
214 #ifdef __WINDOWS__
215 static int win_command_line(term_t t, int arity,
216 			    const wchar_t *exepath, wchar_t **cmdline);
217 #endif
218 
219 		 /*******************************
220 		 *	  STRING BUFFER		*
221 		 *******************************/
222 
223 static void
free_ecbuf(ecbuf * b)224 free_ecbuf(ecbuf *b)
225 { if ( b->buffer )
226   { PL_free(b->buffer);
227     b->buffer = NULL;
228   }
229 }
230 
231 
232 static int
add_ecbuf(ecbuf * b,echar * data,size_t len)233 add_ecbuf(ecbuf *b, echar *data, size_t len)
234 { if ( b->size + len > b->allocated )
235   { size_t newsize = (b->allocated ? b->allocated * 2 : 2048);
236 
237     while( b->size + len > newsize )
238       newsize *= 2;
239 
240     if ( b->buffer )
241     { b->buffer = PL_realloc(b->buffer, newsize*sizeof(echar));
242     } else
243     { b->buffer = PL_malloc(newsize*sizeof(echar));
244     }
245 
246     b->allocated = newsize;
247   }
248 
249   memcpy(b->buffer+b->size, data, len*sizeof(echar));
250   b->size += len;
251 
252   return TRUE;
253 }
254 
255 		 /*******************************
256 		 *	ENVIRONMENT PARSING	*
257 		 *******************************/
258 
259 static int
get_echars_arg_ex(int i,term_t from,term_t arg,echar ** sp,size_t * lenp)260 get_echars_arg_ex(int i, term_t from, term_t arg, echar **sp, size_t *lenp)
261 { const echar *s, *e;
262 
263   if ( !PL_get_arg(i, from, arg) )
264     return FALSE;
265 
266 #ifdef __WINDOWS__
267   if ( !PL_get_wchars(arg, lenp, sp,
268 		      CVT_ATOMIC|CVT_EXCEPTION) )
269 #else
270   if ( !PL_get_nchars(arg, lenp, sp,
271 		      CVT_ATOMIC|CVT_EXCEPTION|REP_FN) )
272 #endif
273     return FALSE;
274 
275   for(s = *sp, e = s+*lenp; s<e; s++)
276   { if ( !*s )
277       return domain_error(arg, "text_non_zero_code");
278   }
279 
280   return TRUE;
281 }
282 
283 #ifdef __WINDOWS__
284 #define ECHARS(s) L##s
285 #else
286 #define ECHARS(s) s
287 #endif
288 
289 static int
parse_environment(term_t t,p_options * info)290 parse_environment(term_t t, p_options *info)
291 { term_t tail = PL_copy_term_ref(t);
292   term_t head = PL_new_term_ref();
293   term_t tmp  = PL_new_term_ref();
294   ecbuf *eb   = &info->envbuf;
295   int count = 0, c = 0;
296 #ifndef __WINDOWS__
297   echar *q;
298   char **ep;
299 #endif
300 
301   assert(eb->size == 0);
302   assert(eb->allocated == 0);
303   assert(eb->buffer == NULL);
304 
305   while( PL_get_list(tail, head, tail) )
306   { echar *s;
307     size_t len;
308 
309     if ( !PL_is_functor(head, FUNCTOR_eq2) )
310       return type_error(head, "environment_variable");
311 
312     if ( !get_echars_arg_ex(1, head, tmp, &s, &len) )
313       return FALSE;
314     add_ecbuf(eb, s, len);
315     add_ecbuf(eb, ECHARS("="), 1);
316     if ( !get_echars_arg_ex(2, head, tmp, &s, &len) )
317       return FALSE;
318     add_ecbuf(eb, s, len);
319     add_ecbuf(eb, ECHARS("\0"), 1);
320 
321     count++;
322   }
323 
324   if ( !PL_get_nil(tail) )
325     return type_error(tail, "list");
326 
327 #ifdef __WINDOWS__
328   add_ecbuf(eb, ECHARS("\0"), 1);
329 #else
330   info->envp = PL_malloc((count+1)*sizeof(char*));
331 
332   for(ep=info->envp, c=0, q=eb->buffer; c<count; c++, ep++)
333   { *ep = q;
334     q += strlen(q)+1;
335   }
336   assert((size_t)(q-eb->buffer) == eb->size);
337   *ep = NULL;
338 #endif
339 
340   return TRUE;
341 }
342 
343 
344 static int
get_stream(term_t t,p_options * info,p_stream * stream)345 get_stream(term_t t, p_options *info, p_stream *stream)
346 { atom_t a;
347 
348   if ( PL_get_atom(t, &a) )
349   { if ( a == ATOM_null )
350     { stream->type = std_null;
351       return TRUE;
352     } else if ( a == ATOM_std )
353     { stream->type = std_std;
354       return TRUE;
355     } else
356     { return domain_error(t, "process_stream");
357     }
358   } else if ( PL_is_functor(t, FUNCTOR_pipe1) )
359   { stream->term = PL_new_term_ref();
360     _PL_get_arg(1, t, stream->term);
361     stream->type = std_pipe;
362     info->pipes++;
363     return TRUE;
364   } else
365     return type_error(t, "process_stream");
366 }
367 
368 
369 static int
parse_options(term_t options,p_options * info)370 parse_options(term_t options, p_options *info)
371 { term_t tail = PL_copy_term_ref(options);
372   term_t head = PL_new_term_ref();
373   term_t arg = PL_new_term_ref();
374 
375   info->window = MAYBE;
376 
377   while(PL_get_list(tail, head, tail))
378   { atom_t name;
379     int arity;
380 
381     if ( !PL_get_name_arity(head, &name, &arity) || arity != 1 )
382       return type_error(head, "option");
383     _PL_get_arg(1, head, arg);
384 
385     if ( name == ATOM_stdin )
386     { if ( !get_stream(arg, info, &info->streams[0]) )
387 	return FALSE;
388     } else if ( name == ATOM_stdout )
389     { if ( !get_stream(arg, info, &info->streams[1]) )
390 	return FALSE;
391     } else if ( name == ATOM_stderr )
392     { if ( !get_stream(arg, info, &info->streams[2]) )
393 	return FALSE;
394     } else if ( name == ATOM_process )
395     { info->pid = PL_copy_term_ref(arg);
396     } else if ( name == ATOM_detached )
397     { if ( !PL_get_bool(arg, &info->detached) )
398 	return type_error(arg, "boolean");
399     } else if ( name == ATOM_cwd )
400     {
401 #ifdef __WINDOWS__
402       if ( !PL_get_wchars(arg, NULL, &info->cwd,
403 			 CVT_ATOM|CVT_STRING|CVT_EXCEPTION|BUF_MALLOC) )
404 	return FALSE;
405 #else
406       if ( !PL_get_chars(arg, &info->cwd,
407 			 CVT_ATOM|CVT_STRING|CVT_EXCEPTION|BUF_MALLOC|REP_FN) )
408 	return FALSE;
409 #endif
410     } else if ( name == ATOM_window )
411     { if ( !PL_get_bool(arg, &info->window) )
412 	return type_error(arg, "boolean");
413     } else if ( name == ATOM_env )
414     { if ( !parse_environment(arg, info) )
415 	return FALSE;
416     } else
417       return domain_error(head, "process_option");
418   }
419 
420   if ( !PL_get_nil(tail) )
421     return type_error(tail, "list");
422 
423   return TRUE;
424 }
425 
426 
427 static int
get_exe(term_t exe,p_options * info)428 get_exe(term_t exe, p_options *info)
429 { int arity;
430   term_t arg = PL_new_term_ref();
431 
432   if ( !PL_get_name_arity(exe, &info->exe_name, &arity) )
433     return type_error(exe, "callable");
434 
435   PL_put_atom(arg, info->exe_name);
436 
437 #ifdef __WINDOWS__
438   if ( !PL_get_wchars(arg, NULL, &info->exe, CVT_ATOM|CVT_EXCEPTION|BUF_MALLOC) )
439     return FALSE;
440   if ( !win_command_line(exe, arity, info->exe, &info->cmdline) )
441     return FALSE;
442 #else /*__WINDOWS__*/
443   if ( !PL_get_chars(arg, &info->exe, CVT_ATOM|CVT_EXCEPTION|BUF_MALLOC|REP_FN) )
444     return FALSE;
445 
446   info->argv = PL_malloc((arity+2)*sizeof(char*));
447   memset(info->argv, 0, (arity+2)*sizeof(char*));
448   info->argv[0] = strdup(info->exe);
449   { int i;
450 
451     for(i=1; i<=arity; i++)
452     { _PL_get_arg(i, exe, arg);
453 
454       if ( !PL_get_chars(arg, &info->argv[i],
455 			 CVT_ATOMIC|CVT_EXCEPTION|BUF_MALLOC|REP_FN) )
456 	return FALSE;
457     }
458     info->argv[i] = NULL;
459   }
460 #endif /*__WINDOWS__*/
461 
462   return TRUE;
463 }
464 
465 
466 static void
free_options(p_options * info)467 free_options(p_options *info)		/* TBD: close streams */
468 { if ( info->exe )
469   { PL_free(info->exe);
470     info->exe = NULL;
471   }
472   if ( info->cwd )
473   { PL_free(info->cwd);
474     info->cwd = NULL;
475   }
476 #ifndef __WINDOWS__
477   if ( info->envp )
478   { PL_free(info->envp);
479     info->envp = NULL;
480   }
481 #endif
482   free_ecbuf(&info->envbuf);
483 #ifdef __WINDOWS__
484   if ( info->cmdline )
485   { PL_free(info->cmdline);
486     info->cmdline = NULL;
487   }
488 
489 #else /*__WINDOWS__*/
490 
491   if ( info->argv )
492   { char **a;
493     for(a=info->argv; *a; a++)
494     { if ( *a )
495 	PL_free(*a);
496     }
497     PL_free(info->argv);
498 
499     info->argv = NULL;
500   }
501 
502 #endif /*__WINDOWS__*/
503 }
504 
505 
506 		 /*******************************
507 		 *	   PROCESS READS	*
508 		 *******************************/
509 
510 #define	PROCESS_MAGIC	0x29498001
511 
512 typedef struct process_context
513 { int	magic;				/* PROCESS_MAGIC */
514 #ifdef __WINDOWS__
515   HANDLE handle;			/* process handle */
516 #else
517   pid_t	pid;				/* the process id */
518 #endif
519   int   open_mask;			/* Open streams */
520   int   pipes[3];			/* stdin/stdout/stderr */
521 } process_context;
522 
523 static int wait_for_process(process_context *pc);
524 
525 static int
process_fd(void * handle,process_context ** PC)526 process_fd(void *handle, process_context **PC)
527 { process_context *pc = (process_context*) ((uintptr_t)handle&~(uintptr_t)0x3);
528   int pipe = (int)(uintptr_t)handle & 0x3;
529 
530   if ( pc->magic == PROCESS_MAGIC )
531   { if ( PC )
532       *PC = pc;
533     return pc->pipes[pipe];
534   }
535 
536   return -1;
537 }
538 
539 
540 static ssize_t
Sread_process(void * handle,char * buf,size_t size)541 Sread_process(void *handle, char *buf, size_t size)
542 { int fd = process_fd(handle, NULL);
543 
544   return (*Sfilefunctions.read)((void*)(uintptr_t)fd, buf, size);
545 }
546 
547 
548 static ssize_t
Swrite_process(void * handle,char * buf,size_t size)549 Swrite_process(void *handle, char *buf, size_t size)
550 { int fd = process_fd(handle, NULL);
551 
552   return (*Sfilefunctions.write)((void*)(uintptr_t)fd, buf, size);
553 }
554 
555 
556 static int
Sclose_process(void * handle)557 Sclose_process(void *handle)
558 { process_context *pc;
559   int fd = process_fd(handle, &pc);
560 
561   if ( fd >= 0 )
562   { int which = (int)(uintptr_t)handle & 0x3;
563     int rc;
564 
565     rc = (*Sfilefunctions.close)((void*)(uintptr_t)fd);
566     pc->open_mask &= ~(1<<which);
567 
568     DEBUG(Sdprintf("Closing fd[%d]; mask = 0x%x\n", which, pc->open_mask));
569 
570     if ( !pc->open_mask )
571     { int rcw = wait_for_process(pc);
572 
573       return rcw ? 0 : -1;
574     }
575 
576     return rc;
577   }
578 
579   return -1;
580 }
581 
582 
583 static int
Scontrol_process(void * handle,int action,void * arg)584 Scontrol_process(void *handle, int action, void *arg)
585 { process_context *pc;
586   int fd = process_fd(handle, &pc);
587 
588   switch(action)
589   { case SIO_GETFILENO:
590     { int *fdp = arg;
591       *fdp = fd;
592       return 0;
593     }
594     default:
595       return (*Sfilefunctions.control)((void*)(uintptr_t)fd, action, arg);
596   }
597 }
598 
599 
600 static IOFUNCTIONS Sprocessfunctions =
601 { Sread_process,
602   Swrite_process,
603   NULL,					/* seek */
604   Sclose_process,
605   Scontrol_process,
606   NULL					/* seek64 */
607 };
608 
609 
610 static IOSTREAM *
611 #ifdef __WINDOWS__
open_process_pipe(process_context * pc,int which,HANDLE fd)612 open_process_pipe(process_context *pc, int which, HANDLE fd)
613 #else
614 open_process_pipe(process_context *pc, int which, int fd)
615 #endif
616 { void *handle;
617   int flags;
618 
619   pc->open_mask |= (1<<which);
620 #ifdef __WINDOWS__
621   pc->pipes[which] = _open_osfhandle((long)fd, _O_BINARY);
622 #else
623   pc->pipes[which] = fd;
624 #endif
625 
626 #define ISO_FLAGS (SIO_RECORDPOS|SIO_FBUF|SIO_TEXT)
627 
628   if ( which == 0 )
629     flags = SIO_OUTPUT|ISO_FLAGS;
630   else
631     flags = SIO_INPUT|ISO_FLAGS;
632 
633   handle = (void *)((uintptr_t)pc | (uintptr_t)which);
634 
635   return Snew(handle, flags, &Sprocessfunctions);
636 }
637 
638 
639 		 /*******************************
640 		 *	       OS STUFF		*
641 		 *******************************/
642 
643 
644 #ifdef __WINDOWS__
645 
646 CRITICAL_SECTION process_lock;
647 #define LOCK()   EnterCriticalSection(&process_lock);
648 #define UNLOCK() LeaveCriticalSection(&process_lock);
649 
650 static void
win_init()651 win_init()
652 { InitializeCriticalSection(&process_lock);
653 }
654 
655 
656 static atom_t
WinError()657 WinError()
658 { int id = GetLastError();
659   char *msg;
660   static WORD lang;
661   static lang_initialised = 0;
662 
663   if ( !lang_initialised )
664     lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK);
665 
666 again:
667   if ( FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
668 		     FORMAT_MESSAGE_IGNORE_INSERTS|
669 		     FORMAT_MESSAGE_FROM_SYSTEM,
670 		     NULL,			/* source */
671 		     id,			/* identifier */
672 		     lang,
673 		     (LPTSTR) &msg,
674 		     0,				/* size */
675 		     NULL) )			/* arguments */
676   { atom_t a = PL_new_atom(msg);
677 
678     LocalFree(msg);
679     lang_initialised = 1;
680 
681     return a;
682   } else
683   { if ( lang_initialised == 0 )
684     { lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
685       lang_initialised = 1;
686       goto again;
687     }
688 
689     return PL_new_atom("Unknown Windows error");
690   }
691 }
692 
693 
694 static int
win_error(const char * op)695 win_error(const char *op)
696 { atom_t msg = WinError();
697   term_t ex = PL_new_term_ref();
698 
699   PL_unify_term(ex, PL_FUNCTOR, FUNCTOR_error2,
700 		      PL_FUNCTOR, FUNCTOR_system_error2,
701 		        PL_CHARS, op,
702 		        PL_ATOM, msg,
703 		      PL_VARIABLE);
704 
705   return PL_raise_exception(ex);
706 }
707 
708 
709 typedef struct arg_string
710 { size_t  len;
711   wchar_t *text;
712   wchar_t quote;
713 } arg_string;
714 
715 #define QMISC	0x1
716 #define QDBLQ	0x2
717 #define QSBLQ	0x4
718 
719 static int
set_quote(arg_string * as)720 set_quote(arg_string *as)
721 { int needq = 0;
722   const wchar_t *s = as->text;
723 
724   for(; *s; s++)
725   { if ( !iswalnum(*s) )
726     { if ( *s == '"' )
727 	needq |= QDBLQ;
728       else if ( *s == '\'' )
729 	needq |= QSBLQ;
730       else
731 	needq |= QMISC;
732     }
733   }
734 
735   if ( !needq )
736   { as->quote = 0;
737     return TRUE;
738   }
739   needq &= ~QMISC;
740   switch( needq )
741   { case QDBLQ:
742       as->quote = '\'';
743       return TRUE;
744     case 0:
745     case QSBLQ:
746       as->quote = '"';
747       return TRUE;
748     default:
749       return FALSE;
750   }
751 }
752 
753 
754 static int
win_command_line(term_t t,int arity,const wchar_t * exe,wchar_t ** cline)755 win_command_line(term_t t, int arity, const wchar_t *exe, wchar_t **cline)
756 { if ( arity > 0 )
757   { arg_string *av = PL_malloc((arity+1)*sizeof(*av));
758     term_t arg = PL_new_term_ref();
759     size_t cmdlen;
760     wchar_t *cmdline, *o;
761     const wchar_t *b;
762     int i;
763 
764     if ( (b=wcsrchr(exe, '\\')) )
765       b++;
766     else
767       b = exe;
768     av[0].text = (wchar_t*)b;
769     av[0].len = wcslen(av[0].text);
770     set_quote(&av[0]);
771     cmdlen = av[0].len+(av[0].quote?2:0)+1;
772 
773     for( i=1; i<=arity; i++)
774     { PL_get_arg(i, t, arg);
775 
776       if ( !PL_get_wchars(arg, &av[i].len, &av[i].text,
777 			  CVT_ATOMIC|CVT_EXCEPTION|BUF_MALLOC) )
778 	return FALSE;
779 
780       if ( wcslen(av[i].text) != av[i].len )
781 	return domain_error(arg, "no_zero_code_atom");
782 
783       if ( !set_quote(&av[i]) )
784 	return domain_error(arg, "dos_quotable_atom");
785 
786       cmdlen += av[i].len+(av[i].quote?2:0)+1;
787     }
788 
789     cmdline = PL_malloc(cmdlen*sizeof(wchar_t));
790     for( o=cmdline,i=0; i<=arity; )
791     { wchar_t *s = av[i].text;
792 
793       if ( av[i].quote )
794 	*o++ = av[i].quote;
795       wcsncpy(o, s, av[i].len);
796       o += av[i].len;
797       if ( i > 0 )
798 	PL_free(s);			/* do not free shared exename */
799       if ( av[i].quote )
800 	*o++ = av[i].quote;
801 
802       if (++i <= arity)
803 	*o++ = ' ';
804     }
805     *o = 0;
806     PL_free(av);
807 
808     *cline = cmdline;
809   } else
810   { *cline = NULL;
811   }
812 
813   return TRUE;
814 }
815 
816 
817 typedef struct win_process
818 { DWORD pid;
819   HANDLE handle;
820   struct win_process *next;
821 } win_process;
822 
823 
824 static win_process *processes;
825 
826 static void
register_process(DWORD pid,HANDLE h)827 register_process(DWORD pid, HANDLE h)
828 { win_process *wp = PL_malloc(sizeof(*wp));
829 
830   wp->pid = pid;
831   wp->handle = h;
832   LOCK();
833   wp->next = processes;
834   processes = wp;
835   UNLOCK();
836 }
837 
838 
839 static int
unregister_process(DWORD pid)840 unregister_process(DWORD pid)
841 { win_process **wpp, *wp;
842 
843   LOCK();
844   for(wpp=&processes, wp=*wpp; wp; wpp=&wp->next, wp=*wpp)
845   { if ( wp->pid == pid )
846     { *wpp = wp->next;
847       PL_free(wp);
848       UNLOCK();
849       return TRUE;
850     }
851   }
852 
853   UNLOCK();
854   return FALSE;
855 }
856 
857 
858 static HANDLE
find_process_from_pid(DWORD pid,const char * pred)859 find_process_from_pid(DWORD pid, const char *pred)
860 { win_process *wp;
861 
862   LOCK();
863   for(wp=processes; wp; wp=wp->next)
864   { if ( wp->pid == pid )
865     { HANDLE h = wp->handle;
866       UNLOCK();
867       return h;
868     }
869   }
870 
871   UNLOCK();
872 
873   if ( pred )
874   { term_t ex = PL_new_term_ref();
875 
876     PL_put_integer(ex, pid);
877     pl_error(NULL, 2, NULL, ERR_EXISTENCE,
878 	     "process", ex);
879   }
880 
881   return (HANDLE)0;
882 }
883 
884 
885 #define WP_TIMEOUT 2
886 
887 static int
wait_process_handle(HANDLE process,ULONG * rc,DWORD timeout)888 wait_process_handle(HANDLE process, ULONG *rc, DWORD timeout)
889 { DWORD wc;
890 
891 retry:
892   wc = MsgWaitForMultipleObjects(1,
893 				 &process,
894 				 FALSE,	/* return on any event */
895 				 timeout,
896 				 QS_ALLINPUT);
897 
898   switch(wc)
899   { case WAIT_OBJECT_0:
900       if ( !GetExitCodeProcess(process, rc) )
901       { win_error("GetExitCodeProcess");
902 	CloseHandle(process);
903 	return FALSE;
904       }
905       CloseHandle(process);
906       return TRUE;
907     case WAIT_OBJECT_0+1:
908     { MSG msg;
909 
910       while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
911       { TranslateMessage(&msg);
912 	DispatchMessage(&msg);
913 	if ( PL_handle_signals() < 0 )
914 	  return FALSE;
915       }
916       goto retry;
917     }
918     case WAIT_TIMEOUT:
919       return WP_TIMEOUT;
920     default:
921       win_error("WaitForSingleObject");
922       CloseHandle(process);
923       return FALSE;
924   }
925 }
926 
927 
928 static int
wait_for_pid(pid_t pid,term_t code,wait_options * opts)929 wait_for_pid(pid_t pid, term_t code, wait_options *opts)
930 { HANDLE *h;
931 
932   if ( (h=find_process_from_pid(pid, "process_wait")) )
933   { ULONG rc;
934     DWORD timeout;
935     int wc;
936 
937     if ( opts->has_timeout )
938       timeout = (DWORD)(opts->timeout * 1000.0);
939     else
940       timeout = INFINITE;
941 
942     if ( !(wc=wait_process_handle(h, &rc, timeout)) )
943       return FALSE;
944     if ( wc == WP_TIMEOUT )
945       return PL_unify_atom(code, ATOM_timeout);
946 
947     unregister_process(pid);
948 
949     return PL_unify_term(code,
950 			 PL_FUNCTOR, FUNCTOR_exit1,
951 			   PL_LONG, rc);
952   } else
953   { return FALSE;
954   }
955 }
956 
957 
958 static int
wait_for_process(process_context * pc)959 wait_for_process(process_context *pc)
960 { int rc;
961   ULONG prc;
962 
963   rc = wait_process_handle(pc->handle, &prc, INFINITE);
964   CloseHandle(pc->handle);
965   PL_free(pc);
966 
967   return rc;
968 }
969 
970 
971 static int
win_wait_success(atom_t exe,HANDLE process)972 win_wait_success(atom_t exe, HANDLE process)
973 { ULONG rc;
974 
975   if ( !wait_process_handle(process, &rc, INFINITE) )
976     return FALSE;
977 
978   if ( rc != 0 )
979   { term_t code = PL_new_term_ref();
980     term_t ex = PL_new_term_ref();
981 
982     PL_unify_term(ex,
983 		  PL_FUNCTOR, FUNCTOR_error2,
984 		    PL_FUNCTOR, FUNCTOR_process_error2,
985 		      PL_ATOM, exe,
986 		      PL_FUNCTOR, FUNCTOR_exit1,
987 		        PL_LONG, rc,
988 		    PL_VARIABLE);
989     return PL_raise_exception(ex);
990   }
991 
992   return TRUE;
993 }
994 
995 
996 static int
create_pipes(p_options * info)997 create_pipes(p_options *info)
998 { int i;
999   SECURITY_ATTRIBUTES sa;
1000 
1001   sa.nLength = sizeof(sa);          /* Length in bytes */
1002   sa.bInheritHandle = 1;            /* the child must inherit these handles */
1003   sa.lpSecurityDescriptor = NULL;
1004 
1005   for(i=0; i<3; i++)
1006   { p_stream *s = &info->streams[i];
1007 
1008     if ( s->term )
1009     { if ( !CreatePipe(&s->fd[0], &s->fd[1], &sa, 1<<13) )
1010       { return win_error("CreatePipe");
1011       }
1012     }
1013   }
1014 
1015   return TRUE;
1016 }
1017 
1018 
1019 static IOSTREAM *
Sopen_handle(HANDLE h,const char * mode)1020 Sopen_handle(HANDLE h, const char *mode)
1021 { return Sfdopen(_open_osfhandle((long)h, _O_BINARY), mode);
1022 }
1023 
1024 
1025 static HANDLE
open_null_stream(DWORD access)1026 open_null_stream(DWORD access)
1027 { SECURITY_ATTRIBUTES sa;
1028 
1029   sa.nLength = sizeof(sa);          /* Length in bytes */
1030   sa.bInheritHandle = 1;            /* the child must inherit these handles */
1031   sa.lpSecurityDescriptor = NULL;
1032 
1033   return CreateFile("nul",
1034 		    access,
1035 		    FILE_SHARE_READ|FILE_SHARE_WRITE,
1036 		    &sa,		/* security */
1037 		    OPEN_EXISTING,
1038 		    0,
1039 		    NULL);
1040 }
1041 
1042 
1043 static int
console_app(void)1044 console_app(void)
1045 { HANDLE h;
1046 
1047   if ( (h = GetStdHandle(STD_OUTPUT_HANDLE)) != INVALID_HANDLE_VALUE )
1048   { DWORD mode;
1049 
1050     if ( GetConsoleMode(h, &mode) )
1051       return TRUE;
1052   }
1053 
1054   return FALSE;
1055 }
1056 
1057 
1058 static int
do_create_process(p_options * info)1059 do_create_process(p_options *info)
1060 { int flags = 0;
1061   PROCESS_INFORMATION pi;
1062   STARTUPINFOW si;
1063 
1064   switch(info->window)
1065   { case MAYBE:
1066       if ( !console_app() )
1067 	flags |= CREATE_NO_WINDOW;
1068       break;
1069     case TRUE:
1070       break;
1071     case FALSE:
1072       flags |= CREATE_NO_WINDOW;
1073       break;
1074   }
1075 
1076   memset(&si, 0, sizeof(si));
1077   si.cb = sizeof(si);
1078   si.dwFlags = STARTF_USESTDHANDLES;
1079 
1080 				      /* stdin */
1081   switch( info->streams[0].type )
1082   { case std_pipe:
1083       si.hStdInput = info->streams[0].fd[0];
1084       SetHandleInformation(info->streams[0].fd[1],
1085 			   HANDLE_FLAG_INHERIT, FALSE);
1086       break;
1087     case std_null:
1088       si.hStdInput = open_null_stream(GENERIC_READ);
1089       break;
1090     case std_std:
1091       si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1092       break;
1093   }
1094 				      /* stdout */
1095   switch( info->streams[1].type )
1096   { case std_pipe:
1097       si.hStdOutput = info->streams[1].fd[1];
1098       SetHandleInformation(info->streams[1].fd[0],
1099 			   HANDLE_FLAG_INHERIT, FALSE);
1100       break;
1101     case std_null:
1102       si.hStdOutput = open_null_stream(GENERIC_WRITE);
1103       break;
1104     case std_std:
1105       si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1106       break;
1107   }
1108 				      /* stderr */
1109   switch( info->streams[2].type )
1110   { case std_pipe:
1111       si.hStdError = info->streams[2].fd[1];
1112       SetHandleInformation(info->streams[2].fd[0],
1113 			   HANDLE_FLAG_INHERIT, FALSE);
1114       break;
1115     case std_null:
1116       si.hStdError = open_null_stream(GENERIC_WRITE);
1117       break;
1118     case std_std:
1119       si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1120       break;
1121   }
1122 
1123   if ( CreateProcessW(info->exe,
1124 		      info->cmdline,
1125 		      NULL,		/* Process security */
1126 		      NULL,		/* Thread security */
1127 		      TRUE,		/* Inherit handles */
1128 		      flags,		/* Creation flags */
1129 		      info->envbuf.buffer, /* Environment */
1130 		      info->cwd,	/* Directory */
1131 		      &si,		/* Startup info */
1132 		      &pi) )		/* Process information */
1133   { CloseHandle(pi.hThread);
1134 
1135     if ( info->pipes > 0 && info->pid == 0 )
1136     { IOSTREAM *s;
1137       process_context *pc = PL_malloc(sizeof(*pc));
1138 
1139       DEBUG(Sdprintf("Wait on pipes\n"));
1140 
1141       memset(pc, 0, sizeof(*pc));
1142       pc->magic  = PROCESS_MAGIC;
1143       pc->handle = pi.hProcess;
1144 
1145       if ( info->streams[0].type == std_pipe )
1146       { CloseHandle(info->streams[0].fd[0]);
1147 	s = open_process_pipe(pc, 0, info->streams[0].fd[1]);
1148 	PL_unify_stream(info->streams[0].term, s);
1149       }
1150       if ( info->streams[1].type == std_pipe )
1151       { CloseHandle(info->streams[1].fd[1]);
1152 	s = open_process_pipe(pc, 1, info->streams[1].fd[0]);
1153 	PL_unify_stream(info->streams[1].term, s);
1154       }
1155       if ( info->streams[2].type == std_pipe )
1156       { CloseHandle(info->streams[2].fd[1]);
1157 	s = open_process_pipe(pc, 2, info->streams[2].fd[0]);
1158 	PL_unify_stream(info->streams[2].term, s);
1159       }
1160 
1161       return TRUE;
1162     } else if ( info->pipes > 0 )
1163     { IOSTREAM *s;
1164 
1165       if ( info->streams[0].type == std_pipe )
1166       { CloseHandle(info->streams[0].fd[0]);
1167 	s = Sopen_handle(info->streams[0].fd[1], "w");
1168 	PL_unify_stream(info->streams[0].term, s);
1169       }
1170       if ( info->streams[1].type == std_pipe )
1171       { CloseHandle(info->streams[1].fd[1]);
1172 	s = Sopen_handle(info->streams[1].fd[0], "r");
1173 	PL_unify_stream(info->streams[1].term, s);
1174       }
1175       if ( info->streams[2].type == std_pipe )
1176       { CloseHandle(info->streams[2].fd[1]);
1177 	s = Sopen_handle(info->streams[2].fd[0], "r");
1178 	PL_unify_stream(info->streams[2].term, s);
1179       }
1180     }
1181 
1182     if ( info->pid )
1183     { register_process(pi.dwProcessId, pi.hProcess);
1184       return PL_unify_integer(info->pid, pi.dwProcessId);
1185     }
1186 
1187     return win_wait_success(info->exe_name, pi.hProcess);
1188   } else
1189   { return win_error("CreateProcess");
1190   }
1191 }
1192 
1193 #else /*__WINDOWS__*/
1194 
1195 static int
create_pipes(p_options * info)1196 create_pipes(p_options *info)
1197 { int i;
1198 
1199   for(i=0; i<3; i++)
1200   { p_stream *s = &info->streams[i];
1201 
1202     if ( s->term )
1203     { if ( pipe(s->fd) )
1204       { assert(errno = EMFILE);
1205 	return resource_error("open_files");
1206       }
1207     }
1208   }
1209 
1210   return TRUE;
1211 }
1212 
1213 
1214 static int
unify_exit_status(term_t code,int status)1215 unify_exit_status(term_t code, int status)
1216 { if ( WIFEXITED(status) )
1217   { return PL_unify_term(code,
1218 			 PL_FUNCTOR, FUNCTOR_exit1,
1219 			   PL_INT, (int)WEXITSTATUS(status));
1220   } else if ( WIFSIGNALED(status) )
1221   { return PL_unify_term(code,
1222 			 PL_FUNCTOR, FUNCTOR_killed1,
1223 			   PL_INT, (int)WTERMSIG(status));
1224   } else
1225   { assert(0);
1226     return FALSE;
1227   }
1228 }
1229 
1230 
1231 static int
wait_for_pid(pid_t pid,term_t code,wait_options * opts)1232 wait_for_pid(pid_t pid, term_t code, wait_options *opts)
1233 { pid_t p2;
1234   int status;
1235 
1236   if ( opts->has_timeout && opts->timeout == 0.0 )
1237   { if ( (p2=waitpid(pid, &status, WNOHANG)) == pid )
1238       return unify_exit_status(code, status);
1239     else if ( p2 == 0 )
1240       return PL_unify_atom(code, ATOM_timeout);
1241     else
1242     { term_t PID;
1243 
1244     error:
1245       return ((PID = PL_new_term_ref()) &&
1246 	      PL_put_integer(PID, pid) &&
1247 	      pl_error(NULL, 0, "waitpid", ERR_ERRNO,
1248 		       errno, "wait", "process", PID));
1249     }
1250   }
1251 
1252   for(;;)
1253   { if ( (p2=waitpid(pid, &status, 0)) == pid )
1254       return unify_exit_status(code, status);
1255 
1256     if ( p2 == -1 && errno == EINTR )
1257     { if ( PL_handle_signals() < 0 )
1258 	return FALSE;
1259     } else
1260     { goto error;
1261     }
1262   }
1263 }
1264 
1265 
1266 static int
wait_for_process(process_context * pc)1267 wait_for_process(process_context *pc)
1268 { for(;;)
1269   { int status;
1270     pid_t p2;
1271 
1272     if ( (p2=waitpid(pc->pid, &status, 0)) == pc->pid )
1273     { PL_free(pc);
1274       return TRUE;
1275     }
1276 
1277     if ( errno == EINTR && PL_handle_signals() >= 0 )
1278       continue;
1279 
1280     PL_free(pc);
1281     return FALSE;
1282   }
1283 }
1284 
1285 
1286 static int
wait_success(atom_t name,pid_t pid)1287 wait_success(atom_t name, pid_t pid)
1288 { pid_t p2;
1289 
1290   for(;;)
1291   { int status;
1292 
1293     if ( (p2=waitpid(pid, &status, 0)) == pid )
1294     { if ( WIFEXITED(status) && WEXITSTATUS(status) == 0 )
1295       { return TRUE;
1296       } else
1297       { term_t code, ex;
1298 
1299 	if ( (code = PL_new_term_ref()) &&
1300 	     (ex = PL_new_term_ref()) &&
1301 	     unify_exit_status(code, status) &&
1302 	     PL_unify_term(ex,
1303 			   PL_FUNCTOR, FUNCTOR_error2,
1304 			     PL_FUNCTOR, FUNCTOR_process_error2,
1305 			       PL_ATOM, name,
1306 			       PL_TERM, code,
1307 			     PL_VARIABLE) )
1308 	  return PL_raise_exception(ex);
1309 	return FALSE;
1310       }
1311     }
1312 
1313     if ( p2 == -1 && errno == EINTR )
1314     { if ( PL_handle_signals() < 0 )
1315 	return FALSE;
1316     }
1317   }
1318 }
1319 
1320 
1321 static int
do_create_process(p_options * info)1322 do_create_process(p_options *info)
1323 { int pid;
1324 
1325   if ( !(pid=fork()) )			/* child */
1326   { int fd;
1327     int rc;
1328 
1329     PL_cleanup_fork();
1330 
1331     if ( info->detached )
1332       setsid();
1333 
1334     if ( info->cwd )
1335     { if ( chdir(info->cwd) )
1336       { perror(info->cwd);
1337 	exit(1);
1338       }
1339     }
1340 
1341 					/* stdin */
1342     switch( info->streams[0].type )
1343     { case std_pipe:
1344 	dup2(info->streams[0].fd[0], 0);
1345 	close(info->streams[0].fd[1]);
1346 	break;
1347       case std_null:
1348 	if ( (fd = open("/dev/null", O_RDONLY)) >= 0 )
1349 	  dup2(fd, 0);
1350         break;
1351       case std_std:
1352 	break;
1353     }
1354 					/* stdout */
1355     switch( info->streams[1].type )
1356     { case std_pipe:
1357 	dup2(info->streams[1].fd[1], 1);
1358         close(info->streams[1].fd[0]);
1359 	break;
1360       case std_null:
1361 	if ( (fd = open("/dev/null", O_WRONLY)) >= 0 )
1362 	  dup2(fd, 1);
1363         break;
1364       case std_std:
1365 	break;
1366     }
1367 					/* stderr */
1368     switch( info->streams[2].type )
1369     { case std_pipe:
1370 	dup2(info->streams[2].fd[1], 2);
1371         close(info->streams[2].fd[0]);
1372 	break;
1373       case std_null:
1374 	if ( (fd = open("/dev/null", O_WRONLY)) >= 0 )
1375 	  dup2(fd, 2);
1376         break;
1377       case std_std:
1378 	break;
1379     }
1380 
1381     if ( info->envp )
1382       rc = execve(info->exe, info->argv, info->envp);
1383     else
1384       rc = execv(info->exe, info->argv);
1385 
1386     if ( rc )
1387     { perror(info->exe);
1388       exit(1);
1389     }
1390 
1391     { term_t exe = PL_new_term_ref();
1392       PL_put_atom_chars(exe, info->exe);
1393 
1394       return pl_error(NULL, 0, "execv", ERR_ERRNO, errno, "exec", "process", exe);
1395     }
1396   } else				/* parent */
1397   { if ( info->pipes > 0 && info->pid == 0 )
1398     { IOSTREAM *s;
1399       process_context *pc = PL_malloc(sizeof(*pc));
1400 
1401       DEBUG(Sdprintf("Wait on pipes\n"));
1402 
1403       memset(pc, 0, sizeof(*pc));
1404       pc->magic = PROCESS_MAGIC;
1405       pc->pid = pid;
1406 
1407       if ( info->streams[0].type == std_pipe )
1408       { close(info->streams[0].fd[0]);
1409 	s = open_process_pipe(pc, 0, info->streams[0].fd[1]);
1410 	PL_unify_stream(info->streams[0].term, s);
1411       }
1412       if ( info->streams[1].type == std_pipe )
1413       { close(info->streams[1].fd[1]);
1414 	s = open_process_pipe(pc, 1, info->streams[1].fd[0]);
1415 	PL_unify_stream(info->streams[1].term, s);
1416       }
1417       if ( info->streams[2].type == std_pipe )
1418       { close(info->streams[2].fd[1]);
1419 	s = open_process_pipe(pc, 2, info->streams[2].fd[0]);
1420 	PL_unify_stream(info->streams[2].term, s);
1421       }
1422 
1423       return TRUE;
1424     } else if ( info->pipes > 0 )
1425     { IOSTREAM *s;
1426 
1427       if ( info->streams[0].type == std_pipe )
1428       { close(info->streams[0].fd[0]);
1429 	s = Sfdopen(info->streams[0].fd[1], "w");
1430 	PL_unify_stream(info->streams[0].term, s);
1431       }
1432       if ( info->streams[1].type == std_pipe )
1433       { close(info->streams[1].fd[1]);
1434 	s = Sfdopen(info->streams[1].fd[0], "r");
1435 	PL_unify_stream(info->streams[1].term, s);
1436       }
1437       if ( info->streams[2].type == std_pipe )
1438       { close(info->streams[2].fd[1]);
1439 	s = Sfdopen(info->streams[2].fd[0], "r");
1440 	PL_unify_stream(info->streams[2].term, s);
1441       }
1442     }
1443 
1444     if ( info->pid )
1445       return PL_unify_integer(info->pid, pid);
1446 
1447     return wait_success(info->exe_name, pid);
1448   }
1449 }
1450 
1451 #endif /*__WINDOWS__*/
1452 
1453 
1454 		 /*******************************
1455 		 *	      BINDING		*
1456 		 *******************************/
1457 
1458 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1459 Basic process creation interface takes
1460 
1461 	* Exe file
1462 	* List of arguments
1463 	* standard streams		% std, null, pipe(S)
1464 	* Working directory
1465 	* detached			% Unix
1466 	* window			% Windows
1467 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1468 
1469 static foreign_t
process_create(term_t exe,term_t options)1470 process_create(term_t exe, term_t options)
1471 { p_options info;
1472   int rc = FALSE;
1473 
1474   memset(&info, 0, sizeof(info));
1475 
1476   if ( !get_exe(exe, &info) )
1477     goto out;
1478   if ( !parse_options(options, &info) )
1479     goto out;
1480   if ( !create_pipes(&info) )
1481     goto out;
1482 
1483   rc = do_create_process(&info);
1484 
1485 out:
1486   free_options(&info);
1487 
1488   return rc;
1489 }
1490 
1491 
1492 static int
get_pid(term_t pid,pid_t * p)1493 get_pid(term_t pid, pid_t *p)
1494 { int n;
1495 
1496   if ( !PL_get_integer(pid, &n) )
1497     return type_error(pid, "integer");
1498   if ( n < 0 )
1499     return domain_error(pid, "not_less_than_zero");
1500 
1501   *p = n;
1502   return TRUE;
1503 }
1504 
1505 
1506 static foreign_t
process_wait(term_t pid,term_t code,term_t options)1507 process_wait(term_t pid, term_t code, term_t options)
1508 { pid_t p;
1509   wait_options opts;
1510   term_t tail = PL_copy_term_ref(options);
1511   term_t head = PL_new_term_ref();
1512   term_t arg  = PL_new_term_ref();
1513 
1514   if ( !get_pid(pid, &p) )
1515     return FALSE;
1516 
1517   memset(&opts, 0, sizeof(opts));
1518   while(PL_get_list(tail, head, tail))
1519   { atom_t name;
1520     int arity;
1521 
1522     if ( !PL_get_name_arity(head, &name, &arity) || arity != 1 )
1523       return type_error(head, "option");
1524     _PL_get_arg(1, head, arg);
1525     if ( name == ATOM_timeout )
1526     { atom_t a;
1527 
1528       if ( !(PL_get_atom(arg, &a) && a == ATOM_infinite) )
1529       { if ( !PL_get_float(arg, &opts.timeout) )
1530 	  return type_error(arg, "timeout");
1531 	opts.has_timeout = TRUE;
1532       }
1533     } else if ( name == ATOM_release )
1534     { if ( !PL_get_bool(arg, &opts.release) )
1535 	return type_error(arg, "boolean");
1536       if ( opts.release == FALSE )
1537 	return domain_error(arg, "true");
1538     } else
1539       return domain_error(head, "process_wait_option");
1540   }
1541   if ( !PL_get_nil(tail) )
1542     return type_error(tail, "list");
1543 
1544   return wait_for_pid(p, code, &opts);
1545 }
1546 
1547 
1548 static foreign_t
process_kill(term_t pid,term_t signal)1549 process_kill(term_t pid, term_t signal)
1550 { int p;
1551 
1552   if ( !get_pid(pid, &p) )
1553     return FALSE;
1554 
1555 {
1556 #ifdef __WINDOWS__
1557   HANDLE h;
1558 
1559   if ( !(h=find_process_from_pid(p, "process_kill")) )
1560     return FALSE;
1561 
1562   if ( TerminateProcess(h, 255) )
1563     return TRUE;
1564 
1565   return win_error("TerminateProcess");
1566 #else /*__WINDOWS__*/
1567   int sig;
1568 
1569   if ( !PL_get_signum_ex(signal, &sig) )
1570     return FALSE;
1571 
1572   if ( kill(p, sig) == 0 )
1573     return TRUE;
1574 
1575   switch(errno)
1576   { case EPERM:
1577       return pl_error("process_kill", 2, NULL, ERR_PERMISSION,
1578 		      pid, "kill", "process");
1579     case ESRCH:
1580       return pl_error("process_kill", 2, NULL, ERR_EXISTENCE,
1581 		      "process", pid);
1582     default:
1583       return pl_error("process_kill", 2, "kill", ERR_ERRNO, errno, "kill", "process", pid);
1584   }
1585 #endif /*__WINDOWS__*/
1586 }
1587 }
1588 
1589 
1590 #define MKATOM(n) ATOM_ ## n = PL_new_atom(#n)
1591 #define MKFUNCTOR(n,a) FUNCTOR_ ## n ## a = PL_new_functor(PL_new_atom(#n), a)
1592 
1593 install_t
install_process()1594 install_process()
1595 {
1596 #ifdef __WINDOWS__
1597   win_init();
1598 #endif
1599 
1600   MKATOM(stdin);
1601   MKATOM(stdout);
1602   MKATOM(stderr);
1603   MKATOM(std);
1604   MKATOM(null);
1605   MKATOM(process);
1606   MKATOM(detached);
1607   MKATOM(cwd);
1608   MKATOM(env);
1609   MKATOM(window);
1610   MKATOM(timeout);
1611   MKATOM(release);
1612   MKATOM(infinite);
1613 
1614   MKFUNCTOR(pipe, 1);
1615   MKFUNCTOR(error, 2);
1616   MKFUNCTOR(type_error, 2);
1617   MKFUNCTOR(domain_error, 2);
1618   MKFUNCTOR(process_error, 2);
1619   MKFUNCTOR(system_error, 2);
1620   MKFUNCTOR(resource_error, 1);
1621   MKFUNCTOR(exit, 1);
1622   MKFUNCTOR(killed, 1);
1623 
1624   FUNCTOR_eq2 = PL_new_functor(PL_new_atom("="), 2);
1625 
1626   PL_register_foreign("process_create", 2, process_create, 0);
1627   PL_register_foreign("process_wait", 3, process_wait, 0);
1628   PL_register_foreign("process_kill", 2, process_kill, 0);
1629 }
1630