root/anacron/readtab.c

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

DEFINITIONS

This source file includes following definitions.
  1. xmalloc
  2. conv2int
  3. read_tab_line
  4. job_arg_num
  5. register_env
  6. register_job
  7. register_period_job
  8. unbiased_rand
  9. parse_tab_line
  10. read_tab
  11. execution_order
  12. arrange_jobs

   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 
  26 /*  /etc/anacrontab parsing, and job sorting
  27  */
  28 
  29 #include <string.h>
  30 #include <errno.h>
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <obstack.h>
  34 #include <limits.h>
  35 #include <fnmatch.h>
  36 #include <unistd.h>
  37 #include <signal.h>
  38 #include "global.h"
  39 #include "matchrx.h"
  40 
  41 static struct obstack input_o;   /* holds input line */
  42 static struct obstack tab_o;    /* holds processed data read from anacrontab */
  43 static FILE *tab;
  44 job_rec **job_array;
  45 int njobs;                       /* number of jobs to run */
  46 static int jobs_read;            /* number of jobs read */
  47 static int line_num;             /* current line in anacrontab */
  48 static job_rec *last_job_rec;    /* last job stored in memory, at the moment */
  49 static env_rec *last_env_rec;    /* last environment assignment stored */
  50 
  51 static int random_number = 0;
  52 
  53 /* some definitions for the obstack macros */
  54 #define obstack_chunk_alloc xmalloc
  55 #define obstack_chunk_free free
  56 
  57 static void *
  58 xmalloc (size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
  59 /* Just like standard malloc(), only never returns NULL. */
  60 {
  61     void * ptr;
  62 
  63     ptr = malloc(size);
  64     if (ptr == NULL)
  65         die("Memory exhausted");
  66     return ptr;
  67 }
  68 
  69 static int
  70 conv2int(const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
  71 /* Return the int or -1 on over/under-flow
  72  */
  73 {
  74     long l;
  75 
  76     errno = 0;
  77     l = strtol(s, NULL, 10);
  78     /* we use negative as error, so I am really returning unsigned int */
  79     if (errno == ERANGE || l < 0 || l > INT_MAX) return - 1;
  80     return (int)l;
  81 }
  82 
  83 static char *
  84 read_tab_line (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  85 /* Read one line and return a pointer to it.
  86 Return NULL if no more lines.
  87  */
  88 {
  89     int c, prev=0;
  90 
  91     if (feof(tab)) return NULL;
  92     while (1)
  93     {
  94         c = getc(tab);
  95         if ((c == '\n' && prev != '\\') || c == EOF)
  96         {
  97             if (0 != prev) obstack_1grow(&input_o, (char)prev);
  98             break;
  99         }
 100 
 101         if ('\\' != prev && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)prev);
 102         else if ('\n' == prev) obstack_1grow(&input_o, ' ');
 103 
 104         prev = c;
 105     }
 106     if (ferror(tab)) die_e("Error reading %s", anacrontab);
 107     obstack_1grow(&input_o, '\0');
 108     return obstack_finish(&input_o);
 109 }
 110 
 111 static int
 112 job_arg_num(const char *ident)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 /* Return the command-line-argument number referring to this job-identifier.
 114  * If it isn't specified, return -1.
 115  */
 116 {
 117     int i, r;
 118 
 119     for (i = 0; i < job_nargs; i++)
 120     {
 121         r = fnmatch(job_args[i], ident, 0);
 122         if (r == 0) return i;
 123         if (r != FNM_NOMATCH) die("fnmatch() error");
 124     }
 125     return - 1;
 126 }
 127 
 128 static void
 129 register_env(const char *env_var, const char *value)
     /* [previous][next][first][last][top][bottom][index][help]  */
 130 /* Store the environment assignment "env_var"="value" */
 131 {
 132     env_rec *er;
 133     int var_len, val_len;
 134 
 135     var_len = (int)strlen(env_var);
 136     val_len = (int)strlen(value);
 137     if (!var_len) {
 138         return;
 139     }
 140 
 141     er = obstack_alloc(&tab_o, sizeof(env_rec));
 142     if (er == NULL) {
 143         die_e("Cannot allocate memory.");
 144     }
 145 
 146     er->assign = obstack_alloc(&tab_o, var_len + 1 + val_len + 1);
 147     if (er->assign == NULL) {
 148         die_e("Cannot allocate memory.");
 149     }
 150     strcpy(er->assign, env_var);
 151     er->assign[var_len] = '=';
 152     strcpy(er->assign + var_len + 1, value);
 153     er->assign[var_len + 1 + val_len] = 0;
 154     if (last_env_rec != NULL) last_env_rec->next = er;
 155     else first_env_rec = er;
 156     last_env_rec = er;
 157     Debug(("on line %d: %s", line_num, er->assign));
 158 }
 159 
 160 static void
 161 register_job(const char *periods, const char *delays,
     /* [previous][next][first][last][top][bottom][index][help]  */
 162              const char *ident, char *command)
 163 /* Store a job definition */
 164 {
 165     int period, delay;
 166     job_rec *jr;
 167     int ident_len, command_len;
 168 
 169     ident_len = (int)strlen(ident);
 170     command_len = (int)strlen(command);
 171     jobs_read++;
 172     period = conv2int(periods);
 173     delay = conv2int(delays);
 174     if (period < 0 || delay < 0)
 175     {
 176         complain("%s: number out of range on line %d, skipping",
 177                  anacrontab, line_num);
 178         return;
 179     }
 180     jr = obstack_alloc(&tab_o, sizeof(job_rec));
 181     if (jr == NULL) {
 182         die_e("Cannot allocate memory.");
 183     }
 184     jr->period = period;
 185     jr->named_period = 0;
 186     delay += random_number;
 187     jr->delay = delay;
 188     jr->tab_line = line_num;
 189     jr->ident = obstack_alloc(&tab_o, ident_len + 1);
 190     if (jr->ident == NULL) {
 191         die_e("Cannot allocate memory.");
 192     }
 193     strcpy(jr->ident, ident);
 194     jr->arg_num = job_arg_num(ident);
 195     jr->command = obstack_alloc(&tab_o, command_len + 1);
 196     if (jr->command == NULL) {
 197         die_e("Cannot allocate memory.");
 198     }
 199     strcpy(jr->command, command);
 200     jr->job_pid = jr->mailer_pid = 0;
 201     if (last_job_rec != NULL) last_job_rec->next = jr;
 202     else first_job_rec = jr;
 203     last_job_rec = jr;
 204     jr->prev_env_rec = last_env_rec;
 205     jr->next = NULL;
 206     Debug(("Read job - period=%d, delay=%d, ident=%s, command=%s",
 207            jr->period, jr->delay, jr->ident, jr->command));
 208 }
 209 
 210 static void
 211 register_period_job(const char *periods, const char *delays,
     /* [previous][next][first][last][top][bottom][index][help]  */
 212                     const char *ident, char *command)
 213 /* Store a job definition with a named period */
 214 {
 215     int delay;
 216     job_rec *jr;
 217     int ident_len, command_len;
 218 
 219     ident_len = (int)strlen(ident);
 220     command_len = (int)strlen(command);
 221     jobs_read++;
 222     delay = conv2int(delays);
 223     if (delay < 0)
 224     {
 225         complain("%s: number out of range on line %d, skipping",
 226                  anacrontab, line_num);
 227         return;
 228     }
 229 
 230     jr = obstack_alloc(&tab_o, sizeof(job_rec));
 231     if (jr == NULL) {
 232         die_e("Cannot allocate memory.");
 233     }
 234     if (!strncmp ("@monthly", periods, 8)) {
 235                 jr->named_period = 1;
 236     } else if (!strncmp("@yearly", periods, 7) || !strncmp("@annually", periods, 9) || !strncmp(/* backwards compat misspelling */"@annualy", periods, 8)) {
 237                 jr->named_period = 2;
 238         } else if (!strncmp ("@daily", periods, 6)) {
 239                 jr->named_period = 3;
 240         } else if (!strncmp ("@weekly", periods, 7)) {
 241                 jr->named_period = 4;
 242     } else {
 243                 complain("%s: Unknown named period on line %d, skipping",
 244                          anacrontab, line_num);
 245     }
 246     jr->period = 0;
 247     delay += random_number;
 248     jr->delay = delay;
 249     jr->tab_line = line_num;
 250     jr->ident = obstack_alloc(&tab_o, ident_len + 1);
 251     if (jr->ident == NULL) {
 252         die_e("Cannot allocate memory.");
 253     }
 254     strcpy(jr->ident, ident);
 255     jr->arg_num = job_arg_num(ident);
 256     jr->command = obstack_alloc(&tab_o, command_len + 1);
 257     if (jr->command == NULL) {
 258         die_e("Cannot allocate memory.");
 259     }
 260     strcpy(jr->command, command);
 261     jr->job_pid = jr->mailer_pid = 0;
 262     if (last_job_rec != NULL) last_job_rec->next = jr;
 263     else first_job_rec = jr;
 264     last_job_rec = jr;
 265     jr->prev_env_rec = last_env_rec;
 266     jr->next = NULL;
 267     Debug(("Read job - period %d, delay=%d, ident%s, command=%s",
 268           jr->named_period, jr->delay, jr->ident, jr->command));
 269 }
 270 
 271 static long int
 272 unbiased_rand(long int max)
     /* [previous][next][first][last][top][bottom][index][help]  */
 273 {
 274     long int rn;
 275     long int divisor;
 276 
 277     divisor = RAND_MAX / (max + 1);
 278 
 279     do {
 280         rn = random() / divisor;
 281     } while (rn > max);
 282 
 283     return rn;
 284 }
 285 
 286 static void
 287 parse_tab_line(char *line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 288 {
 289     int r;
 290     char *env_var;
 291     char *value;
 292     char *periods;
 293     char *delays;
 294     char *ident;
 295     char *command;
 296     char *from;
 297     char *to;
 298     char *pref_hour;
 299 
 300     /* an empty line? */
 301     r = match_rx("^[ \t]*($|#)", line, 0);
 302     if (r == -1) goto reg_err;
 303     if (r)
 304     {
 305         Debug(("line %d empty", line_num));
 306         return;
 307     }
 308 
 309     /* an environment assignment? */
 310     r = match_rx("^[ \t]*([^ \t=]+)[ \t]*=(.*)$", line, 2,
 311                  &env_var, &value);
 312     if (r == -1) goto reg_err;
 313     if (r)
 314     {
 315         if (strncmp(env_var, "START_HOURS_RANGE", 17) == 0)
 316         {
 317             r = match_rx("^([[:digit:]]+)-([[:digit:]]+)$", value, 2, &from, &to);
 318             if (r == -1) goto reg_err;
 319             if (r == 0) goto reg_invalid;
 320             range_start = atoi(from);
 321             range_stop = atoi(to);
 322             if (range_stop < range_start) {
 323                 range_start = 0; range_stop = 0;
 324                 goto reg_invalid;
 325             }
 326             Debug(("Jobs will start in the %02d:00-%02d:00 range.", range_start, range_stop));
 327         }
 328         else if (strncmp(env_var, "RANDOM_DELAY", 12) == 0) {
 329             r = match_rx("^([[:digit:]]+)$", value, 0);
 330             if (r == -1) goto reg_err;
 331             if (r == 0) goto reg_invalid;
 332 
 333             random_number = (int)unbiased_rand(atoi(value));
 334             Debug(("Randomized delay set: %d", random_number));
 335         }
 336         else if (strncmp(env_var, "PREFERRED_HOUR", 14) == 0) {
 337             r = match_rx("^([[:digit:]]+)$", value, 1, &pref_hour);
 338             if (r == -1) goto reg_err;
 339 
 340             if (r) {
 341                 preferred_hour = atoi(pref_hour);
 342                 if ((preferred_hour < 0) || (preferred_hour > 24)) {
 343                     preferred_hour = -1;
 344                     goto reg_invalid;
 345                 }
 346             }
 347         }
 348         register_env(env_var, value);
 349         return;
 350     }
 351 
 352     /* a job? */
 353     r = match_rx("^[ \t]*([[:digit:]]+)[ \t]+([[:digit:]]+)[ \t]+"
 354                  "([^ \t/]+)[ \t]+([^ \t].*)$",
 355                  line, 4, &periods, &delays, &ident, &command);
 356     if (r == -1) goto reg_err;
 357     if (r)
 358     {
 359         register_job(periods, delays, ident, command);
 360         return;
 361     }
 362 
 363     /* A period job? */
 364     r = match_rx("^[ \t]*(@[^ \t]+)[ \t]+([[:digit:]]+)[ \t]+"
 365                  "([^ \t/]+)[ \t]+([^ \t].*)$",
 366                  line, 4, &periods, &delays, &ident, &command);
 367     if (r == -1) goto reg_err;
 368     if (r)
 369     {
 370         register_period_job(periods, delays, ident, command);
 371         return;
 372     }
 373 
 374  reg_invalid:
 375     complain("Invalid syntax in %s on line %d - skipping this line",
 376              anacrontab, line_num);
 377     return;
 378 
 379  reg_err:
 380     die("Regex error reading %s", anacrontab);
 381 }
 382 
 383 void
 384 read_tab(int cwd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 /* Read the anacrontab file into memory */
 386 {
 387     char *tab_line;
 388 
 389     first_job_rec = last_job_rec = NULL;
 390     first_env_rec = last_env_rec = NULL;
 391     jobs_read = 0;
 392     line_num = 0;
 393     /* Open the anacrontab file */
 394     if (fchdir(cwd)) die_e("Can't chdir to original cwd");
 395     tab = fopen(anacrontab, "r");
 396     if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir);
 397 
 398     if (tab == NULL) die_e("Error opening %s", anacrontab);
 399     /* Initialize the obstacks */
 400     obstack_init(&input_o);
 401     obstack_init(&tab_o);
 402     while ((tab_line = read_tab_line()) != NULL)
 403     {
 404         line_num++;
 405         parse_tab_line(tab_line);
 406         obstack_free(&input_o, tab_line);
 407     }
 408     if (fclose(tab)) die_e("Error closing %s", anacrontab);
 409 }
 410 
 411 static int
 412 execution_order(const job_rec **job1, const job_rec **job2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 413 /* Comparison function for sorting the jobs.
 414  */
 415 {
 416     int d;
 417 
 418     d = (*job1)->arg_num - (*job2)->arg_num;
 419     if (d != 0 && now) return d;
 420     d = (*job1)->delay - (*job2)->delay;
 421     if (d != 0) return d;
 422     d = (*job1)->tab_line - (*job2)->tab_line;
 423     return d;
 424 }
 425 
 426 void
 427 arrange_jobs(void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 428 /* Make an array of pointers to jobs that are going to be executed,
 429  * and arrange them in the order of execution.
 430  * Also lock these jobs.
 431  */
 432 {
 433     job_rec *j;
 434 
 435     j = first_job_rec;
 436     njobs = 0;
 437     while (j != NULL)
 438     {
 439         if (j->arg_num != -1 && (update_only || testing_only || consider_job(j)))
 440         {
 441             njobs++;
 442             obstack_grow(&tab_o, &j, sizeof(j));
 443         }
 444         j = j->next;
 445     }
 446     job_array = obstack_finish(&tab_o);
 447 
 448     /* sort the jobs */
 449     qsort(job_array, (size_t)njobs, sizeof(*job_array),
 450           (int (*)(const void *, const void *))execution_order);
 451 }

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