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(LINT) 19 static const char rcsid[] = 20 "$FreeBSD$"; 21 #endif 22 23 /* vix 26jan87 [RCS'd; rest of log is in RCS file] 24 * vix 01jan87 [added line-level error recovery] 25 * vix 31dec86 [added /step to the from-to range, per bob@acornrc] 26 * vix 30dec86 [written] 27 */ 28 29 30 #include "cron.h" 31 #include <grp.h> 32 #ifdef LOGIN_CAP 33 #include <login_cap.h> 34 #endif 35 36 typedef enum ecode { 37 e_none, e_minute, e_hour, e_dom, e_month, e_dow, 38 e_cmd, e_timespec, e_username, e_group, e_mem 39 #ifdef LOGIN_CAP 40 , e_class 41 #endif 42 } ecode_e; 43 44 static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)), 45 get_range __P((bitstr_t *, int, int, char *[], int, FILE *)), 46 get_number __P((int *, int, char *[], int, FILE *)); 47 static int set_element __P((bitstr_t *, int, int, int)); 48 49 static char *ecodes[] = 50 { 51 "no error", 52 "bad minute", 53 "bad hour", 54 "bad day-of-month", 55 "bad month", 56 "bad day-of-week", 57 "bad command", 58 "bad time specifier", 59 "bad username", 60 "bad group name", 61 "out of memory", 62 #ifdef LOGIN_CAP 63 "bad class name", 64 #endif 65 }; 66 67 68 void 69 free_entry(e) 70 entry *e; 71 { 72 #ifdef LOGIN_CAP 73 if (e->class != NULL) 74 free(e->class); 75 #endif 76 free(e->cmd); 77 env_free(e->envp); 78 free(e); 79 } 80 81 82 /* return NULL if eof or syntax error occurs; 83 * otherwise return a pointer to a new entry. 84 */ 85 entry * 86 load_entry(file, error_func, pw, envp) 87 FILE *file; 88 void (*error_func)(); 89 struct passwd *pw; 90 char **envp; 91 { 92 /* this function reads one crontab entry -- the next -- from a file. 93 * it skips any leading blank lines, ignores comments, and returns 94 * EOF if for any reason the entry can't be read and parsed. 95 * 96 * the entry is also parsed here. 97 * 98 * syntax: 99 * user crontab: 100 * minutes hours doms months dows cmd\n 101 * system crontab (/etc/crontab): 102 * minutes hours doms months dows USERNAME cmd\n 103 */ 104 105 ecode_e ecode = e_none; 106 entry *e; 107 int ch; 108 char cmd[MAX_COMMAND]; 109 char envstr[MAX_ENVSTR]; 110 char **prev_env; 111 112 Debug(DPARS, ("load_entry()...about to eat comments\n")) 113 114 skip_comments(file); 115 116 ch = get_char(file); 117 if (ch == EOF) 118 return NULL; 119 120 /* ch is now the first useful character of a useful line. 121 * it may be an @special or it may be the first character 122 * of a list of minutes. 123 */ 124 125 e = (entry *) calloc(sizeof(entry), sizeof(char)); 126 127 if (e == NULL) { 128 warn("load_entry: calloc failed"); 129 return NULL; 130 } 131 132 if (ch == '@') { 133 /* all of these should be flagged and load-limited; i.e., 134 * instead of @hourly meaning "0 * * * *" it should mean 135 * "close to the front of every hour but not 'til the 136 * system load is low". Problems are: how do you know 137 * what "low" means? (save me from /etc/cron.conf!) and: 138 * how to guarantee low variance (how low is low?), which 139 * means how to we run roughly every hour -- seems like 140 * we need to keep a history or let the first hour set 141 * the schedule, which means we aren't load-limited 142 * anymore. too much for my overloaded brain. (vix, jan90) 143 * HINT 144 */ 145 Debug(DPARS, ("load_entry()...about to test shortcuts\n")) 146 ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); 147 if (!strcmp("reboot", cmd)) { 148 Debug(DPARS, ("load_entry()...reboot shortcut\n")) 149 e->flags |= WHEN_REBOOT; 150 } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ 151 Debug(DPARS, ("load_entry()...yearly shortcut\n")) 152 bit_set(e->minute, 0); 153 bit_set(e->hour, 0); 154 bit_set(e->dom, 0); 155 bit_set(e->month, 0); 156 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 157 } else if (!strcmp("monthly", cmd)) { 158 Debug(DPARS, ("load_entry()...monthly shortcut\n")) 159 bit_set(e->minute, 0); 160 bit_set(e->hour, 0); 161 bit_set(e->dom, 0); 162 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 163 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 164 } else if (!strcmp("weekly", cmd)) { 165 Debug(DPARS, ("load_entry()...weekly shortcut\n")) 166 bit_set(e->minute, 0); 167 bit_set(e->hour, 0); 168 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 169 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 170 bit_set(e->dow, 0); 171 } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { 172 Debug(DPARS, ("load_entry()...daily shortcut\n")) 173 bit_set(e->minute, 0); 174 bit_set(e->hour, 0); 175 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 176 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 177 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 178 } else if (!strcmp("hourly", cmd)) { 179 Debug(DPARS, ("load_entry()...hourly shortcut\n")) 180 bit_set(e->minute, 0); 181 bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 182 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 183 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 184 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 185 } else { 186 ecode = e_timespec; 187 goto eof; 188 } 189 /* Advance past whitespace between shortcut and 190 * username/command. 191 */ 192 Skip_Blanks(ch, file); 193 if (ch == EOF) { 194 ecode = e_cmd; 195 goto eof; 196 } 197 } else { 198 Debug(DPARS, ("load_entry()...about to parse numerics\n")) 199 200 ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, 201 PPC_NULL, ch, file); 202 if (ch == EOF) { 203 ecode = e_minute; 204 goto eof; 205 } 206 207 /* hours 208 */ 209 210 ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, 211 PPC_NULL, ch, file); 212 if (ch == EOF) { 213 ecode = e_hour; 214 goto eof; 215 } 216 217 /* DOM (days of month) 218 */ 219 220 if (ch == '*') 221 e->flags |= DOM_STAR; 222 ch = get_list(e->dom, FIRST_DOM, LAST_DOM, 223 PPC_NULL, ch, file); 224 if (ch == EOF) { 225 ecode = e_dom; 226 goto eof; 227 } 228 229 /* month 230 */ 231 232 ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, 233 MonthNames, ch, file); 234 if (ch == EOF) { 235 ecode = e_month; 236 goto eof; 237 } 238 239 /* DOW (days of week) 240 */ 241 242 if (ch == '*') 243 e->flags |= DOW_STAR; 244 ch = get_list(e->dow, FIRST_DOW, LAST_DOW, 245 DowNames, ch, file); 246 if (ch == EOF) { 247 ecode = e_dow; 248 goto eof; 249 } 250 } 251 252 /* make sundays equivilent */ 253 if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { 254 bit_set(e->dow, 0); 255 bit_set(e->dow, 7); 256 } 257 258 /* ch is the first character of a command, or a username */ 259 unget_char(ch, file); 260 261 if (!pw) { 262 char *username = cmd; /* temp buffer */ 263 char *s, *group; 264 struct group *grp; 265 266 Debug(DPARS, ("load_entry()...about to parse username\n")) 267 ch = get_string(username, MAX_COMMAND, file, " \t"); 268 269 Debug(DPARS, ("load_entry()...got %s\n",username)) 270 if (ch == EOF) { 271 ecode = e_cmd; 272 goto eof; 273 } 274 275 #ifdef LOGIN_CAP 276 if ((s = strrchr(username, '/')) != NULL) { 277 *s = '\0'; 278 e->class = strdup(s + 1); 279 if (e->class == NULL) 280 warn("strdup(\"%s\")", s + 1); 281 } else { 282 e->class = strdup(RESOURCE_RC); 283 if (e->class == NULL) 284 warn("strdup(\"%s\")", RESOURCE_RC); 285 } 286 if (e->class == NULL) { 287 ecode = e_mem; 288 goto eof; 289 } 290 if (login_getclass(e->class) == NULL) { 291 ecode = e_class; 292 goto eof; 293 } 294 #endif 295 grp = NULL; 296 if ((s = strrchr(username, ':')) != NULL) { 297 *s = '\0'; 298 if ((grp = getgrnam(s + 1)) == NULL) { 299 ecode = e_group; 300 goto eof; 301 } 302 } 303 304 pw = getpwnam(username); 305 if (pw == NULL) { 306 ecode = e_username; 307 goto eof; 308 } 309 if (grp != NULL) 310 pw->pw_gid = grp->gr_gid; 311 Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid)) 312 #ifdef LOGIN_CAP 313 Debug(DPARS, ("load_entry()...class %s\n",e->class)) 314 #endif 315 } 316 317 if (pw->pw_expire && time(NULL) >= pw->pw_expire) { 318 ecode = e_username; 319 goto eof; 320 } 321 322 e->uid = pw->pw_uid; 323 e->gid = pw->pw_gid; 324 325 /* copy and fix up environment. some variables are just defaults and 326 * others are overrides. 327 */ 328 e->envp = env_copy(envp); 329 if (e->envp == NULL) { 330 warn("env_copy"); 331 ecode = e_mem; 332 goto eof; 333 } 334 if (!env_get("SHELL", e->envp)) { 335 prev_env = e->envp; 336 sprintf(envstr, "SHELL=%s", _PATH_BSHELL); 337 e->envp = env_set(e->envp, envstr); 338 if (e->envp == NULL) { 339 warn("env_set(%s)", envstr); 340 env_free(prev_env); 341 ecode = e_mem; 342 goto eof; 343 } 344 } 345 prev_env = e->envp; 346 sprintf(envstr, "HOME=%s", pw->pw_dir); 347 e->envp = env_set(e->envp, envstr); 348 if (e->envp == NULL) { 349 warn("env_set(%s)", envstr); 350 env_free(prev_env); 351 ecode = e_mem; 352 goto eof; 353 } 354 if (!env_get("PATH", e->envp)) { 355 prev_env = e->envp; 356 sprintf(envstr, "PATH=%s", _PATH_DEFPATH); 357 e->envp = env_set(e->envp, envstr); 358 if (e->envp == NULL) { 359 warn("env_set(%s)", envstr); 360 env_free(prev_env); 361 ecode = e_mem; 362 goto eof; 363 } 364 } 365 prev_env = e->envp; 366 sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); 367 e->envp = env_set(e->envp, envstr); 368 if (e->envp == NULL) { 369 warn("env_set(%s)", envstr); 370 env_free(prev_env); 371 ecode = e_mem; 372 goto eof; 373 } 374 #if defined(BSD) 375 prev_env = e->envp; 376 sprintf(envstr, "%s=%s", "USER", pw->pw_name); 377 e->envp = env_set(e->envp, envstr); 378 if (e->envp == NULL) { 379 warn("env_set(%s)", envstr); 380 env_free(prev_env); 381 ecode = e_mem; 382 goto eof; 383 } 384 #endif 385 386 Debug(DPARS, ("load_entry()...about to parse command\n")) 387 388 /* Everything up to the next \n or EOF is part of the command... 389 * too bad we don't know in advance how long it will be, since we 390 * need to malloc a string for it... so, we limit it to MAX_COMMAND. 391 * XXX - should use realloc(). 392 */ 393 ch = get_string(cmd, MAX_COMMAND, file, "\n"); 394 395 /* a file without a \n before the EOF is rude, so we'll complain... 396 */ 397 if (ch == EOF) { 398 env_free(e->envp); 399 ecode = e_cmd; 400 goto eof; 401 } 402 403 /* got the command in the 'cmd' string; save it in *e. 404 */ 405 e->cmd = strdup(cmd); 406 if (e->cmd == NULL) { 407 warn("strdup(\"%d\")", cmd); 408 env_free(e->envp); 409 ecode = e_mem; 410 goto eof; 411 } 412 Debug(DPARS, ("load_entry()...returning successfully\n")) 413 414 /* success, fini, return pointer to the entry we just created... 415 */ 416 return e; 417 418 eof: 419 free(e); 420 if (ecode != e_none && error_func) 421 (*error_func)(ecodes[(int)ecode]); 422 while (ch != EOF && ch != '\n') 423 ch = get_char(file); 424 return NULL; 425 } 426 427 428 static char 429 get_list(bits, low, high, names, ch, file) 430 bitstr_t *bits; /* one bit per flag, default=FALSE */ 431 int low, high; /* bounds, impl. offset for bitstr */ 432 char *names[]; /* NULL or *[] of names for these elements */ 433 int ch; /* current character being processed */ 434 FILE *file; /* file being read */ 435 { 436 register int done; 437 438 /* we know that we point to a non-blank character here; 439 * must do a Skip_Blanks before we exit, so that the 440 * next call (or the code that picks up the cmd) can 441 * assume the same thing. 442 */ 443 444 Debug(DPARS|DEXT, ("get_list()...entered\n")) 445 446 /* list = range {"," range} 447 */ 448 449 /* clear the bit string, since the default is 'off'. 450 */ 451 bit_nclear(bits, 0, (high-low+1)); 452 453 /* process all ranges 454 */ 455 done = FALSE; 456 while (!done) { 457 ch = get_range(bits, low, high, names, ch, file); 458 if (ch == ',') 459 ch = get_char(file); 460 else 461 done = TRUE; 462 } 463 464 /* exiting. skip to some blanks, then skip over the blanks. 465 */ 466 Skip_Nonblanks(ch, file) 467 Skip_Blanks(ch, file) 468 469 Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) 470 471 return ch; 472 } 473 474 475 static char 476 get_range(bits, low, high, names, ch, file) 477 bitstr_t *bits; /* one bit per flag, default=FALSE */ 478 int low, high; /* bounds, impl. offset for bitstr */ 479 char *names[]; /* NULL or names of elements */ 480 int ch; /* current character being processed */ 481 FILE *file; /* file being read */ 482 { 483 /* range = number | number "-" number [ "/" number ] 484 */ 485 486 register int i; 487 auto int num1, num2, num3; 488 489 Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) 490 491 if (ch == '*') { 492 /* '*' means "first-last" but can still be modified by /step 493 */ 494 num1 = low; 495 num2 = high; 496 ch = get_char(file); 497 if (ch == EOF) 498 return EOF; 499 } else { 500 if (EOF == (ch = get_number(&num1, low, names, ch, file))) 501 return EOF; 502 503 if (ch != '-') { 504 /* not a range, it's a single number. 505 */ 506 if (EOF == set_element(bits, low, high, num1)) 507 return EOF; 508 return ch; 509 } else { 510 /* eat the dash 511 */ 512 ch = get_char(file); 513 if (ch == EOF) 514 return EOF; 515 516 /* get the number following the dash 517 */ 518 ch = get_number(&num2, low, names, ch, file); 519 if (ch == EOF) 520 return EOF; 521 } 522 } 523 524 /* check for step size 525 */ 526 if (ch == '/') { 527 /* eat the slash 528 */ 529 ch = get_char(file); 530 if (ch == EOF) 531 return EOF; 532 533 /* get the step size -- note: we don't pass the 534 * names here, because the number is not an 535 * element id, it's a step size. 'low' is 536 * sent as a 0 since there is no offset either. 537 */ 538 ch = get_number(&num3, 0, PPC_NULL, ch, file); 539 if (ch == EOF) 540 return EOF; 541 } else { 542 /* no step. default==1. 543 */ 544 num3 = 1; 545 } 546 547 /* range. set all elements from num1 to num2, stepping 548 * by num3. (the step is a downward-compatible extension 549 * proposed conceptually by bob@acornrc, syntactically 550 * designed then implmented by paul vixie). 551 */ 552 for (i = num1; i <= num2; i += num3) 553 if (EOF == set_element(bits, low, high, i)) 554 return EOF; 555 556 return ch; 557 } 558 559 560 static char 561 get_number(numptr, low, names, ch, file) 562 int *numptr; /* where does the result go? */ 563 int low; /* offset applied to result if symbolic enum used */ 564 char *names[]; /* symbolic names, if any, for enums */ 565 int ch; /* current character */ 566 FILE *file; /* source */ 567 { 568 char temp[MAX_TEMPSTR], *pc; 569 int len, i, all_digits; 570 571 /* collect alphanumerics into our fixed-size temp array 572 */ 573 pc = temp; 574 len = 0; 575 all_digits = TRUE; 576 while (isalnum(ch)) { 577 if (++len >= MAX_TEMPSTR) 578 return EOF; 579 580 *pc++ = ch; 581 582 if (!isdigit(ch)) 583 all_digits = FALSE; 584 585 ch = get_char(file); 586 } 587 *pc = '\0'; 588 589 /* try to find the name in the name list 590 */ 591 if (names) { 592 for (i = 0; names[i] != NULL; i++) { 593 Debug(DPARS|DEXT, 594 ("get_num, compare(%s,%s)\n", names[i], temp)) 595 if (!strcasecmp(names[i], temp)) { 596 *numptr = i+low; 597 return ch; 598 } 599 } 600 } 601 602 /* no name list specified, or there is one and our string isn't 603 * in it. either way: if it's all digits, use its magnitude. 604 * otherwise, it's an error. 605 */ 606 if (all_digits) { 607 *numptr = atoi(temp); 608 return ch; 609 } 610 611 return EOF; 612 } 613 614 615 static int 616 set_element(bits, low, high, number) 617 bitstr_t *bits; /* one bit per flag, default=FALSE */ 618 int low; 619 int high; 620 int number; 621 { 622 Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) 623 624 if (number < low || number > high) 625 return EOF; 626 627 bit_set(bits, (number-low)); 628 return OK; 629 } 630