View | Details | Raw Unified | Return to bug 237538 | Differences between
and this patch

Collapse All | Expand All

(-)b/usr.sbin/cron/cron/cron.h (-1 / +3 lines)
Lines 191-196 typedef struct _entry { Link Here
191
#define	NOT_UNTIL	0x10
191
#define	NOT_UNTIL	0x10
192
#define	SEC_RES		0x20
192
#define	SEC_RES		0x20
193
#define	INTERVAL	0x40
193
#define	INTERVAL	0x40
194
#define	DONT_LOG	0x80
195
#define	MAIL_WHEN_ERR	0x100
194
	time_t	lastrun;
196
	time_t	lastrun;
195
} entry;
197
} entry;
196
198
Lines 257-263 user *load_user(int, struct passwd *, char *), Link Here
257
entry		*load_entry(FILE *, void (*)(char *),
259
entry		*load_entry(FILE *, void (*)(char *),
258
				 struct passwd *, char **);
260
				 struct passwd *, char **);
259
261
260
FILE		*cron_popen(char *, char *, entry *);
262
FILE		*cron_popen(char *, char *, entry *, PID_T *);
261
263
262
264
263
				/* in the C tradition, we only create
265
				/* in the C tradition, we only create
(-)b/usr.sbin/cron/cron/do_command.c (-53 / +83 lines)
Lines 41-46 static const char rcsid[] = Link Here
41
static void		child_process(entry *, user *),
41
static void		child_process(entry *, user *),
42
			do_univ(user *);
42
			do_univ(user *);
43
43
44
static WAIT_T		wait_on_child(PID_T, char *);
44
45
45
void
46
void
46
do_command(e, u)
47
do_command(e, u)
Lines 94-100 child_process(e, u) Link Here
94
	int		stdin_pipe[2], stdout_pipe[2];
95
	int		stdin_pipe[2], stdout_pipe[2];
95
	register char	*input_data;
96
	register char	*input_data;
96
	char		*usernm, *mailto, *mailfrom;
97
	char		*usernm, *mailto, *mailfrom;
97
	int		children = 0;
98
	PID_T		jobpid, stdinjob, mailpid;
99
100
	register FILE	*mail;
101
	register int	bytes = 1;
102
	int		status = 0;
103
98
# if defined(LOGIN_CAP)
104
# if defined(LOGIN_CAP)
99
	struct passwd	*pwd;
105
	struct passwd	*pwd;
100
	login_cap_t *lc;
106
	login_cap_t *lc;
Lines 216-222 child_process(e, u) Link Here
216
222
217
	/* fork again, this time so we can exec the user's command.
223
	/* fork again, this time so we can exec the user's command.
218
	 */
224
	 */
