xref: /freebsd/usr.sbin/cron/cron/do_command.c (revision 3e0f6b97b257a96f7275e4442204263e44b16686)
1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
2  * All rights reserved
3  *
4  * Distribute freely, except: don't remove my name from the source or
5  * documentation (don't take credit for my work), mark your changes (don't
6  * get me blamed for your possible bugs), don't alter or remove this
7  * notice.  May be sold if buildable source is provided to buyer.  No
8  * warrantee of any kind, express or implied, is included with this
9  * software; use at your own risk, responsibility for damages (if any) to
10  * anyone resulting from the use of this software rests entirely with the
11  * user.
12  *
13  * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
14  * I'll try to keep a version up to date.  I can be reached as follows:
15  * Paul Vixie          <paul@vix.com>          uunet!decwrl!vixie!paul
16  */
17 
18 #if !defined(lint) && !defined(LINT)
19 static char rcsid[] = "$FreeBSD$";
20 #endif
21 
22 
23 #include "cron.h"
24 #include <sys/signal.h>
25 #if defined(sequent)
26 # include <sys/universe.h>
27 #endif
28 #if defined(SYSLOG)
29 # include <syslog.h>
30 #endif
31 #if defined(LOGIN_CAP)
32 # include <login_cap.h>
33 #endif
34 
35 
36 static void		child_process __P((entry *, user *)),
37 			do_univ __P((user *));
38 
39 
40 void
41 do_command(e, u)
42 	entry	*e;
43 	user	*u;
44 {
45 	Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
46 		getpid(), e->cmd, u->name, e->uid, e->gid))
47 
48 	/* fork to become asynchronous -- parent process is done immediately,
49 	 * and continues to run the normal cron code, which means return to
50 	 * tick().  the child and grandchild don't leave this function, alive.
51 	 *
52 	 * vfork() is unsuitable, since we have much to do, and the parent
53 	 * needs to be able to run off and fork other processes.
54 	 */
55 	switch (fork()) {
56 	case -1:
57 		log_it("CRON",getpid(),"error","can't fork");
58 		break;
59 	case 0:
60 		/* child process */
61 		acquire_daemonlock(1);
62 		child_process(e, u);
63 		Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
64 		_exit(OK_EXIT);
65 		break;
66 	default:
67 		/* parent process */
68 		break;
69 	}
70 	Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
71 }
72 
73 
74 static void
75 child_process(e, u)
76 	entry	*e;
77 	user	*u;
78 {
79 	int		stdin_pipe[2], stdout_pipe[2];
80 	register char	*input_data;
81 	char		*usernm, *mailto;
82 	int		children = 0;
83 # if defined(LOGIN_CAP)
84 	struct passwd	*pwd = getpwuid(e->uid);
85 	login_cap_t	*lc = login_getclass(pwd);
86 # endif
87 
88 	Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
89 
90 	/* mark ourselves as different to PS command watchers by upshifting
91 	 * our program name.  This has no effect on some kernels.
92 	 */
93 	/*local*/{
94 		register char	*pch;
95 
96 		for (pch = ProgramName;  *pch;  pch++)
97 			*pch = MkUpper(*pch);
98 	}
99 
100 	/* discover some useful and important environment settings
101 	 */
102 	usernm = env_get("LOGNAME", e->envp);
103 	mailto = env_get("MAILTO", e->envp);
104 
105 #ifdef USE_SIGCHLD
106 	/* our parent is watching for our death by catching SIGCHLD.  we
107 	 * do not care to watch for our children's deaths this way -- we
108 	 * use wait() explictly.  so we have to disable the signal (which
109 	 * was inherited from the parent).
110 	 */
111 	(void) signal(SIGCHLD, SIG_IGN);
112 #else
113 	/* on system-V systems, we are ignoring SIGCLD.  we have to stop
114 	 * ignoring it now or the wait() in cron_pclose() won't work.
115 	 * because of this, we have to wait() for our children here, as well.
116 	 */
117 	(void) signal(SIGCLD, SIG_DFL);
118 #endif /*BSD*/
119 
120 	/* create some pipes to talk to our future child
121 	 */
122 	pipe(stdin_pipe);	/* child's stdin */
123 	pipe(stdout_pipe);	/* child's stdout */
124 
125 	/* since we are a forked process, we can diddle the command string
126 	 * we were passed -- nobody else is going to use it again, right?
127 	 *
128 	 * if a % is present in the command, previous characters are the
129 	 * command, and subsequent characters are the additional input to
130 	 * the command.  Subsequent %'s will be transformed into newlines,
131 	 * but that happens later.
132 	 *
133 	 * If there are escaped %'s, remove the escape character.
134 	 */
135 	/*local*/{
136 		register int escaped = FALSE;
137 		register int ch;
138 		register char *p;
139 
140 		for (input_data = p = e->cmd; ch = *input_data;
141 		     input_data++, p++) {
142 			if (p != input_data)
143 			    *p = ch;
144 			if (escaped) {
145 				if (ch == '%' || ch == '\\')
146 					*--p = ch;
147 				escaped = FALSE;
148 				continue;
149 			}
150 			if (ch == '\\') {
151 				escaped = TRUE;
152 				continue;
153 			}
154 			if (ch == '%') {
155 				*input_data++ = '\0';
156 				break;
157 			}
158 		}
159 		*p = '\0';
160 	}
161 
162 	/* fork again, this time so we can exec the user's command.
163 	 */
164 	switch (vfork()) {
165 	case -1:
166 		log_it("CRON",getpid(),"error","can't vfork");
167 		exit(ERROR_EXIT);
168 		/*NOTREACHED*/
169 	case 0:
170 		Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
171 			      getpid()))
172 
173 		/* write a log message.  we've waited this long to do it
174 		 * because it was not until now that we knew the PID that
175 		 * the actual user command shell was going to get and the
176 		 * PID is part of the log message.
177 		 */
178 		/*local*/{
179 			char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
180 
181 			log_it(usernm, getpid(), "CMD", x);
182 			free(x);
183 		}
184 
185 		/* that's the last thing we'll log.  close the log files.
186 		 */
187 #ifdef SYSLOG
188 		closelog();
189 #endif
190 
191 		/* get new pgrp, void tty, etc.
192 		 */
193 		(void) setsid();
194 
195 		/* close the pipe ends that we won't use.  this doesn't affect
196 		 * the parent, who has to read and write them; it keeps the
197 		 * kernel from recording us as a potential client TWICE --
198 		 * which would keep it from sending SIGPIPE in otherwise
199 		 * appropriate circumstances.
200 		 */
201 		close(stdin_pipe[WRITE_PIPE]);
202 		close(stdout_pipe[READ_PIPE]);
203 
204 		/* grandchild process.  make std{in,out} be the ends of
205 		 * pipes opened by our daddy; make stderr go to stdout.
206 		 */
207 		close(STDIN);	dup2(stdin_pipe[READ_PIPE], STDIN);
208 		close(STDOUT);	dup2(stdout_pipe[WRITE_PIPE], STDOUT);
209 		close(STDERR);	dup2(STDOUT, STDERR);
210 
211 		/* close the pipes we just dup'ed.  The resources will remain.
212 		 */
213 		close(stdin_pipe[READ_PIPE]);
214 		close(stdout_pipe[WRITE_PIPE]);
215 
216 		/* set our login universe.  Do this in the grandchild
217 		 * so that the child can invoke /usr/lib/sendmail
218 		 * without surprises.
219 		 */
220 		do_univ(u);
221 
222 # if defined(LOGIN_CAP)
223 		/* Set user's entire context, but skip the environment
224 		 * as cron provides a separate interface for this
225 		 */
226 		setusercontext(lc, pwd, e->uid, LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV));
227 		login_close(lc);
228 # else
229 		/* set our directory, uid and gid.  Set gid first, since once
230 		 * we set uid, we've lost root privledges.
231 		 */
232 		setgid(e->gid);
233 # if defined(BSD)
234 		initgroups(env_get("LOGNAME", e->envp), e->gid);
235 # endif
236 		setlogin(usernm);
237 		setuid(e->uid);		/* we aren't root after this... */
238 #endif
239 		chdir(env_get("HOME", e->envp));
240 
241 		/* exec the command.
242 		 */
243 		{
244 			char	*shell = env_get("SHELL", e->envp);
245 
246 # if DEBUGGING
247 			if (DebugFlags & DTEST) {
248 				fprintf(stderr,
249 				"debug DTEST is on, not exec'ing command.\n");
250 				fprintf(stderr,
251 				"\tcmd='%s' shell='%s'\n", e->cmd, shell);
252 				_exit(OK_EXIT);
253 			}
254 # endif /*DEBUGGING*/
255 			execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
256 			fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
257 			perror("execl");
258 			_exit(ERROR_EXIT);
259 		}
260 		break;
261 	default:
262 		/* parent process */
263 		break;
264 	}
265 
266 	children++;
267 
268 	/* middle process, child of original cron, parent of process running
269 	 * the user's command.
270 	 */
271 
272 	Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
273 
274 	/* close the ends of the pipe that will only be referenced in the
275 	 * grandchild process...
276 	 */
277 	close(stdin_pipe[READ_PIPE]);
278 	close(stdout_pipe[WRITE_PIPE]);
279 
280 	/*
281 	 * write, to the pipe connected to child's stdin, any input specified
282 	 * after a % in the crontab entry.  while we copy, convert any
283 	 * additional %'s to newlines.  when done, if some characters were
284 	 * written and the last one wasn't a newline, write a newline.
285 	 *
286 	 * Note that if the input data won't fit into one pipe buffer (2K
287 	 * or 4K on most BSD systems), and the child doesn't read its stdin,
288 	 * we would block here.  thus we must fork again.
289 	 */
290 
291 	if (*input_data && fork() == 0) {
292 		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
293 		register int	need_newline = FALSE;
294 		register int	escaped = FALSE;
295 		register int	ch;
296 
297 		Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
298 
299 		/* close the pipe we don't use, since we inherited it and
300 		 * are part of its reference count now.
301 		 */
302 		close(stdout_pipe[READ_PIPE]);
303 
304 		/* translation:
305 		 *	\% -> %
306 		 *	%  -> \n
307 		 *	\x -> \x	for all x != %
308 		 */
309 		while (ch = *input_data++) {
310 			if (escaped) {
311 				if (ch != '%')
312 					putc('\\', out);
313 			} else {
314 				if (ch == '%')
315 					ch = '\n';
316 			}
317 
318 			if (!(escaped = (ch == '\\'))) {
319 				putc(ch, out);
320 				need_newline = (ch != '\n');
321 			}
322 		}
323 		if (escaped)
324 			putc('\\', out);
325 		if (need_newline)
326 			putc('\n', out);
327 
328 		/* close the pipe, causing an EOF condition.  fclose causes
329 		 * stdin_pipe[WRITE_PIPE] to be closed, too.
330 		 */
331 		fclose(out);
332 
333 		Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
334 		exit(0);
335 	}
336 
337 	/* close the pipe to the grandkiddie's stdin, since its wicked uncle
338 	 * ernie back there has it open and will close it when he's done.
339 	 */
340 	close(stdin_pipe[WRITE_PIPE]);
341 
342 	children++;
343 
344 	/*
345 	 * read output from the grandchild.  it's stderr has been redirected to
346 	 * it's stdout, which has been redirected to our pipe.  if there is any
347 	 * output, we'll be mailing it to the user whose crontab this is...
348 	 * when the grandchild exits, we'll get EOF.
349 	 */
350 
351 	Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
352 
353 	/*local*/{
354 		register FILE	*in = fdopen(stdout_pipe[READ_PIPE], "r");
355 		register int	ch = getc(in);
356 
357 		if (ch != EOF) {
358 			register FILE	*mail;
359 			register int	bytes = 1;
360 			int		status = 0;
361 
362 			Debug(DPROC|DEXT,
363 				("[%d] got data (%x:%c) from grandchild\n",
364 					getpid(), ch, ch))
365 
366 			/* get name of recipient.  this is MAILTO if set to a
367 			 * valid local username; USER otherwise.
368 			 */
369 			if (mailto) {
370 				/* MAILTO was present in the environment
371 				 */
372 				if (!*mailto) {
373 					/* ... but it's empty. set to NULL
374 					 */
375 					mailto = NULL;
376 				}
377 			} else {
378 				/* MAILTO not present, set to USER.
379 				 */
380 				mailto = usernm;
381 			}
382 
383 			/* if we are supposed to be mailing, MAILTO will
384 			 * be non-NULL.  only in this case should we set
385 			 * up the mail command and subjects and stuff...
386 			 */
387 
388 			if (mailto) {
389 				register char	**env;
390 				auto char	mailcmd[MAX_COMMAND];
391 				auto char	hostname[MAXHOSTNAMELEN];
392 
393 				(void) gethostname(hostname, MAXHOSTNAMELEN);
394 				(void) snprintf(mailcmd, sizeof(mailcmd),
395 					       MAILARGS, MAILCMD);
396 				if (!(mail = cron_popen(mailcmd, "w"))) {
397 					perror(MAILCMD);
398 					(void) _exit(ERROR_EXIT);
399 				}
400 				fprintf(mail, "From: root (Cron Daemon)\n");
401 				fprintf(mail, "To: %s\n", mailto);
402 				fprintf(mail, "Subject: Cron <%s@%s> %s\n",
403 					usernm, first_word(hostname, "."),
404 					e->cmd);
405 # if defined(MAIL_DATE)
406 				fprintf(mail, "Date: %s\n",
407 					arpadate(&TargetTime));
408 # endif /* MAIL_DATE */
409 				for (env = e->envp;  *env;  env++)
410 					fprintf(mail, "X-Cron-Env: <%s>\n",
411 						*env);
412 				fprintf(mail, "\n");
413 
414 				/* this was the first char from the pipe
415 				 */
416 				putc(ch, mail);
417 			}
418 
419 			/* we have to read the input pipe no matter whether
420 			 * we mail or not, but obviously we only write to
421 			 * mail pipe if we ARE mailing.
422 			 */
423 
424 			while (EOF != (ch = getc(in))) {
425 				bytes++;
426 				if (mailto)
427 					putc(ch, mail);
428 			}
429 
430 			/* only close pipe if we opened it -- i.e., we're
431 			 * mailing...
432 			 */
433 
434 			if (mailto) {
435 				Debug(DPROC, ("[%d] closing pipe to mail\n",
436 					getpid()))
437 				/* Note: the pclose will probably see
438 				 * the termination of the grandchild
439 				 * in addition to the mail process, since
440 				 * it (the grandchild) is likely to exit
441 				 * after closing its stdout.
442 				 */
443 				status = cron_pclose(mail);
444 			}
445 
446 			/* if there was output and we could not mail it,
447 			 * log the facts so the poor user can figure out
448 			 * what's going on.
449 			 */
450 			if (mailto && status) {
451 				char buf[MAX_TEMPSTR];
452 
453 				snprintf(buf, sizeof(buf),
454 			"mailed %d byte%s of output but got status 0x%04x\n",
455 					bytes, (bytes==1)?"":"s",
456 					status);
457 				log_it(usernm, getpid(), "MAIL", buf);
458 			}
459 
460 		} /*if data from grandchild*/
461 
462 		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
463 
464 		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
465 	}
466 
467 	/* wait for children to die.
468 	 */
469 	for (;  children > 0;  children--)
470 	{
471 		WAIT_T		waiter;
472 		PID_T		pid;
473 
474 		Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
475 			getpid(), children))
476 		pid = wait(&waiter);
477 		if (pid < OK) {
478 			Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
479 				getpid()))
480 			break;
481 		}
482 		Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
483 			getpid(), pid, WEXITSTATUS(waiter)))
484 		if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
485 			Debug(DPROC, (", dumped core"))
486 		Debug(DPROC, ("\n"))
487 	}
488 }
489 
490 
491 static void
492 do_univ(u)
493 	user	*u;
494 {
495 #if defined(sequent)
496 /* Dynix (Sequent) hack to put the user associated with
497  * the passed user structure into the ATT universe if
498  * necessary.  We have to dig the gecos info out of
499  * the user's password entry to see if the magic
500  * "universe(att)" string is present.
501  */
502 
503 	struct	passwd	*p;
504 	char	*s;
505 	int	i;
506 
507 	p = getpwuid(u->uid);
508 	(void) endpwent();
509 
510 	if (p == NULL)
511 		return;
512 
513 	s = p->pw_gecos;
514 
515 	for (i = 0; i < 4; i++)
516 	{
517 		if ((s = strchr(s, ',')) == NULL)
518 			return;
519 		s++;
520 	}
521 	if (strcmp(s, "universe(att)"))
522 		return;
523 
524 	(void) universe(U_ATT);
525 #endif
526 }
527