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

Reply via email to