219
	switch (vfork()) {
225
226
	switch (jobpid = vfork()) {
220
	case -1:
227
	case -1:
221
		log_it("CRON",getpid(),"error","can't vfork");
228
		log_it("CRON",getpid(),"error","can't vfork");
222
		exit(ERROR_EXIT);
229
		exit(ERROR_EXIT);
Lines 237-243 child_process(e, u) Link Here
237
		 * the actual user command shell was going to get and the
244
		 * the actual user command shell was going to get and the
238
		 * PID is part of the log message.
245
		 * PID is part of the log message.
239
		 */
246
		 */
240
		/*local*/{
247
		if ((e->flags & DONT_LOG) == 0) {
241
			char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
248
			char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
242
249
243
			log_it(usernm, getpid(), "CMD", x);
250
			log_it(usernm, getpid(), "CMD", x);
Lines 359-366 child_process(e, u) Link Here
359
		break;
366
		break;
360
	}
367
	}
361
368
362
	children++;
363
364
	/* middle process, child of original cron, parent of process running
369
	/* middle process, child of original cron, parent of process running
365
	 * the user's command.
370
	 * the user's command.
366
	 */
371
	 */
Lines 384-390 child_process(e, u) Link Here
384
	 * we would block here.  thus we must fork again.
389
	 * we would block here.  thus we must fork again.
385
	 */
390
	 */
386
391
387
	if (*input_data && fork() == 0) {
392
	if (*input_data && (stdinjob = fork()) == 0) {
388
		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
393
		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
389
		register int	need_newline = FALSE;
394
		register int	need_newline = FALSE;
390
		register int	escaped = FALSE;
395
		register int	escaped = FALSE;
Lines 440-447 child_process(e, u) Link Here
440
	 */
445
	 */
441
	close(stdin_pipe[WRITE_PIPE]);
446
	close(stdin_pipe[WRITE_PIPE]);
442
447
443
	children++;
444
445
	/*
448
	/*
446
	 * read output from the grandchild.  it's stderr has been redirected to
449
	 * read output from the grandchild.  it's stderr has been redirected to
447
	 * it's stdout, which has been redirected to our pipe.  if there is any
450
	 * it's stdout, which has been redirected to our pipe.  if there is any
Lines 462-471 child_process(e, u) Link Here
462
465
463
		ch = getc(in);
466
		ch = getc(in);
464
		if (ch != EOF) {
467
		if (ch != EOF) {
465
			register FILE	*mail;
466
			register int	bytes = 1;
467
			int		status = 0;
468
469
			Debug(DPROC|DEXT,
468
			Debug(DPROC|DEXT,
470
				("[%d] got data (%x:%c) from grandchild\n",
469
				("[%d] got data (%x:%c) from grandchild\n",
471
					getpid(), ch, ch))
470
					getpid(), ch, ch))
Lines 500-506 child_process(e, u) Link Here
500
				hostname[sizeof(hostname) - 1] = '\0';
499
				hostname[sizeof(hostname) - 1] = '\0';
501
				(void) snprintf(mailcmd, sizeof(mailcmd),
500
				(void) snprintf(mailcmd, sizeof(mailcmd),
502
					       MAILARGS, MAILCMD);
501
					       MAILARGS, MAILCMD);
503
				if (!(mail = cron_popen(mailcmd, "w", e))) {
502
				if (!(mail = cron_popen(mailcmd, "w", e, &mailpid))) {
504
					warn("%s", MAILCMD);
503
					warn("%s", MAILCMD);
505
					(void) _exit(ERROR_EXIT);
504
					(void) _exit(ERROR_EXIT);
506
				}
505
				}
Lines 538-565 child_process(e, u) Link Here
538
				if (mailto)
537
				if (mailto)
539
					putc(ch, mail);
538
					putc(ch, mail);
540
			}
539
			}
540
		} /*if data from grandchild*/
541
541
542
			/* only close pipe if we opened it -- i.e., we're
542
		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
543
			 * mailing...
544
			 */
545
543
546
			if (mailto) {
544
		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
547
				Debug(DPROC, ("[%d] closing pipe to mail\n",
545
	}
548
					getpid()))
546
549
				/* Note: the pclose will probably see
547
	/* wait for children to die.
550
				 * the termination of the grandchild
548
	 */
551
				 * in addition to the mail process, since
549
	if (jobpid > 0) {
552
				 * it (the grandchild) is likely to exit
550
		WAIT_T	waiter;
553
				 * after closing its stdout.
551
554
				 */
552
		waiter = wait_on_child(jobpid, "grandchild command job");
555
				status = cron_pclose(mail);
553
556
			}
554
		/* If everything went well, and -n was set, _and_ we have mail,
555
		 * we won't be mailing... so shoot the messenger!
556
		 */
557
		if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0
558
		    && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR
559
		    && mailto) {
560
			Debug(DPROC, ("[%d] %s executed successful, mail suppressed\n",
561
				getpid(), "grandchild command job"))
562
			kill(mailpid, SIGKILL);
563
			(void)fclose(mail);
564
			mailto = NULL;
565
		}
566
567
568
		/* only close pipe if we opened it -- i.e., we're
569
		 * mailing...
570
		 */
571
572
		if (mailto) {
573
			Debug(DPROC, ("[%d] closing pipe to mail\n",
574
				getpid()))
575
			/* Note: the pclose will probably see
576
			 * the termination of the grandchild
577
			 * in addition to the mail process, since
578
			 * it (the grandchild) is likely to exit
579
			 * after closing its stdout.
580
			 */
581
			status = cron_pclose(mail);
557
582
558
			/* if there was output and we could not mail it,
583
			/* if there was output and we could not mail it,
559
			 * log the facts so the poor user can figure out
584
			 * log the facts so the poor user can figure out
560
			 * what's going on.
585
			 * what's going on.
561
			 */
586
			 */
562
			if (mailto && status) {
587
			if (status) {
563
				char buf[MAX_TEMPSTR];
588
				char buf[MAX_TEMPSTR];
564
589
565
				snprintf(buf, sizeof(buf),
590
				snprintf(buf, sizeof(buf),
Lines 568-602 child_process(e, u) Link Here
568
					status);
593
					status);
569
				log_it(usernm, getpid(), "MAIL", buf);
594
				log_it(usernm, getpid(), "MAIL", buf);
570
			}
595
			}
596
		}
597
	}
571
598
572
		} /*if data from grandchild*/
599
	if (*input_data && stdinjob > 0)
600
		wait_on_child(stdinjob, "grandchild stdinjob");
601
}
573
602
574
		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
