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: }