diff -uprk.orig vixie-cron-4.1.20040916.orig/usr.sbin/cron/atrun.c vixie-cron-4.1.20040916/usr.sbin/cron/atrun.c --- vixie-cron-4.1.20040916.orig/usr.sbin/cron/atrun.c 2004-12-18 20:35:14 +0300 +++ vixie-cron-4.1.20040916/usr.sbin/cron/atrun.c 2004-12-18 20:40:47 +0300 @@ -227,6 +227,24 @@ unlink_job(at_db *db, atjob *job) job->next->prev = job->prev; } +static void +sigchld_handler(int signo) { + for (;;) { + WAIT_T waiter; + PID_T pid = waitpid(-1, &waiter, WNOHANG); + + switch (pid) { + case -1: + if (errno == EINTR) + continue; + case 0: + return; + default: + break; + } + } +} + /* * Run the specified job contained in atfile. */ @@ -238,7 +256,7 @@ run_job(atjob *job, char *atfile) pid_t pid; long nuid, ngid; FILE *fp; - WAIT_T waiter; + struct sigaction sact; size_t nread; char *cp, *ep, mailto[MAX_UNAME], buf[BUFSIZ]; int fd, always_mail, retval = OK_EXIT; @@ -279,7 +297,14 @@ run_job(atjob *job, char *atfile) * We don't want the main cron daemon to wait for our children-- * we will do it ourselves via waitpid(). */ - (void) signal(SIGCHLD, SIG_DFL); + bzero((char *)&sact, sizeof sact); + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; +#ifdef SA_RESTART + sact.sa_flags |= SA_RESTART; +#endif + sact.sa_handler = sigchld_handler; + (void) sigaction(SIGCHLD, &sact, NULL); /* * Verify the user still exists and their account has not expired. @@ -601,25 +626,7 @@ run_job(atjob *job, char *atfile) fclose(fp); /* also closes output_pipe[READ_PIPE] */ /* Wait for grandchild to die. */ - Debug(DPROC, ("[%ld] waiting for grandchild (%ld) to finish\n", - (long)getpid(), (long)pid)) - for (;;) { - if (waitpid(pid, &waiter, 0) == -1) { - if (errno == EINTR) - continue; - Debug(DPROC, - ("[%ld] no grandchild process--mail written?\n", - (long)getpid())) - break; - } else { - Debug(DPROC, ("[%ld] grandchild (%ld) finished, status=%04x", - (long)getpid(), (long)pid, WEXITSTATUS(waiter))) - if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) - Debug(DPROC, (", dumped core")) - Debug(DPROC, ("\n")) - break; - } - } + sigchld_handler (0); run_job_end: #ifdef USE_PAM diff -uprk.orig vixie-cron-4.1.20040916.orig/usr.sbin/cron/cron.c vixie-cron-4.1.20040916/usr.sbin/cron/cron.c --- vixie-cron-4.1.20040916.orig/usr.sbin/cron/cron.c 2004-12-18 20:35:14 +0300 +++ vixie-cron-4.1.20040916/usr.sbin/cron/cron.c 2004-12-18 20:35:34 +0300 @@ -482,11 +482,10 @@ quit(int x) { static void sigchld_reaper(void) { - WAIT_T waiter; - PID_T pid; + for (;;) { + WAIT_T waiter; + PID_T pid = waitpid(-1, &waiter, WNOHANG); - do { - pid = waitpid(-1, &waiter, WNOHANG); switch (pid) { case -1: if (errno == EINTR) @@ -494,19 +493,19 @@ sigchld_reaper(void) { Debug(DPROC, ("[%ld] sigchld...no children\n", (long)getpid())) - break; + return; case 0: Debug(DPROC, ("[%ld] sigchld...no dead kids\n", (long)getpid())) - break; + return; default: Debug(DPROC, ("[%ld] sigchld...pid #%ld died, stat=%d\n", (long)getpid(), (long)pid, WEXITSTATUS(waiter))) break; } - } while (pid > 0); + } } static void diff -uprk.orig vixie-cron-4.1.20040916.orig/usr.sbin/cron/do_command.c vixie-cron-4.1.20040916/usr.sbin/cron/do_command.c --- vixie-cron-4.1.20040916.orig/usr.sbin/cron/do_command.c 2004-12-18 20:35:14 +0300 +++ vixie-cron-4.1.20040916/usr.sbin/cron/do_command.c 2004-12-18 20:35:34 +0300 @@ -63,11 +63,29 @@ do_command(entry *e, user *u) { Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())) } +static void +sigchld_handler(int signo) { + for (;;) { + WAIT_T waiter; + PID_T pid = waitpid(-1, &waiter, WNOHANG); + + switch (pid) { + case -1: + if (errno == EINTR) + continue; + case 0: + return; + default: + break; + } + } +} + static int child_process(entry *e, user *u) { int stdin_pipe[2], stdout_pipe[2]; char *input_data, *usernm, *mailto; - int children = 0; + struct sigaction sact; char **envp = e->envp; int retval = OK_EXIT; @@ -83,12 +101,14 @@ child_process(entry *e, user *u) { usernm = e->pwd->pw_name; mailto = env_get("MAILTO", envp); - /* our parent is watching for our death by catching SIGCHLD. we - * do not care to watch for our children's deaths this way -- we - * use wait() explicitly. so we have to reset the signal (which - * was inherited from the parent). - */ - (void) signal(SIGCHLD, SIG_DFL); + bzero((char *)&sact, sizeof sact); + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; +#ifdef SA_RESTART + sact.sa_flags |= SA_RESTART; +#endif + sact.sa_handler = sigchld_handler; + (void) sigaction(SIGCHLD, &sact, NULL); /* create some pipes to talk to our future child */ @@ -301,8 +321,6 @@ child_process(entry *e, user *u) { break; } - children++; - /* middle process, child of original cron, parent of process running * the user's command. */ @@ -385,8 +403,6 @@ child_process(entry *e, user *u) { */ close(stdin_pipe[WRITE_PIPE]); - children++; - /* * read output from the grandchild. it's stderr has been redirected to * it's stdout, which has been redirected to our pipe. if there is any @@ -513,26 +529,7 @@ child_process(entry *e, user *u) { /* wait for children to die. */ - for (; children > 0; children--) { - WAIT_T waiter; - PID_T pid; - - Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", - (long)getpid(), children)) - while ((pid = wait(&waiter)) < OK && errno == EINTR) - ; - if (pid < OK) { - Debug(DPROC, - ("[%ld] no more grandchildren--mail written?\n", - (long)getpid())) - break; - } - Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", - (long)getpid(), (long)pid, WEXITSTATUS(waiter))) - if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) - Debug(DPROC, (", dumped core")) - Debug(DPROC, ("\n")) - } + sigchld_handler (0); child_process_end: #ifdef USE_PAM