tag 155109 patch thanks #155109 - cron: sendmail can time out during extended interval of job execution http://bugs.debian.org./155109
I'm including patches for cron-3 and cron-4 which effect the saving of job output to a tempfile rather than a potentially-longlived pipe to sendmail. That also allowed me to output a warning when a job fails.
diff -u cron-3.0pl1/debian/changelog cron-3.0pl1/debian/changelog --- cron-3.0pl1/debian/changelog +++ cron-3.0pl1/debian/changelog @@ -1,3 +1,11 @@ +cron (3.0pl1-104.1) unstable; urgency=low + + * Non-maintainer upload. + * do_command.c: Call log_it rather than fprintf when execing a user command + fails (Closes: 443615). + + -- Justin Pryzby <[EMAIL PROTECTED]> Thu, 27 Mar 2008 09:04:26 -0400 + cron (3.0pl1-104) unstable; urgency=low * Discard errors from df in the standard daily cron task to prevent errors diff -u cron-3.0pl1/debian/NEWS cron-3.0pl1/debian/NEWS --- cron-3.0pl1/debian/NEWS +++ cron-3.0pl1/debian/NEWS @@ -1,3 +1,10 @@ +cron (3.0pl1-104.1) unstable; urgency=low + + Debian cron now outputs a warning to the logfile and to the mail when + commands fail. + + -- Justin Pryzby <[EMAIL PROTECTED]> Thu, 27 Mar 2008 15:29:32 -0400 + cron (3.0pl1-74) unstable; urgency=low The checksecurity script is no longer included with the cron package: @@ -9 +15,0 @@ - diff -u cron-3.0pl1/do_command.c cron-3.0pl1/do_command.c --- cron-3.0pl1/do_command.c +++ cron-3.0pl1/do_command.c @@ -111,12 +111,21 @@ } +/* + * CROND + * - cron (runs child_process); + * - cron (runs exec sh -c 'tab entry'); + * - cron (writes any %-style stdin to the command); + * - mail (popen writes any stdout to mailcmd); + */ + static void child_process(e, u) entry *e; user *u; { - int stdin_pipe[2], stdout_pipe[2]; + int stdin_pipe[2]; + FILE *out; register char *input_data; char *usernm, *mailto; int children = 0; @@ -179,7 +188,11 @@ /* create some pipes to talk to our future child */ pipe(stdin_pipe); /* child's stdin */ - pipe(stdout_pipe); /* child's stdout */ + /* child's stdout */ + if ((out=tmpfile())==NULL) { + log_it("CRON",getpid(),"error","create tmpfile"); + exit(ERROR_EXIT); + } /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? @@ -270,7 +283,6 @@ * appropriate circumstances. */ close(stdin_pipe[WRITE_PIPE]); - close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. @@ -278,14 +290,14 @@ /* Closes are unnecessary -- let dup2() do it */ /* close(STDIN) */; dup2(stdin_pipe[READ_PIPE], STDIN); - /* close(STDOUT) */; dup2(stdout_pipe[WRITE_PIPE], STDOUT); + dup2(fileno(out), STDOUT); /* close(STDERR)*/; dup2(STDOUT, STDERR); /* close the pipes we just dup'ed. The resources will remain. */ close(stdin_pipe[READ_PIPE]); - close(stdout_pipe[WRITE_PIPE]); + // Don't do this: fclose(out); /* set our login universe. Do this in the grandchild * so that the child can invoke /usr/lib/sendmail @@ -364,7 +376,6 @@ * grandchild process... */ close(stdin_pipe[READ_PIPE]); - close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified @@ -385,11 +396,6 @@ Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) - /* close the pipe we don't use, since we inherited it and - * are part of its reference count now. - */ - close(stdout_pipe[READ_PIPE]); - /* translation: * \% -> % * % -> \n @@ -439,165 +445,12 @@ Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) - /*local*/{ - register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); - register int ch = getc(in); - - if (ch != EOF) { - register FILE *mail; - register int bytes = 1; - int status = 0; - - Debug(DPROC|DEXT, - ("[%d] got data (%x:%c) from grandchild\n", - 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) { - register char **env; - char **jobenv = build_env(e->envp); - auto char mailcmd[MAX_COMMAND]; - auto char hostname[MAXHOSTNAMELEN]; - char *content_type = env_get("CONTENT_TYPE",jobenv), - *content_transfer_encoding = env_get("CONTENT_TRANSFER_ENCODING",jobenv); - - - (void) gethostname(hostname, MAXHOSTNAMELEN); - (void) snprintf(mailcmd, sizeof(mailcmd), - MAILARGS, MAILCMD, mailto); - if (!(mail = cron_popen(mailcmd, "w", e))) { - perror(MAILCMD); - (void) _exit(ERROR_EXIT); - } - fprintf(mail, "From: root (Cron Daemon)\n"); - fprintf(mail, "To: %s\n", mailto); - fprintf(mail, "Subject: Cron <[EMAIL PROTECTED]> %s\n", - usernm, first_word(hostname, "."), - e->cmd); -# if defined(MAIL_DATE) - fprintf(mail, "Date: %s\n", - arpadate(&StartTime)); -# endif /* MAIL_DATE */ - if ( content_type == 0L ) { - fprintf(mail, "Content-Type: text/plain; charset=%s\n", - cron_default_mail_charset - ); - } else { - /* user specified Content-Type header. - * disallow new-lines for security reasons - * (else users could specify arbitrary mail headers!) - */ - char *nl=content_type; - size_t ctlen = strlen(content_type); - - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_type+ctlen)) - ) *nl = ' '; - fprintf(mail,"Content-Type: %s\n", content_type); - } - if ( content_transfer_encoding != 0L ) { - char *nl=content_transfer_encoding; - size_t ctlen = strlen(content_transfer_encoding); - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_transfer_encoding+ctlen)) - ) *nl = ' '; - - fprintf(mail,"Content-Transfer-Encoding: %s\n", content_transfer_encoding); - } - - - for (env = e->envp; *env; env++) - fprintf(mail, "X-Cron-Env: <%s>\n", - *env); - fprintf(mail, "\n"); - - /* this was the first char from the pipe - */ - 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) - putc(ch, mail); - } - - /* only close pipe if we opened it -- i.e., we're - * mailing... - */ - - if (mailto) { - Debug(DPROC, ("[%d] closing pipe to mail\n", - 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) { - char buf[MAX_TEMPSTR]; - - snprintf(buf, MAX_TEMPSTR, - "mailed %d byte%s of output but got status 0x%04x\n", - bytes, (bytes==1)?"":"s", - status); - log_it(usernm, getpid(), "MAIL", buf); - } - - } /*if data from grandchild*/ - - if (log_level >= 2) { - char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); - - log_it(usernm, getpid(), "END", x); - free(x); - } - - Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) - - fclose(in); /* also closes stdout_pipe[READ_PIPE] */ - } - /* wait for children to die. */ + int status=0; for (; children > 0; children--) { + char msg[256]; WAIT_T waiter; PID_T pid; @@ -605,16 +458,159 @@ getpid(), children)) pid = wait(&waiter); if (pid < OK) { - Debug(DPROC, ("[%d] no more grandchildren--mail written?\n", - getpid())) + Debug(DPROC, ("[%d] no more grandchildren\n", getpid())) + break; + } + Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x\n", + getpid(), pid, waiter)) + + if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + status=waiter; + snprintf(msg, 256, "grandchild #%d failed with exit status %d", pid, WEXITSTATUS(waiter)); + log_it("CRON",getpid(),"error",msg); + } else if (WIFSIGNALED(waiter)) { + status=waiter; + snprintf(msg, 256, "grandchild #%d terminated by signal %d%s", + pid, WTERMSIG(waiter), + WCOREDUMP(waiter)?", dumped core":""); + log_it("CRON",getpid(),"error",msg); + } + } + +// Finally, send any output of the command to the mailer; also, alert +// the user if their job failed. Avoid popening the mailcmd until now +// since sendmail may time out, and to write info about the exit +// status. + + long pos; + + fseek(out, 0, SEEK_END); + pos=ftell(out); + fseek(out, 0, SEEK_SET); + + Debug(DPROC|DEXT, ("[%d] got %ld bytes data from grandchild tmpfile\n", + getpid(), (long)pos)) + if (pos==0 && status==0) return; + + // get name of recipient. + if (mailto==NULL /*|| !*mailto*/) mailto=usernm; + else if (!*mailto) mailto=NULL; + + register FILE *mail; + register int bytes = 1; + + register char **env; + char **jobenv = build_env(e->envp); + auto char mailcmd[MAX_COMMAND]; + auto char hostname[MAXHOSTNAMELEN]; + char *content_type = env_get("CONTENT_TYPE",jobenv), + *content_transfer_encoding = env_get("CONTENT_TRANSFER_ENCODING",jobenv); + + (void) gethostname(hostname, MAXHOSTNAMELEN); + (void) snprintf(mailcmd, sizeof(mailcmd), + MAILARGS, MAILCMD, mailto); + if (!(mail = cron_popen(mailcmd, "w", e))) { + perror(MAILCMD); + (void) _exit(ERROR_EXIT); + } + fprintf(mail, "From: root (Cron Daemon)\n"); + fprintf(mail, "To: %s\n", mailto); + fprintf(mail, "Subject: Cron <[EMAIL PROTECTED]> %s%s\n", + usernm, first_word(hostname, "."), + e->cmd, status?" (failed)":""); +# if defined(MAIL_DATE) + fprintf(mail, "Date: %s\n", + arpadate(&StartTime)); +# endif /* MAIL_DATE */ + if ( content_type == 0L ) { + fprintf(mail, "Content-Type: text/plain; charset=%s\n", + cron_default_mail_charset + ); + } else { + /* user specified Content-Type header. + * disallow new-lines for security reasons + * (else users could specify arbitrary mail headers!) + */ + char *nl=content_type; + size_t ctlen = strlen(content_type); + + while( (*nl != '\0') + && ((nl=strchr(nl,'\n')) != 0L) + && (nl < (content_type+ctlen)) + ) *nl = ' '; + fprintf(mail,"Content-Type: %s\n", content_type); + } + if ( content_transfer_encoding != 0L ) { + char *nl=content_transfer_encoding; + size_t ctlen = strlen(content_transfer_encoding); + while( (*nl != '\0') + && ((nl=strchr(nl,'\n')) != 0L) + && (nl < (content_transfer_encoding+ctlen)) + ) *nl = ' '; + + fprintf(mail,"Content-Transfer-Encoding: %s\n", content_transfer_encoding); + } + + for (env = e->envp; *env; env++) + fprintf(mail, "X-Cron-Env: <%s>\n", + *env); + fputc('\n', mail); + + if (WIFEXITED(status)) { + status=WEXITSTATUS(status); + fprintf(mail, "command failed with exit status %d\n\n", status); + } else if (WIFSIGNALED(status)) { + fprintf(mail, "command terminated by signal %d%s\n\n", + WTERMSIG(status), + WCOREDUMP(status)?", dumped core":""); + } + +// Finally, send any output of the command to the mailer; also, alert +// the user if their job failed. Avoid popening the mailcmd until now +// since sendmail may time out, and to write info about the exit +// status. + + for ( ; 1; ) { + char buf[1<<12]; + int ret, remain; + if ((ret=fread(buf, 1, sizeof(buf), out))==0) break; + for (remain=ret; remain!=0; ) { + ret=fwrite(buf, 1, remain, mail); + if (ret>0) { remain-=ret; continue; } + // XXX error break; } - Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x", - getpid(), pid, WEXITSTATUS(waiter))) - if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) - Debug(DPROC, (", dumped core")) - Debug(DPROC, ("\n")) } + + Debug(DPROC, ("[%d] closing pipe to mail\n", getpid())) + 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 (status) { + char buf[MAX_TEMPSTR]; + snprintf(buf, MAX_TEMPSTR, + "mailed %d byte%s of output; " + "but got status 0x%04x, " + "\n", + bytes, (bytes==1)?"":"s", status); + log_it(usernm, getpid(), "MAIL", buf); + } + + if (ferror(out)) { + log_it(usernm, getpid(), "MAIL", "stream error reading output"); + } + + fclose(out); + + if (log_level >= 2) { + char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); + log_it(usernm, getpid(), "END", x); + free(x); + } + #if defined(USE_PAM) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); retcode = pam_close_session(pamh, PAM_SILENT); diff -u cron-3.0pl1/popen.c cron-3.0pl1/popen.c --- cron-3.0pl1/popen.c +++ cron-3.0pl1/popen.c @@ -167,7 +167,7 @@ FILE *iop; { register int fdes; - int omask; + sigset_t omask, mask; WAIT_T stat_loc; PID_T pid; @@ -180,8 +180,12 @@ (void)fclose(iop); - omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); - while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) - ; - (void)sigsetmask(omask); + sigemptyset(&mask); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_BLOCK, &mask, &omask); + pid=waitpid(pids[fdes], &stat_loc, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); pids[fdes] = 0; - return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); + if (pid==-1 || !WIFEXITED(stat_loc)) return -1; + return WEXITSTATUS(stat_loc); }
Binary files cron-4.1.orig/cron and cron-4.1/cron differ Binary files cron-4.1.orig/cron.o and cron-4.1/cron.o differ Binary files cron-4.1.orig/crontab and cron-4.1/crontab differ Binary files cron-4.1.orig/crontab.o and cron-4.1/crontab.o differ Binary files cron-4.1.orig/database.o and cron-4.1/database.o differ diff -Nur cron-4.1.orig/do_command.c cron-4.1/do_command.c --- cron-4.1.orig/do_command.c 2008-03-27 16:12:51.000000000 -0400 +++ cron-4.1/do_command.c 2008-03-27 20:11:35.000000000 -0400 @@ -60,9 +60,18 @@ Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())) } +/* + * CROND + * - cron (runs child_process); + * - cron (runs exec sh -c 'tab entry'); + * - cron (writes any %-style stdin to the command); + * - mail (popen writes any stdout to mailcmd); + */ + static void child_process(entry *e, user *u) { - int stdin_pipe[2], stdout_pipe[2]; + int stdin_pipe[2]; + FILE *out; char *input_data, *usernm, *mailto; int children = 0; @@ -95,7 +104,11 @@ /* create some pipes to talk to our future child */ pipe(stdin_pipe); /* child's stdin */ - pipe(stdout_pipe); /* child's stdout */ + /* child's stdout */ + if ((out=tmpfile())==NULL) { + log_it("CRON",getpid(),"error","create tmpfile"); + exit(ERROR_EXIT); + } /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? @@ -172,7 +185,6 @@ * appropriate circumstances. */ close(stdin_pipe[WRITE_PIPE]); - close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. @@ -181,11 +193,9 @@ dup2(stdin_pipe[READ_PIPE], STDIN); close(stdin_pipe[READ_PIPE]); } - if (stdout_pipe[WRITE_PIPE] != STDOUT) { - dup2(stdout_pipe[WRITE_PIPE], STDOUT); - close(stdout_pipe[WRITE_PIPE]); - } + dup2(fileno(out), STDOUT); dup2(STDOUT, STDERR); + // Don't do this: fclose(out); /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. @@ -288,7 +298,6 @@ * grandchild process... */ close(stdin_pipe[READ_PIPE]); - close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified @@ -310,11 +319,6 @@ Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", (long)getpid())) - /* close the pipe we don't use, since we inherited it and - * are part of its reference count now. - */ - close(stdout_pipe[READ_PIPE]); - /* translation: * \% -> % * % -> \n @@ -366,128 +370,11 @@ 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; - 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]; - - gethostname(hostname, MAXHOSTNAMELEN); - if (strlens(MAILFMT, MAILARG, NULL) + 1 - >= sizeof mailcmd) { - fprintf(stderr, "mailcmd too long\n"); - (void) _exit(ERROR_EXIT); - } - (void)sprintf(mailcmd, MAILFMT, MAILARG); - if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { - perror(mailcmd); - (void) _exit(ERROR_EXIT); - } - fprintf(mail, "From: root (Cron Daemon)\n"); - fprintf(mail, "To: %s\n", mailto); - fprintf(mail, "Subject: Cron <[EMAIL PROTECTED]> %s\n", - usernm, first_word(hostname, "."), - e->cmd); -#ifdef MAIL_DATE - fprintf(mail, "Date: %s\n", - arpadate(&StartTime)); -#endif /*MAIL_DATE*/ - for (env = e->envp; *env; env++) - fprintf(mail, "X-Cron-Env: <%s>\n", - *env); - fprintf(mail, "\n"); - - /* this was the first char from the pipe - */ - 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) - 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) { - char buf[MAX_TEMPSTR]; - - sprintf(buf, - "mailed %d byte%s of output but got status 0x%04x\n", - bytes, (bytes==1)?"":"s", - status); - log_it(usernm, getpid(), "MAIL", buf); - } - - } /*if data from grandchild*/ - - Debug(DPROC, ("[%ld] got EOF from grandchild\n", - (long)getpid())) - - fclose(in); /* also closes stdout_pipe[READ_PIPE] */ - } - /* wait for children to die. */ + int status; for (; children > 0; children--) { + char msg[256]; WAIT_T waiter; PID_T pid; @@ -497,16 +384,127 @@ ; if (pid < OK) { Debug(DPROC, - ("[%ld] no more grandchildren--mail written?\n", + ("[%ld] no more grandchildren\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")) - } + (long)getpid(), (long)pid, waiter)) + + if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + status=waiter; + snprintf(msg, 256, "grandchild #%d failed with exit status %d", pid, WEXITSTATUS(waiter)); + log_it("CRON",getpid(),"error",msg); + } else if (WIFSIGNALED(waiter)) { + status=waiter; + snprintf(msg, 256, "grandchild #%d terminated by signal %d%s", + pid, WTERMSIG(waiter), + WCOREDUMP(waiter)?", dumped core":""); + log_it("CRON",getpid(),"error",msg); + } + } + + // Finally, send any output of the command to the mailer; also, alert + // the user if their job failed. Avoid popening the mailcmd until now + // since sendmail may time out, and to write info about the exit + // status. + + long pos; + + fseek(out, 0, SEEK_END); + pos=ftell(out); + fseek(out, 0, SEEK_SET); + + Debug(DPROC|DEXT, ("[%d] got %ld bytes data from grandchild tmpfile\n", + getpid(), (long)pos)) + if (pos==0 && status==0) return; + + // get name of recipient. + if (mailto==NULL /*|| !*mailto*/) mailto=usernm; + else if (!*mailto) mailto=NULL; + + register FILE *mail; + register int bytes = 1; + + register char **env; + auto char mailcmd[MAX_COMMAND]; + auto char hostname[MAXHOSTNAMELEN]; + + (void) gethostname(hostname, MAXHOSTNAMELEN); + (void)sprintf(mailcmd, MAILFMT, MAILARG); + // mailcmd, mailto); + if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { + perror(mailcmd); + (void) _exit(ERROR_EXIT); + } + fprintf(mail, "From: root (Cron Daemon)\n"); + fprintf(mail, "To: %s\n", mailto); + fprintf(mail, "Subject: Cron <[EMAIL PROTECTED]> %s%s\n", + usernm, first_word(hostname, "."), + e->cmd, status?" (failed)":""); + # if defined(MAIL_DATE) + fprintf(mail, "Date: %s\n", + arpadate(&StartTime)); + # endif /* MAIL_DATE */ + + for (env = e->envp; *env; env++) + fprintf(mail, "X-Cron-Env: <%s>\n", + *env); + fputc('\n', mail); + + if (WIFEXITED(status) && (status=WEXITSTATUS(status))) { + fprintf(mail, "command failed with exit status %d\n\n", status); + } else if (WIFSIGNALED(status)) { + fprintf(mail, "command terminated by signal %d%s\n\n", + WTERMSIG(status), + WCOREDUMP(status)?", dumped core":""); + } + + // Finally, send any output of the command to the mailer; also, alert + // the user if their job failed. Avoid popening the mailcmd until now + // since sendmail may time out, and to write info about the exit + // status. + + for ( ; 1; ) { + char buf[1<<12]; + int ret, remain; + if ((ret=fread(buf, 1, sizeof(buf), out))==0) break; + for (remain=ret; remain!=0; ) { + ret=fwrite(buf, 1, remain, mail); + if (ret>0) { remain-=ret; continue; } + // XXX error + break; + } + } + + Debug(DPROC, ("[%d] closing pipe to mail\n", getpid())) + 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 (status) { + char buf[MAX_TEMPSTR]; + snprintf(buf, MAX_TEMPSTR, + "mailed %d byte%s of output; " + "but got status 0x%04x, " + "\n", + bytes, (bytes==1)?"":"s", status); + log_it(usernm, getpid(), "MAIL", buf); + } + + if (ferror(out)) { + log_it(usernm, getpid(), "MAIL", "stream error reading output"); + } + + fclose(out); + + if ((e->flags & DONT_LOG) == 0) { + char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); + log_it(usernm, getpid(), "END", x); + free(x); + } } static int Binary files cron-4.1.orig/do_command.o and cron-4.1/do_command.o differ Binary files cron-4.1.orig/entry.o and cron-4.1/entry.o differ Binary files cron-4.1.orig/env.o and cron-4.1/env.o differ Binary files cron-4.1.orig/job.o and cron-4.1/job.o differ Binary files cron-4.1.orig/misc.o and cron-4.1/misc.o differ Binary files cron-4.1.orig/popen.o and cron-4.1/popen.o differ Binary files cron-4.1.orig/pw_dup.o and cron-4.1/pw_dup.o differ Binary files cron-4.1.orig/.swp and cron-4.1/.swp differ Binary files cron-4.1.orig/user.o and cron-4.1/user.o differ