1: /* Copyright 1988,1990,1993,1994 by Paul Vixie
   2:  * All rights reserved
   3:  *
   4:  * Distribute freely, except: don't remove my name from the source or
   5:  * documentation (don't take credit for my work), mark your changes (don't
   6:  * get me blamed for your possible bugs), don't alter or remove this
   7:  * notice.  May be sold if buildable source is provided to buyer.  No
   8:  * warrantee of any kind, express or implied, is included with this
   9:  * software; use at your own risk, responsibility for damages (if any) to
  10:  * anyone resulting from the use of this software rests entirely with the
  11:  * user.
  12:  *
  13:  * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  14:  * I'll try to keep a version up to date.  I can be reached as follows:
  15:  * Paul Vixie          <paul@vix.com>          uunet!decwrl!vixie!paul
  16:  */
  17: 
  18: #if !defined(lint) && defined(DOSCCS)
  19: static char sccsid[] = "@(#)entry.c 2.12.2 (2.11BSD) 1999/08/05";
  20: #endif
  21: 
  22: /* vix 26jan87 [RCS'd; rest of log is in RCS file]
  23:  * vix 01jan87 [added line-level error recovery]
  24:  * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
  25:  * vix 30dec86 [written]
  26:  */
  27: 
  28: #include "cron.h"
  29: 
  30: typedef enum ecode {
  31:     e_none, e_minute, e_hour, e_dom, e_month, e_dow,
  32:     e_cmd, e_timespec, e_username
  33: } ecode_e;
  34: 
  35: static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)),
  36:         get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
  37:         get_number __P((int *, int, char *[], int, FILE *));
  38: static int  set_element __P((bitstr_t *, int, int, int));
  39: 
  40: static char *ecodes[] =
  41:     {
  42:         "no error",
  43:         "bad minute",
  44:         "bad hour",
  45:         "bad day-of-month",
  46:         "bad month",
  47:         "bad day-of-week",
  48:         "bad command",
  49:         "bad time specifier",
  50:         "bad username",
  51:     };
  52: 
  53: 
  54: void
  55: free_entry(e)
  56:     register entry  *e;
  57: {
  58:     free(e->cmd);
  59:     env_free(e->envp);
  60:     free(e);
  61: }
  62: 
  63: 
  64: /* return NULL if eof or syntax error occurs;
  65:  * otherwise return a pointer to a new entry.
  66:  */
  67: entry *
  68: load_entry(file, error_func, pw, envp)
  69:     FILE        *file;
  70:     void        (*error_func)();
  71:     register struct passwd  *pw;
  72:     char        **envp;
  73: {
  74:     /* this function reads one crontab entry -- the next -- from a file.
  75: 	 * it skips any leading blank lines, ignores comments, and returns
  76: 	 * EOF if for any reason the entry can't be read and parsed.
  77: 	 *
  78: 	 * the entry is also parsed here.
  79: 	 *
  80: 	 * syntax:
  81: 	 *   user crontab:
  82: 	 *	minutes hours doms months dows cmd\n
  83: 	 *   system crontab (/etc/crontab):
  84: 	 *	minutes hours doms months dows USERNAME cmd\n
  85: 	 */
  86: 
  87:     ecode_e ecode = e_none;
  88:     register entry  *e;
  89:     int ch;
  90:     char    cmd[MAX_COMMAND];
  91:     char    envstr[MAX_ENVSTR];
  92: 
  93:     Debug(DPARS, ("load_entry()...about to eat comments\n"))
  94: 
  95:     skip_comments(file);
  96: 
  97:     ch = get_char(file);
  98:     if (ch == EOF)
  99:         return NULL;
 100: 
 101:     /* ch is now the first useful character of a useful line.
 102: 	 * it may be an @special or it may be the first character
 103: 	 * of a list of minutes.
 104: 	 */
 105: 
 106:     e = (entry *) calloc(sizeof(entry), sizeof(char));
 107: 
 108:     if (ch == '@') {
 109:         /* all of these should be flagged and load-limited; i.e.,
 110: 		 * instead of @hourly meaning "0 * * * *" it should mean
 111: 		 * "close to the front of every hour but not 'til the
 112: 		 * system load is low".  Problems are: how do you know
 113: 		 * what "low" means? (save me from /etc/cron.conf!) and:
 114: 		 * how to guarantee low variance (how low is low?), which
 115: 		 * means how to we run roughly every hour -- seems like
 116: 		 * we need to keep a history or let the first hour set
 117: 		 * the schedule, which means we aren't load-limited
 118: 		 * anymore.  too much for my overloaded brain. (vix, jan90)
 119: 		 * HINT
 120: 		 */
 121:         ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
 122:         if (!strcmp("reboot", cmd)) {
 123:             e->flags |= WHEN_REBOOT;
 124:         } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
 125:             bit_set(e->minute, 0);
 126:             bit_set(e->hour, 0);
 127:             bit_set(e->dom, 0);
 128:             bit_set(e->month, 0);
 129:             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
 130:         } else if (!strcmp("monthly", cmd)) {
 131:             bit_set(e->minute, 0);
 132:             bit_set(e->hour, 0);
 133:             bit_set(e->dom, 0);
 134:             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
 135:             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
 136:         } else if (!strcmp("weekly", cmd)) {
 137:             bit_set(e->minute, 0);
 138:             bit_set(e->hour, 0);
 139:             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
 140:             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
 141:             bit_set(e->dow, 0);
 142:         } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
 143:             bit_set(e->minute, 0);
 144:             bit_set(e->hour, 0);
 145:             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
 146:             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
 147:             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
 148:         } else if (!strcmp("hourly", cmd)) {
 149:             bit_set(e->minute, 0);
 150:             bit_set(e->hour, (LAST_HOUR-FIRST_HOUR+1));
 151:             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
 152:             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
 153:             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
 154:         } else {
 155:             ecode = e_timespec;
 156:             goto eof;
 157:         }
 158:     } else {
 159:         Debug(DPARS, ("load_entry()...about to parse numerics\n"))
 160: 
 161:         ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
 162:                   PPC_NULL, ch, file);
 163:         if (ch == EOF) {
 164:             ecode = e_minute;
 165:             goto eof;
 166:         }
 167: 
 168:         /* hours
 169: 		 */
 170: 
 171:         ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
 172:                   PPC_NULL, ch, file);
 173:         if (ch == EOF) {
 174:             ecode = e_hour;
 175:             goto eof;
 176:         }
 177: 
 178:         /* DOM (days of month)
 179: 		 */
 180: 
 181:         if (ch == '*')
 182:             e->flags |= DOM_STAR;
 183:         ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
 184:                   PPC_NULL, ch, file);
 185:         if (ch == EOF) {
 186:             ecode = e_dom;
 187:             goto eof;
 188:         }
 189: 
 190:         /* month
 191: 		 */
 192: 
 193:         ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
 194:                   MonthNames, ch, file);
 195:         if (ch == EOF) {
 196:             ecode = e_month;
 197:             goto eof;
 198:         }
 199: 
 200:         /* DOW (days of week)
 201: 		 */
 202: 
 203:         if (ch == '*')
 204:             e->flags |= DOW_STAR;
 205:         ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
 206:                   DowNames, ch, file);
 207:         if (ch == EOF) {
 208:             ecode = e_dow;
 209:             goto eof;
 210:         }
 211:     }
 212: 
 213:     /* make sundays equivilent */
 214:     if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
 215:         bit_set(e->dow, 0);
 216:         bit_set(e->dow, 7);
 217:     }
 218: 
 219:     /* ch is the first character of a command, or a username */
 220:     unget_char(ch, file);
 221: 
 222:     if (!pw) {
 223:         char        *username = cmd;    /* temp buffer */
 224: 
 225:         Debug(DPARS, ("load_entry()...about to parse username\n"))
 226:         ch = get_string(username, MAX_COMMAND, file, " \t");
 227: 
 228:         Debug(DPARS, ("load_entry()...got %s\n",username))
 229:         if (ch == EOF) {
 230:             ecode = e_cmd;
 231:             goto eof;
 232:         }
 233: 
 234:         pw = getpwnam(username);
 235:         if (pw == NULL) {
 236:             ecode = e_username;
 237:             goto eof;
 238:         }
 239:         Debug(DPARS, ("load_entry()...uid %d, gid %d\n",e->uid,e->gid))
 240:     }
 241: 
 242:     e->uid = pw->pw_uid;
 243:     e->gid = pw->pw_gid;
 244: 
 245:     /* copy and fix up environment.  some variables are just defaults and
 246: 	 * others are overrides.
 247: 	 */
 248:     e->envp = env_copy(envp);
 249:     if (!env_get("SHELL", e->envp)) {
 250:         sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
 251:         e->envp = env_set(e->envp, envstr);
 252:     }
 253:     if (!env_get("HOME", e->envp)) {
 254:         sprintf(envstr, "HOME=%s", pw->pw_dir);
 255:         e->envp = env_set(e->envp, envstr);
 256:     }
 257:     if (!env_get("PATH", e->envp)) {
 258:         sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
 259:         e->envp = env_set(e->envp, envstr);
 260:     }
 261:     sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
 262:     e->envp = env_set(e->envp, envstr);
 263:     sprintf(envstr, "%s=%s", "USER", pw->pw_name);
 264:     e->envp = env_set(e->envp, envstr);
 265: 
 266:     Debug(DPARS, ("load_entry()...about to parse command\n"))
 267: 
 268:     /* Everything up to the next \n or EOF is part of the command...
 269: 	 * too bad we don't know in advance how long it will be, since we
 270: 	 * need to malloc a string for it... so, we limit it to MAX_COMMAND.
 271: 	 * XXX - should use realloc().
 272: 	 */
 273:     ch = get_string(cmd, MAX_COMMAND, file, "\n");
 274: 
 275:     /* a file without a \n before the EOF is rude, so we'll complain...
 276: 	 */
 277:     if (ch == EOF) {
 278:         ecode = e_cmd;
 279:         goto eof;
 280:     }
 281: 
 282:     /* got the command in the 'cmd' string; save it in *e.
 283: 	 */
 284:     e->cmd = strdup(cmd);
 285: 
 286:     Debug(DPARS, ("load_entry()...returning successfully\n"))
 287: 
 288:     /* success, fini, return pointer to the entry we just created...
 289: 	 */
 290:     return e;
 291: 
 292:  eof:
 293:     free(e);
 294:     if (ecode != e_none && error_func)
 295:         (*error_func)(ecodes[(int)ecode]);
 296:     while (ch != EOF && ch != '\n')
 297:         ch = get_char(file);
 298:     return NULL;
 299: }
 300: 
 301: 
 302: static char
 303: get_list(bits, low, high, names, ch, file)
 304:     bitstr_t    *bits;      /* one bit per flag, default=FALSE */
 305:     int     low, high;  /* bounds, impl. offset for bitstr */
 306:     char        *names[];   /* NULL or *[] of names for these elements */
 307:     int     ch;     /* current character being processed */
 308:     register FILE       *file;      /* file being read */
 309: {
 310:     register int    done;
 311: 
 312:     /* we know that we point to a non-blank character here;
 313: 	 * must do a Skip_Blanks before we exit, so that the
 314: 	 * next call (or the code that picks up the cmd) can
 315: 	 * assume the same thing.
 316: 	 */
 317: 
 318:     Debug(DPARS|DEXT, ("get_list()...entered\n"))
 319: 
 320:     /* list = range {"," range}
 321: 	 */
 322: 
 323:     /* clear the bit string, since the default is 'off'.  DONT add an
 324: 	 * extra bit here, that's done in the macro!
 325: 	 */
 326:     bit_nclear(bits, 0, (high-low));
 327: 
 328:     /* process all ranges
 329: 	 */
 330:     done = FALSE;
 331:     while (!done) {
 332:         ch = get_range(bits, low, high, names, ch, file);
 333:         if (ch == ',')
 334:             ch = get_char(file);
 335:         else
 336:             done = TRUE;
 337:     }
 338: 
 339:     /* exiting.  skip to some blanks, then skip over the blanks.
 340: 	 */
 341:     Skip_Nonblanks(ch, file)
 342:     Skip_Blanks(ch, file)
 343: 
 344:     Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
 345: 
 346:     return ch;
 347: }
 348: 
 349: 
 350: static char
 351: get_range(bits, low, high, names, ch, file)
 352:     bitstr_t    *bits;      /* one bit per flag, default=FALSE */
 353:     int     low, high;  /* bounds, impl. offset for bitstr */
 354:     char        *names[];   /* NULL or names of elements */
 355:     register int    ch;     /* current character being processed */
 356:     FILE        *file;      /* file being read */
 357: {
 358:     /* range = number | number "-" number [ "/" number ]
 359: 	 */
 360: 
 361:     register int    i;
 362:     auto int    num1, num2, num3;
 363: 
 364:     Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
 365: 
 366:     if (ch == '*') {
 367:         /* '*' means "first-last" but can still be modified by /step
 368: 		 */
 369:         num1 = low;
 370:         num2 = high;
 371:         ch = get_char(file);
 372:         if (ch == EOF)
 373:             return EOF;
 374:     } else {
 375:         if (EOF == (ch = get_number(&num1, low, names, ch, file)))
 376:             return EOF;
 377: 
 378:         if (ch != '-') {
 379:             /* not a range, it's a single number.
 380: 			 */
 381:             if (EOF == set_element(bits, low, high, num1))
 382:                 return EOF;
 383:             return ch;
 384:         } else {
 385:             /* eat the dash
 386: 			 */
 387:             ch = get_char(file);
 388:             if (ch == EOF)
 389:                 return EOF;
 390: 
 391:             /* get the number following the dash
 392: 			 */
 393:             ch = get_number(&num2, low, names, ch, file);
 394:             if (ch == EOF)
 395:                 return EOF;
 396:         }
 397:     }
 398: 
 399:     /* check for step size
 400: 	 */
 401:     if (ch == '/') {
 402:         /* eat the slash
 403: 		 */
 404:         ch = get_char(file);
 405:         if (ch == EOF)
 406:             return EOF;
 407: 
 408:         /* get the step size -- note: we don't pass the
 409: 		 * names here, because the number is not an
 410: 		 * element id, it's a step size.  'low' is
 411: 		 * sent as a 0 since there is no offset either.
 412: 		 */
 413:         ch = get_number(&num3, 0, PPC_NULL, ch, file);
 414:         if (ch == EOF)
 415:             return EOF;
 416:     } else {
 417:         /* no step.  default==1.
 418: 		 */
 419:         num3 = 1;
 420:     }
 421: 
 422:     /* range. set all elements from num1 to num2, stepping
 423: 	 * by num3.  (the step is a downward-compatible extension
 424: 	 * proposed conceptually by bob@acornrc, syntactically
 425: 	 * designed then implmented by paul vixie).
 426: 	 */
 427:     for (i = num1;  i <= num2;  i += num3)
 428:         if (EOF == set_element(bits, low, high, i))
 429:             return EOF;
 430: 
 431:     return ch;
 432: }
 433: 
 434: 
 435: static char
 436: get_number(numptr, low, names, ch, file)
 437:     int *numptr;    /* where does the result go? */
 438:     int low;        /* offset applied to result if symbolic enum used */
 439:     char    *names[];   /* symbolic names, if any, for enums */
 440:     register int    ch; /* current character */
 441:     FILE    *file;      /* source */
 442: {
 443:     char    temp[MAX_TEMPSTR], *pc;
 444:     int len, i, all_digits;
 445: 
 446:     /* collect alphanumerics into our fixed-size temp array
 447: 	 */
 448:     pc = temp;
 449:     len = 0;
 450:     all_digits = TRUE;
 451:     while (isalnum(ch)) {
 452:         if (++len >= MAX_TEMPSTR)
 453:             return EOF;
 454: 
 455:         *pc++ = ch;
 456: 
 457:         if (!isdigit(ch))
 458:             all_digits = FALSE;
 459: 
 460:         ch = get_char(file);
 461:     }
 462:     *pc = '\0';
 463: 
 464:     /* try to find the name in the name list
 465: 	 */
 466:     if (names) {
 467:         for (i = 0;  names[i] != NULL;  i++) {
 468:             Debug(DPARS|DEXT,
 469:                 ("get_num, compare(%s,%s)\n", names[i], temp))
 470:             if (!strcasecmp(names[i], temp)) {
 471:                 *numptr = i+low;
 472:                 return ch;
 473:             }
 474:         }
 475:     }
 476: 
 477:     /* no name list specified, or there is one and our string isn't
 478: 	 * in it.  either way: if it's all digits, use its magnitude.
 479: 	 * otherwise, it's an error.
 480: 	 */
 481:     if (all_digits) {
 482:         *numptr = atoi(temp);
 483:         return ch;
 484:     }
 485: 
 486:     return EOF;
 487: }
 488: 
 489: 
 490: static int
 491: set_element(bits, low, high, number)
 492:     bitstr_t    *bits;      /* one bit per flag, default=FALSE */
 493:     int     low;
 494:     int     high;
 495:     int     number;
 496: {
 497:     Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
 498: 
 499:     if (number < low || number > high)
 500:         return EOF;
 501: 
 502:     bit_set(bits, (number-low));
 503:     return OK;
 504: }

Defined functions

free_entry defined in line 54; never used
get_list defined in line 302; used 5 times
get_number defined in line 435; used 3 times
get_range defined in line 350; used 1 times
load_entry defined in line 67; never used
set_element defined in line 490; used 2 times

Defined variables

ecodes defined in line 40; used 1 times
sccsid defined in line 19; never used

Defined enum's

ecode defined in line 30; never used

Defined typedef's

ecode_e defined in line 33; used 1 times
  • in line 87
Last modified: 1999-08-06
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3629
Valid CSS Valid XHTML 1.0 Strict