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) 19c00d650fSPeter Wemm static char rcsid[] = "$Id: do_command.c,v 1.10 1997/02/22 16:04:43 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; 85c00d650fSPeter Wemm login_cap_t *lc; 86b25b7bc1SDavid Nugent # endif 8784f33deaSJordan K. Hubbard 8884f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) 8984f33deaSJordan K. Hubbard 9084f33deaSJordan K. Hubbard /* mark ourselves as different to PS command watchers by upshifting 9184f33deaSJordan K. Hubbard * our program name. This has no effect on some kernels. 9284f33deaSJordan K. Hubbard */ 9384f33deaSJordan K. Hubbard /*local*/{ 9484f33deaSJordan K. Hubbard register char *pch; 9584f33deaSJordan K. Hubbard 9684f33deaSJordan K. Hubbard for (pch = ProgramName; *pch; pch++) 9784f33deaSJordan K. Hubbard *pch = MkUpper(*pch); 9884f33deaSJordan K. Hubbard } 9984f33deaSJordan K. Hubbard 10084f33deaSJordan K. Hubbard /* discover some useful and important environment settings 10184f33deaSJordan K. Hubbard */ 10284f33deaSJordan K. Hubbard usernm = env_get("LOGNAME", e->envp); 10384f33deaSJordan K. Hubbard mailto = env_get("MAILTO", e->envp); 10484f33deaSJordan K. Hubbard 10584f33deaSJordan K. Hubbard #ifdef USE_SIGCHLD 10684f33deaSJordan K. Hubbard /* our parent is watching for our death by catching SIGCHLD. we 10784f33deaSJordan K. Hubbard * do not care to watch for our children's deaths this way -- we 10884f33deaSJordan K. Hubbard * use wait() explictly. so we have to disable the signal (which 10984f33deaSJordan K. Hubbard * was inherited from the parent). 11084f33deaSJordan K. Hubbard */ 11184f33deaSJordan K. Hubbard (void) signal(SIGCHLD, SIG_IGN); 11284f33deaSJordan K. Hubbard #else 11384f33deaSJordan K. Hubbard /* on system-V systems, we are ignoring SIGCLD. we have to stop 11484f33deaSJordan K. Hubbard * ignoring it now or the wait() in cron_pclose() won't work. 11584f33deaSJordan K. Hubbard * because of this, we have to wait() for our children here, as well. 11684f33deaSJordan K. Hubbard */ 11784f33deaSJordan K. Hubbard (void) signal(SIGCLD, SIG_DFL); 11884f33deaSJordan K. Hubbard #endif /*BSD*/ 11984f33deaSJordan K. Hubbard 12084f33deaSJordan K. Hubbard /* create some pipes to talk to our future child 12184f33deaSJordan K. Hubbard */ 12284f33deaSJordan K. Hubbard pipe(stdin_pipe); /* child's stdin */ 12384f33deaSJordan K. Hubbard pipe(stdout_pipe); /* child's stdout */ 12484f33deaSJordan K. Hubbard 12584f33deaSJordan K. Hubbard /* since we are a forked process, we can diddle the command string 12684f33deaSJordan K. Hubbard * we were passed -- nobody else is going to use it again, right? 12784f33deaSJordan K. Hubbard * 12884f33deaSJordan K. Hubbard * if a % is present in the command, previous characters are the 12984f33deaSJordan K. Hubbard * command, and subsequent characters are the additional input to 13084f33deaSJordan K. Hubbard * the command. Subsequent %'s will be transformed into newlines, 13184f33deaSJordan K. Hubbard * but that happens later. 13286ed6de3SJoerg Wunsch * 13386ed6de3SJoerg Wunsch * If there are escaped %'s, remove the escape character. 13484f33deaSJordan K. Hubbard */ 13584f33deaSJordan K. Hubbard /*local*/{ 13684f33deaSJordan K. Hubbard register int escaped = FALSE; 13784f33deaSJordan K. Hubbard register int ch; 13886ed6de3SJoerg Wunsch register char *p; 13984f33deaSJordan K. Hubbard 14086ed6de3SJoerg Wunsch for (input_data = p = e->cmd; ch = *input_data; 14186ed6de3SJoerg Wunsch input_data++, p++) { 14286ed6de3SJoerg Wunsch if (p != input_data) 14386ed6de3SJoerg Wunsch *p = ch; 14484f33deaSJordan K. Hubbard if (escaped) { 14586ed6de3SJoerg Wunsch if (ch == '%' || ch == '\\') 14686ed6de3SJoerg Wunsch *--p = ch; 14784f33deaSJordan K. Hubbard escaped = FALSE; 14884f33deaSJordan K. Hubbard continue; 14984f33deaSJordan K. Hubbard } 15084f33deaSJordan K. Hubbard if (ch == '\\') { 15184f33deaSJordan K. Hubbard escaped = TRUE; 15284f33deaSJordan K. Hubbard continue; 15384f33deaSJordan K. Hubbard } 15484f33deaSJordan K. Hubbard if (ch == '%') { 15584f33deaSJordan K. Hubbard *input_data++ = '\0'; 15684f33deaSJordan K. Hubbard break; 15784f33deaSJordan K. Hubbard } 15884f33deaSJordan K. Hubbard } 15986ed6de3SJoerg Wunsch *p = '\0'; 16084f33deaSJordan K. Hubbard } 16184f33deaSJordan K. Hubbard 16284f33deaSJordan K. Hubbard /* fork again, this time so we can exec the user's command. 16384f33deaSJordan K. Hubbard */ 16484f33deaSJordan K. Hubbard switch (vfork()) { 16584f33deaSJordan K. Hubbard case -1: 16684f33deaSJordan K. Hubbard log_it("CRON",getpid(),"error","can't vfork"); 16784f33deaSJordan K. Hubbard exit(ERROR_EXIT); 16884f33deaSJordan K. Hubbard /*NOTREACHED*/ 16984f33deaSJordan K. Hubbard case 0: 17084f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n", 17184f33deaSJordan K. Hubbard getpid())) 17284f33deaSJordan K. Hubbard 17384f33deaSJordan K. Hubbard /* write a log message. we've waited this long to do it 17484f33deaSJordan K. Hubbard * because it was not until now that we knew the PID that 17584f33deaSJordan K. Hubbard * the actual user command shell was going to get and the 17684f33deaSJordan K. Hubbard * PID is part of the log message. 17784f33deaSJordan K. Hubbard */ 17884f33deaSJordan K. Hubbard /*local*/{ 17984f33deaSJordan K. Hubbard char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 18084f33deaSJordan K. Hubbard 18184f33deaSJordan K. Hubbard log_it(usernm, getpid(), "CMD", x); 18284f33deaSJordan K. Hubbard free(x); 18384f33deaSJordan K. Hubbard } 18484f33deaSJordan K. Hubbard 18584f33deaSJordan K. Hubbard /* that's the last thing we'll log. close the log files. 18684f33deaSJordan K. Hubbard */ 18784f33deaSJordan K. Hubbard #ifdef SYSLOG 18884f33deaSJordan K. Hubbard closelog(); 18984f33deaSJordan K. Hubbard #endif 19084f33deaSJordan K. Hubbard 19184f33deaSJordan K. Hubbard /* get new pgrp, void tty, etc. 19284f33deaSJordan K. Hubbard */ 19384f33deaSJordan K. Hubbard (void) setsid(); 19484f33deaSJordan K. Hubbard 19584f33deaSJordan K. Hubbard /* close the pipe ends that we won't use. this doesn't affect 19684f33deaSJordan K. Hubbard * the parent, who has to read and write them; it keeps the 19784f33deaSJordan K. Hubbard * kernel from recording us as a potential client TWICE -- 19884f33deaSJordan K. Hubbard * which would keep it from sending SIGPIPE in otherwise 19984f33deaSJordan K. Hubbard * appropriate circumstances. 20084f33deaSJordan K. Hubbard */ 20184f33deaSJordan K. Hubbard close(stdin_pipe[WRITE_PIPE]); 20284f33deaSJordan K. Hubbard close(stdout_pipe[READ_PIPE]); 20384f33deaSJordan K. Hubbard 20484f33deaSJordan K. Hubbard /* grandchild process. make std{in,out} be the ends of 20584f33deaSJordan K. Hubbard * pipes opened by our daddy; make stderr go to stdout. 20684f33deaSJordan K. Hubbard */ 20784f33deaSJordan K. Hubbard close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); 20884f33deaSJordan K. Hubbard close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); 20984f33deaSJordan K. Hubbard close(STDERR); dup2(STDOUT, STDERR); 21084f33deaSJordan K. Hubbard 21184f33deaSJordan K. Hubbard /* close the pipes we just dup'ed. The resources will remain. 21284f33deaSJordan K. Hubbard */ 21384f33deaSJordan K. Hubbard close(stdin_pipe[READ_PIPE]); 21484f33deaSJordan K. Hubbard close(stdout_pipe[WRITE_PIPE]); 21584f33deaSJordan K. Hubbard 21684f33deaSJordan K. Hubbard /* set our login universe. Do this in the grandchild 21784f33deaSJordan K. Hubbard * so that the child can invoke /usr/lib/sendmail 21884f33deaSJordan K. Hubbard * without surprises. 21984f33deaSJordan K. Hubbard */ 22084f33deaSJordan K. Hubbard do_univ(u); 22184f33deaSJordan K. Hubbard 222b25b7bc1SDavid Nugent # if defined(LOGIN_CAP) 223b25b7bc1SDavid Nugent /* Set user's entire context, but skip the environment 224b25b7bc1SDavid Nugent * as cron provides a separate interface for this 225b25b7bc1SDavid Nugent */ 226c00d650fSPeter Wemm pwd = getpwuid(e->uid); 227c00d650fSPeter Wemm if (pwd) 228c00d650fSPeter Wemm lc = login_getclass(pwd); 229c00d650fSPeter Wemm else 230c00d650fSPeter Wemm lc = NULL; 231c00d650fSPeter Wemm if (lc && pwd) { 232c00d650fSPeter Wemm setusercontext(lc, pwd, e->uid, 233c00d650fSPeter Wemm LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)); 234b25b7bc1SDavid Nugent login_close(lc); 235c00d650fSPeter Wemm } else { 236c00d650fSPeter Wemm /* fall back to the old method */ 237c00d650fSPeter Wemm # endif 238c00d650fSPeter Wemm /* set our directory, uid and gid. Set gid first, 239c00d650fSPeter Wemm * since once we set uid, we've lost root privledges. 24084f33deaSJordan K. Hubbard */ 241bdddbd2fSPaul Traina setgid(e->gid); 24284f33deaSJordan K. Hubbard # if defined(BSD) 24384f33deaSJordan K. Hubbard initgroups(env_get("LOGNAME", e->envp), e->gid); 24484f33deaSJordan K. Hubbard # endif 245bdddbd2fSPaul Traina setlogin(usernm); 246c00d650fSPeter Wemm setuid(e->uid); /* we aren't root after this..*/ 247c00d650fSPeter Wemm #if defined(LOGIN_CAP) 248c00d650fSPeter Wemm } 249b25b7bc1SDavid Nugent #endif 250bdddbd2fSPaul Traina chdir(env_get("HOME", e->envp)); 25184f33deaSJordan K. Hubbard 25284f33deaSJordan K. Hubbard /* exec the command. 25384f33deaSJordan K. Hubbard */ 25484f33deaSJordan K. Hubbard { 25584f33deaSJordan K. Hubbard char *shell = env_get("SHELL", e->envp); 25684f33deaSJordan K. Hubbard 25784f33deaSJordan K. Hubbard # if DEBUGGING 25884f33deaSJordan K. Hubbard if (DebugFlags & DTEST) { 25984f33deaSJordan K. Hubbard fprintf(stderr, 26084f33deaSJordan K. Hubbard "debug DTEST is on, not exec'ing command.\n"); 26184f33deaSJordan K. Hubbard fprintf(stderr, 26284f33deaSJordan K. Hubbard "\tcmd='%s' shell='%s'\n", e->cmd, shell); 26384f33deaSJordan K. Hubbard _exit(OK_EXIT); 26484f33deaSJordan K. Hubbard } 26584f33deaSJordan K. Hubbard # endif /*DEBUGGING*/ 26684f33deaSJordan K. Hubbard execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); 26784f33deaSJordan K. Hubbard fprintf(stderr, "execl: couldn't exec `%s'\n", shell); 26884f33deaSJordan K. Hubbard perror("execl"); 26984f33deaSJordan K. Hubbard _exit(ERROR_EXIT); 27084f33deaSJordan K. Hubbard } 27184f33deaSJordan K. Hubbard break; 27284f33deaSJordan K. Hubbard default: 27384f33deaSJordan K. Hubbard /* parent process */ 27484f33deaSJordan K. Hubbard break; 27584f33deaSJordan K. Hubbard } 27684f33deaSJordan K. Hubbard 27784f33deaSJordan K. Hubbard children++; 27884f33deaSJordan K. Hubbard 27984f33deaSJordan K. Hubbard /* middle process, child of original cron, parent of process running 28084f33deaSJordan K. Hubbard * the user's command. 28184f33deaSJordan K. Hubbard */ 28284f33deaSJordan K. Hubbard 28384f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) 28484f33deaSJordan K. Hubbard 28584f33deaSJordan K. Hubbard /* close the ends of the pipe that will only be referenced in the 28684f33deaSJordan K. Hubbard * grandchild process... 28784f33deaSJordan K. Hubbard */ 28884f33deaSJordan K. Hubbard close(stdin_pipe[READ_PIPE]); 28984f33deaSJordan K. Hubbard close(stdout_pipe[WRITE_PIPE]); 29084f33deaSJordan K. Hubbard 29184f33deaSJordan K. Hubbard /* 29284f33deaSJordan K. Hubbard * write, to the pipe connected to child's stdin, any input specified 29384f33deaSJordan K. Hubbard * after a % in the crontab entry. while we copy, convert any 29484f33deaSJordan K. Hubbard * additional %'s to newlines. when done, if some characters were 29584f33deaSJordan K. Hubbard * written and the last one wasn't a newline, write a newline. 29684f33deaSJordan K. Hubbard * 29784f33deaSJordan K. Hubbard * Note that if the input data won't fit into one pipe buffer (2K 29884f33deaSJordan K. Hubbard * or 4K on most BSD systems), and the child doesn't read its stdin, 29984f33deaSJordan K. Hubbard * we would block here. thus we must fork again. 30084f33deaSJordan K. Hubbard */ 30184f33deaSJordan K. Hubbard 30284f33deaSJordan K. Hubbard if (*input_data && fork() == 0) { 30384f33deaSJordan K. Hubbard register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 30484f33deaSJordan K. Hubbard register int need_newline = FALSE; 30584f33deaSJordan K. Hubbard register int escaped = FALSE; 30684f33deaSJordan K. Hubbard register int ch; 30784f33deaSJordan K. Hubbard 30884f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) 30984f33deaSJordan K. Hubbard 31084f33deaSJordan K. Hubbard /* close the pipe we don't use, since we inherited it and 31184f33deaSJordan K. Hubbard * are part of its reference count now. 31284f33deaSJordan K. Hubbard */ 31384f33deaSJordan K. Hubbard close(stdout_pipe[READ_PIPE]); 31484f33deaSJordan K. Hubbard 31584f33deaSJordan K. Hubbard /* translation: 31684f33deaSJordan K. Hubbard * \% -> % 31784f33deaSJordan K. Hubbard * % -> \n 31884f33deaSJordan K. Hubbard * \x -> \x for all x != % 31984f33deaSJordan K. Hubbard */ 32084f33deaSJordan K. Hubbard while (ch = *input_data++) { 32184f33deaSJordan K. Hubbard if (escaped) { 32284f33deaSJordan K. Hubbard if (ch != '%') 32384f33deaSJordan K. Hubbard putc('\\', out); 32484f33deaSJordan K. Hubbard } else { 32584f33deaSJordan K. Hubbard if (ch == '%') 32684f33deaSJordan K. Hubbard ch = '\n'; 32784f33deaSJordan K. Hubbard } 32884f33deaSJordan K. Hubbard 32984f33deaSJordan K. Hubbard if (!(escaped = (ch == '\\'))) { 33084f33deaSJordan K. Hubbard putc(ch, out); 33184f33deaSJordan K. Hubbard need_newline = (ch != '\n'); 33284f33deaSJordan K. Hubbard } 33384f33deaSJordan K. Hubbard } 33484f33deaSJordan K. Hubbard if (escaped) 33584f33deaSJordan K. Hubbard putc('\\', out); 33684f33deaSJordan K. Hubbard if (need_newline) 33784f33deaSJordan K. Hubbard putc('\n', out); 33884f33deaSJordan K. Hubbard 33984f33deaSJordan K. Hubbard /* close the pipe, causing an EOF condition. fclose causes 34084f33deaSJordan K. Hubbard * stdin_pipe[WRITE_PIPE] to be closed, too. 34184f33deaSJordan K. Hubbard */ 34284f33deaSJordan K. Hubbard fclose(out); 34384f33deaSJordan K. Hubbard 34484f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid())) 34584f33deaSJordan K. Hubbard exit(0); 34684f33deaSJordan K. Hubbard } 34784f33deaSJordan K. Hubbard 34884f33deaSJordan K. Hubbard /* close the pipe to the grandkiddie's stdin, since its wicked uncle 34984f33deaSJordan K. Hubbard * ernie back there has it open and will close it when he's done. 35084f33deaSJordan K. Hubbard */ 35184f33deaSJordan K. Hubbard close(stdin_pipe[WRITE_PIPE]); 35284f33deaSJordan K. Hubbard 35384f33deaSJordan K. Hubbard children++; 35484f33deaSJordan K. Hubbard 35584f33deaSJordan K. Hubbard /* 35684f33deaSJordan K. Hubbard * read output from the grandchild. it's stderr has been redirected to 35784f33deaSJordan K. Hubbard * it's stdout, which has been redirected to our pipe. if there is any 35884f33deaSJordan K. Hubbard * output, we'll be mailing it to the user whose crontab this is... 35984f33deaSJordan K. Hubbard * when the grandchild exits, we'll get EOF. 36084f33deaSJordan K. Hubbard */ 36184f33deaSJordan K. Hubbard 36284f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) 36384f33deaSJordan K. Hubbard 36484f33deaSJordan K. Hubbard /*local*/{ 36584f33deaSJordan K. Hubbard register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); 36684f33deaSJordan K. Hubbard register int ch = getc(in); 36784f33deaSJordan K. Hubbard 36884f33deaSJordan K. Hubbard if (ch != EOF) { 36984f33deaSJordan K. Hubbard register FILE *mail; 37084f33deaSJordan K. Hubbard register int bytes = 1; 37184f33deaSJordan K. Hubbard int status = 0; 37284f33deaSJordan K. Hubbard 37384f33deaSJordan K. Hubbard Debug(DPROC|DEXT, 37484f33deaSJordan K. Hubbard ("[%d] got data (%x:%c) from grandchild\n", 37584f33deaSJordan K. Hubbard getpid(), ch, ch)) 37684f33deaSJordan K. Hubbard 37784f33deaSJordan K. Hubbard /* get name of recipient. this is MAILTO if set to a 37884f33deaSJordan K. Hubbard * valid local username; USER otherwise. 37984f33deaSJordan K. Hubbard */ 38084f33deaSJordan K. Hubbard if (mailto) { 38184f33deaSJordan K. Hubbard /* MAILTO was present in the environment 38284f33deaSJordan K. Hubbard */ 38384f33deaSJordan K. Hubbard if (!*mailto) { 38484f33deaSJordan K. Hubbard /* ... but it's empty. set to NULL 38584f33deaSJordan K. Hubbard */ 38684f33deaSJordan K. Hubbard mailto = NULL; 38784f33deaSJordan K. Hubbard } 38884f33deaSJordan K. Hubbard } else { 38984f33deaSJordan K. Hubbard /* MAILTO not present, set to USER. 39084f33deaSJordan K. Hubbard */ 39184f33deaSJordan K. Hubbard mailto = usernm; 39284f33deaSJordan K. Hubbard } 39384f33deaSJordan K. Hubbard 39484f33deaSJordan K. Hubbard /* if we are supposed to be mailing, MAILTO will 39584f33deaSJordan K. Hubbard * be non-NULL. only in this case should we set 39684f33deaSJordan K. Hubbard * up the mail command and subjects and stuff... 39784f33deaSJordan K. Hubbard */ 39884f33deaSJordan K. Hubbard 39984f33deaSJordan K. Hubbard if (mailto) { 40084f33deaSJordan K. Hubbard register char **env; 40184f33deaSJordan K. Hubbard auto char mailcmd[MAX_COMMAND]; 40284f33deaSJordan K. Hubbard auto char hostname[MAXHOSTNAMELEN]; 40384f33deaSJordan K. Hubbard 40484f33deaSJordan K. Hubbard (void) gethostname(hostname, MAXHOSTNAMELEN); 405bdddbd2fSPaul Traina (void) snprintf(mailcmd, sizeof(mailcmd), 406bdddbd2fSPaul Traina MAILARGS, MAILCMD); 40784f33deaSJordan K. Hubbard if (!(mail = cron_popen(mailcmd, "w"))) { 40884f33deaSJordan K. Hubbard perror(MAILCMD); 40984f33deaSJordan K. Hubbard (void) _exit(ERROR_EXIT); 41084f33deaSJordan K. Hubbard } 41184f33deaSJordan K. Hubbard fprintf(mail, "From: root (Cron Daemon)\n"); 41284f33deaSJordan K. Hubbard fprintf(mail, "To: %s\n", mailto); 41384f33deaSJordan K. Hubbard fprintf(mail, "Subject: Cron <%s@%s> %s\n", 41484f33deaSJordan K. Hubbard usernm, first_word(hostname, "."), 41584f33deaSJordan K. Hubbard e->cmd); 41684f33deaSJordan K. Hubbard # if defined(MAIL_DATE) 41784f33deaSJordan K. Hubbard fprintf(mail, "Date: %s\n", 41884f33deaSJordan K. Hubbard arpadate(&TargetTime)); 41984f33deaSJordan K. Hubbard # endif /* MAIL_DATE */ 42084f33deaSJordan K. Hubbard for (env = e->envp; *env; env++) 42184f33deaSJordan K. Hubbard fprintf(mail, "X-Cron-Env: <%s>\n", 42284f33deaSJordan K. Hubbard *env); 42384f33deaSJordan K. Hubbard fprintf(mail, "\n"); 42484f33deaSJordan K. Hubbard 42584f33deaSJordan K. Hubbard /* this was the first char from the pipe 42684f33deaSJordan K. Hubbard */ 42784f33deaSJordan K. Hubbard putc(ch, mail); 42884f33deaSJordan K. Hubbard } 42984f33deaSJordan K. Hubbard 43084f33deaSJordan K. Hubbard /* we have to read the input pipe no matter whether 43184f33deaSJordan K. Hubbard * we mail or not, but obviously we only write to 43284f33deaSJordan K. Hubbard * mail pipe if we ARE mailing. 43384f33deaSJordan K. Hubbard */ 43484f33deaSJordan K. Hubbard 43584f33deaSJordan K. Hubbard while (EOF != (ch = getc(in))) { 43684f33deaSJordan K. Hubbard bytes++; 43784f33deaSJordan K. Hubbard if (mailto) 43884f33deaSJordan K. Hubbard putc(ch, mail); 43984f33deaSJordan K. Hubbard } 44084f33deaSJordan K. Hubbard 44184f33deaSJordan K. Hubbard /* only close pipe if we opened it -- i.e., we're 44284f33deaSJordan K. Hubbard * mailing... 44384f33deaSJordan K. Hubbard */ 44484f33deaSJordan K. Hubbard 44584f33deaSJordan K. Hubbard if (mailto) { 44684f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] closing pipe to mail\n", 44784f33deaSJordan K. Hubbard getpid())) 44884f33deaSJordan K. Hubbard /* Note: the pclose will probably see 44984f33deaSJordan K. Hubbard * the termination of the grandchild 45084f33deaSJordan K. Hubbard * in addition to the mail process, since 45184f33deaSJordan K. Hubbard * it (the grandchild) is likely to exit 45284f33deaSJordan K. Hubbard * after closing its stdout. 45384f33deaSJordan K. Hubbard */ 45484f33deaSJordan K. Hubbard status = cron_pclose(mail); 45584f33deaSJordan K. Hubbard } 45684f33deaSJordan K. Hubbard 45784f33deaSJordan K. Hubbard /* if there was output and we could not mail it, 45884f33deaSJordan K. Hubbard * log the facts so the poor user can figure out 45984f33deaSJordan K. Hubbard * what's going on. 46084f33deaSJordan K. Hubbard */ 46184f33deaSJordan K. Hubbard if (mailto && status) { 46284f33deaSJordan K. Hubbard char buf[MAX_TEMPSTR]; 46384f33deaSJordan K. Hubbard 464bdddbd2fSPaul Traina snprintf(buf, sizeof(buf), 46584f33deaSJordan K. Hubbard "mailed %d byte%s of output but got status 0x%04x\n", 46684f33deaSJordan K. Hubbard bytes, (bytes==1)?"":"s", 46784f33deaSJordan K. Hubbard status); 46884f33deaSJordan K. Hubbard log_it(usernm, getpid(), "MAIL", buf); 46984f33deaSJordan K. Hubbard } 47084f33deaSJordan K. Hubbard 47184f33deaSJordan K. Hubbard } /*if data from grandchild*/ 47284f33deaSJordan K. Hubbard 47384f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) 47484f33deaSJordan K. Hubbard 47584f33deaSJordan K. Hubbard fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 47684f33deaSJordan K. Hubbard } 47784f33deaSJordan K. Hubbard 47884f33deaSJordan K. Hubbard /* wait for children to die. 47984f33deaSJordan K. Hubbard */ 48084f33deaSJordan K. Hubbard for (; children > 0; children--) 48184f33deaSJordan K. Hubbard { 48284f33deaSJordan K. Hubbard WAIT_T waiter; 48384f33deaSJordan K. Hubbard PID_T pid; 48484f33deaSJordan K. Hubbard 48584f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n", 48684f33deaSJordan K. Hubbard getpid(), children)) 48784f33deaSJordan K. Hubbard pid = wait(&waiter); 48884f33deaSJordan K. Hubbard if (pid < OK) { 48984f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] no more grandchildren--mail written?\n", 49084f33deaSJordan K. Hubbard getpid())) 49184f33deaSJordan K. Hubbard break; 49284f33deaSJordan K. Hubbard } 49384f33deaSJordan K. Hubbard Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x", 49484f33deaSJordan K. Hubbard getpid(), pid, WEXITSTATUS(waiter))) 49584f33deaSJordan K. Hubbard if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 49684f33deaSJordan K. Hubbard Debug(DPROC, (", dumped core")) 49784f33deaSJordan K. Hubbard Debug(DPROC, ("\n")) 49884f33deaSJordan K. Hubbard } 49984f33deaSJordan K. Hubbard } 50084f33deaSJordan K. Hubbard 50184f33deaSJordan K. Hubbard 50284f33deaSJordan K. Hubbard static void 50384f33deaSJordan K. Hubbard do_univ(u) 50484f33deaSJordan K. Hubbard user *u; 50584f33deaSJordan K. Hubbard { 50684f33deaSJordan K. Hubbard #if defined(sequent) 50784f33deaSJordan K. Hubbard /* Dynix (Sequent) hack to put the user associated with 50884f33deaSJordan K. Hubbard * the passed user structure into the ATT universe if 50984f33deaSJordan K. Hubbard * necessary. We have to dig the gecos info out of 51084f33deaSJordan K. Hubbard * the user's password entry to see if the magic 51184f33deaSJordan K. Hubbard * "universe(att)" string is present. 51284f33deaSJordan K. Hubbard */ 51384f33deaSJordan K. Hubbard 51484f33deaSJordan K. Hubbard struct passwd *p; 51584f33deaSJordan K. Hubbard char *s; 51684f33deaSJordan K. Hubbard int i; 51784f33deaSJordan K. Hubbard 51884f33deaSJordan K. Hubbard p = getpwuid(u->uid); 51984f33deaSJordan K. Hubbard (void) endpwent(); 52084f33deaSJordan K. Hubbard 52184f33deaSJordan K. Hubbard if (p == NULL) 52284f33deaSJordan K. Hubbard return; 52384f33deaSJordan K. Hubbard 52484f33deaSJordan K. Hubbard s = p->pw_gecos; 52584f33deaSJordan K. Hubbard 52684f33deaSJordan K. Hubbard for (i = 0; i < 4; i++) 52784f33deaSJordan K. Hubbard { 52884f33deaSJordan K. Hubbard if ((s = strchr(s, ',')) == NULL) 52984f33deaSJordan K. Hubbard return; 53084f33deaSJordan K. Hubbard s++; 53184f33deaSJordan K. Hubbard } 53284f33deaSJordan K. Hubbard if (strcmp(s, "universe(att)")) 53384f33deaSJordan K. Hubbard return; 53484f33deaSJordan K. Hubbard 53584f33deaSJordan K. Hubbard (void) universe(U_ATT); 53684f33deaSJordan K. Hubbard #endif 53784f33deaSJordan K. Hubbard } 538