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