603
static WAIT_T
604
wait_on_child(PID_T childpid, char *name) {
605
	WAIT_T	waiter;
606
	PID_T	pid;
575
607
576
		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
608
	Debug(DPROC, ("[%d] waiting for %s (%d) to finish\n",
577
	}
609
		getpid(), name, childpid))
578
610
579
	/* wait for children to die.
611
#ifdef POSIX
580
	 */
612
  	while ((pid = waitpid(childpid, &waiter, 0)) < 0 && errno == EINTR)
581
	for (;  children > 0;  children--)
613
#else
582
	{
614
  	while ((pid = wait4(childpid, &waiter, 0, NULL)) < 0 && errno == EINTR)
583
		WAIT_T		waiter;
615
#endif
584
		PID_T		pid;
616
		;
585
617
586
		Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
618
	if (pid < OK)
587
			getpid(), children))
619
		return waiter;
588
		pid = wait(&waiter);
620
589
		if (pid < OK) {
621
	if (WIFEXITED(waiter))
590
			Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
622
		Debug(DPROC, ("[%d] %s (%d) finished, status=%04x",
591
				getpid()))
623
			getpid(), name, pid, WEXITSTATUS(waiter)))
592
			break;
624
593
		}
625
	if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
594
		Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
626
		Debug(DPROC, (", dumped core"))
595
			getpid(), pid, WEXITSTATUS(waiter)))
627
	Debug(DPROC, ("\n"))
596
		if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
628
597
			Debug(DPROC, (", dumped core"))
629
	return waiter;
598
		Debug(DPROC, ("\n"))
599
	}
600
}
630
}
601
631
602
632
(-)b/usr.sbin/cron/cron/popen.c (-1 / +5 lines)
Lines 55-63 static PID_T *pids; Link Here
55
static int fds;
55
static int fds;
56
56
57
FILE *
57
FILE *
58
cron_popen(program, type, e)
58
cron_popen(program, type, e, pidptr)
59
	char *program, *type;
59
	char *program, *type;
60
	entry *e;
60
	entry *e;
61
	PID_T *pidptr;
61
{
62
{
62
	register char *cp;
63
	register char *cp;
63
	FILE *iop;
64
	FILE *iop;
Lines 218-223 cron_popen(program, type, e) Link Here
218
		free((char *)argv[argc]);
219
		free((char *)argv[argc]);
219
	}
220
	}
220
#endif
221
#endif
222
223
	*pidptr = pid;
224
221
	return(iop);
225
	return(iop);
