paul@vixie.UUCP (Paul Vixie Esq) (06/12/87)
#! /bin/sh ## This is a shell archive. Remove anything before this line, then unpack ## it by saving it into a file and typing "sh file". To overwrite existing ## files, type "sh file -c". You can also feed this as standard input via ## unshar, or by typing "sh <file". If this archive is complete, you will ## see the following message at the end: # "End of archive 3 (of 3)." # Contents: do_command.c # Wrapped by paul@vixie on Wed May 6 10:16:04 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f do_command.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"do_command.c\" else echo shar: Extracting \"do_command.c\" \(9944 characters\) sed "s/^X//" >do_command.c <<'END_OF_do_command.c' X#if !defined(lint) && !defined(LINT) Xstatic char rcsid[] = "$Header: do_command.c,v 1.4 87/05/02 17:33:35 paul Exp $"; X#endif X X/* $Source: /usr/src/local/vix/cron/do_command.c,v $ X * $Revision: 1.4 $ X * $Log: do_command.c,v $ X * Revision 1.4 87/05/02 17:33:35 paul X * baseline for mod.sources release X * X * Revision 1.3 87/04/09 00:03:58 paul X * improved data hiding, locality of declaration/references X * fixed a rs@mirror bug by redesigning the mailto stuff completely X * X * Revision 1.2 87/03/19 12:46:24 paul X * implemented suggestions from rs@mirror (Rich $alz): X * MAILTO="" means no mail should be sent X * various fixes of bugs or lint complaints X * put a To: line in the mail message X * X * Revision 1.1 87/01/26 23:47:00 paul X * Initial revision X */ X X/* Copyright 1987 by Vixie Enterprises X * All rights reserved X * X * Distribute freely, except: don't sell it, don't remove my name from the X * source or documentation (don't take credit for my work), mark your changes X * (don't get me blamed for your possible bugs), don't alter or remove this X * notice. Commercial redistribution is negotiable; contact me for details. X * X * Send bug reports, bug fixes, enhancements, requests, flames, etc., and X * I'll try to keep a version up to date. I can be reached as follows: X * Paul Vixie, Vixie Enterprises, 329 Noe Street, San Francisco, CA, 94114, X * (415) 864-7013, {ucbvax!dual,ames,ucsfmis,lll-crg,sun}!ptsfa!vixie!paul. X */ X X X#include "cron.h" X#include <signal.h> X#include <pwd.h> X#if defined(BSD) X# include <syslog.h> X# include <sys/wait.h> X#endif /*BSD*/ X Xvoid Xdo_command(cmd, u) X char *cmd; X user *u; X{ X extern int fork(), _exit(); X extern char *env_get(); X extern void child_process(); X X Debug(DPROC, ("do_command(%s, (%s,%d,%d))\n", X cmd, env_get(USERENV, u->envp), u->uid, u->gid)) X X /* fork to become asyncronous -- parent process is done immediately, X * and continues to run the normal cron code, which means return to X * tick(). the child and grandchild don't leave this function, alive. X * X * vfork() is unsuitable, since we have much to do, and the parent X * needs to be able to run off and fork other processes. X */ X if (fork() == 0) X { X child_process(cmd, u); X Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) X _exit(OK_EXIT); X } X Debug(DPROC, ("[%d] main process returning to work\n", getpid())) X} X X Xvoid Xchild_process(cmd, u) X char *cmd; X user *u; X{ X extern struct passwd *getpwnam(); X extern void sigpipe_func(), be_different(); X extern int VFORK(); X extern char *index(), *env_get(); X X auto int stdin_pipe[2], stdout_pipe[2]; X register char *input_data; X X X Debug(DPROC, ("[%d] child process running\n", getpid())) X X /* mark ourselves as different to PS command watchers by upshifting X * our program name. X */ X { X register char *pch; X X for (pch = PROGNAME; *pch; pch++) X *pch = MkUpper(*pch); X } X X#if defined(BSD) X /* our parent is watching for our death by catching SIGCHLD. we X * do not care to watch for our children's deaths this way -- we X * use wait() explictly. so we have to disable the signal (which X * was inherited from the parent. X * X * this isn't needed for system V, since our parent is already X * SIG_IGN on SIGCLD -- which, hopefully, will cause children to X * simply vanish when then die. X */ X (void) signal(SIGCHLD, SIG_IGN); X#endif /*BSD*/ X X /* create some pipes to talk to our future child X */ X pipe(stdin_pipe); /* child's stdin */ X pipe(stdout_pipe); /* child's stdout */ X X /* since we are a forked process, we can diddle the command string X * we were passed -- nobody else is going to use it again, right? X * X * if a % is present in the command, previous characters are the X * command, and subsequent characters are the additional input to X * the command. Subsequent %'s will be transformed into newlines, X * but that happens later. X */ X if (NULL == (input_data = index(cmd, '%'))) X { X /* no %. point input_data at a null string. X */ X input_data = ""; X } X else X { X /* % found. replace with a null (remember, we're a forked X * process and the string won't be reused), and increment X * input_data to point at the following character. X */ X *input_data++ = '\0'; X } X X /* fork again, this time so we can exec the users' command. X */ X if (VFORK() == 0) X { X Debug(DPROC, ("[%d] grandchild process VFORK()'ed\n", getpid())) X X /* get new pgrp, void tty, etc. X */ X be_different(); X X /* close the pipe ends that we won't use. this doesn't affect X * the parent, who has to read and write them; it keeps the X * kernel from recording us as a potential client TWICE -- X * which would keep it from sending SIGPIPE in otherwise X * appropriate circumstances. X */ X close(stdin_pipe[WRITE_PIPE]); X close(stdout_pipe[READ_PIPE]); X X /* grandchild process. make std{in,out} be the ends of X * pipes opened by our daddy; make stderr go to stdout. X */ X close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); X close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); X close(STDERR); dup2(STDOUT, STDERR); X X /* close the pipes we just dup'ed. The resources will remain, X * since they've been dup'ed... :-)... X */ X close(stdin_pipe[READ_PIPE]); X close(stdout_pipe[WRITE_PIPE]); X X /* set our directory, uid and gid. X */ X setgid(u->gid); /* set group first! */ X initgroups(env_get(USERENV, u->envp), u->gid); X setuid(u->uid); /* you aren't root after this... */ X chdir(env_get("HOME", u->envp)); X X /* exec the command. X */ X { X char *shell = env_get("SHELL", u->envp); X X execle(shell, shell, "-c", cmd, (char *)0, u->envp); X fprintf(stderr, "execl: couldn't exec `%s'\n", shell); X perror("execl"); X _exit(ERROR_EXIT); X } X } X X /* middle process, child of original cron, parent of process running X * the user's command. X */ X X Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) X X /* close the ends of the pipe that will only be referenced in the X * son process... X */ X close(stdin_pipe[READ_PIPE]); X close(stdout_pipe[WRITE_PIPE]); X X /* X * write, to the pipe connected to child's stdin, any input specified X * after a % in the crontab entry; we will assume that it will all X * fit, which means (on BSD anyway) that it should be 4096 bytes or X * less. this seems reasonable. while we copy, convert any X * additional %'s to newlines. when done, if some characters were X * written and the last one wasn't a newline, write a newline. X */ X X Debug(DPROC, ("[%d] child sending data to grandchild\n", getpid())) X X { X register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); X register int need_newline = FALSE; X register int escaped = FALSE; X X while (*input_data) X { X if (!escaped && *input_data == '%') X { X need_newline = FALSE; X putc('\n', out); X } X else X { X need_newline = TRUE; X putc(*input_data, out); X escaped = (*input_data == '\\'); X } X input_data++; X } X if (need_newline) X putc('\n', out); X X /* write 0-length message; this is EOF in a pipe (I hope) X */ X fflush(out); X write(stdin_pipe[WRITE_PIPE], "", 0); X fclose(out); X close(stdin_pipe[WRITE_PIPE]); X X Debug(DPROC, ("[%d] child done sending to grandchild\n", getpid())) X } X X /* X * read output from the grandchild. it's stderr has been redirected to X * it's stdout, which has been redirected to our pipe. if there is any X * output, we'll be mailing it to the user whose crontab this is... X * when the grandchild exits, we'll get EOF. X */ X X Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) X X { X register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); X register int ch; X X if (EOF != (ch = getc(in))) X { X FILE *mail; X char *usernm, *mailto; X X Debug(DPROC, ("[%d] got data (%x:%c) from grandchild\n", X getpid(), ch, ch)) X X /* get name of recipient. this is MAILTO if set to a X * valid local username; USER otherwise. X */ X usernm = env_get(USERENV, u->envp); X mailto = env_get("MAILTO", u->envp); X if (mailto) X { X /* MAILTO was present in the environment X */ X if (!*mailto) X { X /* ... but it's empty. set to NULL X */ X mailto = NULL; X } X else X { X /* not empty -- verify it, X * setting to USER if not valid. X */ X if (NULL == getpwnam(mailto)) X mailto = usernm; X endpwent(); X } X } X else X { X /* MAILTO not present, set to USER. X */ X mailto = usernm; X } X X /* if we are supposed to be mailing, MAILTO will X * be non-NULL. only in this case should we set X * up the mail command and subjects and stuff... X */ X X if (mailto) X { X extern FILE *popen(); X extern char *sprintf(); X register char **env; X auto char mailcmd[MAX_COMMAND]; X X (void) sprintf(mailcmd, MAILCMD, mailto); X if (!(mail = popen(mailcmd, "w"))) X { X perror(MAILCMD); X (void) _exit(ERROR_EXIT); X } X fprintf(mail, "To: %s\n", mailto); X fprintf(mail, "Subject: %s\n", MAILSUBJ); X fprintf(mail, "X-cron-cmd: <%s>\n", cmd); X for (env = u->envp; *env; env++) X fprintf(mail, "X-cron-env: <%s>\n", X *env); X fprintf(mail, "\n"); X X /* this was the first char from the pipe X */ X putc(ch, mail); X } X X /* we have to read the input pipe no matter whether X * we mail or not, but obviously we only write to X * mail pipe if we ARE mailing. X */ X X while (EOF != (ch = getc(in))) X { X if (mailto) X putc(ch, mail); X } X X /* only close pipe if we opened it -- i.e., we're X * mailing... X */ X X if (mailto) X pclose(mail); X X } /*if data from grandchild*/ X X Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) X X fclose(in); X close(stdout_pipe[READ_PIPE]); X } X X#if defined(BSD) X /* wait for child to die. X */ X { X int pid; X union wait waiter; X X Debug(DPROC, ("[%d] waiting for grandchild to finish\n", getpid())) X pid = wait(&waiter); X Debug(DPROC, ("[%d] grandchild #%d finished, status=%d\n", X getpid(), pid, waiter.w_status)) X } X#endif /*BSD*/ X} END_OF_do_command.c if test 9944 -ne `wc -c <do_command.c`; then echo shar: \"do_command.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0