1 /* -*- mode: C; mode: fold; -*- */
2 /*
3 Copyright (C) 2009-2017,2018 John E. Davis
4 
5 This file is part of the S-Lang Library.
6 
7 The S-Lang Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11 
12 The S-Lang Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 USA.
21 */
22 
23 #include "config.h"
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #ifdef HAVE_SYS_TYPES_H
28 # include <sys/types.h>
29 #endif
30 #ifdef HAVE_SYS_WAIT_H
31 # include <sys/wait.h>
32 #endif
33 #include <unistd.h>
34 
35 #include <string.h>
36 #include <slang.h>
37 
38 /* WCONTINUED is not defined on Hurd, in waitflags.h. */
39 #ifndef WCONTINUED
40 # define WCONTINUED 0		       /* Debian patch used 8, but 0 is safer */
41 #endif
42 SLANG_MODULE(fork);
43 
fork_intrinsic(void)44 static int fork_intrinsic (void)
45 {
46    int ret = fork ();
47    if (ret == -1)
48      (void) SLerrno_set_errno (errno);
49    return ret;
50 }
51 
52 typedef struct
53 {
54    int pid;
55    int exited;
56    int exit_status;
57    int signal;
58    int coredump;
59    int stopped;
60    int continued;
61 }
62 Waitpid_Type;
63 
64 static SLang_CStruct_Field_Type Waitpid_Struct [] =
65 {
66    MAKE_CSTRUCT_FIELD(Waitpid_Type, pid, "pid", SLANG_INT_TYPE, 0),
67    MAKE_CSTRUCT_FIELD(Waitpid_Type, exited, "exited", SLANG_INT_TYPE, 0),
68    MAKE_CSTRUCT_FIELD(Waitpid_Type, exit_status, "exit_status", SLANG_INT_TYPE, 0),
69    MAKE_CSTRUCT_FIELD(Waitpid_Type, signal, "signal", SLANG_INT_TYPE, 0),
70    MAKE_CSTRUCT_FIELD(Waitpid_Type, coredump, "coredump", SLANG_INT_TYPE, 0),
71    MAKE_CSTRUCT_FIELD(Waitpid_Type, stopped, "stopped", SLANG_INT_TYPE, 0),
72    MAKE_CSTRUCT_FIELD(Waitpid_Type, continued, "continued", SLANG_INT_TYPE, 0),
73    SLANG_END_CSTRUCT_TABLE
74 };
75 
waitpid_intrinsic(int * pid,int * options)76 static void waitpid_intrinsic (int *pid, int *options)
77 {
78    int status, ret;
79    Waitpid_Type s;
80 
81    while (-1 == (ret = waitpid ((pid_t)*pid, &status, *options)))
82      {
83 	if (errno == EINTR)
84 	  {
85 	     if (-1 != SLang_handle_interrupt ())
86 	       continue;
87 	  }
88 	(void) SLerrno_set_errno (errno);
89 	(void) SLang_push_null ();
90 	return;
91      }
92 
93    memset ((char *)&s, 0, sizeof(Waitpid_Type));
94    if (WIFEXITED(status))
95      {
96 	s.exited = 1;
97 	s.exit_status = WEXITSTATUS(status);
98      }
99    if (WIFSIGNALED(status))
100      {
101 	s.signal = WTERMSIG(status);
102 #ifdef WCOREDUMP
103 	s.coredump = WCOREDUMP(status) != 0;
104 #endif
105      }
106    if (WIFSTOPPED(status))
107      s.stopped = WSTOPSIG(status);
108 #ifdef WIFCONTINUED
109    s.continued = WIFCONTINUED(status);
110 #endif
111    s.pid = ret;
112    (void) SLang_push_cstruct ((VOID_STAR)&s, Waitpid_Struct);
113 }
114 
pop_argv(SLang_Array_Type ** atp)115 static char **pop_argv (SLang_Array_Type **atp)
116 {
117    SLang_Array_Type *at;
118    char **argv;
119    SLuindex_Type i, num, argc;
120    char **strp;
121 
122    *atp = NULL;
123 
124    if (-1 == SLang_pop_array_of_type (&at, SLANG_STRING_TYPE))
125      return NULL;
126 
127    num = at->num_elements;
128    if (NULL == (argv = (char **)SLmalloc ((num+1)*sizeof(char *))))
129      {
130 	SLang_free_array (at);
131 	return NULL;
132      }
133 
134    strp = (char **)at->data;
135    argc = 0;
136    for (i = 0; i < num; i++)
137      {
138 	if (strp[i] != NULL)
139 	  argv[argc++] = strp[i];
140      }
141    argv[argc] = NULL;
142    *atp = at;
143    return argv;
144 }
145 
146 #define CALL_EXECV	1
147 #define CALL_EXECVP	2
148 #define CALL_EXECVE	3
149 
call_what(int what,char * path,char ** argv,char ** envp)150 static int call_what (int what, char *path, char **argv, char **envp)
151 {
152    while (1)
153      {
154 	int ret;
155 
156 	switch (what)
157 	  {
158 	   case CALL_EXECV:
159 	     ret = execv (path, argv);
160 	     break;
161 
162 	   case CALL_EXECVP:
163 	     ret = execvp (path, argv);
164 	     break;
165 
166 	   case CALL_EXECVE:
167 	     ret = execve (path, argv, envp);
168 	     break;
169 	  }
170 
171 	if (ret == 0)
172 	  return 0;		       /* should never happen */
173 
174 	SLerrno_set_errno (errno);
175 	if (errno == EINTR)
176 	  {
177 	     if (-1 != SLang_handle_interrupt ())
178 	       continue;
179 	  }
180 	break;
181      }
182    return -1;
183 }
184 
exec_what(int what,int has_envp)185 static int exec_what (int what, int has_envp)
186 {
187    SLang_Array_Type *at_argv = NULL;
188    SLang_Array_Type *at_envp = NULL;
189    char **argv = NULL, **envp = NULL;
190    char *path = NULL;
191    int status = -1;
192 
193    if (has_envp)
194      {
195 	if (NULL == (envp = pop_argv (&at_envp)))
196 	  goto free_and_return;
197      }
198 
199    if (NULL == (argv = pop_argv (&at_argv)))
200      goto free_and_return;
201 
202    if (-1 == SLang_pop_slstring (&path))
203      goto free_and_return;
204 
205    status = call_what (what, path, argv, envp);
206 
207 free_and_return:
208 
209    if (path != NULL) SLang_free_slstring (path);
210    if (argv != NULL) SLfree ((char *)argv);
211    if (at_argv != NULL) SLang_free_array (at_argv);
212    if (envp != NULL) SLfree ((char *)envp);
213    if (at_envp != NULL) SLang_free_array (at_envp);
214    return status;
215 }
216 
execv_intrin(void)217 static int execv_intrin (void)
218 {
219    if (SLang_Num_Function_Args != 2)
220      SLang_verror (SL_Usage_Error, "Usage: ret = execv(path, argv[]);");
221 
222    return exec_what (CALL_EXECV, 0);
223 }
224 
execvp_intrin(void)225 static int execvp_intrin (void)
226 {
227    if (SLang_Num_Function_Args != 2)
228      SLang_verror (SL_Usage_Error, "Usage: ret = execvp(path, argv[]);");
229 
230    return exec_what (CALL_EXECVP, 0);
231 }
232 
execve_intrin(void)233 static int execve_intrin (void)
234 {
235    if (SLang_Num_Function_Args != 3)
236      SLang_verror (SL_Usage_Error, "Usage: ret = execve(path, argv[], env[]);");
237 
238    return exec_what (CALL_EXECVE, 1);
239 }
240 
_exit_intrin(int * s)241 static void _exit_intrin (int *s)
242 {
243    (void) fflush (stdout);
244    (void) fflush (stderr);
245    _exit (*s);
246 }
247 
pipe_intrin(void)248 static void pipe_intrin (void)
249 {
250    int fds[2];
251    SLFile_FD_Type *f0;
252    SLFile_FD_Type *f1;
253 
254    while (-1 == pipe (fds))
255      {
256 	if (errno == EINTR)
257 	  {
258 	     if (-1 != SLang_handle_interrupt ())
259 	       continue;
260 	  }
261 	SLerrno_set_errno (errno);
262 	SLang_verror (SL_OS_Error, "pipe failed: %s", SLerrno_strerror(errno));
263 	return;
264      }
265 
266    f0 = SLfile_create_fd ("*pipe*", fds[0]);
267    f1 = SLfile_create_fd ("*pipe*", fds[1]);
268    if ((NULL != f0) && (NULL != f1))
269      {
270 	/* Ignore errors and allow the free_fd routines to clean up */
271 	(void) SLfile_push_fd (f0);
272 	(void) SLfile_push_fd (f1);
273      }
274    SLfile_free_fd (f1);
275    SLfile_free_fd (f0);
276 }
277 
278 static SLang_IConstant_Type Module_IConstants [] =
279 {
280    MAKE_ICONSTANT("WNOHANG", WNOHANG),
281    MAKE_ICONSTANT("WUNTRACED", WUNTRACED),
282    MAKE_ICONSTANT("WCONTINUED", WCONTINUED),
283    SLANG_END_ICONST_TABLE
284 };
285 
286 static SLang_Intrin_Fun_Type Module_Intrinsics [] =
287 {
288    MAKE_INTRINSIC_0("fork", fork_intrinsic, SLANG_INT_TYPE),
289    MAKE_INTRINSIC_2("waitpid", waitpid_intrinsic, SLANG_VOID_TYPE, SLANG_INT_TYPE, SLANG_INT_TYPE),
290    MAKE_INTRINSIC_0("execv", execv_intrin, SLANG_INT_TYPE),
291    MAKE_INTRINSIC_0("execvp", execvp_intrin, SLANG_INT_TYPE),
292    MAKE_INTRINSIC_0("execve", execve_intrin, SLANG_INT_TYPE),
293    MAKE_INTRINSIC_0("pipe", pipe_intrin, SLANG_VOID_TYPE),
294    MAKE_INTRINSIC_1("_exit", _exit_intrin, SLANG_VOID_TYPE, SLANG_INT_TYPE),
295    SLANG_END_INTRIN_FUN_TABLE
296 };
297 
init_fork_module_ns(char * ns_name)298 int init_fork_module_ns (char *ns_name)
299 {
300    SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
301    if (ns == NULL)
302      return -1;
303 
304    if ((-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
305        || (-1 == SLns_add_iconstant_table (ns, Module_IConstants, NULL)))
306      return -1;
307 
308    return 0;
309 }
310 
311 /* This function is optional */
deinit_fork_module(void)312 void deinit_fork_module (void)
313 {
314 }
315