From jh@sirocco.sandstorm.net  Mon May  8 23:46:16 2006
Return-Path: <jh@sirocco.sandstorm.net>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id A6DC016A433
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  8 May 2006 23:46:16 +0000 (UTC)
	(envelope-from jh@sirocco.sandstorm.net)
Received: from sirocco.sandstorm.net (ip-69-33-111-75.bos.megapath.net [69.33.111.75])
	by mx1.FreeBSD.org (Postfix) with ESMTP id DDE1243D73
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  8 May 2006 23:46:15 +0000 (GMT)
	(envelope-from jh@sirocco.sandstorm.net)
Received: from sirocco.sandstorm.net (localhost [127.0.0.1])
	by sirocco.sandstorm.net (8.13.3/8.13.3) with ESMTP id k48Np6sr012138;
	Mon, 8 May 2006 19:51:06 -0400 (EDT)
	(envelope-from jh@sirocco.sandstorm.net)
Received: (from jh@localhost)
	by sirocco.sandstorm.net (8.13.3/8.13.3/Submit) id k48Np5fX012137;
	Mon, 8 May 2006 19:51:05 -0400 (EDT)
	(envelope-from jh)
Message-Id: <200605082351.k48Np5fX012137@sirocco.sandstorm.net>
Date: Mon, 8 May 2006 19:51:05 -0400 (EDT)
From: John Hood <jh@sirocco.sandstorm.net>
Reply-To: John Hood <jh@sirocco.sandstorm.net>, cgull@glup.org,
        ni@sirocco.sandstorm.net
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: cron fails quietly if /usr/sbin/sendmail is missing
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         97002
>Category:       bin
>Synopsis:       [patch] cron(8) fails quietly if /usr/sbin/sendmail is missing
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon May 08 23:50:10 GMT 2006
>Closed-Date:    
>Last-Modified:  Sat Jun 14 05:47:32 UTC 2008
>Originator:     John Hood
>Release:        FreeBSD 6.0-RELEASE i386
>Organization:
Sandstorm Enterprises, Inc.
>Environment:
System: FreeBSD faster.sandstorm.net 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov  3 09:36:13 UTC 2005     root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC  i386

>Description:

If cron's job-running child fails to exec /usr/sbin/sendmail and the
cron job writes to stdout, hijinks ensue (thanks for the phrase,
regis!)  The cron job writes on a pipe to a cron process, which
copies it to another pipe to what should be a sendmail.  If this exits
early and the pipe is closed, the cron child process gets killed with
a SIGPIPE.  Since cron delays running sendmail and doing any of this
until it's received something from the cron job, the cron job executes
before any of this happens.  If the cron job writes again on *its*
pipe, it gets a SIGPIPE too, and may fail mysteriously.  There's code
in cron to log mail failures, but the SIGPIPE prevents that code from
running.

>How-To-Repeat:

rm /usr/sbin/sendmail
echo '* * * * * tee /tmp/termcap < /etc/termcap' | crontab -
tail -f /var/log/cron
ls -l /etc/termcap /tmp/termcap

>Fix:

Patch to fix this problem by ignoring SIGPIPE, add logging of failure
to exec /usr/sbin/sendmail, and minor cleanup of logging functions to
get *that* to work.

diff -r -u /usr/src/usr.sbin/cron/Makefile.inc ./Makefile.inc
Only in ./cron: cron
Only in ./cron: cron.8.gz
Only in ./cron: cron.o
Only in ./cron: database.o
diff -r -u /usr/src/usr.sbin/cron/cron/do_command.c ./cron/do_command.c
--- /usr/src/usr.sbin/cron/cron/do_command.c	Sun May 16 15:29:33 2004
+++ ./cron/do_command.c	Mon May  8 17:12:02 2006
@@ -26,9 +26,6 @@
 #if defined(sequent)
 # include <sys/universe.h>
 #endif
-#if defined(SYSLOG)
-# include <syslog.h>
-#endif
 #if defined(LOGIN_CAP)
 # include <login_cap.h>
 #endif
@@ -187,9 +184,7 @@
 
 		/* that's the last thing we'll log.  close the log files.
 		 */
-#ifdef SYSLOG
-		closelog();
-#endif
+		log_close();
 
 		/* get new pgrp, void tty, etc.
 		 */
@@ -388,6 +383,8 @@
 			register FILE	*mail;
 			register int	bytes = 1;
 			int		status = 0;
