184f33deaSJordan K. Hubbard /* Copyright 1988,1990,1993,1994 by Paul Vixie 284f33deaSJordan K. Hubbard * All rights reserved 384f33deaSJordan K. Hubbard * 484f33deaSJordan K. Hubbard * Distribute freely, except: don't remove my name from the source or 584f33deaSJordan K. Hubbard * documentation (don't take credit for my work), mark your changes (don't 684f33deaSJordan K. Hubbard * get me blamed for your possible bugs), don't alter or remove this 784f33deaSJordan K. Hubbard * notice. May be sold if buildable source is provided to buyer. No 884f33deaSJordan K. Hubbard * warrantee of any kind, express or implied, is included with this 984f33deaSJordan K. Hubbard * software; use at your own risk, responsibility for damages (if any) to 1084f33deaSJordan K. Hubbard * anyone resulting from the use of this software rests entirely with the 1184f33deaSJordan K. Hubbard * user. 1284f33deaSJordan K. Hubbard * 1384f33deaSJordan K. Hubbard * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 1484f33deaSJordan K. Hubbard * I'll try to keep a version up to date. I can be reached as follows: 1584f33deaSJordan K. Hubbard * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 1684f33deaSJordan K. Hubbard */ 1784f33deaSJordan K. Hubbard 1884f33deaSJordan K. Hubbard #if !defined(lint) && !defined(LINT) 199efe2eccSPeter Wemm static char rcsid[] = "$Id: do_command.c,v 1.11 1997/03/14 13:48:04 peter Exp $"; 2084f33deaSJordan K. Hubbard #endif 2184f33deaSJordan K. Hubbard 2284f33deaSJordan K. Hubbard 2384f33deaSJordan K. Hubbard #include "cron.h" 2484f33deaSJordan K. Hubbard #include <sys/signal.h> 2584f33deaSJordan K. Hubbard #if defined(sequent) 2684f33deaSJordan K. Hubbard # include <sys/universe.h> 2784f33deaSJordan K. Hubbard #endif 2884f33deaSJordan K. Hubbard #if defined(SYSLOG) 2984f33deaSJordan K. Hubbard # include <syslog.h> 3084f33deaSJordan K. Hubbard #endif 31b25b7bc1SDavid Nugent #if defined(LOGIN_CAP) 32b25b7bc1SDavid Nugent # include <login_cap.h> 33b25b7bc1SDavid Nugent #endif 3484f33deaSJordan K. Hubbard 3584f33deaSJordan K. Hubbard 3684f33deaSJordan K. Hubbard static void child_process __P((entry *, user *)), 3784f33deaSJordan K. Hubbard do_univ __P((user *)); 3884f33deaSJordan K. Hubbard 3984f33deaSJordan K. Hubbard 4084f33deaSJordan K. Hubbard void 4184f33deaSJordan K. Hubbard do_command(e, u) 4284f33deaSJordan K. Hubbard entry *e; 4384f33deaSJordan K. Hubbard user *u; 4484f33deaSJordan K. Hubbard { 4584f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n", 4684f33deaSJordan K. Hubbard getpid(), e->cmd, u->name, e->uid, e->gid)) 4784f33deaSJordan K. Hubbard 4884f33deaSJordan K. Hubbard /* fork to become asynchronous -- parent process is done immediately, 4984f33deaSJordan K. Hubbard * and continues to run the normal cron code, which means return to 5084f33deaSJordan K. Hubbard * tick(). the child and grandchild don't leave this function, alive. 5184f33deaSJordan K. Hubbard * 5284f33deaSJordan K. Hubbard * vfork() is unsuitable, since we have much to do, and the parent 5384f33deaSJordan K. Hubbard * needs to be able to run off and fork other processes. 5484f33deaSJordan K. Hubbard */ 5584f33deaSJordan K. Hubbard switch (fork()) { 5684f33deaSJordan K. Hubbard case -1: 5784f33deaSJordan K. Hubbard log_it("CRON",getpid(),"error","can't fork"); 5884f33deaSJordan K. Hubbard break; 5984f33deaSJordan K. Hubbard case 0: 6084f33deaSJordan K. Hubbard /* child process */ 6184f33deaSJordan K. Hubbard acquire_daemonlock(1); 6284f33deaSJordan K. Hubbard child_process(e, u); 6384f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) 6484f33deaSJordan K. Hubbard _exit(OK_EXIT); 6584f33deaSJordan K. Hubbard break; 6684f33deaSJordan K. Hubbard default: 6784f33deaSJordan K. Hubbard /* parent process */ 6884f33deaSJordan K. Hubbard break; 6984f33deaSJordan K. Hubbard } 7084f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] main process returning to work\n", getpid())) 7184f33deaSJordan K. Hubbard } 7284f33deaSJordan K. Hubbard 7384f33deaSJordan K. Hubbard 7484f33deaSJordan K. Hubbard static void 7584f33deaSJordan K. Hubbard child_process(e, u) 7684f33deaSJordan K. Hubbard entry *e; 7784f33deaSJordan K. Hubbard user *u; 7884f33deaSJordan K. Hubbard { 7984f33deaSJordan K. Hubbard int stdin_pipe[2], stdout_pipe[2]; 8084f33deaSJordan K. Hubbard register char *input_data; 8184f33deaSJordan K. Hubbard char *usernm, *mailto; 8284f33deaSJordan K. Hubbard int children = 0; 83b25b7bc1SDavid Nugent # if defined(LOGIN_CAP) 84c00d650fSPeter Wemm struct passwd *pwd; 85b25b7bc1SDavid Nugent # endif 8684f33deaSJordan K. Hubbard 8784f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) 8884f33deaSJordan K. Hubbard 8984f33deaSJordan K. Hubbard /* mark ourselves as different to PS command watchers by upshifting 9084f33deaSJordan K. Hubbard * our program name. This has no effect on some kernels. 9184f33deaSJordan K. Hubbard */ 9284f33deaSJordan K. Hubbard /*local*/{ 9384f33deaSJordan K. Hubbard register char *pch; 9484f33deaSJordan K. Hubbard 9584f33deaSJordan K. Hubbard for (pch = ProgramName; *pch; pch++) 9684f33deaSJordan K. Hubbard *pch = MkUpper(*pch); 9784f33deaSJordan K. Hubbard } 9884f33deaSJordan K. Hubbard 9984f33deaSJordan K. Hubbard /* discover some useful and important environment settings 10084f33deaSJordan K. Hubbard */ 10184f33deaSJordan K. Hubbard usernm = env_get("LOGNAME", e->envp); 10284f33deaSJordan K. Hubbard mailto = env_get("MAILTO", e->envp); 10384f33deaSJordan K. Hubbard 10484f33deaSJordan K. Hubbard #ifdef USE_SIGCHLD 10584f33deaSJordan K. Hubbard /* our parent is watching for our death by catching SIGCHLD. we 10684f33deaSJordan K. Hubbard * do not care to watch for our children's deaths this way -- we 10784f33deaSJordan K. Hubbard * use wait() explictly. so we have to disable the signal (which 10884f33deaSJordan K. Hubbard * was inherited from the parent). 10984f33deaSJordan K. Hubbard */ 11084f33deaSJordan K. Hubbard (void) signal(SIGCHLD, SIG_IGN); 11184f33deaSJordan K. Hubbard #else 11284f33deaSJordan K. Hubbard /* on system-V systems, we are ignoring SIGCLD. we have to stop 11384f33deaSJordan K. Hubbard * ignoring it now or the wait() in cron_pclose() won't work. 11484f33deaSJordan K. Hubbard * because of this, we have to wait() for our children here, as well. 11584f33deaSJordan K. Hubbard */ 11684f33deaSJordan K. Hubbard (void) signal(SIGCLD, SIG_DFL); 11784f33deaSJordan K. Hubbard #endif /*BSD*/ 11884f33deaSJordan K. Hubbard 11984f33deaSJordan K. Hubbard /* create some pipes to talk to our future child 12084f33deaSJordan K. Hubbard */ 12184f33deaSJordan K. Hubbard pipe(stdin_pipe); /* child's stdin */ 12284f33deaSJordan K. Hubbard pipe(stdout_pipe); /* child's stdout */ 12384f33deaSJordan K. Hubbard 12484f33deaSJordan K. Hubbard /* since we are a forked process, we can diddle the command string 12584f33deaSJordan K. Hubbard * we were passed -- nobody else is going to use it again, right? 12684f33deaSJordan K. Hubbard * 12784f33deaSJordan K. Hubbard * if a % is present in the command, previous characters are the 12884f33deaSJordan K. Hubbard * command, and subsequent characters are the additional input to 12984f33deaSJordan K. Hubbard * the command. Subsequent %'s will be transformed into newlines, 13084f33deaSJordan K. Hubbard * but that happens later. 13186ed6de3SJoerg Wunsch * 13286ed6de3SJoerg Wunsch * If there are escaped %'s, remove the escape character. 13384f33deaSJordan K. Hubbard */ 13484f33deaSJordan K. Hubbard /*local*/{ 13584f33deaSJordan K. Hubbard register int escaped = FALSE; 13684f33deaSJordan K. Hubbard register int ch; 13786ed6de3SJoerg Wunsch register char *p; 13884f33deaSJordan K. Hubbard 13986ed6de3SJoerg Wunsch for (input_data = p = e->cmd; ch = *input_data; 14086ed6de3SJoerg Wunsch input_data++, p++) { 14186ed6de3SJoerg Wunsch if (p != input_data) 14286ed6de3SJoerg Wunsch *p = ch; 14384f33deaSJordan K. Hubbard if (escaped) { 14486ed6de3SJoerg Wunsch if (ch == '%' || ch == '\\') 14586ed6de3SJoerg Wunsch *--p = ch; 14684f33deaSJordan K. Hubbard escaped = FALSE; 14784f33deaSJordan K. Hubbard continue; 14884f33deaSJordan K. Hubbard } 14984f33deaSJordan K. Hubbard if (ch == '\\') { 15084f33deaSJordan K. Hubbard escaped = TRUE; 15184f33deaSJordan K. Hubbard continue; 15284f33deaSJordan K. Hubbard } 15384f33deaSJordan K. Hubbard if (ch == '%') { 15484f33deaSJordan K. Hubbard *input_data++ = '\0'; 15584f33deaSJordan K. Hubbard break; 15684f33deaSJordan K. Hubbard } 15784f33deaSJordan K. Hubbard } 15886ed6de3SJoerg Wunsch *p = '\0'; 15984f33deaSJordan K. Hubbard } 16084f33deaSJordan K. Hubbard 16184f33deaSJordan K. Hubbard /* fork again, this time so we can exec the user's command. 16284f33deaSJordan K. Hubbard */ 16384f33deaSJordan K. Hubbard switch (vfork()) { 16484f33deaSJordan K. Hubbard case -1: 16584f33deaSJordan K. Hubbard log_it("CRON",getpid(),"error","can't vfork"); 16684f33deaSJordan K. Hubbard exit(ERROR_EXIT); 16784f33deaSJordan K. Hubbard /*NOTREACHED*/ 16884f33deaSJordan K. Hubbard case 0: 16984f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n", 17084f33deaSJordan K. Hubbard getpid())) 17184f33deaSJordan K. Hubbard 17284f33deaSJordan K. Hubbard /* write a log message. we've waited this long to do it 17384f33deaSJordan K. Hubbard * because it was not until now that we knew the PID that 17484f33deaSJordan K. Hubbard * the actual user command shell was going to get and the 17584f33deaSJordan K. Hubbard * PID is part of the log message. 17684f33deaSJordan K. Hubbard */ 17784f33deaSJordan K. Hubbard /*local*/{ 17884f33deaSJordan K. Hubbard char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 17984f33deaSJordan K. Hubbard 18084f33deaSJordan K. Hubbard log_it(usernm, getpid(), "CMD", x); 18184f33deaSJordan K. Hubbard free(x); 18284f33deaSJordan K. Hubbard } 18384f33deaSJordan K. Hubbard 18484f33deaSJordan K. Hubbard /* that's the last thing we'll log. close the log files. 18584f33deaSJordan K. Hubbard */ 18684f33deaSJordan K. Hubbard #ifdef SYSLOG 18784f33deaSJordan K. Hubbard closelog(); 18884f33deaSJordan K. Hubbard #endif 18984f33deaSJordan K. Hubbard 19084f33deaSJordan K. Hubbard /* get new pgrp, void tty, etc. 19184f33deaSJordan K. Hubbard */ 19284f33deaSJordan K. Hubbard (void) setsid(); 19384f33deaSJordan K. Hubbard 19484f33deaSJordan K. Hubbard /* close the pipe ends that we won't use. this doesn't affect 19584f33deaSJordan K. Hubbard * the parent, who has to read and write them; it keeps the 19684f33deaSJordan K. Hubbard * kernel from recording us as a potential client TWICE -- 19784f33deaSJordan K. Hubbard * which would keep it from sending SIGPIPE in otherwise 19884f33deaSJordan K. Hubbard * appropriate circumstances. 19984f33deaSJordan K. Hubbard */ 20084f33deaSJordan K. Hubbard close(stdin_pipe[WRITE_PIPE]); 20184f33deaSJordan K. Hubbard close(stdout_pipe[READ_PIPE]); 20284f33deaSJordan K. Hubbard 20384f33deaSJordan K. Hubbard /* grandchild process. make std{in,out} be the ends of 20484f33deaSJordan K. Hubbard * pipes opened by our daddy; make stderr go to stdout. 20584f33deaSJordan K. Hubbard */ 20684f33deaSJordan K. Hubbard close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); 20784f33deaSJordan K. Hubbard close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); 20884f33deaSJordan K. Hubbard close(STDERR); dup2(STDOUT, STDERR); 20984f33deaSJordan K. Hubbard 21084f33deaSJordan K. Hubbard /* close the pipes we just dup'ed. The resources will remain. 21184f33deaSJordan K. Hubbard */ 21284f33deaSJordan K. Hubbard close(stdin_pipe[READ_PIPE]); 21384f33deaSJordan K. Hubbard close(stdout_pipe[WRITE_PIPE]); 21484f33deaSJordan K. Hubbard 21584f33deaSJordan K. Hubbard /* set our login universe. Do this in the grandchild 21684f33deaSJordan K. Hubbard * so that the child can invoke /usr/lib/sendmail 21784f33deaSJordan K. Hubbard * without surprises. 21884f33deaSJordan K. Hubbard */ 21984f33deaSJordan K. Hubbard do_univ(u); 22084f33deaSJordan K. Hubbard 221b25b7bc1SDavid Nugent # if defined(LOGIN_CAP) 222b25b7bc1SDavid Nugent /* Set user's entire context, but skip the environment 223b25b7bc1SDavid Nugent * as cron provides a separate interface for this 224b25b7bc1SDavid Nugent */ 225c00d650fSPeter Wemm pwd = getpwuid(e->uid); 2269efe2eccSPeter Wemm if (pwd && 2279efe2eccSPeter Wemm setusercontext(NULL, pwd, e->uid, 2289efe2eccSPeter Wemm LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0) 2299efe2eccSPeter Wemm (void) endpwent(); 2309efe2eccSPeter Wemm else { 231c00d650fSPeter Wemm /* fall back to the old method */ 2329efe2eccSPeter Wemm (void) endpwent(); 233c00d650fSPeter Wemm # endif 234c00d650fSPeter Wemm /* set our directory, uid and gid. Set gid first, 235c00d650fSPeter Wemm * since once we set uid, we've lost root privledges. 23684f33deaSJordan K. Hubbard */ 237bdddbd2fSPaul Traina setgid(e->gid); 23884f33deaSJordan K. Hubbard # if defined(BSD) 23984f33deaSJordan K. Hubbard initgroups(env_get("LOGNAME", e->envp), e->gid); 24084f33deaSJordan K. Hubbard # endif 241bdddbd2fSPaul Traina setlogin(usernm); 242c00d650fSPeter Wemm setuid(e->uid); /* we aren't root after this..*/ 243c00d650fSPeter Wemm #if defined(LOGIN_CAP) 244c00d650fSPeter Wemm } 245b25b7bc1SDavid Nugent #endif 246bdddbd2fSPaul Traina chdir(env_get("HOME", e->envp)); 24784f33deaSJordan K. Hubbard 24884f33deaSJordan K. Hubbard /* exec the command. 24984f33deaSJordan K. Hubbard */ 25084f33deaSJordan K. Hubbard { 25184f33deaSJordan K. Hubbard char *shell = env_get("SHELL", e->envp); 25284f33deaSJordan K. Hubbard 25384f33deaSJordan K. Hubbard # if DEBUGGING 25484f33deaSJordan K. Hubbard if (DebugFlags & DTEST) { 25584f33deaSJordan K. Hubbard fprintf(stderr, 25684f33deaSJordan K. Hubbard "debug DTEST is on, not exec'ing command.\n"); 25784f33deaSJordan K. Hubbard fprintf(stderr, 25884f33deaSJordan K. Hubbard "\tcmd='%s' shell='%s'\n", e->cmd, shell); 25984f33deaSJordan K. Hubbard _exit(OK_EXIT); 26084f33deaSJordan K. Hubbard } 26184f33deaSJordan K. Hubbard # endif /*DEBUGGING*/ 26284f33deaSJordan K. Hubbard execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); 26384f33deaSJordan K. Hubbard fprintf(stderr, "execl: couldn't exec `%s'\n", shell); 26484f33deaSJordan K. Hubbard perror("execl"); 26584f33deaSJordan K. Hubbard _exit(ERROR_EXIT); 26684f33deaSJordan K. Hubbard } 26784f33deaSJordan K. Hubbard break; 26884f33deaSJordan K. Hubbard default: 26984f33deaSJordan K. Hubbard /* parent process */ 27084f33deaSJordan K. Hubbard break; 27184f33deaSJordan K. Hubbard } 27284f33deaSJordan K. Hubbard 27384f33deaSJordan K. Hubbard children++; 27484f33deaSJordan K. Hubbard 27584f33deaSJordan K. Hubbard /* middle process, child of original cron, parent of process running 27684f33deaSJordan K. Hubbard * the user's command. 27784f33deaSJordan K. Hubbard */ 27884f33deaSJordan K. Hubbard 27984f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) 28084f33deaSJordan K. Hubbard 28184f33deaSJordan K. Hubbard /* close the ends of the pipe that will only be referenced in the 28284f33deaSJordan K. Hubbard * grandchild process... 28384f33deaSJordan K. Hubbard */ 28484f33deaSJordan K. Hubbard close(stdin_pipe[READ_PIPE]); 28584f33deaSJordan K. Hubbard close(stdout_pipe[WRITE_PIPE]); 28684f33deaSJordan K. Hubbard 28784f33deaSJordan K. Hubbard /* 28884f33deaSJordan K. Hubbard * write, to the pipe connected to child's stdin, any input specified 28984f33deaSJordan K. Hubbard * after a % in the crontab entry. while we copy, convert any 29084f33deaSJordan K. Hubbard * additional %'s to newlines. when done, if some characters were 29184f33deaSJordan K. Hubbard * written and the last one wasn't a newline, write a newline. 29284f33deaSJordan K. Hubbard * 29384f33deaSJordan K. Hubbard * Note that if the input data won't fit into one pipe buffer (2K 29484f33deaSJordan K. Hubbard * or 4K on most BSD systems), and the child doesn't read its stdin, 29584f33deaSJordan K. Hubbard * we would block here. thus we must fork again. 29684f33deaSJordan K. Hubbard */ 29784f33deaSJordan K. Hubbard 29884f33deaSJordan K. Hubbard if (*input_data && fork() == 0) { 29984f33deaSJordan K. Hubbard register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 30084f33deaSJordan K. Hubbard register int need_newline = FALSE; 30184f33deaSJordan K. Hubbard register int escaped = FALSE; 30284f33deaSJordan K. Hubbard register int ch; 30384f33deaSJordan K. Hubbard 30484f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) 30584f33deaSJordan K. Hubbard 30684f33deaSJordan K. Hubbard /* close the pipe we don't use, since we inherited it and 30784f33deaSJordan K. Hubbard * are part of its reference count now. 30884f33deaSJordan K. Hubbard */ 30984f33deaSJordan K. Hubbard close(stdout_pipe[READ_PIPE]); 31084f33deaSJordan K. Hubbard 31184f33deaSJordan K. Hubbard /* translation: 31284f33deaSJordan K. Hubbard * \% -> % 31384f33deaSJordan K. Hubbard * % -> \n 31484f33deaSJordan K. Hubbard * \x -> \x for all x != % 31584f33deaSJordan K. Hubbard */ 31684f33deaSJordan K. Hubbard while (ch = *input_data++) { 31784f33deaSJordan K. Hubbard if (escaped) { 31884f33deaSJordan K. Hubbard if (ch != '%') 31984f33deaSJordan K. Hubbard putc('\\', out); 32084f33deaSJordan K. Hubbard } else { 32184f33deaSJordan K. Hubbard if (ch == '%') 32284f33deaSJordan K. Hubbard ch = '\n'; 32384f33deaSJordan K. Hubbard } 32484f33deaSJordan K. Hubbard 32584f33deaSJordan K. Hubbard if (!(escaped = (ch == '\\'))) { 32684f33deaSJordan K. Hubbard putc(ch, out); 32784f33deaSJordan K. Hubbard need_newline = (ch != '\n'); 32884f33deaSJordan K. Hubbard } 32984f33deaSJordan K. Hubbard } 33084f33deaSJordan K. Hubbard if (escaped) 33184f33deaSJordan K. Hubbard putc('\\', out); 33284f33deaSJordan K. Hubbard if (need_newline) 33384f33deaSJordan K. Hubbard putc('\n', out); 33484f33deaSJordan K. Hubbard 33584f33deaSJordan K. Hubbard /* close the pipe, causing an EOF condition. fclose causes 33684f33deaSJordan K. Hubbard * stdin_pipe[WRITE_PIPE] to be closed, too. 33784f33deaSJordan K. Hubbard */ 33884f33deaSJordan K. Hubbard fclose(out); 33984f33deaSJordan K. Hubbard 34084f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid())) 34184f33deaSJordan K. Hubbard exit(0); 34284f33deaSJordan K. Hubbard } 34384f33deaSJordan K. Hubbard 34484f33deaSJordan K. Hubbard /* close the pipe to the grandkiddie's stdin, since its wicked uncle 34584f33deaSJordan K. Hubbard * ernie back there has it open and will close it when he's done. 34684f33deaSJordan K. Hubbard */ 34784f33deaSJordan K. Hubbard close(stdin_pipe[WRITE_PIPE]); 34884f33deaSJordan K. Hubbard 34984f33deaSJordan K. Hubbard children++; 35084f33deaSJordan K. Hubbard 35184f33deaSJordan K. Hubbard /* 35284f33deaSJordan K. Hubbard * read output from the grandchild. it's stderr has been redirected to 35384f33deaSJordan K. Hubbard * it's stdout, which has been redirected to our pipe. if there is any 35484f33deaSJordan K. Hubbard * output, we'll be mailing it to the user whose crontab this is... 35584f33deaSJordan K. Hubbard * when the grandchild exits, we'll get EOF. 35684f33deaSJordan K. Hubbard */ 35784f33deaSJordan K. Hubbard 35884f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) 35984f33deaSJordan K. Hubbard 36084f33deaSJordan K. Hubbard /*local*/{ 36184f33deaSJordan K. Hubbard register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); 36284f33deaSJordan K. Hubbard register int ch = getc(in); 36384f33deaSJordan K. Hubbard 36484f33deaSJordan K. Hubbard if (ch != EOF) { 36584f33deaSJordan K. Hubbard register FILE *mail; 36684f33deaSJordan K. Hubbard register int bytes = 1; 36784f33deaSJordan K. Hubbard int status = 0; 36884f33deaSJordan K. Hubbard 36984f33deaSJordan K. Hubbard Debug(DPROC|DEXT, 37084f33deaSJordan K. Hubbard ("[%d] got data (%x:%c) from grandchild\n", 37184f33deaSJordan K. Hubbard getpid(), ch, ch)) 37284f33deaSJordan K. Hubbard 37384f33deaSJordan K. Hubbard /* get name of recipient. this is MAILTO if set to a 37484f33deaSJordan K. Hubbard * valid local username; USER otherwise. 37584f33deaSJordan K. Hubbard */ 37684f33deaSJordan K. Hubbard if (mailto) { 37784f33deaSJordan K. Hubbard /* MAILTO was present in the environment 37884f33deaSJordan K. Hubbard */ 37984f33deaSJordan K. Hubbard if (!*mailto) { 38084f33deaSJordan K. Hubbard /* ... but it's empty. set to NULL 38184f33deaSJordan K. Hubbard */ 38284f33deaSJordan K. Hubbard mailto = NULL; 38384f33deaSJordan K. Hubbard } 38484f33deaSJordan K. Hubbard } else { 38584f33deaSJordan K. Hubbard /* MAILTO not present, set to USER. 38684f33deaSJordan K. Hubbard */ 38784f33deaSJordan K. Hubbard mailto = usernm; 38884f33deaSJordan K. Hubbard } 38984f33deaSJordan K. Hubbard 39084f33deaSJordan K. Hubbard /* if we are supposed to be mailing, MAILTO will 39184f33deaSJordan K. Hubbard * be non-NULL. only in this case should we set 39284f33deaSJordan K. Hubbard * up the mail command and subjects and stuff... 39384f33deaSJordan K. Hubbard */ 39484f33deaSJordan K. Hubbard 39584f33deaSJordan K. Hubbard if (mailto) { 39684f33deaSJordan K. Hubbard register char **env; 39784f33deaSJordan K. Hubbard auto char mailcmd[MAX_COMMAND]; 39884f33deaSJordan K. Hubbard auto char hostname[MAXHOSTNAMELEN]; 39984f33deaSJordan K. Hubbard 40084f33deaSJordan K. Hubbard (void) gethostname(hostname, MAXHOSTNAMELEN); 401bdddbd2fSPaul Traina (void) snprintf(mailcmd, sizeof(mailcmd), 402bdddbd2fSPaul Traina MAILARGS, MAILCMD); 40384f33deaSJordan K. Hubbard if (!(mail = cron_popen(mailcmd, "w"))) { 40484f33deaSJordan K. Hubbard perror(MAILCMD); 40584f33deaSJordan K. Hubbard (void) _exit(ERROR_EXIT); 40684f33deaSJordan K. Hubbard } 40784f33deaSJordan K. Hubbard fprintf(mail, "From: root (Cron Daemon)\n"); 40884f33deaSJordan K. Hubbard fprintf(mail, "To: %s\n", mailto); 40984f33deaSJordan K. Hubbard fprintf(mail, "Subject: Cron <%s@%s> %s\n", 41084f33deaSJordan K. Hubbard usernm, first_word(hostname, "."), 41184f33deaSJordan K. Hubbard e->cmd); 41284f33deaSJordan K. Hubbard # if defined(MAIL_DATE) 41384f33deaSJordan K. Hubbard fprintf(mail, "Date: %s\n", 41484f33deaSJordan K. Hubbard arpadate(&TargetTime)); 41584f33deaSJordan K. Hubbard # endif /* MAIL_DATE */ 41684f33deaSJordan K. Hubbard for (env = e->envp; *env; env++) 41784f33deaSJordan K. Hubbard fprintf(mail, "X-Cron-Env: <%s>\n", 41884f33deaSJordan K. Hubbard *env); 41984f33deaSJordan K. Hubbard fprintf(mail, "\n"); 42084f33deaSJordan K. Hubbard 42184f33deaSJordan K. Hubbard /* this was the first char from the pipe 42284f33deaSJordan K. Hubbard */ 42384f33deaSJordan K. Hubbard putc(ch, mail); 42484f33deaSJordan K. Hubbard } 42584f33deaSJordan K. Hubbard 42684f33deaSJordan K. Hubbard /* we have to read the input pipe no matter whether 42784f33deaSJordan K. Hubbard * we mail or not, but obviously we only write to 42884f33deaSJordan K. Hubbard * mail pipe if we ARE mailing. 42984f33deaSJordan K. Hubbard */ 43084f33deaSJordan K. Hubbard 43184f33deaSJordan K. Hubbard while (EOF != (ch = getc(in))) { 43284f33deaSJordan K. Hubbard bytes++; 43384f33deaSJordan K. Hubbard if (mailto) 43484f33deaSJordan K. Hubbard putc(ch, mail); 43584f33deaSJordan K. Hubbard } 43684f33deaSJordan K. Hubbard 43784f33deaSJordan K. Hubbard /* only close pipe if we opened it -- i.e., we're 43884f33deaSJordan K. Hubbard * mailing... 43984f33deaSJordan K. Hubbard */ 44084f33deaSJordan K. Hubbard 44184f33deaSJordan K. Hubbard if (mailto) { 44284f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] closing pipe to mail\n", 44384f33deaSJordan K. Hubbard getpid())) 44484f33deaSJordan K. Hubbard /* Note: the pclose will probably see 44584f33deaSJordan K. Hubbard * the termination of the grandchild 44684f33deaSJordan K. Hubbard * in addition to the mail process, since 44784f33deaSJordan K. Hubbard * it (the grandchild) is likely to exit 44884f33deaSJordan K. Hubbard * after closing its stdout. 44984f33deaSJordan K. Hubbard */ 45084f33deaSJordan K. Hubbard status = cron_pclose(mail); 45184f33deaSJordan K. Hubbard } 45284f33deaSJordan K. Hubbard 45384f33deaSJordan K. Hubbard /* if there was output and we could not mail it, 45484f33deaSJordan K. Hubbard * log the facts so the poor user can figure out 45584f33deaSJordan K. Hubbard * what's going on. 45684f33deaSJordan K. Hubbard */ 45784f33deaSJordan K. Hubbard if (mailto && status) { 45884f33deaSJordan K. Hubbard char buf[MAX_TEMPSTR]; 45984f33deaSJordan K. Hubbard 460bdddbd2fSPaul Traina snprintf(buf, sizeof(buf), 46184f33deaSJordan K. Hubbard "mailed %d byte%s of output but got status 0x%04x\n", 46284f33deaSJordan K. Hubbard bytes, (bytes==1)?"":"s", 46384f33deaSJordan K. Hubbard status); 46484f33deaSJordan K. Hubbard log_it(usernm, getpid(), "MAIL", buf); 46584f33deaSJordan K. Hubbard } 46684f33deaSJordan K. Hubbard 46784f33deaSJordan K. Hubbard } /*if data from grandchild*/ 46884f33deaSJordan K. Hubbard 46984f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) 47084f33deaSJordan K. Hubbard 47184f33deaSJordan K. Hubbard fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 47284f33deaSJordan K. Hubbard } 47384f33deaSJordan K. Hubbard 47484f33deaSJordan K. Hubbard /* wait for children to die. 47584f33deaSJordan K. Hubbard */ 47684f33deaSJordan K. Hubbard for (; children > 0; children--) 47784f33deaSJordan K. Hubbard { 47884f33deaSJordan K. Hubbard WAIT_T waiter; 47984f33deaSJordan K. Hubbard PID_T pid; 48084f33deaSJordan K. Hubbard 48184f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n", 48284f33deaSJordan K. Hubbard getpid(), children)) 48384f33deaSJordan K. Hubbard pid = wait(&waiter); 48484f33deaSJordan K. Hubbard if (pid < OK) { 48584f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] no more grandchildren--mail written?\n", 48684f33deaSJordan K. Hubbard getpid())) 48784f33deaSJordan K. Hubbard break; 48884f33deaSJordan K. Hubbard } 48984f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x", 49084f33deaSJordan K. Hubbard getpid(), pid, WEXITSTATUS(waiter))) 49184f33deaSJordan K. Hubbard if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 49284f33deaSJordan K. Hubbard Debug(DPROC, (", dumped core")) 49384f33deaSJordan K. Hubbard Debug(DPROC, ("\n")) 49484f33deaSJordan K. Hubbard } 49584f33deaSJordan K. Hubbard } 49684f33deaSJordan K. Hubbard 49784f33deaSJordan K. Hubbard 49884f33deaSJordan K. Hubbard static void 49984f33deaSJordan K. Hubbard do_univ(u) 50084f33deaSJordan K. Hubbard user *u; 50184f33deaSJordan K. Hubbard { 50284f33deaSJordan K. Hubbard #if defined(sequent) 50384f33deaSJordan K. Hubbard /* Dynix (Sequent) hack to put the user associated with 50484f33deaSJordan K. Hubbard * the passed user structure into the ATT universe if 50584f33deaSJordan K. Hubbard * necessary. We have to dig the gecos info out of 50684f33deaSJordan K. Hubbard * the user's password entry to see if the magic 50784f33deaSJordan K. Hubbard * "universe(att)" string is present. 50884f33deaSJordan K. Hubbard */ 50984f33deaSJordan K. Hubbard 51084f33deaSJordan K. Hubbard struct passwd *p; 51184f33deaSJordan K. Hubbard char *s; 51284f33deaSJordan K. Hubbard int i; 51384f33deaSJordan K. Hubbard 51484f33deaSJordan K. Hubbard p = getpwuid(u->uid); 51584f33deaSJordan K. Hubbard (void) endpwent(); 51684f33deaSJordan K. Hubbard 51784f33deaSJordan K. Hubbard if (p == NULL) 51884f33deaSJordan K. Hubbard return; 51984f33deaSJordan K. Hubbard 52084f33deaSJordan K. Hubbard s = p->pw_gecos; 52184f33deaSJordan K. Hubbard 52284f33deaSJordan K. Hubbard for (i = 0; i < 4; i++) 52384f33deaSJordan K. Hubbard { 52484f33deaSJordan K. Hubbard if ((s = strchr(s, ',')) == NULL) 52584f33deaSJordan K. Hubbard return; 52684f33deaSJordan K. Hubbard s++; 52784f33deaSJordan K. Hubbard } 52884f33deaSJordan K. Hubbard if (strcmp(s, "universe(att)")) 52984f33deaSJordan K. Hubbard return; 53084f33deaSJordan K. Hubbard 53184f33deaSJordan K. Hubbard (void) universe(U_ATT); 53284f33deaSJordan K. Hubbard #endif 53384f33deaSJordan K. Hubbard } 534