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
	stdinjob = 0;
393
394
	if (*input_data && (stdinjob = fork()) == 0) {
388
		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
395
		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
389
		register int	need_newline = FALSE;
396
		register int	need_newline = FALSE;
390
		register int	escaped = FALSE;
397
		register int	escaped = FALSE;
Lines 440-447 child_process(e, u) Link Here
440
	 */
447
	 */
441
	close(stdin_pipe[WRITE_PIPE]);
448
	close(stdin_pipe[WRITE_PIPE]);
442
449
443
	children++;
444
445
	/*
450
	/*
446
	 * read output from the grandchild.  it's stderr has been redirected to
451
	 * 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
452
	 * it's stdout, which has been redirected to our pipe.  if there is any
Lines 462-471 child_process(e, u) Link Here
462
467
463
		ch = getc(in);
468
		ch = getc(in);
464
		if (ch != EOF) {
469
		if (ch != EOF) {
465
			register FILE	*mail;
466
			register int	bytes = 1;
467
			int		status = 0;
468
469
			Debug(DPROC|DEXT,
470
			Debug(DPROC|DEXT,
470
				("[%d] got data (%x:%c) from grandchild\n",
471
				("[%d] got data (%x:%c) from grandchild\n",
471
					getpid(), ch, ch))
472
					getpid(), ch, ch))
Lines 500-506 child_process(e, u) Link Here
500
				hostname[sizeof(hostname) - 1] = '\0';
501
				hostname[sizeof(hostname) - 1] = '\0';
501
				(void) snprintf(mailcmd, sizeof(mailcmd),
502
				(void) snprintf(mailcmd, sizeof(mailcmd),
502
					       MAILARGS, MAILCMD);
503
					       MAILARGS, MAILCMD);
503
				if (!(mail = cron_popen(mailcmd, "w", e))) {
504
				if (!(mail = cron_popen(mailcmd, "w", e, &mailpid))) {
504
					warn("%s", MAILCMD);
505
					warn("%s", MAILCMD);
505
					(void) _exit(ERROR_EXIT);
506
					(void) _exit(ERROR_EXIT);
506
				}
507
				}
Lines 538-565 child_process(e, u) Link Here
538
				if (mailto)
539
				if (mailto)
539
					putc(ch, mail);
540
					putc(ch, mail);
540
			}
541
			}
542
		} /*if data from grandchild*/
541
543
542
			/* only close pipe if we opened it -- i.e., we're
544
		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
543
			 * mailing...
545
544
			 */
546
		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
547
	}
548
549
	/* wait for children to die.
550
	 */
551
	if (jobpid > 0) {
552
		WAIT_T	waiter;
553
554
		waiter = wait_on_child(jobpid, "grandchild command job");
555
556
		/* If everything went well, and -n was set, _and_ we have mail,
557
		 * we won't be mailing... so shoot the messenger!
558
		 */
559
		if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0
560
		    && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR
561
		    && mailto) {
562
			Debug(DPROC, ("[%d] %s executed successful, mail suppressed\n",
563
				getpid(), "grandchild command job"))
564
			kill(mailpid, SIGKILL);
565
			(void)fclose(mail);
566
			mailto = NULL;
567
		}
545
568
546
			if (mailto) {
569
547
				Debug(DPROC, ("[%d] closing pipe to mail\n",
570
		/* only close pipe if we opened it -- i.e., we're
548
					getpid()))
571
		 * mailing...
549
				/* Note: the pclose will probably see
572
		 */
550
				 * the termination of the grandchild
573
551
				 * in addition to the mail process, since
574
		if (mailto) {
552
				 * it (the grandchild) is likely to exit
575
			Debug(DPROC, ("[%d] closing pipe to mail\n",
553
				 * after closing its stdout.
576
				getpid()))
554
				 */
577
			/* Note: the pclose will probably see
555
				status = cron_pclose(mail);
578
			 * the termination of the grandchild
556
			}
579
			 * in addition to the mail process, since
580
			 * it (the grandchild) is likely to exit
581
			 * after closing its stdout.
582
			 */
583
			status = cron_pclose(mail);
557
584
558
			/* if there was output and we could not mail it,
585
			/* if there was output and we could not mail it,
559
			 * log the facts so the poor user can figure out
586
			 * log the facts so the poor user can figure out
560
			 * what's going on.
587
			 * what's going on.
561
			 */
588
			 */
562
			if (mailto && status) {
589
			if (status) {
563
				char buf[MAX_TEMPSTR];
590
				char buf[MAX_TEMPSTR];
564
591
565
				snprintf(buf, sizeof(buf),
592
				snprintf(buf, sizeof(buf),
Lines 568-602 child_process(e, u) Link Here
568
					status);
595
					status);
569
				log_it(usernm, getpid(), "MAIL", buf);
596
				log_it(usernm, getpid(), "MAIL", buf);
570
			}
597
			}
598
		}
599
	}
571
600
572
		} /*if data from grandchild*/
601
	if (stdinjob > 0)
602
		wait_on_child(stdinjob, "grandchild stdinjob");
603
}
573
604
574
		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
605
static WAIT_T
606
wait_on_child(PID_T childpid, char *name) {
607
	WAIT_T	waiter;
608
	PID_T	pid;
575
609
576
		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
610
	Debug(DPROC, ("[%d] waiting for %s (%d) to finish\n",
577
	}
611
		getpid(), name, childpid))
578
612
579
	/* wait for children to die.
613
#ifdef POSIX
580
	 */
614
  	while ((pid = waitpid(childpid, &waiter, 0)) < 0 && errno == EINTR)
581
	for (;  children > 0;  children--)
615
#else
582
	{
616
  	while ((pid = wait4(childpid, &waiter, 0, NULL)) < 0 && errno == EINTR)
583
		WAIT_T		waiter;
617
#endif
584
		PID_T		pid;
618
		;
585
619
586
		Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
620
	if (pid < OK)
587
			getpid(), children))
621
		return waiter;
588
		pid = wait(&waiter);
622
589
		if (pid < OK) {
623
	Debug(DPROC, ("[%d] %s (%d) finished, status=%04x",
590
			Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
624
		getpid(), name, pid, WEXITSTATUS(waiter)))
591
				getpid()))
625
	if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
592
			break;
626
		Debug(DPROC, (", dumped core"))
593
		}
627
	Debug(DPROC, ("\n"))
594
		Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
628
595
			getpid(), pid, WEXITSTATUS(waiter)))
629
	return waiter;
596
		if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
597
			Debug(DPROC, (", dumped core"))
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 271-276 MAILTO=paul Link Here
271
5 4 * * sun     echo "run at 5 after 4 every sunday"
288
5 4 * * sun     echo "run at 5 after 4 every sunday"
272
# run at 5 minutes intervals, no matter how long it takes
289
# run at 5 minutes intervals, no matter how long it takes
273
@300		svnlite up /usr/src
290
@300		svnlite up /usr/src
291
# run every minute, suppress logging
292
* * * * *       -q date
293
# run every minute, only send mail if ping fails
294
* * * * *       -n ping -c 1 freebsd.org
274
.Ed
295
.Ed
275
.Sh SEE ALSO
296
.Sh SEE ALSO
276
.Xr crontab 1 ,
297
.Xr crontab 1 ,
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