222
}
226
}
223
227
(-)b/usr.sbin/cron/crontab/crontab.5 (-1 / +28 lines)
Lines 198-204 Ranges or Link Here
198
lists of names are not allowed.
198
lists of names are not allowed.
199
.Pp
199
.Pp
200
The ``sixth'' field (the rest of the line) specifies the command to be
200
The ``sixth'' field (the rest of the line) specifies the command to be
201
run.
201
run. One or more command options may precede the command to modify
202
processing behavior.
202
The entire command portion of the line, up to a newline or %
203
The entire command portion of the line, up to a newline or %
203
character, will be executed by
204
character, will be executed by
204
.Pa /bin/sh
205
.Pa /bin/sh
Lines 211-216 Percent-signs (%) in the command, unless escaped with backslash Link Here
211
after the first % will be sent to the command as standard
212
after the first % will be sent to the command as standard
212
input.
213
input.
213
.Pp
214
.Pp
215
The following command options can be supplied:
216
.Bl -tag -width Ds
217
.It Fl n
218
No mail is sent after a successful run.
219
The execution output will only be mailed if the command exits with a non-zero
220
exit code.
221
The
222
.Fl n
223
option is an attempt to cure potentially copious volumes of mail coming from
224
.Xr cron 8 .
225
.It Fl q
226
Execution will not be logged.
227
.El
228
229
Duplicate options are not allowed.
230
.Pp
214
Note: The day of a command's execution can be specified by two
231
Note: The day of a command's execution can be specified by two
215
fields \(em day of month, and day of week.
232
fields \(em day of month, and day of week.
216
If both fields are
233
If both fields are
Lines 261-266 SHELL=/bin/sh Link Here
261
# mail any output to `paul', no matter whose crontab this is
278
# mail any output to `paul', no matter whose crontab this is
262
MAILTO=paul
279
MAILTO=paul
263
#
280
#
281
# run every minute, suppress logging
282
* * * * *       -q date
283
# run every minute, suppress logging and mail
284
* * * * *       -q -n echo "no-op"
264
# run five minutes after midnight, every day
285
# run five minutes after midnight, every day
265
5 0 * * *       $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
286
5 0 * * *       $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
266
# run at 2:15pm on the first of every month -- output mailed to paul
287
# run at 2:15pm on the first of every month -- output mailed to paul
Lines 314-319 All of the Link Here
314
.Sq @
335
.Sq @
315
directives that can appear in place of the first five fields
336
directives that can appear in place of the first five fields
316
are extensions.
337
are extensions.
338
.Pp
339
Command processing can be modified using command options. The
340
.Sq -q
341
option suppresses logging. The
342
.Sq -n
343
option does not mail on successful run.
317
.Sh AUTHORS
344
.Sh AUTHORS
318
.An Paul Vixie Aq Mt paul@vix.com
345
.An Paul Vixie Aq Mt paul@vix.com
319
.Sh BUGS
346
.Sh BUGS
(-)b/usr.sbin/cron/lib/entry.c (-1 / +50 lines)
Lines 35-41 static const char rcsid[] = Link Here
35
35
36
typedef	enum ecode {
36
typedef	enum ecode {
37
	e_none, e_minute, e_hour, e_dom, e_month, e_dow,
37
	e_none, e_minute, e_hour, e_dom, e_month, e_dow,
38
	e_cmd, e_timespec, e_username, e_group, e_mem
38
	e_cmd, e_timespec, e_username, e_group, e_option,
39
	e_mem
39
#ifdef LOGIN_CAP
40
#ifdef LOGIN_CAP
40
	, e_class
41
	, e_class
41
#endif
42
#endif
Lines 58-63 static char *ecodes[] = Link Here
58
		"bad time specifier",
59
		"bad time specifier",
59
		"bad username",
60
		"bad username",
60
		"bad group name",
61
		"bad group name",
62
		"bad option",
61
		"out of memory",
63
		"out of memory",
62
#ifdef LOGIN_CAP
64
#ifdef LOGIN_CAP
63
		"bad class name",
65
		"bad class name",
Lines 429-434 load_entry(file, error_func, pw, envp) Link Here
429
	}
431
	}
430
#endif
432
#endif
431
433
434
	Debug(DPARS, ("load_entry()...checking for command options\n"))
435
436
	ch = get_char(file);
437
438
	while (ch == '-') {
439
		Debug(DPARS|DEXT, ("load_entry()...expecting option\n"))
440
		switch(ch = get_char(file)) {
441
		case 'n':
442
			Debug(DPARS|DEXT, ("load_entry()...got MAIL_WHEN_ERR ('n') option\n"))
443
			/* only allow the user to set the option once */
444
			if ((e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR) {
445
				Debug(DPARS|DEXT, ("load_entry()...duplicate MAIL_WHEN_ERR ('n') option\n"))
446
				ecode = e_option;
447
				goto eof;
448
			}
449
			e->flags |= MAIL_WHEN_ERR;
450
			break;
451
		case 'q':
452
			Debug(DPARS|DEXT, ("load_entry()...got DONT_LOG ('q') option\n"))
453
			/* only allow the user to set the option once */
454
			if ((e->flags & DONT_LOG) == DONT_LOG) {
455
				Debug(DPARS|DEXT, ("load_entry()...duplicate DONT_LOG ('q') option\n"))
456
				ecode = e_option;
457
				goto eof;
458
			}
459
			e->flags |= DONT_LOG;
460
			break;
461
		default:
462
			Debug(DPARS|DEXT, ("load_entry()...invalid option '%c'\n", ch))
463
			ecode = e_option;
464
			goto eof;
465
		}
466
		ch = get_char(file);
467
		if (ch!='\t' && ch!=' ') {
468
			ecode = e_option;
469
			goto eof;
470
		}
471
472
		Skip_Blanks(ch, file)
473
		if (ch == EOF || ch == '\n') {
474
			ecode = e_cmd;
475
			goto eof;
476
		}
477
	}
478
479
	unget_char(ch, file);
480
432
	Debug(DPARS, ("load_entry()...about to parse command\n"))
481
	Debug(DPARS, ("load_entry()...about to parse command\n"))
433
482
434
	/* Everything up to the next \n or EOF is part of the command...
483
	/* Everything up to the next \n or EOF is part of the command...

Return to bug 237538