/* * Copyright 2001 by Paul Mattes. * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * x3270, c3270, s3270 and tcl3270 are distributed in the hope that they will * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE * for more details. */ /* * child.c * Child process output support. */ #include "globals.h" #include #include #include "popupsc.h" #include "utilc.h" #define CHILD_BUF 1024 static Boolean child_initted = False; static Boolean child_broken = False; static Boolean child_discarding = False; static int child_outpipe[2]; static int child_errpipe[2]; static struct pr3o { int fd; /* file descriptor */ unsigned long input_id; /* input ID */ unsigned long timeout_id; /* timeout ID */ int count; /* input count */ char buf[CHILD_BUF]; /* input buffer */ } child_stdout = { -1, 0L, 0L, 0 }, child_stderr = { -1, 0L, 0L, 0 }; static void child_output(void); static void child_error(void); static void child_otimeout(void); static void child_etimeout(void); static void child_dump(struct pr3o *p, Boolean is_err); static void init_child(void) { /* If initialization failed, there isn't much we can do. */ if (child_broken) return; /* Create pipes. */ if (pipe(child_outpipe) < 0) { popup_an_errno(errno, "pipe()"); child_broken = True; return; } if (pipe(child_errpipe) < 0) { popup_an_errno(errno, "pipe()"); close(child_outpipe[0]); close(child_outpipe[1]); child_broken = True; return; } /* Make sure their read ends are closed in child processes. */ (void) fcntl(child_outpipe[0], F_SETFD, 1); (void) fcntl(child_errpipe[0], F_SETFD, 1); #if defined(X3270_DISPLAY) /*[*/ /* Initialize the pop-ups. */ child_popup_init(); #endif /* Express interest in their output. */ child_stdout.fd = child_outpipe[0]; child_stdout.input_id = AddInput(child_outpipe[0], child_output); child_stderr.fd = child_errpipe[0]; child_stderr.input_id = AddInput(child_errpipe[0], child_error); child_initted = True; } /* * Fork a child process, with its stdout/stderr connected to pop-up windows. * Returns -1 for an error, 0 for child context, pid for parent context. */ int fork_child(void) { pid_t pid; /* Do initialization, if it hasn't been done already. */ if (!child_initted) init_child(); /* If output was being dumped, turn it back on now. */ if (child_discarding) child_discarding = False; /* Fork and rearrange output. */ pid = fork(); if (pid == 0) { /* Child. */ (void) dup2(child_outpipe[1], 1); (void) close(child_outpipe[1]); (void) dup2(child_errpipe[1], 2); (void) close(child_errpipe[1]); } return pid; } /* There's data from a child. */ static void child_data(struct pr3o *p, Boolean is_err) { int space; int nr; static char exitmsg[] = "Printer session exited"; /* * If we're discarding output, pull it in and drop it on the floor. */ if (child_discarding) { (void) read(p->fd, p->buf, CHILD_BUF); return; } /* Read whatever there is. */ space = CHILD_BUF - p->count - 1; nr = read(p->fd, p->buf + p->count, space); /* Handle read errors and end-of-file. */ if (nr < 0) { popup_an_errno(errno, "child session pipe input"); return; } if (nr == 0) { if (child_stderr.timeout_id != 0L) { /* * Append a termination error message to whatever the * child process said, and pop it up. */ p = &child_stderr; space = CHILD_BUF - p->count - 1; if (p->count && *(p->buf + p->count - 1) != '\n') { *(p->buf + p->count) = '\n'; p->count++; space--; } (void) strncpy(p->buf + p->count, exitmsg, space); p->count += strlen(exitmsg); if (p->count >= CHILD_BUF) p->count = CHILD_BUF - 1; child_dump(p, True); } else { popup_an_error(exitmsg); } return; } /* Add it to the buffer, and add a NULL. */ p->count += nr; p->buf[p->count] = '\0'; /* * If there's no more room in the buffer, dump it now. Otherwise, * give it a second to generate more output. */ if (p->count >= CHILD_BUF - 1) { child_dump(p, is_err); } else if (p->timeout_id == 0L) { p->timeout_id = AddTimeOut(1000, is_err? child_etimeout: child_otimeout); } } /* The child process has some output for us. */ static void child_output(void) { child_data(&child_stdout, False); } /* The child process has some error output for us. */ static void child_error(void) { child_data(&child_stderr, True); } /* Timeout from child output or error output. */ static void child_timeout(struct pr3o *p, Boolean is_err) { /* Forget the timeout ID. */ p->timeout_id = 0L; /* Dump the output. */ child_dump(p, is_err); } /* Timeout from child output. */ static void child_otimeout(void) { child_timeout(&child_stdout, False); } /* Timeout from child error output. */ static void child_etimeout(void) { child_timeout(&child_stderr, True); } /* * Abort button from child output. * Ignore output from the child process, so the user can abort it. */ void child_ignore_output(void) { /* Pitch pending output. */ child_stdout.count = 0; child_stderr.count = 0; /* Remove pendnig timeouts. */ if (child_stdout.timeout_id) { RemoveTimeOut(child_stdout.timeout_id); child_stdout.timeout_id = 0L; } if (child_stderr.timeout_id) { RemoveTimeOut(child_stderr.timeout_id); child_stderr.timeout_id = 0L; } /* Remember it. */ child_discarding = True; } /* Dump pending child process output. */ static void child_dump(struct pr3o *p, Boolean is_err) { if (p->count) { /* * Strip any trailing newline, and make sure the buffer is * NULL terminated. */ if (p->buf[p->count - 1] == '\n') p->buf[--(p->count)] = '\0'; else if (p->buf[p->count]) p->buf[p->count] = '\0'; /* Dump it and clear the buffer. */ #if defined(X3270_DISPLAY) /*[*/ popup_child_output(is_err, child_ignore_output, "%s", p->buf); #else /*][*/ action_output("%s", p->buf); #endif p->count = 0; } }