1*8b18fb30Smillert /* $OpenBSD: do_command.c,v 1.35 2011/03/03 15:08:14 millert Exp $ */ 2e134e629Smillert 3df930be7Sderaadt /* Copyright 1988,1990,1993,1994 by Paul Vixie 4df930be7Sderaadt * All rights reserved 5f454ebdeSmillert */ 6f454ebdeSmillert 7f454ebdeSmillert /* 8a5198fa1Smillert * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 9f454ebdeSmillert * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 10df930be7Sderaadt * 11f454ebdeSmillert * Permission to use, copy, modify, and distribute this software for any 12f454ebdeSmillert * purpose with or without fee is hereby granted, provided that the above 13f454ebdeSmillert * copyright notice and this permission notice appear in all copies. 14df930be7Sderaadt * 15a5198fa1Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16a5198fa1Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17a5198fa1Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18a5198fa1Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19a5198fa1Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20a5198fa1Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21a5198fa1Smillert * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22df930be7Sderaadt */ 23df930be7Sderaadt 24df930be7Sderaadt #include "cron.h" 25df930be7Sderaadt 26f454ebdeSmillert static void child_process(entry *, user *); 27df930be7Sderaadt 28df930be7Sderaadt void 29f454ebdeSmillert do_command(entry *e, user *u) { 30b0ca7b2aSderaadt Debug(DPROC, ("[%ld] do_command(%s, (%s,%lu,%lu))\n", 31f454ebdeSmillert (long)getpid(), e->cmd, u->name, 322b139a93Smillert (u_long)e->pwd->pw_uid, (u_long)e->pwd->pw_gid)) 33df930be7Sderaadt 34df930be7Sderaadt /* fork to become asynchronous -- parent process is done immediately, 35df930be7Sderaadt * and continues to run the normal cron code, which means return to 36df930be7Sderaadt * tick(). the child and grandchild don't leave this function, alive. 37df930be7Sderaadt * 38df930be7Sderaadt * vfork() is unsuitable, since we have much to do, and the parent 39df930be7Sderaadt * needs to be able to run off and fork other processes. 40df930be7Sderaadt */ 41df930be7Sderaadt switch (fork()) { 42df930be7Sderaadt case -1: 43df930be7Sderaadt log_it("CRON", getpid(), "error", "can't fork"); 44df930be7Sderaadt break; 45df930be7Sderaadt case 0: 46df930be7Sderaadt /* child process */ 47df930be7Sderaadt acquire_daemonlock(1); 48df930be7Sderaadt child_process(e, u); 49f454ebdeSmillert Debug(DPROC, ("[%ld] child process done, exiting\n", 50f454ebdeSmillert (long)getpid())) 51df930be7Sderaadt _exit(OK_EXIT); 52df930be7Sderaadt break; 53df930be7Sderaadt default: 54df930be7Sderaadt /* parent process */ 55df930be7Sderaadt break; 56df930be7Sderaadt } 57f454ebdeSmillert Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())) 58df930be7Sderaadt } 59df930be7Sderaadt 60df930be7Sderaadt static void 61f454ebdeSmillert child_process(entry *e, user *u) { 622009bb53Smillert FILE *in; 63df930be7Sderaadt int stdin_pipe[2], stdout_pipe[2]; 642009bb53Smillert char *input_data, *usernm; 65d5919ef3Smillert int children = 0; 66df930be7Sderaadt 67f454ebdeSmillert Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)) 689f52c269Sderaadt 690aec96c6Smillert /* mark ourselves as different to PS command watchers */ 700aec96c6Smillert setproctitle("running job"); 71df930be7Sderaadt 72df930be7Sderaadt /* discover some useful and important environment settings 73df930be7Sderaadt */ 742b139a93Smillert usernm = e->pwd->pw_name; 75df930be7Sderaadt 76df930be7Sderaadt /* our parent is watching for our death by catching SIGCHLD. we 77df930be7Sderaadt * do not care to watch for our children's deaths this way -- we 78e4d25771Stodd * use wait() explicitly. so we have to reset the signal (which 79df930be7Sderaadt * was inherited from the parent). 80df930be7Sderaadt */ 815815f23fSmillert (void) signal(SIGCHLD, SIG_DFL); 82df930be7Sderaadt 83df930be7Sderaadt /* create some pipes to talk to our future child 84df930be7Sderaadt */ 85df930be7Sderaadt pipe(stdin_pipe); /* child's stdin */ 86df930be7Sderaadt pipe(stdout_pipe); /* child's stdout */ 87df930be7Sderaadt 88df930be7Sderaadt /* since we are a forked process, we can diddle the command string 89df930be7Sderaadt * we were passed -- nobody else is going to use it again, right? 90df930be7Sderaadt * 91df930be7Sderaadt * if a % is present in the command, previous characters are the 92df930be7Sderaadt * command, and subsequent characters are the additional input to 932b139a93Smillert * the command. An escaped % will have the escape character stripped 942b139a93Smillert * from it. Subsequent %'s will be transformed into newlines, 95df930be7Sderaadt * but that happens later. 96df930be7Sderaadt */ 97df930be7Sderaadt /*local*/{ 98f454ebdeSmillert int escaped = FALSE; 99f454ebdeSmillert int ch; 100f454ebdeSmillert char *p; 101df930be7Sderaadt 102f454ebdeSmillert for (input_data = p = e->cmd; 103f454ebdeSmillert (ch = *input_data) != '\0'; 104d94ebf94Sderaadt input_data++, p++) { 105d94ebf94Sderaadt if (p != input_data) 106d94ebf94Sderaadt *p = ch; 107df930be7Sderaadt if (escaped) { 10894cf10eeSmillert if (ch == '%') 109d94ebf94Sderaadt *--p = ch; 110df930be7Sderaadt escaped = FALSE; 111df930be7Sderaadt continue; 112df930be7Sderaadt } 113df930be7Sderaadt if (ch == '\\') { 114df930be7Sderaadt escaped = TRUE; 115df930be7Sderaadt continue; 116df930be7Sderaadt } 117df930be7Sderaadt if (ch == '%') { 118df930be7Sderaadt *input_data++ = '\0'; 119df930be7Sderaadt break; 120df930be7Sderaadt } 121df930be7Sderaadt } 122d94ebf94Sderaadt *p = '\0'; 123df930be7Sderaadt } 124df930be7Sderaadt 125df930be7Sderaadt /* fork again, this time so we can exec the user's command. 126df930be7Sderaadt */ 12714852038Smillert switch (fork()) { 128df930be7Sderaadt case -1: 1292b139a93Smillert log_it("CRON", getpid(), "error", "can't fork"); 130d038e17aSmillert _exit(ERROR_EXIT); 131df930be7Sderaadt /*NOTREACHED*/ 132df930be7Sderaadt case 0: 1332b139a93Smillert Debug(DPROC, ("[%ld] grandchild process fork()'ed\n", 134f454ebdeSmillert (long)getpid())) 135df930be7Sderaadt 136df930be7Sderaadt /* write a log message. we've waited this long to do it 137df930be7Sderaadt * because it was not until now that we knew the PID that 138df930be7Sderaadt * the actual user command shell was going to get and the 139df930be7Sderaadt * PID is part of the log message. 140df930be7Sderaadt */ 141f454ebdeSmillert if ((e->flags & DONT_LOG) == 0) { 142df930be7Sderaadt char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 143df930be7Sderaadt 144df930be7Sderaadt log_it(usernm, getpid(), "CMD", x); 145df930be7Sderaadt free(x); 146df930be7Sderaadt } 147df930be7Sderaadt 148df930be7Sderaadt /* that's the last thing we'll log. close the log files. 149df930be7Sderaadt */ 150d8a30772Smillert log_close(); 151df930be7Sderaadt 152df930be7Sderaadt /* get new pgrp, void tty, etc. 153df930be7Sderaadt */ 154df930be7Sderaadt (void) setsid(); 155df930be7Sderaadt 156df930be7Sderaadt /* close the pipe ends that we won't use. this doesn't affect 157df930be7Sderaadt * the parent, who has to read and write them; it keeps the 158df930be7Sderaadt * kernel from recording us as a potential client TWICE -- 159df930be7Sderaadt * which would keep it from sending SIGPIPE in otherwise 160df930be7Sderaadt * appropriate circumstances. 161df930be7Sderaadt */ 162df930be7Sderaadt close(stdin_pipe[WRITE_PIPE]); 163df930be7Sderaadt close(stdout_pipe[READ_PIPE]); 164df930be7Sderaadt 165df930be7Sderaadt /* grandchild process. make std{in,out} be the ends of 166df930be7Sderaadt * pipes opened by our daddy; make stderr go to stdout. 167df930be7Sderaadt */ 16814852038Smillert if (stdin_pipe[READ_PIPE] != STDIN) { 16914852038Smillert dup2(stdin_pipe[READ_PIPE], STDIN); 170df930be7Sderaadt close(stdin_pipe[READ_PIPE]); 17114852038Smillert } 17214852038Smillert if (stdout_pipe[WRITE_PIPE] != STDOUT) { 17314852038Smillert dup2(stdout_pipe[WRITE_PIPE], STDOUT); 174a0f600ecSmillert close(stdout_pipe[WRITE_PIPE]); 17514852038Smillert } 17614852038Smillert dup2(STDOUT, STDERR); 177df930be7Sderaadt 178df930be7Sderaadt /* set our directory, uid and gid. Set gid first, since once 179626e3f5fSmiod * we set uid, we've lost root privileges. 180df930be7Sderaadt */ 181fed231abSmillert #ifdef LOGIN_CAP 182fed231abSmillert { 183b746b40dSmillert #ifdef BSD_AUTH 184b746b40dSmillert auth_session_t *as; 185b746b40dSmillert #endif 1862b139a93Smillert login_cap_t *lc; 1872b139a93Smillert char **p; 1882b139a93Smillert extern char **environ; 189fed231abSmillert 190fed231abSmillert /* XXX - should just pass in a login_cap_t * */ 1912b139a93Smillert if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { 1922b139a93Smillert fprintf(stderr, 1932b139a93Smillert "unable to get login class for %s\n", 1942b139a93Smillert e->pwd->pw_name); 195fed231abSmillert _exit(ERROR_EXIT); 196fed231abSmillert } 1972b139a93Smillert if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { 198e134e629Smillert fprintf(stderr, 199e134e629Smillert "setusercontext failed for %s\n", 200e134e629Smillert e->pwd->pw_name); 201fed231abSmillert _exit(ERROR_EXIT); 202fed231abSmillert } 203f454ebdeSmillert #ifdef BSD_AUTH 204b746b40dSmillert as = auth_open(); 205b746b40dSmillert if (as == NULL || auth_setpwd(as, e->pwd) != 0) { 206b746b40dSmillert fprintf(stderr, "can't malloc\n"); 207b746b40dSmillert _exit(ERROR_EXIT); 208b746b40dSmillert } 209b746b40dSmillert if (auth_approval(as, lc, usernm, "cron") <= 0) { 2102b139a93Smillert fprintf(stderr, "approval failed for %s\n", 2112b139a93Smillert e->pwd->pw_name); 212f454ebdeSmillert _exit(ERROR_EXIT); 213f454ebdeSmillert } 214b746b40dSmillert auth_close(as); 215f454ebdeSmillert #endif /* BSD_AUTH */ 2162b139a93Smillert login_close(lc); 2172b139a93Smillert 218fed231abSmillert /* If no PATH specified in crontab file but 219f454ebdeSmillert * we just added one via login.conf, add it to 220fed231abSmillert * the crontab environment. 221fed231abSmillert */ 2222b139a93Smillert if (env_get("PATH", e->envp) == NULL && environ != NULL) { 2232b139a93Smillert for (p = environ; *p; p++) { 2242b139a93Smillert if (strncmp(*p, "PATH=", 5) == 0) { 2252b139a93Smillert e->envp = env_set(e->envp, *p); 2262b139a93Smillert break; 227fed231abSmillert } 228fed231abSmillert } 2292b139a93Smillert } 230f454ebdeSmillert } 231fed231abSmillert #else 232186e6e6eSmillert if (setgid(e->pwd->pw_gid) || initgroups(usernm, e->pwd->pw_gid)) { 23370805688Smillert fprintf(stderr, 23470805688Smillert "unable to set groups for %s\n", e->pwd->pw_name); 23570805688Smillert _exit(ERROR_EXIT); 23670805688Smillert } 237e134e629Smillert #if (defined(BSD)) && (BSD >= 199103) 238bd5a2f8eSderaadt setlogin(usernm); 239e134e629Smillert #endif /* BSD */ 24070805688Smillert if (setuid(e->pwd->pw_uid)) { 24170805688Smillert fprintf(stderr, 242a5198fa1Smillert "unable to set uid to %lu\n", 243a5198fa1Smillert (unsigned long)e->pwd->pw_uid); 24470805688Smillert _exit(ERROR_EXIT); 24570805688Smillert } 246f454ebdeSmillert 247f454ebdeSmillert #endif /* LOGIN_CAP */ 248df930be7Sderaadt chdir(env_get("HOME", e->envp)); 249df930be7Sderaadt 250*8b18fb30Smillert (void) signal(SIGPIPE, SIG_DFL); 251*8b18fb30Smillert 252f454ebdeSmillert /* 253f454ebdeSmillert * Exec the command. 254df930be7Sderaadt */ 255df930be7Sderaadt { 256df930be7Sderaadt char *shell = env_get("SHELL", e->envp); 257df930be7Sderaadt 258df930be7Sderaadt # if DEBUGGING 259df930be7Sderaadt if (DebugFlags & DTEST) { 260df930be7Sderaadt fprintf(stderr, 261df930be7Sderaadt "debug DTEST is on, not exec'ing command.\n"); 262df930be7Sderaadt fprintf(stderr, 263df930be7Sderaadt "\tcmd='%s' shell='%s'\n", e->cmd, shell); 264df930be7Sderaadt _exit(OK_EXIT); 265df930be7Sderaadt } 266df930be7Sderaadt # endif /*DEBUGGING*/ 267cce75673Smillert execle(shell, shell, "-c", e->cmd, (char *)NULL, e->envp); 268cce75673Smillert fprintf(stderr, "execle: couldn't exec `%s'\n", shell); 269cce75673Smillert perror("execle"); 270df930be7Sderaadt _exit(ERROR_EXIT); 271df930be7Sderaadt } 272df930be7Sderaadt break; 273df930be7Sderaadt default: 274df930be7Sderaadt /* parent process */ 275df930be7Sderaadt break; 276df930be7Sderaadt } 277df930be7Sderaadt 278df930be7Sderaadt children++; 279df930be7Sderaadt 280df930be7Sderaadt /* middle process, child of original cron, parent of process running 281df930be7Sderaadt * the user's command. 282df930be7Sderaadt */ 283df930be7Sderaadt 284f454ebdeSmillert Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())) 285df930be7Sderaadt 286df930be7Sderaadt /* close the ends of the pipe that will only be referenced in the 287df930be7Sderaadt * grandchild process... 288df930be7Sderaadt */ 289df930be7Sderaadt close(stdin_pipe[READ_PIPE]); 290df930be7Sderaadt close(stdout_pipe[WRITE_PIPE]); 291df930be7Sderaadt 292df930be7Sderaadt /* 293df930be7Sderaadt * write, to the pipe connected to child's stdin, any input specified 294df930be7Sderaadt * after a % in the crontab entry. while we copy, convert any 295df930be7Sderaadt * additional %'s to newlines. when done, if some characters were 296df930be7Sderaadt * written and the last one wasn't a newline, write a newline. 297df930be7Sderaadt * 298df930be7Sderaadt * Note that if the input data won't fit into one pipe buffer (2K 299df930be7Sderaadt * or 4K on most BSD systems), and the child doesn't read its stdin, 300df930be7Sderaadt * we would block here. thus we must fork again. 301df930be7Sderaadt */ 302df930be7Sderaadt 303df930be7Sderaadt if (*input_data && fork() == 0) { 304f454ebdeSmillert FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 305f454ebdeSmillert int need_newline = FALSE; 306f454ebdeSmillert int escaped = FALSE; 307f454ebdeSmillert int ch; 308df930be7Sderaadt 309f454ebdeSmillert Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", 310f454ebdeSmillert (long)getpid())) 311df930be7Sderaadt 312df930be7Sderaadt /* close the pipe we don't use, since we inherited it and 313df930be7Sderaadt * are part of its reference count now. 314df930be7Sderaadt */ 315df930be7Sderaadt close(stdout_pipe[READ_PIPE]); 316df930be7Sderaadt 317df930be7Sderaadt /* translation: 318df930be7Sderaadt * \% -> % 319df930be7Sderaadt * % -> \n 320df930be7Sderaadt * \x -> \x for all x != % 321df930be7Sderaadt */ 3229f52c269Sderaadt while ((ch = *input_data++) != '\0') { 323df930be7Sderaadt if (escaped) { 324df930be7Sderaadt if (ch != '%') 325df930be7Sderaadt putc('\\', out); 326df930be7Sderaadt } else { 327df930be7Sderaadt if (ch == '%') 328df930be7Sderaadt ch = '\n'; 329df930be7Sderaadt } 330df930be7Sderaadt 331df930be7Sderaadt if (!(escaped = (ch == '\\'))) { 332df930be7Sderaadt putc(ch, out); 333df930be7Sderaadt need_newline = (ch != '\n'); 334df930be7Sderaadt } 335df930be7Sderaadt } 336df930be7Sderaadt if (escaped) 337df930be7Sderaadt putc('\\', out); 338df930be7Sderaadt if (need_newline) 339df930be7Sderaadt putc('\n', out); 340df930be7Sderaadt 341df930be7Sderaadt /* close the pipe, causing an EOF condition. fclose causes 342df930be7Sderaadt * stdin_pipe[WRITE_PIPE] to be closed, too. 343df930be7Sderaadt */ 344df930be7Sderaadt fclose(out); 345df930be7Sderaadt 346f454ebdeSmillert Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", 347f454ebdeSmillert (long)getpid())) 348d038e17aSmillert _exit(OK_EXIT); 349df930be7Sderaadt } 350df930be7Sderaadt 351df930be7Sderaadt /* close the pipe to the grandkiddie's stdin, since its wicked uncle 352df930be7Sderaadt * ernie back there has it open and will close it when he's done. 353df930be7Sderaadt */ 354df930be7Sderaadt close(stdin_pipe[WRITE_PIPE]); 355df930be7Sderaadt 356df930be7Sderaadt children++; 357df930be7Sderaadt 358df930be7Sderaadt /* 359df930be7Sderaadt * read output from the grandchild. it's stderr has been redirected to 360df930be7Sderaadt * it's stdout, which has been redirected to our pipe. if there is any 361df930be7Sderaadt * output, we'll be mailing it to the user whose crontab this is... 362df930be7Sderaadt * when the grandchild exits, we'll get EOF. 363df930be7Sderaadt */ 364df930be7Sderaadt 365f454ebdeSmillert Debug(DPROC, ("[%ld] child reading output from grandchild\n", 366f454ebdeSmillert (long)getpid())) 367df930be7Sderaadt 3682009bb53Smillert (void) signal(SIGPIPE, SIG_IGN); 3692009bb53Smillert in = fdopen(stdout_pipe[READ_PIPE], "r"); 3702009bb53Smillert if (in != NULL) { 371f454ebdeSmillert int ch = getc(in); 372df930be7Sderaadt 373df930be7Sderaadt if (ch != EOF) { 3742009bb53Smillert FILE *mail = NULL; 3752009bb53Smillert char *mailto; 376f454ebdeSmillert int bytes = 1; 377df930be7Sderaadt int status = 0; 378df930be7Sderaadt 379df930be7Sderaadt Debug(DPROC|DEXT, 380f454ebdeSmillert ("[%ld] got data (%x:%c) from grandchild\n", 381f454ebdeSmillert (long)getpid(), ch, ch)) 382df930be7Sderaadt 383df930be7Sderaadt /* get name of recipient. this is MAILTO if set to a 384df930be7Sderaadt * valid local username; USER otherwise. 385df930be7Sderaadt */ 3862009bb53Smillert mailto = env_get("MAILTO", e->envp); 38770805688Smillert if (!mailto) { 388df930be7Sderaadt /* MAILTO not present, set to USER. 389df930be7Sderaadt */ 390df930be7Sderaadt mailto = usernm; 39170805688Smillert } else if (!*mailto || !safe_p(usernm, mailto)) { 39270805688Smillert mailto = NULL; 393df930be7Sderaadt } 394df930be7Sderaadt 395df930be7Sderaadt /* if we are supposed to be mailing, MAILTO will 396df930be7Sderaadt * be non-NULL. only in this case should we set 397df930be7Sderaadt * up the mail command and subjects and stuff... 398df930be7Sderaadt */ 399df930be7Sderaadt 40070805688Smillert if (mailto) { 401f454ebdeSmillert char **env; 402f454ebdeSmillert char mailcmd[MAX_COMMAND]; 403f454ebdeSmillert char hostname[MAXHOSTNAMELEN]; 404df930be7Sderaadt 4054af80cabSmpech gethostname(hostname, sizeof(hostname)); 406f454ebdeSmillert if (snprintf(mailcmd, sizeof mailcmd, MAILFMT, 407f454ebdeSmillert MAILARG) >= sizeof mailcmd) { 408f454ebdeSmillert fprintf(stderr, "mailcmd too long\n"); 409f454ebdeSmillert (void) _exit(ERROR_EXIT); 410f454ebdeSmillert } 4112b139a93Smillert if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { 412f454ebdeSmillert perror(mailcmd); 413df930be7Sderaadt (void) _exit(ERROR_EXIT); 414df930be7Sderaadt } 415df930be7Sderaadt fprintf(mail, "From: root (Cron Daemon)\n"); 416df930be7Sderaadt fprintf(mail, "To: %s\n", mailto); 417df930be7Sderaadt fprintf(mail, "Subject: Cron <%s@%s> %s\n", 418df930be7Sderaadt usernm, first_word(hostname, "."), 419df930be7Sderaadt e->cmd); 42013c10301Smillert fprintf(mail, "Auto-Submitted: auto-generated\n"); 421f454ebdeSmillert #ifdef MAIL_DATE 422df930be7Sderaadt fprintf(mail, "Date: %s\n", 423f326c411Sderaadt arpadate(&StartTime)); 424df930be7Sderaadt #endif /*MAIL_DATE*/ 425df930be7Sderaadt for (env = e->envp; *env; env++) 426df930be7Sderaadt fprintf(mail, "X-Cron-Env: <%s>\n", 427df930be7Sderaadt *env); 428df930be7Sderaadt fprintf(mail, "\n"); 429df930be7Sderaadt 430df930be7Sderaadt /* this was the first char from the pipe 431df930be7Sderaadt */ 432f454ebdeSmillert fputc(ch, mail); 433df930be7Sderaadt } 434df930be7Sderaadt 435df930be7Sderaadt /* we have to read the input pipe no matter whether 436df930be7Sderaadt * we mail or not, but obviously we only write to 437df930be7Sderaadt * mail pipe if we ARE mailing. 438df930be7Sderaadt */ 439df930be7Sderaadt 440df930be7Sderaadt while (EOF != (ch = getc(in))) { 441df930be7Sderaadt bytes++; 4422009bb53Smillert if (mail) 443f454ebdeSmillert fputc(ch, mail); 444df930be7Sderaadt } 445df930be7Sderaadt 446df930be7Sderaadt /* only close pipe if we opened it -- i.e., we're 447df930be7Sderaadt * mailing... 448df930be7Sderaadt */ 449df930be7Sderaadt 4502009bb53Smillert if (mail) { 451f454ebdeSmillert Debug(DPROC, ("[%ld] closing pipe to mail\n", 452f454ebdeSmillert (long)getpid())) 453df930be7Sderaadt /* Note: the pclose will probably see 454df930be7Sderaadt * the termination of the grandchild 455df930be7Sderaadt * in addition to the mail process, since 456df930be7Sderaadt * it (the grandchild) is likely to exit 457df930be7Sderaadt * after closing its stdout. 458df930be7Sderaadt */ 459df930be7Sderaadt status = cron_pclose(mail); 460df930be7Sderaadt } 461df930be7Sderaadt 462df930be7Sderaadt /* if there was output and we could not mail it, 463df930be7Sderaadt * log the facts so the poor user can figure out 464df930be7Sderaadt * what's going on. 465df930be7Sderaadt */ 4662009bb53Smillert if (mail && status) { 467df930be7Sderaadt char buf[MAX_TEMPSTR]; 468df930be7Sderaadt 469f5e9cd7eSderaadt snprintf(buf, sizeof buf, 470df930be7Sderaadt "mailed %d byte%s of output but got status 0x%04x\n", 471df930be7Sderaadt bytes, (bytes==1)?"":"s", 472df930be7Sderaadt status); 473df930be7Sderaadt log_it(usernm, getpid(), "MAIL", buf); 474df930be7Sderaadt } 475df930be7Sderaadt 476df930be7Sderaadt } /*if data from grandchild*/ 477df930be7Sderaadt 478f454ebdeSmillert Debug(DPROC, ("[%ld] got EOF from grandchild\n", 479f454ebdeSmillert (long)getpid())) 480df930be7Sderaadt 481df930be7Sderaadt fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 482df930be7Sderaadt } 483df930be7Sderaadt 484df930be7Sderaadt /* wait for children to die. 485df930be7Sderaadt */ 486f454ebdeSmillert for (; children > 0; children--) { 487df930be7Sderaadt WAIT_T waiter; 488df930be7Sderaadt PID_T pid; 489df930be7Sderaadt 490f454ebdeSmillert Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", 491f454ebdeSmillert (long)getpid(), children)) 492e134e629Smillert while ((pid = wait(&waiter)) < OK && errno == EINTR) 493e134e629Smillert ; 494df930be7Sderaadt if (pid < OK) { 495f454ebdeSmillert Debug(DPROC, 496f454ebdeSmillert ("[%ld] no more grandchildren--mail written?\n", 497f454ebdeSmillert (long)getpid())) 498df930be7Sderaadt break; 499df930be7Sderaadt } 500f454ebdeSmillert Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", 501f454ebdeSmillert (long)getpid(), (long)pid, WEXITSTATUS(waiter))) 502df930be7Sderaadt if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 503df930be7Sderaadt Debug(DPROC, (", dumped core")) 504df930be7Sderaadt Debug(DPROC, ("\n")) 505df930be7Sderaadt } 506df930be7Sderaadt } 507df930be7Sderaadt 5082b139a93Smillert int 509f454ebdeSmillert safe_p(const char *usernm, const char *s) { 510f454ebdeSmillert static const char safe_delim[] = "@!:%-.,"; /* conservative! */ 511f454ebdeSmillert const char *t; 512f454ebdeSmillert int ch, first; 513df930be7Sderaadt 514f454ebdeSmillert for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) { 515f454ebdeSmillert if (isascii(ch) && isprint(ch) && 5163c2f3a11Smillert (isalnum(ch) || ch == '_' || 5173c2f3a11Smillert (!first && strchr(safe_delim, ch)))) 518f454ebdeSmillert continue; 519f454ebdeSmillert log_it(usernm, getpid(), "UNSAFE", s); 520f454ebdeSmillert return (FALSE); 521df930be7Sderaadt } 522f454ebdeSmillert return (TRUE); 523df930be7Sderaadt } 524