1 /*  $Id$
2 
3     Part of SWI-Prolog
4 
5     Author:        Jan Wielemaker
6     E-mail:        jan@swi.psy.uva.nl
7     WWW:           http://www.swi-prolog.org
8     Copyright (C): 1985-2002, 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 #include <SWI-Stream.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <sys/types.h>u
34 #include <sys/wait.h>
35 #include <fcntl.h>
36 #include <assert.h>
37 #include "clib.h"
38 #include <signal.h>
39 #include <string.h>
40 #include <errno.h>
41 #ifdef HAVE_ALLOCA_H
42 #include <alloca.h>
43 #endif
44 
45 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46 Unix process management.
47 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
48 
49 static IOSTREAM *
name_to_stream(const char * name)50 name_to_stream(const char *name)
51 { IOSTREAM *s;
52   term_t t = PL_new_term_ref();
53 
54   PL_put_atom_chars(t, name);
55   if ( PL_get_stream_handle(t, &s) )
56     return s;
57 
58   return NULL;
59 }
60 
61 
62 static void
flush_stream(const char * name)63 flush_stream(const char *name)
64 { IOSTREAM *s;
65 
66   if ( (s = name_to_stream(name)) )
67     Sflush(s);
68 
69   PL_release_stream(s);
70 }
71 
72 
73 
74 static foreign_t
pl_fork(term_t a0)75 pl_fork(term_t a0)
76 { pid_t pid;
77 
78   flush_stream("user_output");		/* general call to flush all IO? */
79 
80   if ( (pid = fork()) < 0 )
81     return PL_warning("fork/1: failed: %s", strerror(errno));
82 
83   if ( pid > 0 )
84     return PL_unify_integer(a0, pid);
85   else
86     return PL_unify_atom_chars(a0, "child");
87 }
88 
89 
90 #define free_argv(n) \
91 	{ int _k; \
92 	  for( _k=1; _k <= n; _k++) \
93 	    free(argv[_k]); \
94 	  free(argv); \
95 	}
96 
97 static foreign_t
pl_exec(term_t cmd)98 pl_exec(term_t cmd)
99 { int argc;
100   atom_t name;
101 
102   if ( PL_get_name_arity(cmd, &name, &argc) )
103   { term_t a = PL_new_term_ref();
104     char **argv = malloc(sizeof(char*) * (argc + 2));
105     int i;
106 
107     argv[0] = (char *)PL_atom_chars(name);
108 
109     for(i=1; i<=argc; i++)
110     { char *s;
111 
112       if ( PL_get_arg(i, cmd, a) &&
113 	   PL_get_chars(a, &s, CVT_ALL|REP_MB|BUF_MALLOC) )
114 	argv[i] = s;
115       else
116       { free_argv(i-1);
117 	return pl_error("exec", 1, NULL, ERR_ARGTYPE, i, a, "atomic");
118       }
119     }
120     argv[argc+1] = NULL;
121 
122     execvp(argv[0], argv);
123     free_argv(argc);
124     return pl_error("exec", 1, NULL, ERR_ERRNO, errno, "execute", "command", cmd);
125   }
126 
127   return pl_error("exec", 1, NULL, ERR_ARGTYPE, 1, cmd, "compound");
128 }
129 
130 
131 static foreign_t
pl_wait(term_t Pid,term_t Status)132 pl_wait(term_t Pid, term_t Status)
133 { int status;
134   pid_t pid = wait(&status);
135 
136   if ( pid == -1 )
137     return pl_error("wait", 2, NULL, ERR_ERRNO, errno, "wait", "process", Pid);
138 
139   if ( PL_unify_integer(Pid, pid) )
140   { if ( WIFEXITED(status) )
141       return PL_unify_term(Status,
142 			   CompoundArg("exited", 1),
143 			   IntArg(WEXITSTATUS(status)));
144     if ( WIFSIGNALED(status) )
145       return PL_unify_term(Status,
146 			   CompoundArg("signaled", 1),
147 			   IntArg(WTERMSIG(status)));
148     if ( WIFSTOPPED(status) )
149       return PL_unify_term(Status,
150 			   CompoundArg("stopped", 1),
151 			   IntArg(WSTOPSIG(status)));
152     assert(0);
153   }
154 
155   return FALSE;
156 }
157 
158 
159 static foreign_t
pl_kill(term_t Pid,term_t Sig)160 pl_kill(term_t Pid, term_t Sig)
161 { int pid;
162   int sig;
163 
164   if ( !PL_get_integer(Pid, &pid) )
165     return pl_error("kill", 2, NULL, ERR_ARGTYPE, 1, Pid, "pid");
166   if ( !PL_get_signum_ex(Sig, &sig) )
167     return FALSE;
168 
169   if ( kill(pid, sig) < 0 )
170     return pl_error("kill", 2, NULL, ERR_ERRNO, errno,
171 		    "kill", "process", Pid);
172 
173   return TRUE;
174 }
175 
176 
177 		 /*******************************
178 		 *	   STREAM STUFF		*
179 		 *******************************/
180 
181 static foreign_t
pl_pipe(term_t Read,term_t Write)182 pl_pipe(term_t Read, term_t Write)
183 { int fd[2];
184   IOSTREAM *in, *out;
185 
186   if ( pipe(fd) != 0 )
187     return pl_error("pipe", 2, NULL, ERR_ERRNO, errno, "create", "pipe", 0);
188 
189   in  = Sfdopen(fd[0], "r");
190   out = Sfdopen(fd[1], "w");
191 
192   if ( PL_open_stream(Read, in) &&
193        PL_open_stream(Write, out) )
194     return TRUE;
195 
196   return FALSE;
197 }
198 
199 
200 static int
get_stream_no(term_t t,IOSTREAM ** s,int * fn)201 get_stream_no(term_t t, IOSTREAM **s, int *fn)
202 { if ( PL_get_integer(t, fn) )
203     return TRUE;
204   if ( PL_get_stream_handle(t, s) )
205   { *fn = Sfileno(*s);
206     return TRUE;
207   }
208 
209   return FALSE;
210 }
211 
212 
213 static foreign_t
pl_dup(term_t from,term_t to)214 pl_dup(term_t from, term_t to)
215 { IOSTREAM *f = NULL, *t = NULL;
216   int rval = FALSE;
217   int fn, tn;
218 
219   if ( !get_stream_no(from, &f, &fn) ||
220        !get_stream_no(to, &t, &tn) )
221     goto out;
222 
223   if ( dup2(fn, tn) < 0 )
224   { pl_error("dup", 2, NULL, ERR_ERRNO, errno, "dup", "stream", from);
225     goto out;
226   } else
227   { rval = TRUE;
228   }
229 
230 out:
231   if ( f )
232     PL_release_stream(f);
233   if ( t )
234     PL_release_stream(t);
235 
236   return rval;
237 }
238 
239 
240 static foreign_t
pl_environ(term_t l)241 pl_environ(term_t l)
242 { extern char **environ;
243   char **e;
244   term_t t = PL_copy_term_ref(l);
245   term_t t2 = PL_new_term_ref();
246   term_t nt = PL_new_term_ref();
247   term_t vt = PL_new_term_ref();
248   functor_t FUNCTOR_equal2 = PL_new_functor(PL_new_atom("="), 2);
249 
250 #if HAVE__NSGETENVIRON
251   for(e = _NSGetEnviron(); *e; e++)
252 #else
253   for(e = environ; *e; e++)
254 #endif
255   { char *s = strchr(*e, '=');
256 
257     if ( !s )
258       s = *e + strlen(*e);
259 
260     { int len = s-*e;
261       char *name = alloca(len+1);
262 
263       strncpy(name, *e, len);
264       name[len] = '\0';
265       PL_put_atom_chars(nt, name);
266       PL_put_atom_chars(vt, s+1);
267       if ( !PL_cons_functor(nt, FUNCTOR_equal2, nt, vt) ||
268 	   !PL_unify_list(t, t2, t) ||
269 	   !PL_unify(t2, nt) )
270 	return FALSE;
271     }
272   }
273 
274   return PL_unify_nil(t);
275 }
276 
277 
278 		 /*******************************
279 		 *	    DEAMON IO		*
280 		 *******************************/
281 
282 static atom_t error_file;		/* file for output */
283 static int    error_fd;			/* and its fd */
284 
285 static ssize_t
read_eof(void * handle,char * buf,size_t count)286 read_eof(void *handle, char *buf, size_t count)
287 { return 0;
288 }
289 
290 
291 static ssize_t
write_null(void * handle,char * buf,size_t count)292 write_null(void *handle, char *buf, size_t count)
293 { if ( error_fd )
294   { if ( error_fd >= 0 )
295       return write(error_fd, buf, count);
296   } else if ( error_file )
297   { error_fd = open(PL_atom_chars(error_file), O_WRONLY|O_CREAT|O_TRUNC, 0644);
298     return write_null(handle, buf, count);
299   }
300 
301   return count;
302 }
303 
304 
305 static long
seek_error(void * handle,long pos,int whence)306 seek_error(void *handle, long pos, int whence)
307 { return -1;
308 }
309 
310 
311 static int
close_null(void * handle)312 close_null(void *handle)
313 { return 0;
314 }
315 
316 
317 static IOFUNCTIONS dummy =
318 { read_eof,
319   write_null,
320   seek_error,
321   close_null,
322   NULL
323 };
324 
325 
326 static void
close_underlying_fd(IOSTREAM * s)327 close_underlying_fd(IOSTREAM *s)
328 { if ( s )
329   { int fd;
330 
331     if ( (fd = Sfileno(s)) >= 0 )
332       close(fd);
333 
334     s->functions = &dummy;
335     s->flags &= ~SIO_FILE;		/* no longer a file */
336     s->flags |= SIO_LBUF;		/* do line-buffering */
337   }
338 }
339 
340 
341 static foreign_t
pl_detach_IO()342 pl_detach_IO()
343 { char buf[100];
344 
345   sprintf(buf, "/tmp/pl-out.%d", (int)getpid());
346   error_file = PL_new_atom(buf);
347 
348   close_underlying_fd(Serror);
349   close_underlying_fd(Soutput);
350   close_underlying_fd(Sinput);
351   close_underlying_fd(name_to_stream("user_input"));
352   close_underlying_fd(name_to_stream("user_output"));
353   close_underlying_fd(name_to_stream("user_error"));
354 
355 #ifdef HAVE_SETSID
356   setsid();
357 #else
358 { int fd;
359 
360   if ( (fd = open("/dev/tty", 2)) )
361   { ioctl(fd, TIOCNOTTY, NULL);		/* detach from controlling tty */
362     close(fd);
363   }
364 }
365 #endif
366 
367   return TRUE;
368 }
369 
370 
371 install_t
install_unix()372 install_unix()
373 { PL_register_foreign("fork",      1, pl_fork, 0);
374   PL_register_foreign("exec",      1, pl_exec, 0);
375   PL_register_foreign("wait",      2, pl_wait, 0);
376   PL_register_foreign("kill",      2, pl_kill, 0);
377   PL_register_foreign("pipe",      2, pl_pipe, 0);
378   PL_register_foreign("dup",       2, pl_dup, 0);
379   PL_register_foreign("detach_IO", 0, pl_detach_IO, 0);
380   PL_register_foreign("environ",   1, pl_environ, 0);
381 }
382 
383 
384 
385 
386 
387 
388 
389