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