"Fossies" - the Fresh Open Source Software Archive

Member "anacron-2.5.3/runjob.c" (9 Dec 2007, 7502 Bytes) of package /linux/misc/old/anacron.2.5.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "runjob.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2     Anacron - run commands periodically
    3     Copyright (C) 1998  Itai Tzur <itzur@actcom.co.il>
    4     Copyright (C) 1999  Sean 'Shaleh' Perry <shaleh@debian.org>
    5  
    6     This program is free software; you can redistribute it and/or modify
    7     it under the terms of the GNU General Public License as published by
    8     the Free Software Foundation; either version 2 of the License, or
    9     (at your option) any later version.
   10  
   11     This program is distributed in the hope that it will be useful,
   12     but WITHOUT ANY WARRANTY; without even the implied warranty of
   13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14     GNU General Public License for more details.
   15  
   16     You should have received a copy of the GNU General Public License
   17     along with this program; if not, write to the Free Software
   18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   19  
   20     The GNU General Public License can also be found in the file
   21     `COPYING' that comes with the Anacron source distribution.
   22 */
   23 
   24 
   25 #include <errno.h>
   26 #include <unistd.h>
   27 #include <stdlib.h>
   28 #include <sys/stat.h>
   29 #include <pwd.h>
   30 #include <sys/types.h>
   31 #include <sys/wait.h>
   32 #include <fcntl.h>
   33 #include <signal.h>
   34 #include <stdio.h>
   35 #include <string.h>
   36 #include "global.h"
   37 
   38 static int
   39 temp_file()
   40 /* Open a temporary file and return its file descriptor */
   41 {
   42     const int max_retries = 50;
   43     char *name;
   44     int fd, i;
   45 
   46     i = 0;
   47     name = NULL;
   48     do
   49     {
   50     i++;
   51     free(name);
   52     name = tempnam(NULL, NULL);
   53     if (name == NULL) die("Can't find a unique temporary filename");
   54     fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_APPEND,
   55           S_IRUSR | S_IWUSR);
   56     /* I'm not sure we actually need to be so persistent here */
   57     } while (fd == -1 && errno == EEXIST && i < max_retries);
   58     
   59     if (fd == -1) die_e("Can't open temporary file");
   60     if (unlink(name)) die_e("Can't unlink temporary file");
   61     free(name);
   62     fcntl(fd, F_SETFD, 1);    /* set close-on-exec flag */
   63     return fd;
   64 }
   65 
   66 static off_t
   67 file_size(int fd)
   68 /* Return the size of temporary file fd */
   69 {
   70     struct stat st;
   71 
   72     if (fstat(fd, &st)) die_e("Can't fstat temporary file");
   73     return st.st_size;
   74 }
   75 
   76 static char *
   77 username()
   78 {
   79     struct passwd *ps;
   80 
   81     ps = getpwuid(geteuid());
   82     if (ps == NULL) die_e("getpwuid() error");
   83     return ps->pw_name;
   84 }
   85 
   86 static void
   87 xputenv(const char *s)
   88 {
   89     if (putenv(s)) die_e("Can't set the environment");
   90 }
   91 
   92 static void
   93 setup_env(const job_rec *jr)
   94 /* Setup the environment for the job according to /etc/anacrontab */
   95 {
   96     env_rec *er;
   97 
   98     er = first_env_rec;
   99     if (er == NULL || jr->prev_env_rec == NULL) return;
  100     xputenv(er->assign);
  101     while (er != jr->prev_env_rec)
  102     {
  103     er = er->next;
  104     xputenv(er->assign);
  105     }
  106 }
  107 
  108 static void
  109 run_job(const job_rec *jr)
  110 /* This is called to start the job, after the fork */
  111 {
  112     setup_env(jr);
  113     /* setup stdout and stderr */
  114     xclose(1);
  115     xclose(2);
  116     if (dup2(jr->output_fd, 1) != 1 || dup2(jr->output_fd, 2) != 2)
  117     die_e("dup2() error");     /* dup2 also clears close-on-exec flag */
  118     in_background = 0;  /* now, errors will be mailed to the user */
  119     if (chdir("/")) die_e("Can't chdir to '/'");
  120 
  121     umask(old_umask);
  122     if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
  123     die_e("sigprocmask error");
  124     xcloselog();
  125     execl("/bin/sh", "/bin/sh", "-c", jr->command, (char *)NULL);
  126     die_e("execl() error");
  127 }
  128 
  129 static void
  130 xwrite(int fd, const char *string)
  131 /* Write (using write()) the string "string" to temporary file "fd".
  132  * Don't return on failure */
  133 {
  134     if (write(fd, string, strlen(string)) == -1)
  135     die_e("Can't write to temporary file");
  136 }
  137 
  138 static int
  139 xwait(pid_t pid , int *status)
  140 /* Check if child process "pid" has finished.  If it has, return 1 and its
  141  * exit status in "*status".  If not, return 0.
  142  */
  143 {
  144     pid_t r;
  145 
  146     r = waitpid(pid, status, WNOHANG);
  147     if (r == -1) die_e("waitpid() error");
  148     if (r == 0) return 0;
  149     return 1;
  150 }
  151 
  152 static void
  153 launch_mailer(job_rec *jr)
  154 {
  155     pid_t pid;
  156 
  157     pid = xfork();
  158     if (pid == 0)
  159     {
  160     /* child */
  161     in_background = 1;
  162     /* set stdin to the job's output */
  163     xclose(0);
  164     if (dup2(jr->output_fd, 0) != 0) die_e("Can't dup2()");
  165     if (lseek(0, 0, SEEK_SET) != 0) die_e("Can't lseek()");
  166     umask(old_umask);
  167     if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
  168         die_e("sigprocmask error");
  169     xcloselog();
  170 
  171     /* Here, I basically mirrored the way /usr/sbin/sendmail is called
  172      * by cron on a Debian system, except for the "-oem" and "-or0s"
  173      * options, which don't seem to be appropriate here.
  174      * Hopefully, this will keep all the MTAs happy. */
  175     execl(SENDMAIL, SENDMAIL, "-FAnacron", "-odi",
  176           username(), (char *)NULL);
  177     die_e("Can't exec " SENDMAIL);
  178     }
  179     /* parent */
  180     /* record mailer pid */
  181     jr->mailer_pid = pid;
  182     running_mailers++;
  183 }
  184 
  185 static void
  186 tend_mailer(job_rec *jr, int status)
  187 {
  188     if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
  189     complain("Tried to mail output of job `%s', "
  190          "but mailer process (" SENDMAIL ") exited with ststus %d",
  191          jr->ident, WEXITSTATUS(status));
  192     else if (!WIFEXITED(status) && WIFSIGNALED(status))
  193     complain("Tried to mail output of job `%s', "
  194          "but mailer process (" SENDMAIL ") got signal %d",
  195          jr->ident, WTERMSIG(status));
  196     else if (!WIFEXITED(status) && !WIFSIGNALED(status))
  197     complain("Tried to mail output of job `%s', "
  198          "but mailer process (" SENDMAIL ") terminated abnormally"
  199          , jr->ident);
  200 
  201     jr->mailer_pid = 0;
  202     running_mailers--;
  203 }
  204 
  205 void
  206 launch_job(job_rec *jr)
  207 {
  208     pid_t pid;
  209     int fd;
  210 
  211     /* create temporary file for stdout and stderr of the job */
  212     fd = jr->output_fd = temp_file();
  213     /* write mail header */
  214     xwrite(fd, "From: ");
  215     xwrite(fd, username());
  216     xwrite(fd, " (Anacron)\n");
  217     xwrite(fd, "To: ");
  218     xwrite(fd, username());
  219     xwrite(fd, "\n");
  220     xwrite(fd, "Subject: Anacron job '");
  221     xwrite(fd, jr->ident);
  222     xwrite(fd, "'\n\n");
  223     jr->mail_header_size = file_size(fd);
  224 
  225     pid = xfork();
  226     if (pid == 0)
  227     {
  228     /* child */
  229     in_background = 1;
  230     run_job(jr);
  231     /* execution never gets here */
  232     }
  233     /* parent */
  234     explain("Job `%s' started", jr->ident);
  235     jr->job_pid = pid;
  236     running_jobs++;
  237 }
  238 
  239 static void
  240 tend_job(job_rec *jr, int status)
  241 /* Take care of a finished job */
  242 {
  243     int mail_output;
  244     char *m;
  245 
  246     update_timestamp(jr);
  247     unlock(jr);
  248     if (file_size(jr->output_fd) > jr->mail_header_size) mail_output = 1;
  249     else mail_output = 0;
  250 
  251     m = mail_output ? " (mailing output)" : "";
  252     if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
  253     explain("Job `%s' terminated%s", jr->ident, m);
  254     else if (WIFEXITED(status))
  255     explain("Job `%s' terminated (exit status: %d)%s",
  256         jr->ident, WEXITSTATUS(status), m);
  257     else if (WIFSIGNALED(status))
  258     complain("Job `%s' terminated due to signal %d%s",
  259          jr->ident, WTERMSIG(status), m);
  260     else /* is this possible? */
  261     complain("Job `%s' terminated abnormally%s", jr->ident, m);
  262 
  263     jr->job_pid = 0;
  264     running_jobs--;
  265     if (mail_output) launch_mailer(jr);
  266     xclose(jr->output_fd);
  267 }
  268 
  269 void
  270 tend_children()
  271 /* This is called whenever we get a SIGCHLD.
  272  * Takes care of zombie children.
  273  */
  274 {
  275     int j;
  276     int status;
  277 
  278     j = 0;
  279     while (j < njobs)
  280     {
  281     if (job_array[j]->mailer_pid != 0 &&
  282         xwait(job_array[j]->mailer_pid, &status))
  283         tend_mailer(job_array[j], status);
  284     if (job_array[j]->job_pid != 0 &&
  285         xwait(job_array[j]->job_pid, &status))
  286         tend_job(job_array[j], status);
  287     j++;
  288     }
  289 }