# 2.11BSD#431 - /usr/src/usr.sbin/cron/do_command.c

##### Functions

    do_command        X

       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[] = "$Id: do_command.c,v 2.12 1994/01/15 20:43:43 vixie Exp $";
      20: #endif
      21:
      22: #include "cron.h"
      23: #include <sys/signal.h>
      24: #if defined(SYSLOG)
      25: # include <syslog.h>
      26: #endif
      27:
      28: static void     child_process __P((entry *, user *));
      29:
      30: void
      31: do_command(e, u)
      32:     entry   *e;
      33:     user    *u;
      34: {
      35:     Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
      36:         getpid(), e->cmd, u->name, e->uid, e->gid))
      37:
      38:     /* fork to become asynchronous -- parent process is done immediately,
      39: 	 * and continues to run the normal cron code, which means return to
      40: 	 * tick().  the child and grandchild don't leave this function, alive.
      41: 	 *
      42: 	 * vfork() is unsuitable, since we have much to do, and the parent
      43: 	 * needs to be able to run off and fork other processes.
      44: 	 */
      45:     switch (fork()) {
      46:     case -1:
      47:         log_it("CRON",getpid(),"error","can't fork");
      48:         break;
      49:     case 0:
      50:         /* child process */
      51:         acquire_daemonlock(1);
      52:         child_process(e, u);
      53:         Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
      54:         _exit(OK_EXIT);
      55:         break;
      56:     default:
      57:         /* parent process */
      58:         break;
      59:     }
      60:     Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
      61: }
      62:
      63:
      64: static void
      65: child_process(e, u)
      66:     entry   *e;
      67:     user    *u;
      68: {
      69:     int     stdin_pipe[2], stdout_pipe[2];
      70:     register char   *input_data;
      71:     char        *usernm, *mailto;
      72:     register int ch;
      73:     int     children = 0, escaped;
      74:
      75:     Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
      76:
      77:     /* mark ourselves as different to PS command watchers by upshifting
      78: 	 * our program name.  This has no effect on some kernels.
      79: 	 */
      80:     for (input_data = ProgramName;  ch = *input_data;  input_data++)
      81:         *input_data = (islower(ch) ? toupper(ch) : ch);
      82:
      83:     /* discover some useful and important environment settings
      84: 	 */
      85:     usernm = env_get("LOGNAME", e->envp);
      86:     mailto = env_get("MAILTO", e->envp);
      87:
      88:     /* our parent is watching for our death by catching SIGCHLD.  we
      89: 	 * do not care to watch for our children's deaths this way -- we
      90: 	 * use wait() explictly.  so we have to disable the signal (which
      91: 	 * was inherited from the parent).
      92: 	 */
      93:     (void) signal(SIGCHLD, SIG_IGN);
      94:
      95:     /* create some pipes to talk to our future child
      96: 	 */
      97:     pipe(stdin_pipe);   /* child's stdin */
      98:     pipe(stdout_pipe);  /* child's stdout */
      99:
     100:     /* since we are a forked process, we can diddle the command string
     101: 	 * we were passed -- nobody else is going to use it again, right?
     102: 	 *
     103: 	 * if a % is present in the command, previous characters are the
     104: 	 * command, and subsequent characters are the additional input to
     105: 	 * the command.  Subsequent %'s will be transformed into newlines,
     106: 	 * but that happens later.
     107: 	 */
     108:     escaped = FALSE;
     109:     for (input_data = e->cmd;  ch = *input_data;  input_data++) {
     110:         if (escaped) {
     111:             escaped = FALSE;
     112:             continue;
     113:         }
     114:         if (ch == '\\') {
     115:             escaped = TRUE;
     116:             continue;
     117:         }
     118:         if (ch == '%') {
     119:             *input_data++ = '\0';
     120:             break;
     121:         }
     122:     }
     123:
     124:     /* fork again, this time so we can exec the user's command.
     125: 	 */
     126:     switch (vfork()) {
     127:     case -1:
     128:         log_it("CRON",getpid(),"error","can't vfork");
     129:         exit(ERROR_EXIT);
     130:         /*NOTREACHED*/
     131:     case 0:
     132:         Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
     133:                   getpid()))
     134:
     135:         /* write a log message.  we've waited this long to do it
     136: 		 * because it was not until now that we knew the PID that
     137: 		 * the actual user command shell was going to get and the
     138: 		 * PID is part of the log message.
     139: 		 */
     140:         /*local*/{
     141:             char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
     142:
     143:             log_it(usernm, getpid(), "CMD", x);
     144:             free(x);
     145:         }
     146:
     147:         /* that's the last thing we'll log.  close the log files.
     148: 		 */
     149: #ifdef SYSLOG
     150:         closelog();
     151: #endif
     152:
     153:         /* get new pgrp, void tty, etc.
     154: 		 */
     155:         (void) setsid();
     156:
     157:         /* close the pipe ends that we won't use.  this doesn't affect
     158: 		 * the parent, who has to read and write them; it keeps the
     159: 		 * kernel from recording us as a potential client TWICE --
     160: 		 * which would keep it from sending SIGPIPE in otherwise
     161: 		 * appropriate circumstances.
     162: 		 */
     163:         close(stdin_pipe[WRITE_PIPE]);
     164:         close(stdout_pipe[READ_PIPE]);
     165:
     166:         /* grandchild process.  make std{in,out} be the ends of
     167: 		 * pipes opened by our daddy; make stderr go to stdout.
     168: 		 */
     169:         close(STDIN);   dup2(stdin_pipe[READ_PIPE], STDIN);
     170:         close(STDOUT);  dup2(stdout_pipe[WRITE_PIPE], STDOUT);
     171:         close(STDERR);  dup2(STDOUT, STDERR);
     172:
     173:         /* close the pipes we just dup'ed.  The resources will remain.
     174: 		 */
     175:         close(stdin_pipe[READ_PIPE]);
     176:         close(stdout_pipe[WRITE_PIPE]);
     177:
     178:         /* set our directory, uid and gid.  Set gid first, since once
     179: 		 * we set uid, we've lost root privledges.
     180: 		 */
     181:         setgid(e->gid);
     182:         initgroups(env_get("LOGNAME", e->envp), e->gid);
     183:         setuid(e->uid);     /* we aren't root after this... */
     184:         chdir(env_get("HOME", e->envp));
     185:
     186:         /* exec the command.
     187: 		 */
     188:         {
     189:             char    *shell = env_get("SHELL", e->envp);
     190:
     191: # if DEBUGGING
     192:             if (DebugFlags & DTEST) {
     193:                 fprintf(stderr,
     194:                 "debug DTEST is on, not exec'ing command.\n");
     195:                 fprintf(stderr,
     196:                 "\tcmd='%s' shell='%s'\n", e->cmd, shell);
     197:                 _exit(OK_EXIT);
     198:             }
     199: # endif /*DEBUGGING*/
     200:             execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
     201:             fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
     202:             perror("execl");
     203:             _exit(ERROR_EXIT);
     204:         }
     205:         break;
     206:     default:
     207:         /* parent process */
     208:         break;
     209:     }
     210:
     211:     children++;
     212:
     213:     /* middle process, child of original cron, parent of process running
     214: 	 * the user's command.
     215: 	 */
     216:
     217:     Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
     218:
     219:     /* close the ends of the pipe that will only be referenced in the
     220: 	 * grandchild process...
     221: 	 */
     222:     close(stdin_pipe[READ_PIPE]);
     223:     close(stdout_pipe[WRITE_PIPE]);
     224:
     225:     /*
     226: 	 * write, to the pipe connected to child's stdin, any input specified
     227: 	 * after a % in the crontab entry.  while we copy, convert any
     228: 	 * additional %'s to newlines.  when done, if some characters were
     229: 	 * written and the last one wasn't a newline, write a newline.
     230: 	 *
     231: 	 * Note that if the input data won't fit into one pipe buffer (2K
     232: 	 * or 4K on most BSD systems), and the child doesn't read its stdin,
     233: 	 * we would block here.  thus we must fork again.
     234: 	 */
     235:
     236:     if (*input_data && fork() == 0) {
     237:         register FILE   *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
     238:         register int    need_newline = FALSE;
     239:         register int    escaped = FALSE;
     240:         register int    ch;
     241:
     242:         Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
     243:
     244:         /* close the pipe we don't use, since we inherited it and
     245: 		 * are part of its reference count now.
     246: 		 */
     247:         close(stdout_pipe[READ_PIPE]);
     248:
     249:         /* translation:
     250: 		 *	\% -> %
     251: 		 *	%  -> \n
     252: 		 *	\x -> \x	for all x != %
     253: 		 */
     254:         while (ch = *input_data++) {
     255:             if (escaped) {
     256:                 if (ch != '%')
     257:                     putc('\\', out);
     258:             } else {
     259:                 if (ch == '%')
     260:                     ch = '\n';
     261:             }
     262:
     263:             if (!(escaped = (ch == '\\'))) {
     264:                 putc(ch, out);
     265:                 need_newline = (ch != '\n');
     266:             }
     267:         }
     268:         if (escaped)
     269:             putc('\\', out);
     270:         if (need_newline)
     271:             putc('\n', out);
     272:
     273:         /* close the pipe, causing an EOF condition.  fclose causes
     274: 		 * stdin_pipe[WRITE_PIPE] to be closed, too.
     275: 		 */
     276:         fclose(out);
     277:
     278:         Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
     279:         exit(0);
     280:     }
     281:
     282:     /* close the pipe to the grandkiddie's stdin, since its wicked uncle
     283: 	 * ernie back there has it open and will close it when he's done.
     284: 	 */
     285:     close(stdin_pipe[WRITE_PIPE]);
     286:
     287:     children++;
     288:
     289:     /*
     290: 	 * read output from the grandchild.  it's stderr has been redirected to
     291: 	 * it's stdout, which has been redirected to our pipe.  if there is any
     292: 	 * output, we'll be mailing it to the user whose crontab this is...
     293: 	 * when the grandchild exits, we'll get EOF.
     294: 	 */
     295:
     296:     Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
     297:
     298:     /*local*/{
     299:         register FILE   *in = fdopen(stdout_pipe[READ_PIPE], "r");
     300:         register int    ch = getc(in);
     301:
     302:         if (ch != EOF) {
     303:             register FILE   *mail;
     304:             register int    bytes = 1;
     305:             int     status = 0;
     306:
     307:             Debug(DPROC|DEXT,
     308:                 ("[%d] got data (%x:%c) from grandchild\n",
     309:                     getpid(), ch, ch))
     310:
     311:             /* get name of recipient.  this is MAILTO if set to a
     312: 			 * valid local username; USER otherwise.
     313: 			 */
     314:             if (mailto) {
     315:                 /* MAILTO was present in the environment
     316: 				 */
     317:                 if (!*mailto) {
     318:                     /* ... but it's empty. set to NULL
     319: 					 */
     320:                     mailto = NULL;
     321:                 }
     322:             } else {
     323:                 /* MAILTO not present, set to USER.
     324: 				 */
     325:                 mailto = usernm;
     326:             }
     327:
     328:             /* if we are supposed to be mailing, MAILTO will
     329: 			 * be non-NULL.  only in this case should we set
     330: 			 * up the mail command and subjects and stuff...
     331: 			 */
     332:
     333:             if (mailto) {
     334:                 register char   **env;
     335:                 auto char   mailcmd[MAX_COMMAND];
     336:                 auto char   hostname[MAXHOSTNAMELEN];
     337:
     338:                 (void) gethostname(hostname, MAXHOSTNAMELEN);
     339:                 (void) sprintf(mailcmd, MAILARGS,
     340:                            MAILCMD, mailto);
     341:                 if (!(mail = cron_popen(mailcmd, "w"))) {
     342:                     perror(MAILCMD);
     343:                     (void) _exit(ERROR_EXIT);
     344:                 }
     345:                 fprintf(mail, "From: root (Cron Daemon)\n");
     346:                 fprintf(mail, "To: %s\n", mailto);
     347:                 fprintf(mail, "Subject: Cron <%s@%s> %s\n",
     348:                     usernm, first_word(hostname, "."),
     349:                     e->cmd);
     350: # if defined(MAIL_DATE)
     351:                 fprintf(mail, "Date: %s\n",
     352:                     arpadate(&TargetTime));
     353: # endif /* MAIL_DATE */
     354:                 for (env = e->envp;  *env;  env++)
     355:                     fprintf(mail, "X-Cron-Env: <%s>\n",
     356:                         *env);
     357:                 fprintf(mail, "\n");
     358:
     359:                 /* this was the first char from the pipe
     360: 				 */
     361:                 putc(ch, mail);
     362:             }
     363:
     364:             /* we have to read the input pipe no matter whether
     365: 			 * we mail or not, but obviously we only write to
     366: 			 * mail pipe if we ARE mailing.
     367: 			 */
     368:
     369:             while (EOF != (ch = getc(in))) {
     370:                 bytes++;
     371:                 if (mailto)
     372:                     putc(ch, mail);
     373:             }
     374:
     375:             /* only close pipe if we opened it -- i.e., we're
     376: 			 * mailing...
     377: 			 */
     378:
     379:             if (mailto) {
     380:                 Debug(DPROC, ("[%d] closing pipe to mail\n",
     381:                     getpid()))
     382:                 /* Note: the pclose will probably see
     383: 				 * the termination of the grandchild
     384: 				 * in addition to the mail process, since
     385: 				 * it (the grandchild) is likely to exit
     386: 				 * after closing its stdout.
     387: 				 */
     388:                 status = cron_pclose(mail);
     389:             }
     390:
     391:             /* if there was output and we could not mail it,
     392: 			 * log the facts so the poor user can figure out
     393: 			 * what's going on.
     394: 			 */
     395:             if (mailto && status) {
     396:                 char buf[MAX_TEMPSTR];
     397:
     398:                 sprintf(buf,
     399:             "mailed %d byte%s of output but got status 0x%04x\n",
     400:                     bytes, (bytes==1)?"":"s",
     401:                     status);
     402:                 log_it(usernm, getpid(), "MAIL", buf);
     403:             }
     404:
     405:         } /*if data from grandchild*/
     406:
     407:         Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
     408:
     409:         fclose(in); /* also closes stdout_pipe[READ_PIPE] */
     410:     }
     411:
     412:     /* wait for children to die.
     413: 	 */
     414:     for (;  children > 0;  children--)
     415:     {
     416:         WAIT_T      waiter;
     417:         PID_T       pid;
     418:
     419:         Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
     420:             getpid(), children))
     421:         pid = wait(&waiter);
     422:         if (pid < OK) {
     423:             Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
     424:                 getpid()))
     425:             break;
     426:         }
     427:         Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
     428:             getpid(), pid, WEXITSTATUS(waiter)))
     429:         if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
     430:             Debug(DPROC, (", dumped core"))
     431:         Debug(DPROC, ("\n"))
     432:     }
     433: }

#### Defined functions

child_process defined in line 64; used 1 times

- in line 52

do_command defined in line 30; never used

#### Defined variables

rcsid defined in line 19; never used

Last modified: 1999-06-16 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 1899 |