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: 4034
Valid CSS Valid XHTML 1.0 Strict