# 2.11BSD#431 - /usr/src/usr.sbin/cron/entry.c

##### Functions

    free_entry        X
    load_entry        X

##### enums's

    ecode             X

##### typedef's

    ecode_e           X

       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

- in line 161, 171, 183, 193, 205

get_number defined in line 435; used 3 times

- in line 375, 393, 413

get_range defined in line 350; used 1 times

- in line 332

load_entry defined in line 67; never used
set_element defined in line 490; used 2 times

- in line 381, 428

#### Defined variables

ecodes defined in line 40; used 1 times

- in line 295

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: 1704 |