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