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