static void fillSpecialObject(ObjectPtr object, void (*fn) (FILE *, char *), void *closure) { int rc; int filedes[2]; pid_t pid; sigset_t ss, old_mask; if (object->flags & OBJECT_INPROGRESS) return; rc = pipe(filedes); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't create pipe"); abortObject(object, 503, internAtomError(errno, "Couldn't create pipe")); return; } fflush(stdout); fflush(stderr); flushLog(); interestingSignals(&ss); do { rc = sigprocmask(SIG_BLOCK, &ss, &old_mask); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Sigprocmask failed"); abortObject(object, 503, internAtomError(errno, "Sigprocmask failed")); close(filedes[0]); close(filedes[1]); return; } pid = fork(); if (pid < 0) { do_log_error(L_ERROR, errno, "Couldn't fork"); abortObject(object, 503, internAtomError(errno, "Couldn't fork")); close(filedes[0]); close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); s_serverExit(); } return; } if (pid > 0) { SpecialRequestPtr request; close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); s_serverExit(); return; } request = malloc(sizeof(SpecialRequestRec)); if (request == NULL) { kill(pid, SIGTERM); close(filedes[0]); abortObject(object, 503, internAtom("Couldn't allocate request\n")); notifyObject(object); } else { request->buf = get_chunk(); if (request->buf == NULL) { kill(pid, SIGTERM); close(filedes[0]); free(request); abortObject(object, 503, internAtom ("Couldn't allocate request\n")); notifyObject(object); } } object->flags |= OBJECT_INPROGRESS; retainObject(object); request->object = object; request->fd = filedes[0]; request->pid = pid; request->offset = 0; do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE, specialRequestHandler, request); } else { close(filedes[0]); uninitEvents(); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) exit(1); if (filedes[1] != 1) dup2(filedes[1], 1); (*fn) (stdout, closure); exit(0); } }
int runRedirector(pid_t *pid_return, int *read_fd_return, int *write_fd_return) { int rc, rc2, status; pid_t pid; int filedes1[2], filedes2[2]; sigset_t ss, old_mask; assert(redirector); if(redirector_buffer == NULL) { redirector_buffer = malloc(REDIRECTOR_BUFFER_SIZE); if(redirector_buffer == NULL) return -errno; } rc = pipe(filedes1); if(rc < 0) { rc = -errno; goto fail1; } rc = pipe(filedes2); if(rc < 0) { rc = -errno; goto fail2; } fflush(stdout); fflush(stderr); flushLog(); interestingSignals(&ss); do { rc = sigprocmask(SIG_BLOCK, &ss, &old_mask); } while (rc < 0 && errno == EINTR); if(rc < 0) { rc = -errno; goto fail3; } pid = fork(); if(pid < 0) { rc = -errno; goto fail4; } if(pid > 0) { do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while(rc < 0 && errno == EINTR); if(rc < 0) { rc = -errno; goto fail4; } rc = setNonblocking(filedes1[1], 1); if(rc >= 0) rc = setNonblocking(filedes2[0], 1); if(rc < 0) { rc = -errno; goto fail4; } /* This is completely unnecesary -- if the redirector cannot be started, redirectorStreamHandler1 will get EPIPE straight away --, but it improves error messages somewhat. */ rc = waitpid(pid, &status, WNOHANG); if(rc > 0) { logExitStatus(status); rc = -EREDIRECTOR; goto fail4; } else if(rc < 0) { rc = -errno; goto fail4; } *read_fd_return = filedes2[0]; *write_fd_return = filedes1[1]; *pid_return = pid; /* This comes at the end so that the fail* labels can work */ close(filedes1[0]); close(filedes2[1]); } else { close(filedes1[1]); close(filedes2[0]); uninitEvents(); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if(rc < 0) exit(142); if(filedes1[0] != 0) dup2(filedes1[0], 0); if(filedes2[1] != 1) dup2(filedes2[1], 1); execlp(redirector->string, redirector->string, NULL); exit(142); /* NOTREACHED */ } return 1; fail4: do { rc2 = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while(rc2 < 0 && errno == EINTR); fail3: close(filedes2[0]); close(filedes2[1]); fail2: close(filedes1[0]); close(filedes1[1]); fail1: free(redirector_buffer); redirector_buffer = NULL; return rc; }
static void fillSpecialObject(ObjectPtr object, void (*fn)(FILE*, char*), void* closure) { int rc; int filedes[2]; pid_t pid; sigset_t ss, old_mask; if(object->flags & OBJECT_INPROGRESS) return; rc = pipe(filedes); if(rc < 0) { do_log_error(L_ERROR, errno, "Couldn't create pipe"); abortObject(object, 503, internAtomError(errno, "Couldn't create pipe")); return; } fflush(stdout); fflush(stderr); flushLog(); /* Block signals that we handle specially until the child can disable the handlers. */ interestingSignals(&ss); /* I'm a little confused. POSIX doesn't allow EINTR here, but I think that both Linux and SVR4 do. */ do { rc = sigprocmask(SIG_BLOCK, &ss, &old_mask); } while (rc < 0 && errno == EINTR); if(rc < 0) { do_log_error(L_ERROR, errno, "Sigprocmask failed"); abortObject(object, 503, internAtomError(errno, "Sigprocmask failed")); close(filedes[0]); close(filedes[1]); return; } pid = fork(); if(pid < 0) { do_log_error(L_ERROR, errno, "Couldn't fork"); abortObject(object, 503, internAtomError(errno, "Couldn't fork")); close(filedes[0]); close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if(rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); polipoExit(); } return; } if(pid > 0) { SpecialRequestPtr request; close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if(rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); polipoExit(); return; } request = malloc(sizeof(SpecialRequestRec)); if(request == NULL) { kill(pid, SIGTERM); close(filedes[0]); abortObject(object, 503, internAtom("Couldn't allocate request\n")); notifyObject(object); return; } else { request->buf = get_chunk(); if(request->buf == NULL) { kill(pid, SIGTERM); close(filedes[0]); free(request); abortObject(object, 503, internAtom("Couldn't allocate request\n")); notifyObject(object); return; } } object->flags |= OBJECT_INPROGRESS; retainObject(object); request->object = object; request->fd = filedes[0]; request->pid = pid; request->offset = 0; /* Under any sensible scheduler, the child will run before the parent. So no need for IO_NOTNOW. */ do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE, specialRequestHandler, request); } else { /* child */ close(filedes[0]); uninitEvents(); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if(rc < 0) exit(1); if(filedes[1] != 1) dup2(filedes[1], 1); (*fn)(stdout, closure); exit(0); } }