root/anacron/main.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. print_version
  2. print_usage
  3. parse_opts
  4. xfork
  5. xopen
  6. xclose
  7. go_background
  8. handle_sigalrm
  9. handle_sigchld
  10. handle_sigusr1
  11. set_signal_handling
  12. wait_signal
  13. wait_children
  14. orderly_termination
  15. xsleep
  16. wait_jobs
  17. record_start_time
  18. time_till
  19. fake_jobs
  20. explain_intentions
  21. main

   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     Copyright (C) 2004  Pascal Hakim <pasc@redellipse.net>
   6 
   7     This program is free software; you can redistribute it and/or modify
   8     it under the terms of the GNU General Public License as published by
   9     the Free Software Foundation; either version 2 of the License, or
  10     (at your option) any later version.
  11 
  12     This program is distributed in the hope that it will be useful,
  13     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15     GNU General Public License for more details.
  16 
  17     You should have received a copy of the GNU General Public License along
  18     with this program; if not, write to the Free Software Foundation, Inc.,
  19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20  
  21     The GNU General Public License can also be found in the file
  22     `COPYING' that comes with the Anacron source distribution.
  23 */
  24 
  25 #include "config.h"
  26 
  27 #include <time.h>
  28 #include <sys/time.h>
  29 #include <stdio.h>
  30 #include <unistd.h>
  31 #include <signal.h>
  32 #include <fcntl.h>
  33 #include <sys/types.h>
  34 #include <sys/stat.h>
  35 #include <string.h>
  36 #include <stdlib.h>
  37 #include <locale.h>
  38 #include "global.h"
  39 #include "gregor.h"
  40 #include "cronie_common.h"
  41 
  42 pid_t primary_pid;
  43 int day_now;
  44 int year, month, day_of_month;                 /* date anacron started */
  45 
  46 char *program_name;
  47 char *anacrontab;
  48 char *spooldir;
  49 int serialize, force, update_only, now,
  50     no_daemon, quiet, testing_only;            /* command-line options */
  51 char **job_args;                               /* vector of "job" command-line arguments */
  52 int job_nargs;                                 /* number of these */
  53 char *defarg = "*";
  54 int in_background;                             /* are we in the background? */
  55 sigset_t old_sigmask;                          /* signal mask when started */
  56 
  57 job_rec *first_job_rec;
  58 env_rec *first_env_rec;
  59 
  60 time_t start_sec;                       /* time anacron started */
  61 static volatile int got_sigalrm, got_sigchld, got_sigusr1;
  62 int running_jobs, running_mailers;              /* , number of */
  63 int range_start = -1;
  64 int range_stop = -1;
  65 int preferred_hour = -1;
  66 
  67 static void
  68 print_version(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  69 {
  70     printf("Anacron from project %s\n"
  71            "Copyright (C) 1998  Itai Tzur <itzur@actcom.co.il>\n"
  72            "Copyright (C) 1999  Sean 'Shaleh' Perry <shaleh@debian.org>\n"
  73            "Copyright (C) 2004  Pascal Hakim <pasc@redellipse.net>\n"
  74            "\n"
  75            "Mail comments, suggestions and bug reports to <pasc@redellipse.net>."
  76            "\n\n", PACKAGE_STRING);
  77 }
  78 
  79 static void
  80 print_usage(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  81 {
  82     printf("Usage:\n");
  83     printf(" %s [options] [job] ...\n", program_name);
  84     printf(" %s -T [-t anacrontab-file]\n", program_name);
  85     printf("\nOptions:\n");
  86     printf(" -s         Serialize execution of jobs\n");
  87     printf(" -f         Force execution of jobs, even before their time\n");
  88     printf(" -n         Run jobs with no delay, implies -s\n");
  89     printf(" -d         Don't fork to the background\n");
  90     printf(" -q         Suppress stderr messages, only applicable with -d\n");
  91     printf(" -u         Update the timestamps without actually running anything\n");
  92     printf(" -V         Print version information\n");
  93     printf(" -h         Print this message\n");
  94     printf(" -t <file>  Use alternative anacrontab\n");
  95     printf(" -T         Test an anacrontab\n");
  96     printf(" -S <dir>   Select a different spool directory\n");
  97     printf("\nSee the anacron(8) manpage for more details.\n");
  98 }
  99 
 100 static void
 101 parse_opts(int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 102 /* Parse command-line options */
 103 {
 104     int opt;
 105 
 106     quiet = no_daemon = serialize = force = update_only = now = 0;
 107     opterr = 0;
 108     while ((opt = getopt(argc, argv, "sfundqt:TS:Vh")) != EOF)
 109     {
 110         switch (opt)
 111         {
 112         case 's':
 113             serialize = 1;
 114             break;
 115         case 'f':
 116             force = 1;
 117             break;
 118         case 'u':
 119             update_only = 1;
 120             break;
 121         case 'n':
 122             now = serialize = 1;
 123             break;
 124         case 'd':
 125             no_daemon = 1;
 126             break;
 127         case 'q':
 128             quiet = 1;
 129             break;
 130         case 't':
 131             anacrontab = strdup(optarg);
 132             break;
 133         case 'T':
 134             testing_only = 1;
 135             break;
 136         case 'S':
 137             spooldir = strdup(optarg);
 138             break;
 139         case 'V':
 140             print_version();
 141             exit(EXIT_SUCCESS);
 142         case 'h':
 143             print_usage();
 144             exit(EXIT_SUCCESS);
 145         case '?':
 146             fprintf(stderr, "%s: invalid option: %c\n",
 147                     program_name, optopt);
 148             fprintf(stderr, "type: `%s -h' for more information\n",
 149                     program_name);
 150             exit(FAILURE_EXIT);
 151         }
 152     }
 153     if (optind == argc)
 154     {
 155         /* no arguments. Equivalent to: `*' */
 156         job_nargs = 1;
 157         job_args = &defarg;
 158     }
 159     else
 160     {
 161         job_nargs = argc - optind;
 162         job_args = argv + optind;
 163     }
 164 }
 165 
 166 pid_t
 167 xfork(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 168 /* Like fork(), only never returns on failure */
 169 {
 170     pid_t pid;
 171 
 172     pid = fork();
 173     if (pid == -1) die_e("Can't fork");
 174     return pid;
 175 }
 176 
 177 int
 178 xopen(int fd, const char *file_name, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 179 /* Like open, only it:
 180  * a) never returns on failure, and
 181  * b) if "fd" is non-negative, expect the file to open
 182  *    on file-descriptor "fd".
 183  */
 184 {
 185     int rfd;
 186 
 187     rfd = open(file_name, flags);
 188     if (fd >= 0 && rfd != fd)
 189         die_e("Can't open %s on file-descriptor %d", file_name, fd);
 190     else if (rfd < 0)
 191         die_e("Can't open %s", file_name);
 192     return rfd;
 193 }
 194 
 195 void
 196 xclose(int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 /* Like close(), only doesn't return on failure */
 198 {
 199     if (close(fd)) die_e("Can't close file descriptor %d", fd);
 200 }
 201 
 202 static void
 203 go_background(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 204 /* Become a daemon. The foreground process exits successfully. */
 205 {
 206     pid_t pid;
 207 
 208     /* stdin is already closed */
 209 
 210     if (fclose(stdout)) die_e("Can't close stdout");
 211     xopen(1, "/dev/null", O_WRONLY);
 212 
 213     if (fclose(stderr)) die_e("Can't close stderr");
 214     xopen(2, "/dev/null", O_WRONLY);
 215 
 216     pid = xfork();
 217     if (pid != 0)
 218     {
 219         /* parent */
 220         exit(EXIT_SUCCESS);
 221     }
 222     else
 223     {
 224         /* child */
 225         primary_pid = getpid();
 226         if (setsid() == -1) die_e("setsid() error");
 227         in_background = 1;
 228     }
 229 }
 230 
 231 static void
 232 handle_sigalrm(int unused ATTRIBUTE_UNUSED)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     got_sigalrm = 1;
 235 }
 236 
 237 static void
 238 handle_sigchld(int unused ATTRIBUTE_UNUSED)
     /* [previous][next][first][last][top][bottom][index][help]  */
 239 {
 240     got_sigchld = 1;
 241 }
 242 
 243 static void
 244 handle_sigusr1(int unused ATTRIBUTE_UNUSED)
     /* [previous][next][first][last][top][bottom][index][help]  */
 245 {
 246     got_sigusr1 = 1;
 247 }
 248 
 249 static void
 250 set_signal_handling(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 251 /* We only use SIGALRM, SIGCHLD and SIGUSR1, and we unblock them only
 252  * in wait_signal().
 253  */
 254 {
 255     sigset_t ss;
 256     struct sigaction sa;
 257 
 258     got_sigalrm = got_sigchld = got_sigusr1 = 0;
 259 
 260     /* block SIGALRM, SIGCHLD and SIGUSR1 */
 261     if (sigemptyset(&ss) ||
 262         sigaddset(&ss, SIGALRM) ||
 263         sigaddset(&ss, SIGCHLD) ||
 264         sigaddset(&ss, SIGUSR1)) die_e("sigset error");
 265     if (sigprocmask(SIG_BLOCK, &ss, NULL)) die_e ("sigprocmask error");
 266 
 267     /* setup SIGALRM handler */
 268     sa.sa_handler = handle_sigalrm;
 269     sa.sa_mask = ss;
 270     sa.sa_flags = 0;
 271     if (sigaction(SIGALRM, &sa, NULL)) die_e("sigaction error");
 272 
 273     /* setup SIGCHLD handler */
 274     sa.sa_handler = handle_sigchld;
 275     sa.sa_mask = ss;
 276     sa.sa_flags = SA_NOCLDSTOP;
 277     if (sigaction(SIGCHLD, &sa, NULL)) die_e("sigaction error");
 278 
 279     /* setup SIGUSR1 handler */
 280     sa.sa_handler = handle_sigusr1;
 281     sa.sa_mask = ss;
 282     sa.sa_flags = 0;
 283     if (sigaction(SIGUSR1, &sa, NULL)) die_e("sigaction error");
 284 }
 285 
 286 static void
 287 wait_signal(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 288 /* Return after a signal is caught */
 289 {
 290     sigset_t ss;
 291 
 292     if (sigprocmask(0, NULL, &ss)) die_e("sigprocmask error");
 293     if (sigdelset(&ss, SIGALRM) ||
 294         sigdelset(&ss, SIGCHLD) ||
 295         sigdelset(&ss, SIGUSR1)) die_e("sigset error");
 296     sigsuspend(&ss);
 297 }
 298 
 299 static void
 300 wait_children(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 301 /* Wait until we have no more children (of any kind) */
 302 {
 303     while (running_jobs > 0 || running_mailers > 0)
 304     {
 305         wait_signal();
 306         if (got_sigchld) tend_children();
 307         got_sigchld = 0;
 308         if (got_sigusr1) explain("Received SIGUSR1");
 309         got_sigusr1 = 0;
 310     }
 311 }
 312 
 313 static void
 314 orderly_termination(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 315 /* Execution is diverted here, when we get SIGUSR1 */
 316 {
 317     explain("Received SIGUSR1");
 318     got_sigusr1 = 0;
 319     wait_children();
 320     explain("Exited");
 321     exit(EXIT_SUCCESS);
 322 }
 323 
 324 static void
 325 xsleep(unsigned int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 326 /* Sleep for n seconds, servicing SIGCHLDs and SIGUSR1s in the meantime.
 327  * If n=0, return immediately.
 328  */
 329 {
 330     if (n == 0) return;
 331     alarm(n);
 332     do
 333     {
 334         wait_signal();
 335         if (got_sigchld) tend_children();
 336         got_sigchld = 0;
 337         if (got_sigusr1) orderly_termination();
 338     }
 339     while (!got_sigalrm);
 340     got_sigalrm = 0;
 341 }
 342 
 343 static void
 344 wait_jobs(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 345 /* Wait until there are no running jobs,
 346  * servicing SIGCHLDs and SIGUSR1s in the meantime.
 347  */
 348 {
 349     while (running_jobs > 0)
 350     {
 351         wait_signal();
 352         if (got_sigchld) tend_children();
 353         got_sigchld = 0;
 354         if (got_sigusr1) orderly_termination();
 355     }
 356 }
 357 
 358 static void
 359 record_start_time(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 360 {
 361     struct tm *tm_now;
 362 
 363     start_sec = time(NULL);
 364     tm_now = localtime(&start_sec);
 365     year = tm_now->tm_year + 1900;
 366     month = tm_now->tm_mon + 1;
 367     day_of_month = tm_now->tm_mday;
 368     day_now = day_num(year, month, day_of_month);
 369     if (day_now == -1) die("Invalid date (this is really embarrassing)");
 370     if (!update_only && !testing_only)
 371         explain("Anacron started on %04d-%02d-%02d",
 372                 year, month, day_of_month);
 373 }
 374 
 375 static unsigned int
 376 time_till(job_rec *jr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 377 /* Return the number of seconds that we have to wait until it's time
 378  * to start job jr.
 379  */
 380 {
 381     time_t tj, tn;
 382 
 383     if (now) return 0;
 384     tn = time(NULL);
 385     tj = start_sec + (time_t)jr->delay * 60;
 386     if (tj < tn) return 0;
 387     if (tj - tn > 3600*24)
 388     {
 389         explain("System time manipulation detected, job `%s' will run immediately",
 390             jr->ident);
 391         return 0;
 392     }
 393     return (unsigned int)(tj - tn);
 394 }
 395 
 396 static void
 397 fake_jobs(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 398 {
 399     int j;
 400 
 401     j = 0;
 402     while (j < njobs)
 403     {
 404         fake_job(job_array[j]);
 405         explain("Updated timestamp for job `%s' to %04d-%02d-%02d",
 406                 job_array[j]->ident, year, month, day_of_month);
 407         j++;
 408     }
 409 }
 410 
 411 static void
 412 explain_intentions(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 413 {
 414     int j;
 415 
 416     j = 0;
 417     while (j < njobs)
 418     {
 419         if (now)
 420         {
 421             explain("Will run job `%s'", job_array[j]->ident);
 422         }
 423         else
 424         {
 425             explain("Will run job `%s' in %d min.",
 426                     job_array[j]->ident, job_array[j]->delay);
 427         }
 428         j++;
 429     }
 430     if (serialize && njobs > 0)
 431         explain("Jobs will be executed sequentially");
 432 }
 433 
 434 int
 435 main(int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 436 {
 437     int j;
 438     int cwd;
 439     struct timeval tv;
 440     struct timezone tz;
 441 
 442     anacrontab = NULL;
 443     spooldir = NULL;
 444 
 445     setlocale(LC_ALL, "");
 446 
 447     if (gettimeofday(&tv, &tz) != 0)
 448         explain("Can't get exact time, failure.");
 449 
 450     srandom((unsigned int)(getpid() + tv.tv_usec));
 451 
 452     if((program_name = strrchr(argv[0], '/')) == NULL)
 453         program_name = argv[0];
 454     else
 455         ++program_name; /* move pointer to char after '/' */
 456 
 457     parse_opts(argc, argv);
 458 
 459     if (anacrontab == NULL)
 460         anacrontab = strdup(ANACRONTAB);
 461 
 462     if (spooldir == NULL)
 463         spooldir = strdup(ANACRON_SPOOL_DIR);
 464 
 465     if ((cwd = open ("./", O_RDONLY)) == -1) {
 466         die_e ("Can't save current directory");
 467     }
 468 
 469     in_background = 0;
 470 
 471     if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir );
 472 
 473     if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error");
 474 
 475     if (fclose(stdin)) die_e("Can't close stdin");
 476     xopen(STDIN_FILENO, "/dev/null", O_RDONLY);
 477 
 478     if (!no_daemon && !testing_only)
 479         go_background();
 480     else
 481         primary_pid = getpid();
 482 
 483     record_start_time();
 484     read_tab(cwd);
 485     close(cwd);
 486     arrange_jobs();
 487 
 488     if (testing_only)
 489     {
 490         if (complaints) exit (EXIT_FAILURE);
 491         
 492         exit (EXIT_SUCCESS);
 493     }
 494 
 495     if (update_only)
 496     {
 497         fake_jobs();
 498         exit(EXIT_SUCCESS);
 499     }
 500 
 501     explain_intentions();
 502     set_signal_handling();
 503     running_jobs = running_mailers = 0;
 504     for(j = 0; j < njobs; ++j)
 505     {
 506         xsleep(time_till(job_array[j]));
 507         if (serialize) wait_jobs();
 508         launch_job(job_array[j]);
 509     }
 510     wait_children();
 511     explain("Normal exit (%d job%s run)", njobs, njobs == 1 ? "" : "s");
 512     exit(EXIT_SUCCESS);
 513 }

/* [previous][next][first][last][top][bottom][index][help]  */