+
+			signal(SIGPIPE, SIG_IGN);
 
 			Debug(DPROC|DEXT,
 				("[%d] got data (%x:%c) from grandchild\n",
Only in ./cron: do_command.o
Only in ./cron: job.o
diff -r -u /usr/src/usr.sbin/cron/cron/popen.c ./cron/popen.c
--- /usr/src/usr.sbin/cron/cron/popen.c	Tue Feb  5 21:00:07 2002
+++ ./cron/popen.c	Mon May  8 16:01:24 2006
@@ -35,9 +35,6 @@
 #include <sys/signal.h>
 #include <fcntl.h>
 #include <paths.h>
-#if defined(SYSLOG)
-# include <syslog.h>
-#endif
 #if defined(LOGIN_CAP)
 # include <login_cap.h>
 #endif
@@ -65,6 +62,7 @@
 	PID_T pid;
 	char *usernm;
 	char *argv[MAX_ARGS + 1];
+	int status;
 # if defined(LOGIN_CAP)
 	struct passwd	*pwd;
 	login_cap_t *lc;
@@ -112,7 +110,7 @@
 #endif
 
 	iop = NULL;
-	switch(pid = vfork()) {
+	switch(pid = fork()) {
 	case -1:			/* error */
 		(void)close(pdes[0]);
 		(void)close(pdes[1]);
@@ -120,10 +118,7 @@
 		/* NOTREACHED */
 	case 0:				/* child */
 		if (e != NULL) {
-#ifdef SYSLOG
-			closelog();
-#endif
-
+			log_close();
 			/* get new pgrp, void tty, etc.
 			 */
 			(void) setsid();
@@ -173,7 +168,7 @@
 				(void) endpwent();
 # endif
 				/* set our directory, uid and gid.  Set gid first,
-				 * since once we set uid, we've lost root privledges.
+				 * since once we set uid, we've lost root privileges.
 				 */
 				setgid(e->gid);
 # if defined(BSD)
@@ -193,6 +188,7 @@
 #else
 		execvp(argv[0], argv);
 #endif
+		log_it("CRON", getpid(), "CAN'T EXEC", argv[0]);
 		_exit(1);
 	}
 	/* parent; assume fdopen can't fail...  */
Only in ./cron: popen.o
Only in ./cron: user.o
Only in ./crontab: crontab
Only in ./crontab: crontab.1.gz
Only in ./crontab: crontab.5.gz
Only in ./crontab: crontab.o
Only in ./lib: entry.o
Only in ./lib: env.o
Only in ./lib: libcron.a
diff -r -u /usr/src/usr.sbin/cron/lib/misc.c ./lib/misc.c
--- /usr/src/usr.sbin/cron/lib/misc.c	Wed Feb  9 08:02:43 2005
+++ ./lib/misc.c	Mon May  8 14:37:33 2006
@@ -49,6 +49,11 @@
 
 static int		LogFD = ERR;
 
+#if defined(SYSLOG)
+static int		syslog_open = 0;
+#endif
+
+
 
 int
 strcmp_until(left, right, until)
@@ -459,10 +464,6 @@
 	register struct tm	*t = localtime(&now);
 #endif /*LOG_FILE*/
 
-#if defined(SYSLOG)
-	static int		syslog_open = 0;
-#endif
-
 #if defined(LOG_FILE)
 	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
 	 */
@@ -535,10 +536,18 @@
 
 void
 log_close() {
+#if defined(LOG_FILE)
 	if (LogFD != ERR) {
 		close(LogFD);
 		LogFD = ERR;
 	}
+#endif
+#ifdef SYSLOG
+	if (syslog_open) {
+		closelog();
+		syslog_open = 0;
+	}
+#endif
 }
 
 
Only in ./lib: misc.o

>Release-Note:
>Audit-Trail:

From: John Hood <jh@sandstorm.net>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/97002: cron fails quietly if /usr/sbin/sendmail is missing
Date: Tue, 9 May 2006 13:16:43 -0400

 The last vfork() remaining in do_command.c should be replaced with
 fork(), too.  The child is doing way too much for vfork to be
 appropriate any more.
 
   --jh

Adding to audit trail from misfiled PR bin/103287:

Date: Thu, 14 Sep 2006 21:19:11 -0400
From: John Hood <jh@sandstorm.net>
 
 I reported this cron bug a while ago.  Anybody?  Anybody?
 
 ISTR the cron job will get SIGPIPE on any failure of sendmail that
 causes its pipe to close early, not just failure-to-exec.
 
   --jh
>Unformatted:
