1 /*
2  * grace_np - a library for interfacing with Grace using pipes
3  *
4  * Copyright (c) 1997-1998 Henrik Seidel
5  * Copyright (c) 1999-2003 Grace Development Team
6  *
7  *
8  *                           All Rights Reserved
9  *
10  *    This library is free software; you can redistribute it and/or
11  *    modify it under the terms of the GNU Library General Public
12  *    License as published by the Free Software Foundation; either
13  *    version 2 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  *    Library General Public License for more details.
19  *
20  *    You should have received a copy of the GNU Library General Public
21  *    License along with this library; if not, write to the Free
22  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 
25 #include <config.h>
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <errno.h>
34 #ifdef HAVE_SYS_PARAM_H
35 #  include <sys/param.h>
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #ifdef HAVE_FCNTL_H
40 #  include <fcntl.h>
41 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #  include <sys/wait.h>
44 #endif
45 #include <limits.h>
46 #ifndef OPEN_MAX
47 #  define OPEN_MAX 256
48 #endif
49 
50 #include "grace_np.h"
51 
52 /* static global variables */
53 static char* buf = NULL;               /* global write buffer */
54 static int bufsize;                    /* size of the global write buffer */
55 static int bufsizeforce;               /* threshold for forcing a flush */
56 static int fd_pipe = -1;               /* file descriptor of the pipe */
57 static pid_t pid = (pid_t) -1;         /* pid of grace */
58 
59 /*
60  * notify grace when finished
61  */
62 static void
63 #ifdef HAVE_ON_EXIT
notify_grace_on_exit(int status,void * arg)64 notify_grace_on_exit(int status, void* arg)
65 #else
66 notify_grace_on_exit(void)
67 #endif
68 {
69     if (fd_pipe != -1) {
70         GraceClosePipe();
71     }
72 }
73 
74 /*
75  * default function for reporting errors
76  */
77 static void
GraceDefaultError(const char * msg)78 GraceDefaultError(const char *msg)
79 {
80     fprintf(stderr, "%s\n", msg);
81 }
82 
83 /*
84  * variable holding user function for reporting errors
85  */
86 static GraceErrorFunctionType error_function = GraceDefaultError;
87 
88 /*
89  * function for reporting system errors
90  */
91 static void
GracePerror(const char * prefix)92 GracePerror(const char *prefix)
93 {
94     char msg[1024];
95 
96 #ifdef HAVE_STRERROR
97     sprintf(msg, "%s: %s", prefix, strerror(errno));
98 #else
99 # ifdef HAVE_SYS_ERRLIST_IN_STDIO_H
100     sprintf(msg, "%s: %s", prefix, sys_errlist[errno]);
101 # else
102     if (errno == EPIPE) {
103         /* this one deserve special attention here */
104         sprintf(msg, "%s: Broken pipe", prefix);
105     } else {
106         sprintf(msg, "%s: System error (errno = %d)", prefix, errno);
107     }
108 # endif
109 #endif
110 
111     error_function(msg);
112 }
113 
114 /* Close the pipe and free the buffer */
115 static void
GraceCleanup(void)116 GraceCleanup(void)
117 {
118     if (fd_pipe != -1) {
119         if (close(fd_pipe) != 0) {
120             GracePerror("GraceCleanup");
121         }
122         fd_pipe = -1;
123     }
124 
125     free(buf);
126     buf = NULL;
127 }
128 
129 /*
130  * try to send data to grace (one pass only)
131  */
132 static int
GraceOneWrite(int left)133 GraceOneWrite(int left)
134 {
135     int written;
136 
137     written = write(fd_pipe, buf, left);
138 
139     if (written > 0) {
140 
141         left -= written;
142 
143         if (left > 0) {
144             /* move the remaining characters (and the final '\0') */
145 #ifdef HAVE_MEMMOVE
146             memmove(buf, buf + written, left + 1);
147 #else
148             bcopy(buf + written, buf, left + 1);
149 #endif
150         } else {
151             /* clear the buffer */
152             *buf = '\0';
153         }
154 
155     } else if (written < 0) {
156         if (errno == EPIPE) {
157             /* Grace has closed the pipe : we cannot write anymore */
158             GraceCleanup();
159         } else {
160             GracePerror("GraceOneWrite");
161         }
162         return (-1);
163     }
164 
165     return (left);
166 
167 }
168 
169 /*
170  * register a user function to report errors
171  */
172 GraceErrorFunctionType
GraceRegisterErrorFunction(GraceErrorFunctionType f)173 GraceRegisterErrorFunction(GraceErrorFunctionType f)
174 {
175     GraceErrorFunctionType old = error_function;
176     if (f != (GraceErrorFunctionType) NULL) {
177         error_function = f;
178     }
179     return old;
180 }
181 
182 static void
handle_sigchld(int signum)183 handle_sigchld(int signum)
184 {
185     int status;
186     pid_t retval;
187 
188     if (fd_pipe != -1) {
189         if (pid > 0) {
190             retval = waitpid(pid, &status, WNOHANG);
191             if (retval == pid) {
192                 /* Grace just died */
193                 pid = (pid_t) -1;
194 		close(fd_pipe);
195 		fd_pipe = -1;
196             }
197         }
198     }
199 }
200 
201 static int
_GraceFlush(void)202 _GraceFlush(void)
203 {
204     int loop, left;
205 
206     if (fd_pipe == -1) {
207         return (-1);
208     }
209 
210     left = strlen(buf);
211 
212     for (loop = 0; loop < 30; loop++) {
213         left = GraceOneWrite(left);
214         if (left < 0) {
215             return (-1);
216         } else if (left == 0) {
217             return (0);
218         }
219     }
220 
221     error_function("GraceFlush: ran into eternal loop");
222 
223     return (-1);
224 }
225 
226 
227 int
GraceOpenVA(char * exe,int bs,...)228 GraceOpenVA(char* exe, int bs, ...)
229 {
230     int i, fd[2];
231     char fd_number[4];
232     va_list ap;
233     char **arglist;
234     char *s;
235     int numarg;
236 
237     if (fd_pipe != -1) {
238         error_function("Grace subprocess already running");
239         return (-1);
240     }
241 
242     /* Make sure the buffer is not too small */
243     if (bs < 64) {
244         error_function("The buffer size in GraceOpenVA should be >= 64");
245         return (-1);
246     }
247     bufsize = bs;
248     bufsizeforce = bs / 2;
249 
250     /* make sure the grace subprocess is notified at the end */
251 #ifdef HAVE_ON_EXIT
252     on_exit(notify_grace_on_exit, NULL);
253 #else
254     atexit(notify_grace_on_exit);
255 #endif
256 
257     /* Don't exit on SIGPIPE */
258     signal(SIGPIPE, SIG_IGN);
259 
260     /* Clean up zombie prcesses */
261     signal(SIGCHLD, handle_sigchld);
262 
263     /* Make the pipe */
264     if (pipe(fd)) {
265         GracePerror("GraceOpenVA");
266         return (-1);
267     }
268 
269     /* Fork a subprocess for starting grace */
270     pid = fork();
271     if (pid == (pid_t) (-1)) {
272         GracePerror("GraceOpenVA");
273         close(fd[0]);
274         close(fd[1]);
275         return (-1);
276     }
277 
278     /* If we are the child, replace ourselves with grace */
279     if (pid == (pid_t) 0) {
280         for (i = 0; i < OPEN_MAX; i++) {
281             /* we close everything except stdin, stdout, stderr
282                and the read part of the pipe */
283             if (i != fd[0]         &&
284                 i != STDIN_FILENO  &&
285                 i != STDOUT_FILENO &&
286                 i != STDERR_FILENO) {
287                 close(i);
288             }
289         }
290 
291         /* build the argument list */
292         va_start(ap, bs);
293         numarg = 3;
294         arglist = malloc((numarg + 1)*SIZEOF_VOID_P);
295         arglist[0] = exe;
296         arglist[1] = "-dpipe";
297         sprintf(fd_number, "%d", fd[0]);
298         arglist[2] = fd_number;
299         while ((s = va_arg(ap, char *)) != NULL) {
300             numarg++;
301             arglist = realloc(arglist, (numarg + 1)*SIZEOF_VOID_P);
302             arglist[numarg - 1] = s;
303         }
304         arglist[numarg] = NULL;
305         va_end(ap);
306 
307         execvp(exe, arglist);
308 
309         /* if we get here execvp failed */
310         fprintf(stderr, "GraceOpenVA: Could not start %s\n", exe);
311 
312         _exit(EXIT_FAILURE);
313     }
314 
315     /* We are the parent -> keep the write part of the pipe
316        and allocate the write buffer */
317     buf = malloc(bufsize);
318     if (buf == NULL) {
319         error_function("GraceOpenVA: Not enough memory");
320         close(fd[0]);
321         close(fd[1]);
322         return (-1);
323     }
324     *buf = '\0';
325 
326     close(fd[0]);
327     fd_pipe = fd[1];
328 
329     return (0);
330 }
331 
332 int
GraceOpen(int bs)333 GraceOpen(int bs)
334 {
335     return GraceOpenVA("xmgrace", bs, "-nosafe", "-noask", NULL);
336 }
337 
338 int
GraceIsOpen(void)339 GraceIsOpen(void)
340 {
341     return (fd_pipe >= 0) ? 1 : 0;
342 }
343 
344 int
GraceClose(void)345 GraceClose(void)
346 {
347     if (fd_pipe == -1) {
348         error_function("No grace subprocess1");
349         return (-1);
350     }
351 
352     /* Tell grace to exit */
353     if (pid > 0) {
354         /* what the mess with globals... */
355         if ((GraceCommand ("exit") == -1 || _GraceFlush() == -1) && pid > 0) {
356             kill(pid, SIGTERM);
357         }
358     }
359 
360     GraceCleanup();
361 
362     return (0);
363 }
364 
365 int
GraceClosePipe(void)366 GraceClosePipe(void)
367 {
368     if (fd_pipe == -1) {
369         error_function("No grace subprocess2");
370         return (-1);
371     }
372 
373     /* Tell grace to close the pipe */
374     if (GraceCommand ("close") == -1 || _GraceFlush() == -1){
375         GraceCleanup();
376         return (-1);
377     }
378 
379     GraceCleanup();
380 
381     return (0);
382 }
383 
384 int
GraceFlush(void)385 GraceFlush(void)
386 {
387     if (fd_pipe == -1) {
388         error_function("No grace subprocess3");
389         return (-1);
390     }
391 
392     return _GraceFlush();
393 }
394 
395 int
GracePrintf(const char * fmt,...)396 GracePrintf(const char* fmt, ...)
397 {
398     va_list ap;
399     char* str;
400     int nchar;
401 
402     if (fd_pipe == -1) {
403         error_function("No grace subprocess4");
404         return (0);
405     }
406 
407     /* Allocate a new string buffer for the function arguments */
408     str = (char *) malloc ((size_t) bufsize);
409     if (str == (char *) NULL) {
410         error_function("GracePrintf: Not enough memory");
411         return (0);
412     }
413     /* Print to the string buffer according to the function arguments */
414     va_start (ap, fmt);
415 #if defined(HAVE_VSNPRINTF)
416     nchar = vsnprintf (str, bufsize - 2, fmt, ap);
417 #else
418     nchar = vsprintf (str, fmt, ap);
419 #endif
420     va_end (ap);
421     nchar++;               /* This is for the appended "\n" */
422     if (GraceCommand (str) == -1) {
423         nchar = 0;
424     }
425     free (str);
426     return (nchar);
427 }
428 
429 int
GraceCommand(const char * cmd)430 GraceCommand(const char* cmd)
431 {
432     int left;
433 
434     if (fd_pipe == -1) {
435         error_function("No grace subprocess5");
436         return (-1);
437     }
438 
439     /* Append the new string to the global write buffer */
440     if (strlen(buf) + strlen(cmd) + 2 > bufsize) {
441         error_function("GraceCommand: Buffer full");
442         return (-1);
443     }
444     strcat(buf, cmd);
445     strcat(buf, "\n");
446     left = strlen(buf);
447 
448     /* Try to send the global write buffer to grace */
449     left = GraceOneWrite(left);
450     if (left >= bufsizeforce) {
451         if (_GraceFlush() != 0) {
452             return (-1);
453         }
454     } else if (left < 0) {
455         return (-1);
456     }
457 
458     return (0);
459 }
460