Module Name: src Committed By: christos Date: Mon Sep 25 08:30:46 UTC 2017
Modified Files: src/external/bsd/cron/dist: do_command.c Log Message: Factor out the read and write data code from the huge child_process function. When we create a pipe to read data, restore the sigchld signal handler since pclose expects to wait for the child (to avoid spurious error reporting later). To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/external/bsd/cron/dist/do_command.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/external/bsd/cron/dist/do_command.c diff -u src/external/bsd/cron/dist/do_command.c:1.9 src/external/bsd/cron/dist/do_command.c:1.10 --- src/external/bsd/cron/dist/do_command.c:1.9 Thu Aug 17 04:53:00 2017 +++ src/external/bsd/cron/dist/do_command.c Mon Sep 25 04:30:46 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: do_command.c,v 1.9 2017/08/17 08:53:00 christos Exp $ */ +/* $NetBSD: do_command.c,v 1.10 2017/09/25 08:30:46 christos Exp $ */ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved @@ -25,7 +25,7 @@ #if 0 static char rcsid[] = "Id: do_command.c,v 1.9 2004/01/23 18:56:42 vixie Exp"; #else -__RCSID("$NetBSD: do_command.c,v 1.9 2017/08/17 08:53:00 christos Exp $"); +__RCSID("$NetBSD: do_command.c,v 1.10 2017/09/25 08:30:46 christos Exp $"); #endif #endif @@ -87,6 +87,190 @@ sigchld_handler(int signo) { } } +static void +write_data(char *volatile input_data, int *stdin_pipe, int *stdout_pipe) +{ + FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); + int need_newline = FALSE; + int escaped = FALSE; + int ch; + + Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", + (long)getpid())); + +#ifdef USE_PAM + cron_pam_child_close(); +#else + log_close(); +#endif + + /* close the pipe we don't use, since we inherited it and + * are part of its reference count now. + */ + (void)close(stdout_pipe[READ_PIPE]); + + /* translation: + * \% -> % + * % -> \n + * \x -> \x for all x != % + */ + while ((ch = *input_data++) != '\0') { + if (escaped) { + if (ch != '%') + (void)putc('\\', out); + } else { + if (ch == '%') + ch = '\n'; + } + + if (!(escaped = (ch == '\\'))) { + (void)putc(ch, out); + need_newline = (ch != '\n'); + } + } + if (escaped) + (void)putc('\\', out); + if (need_newline) + (void)putc('\n', out); + + /* close the pipe, causing an EOF condition. fclose causes + * stdin_pipe[WRITE_PIPE] to be closed, too. + */ + (void)fclose(out); + + Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", + (long)getpid())); +} + +static int +read_data(entry *e, const char *mailto, const char *usernm, char **envp, + int *stdout_pipe) +{ + FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); + FILE *mail = NULL; + int bytes = 1; + int status = 0; + int ch = getc(in); + int retval = 0; + sig_t oldchld = NULL; + + if (ch == EOF) + goto out; + + Debug(DPROC|DEXT, ("[%ld] got data (%x:%c) from grandchild\n", + (long)getpid(), ch, ch)); + + /* get name of recipient. this is MAILTO if set to a + * valid local username; USER otherwise. + */ + if (mailto) { + /* MAILTO was present in the environment + */ + if (!*mailto) { + /* ... but it's empty. set to NULL + */ + mailto = NULL; + } + } else { + /* MAILTO not present, set to USER. + */ + mailto = usernm; + } + + /* + * Unsafe, disable mailing. + */ + if (!safe_p(usernm, mailto)) + mailto = NULL; + + /* if we are supposed to be mailing, MAILTO will + * be non-NULL. only in this case should we set + * up the mail command and subjects and stuff... + */ + + if (mailto) { + char **env; + char mailcmd[MAX_COMMAND]; + char hostname[MAXHOSTNAMELEN + 1]; + + (void)gethostname(hostname, MAXHOSTNAMELEN); + if (strlens(MAILFMT, MAILARG, NULL) + 1 >= sizeof mailcmd) { + log_it(usernm, getpid(), "MAIL", "mailcmd too long"); + retval = ERROR_EXIT; + goto out; + } + (void)snprintf(mailcmd, sizeof(mailcmd), MAILFMT, MAILARG); + oldchld = signal(SIGCHLD, SIG_DFL); + if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { + log_itx(usernm, getpid(), "MAIL", + "cannot run `%s'", mailcmd); + (void) signal(SIGCHLD, oldchld); + retval = ERROR_EXIT; + goto out; + } + log_itx(usernm, getpid(), "MAIL", "opened pipe `%s'", mailcmd); + (void)fprintf(mail, "From: root (Cron Daemon)\n"); + (void)fprintf(mail, "To: %s\n", mailto); + (void)fprintf(mail, "Subject: Cron <%s@%s> %s\n", + usernm, hostname, e->cmd); + (void)fprintf(mail, "Auto-Submitted: auto-generated\n"); +#ifdef MAIL_DATE + (void)fprintf(mail, "Date: %s\n", arpadate(&StartTime)); +#endif /*MAIL_DATE*/ + for (env = envp; *env; env++) + (void)fprintf(mail, "X-Cron-Env: <%s>\n", *env); + (void)fprintf(mail, "\n"); + + /* this was the first char from the pipe + */ + (void)putc(ch, mail); + } + + /* we have to read the input pipe no matter whether + * we mail or not, but obviously we only write to + * mail pipe if we ARE mailing. + */ + + while (EOF != (ch = getc(in))) { + bytes++; + if (mailto) + (void)putc(ch, mail); + } + + /* only close pipe if we opened it -- i.e., we're + * mailing... + */ + + if (mailto) { + Debug(DPROC, ("[%ld] closing pipe to mail\n", (long)getpid())); + /* Note: the pclose will probably see + * the termination of the grandchild + * in addition to the mail process, since + * it (the grandchild) is likely to exit + * after closing its stdout. + */ + status = cron_pclose(mail); + (void) signal(SIGCHLD, oldchld); + } + + /* if there was output and we could not mail it, + * log the facts so the poor user can figure out + * what's going on. + */ + if (mailto && status) { + log_itx(usernm, getpid(), "MAIL", + "mailed %d byte%s of output to `%s' but" + " got status %#04x", bytes, + bytes == 1 ? "" : "s", mailto, status); + } + +out: + Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long)getpid())); + + (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */ + return retval; +} + extern char **environ; static int child_process(entry *e) { @@ -372,58 +556,17 @@ child_process(entry *e) { * we would block here. thus we must fork again. */ - if (*input_data && fork() == 0) { - FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); - int need_newline = FALSE; - int escaped = FALSE; - int ch; - - Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", - (long)getpid())); - -#ifdef USE_PAM - cron_pam_child_close(); -#else - log_close(); -#endif - - /* close the pipe we don't use, since we inherited it and - * are part of its reference count now. - */ - (void)close(stdout_pipe[READ_PIPE]); - - /* translation: - * \% -> % - * % -> \n - * \x -> \x for all x != % - */ - while ((ch = *input_data++) != '\0') { - if (escaped) { - if (ch != '%') - (void)putc('\\', out); - } else { - if (ch == '%') - ch = '\n'; - } - - if (!(escaped = (ch == '\\'))) { - (void)putc(ch, out); - need_newline = (ch != '\n'); - } + if (*input_data) { + switch (fork()) { + case 0: + write_data(input_data, stdin_pipe, stdout_pipe); + exit(EXIT_SUCCESS); + case -1: + retval = ERROR_EXIT; + goto child_process_end; + default: + break; } - if (escaped) - (void)putc('\\', out); - if (need_newline) - (void)putc('\n', out); - - /* close the pipe, causing an EOF condition. fclose causes - * stdin_pipe[WRITE_PIPE] to be closed, too. - */ - (void)fclose(out); - - Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", - (long)getpid())); - exit(0); } /* close the pipe to the grandkiddie's stdin, since its wicked uncle @@ -441,129 +584,10 @@ child_process(entry *e) { Debug(DPROC, ("[%ld] child reading output from grandchild\n", (long)getpid())); - /*local*/{ - FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); - int ch = getc(in); - - if (ch != EOF) { - FILE *mail = NULL; - int bytes = 1; - int status = 0; - - Debug(DPROC|DEXT, - ("[%ld] got data (%x:%c) from grandchild\n", - (long)getpid(), ch, ch)); - - /* get name of recipient. this is MAILTO if set to a - * valid local username; USER otherwise. - */ - if (mailto) { - /* MAILTO was present in the environment - */ - if (!*mailto) { - /* ... but it's empty. set to NULL - */ - mailto = NULL; - } - } else { - /* MAILTO not present, set to USER. - */ - mailto = usernm; - } - - /* if we are supposed to be mailing, MAILTO will - * be non-NULL. only in this case should we set - * up the mail command and subjects and stuff... - */ - - if (mailto && safe_p(usernm, mailto)) { - char **env; - char mailcmd[MAX_COMMAND]; - char hostname[MAXHOSTNAMELEN + 1]; - - (void)gethostname(hostname, MAXHOSTNAMELEN); - if (strlens(MAILFMT, MAILARG, NULL) + 1 - >= sizeof mailcmd) { - log_it(usernm, getpid(), "MAIL", - "mailcmd too long"); - retval = ERROR_EXIT; - goto child_process_end; - } - (void)snprintf(mailcmd, sizeof(mailcmd), - MAILFMT, MAILARG); - if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { - log_itx(usernm, getpid(), "MAIL", - "cannot run `%s'", mailcmd); - retval = ERROR_EXIT; - goto child_process_end; - } - (void)fprintf(mail, - "From: root (Cron Daemon)\n"); - (void)fprintf(mail, "To: %s\n", mailto); - (void)fprintf(mail, - "Subject: Cron <%s@%s> %s\n", - usernm, hostname, e->cmd); - (void)fprintf(mail, - "Auto-Submitted: auto-generated\n"); -#ifdef MAIL_DATE - (void)fprintf(mail, "Date: %s\n", - arpadate(&StartTime)); -#endif /*MAIL_DATE*/ - for (env = envp; *env; env++) - (void)fprintf(mail, - "X-Cron-Env: <%s>\n", *env); - (void)fprintf(mail, "\n"); - - /* this was the first char from the pipe - */ - (void)putc(ch, mail); - } - - /* we have to read the input pipe no matter whether - * we mail or not, but obviously we only write to - * mail pipe if we ARE mailing. - */ - - while (EOF != (ch = getc(in))) { - bytes++; - if (mailto) - (void)putc(ch, mail); - } - - /* only close pipe if we opened it -- i.e., we're - * mailing... - */ - - if (mailto) { - Debug(DPROC, ("[%ld] closing pipe to mail\n", - (long)getpid())); - /* Note: the pclose will probably see - * the termination of the grandchild - * in addition to the mail process, since - * it (the grandchild) is likely to exit - * after closing its stdout. - */ - status = cron_pclose(mail); - } - - /* if there was output and we could not mail it, - * log the facts so the poor user can figure out - * what's going on. - */ - if (mailto && status) { - log_itx(usernm, getpid(), "MAIL", - "mailed %d byte%s of output but got status" - " %#04x", bytes, bytes == 1 ? "" : "s", - status); - } - - } /*if data from grandchild*/ - - Debug(DPROC, ("[%ld] got EOF from grandchild\n", - (long)getpid())); + retval = read_data(e, mailto, usernm, envp, stdout_pipe); + if (retval) + goto child_process_end; - (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */ - } /* wait for children to die. */