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