]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/cron/cron/do_command.c
zfs: merge openzfs/zfs@797f55ef1
[FreeBSD/FreeBSD.git] / usr.sbin / cron / cron / do_command.c
1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
2  * All rights reserved
3  */
4
5 /*
6  * Copyright (c) 1997 by Internet Software Consortium
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
13  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
15  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
19  * SOFTWARE.
20  */
21
22 #if !defined(lint) && !defined(LINT)
23 static const char rcsid[] =
24     "$Id: do_command.c,v 1.3 1998/08/14 00:32:39 vixie Exp $";
25 #endif
26
27 #include "cron.h"
28 #if defined(LOGIN_CAP)
29 # include <login_cap.h>
30 #endif
31 #ifdef PAM
32 # include <security/pam_appl.h>
33 # include <security/openpam.h>
34 #endif
35
36 static void             child_process(entry *, user *);
37 static WAIT_T           wait_on_child(PID_T, const char *);
38
39 extern char     *environ;
40
41 void
42 do_command(entry *e, user *u)
43 {
44         pid_t pid;
45
46         Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
47                 getpid(), e->cmd, u->name, e->uid, e->gid))
48
49         /* fork to become asynchronous -- parent process is done immediately,
50          * and continues to run the normal cron code, which means return to
51          * tick().  the child and grandchild don't leave this function, alive.
52          */
53         switch ((pid = fork())) {
54         case -1:
55                 log_it("CRON", getpid(), "error", "can't fork");
56                 if (e->flags & INTERVAL)
57                         e->lastexit = time(NULL);
58                 break;
59         case 0:
60                 /* child process */
61                 pidfile_close(pfh);
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                 Debug(DPROC, ("[%d] main process forked child #%d, "
69                     "returning to work\n", getpid(), pid))
70                 if (e->flags & INTERVAL) {
71                         e->lastexit = 0;
72                         e->child = pid;
73                 }
74                 break;
75         }
76         Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
77 }
78
79
80 static void
81 child_process(entry *e, user *u)
82 {
83         int stdin_pipe[2], stdout_pipe[2];
84         char *input_data;
85         const char *usernm, *mailto, *mailfrom;
86         PID_T jobpid, stdinjob, mailpid;
87         FILE *mail;
88         int bytes = 1;
89         int status = 0;
90         const char *homedir = NULL;
91 # if defined(LOGIN_CAP)
92         struct passwd *pwd;
93         login_cap_t *lc;
94 # endif
95
96         Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
97
98         /* mark ourselves as different to PS command watchers by upshifting
99          * our program name.  This has no effect on some kernels.
100          */
101         setproctitle("running job");
102
103         /* discover some useful and important environment settings
104          */
105         usernm = env_get("LOGNAME", e->envp);
106         mailto = env_get("MAILTO", e->envp);
107         mailfrom = env_get("MAILFROM", e->envp);
108
109 #ifdef PAM
110         /* use PAM to see if the user's account is available,
111          * i.e., not locked or expired or whatever.  skip this
112          * for system tasks from /etc/crontab -- they can run
113          * as any user.
114          */
115         if (strcmp(u->name, SYS_NAME)) {        /* not equal */
116                 pam_handle_t *pamh = NULL;
117                 int pam_err;
118                 struct pam_conv pamc = {
119                         .conv = openpam_nullconv,
120                         .appdata_ptr = NULL
121                 };
122
123                 Debug(DPROC, ("[%d] checking account with PAM\n", getpid()))
124
125                 /* u->name keeps crontab owner name while LOGNAME is the name
126                  * of user to run command on behalf of.  they should be the
127                  * same for a task from a per-user crontab.
128                  */
129                 if (strcmp(u->name, usernm)) {
130                         log_it(usernm, getpid(), "username ambiguity", u->name);
131                         exit(ERROR_EXIT);
132                 }
133
134                 pam_err = pam_start("cron", usernm, &pamc, &pamh);
135                 if (pam_err != PAM_SUCCESS) {
136                         log_it("CRON", getpid(), "error", "can't start PAM");
137                         exit(ERROR_EXIT);
138                 }
139
140                 pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
141                 /* Expired password shouldn't prevent the job from running. */
142                 if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) {
143                         log_it(usernm, getpid(), "USER", "account unavailable");
144                         exit(ERROR_EXIT);
145                 }
146
147                 pam_end(pamh, pam_err);
148         }
149 #endif
150
151         /* our parent is watching for our death by catching SIGCHLD.  we
152          * do not care to watch for our children's deaths this way -- we
153          * use wait() explicitly.  so we have to disable the signal (which
154          * was inherited from the parent).
155          */
156         (void) signal(SIGCHLD, SIG_DFL);
157
158         /* create some pipes to talk to our future child
159          */
160         if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0) {
161                 log_it("CRON", getpid(), "error", "can't pipe");
162                 exit(ERROR_EXIT);
163         }
164
165         /* since we are a forked process, we can diddle the command string
166          * we were passed -- nobody else is going to use it again, right?
167          *
168          * if a % is present in the command, previous characters are the
169          * command, and subsequent characters are the additional input to
170          * the command.  Subsequent %'s will be transformed into newlines,
171          * but that happens later.
172          *
173          * If there are escaped %'s, remove the escape character.
174          */
175         /*local*/{
176                 int escaped = FALSE;
177                 int ch;
178                 char *p;
179
180                 for (input_data = p = e->cmd;
181                      (ch = *input_data) != '\0';
182                      input_data++, p++) {
183                         if (p != input_data)
184                                 *p = ch;
185                         if (escaped) {
186                                 if (ch == '%' || ch == '\\')
187                                         *--p = ch;
188                                 escaped = FALSE;
189                                 continue;
190                         }
191                         if (ch == '\\') {
192                                 escaped = TRUE;
193                                 continue;
194                         }
195                         if (ch == '%') {
196                                 *input_data++ = '\0';
197                                 break;
198                         }
199                 }
200                 *p = '\0';
201         }
202
203         /* fork again, this time so we can exec the user's command.
204          */
205         switch (jobpid = fork()) {
206         case -1:
207                 log_it("CRON", getpid(), "error", "can't fork");
208                 exit(ERROR_EXIT);
209                 /*NOTREACHED*/
210         case 0:
211                 Debug(DPROC, ("[%d] grandchild process fork()'ed\n",
212                               getpid()))
213
214                 if (e->uid == ROOT_UID)
215                         Jitter = RootJitter;
216                 if (Jitter != 0) {
217                         srandom(getpid());
218                         sleep(random() % Jitter);
219                 }
220
221                 /* write a log message.  we've waited this long to do it
222                  * because it was not until now that we knew the PID that
223                  * the actual user command shell was going to get and the
224                  * PID is part of the log message.
225                  */
226                 if ((e->flags & DONT_LOG) == 0) {
227                         char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
228
229                         log_it(usernm, getpid(), "CMD", x);
230                         free(x);
231                 }
232
233                 /* that's the last thing we'll log.  close the log files.
234                  */
235 #ifdef SYSLOG
236                 closelog();
237 #endif
238
239                 /* get new pgrp, void tty, etc.
240                  */
241                 (void) setsid();
242
243                 /* close the pipe ends that we won't use.  this doesn't affect
244                  * the parent, who has to read and write them; it keeps the
245                  * kernel from recording us as a potential client TWICE --
246                  * which would keep it from sending SIGPIPE in otherwise
247                  * appropriate circumstances.
248                  */
249                 close(stdin_pipe[WRITE_PIPE]);
250                 close(stdout_pipe[READ_PIPE]);
251
252                 /* grandchild process.  make std{in,out} be the ends of
253                  * pipes opened by our daddy; make stderr go to stdout.
254                  */
255                 close(STDIN);   dup2(stdin_pipe[READ_PIPE], STDIN);
256                 close(STDOUT);  dup2(stdout_pipe[WRITE_PIPE], STDOUT);
257                 close(STDERR);  dup2(STDOUT, STDERR);
258
259                 /* close the pipes we just dup'ed.  The resources will remain.
260                  */
261                 close(stdin_pipe[READ_PIPE]);
262                 close(stdout_pipe[WRITE_PIPE]);
263
264                 environ = NULL;
265
266 # if defined(LOGIN_CAP)
267                 /* Set user's entire context, but note that PATH will
268                  * be overridden later
269                  */
270                 if ((pwd = getpwnam(usernm)) == NULL)
271                         pwd = getpwuid(e->uid);
272                 lc = NULL;
273                 if (pwd != NULL) {
274                         if (pwd->pw_dir != NULL
275                             && pwd->pw_dir[0] != '\0') {
276                                 homedir = strdup(pwd->pw_dir);
277                                 if (homedir == NULL) {
278                                         warn("strdup");
279                                         _exit(ERROR_EXIT);
280                                 }
281                         }
282                         pwd->pw_gid = e->gid;
283                         if (e->class != NULL)
284                                 lc = login_getclass(e->class);
285                 }
286                 if (pwd &&
287                     setusercontext(lc, pwd, e->uid,
288                             LOGIN_SETALL) == 0)
289                         (void) endpwent();
290                 else {
291                         /* fall back to the old method */
292                         (void) endpwent();
293 # endif
294                         /* set our directory, uid and gid.  Set gid first,
295                          * since once we set uid, we've lost root privileges.
296                          */
297                         if (setgid(e->gid) != 0) {
298                                 log_it(usernm, getpid(),
299                                     "error", "setgid failed");
300                                 _exit(ERROR_EXIT);
301                         }
302                         if (initgroups(usernm, e->gid) != 0) {
303                                 log_it(usernm, getpid(),
304                                     "error", "initgroups failed");
305                                 _exit(ERROR_EXIT);
306                         }
307                         if (setlogin(usernm) != 0) {
308                                 log_it(usernm, getpid(),
309                                     "error", "setlogin failed");
310                                 _exit(ERROR_EXIT);
311                         }
312                         if (setuid(e->uid) != 0) {
313                                 log_it(usernm, getpid(),
314                                     "error", "setuid failed");
315                                 _exit(ERROR_EXIT);
316                         }
317                         /* we aren't root after this..*/
318 #if defined(LOGIN_CAP)
319                 }
320                 if (lc != NULL)
321                         login_close(lc);
322 #endif
323
324                 /* For compatibility, we chdir to the value of HOME if it was
325                  * specified explicitly in the crontab file, but not if it was
326                  * set in the environment by some other mechanism. We chdir to
327                  * the homedir given by the pw entry otherwise.
328                  *
329                  * If !LOGIN_CAP, then HOME is always set in e->envp.
330                  *
331                  * XXX: probably should also consult PAM.
332                  */
333                 {
334                         char    *new_home = env_get("HOME", e->envp);
335                         if (new_home != NULL && new_home[0] != '\0')
336                                 chdir(new_home);
337                         else if (homedir != NULL)
338                                 chdir(homedir);
339                         else
340                                 chdir("/");
341                 }
342
343                 /* exec the command. Note that SHELL is not respected from
344                  * either login.conf or pw_shell, only an explicit setting
345                  * in the crontab. (default of _PATH_BSHELL is supplied when
346                  * setting up the entry)
347                  */
348                 {
349                         char    *shell = env_get("SHELL", e->envp);
350                         char    **p;
351
352                         /* Apply the environment from the entry, overriding
353                          * existing values (this will always set LOGNAME and
354                          * SHELL). putenv should not fail unless malloc does.
355                          */
356                         for (p = e->envp; *p; ++p) {
357                                 if (putenv(*p) != 0) {
358                                         warn("putenv");
359                                         _exit(ERROR_EXIT);
360                                 }
361                         }
362
363                         /* HOME in login.conf overrides pw, and HOME in the
364                          * crontab overrides both. So set pw's value only if
365                          * nothing was already set (overwrite==0).
366                          */
367                         if (homedir != NULL
368                             && setenv("HOME", homedir, 0) < 0) {
369                                 warn("setenv(HOME)");
370                                 _exit(ERROR_EXIT);
371                         }
372
373                         /* PATH in login.conf is respected, but the crontab
374                          * overrides; set a default value only if nothing
375                          * already set.
376                          */
377                         if (setenv("PATH", _PATH_DEFPATH, 0) < 0) {
378                                 warn("setenv(PATH)");
379                                 _exit(ERROR_EXIT);
380                         }
381
382 # if DEBUGGING
383                         if (DebugFlags & DTEST) {
384                                 fprintf(stderr,
385                                 "debug DTEST is on, not exec'ing command.\n");
386                                 fprintf(stderr,
387                                 "\tcmd='%s' shell='%s'\n", e->cmd, shell);
388                                 _exit(OK_EXIT);
389                         }
390 # endif /*DEBUGGING*/
391                         execl(shell, shell, "-c", e->cmd, (char *)NULL);
392                         warn("execl: couldn't exec `%s'", shell);
393                         _exit(ERROR_EXIT);
394                 }
395                 break;
396         default:
397                 /* parent process */
398                 break;
399         }
400
401         /* middle process, child of original cron, parent of process running
402          * the user's command.
403          */
404
405         Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
406
407         /* close the ends of the pipe that will only be referenced in the
408          * grandchild process...
409          */
410         close(stdin_pipe[READ_PIPE]);
411         close(stdout_pipe[WRITE_PIPE]);
412
413         /*
414          * write, to the pipe connected to child's stdin, any input specified
415          * after a % in the crontab entry.  while we copy, convert any
416          * additional %'s to newlines.  when done, if some characters were
417          * written and the last one wasn't a newline, write a newline.
418          *
419          * Note that if the input data won't fit into one pipe buffer (2K
420          * or 4K on most BSD systems), and the child doesn't read its stdin,
421          * we would block here.  thus we must fork again.
422          */
423
424         if (*input_data && (stdinjob = fork()) == 0) {
425                 FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
426                 int need_newline = FALSE;
427                 int escaped = FALSE;
428                 int ch;
429
430                 if (out == NULL) {
431                         warn("fdopen failed in child2");
432                         _exit(ERROR_EXIT);
433                 }
434
435                 Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
436
437                 /* close the pipe we don't use, since we inherited it and
438                  * are part of its reference count now.
439                  */
440                 close(stdout_pipe[READ_PIPE]);
441
442                 /* translation:
443                  *      \% -> %
444                  *      %  -> \n
445                  *      \x -> \x        for all x != %
446                  */
447                 while ((ch = *input_data++) != '\0') {
448                         if (escaped) {
449                                 if (ch != '%')
450                                         putc('\\', out);
451                         } else {
452                                 if (ch == '%')
453                                         ch = '\n';
454                         }
455
456                         if (!(escaped = (ch == '\\'))) {
457                                 putc(ch, out);
458                                 need_newline = (ch != '\n');
459                         }
460                 }
461                 if (escaped)
462                         putc('\\', out);
463                 if (need_newline)
464                         putc('\n', out);
465
466                 /* close the pipe, causing an EOF condition.  fclose causes
467                  * stdin_pipe[WRITE_PIPE] to be closed, too.
468                  */
469                 fclose(out);
470
471                 Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
472                 exit(0);
473         }
474
475         /* close the pipe to the grandkiddie's stdin, since its wicked uncle
476          * ernie back there has it open and will close it when he's done.
477          */
478         close(stdin_pipe[WRITE_PIPE]);
479
480         /*
481          * read output from the grandchild.  it's stderr has been redirected to
482          * it's stdout, which has been redirected to our pipe.  if there is any
483          * output, we'll be mailing it to the user whose crontab this is...
484          * when the grandchild exits, we'll get EOF.
485          */
486
487         Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
488
489         /*local*/{
490                 FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
491                 int ch;
492
493                 if (in == NULL) {
494                         warn("fdopen failed in child");
495                         _exit(ERROR_EXIT);
496                 }
497
498                 mail = NULL;
499
500                 ch = getc(in);
501                 if (ch != EOF) {
502                         Debug(DPROC|DEXT,
503                                 ("[%d] got data (%x:%c) from grandchild\n",
504                                         getpid(), ch, ch))
505
506                         /* get name of recipient.  this is MAILTO if set to a
507                          * valid local username; USER otherwise.
508                          */
509                         if (mailto == NULL) {
510                                 /* MAILTO not present, set to USER,
511                                  * unless globally overridden.
512                                  */
513                                 if (defmailto)
514                                         mailto = defmailto;
515                                 else
516                                         mailto = usernm;
517                         }
518                         if (mailto && *mailto == '\0')
519                                 mailto = NULL;
520
521                         /* if we are supposed to be mailing, MAILTO will
522                          * be non-NULL.  only in this case should we set
523                          * up the mail command and subjects and stuff...
524                          */
525
526                         if (mailto) {
527                                 char    **env;
528                                 char    mailcmd[MAX_COMMAND];
529                                 char    hostname[MAXHOSTNAMELEN];
530
531                                 if (gethostname(hostname, MAXHOSTNAMELEN) == -1)
532                                         hostname[0] = '\0';
533                                 hostname[sizeof(hostname) - 1] = '\0';
534                                 if (snprintf(mailcmd, sizeof(mailcmd), MAILFMT,
535                                     MAILARG) >= sizeof(mailcmd)) {
536                                         warnx("mail command too long");
537                                         (void) _exit(ERROR_EXIT);
538                                 }
539                                 if (!(mail = cron_popen(mailcmd, "w", e, &mailpid))) {
540                                         warn("%s", mailcmd);
541                                         (void) _exit(ERROR_EXIT);
542                                 }
543                                 if (mailfrom == NULL || *mailfrom == '\0')
544                                         fprintf(mail, "From: Cron Daemon <%s@%s>\n",
545                                             usernm, hostname);
546                                 else
547                                         fprintf(mail, "From: Cron Daemon <%s>\n",
548                                             mailfrom);
549                                 fprintf(mail, "To: %s\n", mailto);
550                                 fprintf(mail, "Subject: Cron <%s@%s> %s\n",
551                                         usernm, first_word(hostname, "."),
552                                         e->cmd);
553 #ifdef MAIL_DATE
554                                 fprintf(mail, "Date: %s\n",
555                                         arpadate(&TargetTime));
556 #endif /*MAIL_DATE*/
557                                 for (env = e->envp;  *env;  env++)
558                                         fprintf(mail, "X-Cron-Env: <%s>\n",
559                                                 *env);
560                                 fprintf(mail, "\n");
561
562                                 /* this was the first char from the pipe
563                                  */
564                                 putc(ch, mail);
565                         }
566
567                         /* we have to read the input pipe no matter whether
568                          * we mail or not, but obviously we only write to
569                          * mail pipe if we ARE mailing.
570                          */
571
572                         while (EOF != (ch = getc(in))) {
573                                 bytes++;
574                                 if (mail)
575                                         putc(ch, mail);
576                         }
577                 }
578                 /*if data from grandchild*/
579
580                 Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
581
582                 /* also closes stdout_pipe[READ_PIPE] */
583                 fclose(in);
584         }
585
586         /* wait for children to die.
587          */
588         if (jobpid > 0) {
589                 WAIT_T waiter;
590
591                 waiter = wait_on_child(jobpid, "grandchild command job");
592
593                 /* If everything went well, and -n was set, _and_ we have mail,
594                  * we won't be mailing... so shoot the messenger!
595                  */
596                 if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0
597                     && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR
598                     && mail) {
599                         Debug(DPROC, ("[%d] %s executed successfully, mail suppressed\n",
600                                 getpid(), "grandchild command job"))
601                         kill(mailpid, SIGKILL);
602                         (void)fclose(mail);
603                         mail = NULL;
604                 }
605
606                 /* only close pipe if we opened it -- i.e., we're
607                  * mailing...
608                  */
609
610                 if (mail) {
611                         Debug(DPROC, ("[%d] closing pipe to mail\n",
612                                 getpid()))
613                         /* Note: the pclose will probably see
614                          * the termination of the grandchild
615                          * in addition to the mail process, since
616                          * it (the grandchild) is likely to exit
617                          * after closing its stdout.
618                          */
619                         status = cron_pclose(mail);
620
621                         /* if there was output and we could not mail it,
622                          * log the facts so the poor user can figure out
623                          * what's going on.
624                          */
625                         if (status) {
626                                 char buf[MAX_TEMPSTR];
627
628                                 snprintf(buf, sizeof(buf),
629                         "mailed %d byte%s of output but got status 0x%04x\n",
630                                         bytes, (bytes==1)?"":"s",
631                                         status);
632                                 log_it(usernm, getpid(), "MAIL", buf);
633                         }
634                 }
635         }
636
637         if (*input_data && stdinjob > 0)
638                 wait_on_child(stdinjob, "grandchild stdinjob");
639 }
640
641 static WAIT_T
642 wait_on_child(PID_T childpid, const char *name)
643 {
644         WAIT_T waiter;
645         PID_T pid;
646
647         Debug(DPROC, ("[%d] waiting for %s (%d) to finish\n",
648                 getpid(), name, childpid))
649
650 #ifdef POSIX
651         while ((pid = waitpid(childpid, &waiter, 0)) < 0 && errno == EINTR)
652 #else
653         while ((pid = wait4(childpid, &waiter, 0, NULL)) < 0 && errno == EINTR)
654 #endif
655                 ;
656
657         if (pid < OK)
658                 return waiter;
659
660         Debug(DPROC, ("[%d] %s (%d) finished, status=%04x",
661                 getpid(), name, pid, WEXITSTATUS(waiter)))
662         if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
663                 Debug(DPROC, (", dumped core"))
664         Debug(DPROC, ("\n"))
665
666         return waiter;
667 }