1 /* -*- mode: C; mode: fold; -*- */
2 /*
3 Copyright (C) 2007 John E. Davis
4
5 This file is part of the S-Lang grace Module
6
7 The S-Lang grace Module 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 grace Module 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
24 /* Note: grace_np is not used. This is because it contains
25 * system calls that do not check for errno==EINTR, which can occur
26 * if a system call is interrupted by a signal and the disposition of
27 * the signal is set to no restart the system calls.
28 */
29
30 #include "config.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <string.h>
38
39 #include <limits.h> /* OPEN_MAX */
40
41 #include <slang.h>
42
43 #include <signal.h>
44
45 #ifdef HAVE_SYS_TYPES_H
46 # include <sys/types.h>
47 #endif
48 #ifdef HAVE_SYS_WAIT_H
49 # include <sys/wait.h>
50 #endif
51
52 #include <errno.h>
53
54 #ifdef __cplusplus
55 extern "C"
56 {
57 #endif
58 SLANG_MODULE(grace);
59 #ifdef __cplusplus
60 }
61 #endif
62
63 #include "version.h"
64
65 #ifndef OPEN_MAX
66 # define OPEN_MAX 256
67 #endif
68
69 static int Grace_Type_Id = -1;
70
is_interrupt(int e,int run_hooks)71 static int is_interrupt (int e, int run_hooks)
72 {
73 int ok = 0;
74
75 #ifdef EINTR
76 if (e == EINTR) ok++;
77 #endif
78 #ifdef EAGAIN
79 if (e == EAGAIN) ok++;
80 #endif
81 #ifdef ERESTARTSYS
82 if (e == ERESTARTSYS) ok++;
83 #endif
84
85 if (ok)
86 {
87 if (run_hooks && (-1 == SLang_handle_interrupt ()))
88 return 0;
89 return 1;
90 }
91
92 SLerrno_set_errno (e);
93 return 0;
94 }
95
signal_safe_close(int fd,int run_hooks)96 static int signal_safe_close (int fd, int run_hooks)
97 {
98 while (-1 == close (fd))
99 {
100 if (is_interrupt (errno, run_hooks))
101 continue;
102
103 return -1;
104 }
105 return 0;
106 }
107
open_grace(int argc,char ** argv,pid_t * pidp)108 static int open_grace (int argc, char **argv, pid_t *pidp)
109 {
110 int fds[2];
111 pid_t pid;
112
113 while (-1 == pipe(fds))
114 {
115 if (0 == is_interrupt (errno, 1))
116 return -1;
117 }
118
119 while (-1 == (int)(pid = fork ()))
120 {
121 if (0 == is_interrupt (errno, 1))
122 {
123 signal_safe_close (fds[0], 1);
124 signal_safe_close (fds[1], 1);
125 return -1;
126 }
127 }
128
129 if (pid == 0)
130 {
131 /* child */
132 int i, fd;
133 char **args;
134 char buf[32];
135 /* Insert -dpipe N ... NULL into the arg list --- need 3 additional elements */
136 if (NULL == (args = (char **) SLmalloc ((argc+3)*sizeof(char **))))
137 {
138 (void) fprintf (stderr, "grace: out of memory\n");
139 _exit(127);
140 }
141 fd = fds[0];
142 (void) sprintf (buf, "%d", fd);
143 args[0] = argv[0];
144 args[1] = "-dpipe";
145 args[2] = buf;
146 for (i = 1; i < argc; i++)
147 args[2+i] = argv[i];
148 args[2+argc] = NULL;
149
150 /* Now close the unwanted open descriptors */
151 for (i = 3; i < OPEN_MAX; i++)
152 {
153 if (i != fd)
154 (void) signal_safe_close (i, 0);
155 }
156 (void) setpgid (0,0);
157 (void) execvp (args[0], args);
158 _exit (127);
159 }
160
161 /* parent */
162 signal_safe_close (fds[0], 1);
163 *pidp = pid;
164
165 return fds[1];
166 }
167
168 typedef struct _Grace_Type
169 {
170 pid_t pid;
171 int fd;
172 int kill_on_exit;
173 struct _Grace_Type *next;
174 }
175 Grace_Type;
176
177 static Grace_Type *Grace_Root = NULL;
178
perform_write(int fd,char * buf,int buflen)179 static int perform_write (int fd, char *buf, int buflen)
180 {
181 int len;
182
183 len = 0;
184 while (len < buflen)
185 {
186 int dlen;
187 while (-1 == (dlen = write (fd, buf + len, buflen - len)))
188 {
189 if (1 == is_interrupt (errno, 1))
190 continue;
191
192 return len;
193 }
194
195 len += dlen;
196 }
197
198 return len;
199 }
200
send_command(Grace_Type * g,char * cmd)201 static int send_command (Grace_Type *g, char *cmd)
202 {
203 int len;
204
205 if (g->fd == -1)
206 return -1;
207
208 len = (int) strlen (cmd);
209 if (len != perform_write (g->fd, cmd, len))
210 return -1;
211
212 return 0;
213 }
214
close_this_grace(Grace_Type * g,int reap_pid)215 static void close_this_grace (Grace_Type *g, int reap_pid)
216 {
217 if (g->fd != -1)
218 {
219 if (-1 == send_command (g, "exit\n"))
220 (void) kill (g->pid, SIGTERM);
221 (void) signal_safe_close (g->fd, 0);
222 if (reap_pid)
223 {
224 int status;
225 while (-1 == (int) waitpid (g->pid, &status, 0))
226 {
227 if (is_interrupt (errno, 1))
228 continue;
229 break; /* be a zombie */
230 }
231 }
232 }
233 SLfree ((char *)g);
234 }
235
close_grace(Grace_Type * g)236 static void close_grace (Grace_Type *g)
237 {
238 Grace_Type *g1;
239
240 g1 = Grace_Root;
241 if (g == g1)
242 {
243 Grace_Root = g->next;
244 }
245 else while (g1 != NULL)
246 {
247 if (g1->next != g)
248 {
249 g1 = g1->next;
250 continue;
251 }
252 g1->next = g->next;
253 break;
254 }
255 close_this_grace (g, 1);
256 }
257
cleanup_grace_jobs(void)258 static void cleanup_grace_jobs (void)
259 {
260 Grace_Type *g = Grace_Root;
261
262 while (g != NULL)
263 {
264 Grace_Type *gnext = g->next;
265 if (g->kill_on_exit)
266 close_this_grace (g, 0);
267 g = gnext;
268 }
269 }
270
allocate_grace_type(int fd,pid_t pid,int kill_on_exit)271 static Grace_Type *allocate_grace_type (int fd, pid_t pid, int kill_on_exit)
272 {
273 Grace_Type *g;
274
275 if (NULL == (g = (Grace_Type *) SLmalloc (sizeof (Grace_Type))))
276 return NULL;
277
278 g->pid = pid;
279 g->fd = fd;
280 g->next = Grace_Root;
281 g->kill_on_exit = kill_on_exit;
282 Grace_Root = g;
283 return g;
284 }
285
grace_from_fd(SLFile_FD_Type * f)286 static Grace_Type *grace_from_fd (SLFile_FD_Type *f)
287 {
288 Grace_Type *g;
289
290 if (-1 == SLfile_get_clientdata (f, Grace_Type_Id, (VOID_STAR *)(void *)&g))
291 {
292 SLang_verror (SL_TypeMismatch_Error, "File descriptor does not represent a Grace one");
293 return NULL;
294 }
295 return g;
296 }
297
298 /* This function gets called when the variable goes out of scope to close
299 * the grace_type attached to the descriptor. However, we want an
300 * explicit call to grace_close to close the descriptor. So here, we
301 * do nothing, unless kill_on_exit is non-zero.
302 */
close_grace_callback(VOID_STAR cd)303 static int close_grace_callback (VOID_STAR cd)
304 {
305 Grace_Type *g = (Grace_Type *)cd;
306
307 if (g == NULL)
308 return 0;
309
310 if (g->kill_on_exit)
311 {
312 close_grace (g);
313 return 0;
314 }
315 return -1;
316 }
317
close_grace_intrin(SLFile_FD_Type * f)318 static void close_grace_intrin (SLFile_FD_Type *f)
319 {
320 Grace_Type *g;
321
322 if (NULL == (g = grace_from_fd (f)))
323 return;
324
325 /* we are done with this descriptor so remove if from f */
326 (void) SLfile_set_clientdata (f, NULL, NULL, Grace_Type_Id);
327 close_grace (g);
328 }
329
grace_to_fd(int fd,pid_t pid,int kill_on_exit)330 static SLFile_FD_Type *grace_to_fd (int fd, pid_t pid, int kill_on_exit)
331 {
332 SLFile_FD_Type *f;
333 Grace_Type *g;
334
335 if (NULL == (g = allocate_grace_type (fd, pid, kill_on_exit)))
336 return NULL;
337
338 if (NULL == (f = SLfile_create_fd ("*grace*", fd)))
339 {
340 close_grace (g);
341 return NULL;
342 }
343
344 (void) SLfile_set_clientdata (f, NULL, (VOID_STAR)g, Grace_Type_Id);
345 (void) SLfile_set_close_method (f, close_grace_callback);
346
347 return f;
348 }
349
open_grace_intrin(void)350 static void open_grace_intrin (void)
351 {
352 SLFile_FD_Type *f;
353 SLang_Array_Type *at = NULL;
354 char *pgm;
355 int is_batch = 0;
356 int fd;
357 pid_t pid;
358
359 switch (SLang_Num_Function_Args)
360 {
361 case 1:
362 if (-1 == SLang_pop_array_of_type (&at, SLANG_STRING_TYPE))
363 return;
364 if (at->num_elements == 0)
365 {
366 SLang_verror (SL_INVALID_PARM, "grace_open: argument list is empty");
367 SLang_free_array (at);
368 return;
369 }
370
371 pgm = *(char **) at->data;
372 pgm = SLpath_basename (pgm);
373 if ((pgm != NULL) && (0 == strcmp (pgm, "gracebat")))
374 is_batch = 1;
375
376 fd = open_grace (at->num_elements, (char **)at->data, &pid);
377 SLang_free_array (at);
378 break;
379
380 default:
381 SLang_verror (SL_Usage_Error, "Usage: fd = grace_open (argv)");
382 return;
383 }
384
385 if (fd == -1)
386 {
387 SLang_push_null ();
388 return;
389 }
390
391 if (NULL == (f = grace_to_fd (fd, pid, is_batch)))
392 {
393 signal_safe_close (fd, 1);
394 return;
395 }
396
397 if (-1 == SLfile_push_fd (f))
398 close_grace_intrin (f);
399
400 SLfile_free_fd (f);
401 }
402
403 static SLang_Intrin_Fun_Type Module_Intrinsics [] =
404 {
405 MAKE_INTRINSIC_0("_grace_open", open_grace_intrin, SLANG_VOID_TYPE),
406 MAKE_INTRINSIC_1("_grace_close", close_grace_intrin, SLANG_VOID_TYPE, SLANG_FILE_FD_TYPE),
407 SLANG_END_INTRIN_FUN_TABLE
408 };
409
410 static SLang_Intrin_Var_Type Module_Variables [] =
411 {
412 MAKE_VARIABLE("_grace_module_version_string", &Module_Version_String, SLANG_STRING_TYPE, 1),
413 SLANG_END_INTRIN_VAR_TABLE
414 };
415
416 static SLang_IConstant_Type Module_IConstants [] =
417 {
418 MAKE_ICONSTANT("_grace_module_version", MODULE_VERSION_NUMBER),
419 SLANG_END_ICONST_TABLE
420 };
421
init_grace_module_ns(char * ns_name)422 int init_grace_module_ns (char *ns_name)
423 {
424 SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
425 if (ns == NULL)
426 return -1;
427
428 if (Grace_Type_Id == -1)
429 {
430 (void) SLfile_create_clientdata_id (&Grace_Type_Id);
431 (void) SLang_add_cleanup_function (cleanup_grace_jobs);
432 }
433
434 if (
435 (-1 == SLns_add_intrin_var_table (ns, Module_Variables, NULL))
436 || (-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
437 || (-1 == SLns_add_iconstant_table (ns, Module_IConstants, NULL))
438 )
439 return -1;
440
441 return 0;
442 }
443
444 /* This function is optional */
deinit_grace_module(void)445 void deinit_grace_module (void)
446 {
447 }
448