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