home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8706 / 12 < prev    next >
Internet Message Format  |  1993-09-01  |  12KB

  1. From mipos3!intelca!oliveb!pyramid!uccba!hal!ncoast!allbery Mon Jun 15 08:15:36 PDT 1987
  2. Article 60 of comp.sources.misc:
  3. Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site mipos3.UUCP
  4. Path: mipos3!intelca!oliveb!pyramid!uccba!hal!ncoast!allbery
  5. >From: paul@vixie.UUCP (Paul Vixie Esq)
  6. Newsgroups: comp.sources.misc
  7. Subject: PD Cron 03/03
  8. Message-ID: <2640@ncoast.UUCP>
  9. Date: 11 Jun 87 21:48:57 GMT
  10. Date-Received: 13 Jun 87 13:44:36 GMT
  11. Sender: allbery@ncoast.UUCP
  12. Lines: 412
  13. Approved: allbery@ncoast.UUCP
  14. X-Archive: comp.sources.misc/8706/12
  15.  
  16. #! /bin/sh
  17. ##  This is a shell archive.  Remove anything before this line, then unpack
  18. ##  it by saving it into a file and typing "sh file".  To overwrite existing
  19. ##  files, type "sh file -c".  You can also feed this as standard input via
  20. ##  unshar, or by typing "sh <file".  If this archive is complete, you will
  21. ##  see the following message at the end:
  22. #        "End of archive 3 (of 3)."
  23. # Contents:  do_command.c
  24. # Wrapped by paul@vixie on Wed May  6 10:16:04 1987
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f do_command.c -a "${1}" != "-c" ; then 
  27.   echo shar: Will not over-write existing file \"do_command.c\"
  28. else
  29. echo shar: Extracting \"do_command.c\" \(9944 characters\)
  30. sed "s/^X//" >do_command.c <<'END_OF_do_command.c'
  31. X#if !defined(lint) && !defined(LINT)
  32. Xstatic char rcsid[] = "$Header: do_command.c,v 1.4 87/05/02 17:33:35 paul Exp $";
  33. X#endif
  34. X
  35. X/* $Source: /usr/src/local/vix/cron/do_command.c,v $
  36. X * $Revision: 1.4 $
  37. X * $Log:    do_command.c,v $
  38. X * Revision 1.4  87/05/02  17:33:35  paul
  39. X * baseline for mod.sources release
  40. X * 
  41. X * Revision 1.3  87/04/09  00:03:58  paul
  42. X * improved data hiding, locality of declaration/references
  43. X * fixed a rs@mirror bug by redesigning the mailto stuff completely
  44. X * 
  45. X * Revision 1.2  87/03/19  12:46:24  paul
  46. X * implemented suggestions from rs@mirror (Rich $alz):
  47. X *    MAILTO="" means no mail should be sent
  48. X *    various fixes of bugs or lint complaints
  49. X *    put a To: line in the mail message
  50. X * 
  51. X * Revision 1.1  87/01/26  23:47:00  paul
  52. X * Initial revision
  53. X */
  54. X
  55. X/* Copyright 1987 by Vixie Enterprises
  56. X * All rights reserved
  57. X *
  58. X * Distribute freely, except: don't sell it, don't remove my name from the
  59. X * source or documentation (don't take credit for my work), mark your changes
  60. X * (don't get me blamed for your possible bugs), don't alter or remove this
  61. X * notice.  Commercial redistribution is negotiable; contact me for details.
  62. X *
  63. X * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  64. X * I'll try to keep a version up to date.  I can be reached as follows:
  65. X * Paul Vixie, Vixie Enterprises, 329 Noe Street, San Francisco, CA, 94114,
  66. X * (415) 864-7013, {ucbvax!dual,ames,ucsfmis,lll-crg,sun}!ptsfa!vixie!paul.
  67. X */
  68. X
  69. X
  70. X#include "cron.h"
  71. X#include <signal.h>
  72. X#include <pwd.h>
  73. X#if defined(BSD)
  74. X# include <syslog.h>
  75. X# include <sys/wait.h>
  76. X#endif /*BSD*/
  77. X
  78. Xvoid
  79. Xdo_command(cmd, u)
  80. X    char    *cmd;
  81. X    user    *u;
  82. X{
  83. X    extern int    fork(), _exit();
  84. X    extern char    *env_get();
  85. X    extern void    child_process();
  86. X
  87. X    Debug(DPROC, ("do_command(%s, (%s,%d,%d))\n",
  88. X        cmd, env_get(USERENV, u->envp), u->uid, u->gid))
  89. X
  90. X    /* fork to become asyncronous -- parent process is done immediately,
  91. X     * and continues to run the normal cron code, which means return to
  92. X     * tick().  the child and grandchild don't leave this function, alive.
  93. X     *
  94. X     * vfork() is unsuitable, since we have much to do, and the parent
  95. X     * needs to be able to run off and fork other processes.
  96. X     */
  97. X    if (fork() == 0)
  98. X    {
  99. X        child_process(cmd, u);
  100. X        Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
  101. X        _exit(OK_EXIT);
  102. X    }
  103. X    Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
  104. X}
  105. X
  106. X
  107. Xvoid
  108. Xchild_process(cmd, u)
  109. X    char    *cmd;
  110. X    user    *u;
  111. X{
  112. X    extern struct passwd    *getpwnam();
  113. X    extern void    sigpipe_func(), be_different();
  114. X    extern int    VFORK();
  115. X    extern char    *index(), *env_get();
  116. X
  117. X    auto int    stdin_pipe[2], stdout_pipe[2];
  118. X    register char    *input_data;
  119. X
  120. X
  121. X    Debug(DPROC, ("[%d] child process running\n", getpid()))
  122. X
  123. X    /* mark ourselves as different to PS command watchers by upshifting
  124. X     * our program name.
  125. X     */
  126. X    {
  127. X        register char    *pch;
  128. X
  129. X        for (pch = PROGNAME;  *pch;  pch++)
  130. X            *pch = MkUpper(*pch);
  131. X    }
  132. X
  133. X#if defined(BSD)
  134. X    /* our parent is watching for our death by catching SIGCHLD.  we
  135. X     * do not care to watch for our children's deaths this way -- we
  136. X     * use wait() explictly.  so we have to disable the signal (which
  137. X     * was inherited from the parent.
  138. X     *
  139. X     * this isn't needed for system V, since our parent is already
  140. X     * SIG_IGN on SIGCLD -- which, hopefully, will cause children to
  141. X     * simply vanish when then die.
  142. X     */
  143. X    (void) signal(SIGCHLD, SIG_IGN);
  144. X#endif /*BSD*/
  145. X
  146. X    /* create some pipes to talk to our future child
  147. X     */
  148. X    pipe(stdin_pipe);    /* child's stdin */
  149. X    pipe(stdout_pipe);    /* child's stdout */
  150. X    
  151. X    /* since we are a forked process, we can diddle the command string
  152. X     * we were passed -- nobody else is going to use it again, right?
  153. X     *
  154. X     * if a % is present in the command, previous characters are the
  155. X     * command, and subsequent characters are the additional input to
  156. X     * the command.  Subsequent %'s will be transformed into newlines,
  157. X     * but that happens later.
  158. X     */
  159. X    if (NULL == (input_data = index(cmd, '%')))
  160. X    {
  161. X        /* no %.  point input_data at a null string.
  162. X         */
  163. X        input_data = "";
  164. X    }
  165. X    else
  166. X    {
  167. X        /* % found.  replace with a null (remember, we're a forked
  168. X         * process and the string won't be reused), and increment
  169. X         * input_data to point at the following character.
  170. X         */
  171. X        *input_data++ = '\0';
  172. X    }
  173. X
  174. X    /* fork again, this time so we can exec the users' command.
  175. X     */
  176. X    if (VFORK() == 0)
  177. X    {
  178. X        Debug(DPROC, ("[%d] grandchild process VFORK()'ed\n", getpid()))
  179. X
  180. X        /* get new pgrp, void tty, etc.
  181. X         */
  182. X        be_different();
  183. X
  184. X        /* close the pipe ends that we won't use.  this doesn't affect
  185. X         * the parent, who has to read and write them; it keeps the
  186. X         * kernel from recording us as a potential client TWICE --
  187. X         * which would keep it from sending SIGPIPE in otherwise
  188. X         * appropriate circumstances.
  189. X         */
  190. X        close(stdin_pipe[WRITE_PIPE]);
  191. X        close(stdout_pipe[READ_PIPE]);
  192. X
  193. X        /* grandchild process.  make std{in,out} be the ends of
  194. X         * pipes opened by our daddy; make stderr go to stdout.
  195. X         */
  196. X        close(STDIN);    dup2(stdin_pipe[READ_PIPE], STDIN);
  197. X        close(STDOUT);    dup2(stdout_pipe[WRITE_PIPE], STDOUT);
  198. X        close(STDERR);    dup2(STDOUT, STDERR);
  199. X
  200. X        /* close the pipes we just dup'ed.  The resources will remain,
  201. X         * since they've been dup'ed... :-)...
  202. X         */
  203. X        close(stdin_pipe[READ_PIPE]);
  204. X        close(stdout_pipe[WRITE_PIPE]);
  205. X
  206. X        /* set our directory, uid and gid.
  207. X         */
  208. X        setgid(u->gid);        /* set group first! */
  209. X        initgroups(env_get(USERENV, u->envp), u->gid);
  210. X        setuid(u->uid);        /* you aren't root after this... */
  211. X        chdir(env_get("HOME", u->envp));
  212. X
  213. X        /* exec the command.
  214. X         */
  215. X        {
  216. X            char    *shell = env_get("SHELL", u->envp);
  217. X
  218. X            execle(shell, shell, "-c", cmd, (char *)0, u->envp);
  219. X            fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
  220. X            perror("execl");
  221. X            _exit(ERROR_EXIT);
  222. X        }
  223. X    }
  224. X
  225. X    /* middle process, child of original cron, parent of process running
  226. X     * the user's command.
  227. X     */
  228. X
  229. X    Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
  230. X
  231. X    /* close the ends of the pipe that will only be referenced in the
  232. X     * son process...
  233. X     */
  234. X    close(stdin_pipe[READ_PIPE]);
  235. X    close(stdout_pipe[WRITE_PIPE]);
  236. X
  237. X    /*
  238. X     * write, to the pipe connected to child's stdin, any input specified
  239. X     * after a % in the crontab entry; we will assume that it will all
  240. X     * fit, which means (on BSD anyway) that it should be 4096 bytes or
  241. X     * less.  this seems reasonable.  while we copy, convert any
  242. X     * additional %'s to newlines.  when done, if some characters were
  243. X     * written and the last one wasn't a newline, write a newline.
  244. X     */
  245. X
  246. X    Debug(DPROC, ("[%d] child sending data to grandchild\n", getpid()))
  247. X
  248. X    {
  249. X        register FILE    *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
  250. X        register int    need_newline = FALSE;
  251. X        register int    escaped = FALSE;
  252. X
  253. X        while (*input_data)
  254. X        {
  255. X            if (!escaped && *input_data == '%')
  256. X            {
  257. X                need_newline = FALSE;
  258. X                putc('\n', out);
  259. X            }
  260. X            else
  261. X            {
  262. X                need_newline = TRUE;
  263. X                putc(*input_data, out);
  264. X                escaped = (*input_data == '\\');
  265. X            }
  266. X            input_data++;
  267. X        }
  268. X        if (need_newline)
  269. X            putc('\n', out);
  270. X
  271. X        /* write 0-length message; this is EOF in a pipe (I hope)
  272. X         */
  273. X        fflush(out);
  274. X        write(stdin_pipe[WRITE_PIPE], "", 0);
  275. X        fclose(out);
  276. X        close(stdin_pipe[WRITE_PIPE]);
  277. X
  278. X        Debug(DPROC, ("[%d] child done sending to grandchild\n", getpid()))
  279. X    }
  280. X
  281. X    /*
  282. X     * read output from the grandchild.  it's stderr has been redirected to
  283. X     * it's stdout, which has been redirected to our pipe.  if there is any
  284. X     * output, we'll be mailing it to the user whose crontab this is...
  285. X     * when the grandchild exits, we'll get EOF.
  286. X     */
  287. X
  288. X    Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
  289. X
  290. X    {
  291. X        register FILE    *in = fdopen(stdout_pipe[READ_PIPE], "r");
  292. X        register int    ch;
  293. X
  294. X        if (EOF != (ch = getc(in)))
  295. X        {
  296. X            FILE    *mail;
  297. X            char    *usernm, *mailto;
  298. X
  299. X            Debug(DPROC, ("[%d] got data (%x:%c) from grandchild\n",
  300. X                getpid(), ch, ch))
  301. X
  302. X            /* get name of recipient.  this is MAILTO if set to a
  303. X             * valid local username; USER otherwise.
  304. X             */
  305. X            usernm = env_get(USERENV, u->envp);
  306. X            mailto = env_get("MAILTO", u->envp);
  307. X            if (mailto)
  308. X            {
  309. X                /* MAILTO was present in the environment
  310. X                 */
  311. X                if (!*mailto)
  312. X                {
  313. X                    /* ... but it's empty. set to NULL
  314. X                     */
  315. X                    mailto = NULL;
  316. X                }
  317. X                else
  318. X                {
  319. X                    /* not empty -- verify it,
  320. X                     * setting to USER if not valid.
  321. X                     */
  322. X                    if (NULL == getpwnam(mailto))
  323. X                        mailto = usernm;
  324. X                    endpwent();
  325. X                }
  326. X            }
  327. X            else
  328. X            {
  329. X                /* MAILTO not present, set to USER.
  330. X                 */
  331. X                mailto = usernm;
  332. X            }
  333. X        
  334. X            /* if we are supposed to be mailing, MAILTO will
  335. X             * be non-NULL.  only in this case should we set
  336. X             * up the mail command and subjects and stuff...
  337. X             */
  338. X
  339. X            if (mailto)
  340. X            {
  341. X                extern FILE    *popen();
  342. X                extern char    *sprintf();
  343. X                register char    **env;
  344. X                auto char    mailcmd[MAX_COMMAND];
  345. X
  346. X                (void) sprintf(mailcmd, MAILCMD, mailto);
  347. X                if (!(mail = popen(mailcmd, "w")))
  348. X                {
  349. X                    perror(MAILCMD);
  350. X                    (void) _exit(ERROR_EXIT);
  351. X                }
  352. X                fprintf(mail, "To: %s\n", mailto);
  353. X                fprintf(mail, "Subject: %s\n", MAILSUBJ);
  354. X                fprintf(mail, "X-cron-cmd: <%s>\n", cmd);
  355. X                for (env = u->envp;  *env;  env++)
  356. X                    fprintf(mail, "X-cron-env: <%s>\n",
  357. X                        *env);
  358. X                fprintf(mail, "\n");
  359. X
  360. X                /* this was the first char from the pipe
  361. X                 */
  362. X                putc(ch, mail);
  363. X            }
  364. X
  365. X            /* we have to read the input pipe no matter whether
  366. X             * we mail or not, but obviously we only write to
  367. X             * mail pipe if we ARE mailing.
  368. X             */
  369. X
  370. X            while (EOF != (ch = getc(in)))
  371. X            {
  372. X                if (mailto)
  373. X                    putc(ch, mail);
  374. X            }
  375. X
  376. X            /* only close pipe if we opened it -- i.e., we're
  377. X             * mailing...
  378. X             */
  379. X
  380. X            if (mailto)
  381. X                pclose(mail);
  382. X
  383. X        } /*if data from grandchild*/
  384. X
  385. X        Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
  386. X
  387. X        fclose(in);
  388. X        close(stdout_pipe[READ_PIPE]);
  389. X    }
  390. X
  391. X#if defined(BSD)
  392. X    /* wait for child to die.
  393. X     */
  394. X    {
  395. X        int        pid;
  396. X        union wait    waiter;
  397. X
  398. X        Debug(DPROC, ("[%d] waiting for grandchild to finish\n", getpid()))
  399. X        pid = wait(&waiter);
  400. X        Debug(DPROC, ("[%d] grandchild #%d finished, status=%d\n",
  401. X            getpid(), pid, waiter.w_status))
  402. X    }
  403. X#endif /*BSD*/
  404. X}
  405. END_OF_do_command.c
  406. if test 9944 -ne `wc -c <do_command.c`; then
  407.     echo shar: \"do_command.c\" unpacked with wrong size!
  408. fi
  409. # end of overwriting check
  410. fi
  411. echo shar: End of archive 3 \(of 3\).
  412. cp /dev/null ark3isdone
  413. MISSING=""
  414. for I in 1 2 3 ; do
  415.     if test ! -f ark${I}isdone ; then
  416.     MISSING="${MISSING} ${I}"
  417.     fi
  418. done
  419. if test "${MISSING}" = "" ; then
  420.     echo You have unpacked all 3 archives.
  421.     rm -f ark[1-9]isdone
  422. else
  423.     echo You still need to unpack the following archives:
  424.     echo "        " ${MISSING}
  425. fi
  426. ##  End of shell archive.
  427. exit 0
  428.  
  429.  
  430.