1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 30 /* All Rights Reserved */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #ifdef lint 35 /* make lint happy */ 36 #define __EXTENSIONS__ 37 #endif 38 39 #include <sys/contract/process.h> 40 #include <sys/ctfs.h> 41 #include <sys/param.h> 42 #include <sys/resource.h> 43 #include <sys/stat.h> 44 #include <sys/task.h> 45 #include <sys/time.h> 46 #include <sys/types.h> 47 #include <sys/utsname.h> 48 #include <sys/wait.h> 49 50 #include <security/pam_appl.h> 51 52 #include <alloca.h> 53 #include <ctype.h> 54 #include <deflt.h> 55 #include <dirent.h> 56 #include <errno.h> 57 #include <fcntl.h> 58 #include <grp.h> 59 #include <libcontract.h> 60 #include <libcontract_priv.h> 61 #include <limits.h> 62 #include <locale.h> 63 #include <poll.h> 64 #include <project.h> 65 #include <pwd.h> 66 #include <signal.h> 67 #include <stdarg.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <stropts.h> 72 #include <time.h> 73 #include <unistd.h> 74 75 #include "cron.h" 76 77 /* 78 * #define DEBUG 79 */ 80 81 #define MAIL "/usr/bin/mail" /* mail program to use */ 82 #define CONSOLE "/dev/console" /* where messages go when cron dies */ 83 84 #define TMPINFILE "/tmp/crinXXXXXX" /* file to put stdin in for cmd */ 85 #define TMPDIR "/tmp" 86 #define PFX "crout" 87 #define TMPOUTFILE "/tmp/croutXXXXXX" /* file to place stdout, stderr */ 88 89 #define INMODE 00400 /* mode for stdin file */ 90 #define OUTMODE 00600 /* mode for stdout file */ 91 #define ISUID S_ISUID /* mode for verifing at jobs */ 92 93 #define INFINITY 2147483647L /* upper bound on time */ 94 #define CUSHION 180L 95 #define ZOMB 100 /* proc slot used for mailing output */ 96 97 #define JOBF 'j' 98 #define NICEF 'n' 99 #define USERF 'u' 100 #define WAITF 'w' 101 102 #define BCHAR '>' 103 #define ECHAR '<' 104 105 #define DEFAULT 0 106 #define LOAD 1 107 #define QBUFSIZ 80 108 109 /* Defined actions for crabort() routine */ 110 #define NO_ACTION 000 111 #define REMOVE_FIFO 001 112 #define CONSOLE_MSG 002 113 114 #define BADCD "can't change directory to the crontab directory." 115 #define NOREADDIR "can't read the crontab directory." 116 117 #define BADJOBOPEN "unable to read your at job." 118 #define BADSHELL "because your login shell \ 119 isn't /usr/bin/sh, you can't use cron." 120 121 #define BADSTAT "can't access your crontab or at-job file. Resubmit it." 122 #define BADPROJID "can't set project id for your job." 123 #define CANTCDHOME "can't change directory to your home directory.\ 124 \nYour commands will not be executed." 125 #define CANTEXECSH "unable to exec the shell for one of your commands." 126 #define NOREAD "can't read your crontab file. Resubmit it." 127 #define BADTYPE "crontab or at-job file is not a regular file.\n" 128 #define NOSTDIN "unable to create a standard input file for \ 129 one of your crontab commands. \ 130 \nThat command was not executed." 131 132 #define NOTALLOWED "you are not authorized to use cron. Sorry." 133 #define STDERRMSG "\n\n********************************************\ 134 *****\nCron: The previous message is the \ 135 standard output and standard error \ 136 \nof one of your cron commands.\n" 137 138 #define STDOUTERR "one of your commands generated output or errors, \ 139 but cron was unable to mail you this output.\ 140 \nRemember to redirect standard output and standard \ 141 error for each of your commands." 142 143 #define CLOCK_DRIFT "clock time drifted backwards after event!\n" 144 #define PIDERR "unexpected pid returned %d (ignored)" 145 #define CRONTABERR "Subject: Your crontab file has an error in it\n\n" 146 #define CRONOUT "Subject: Output from \"cron\" command\n\n" 147 #define MALLOCERR "out of space, cannot create new string\n" 148 149 #define DIDFORK didfork 150 #define NOFORK !didfork 151 152 #define MAILBUFLEN (8*1024) 153 #define LINELIMIT 80 154 #define MAILBINITFREE (MAILBUFLEN - (sizeof (cte_intro) - 1) \ 155 - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1) 156 157 #define ERR_CRONTABENT 0 /* error in crontab file entry */ 158 #define ERR_UNIXERR 1 /* error in some system call */ 159 #define ERR_CANTEXECCRON 2 /* error setting up "cron" job environment */ 160 #define ERR_CANTEXECAT 3 /* error setting up "at" job environment */ 161 #define ERR_NOTREG 4 /* error not a regular file */ 162 163 #define PROJECT "project=" 164 165 #define MAX_LOST_CONTRACTS 2048 /* reset if this many failed abandons */ 166 167 #define FORMAT "%a %b %e %H:%M:%S %Y" 168 static char timebuf[80]; 169 170 struct event { 171 time_t time; /* time of the event */ 172 short etype; /* what type of event; 0=cron, 1=at */ 173 char *cmd; /* command for cron, job name for at */ 174 struct usr *u; /* ptr to the owner (usr) of this event */ 175 struct event *link; /* ptr to another event for this user */ 176 union { 177 struct { /* for crontab events */ 178 char *minute; /* (these */ 179 char *hour; /* fields */ 180 char *daymon; /* are */ 181 char *month; /* from */ 182 char *dayweek; /* crontab) */ 183 char *input; /* ptr to stdin */ 184 } ct; 185 struct { /* for at events */ 186 short exists; /* for revising at events */ 187 int eventid; /* for el_remove-ing at events */ 188 } at; 189 } of; 190 }; 191 192 struct usr { 193 char *name; /* name of user (e.g. "root") */ 194 char *home; /* home directory for user */ 195 uid_t uid; /* user id */ 196 gid_t gid; /* group id */ 197 int aruncnt; /* counter for running jobs per uid */ 198 int cruncnt; /* counter for running cron jobs per uid */ 199 int ctid; /* for el_remove-ing crontab events */ 200 short ctexists; /* for revising crontab events */ 201 struct event *ctevents; /* list of this usr's crontab events */ 202 struct event *atevents; /* list of this usr's at events */ 203 struct usr *nextusr; 204 }; /* ptr to next user */ 205 206 static struct queue 207 { 208 int njob; /* limit */ 209 int nice; /* nice for execution */ 210 int nwait; /* wait time to next execution attempt */ 211 int nrun; /* number running */ 212 } 213 qd = {100, 2, 60}, /* default values for queue defs */ 214 qt[NQUEUE]; 215 static struct queue qq; 216 217 static struct runinfo 218 { 219 pid_t pid; 220 short que; 221 struct usr *rusr; /* pointer to usr struct */ 222 char *outfile; /* file where stdout & stderr are trapped */ 223 short jobtype; /* what type of event: 0=cron, 1=at */ 224 char *jobname; /* command for "cron", jobname for "at" */ 225 int mailwhendone; /* 1 = send mail even if no ouptut */ 226 struct runinfo *next; 227 } *rthead; 228 229 static struct miscpid { 230 pid_t pid; 231 struct miscpid *next; 232 } *miscpid_head; 233 234 static pid_t cron_pid; /* own pid */ 235 static char didfork = 0; /* flag to see if I'm process group leader */ 236 static int msgfd; /* file descriptor for fifo queue */ 237 static int ecid = 1; /* event class id for el_remove(); MUST be set to 1 */ 238 static int delayed; /* is job being rescheduled or did it run first time */ 239 static int cwd; /* current working directory */ 240 static struct event *next_event; /* the next event to execute */ 241 static struct usr *uhead; /* ptr to the list of users */ 242 243 /* Variables for error handling at reading crontabs. */ 244 static char cte_intro[] = "Line(s) with errors:\n\n"; 245 static char cte_trail1[] = "\nMax number of errors encountered."; 246 static char cte_trail2[] = " Evaluation of crontab aborted.\n"; 247 static int cte_free = MAILBINITFREE; /* Free buffer space */ 248 static char *cte_text = NULL; /* Text buffer pointer */ 249 static char *cte_lp; /* Next free line in cte_text */ 250 static int cte_nvalid; /* Valid lines found */ 251 252 /* user's default environment for the shell */ 253 #define ROOTPATH "PATH=/usr/sbin:/usr/bin" 254 #define NONROOTPATH "PATH=/usr/bin:" 255 256 static char *Def_supath = NULL; 257 static char *Def_path = NULL; 258 static char path[LINE_MAX] = "PATH="; 259 static char supath[LINE_MAX] = "PATH="; 260 static char homedir[LINE_MAX] = "HOME="; 261 static char logname[LINE_MAX] = "LOGNAME="; 262 static char tzone[LINE_MAX] = "TZ="; 263 static char *envinit[] = { 264 homedir, 265 logname, 266 ROOTPATH, 267 "SHELL=/usr/bin/sh", 268 tzone, 269 NULL 270 }; 271 272 extern char **environ; 273 274 #define DEFTZ "GMT" 275 static int log = 0; 276 static char hzname[10]; 277 278 static void cronend(int); 279 static void thaw_handler(int); 280 static void child_handler(int); 281 static void child_sigreset(void); 282 283 static void mod_ctab(char *, time_t); 284 static void mod_atjob(char *, time_t); 285 static void add_atevent(struct usr *, char *, time_t, int); 286 static void rm_ctevents(struct usr *); 287 static void cleanup(struct runinfo *rn, int r); 288 static void crabort(char *, int); 289 static void msg(char *fmt, ...); 290 static void logit(int, struct runinfo *, int); 291 static void parsqdef(char *); 292 static void defaults(); 293 static void initialize(int); 294 static void quedefs(int); 295 static int idle(long); 296 static struct usr *find_usr(char *); 297 static int ex(struct event *e); 298 static void read_dirs(int); 299 static void mail(char *, char *, int); 300 static char *next_field(int, int); 301 static void readcron(struct usr *, time_t); 302 static int next_ge(int, char *); 303 static void free_if_unused(struct usr *); 304 static void del_atjob(char *, char *); 305 static void del_ctab(char *); 306 static void resched(int); 307 static int msg_wait(long); 308 static struct runinfo *rinfo_get(pid_t); 309 static void rinfo_free(struct runinfo *rp); 310 static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize); 311 static time_t next_time(struct event *, time_t); 312 static time_t get_switching_time(int, time_t); 313 static time_t xmktime(struct tm *); 314 static void process_msg(struct message *, time_t); 315 static void reap_child(void); 316 static void miscpid_insert(pid_t); 317 static int miscpid_delete(pid_t); 318 static void contract_set_template(void); 319 static void contract_clear_template(void); 320 static void contract_abandon_latest(pid_t); 321 322 static void cte_init(void); 323 static void cte_add(int, char *); 324 static void cte_valid(void); 325 static int cte_istoomany(void); 326 static void cte_sendmail(char *); 327 328 static int set_user_cred(const struct usr *, struct project *); 329 330 /* 331 * last_time is set immediately prior to exection of an event (via ex()) 332 * to indicate the last time an event was executed. This was (surely) 333 * it's original intended use. 334 */ 335 static time_t last_time, init_time, t_old; 336 337 static int accept_sigcld, notifypipe[2]; 338 static sigset_t defmask, childmask; 339 340 /* 341 * BSM hooks 342 */ 343 extern int audit_cron_session(char *, char *, uid_t, gid_t, char *); 344 extern void audit_cron_new_job(char *, int, void *); 345 extern void audit_cron_bad_user(char *); 346 extern void audit_cron_user_acct_expired(char *); 347 extern int audit_cron_create_anc_file(char *, char *, char *, uid_t); 348 extern int audit_cron_delete_anc_file(char *, char *); 349 extern int audit_cron_is_anc_name(char *); 350 extern int audit_cron_mode(); 351 352 static int cron_conv(int, struct pam_message **, 353 struct pam_response **, void *); 354 355 static struct pam_conv pam_conv = {cron_conv, NULL}; 356 static pam_handle_t *pamh; /* Authentication handle */ 357 358 /* 359 * Function to help check a user's credentials. 360 */ 361 362 static int verify_user_cred(struct usr *u); 363 364 /* 365 * Values returned by verify_user_cred and set_user_cred: 366 */ 367 368 #define VUC_OK 0 369 #define VUC_BADUSER 1 370 #define VUC_NOTINGROUP 2 371 #define VUC_EXPIRED 3 372 #define VUC_NEW_AUTH 4 373 374 /* 375 * Modes of process_anc_files function 376 */ 377 #define CRON_ANC_DELETE 1 378 #define CRON_ANC_CREATE 0 379 380 /* 381 * Functions to remove a user or job completely from the running database. 382 */ 383 static void clean_out_atjobs(struct usr *u); 384 static void clean_out_ctab(struct usr *u); 385 static void clean_out_user(struct usr *u); 386 static void cron_unlink(char *name); 387 static void process_anc_files(int); 388 389 /* 390 * functions in elm.c 391 */ 392 extern void el_init(int, time_t, time_t, int); 393 extern void el_add(void *, time_t, int); 394 extern void el_remove(int, int); 395 extern int el_empty(void); 396 extern void *el_first(void); 397 extern void el_delete(void); 398 399 static int valid_entry(char *, int); 400 static struct usr *create_ulist(char *, int); 401 static void init_cronevent(char *, int); 402 static void init_atevent(char *, time_t, int, int); 403 static void update_atevent(struct usr *, char *, time_t, int); 404 405 int 406 main(int argc, char *argv[]) 407 { 408 time_t t; 409 time_t ne_time; /* amt of time until next event execution */ 410 time_t newtime, lastmtime = 0L; 411 struct usr *u; 412 struct event *e, *e2, *eprev; 413 struct stat buf; 414 pid_t rfork; 415 struct sigaction act; 416 417 /* 418 * reset is set to 1 via the return from ex() should ex() find 419 * that the event to be executed is being run at the wrong time. 420 * We immediately return to the top of the while (TRUE) loop in 421 * main() where the event list is cleared and rebuilt, and reset 422 * is set back to 0. 423 */ 424 int reset = 0; 425 426 /* 427 * Only the privileged user can run this command. 428 */ 429 if (getuid() != 0) 430 crabort(NOTALLOWED, 0); 431 432 begin: 433 (void) setlocale(LC_ALL, ""); 434 /* fork unless 'nofork' is specified */ 435 if ((argc <= 1) || (strcmp(argv[1], "nofork"))) { 436 if (rfork = fork()) { 437 if (rfork == (pid_t)-1) { 438 (void) sleep(30); 439 goto begin; 440 } 441 return (0); 442 } 443 didfork++; 444 (void) setpgrp(); /* detach cron from console */ 445 } 446 447 (void) umask(022); 448 (void) signal(SIGHUP, SIG_IGN); 449 (void) signal(SIGINT, SIG_IGN); 450 (void) signal(SIGQUIT, SIG_IGN); 451 (void) signal(SIGTERM, cronend); 452 453 defaults(); 454 initialize(1); 455 quedefs(DEFAULT); /* load default queue definitions */ 456 cron_pid = getpid(); 457 msg("*** cron started *** pid = %d", cron_pid); 458 (void) sigset(SIGTHAW, thaw_handler); 459 /* 460 * setup SIGCLD handler/mask 461 */ 462 act.sa_handler = child_handler; 463 act.sa_flags = 0; 464 (void) sigemptyset(&act.sa_mask); 465 (void) sigaddset(&act.sa_mask, SIGCLD); 466 (void) sigaction(SIGCLD, &act, NULL); 467 (void) sigemptyset(&childmask); 468 (void) sigaddset(&childmask, SIGCLD); 469 (void) sigprocmask(SIG_BLOCK, &childmask, &defmask); 470 471 if (pipe(notifypipe) != 0) { 472 crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG); 473 } 474 /* 475 * will set O_NONBLOCK, so that the write() from child_handler 476 * never be blocked. 477 */ 478 (void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK); 479 480 t_old = time(NULL); 481 last_time = t_old; 482 for (;;) { /* MAIN LOOP */ 483 t = time(NULL); 484 if ((t_old > t) || (t-last_time > CUSHION) || reset) { 485 reset = 0; 486 /* the time was set backwards or forward */ 487 el_delete(); 488 u = uhead; 489 while (u != NULL) { 490 rm_ctevents(u); 491 e = u->atevents; 492 while (e != NULL) { 493 free(e->cmd); 494 e2 = e->link; 495 free(e); 496 e = e2; 497 } 498 u->atevents = NULL; 499 u = u->nextusr; 500 } 501 (void) close(msgfd); 502 initialize(0); 503 t = time(NULL); 504 last_time = t; 505 } 506 t_old = t; 507 508 if (next_event == NULL && !el_empty()) { 509 next_event = (struct event *)el_first(); 510 } 511 if (next_event == NULL) { 512 ne_time = INFINITY; 513 } else { 514 ne_time = next_event->time - t; 515 #ifdef DEBUG 516 cftime(timebuf, "%C", &next_event->time); 517 (void) fprintf(stderr, "next_time=%ld %s\n", 518 next_event->time, timebuf); 519 #endif 520 } 521 if (ne_time > 0) { 522 if ((reset = idle(ne_time)) != 0) 523 continue; 524 } 525 526 if (stat(QUEDEFS, &buf)) { 527 msg("cannot stat QUEDEFS file"); 528 } else if (lastmtime != buf.st_mtime) { 529 quedefs(LOAD); 530 lastmtime = buf.st_mtime; 531 } 532 533 last_time = next_event->time; /* save execution time */ 534 535 if (reset = ex(next_event)) 536 continue; 537 538 switch (next_event->etype) { 539 case CRONEVENT: 540 /* add cronevent back into the main event list */ 541 if (delayed) { 542 delayed = 0; 543 break; 544 } 545 546 /* 547 * check if time(0)< last_time. if so, then the 548 * system clock has gone backwards. to prevent this 549 * job from being started twice, we reschedule this 550 * job for the >>next time after last_time<<, and 551 * then set next_event->time to this. note that 552 * crontab's resolution is 1 minute. 553 */ 554 555 if (last_time > time(NULL)) { 556 msg(CLOCK_DRIFT); 557 /* 558 * bump up to next 30 second 559 * increment 560 * 1 <= newtime <= 30 561 */ 562 newtime = 30 - (last_time % 30); 563 newtime += last_time; 564 565 /* 566 * get the next scheduled event, 567 * not the one that we just 568 * kicked off! 569 */ 570 next_event->time = 571 next_time(next_event, newtime); 572 t_old = time(NULL); 573 } else { 574 next_event->time = 575 next_time(next_event, (time_t)0); 576 } 577 #ifdef DEBUG 578 cftime(timebuf, "%C", &next_event->time); 579 (void) fprintf(stderr, 580 "pushing back cron event %s at %ld (%s)\n", 581 next_event->cmd, next_event->time, timebuf); 582 #endif 583 584 el_add(next_event, next_event->time, 585 (next_event->u)->ctid); 586 break; 587 default: 588 /* remove at or batch job from system */ 589 if (delayed) { 590 delayed = 0; 591 break; 592 } 593 eprev = NULL; 594 e = (next_event->u)->atevents; 595 while (e != NULL) { 596 if (e == next_event) { 597 if (eprev == NULL) 598 (e->u)->atevents = e->link; 599 else 600 eprev->link = e->link; 601 free(e->cmd); 602 free(e); 603 break; 604 } else { 605 eprev = e; 606 e = e->link; 607 } 608 } 609 break; 610 } 611 next_event = NULL; 612 } 613 614 /*NOTREACHED*/ 615 } 616 617 static void 618 initialize(int firstpass) 619 { 620 #ifdef DEBUG 621 (void) fprintf(stderr, "in initialize\n"); 622 #endif 623 init_time = time(NULL); 624 el_init(8, init_time, (time_t)(60*60*24), 10); 625 if (firstpass) { 626 /* for mail(1), make sure messages come from root */ 627 if (putenv("LOGNAME=root") != 0) { 628 crabort("cannot expand env variable", 629 REMOVE_FIFO|CONSOLE_MSG); 630 } 631 if (access(FIFO, R_OK) == -1) { 632 if (errno == ENOENT) { 633 if (mknod(FIFO, S_IFIFO|0600, 0) != 0) 634 crabort("cannot create fifo queue", 635 REMOVE_FIFO|CONSOLE_MSG); 636 } else { 637 if (NOFORK) { 638 /* didn't fork... init(1M) is waiting */ 639 (void) sleep(60); 640 } 641 perror("FIFO"); 642 crabort("cannot access fifo queue", 643 REMOVE_FIFO|CONSOLE_MSG); 644 } 645 } else { 646 if (NOFORK) { 647 /* didn't fork... init(1M) is waiting */ 648 (void) sleep(60); 649 /* 650 * the wait is painful, but we don't want 651 * init respawning this quickly 652 */ 653 } 654 crabort("cannot start cron; FIFO exists", CONSOLE_MSG); 655 } 656 } 657 658 if ((msgfd = open(FIFO, O_RDWR)) < 0) { 659 perror("! open"); 660 crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG); 661 } 662 663 /* 664 * read directories, create users list, and add events to the 665 * main event list. Only zero user list on firstpass. 666 */ 667 if (firstpass) 668 uhead = NULL; 669 read_dirs(firstpass); 670 next_event = NULL; 671 672 if (!firstpass) 673 return; 674 675 /* stdout is log file */ 676 if (freopen(ACCTFILE, "a", stdout) == NULL) 677 (void) fprintf(stderr, "cannot open %s\n", ACCTFILE); 678 679 /* log should be root-only */ 680 (void) fchmod(1, S_IRUSR|S_IWUSR); 681 682 /* stderr also goes to ACCTFILE */ 683 (void) close(fileno(stderr)); 684 (void) dup(1); 685 686 /* null for stdin */ 687 (void) freopen("/dev/null", "r", stdin); 688 689 contract_set_template(); 690 } 691 692 static void 693 read_dirs(int first) 694 { 695 DIR *dir; 696 struct dirent *dp; 697 char *ptr; 698 int jobtype; 699 time_t tim; 700 701 702 if (chdir(CRONDIR) == -1) 703 crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG); 704 cwd = CRON; 705 if ((dir = opendir(".")) == NULL) 706 crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG); 707 while ((dp = readdir(dir)) != NULL) { 708 if (!valid_entry(dp->d_name, CRONEVENT)) 709 continue; 710 init_cronevent(dp->d_name, first); 711 } 712 (void) closedir(dir); 713 714 if (chdir(ATDIR) == -1) { 715 msg("cannot chdir to at directory"); 716 return; 717 } 718 if ((dir = opendir(".")) == NULL) { 719 msg("cannot read at at directory"); 720 return; 721 } 722 cwd = AT; 723 while ((dp = readdir(dir)) != NULL) { 724 if (!valid_entry(dp->d_name, ATEVENT)) 725 continue; 726 ptr = dp->d_name; 727 if (((tim = num(&ptr)) == 0) || (*ptr != '.')) 728 continue; 729 ptr++; 730 if (!isalpha(*ptr)) 731 continue; 732 jobtype = *ptr - 'a'; 733 if (jobtype >= NQUEUE) { 734 cron_unlink(dp->d_name); 735 continue; 736 } 737 init_atevent(dp->d_name, tim, jobtype, first); 738 } 739 (void) closedir(dir); 740 } 741 742 static int 743 valid_entry(char *name, int type) 744 { 745 struct stat buf; 746 747 if (strcmp(name, ".") == 0 || 748 strcmp(name, "..") == 0) 749 return (0); 750 751 /* skip over ancillary file names */ 752 if (audit_cron_is_anc_name(name)) 753 return (0); 754 755 if (stat(name, &buf)) { 756 mail(name, BADSTAT, ERR_UNIXERR); 757 cron_unlink(name); 758 return (0); 759 } 760 if (!S_ISREG(buf.st_mode)) { 761 mail(name, BADTYPE, ERR_NOTREG); 762 cron_unlink(name); 763 return (0); 764 } 765 if (type == ATEVENT) { 766 if (!(buf.st_mode & ISUID)) { 767 cron_unlink(name); 768 return (0); 769 } 770 } 771 return (1); 772 } 773 774 struct usr * 775 create_ulist(char *name, int type) 776 { 777 struct usr *u; 778 779 u = xmalloc(sizeof (struct usr)); 780 u->name = xmalloc(strlen(name) + 1); 781 (void) strcpy(u->name, name); 782 u->home = NULL; 783 if (type == CRONEVENT) { 784 u->ctexists = TRUE; 785 u->ctid = ecid++; 786 } else { 787 u->ctexists = FALSE; 788 u->ctid = 0; 789 } 790 u->ctevents = NULL; 791 u->atevents = NULL; 792 u->aruncnt = 0; 793 u->cruncnt = 0; 794 u->nextusr = uhead; 795 uhead = u; 796 return (u); 797 } 798 799 void 800 init_cronevent(char *name, int first) 801 { 802 struct usr *u; 803 804 if (first) { 805 u = create_ulist(name, CRONEVENT); 806 readcron(u, 0); 807 } else { 808 if ((u = find_usr(name)) == NULL) { 809 u = create_ulist(name, CRONEVENT); 810 readcron(u, 0); 811 } else { 812 u->ctexists = TRUE; 813 rm_ctevents(u); 814 el_remove(u->ctid, 0); 815 readcron(u, 0); 816 } 817 } 818 } 819 820 void 821 init_atevent(char *name, time_t tim, int jobtype, int first) 822 { 823 struct usr *u; 824 825 if (first) { 826 u = create_ulist(name, ATEVENT); 827 add_atevent(u, name, tim, jobtype); 828 } else { 829 if ((u = find_usr(name)) == NULL) { 830 u = create_ulist(name, ATEVENT); 831 add_atevent(u, name, tim, jobtype); 832 } else { 833 update_atevent(u, name, tim, jobtype); 834 } 835 } 836 } 837 838 static void 839 mod_ctab(char *name, time_t reftime) 840 { 841 struct passwd *pw; 842 struct stat buf; 843 struct usr *u; 844 char namebuf[PATH_MAX]; 845 char *pname; 846 847 /* skip over ancillary file names */ 848 if (audit_cron_is_anc_name(name)) 849 return; 850 851 if ((pw = getpwnam(name)) == NULL) { 852 msg("No such user as %s - cron entries not created", name); 853 return; 854 } 855 if (cwd != CRON) { 856 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", 857 CRONDIR, name) >= sizeof (namebuf)) { 858 msg("Too long path name %s - cron entries not created", 859 namebuf); 860 return; 861 } 862 pname = namebuf; 863 } else { 864 pname = name; 865 } 866 /* 867 * a warning message is given by the crontab command so there is 868 * no need to give one here...... use this code if you only want 869 * users with a login shell of /usr/bin/sh to use cron 870 */ 871 #ifdef BOURNESHELLONLY 872 if ((strcmp(pw->pw_shell, "") != 0) && 873 (strcmp(pw->pw_shell, SHELL) != 0)) { 874 mail(name, BADSHELL, ERR_CANTEXECCRON); 875 cron_unlink(pname); 876 return; 877 } 878 #endif 879 if (stat(pname, &buf)) { 880 mail(name, BADSTAT, ERR_UNIXERR); 881 cron_unlink(pname); 882 return; 883 } 884 if (!S_ISREG(buf.st_mode)) { 885 mail(name, BADTYPE, ERR_CRONTABENT); 886 return; 887 } 888 if ((u = find_usr(name)) == NULL) { 889 #ifdef DEBUG 890 (void) fprintf(stderr, "new user (%s) with a crontab\n", name); 891 #endif 892 u = create_ulist(name, CRONEVENT); 893 u->home = xmalloc(strlen(pw->pw_dir) + 1); 894 (void) strcpy(u->home, pw->pw_dir); 895 u->uid = pw->pw_uid; 896 u->gid = pw->pw_gid; 897 readcron(u, reftime); 898 } else { 899 u->uid = pw->pw_uid; 900 u->gid = pw->pw_gid; 901 if (strcmp(u->home, pw->pw_dir) != 0) { 902 free(u->home); 903 u->home = xmalloc(strlen(pw->pw_dir) + 1); 904 (void) strcpy(u->home, pw->pw_dir); 905 } 906 u->ctexists = TRUE; 907 if (u->ctid == 0) { 908 #ifdef DEBUG 909 (void) fprintf(stderr, "%s now has a crontab\n", 910 u->name); 911 #endif 912 /* user didnt have a crontab last time */ 913 u->ctid = ecid++; 914 u->ctevents = NULL; 915 readcron(u, reftime); 916 return; 917 } 918 #ifdef DEBUG 919 (void) fprintf(stderr, "%s has revised his crontab\n", u->name); 920 #endif 921 rm_ctevents(u); 922 el_remove(u->ctid, 0); 923 readcron(u, reftime); 924 } 925 } 926 927 /* ARGSUSED */ 928 static void 929 mod_atjob(char *name, time_t reftime) 930 { 931 char *ptr; 932 time_t tim; 933 struct passwd *pw; 934 struct stat buf; 935 struct usr *u; 936 char namebuf[PATH_MAX]; 937 char *pname; 938 int jobtype; 939 940 ptr = name; 941 if (((tim = num(&ptr)) == 0) || (*ptr != '.')) 942 return; 943 ptr++; 944 if (!isalpha(*ptr)) 945 return; 946 jobtype = *ptr - 'a'; 947 948 /* check for audit ancillary file */ 949 if (audit_cron_is_anc_name(name)) 950 return; 951 952 if (cwd != AT) { 953 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name) 954 >= sizeof (namebuf)) { 955 return; 956 } 957 pname = namebuf; 958 } else { 959 pname = name; 960 } 961 if (stat(pname, &buf) || jobtype >= NQUEUE) { 962 cron_unlink(pname); 963 return; 964 } 965 if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) { 966 cron_unlink(pname); 967 return; 968 } 969 if ((pw = getpwuid(buf.st_uid)) == NULL) { 970 cron_unlink(pname); 971 return; 972 } 973 /* 974 * a warning message is given by the at command so there is no 975 * need to give one here......use this code if you only want 976 * users with a login shell of /usr/bin/sh to use cron 977 */ 978 #ifdef BOURNESHELLONLY 979 if ((strcmp(pw->pw_shell, "") != 0) && 980 (strcmp(pw->pw_shell, SHELL) != 0)) { 981 mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT); 982 cron_unlink(pname); 983 return; 984 } 985 #endif 986 if ((u = find_usr(pw->pw_name)) == NULL) { 987 #ifdef DEBUG 988 (void) fprintf(stderr, "new user (%s) with an at job = %s\n", 989 pw->pw_name, name); 990 #endif 991 u = create_ulist(pw->pw_name, ATEVENT); 992 u->home = xmalloc(strlen(pw->pw_dir) + 1); 993 (void) strcpy(u->home, pw->pw_dir); 994 u->uid = pw->pw_uid; 995 u->gid = pw->pw_gid; 996 add_atevent(u, name, tim, jobtype); 997 } else { 998 u->uid = pw->pw_uid; 999 u->gid = pw->pw_gid; 1000 free(u->home); 1001 u->home = xmalloc(strlen(pw->pw_dir) + 1); 1002 (void) strcpy(u->home, pw->pw_dir); 1003 update_atevent(u, name, tim, jobtype); 1004 } 1005 } 1006 1007 static void 1008 add_atevent(struct usr *u, char *job, time_t tim, int jobtype) 1009 { 1010 struct event *e; 1011 1012 e = xmalloc(sizeof (struct event)); 1013 e->etype = jobtype; 1014 e->cmd = xmalloc(strlen(job) + 1); 1015 (void) strcpy(e->cmd, job); 1016 e->u = u; 1017 e->link = u->atevents; 1018 u->atevents = e; 1019 e->of.at.exists = TRUE; 1020 e->of.at.eventid = ecid++; 1021 if (tim < init_time) /* old job */ 1022 e->time = init_time; 1023 else 1024 e->time = tim; 1025 #ifdef DEBUG 1026 (void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n", 1027 u->name, e->cmd, e->time); 1028 #endif 1029 el_add(e, e->time, e->of.at.eventid); 1030 } 1031 1032 void 1033 update_atevent(struct usr *u, char *name, time_t tim, int jobtype) 1034 { 1035 struct event *e; 1036 1037 e = u->atevents; 1038 while (e != NULL) { 1039 if (strcmp(e->cmd, name) == 0) { 1040 e->of.at.exists = TRUE; 1041 break; 1042 } else { 1043 e = e->link; 1044 } 1045 } 1046 if (e == NULL) { 1047 #ifdef DEBUG 1048 (void) fprintf(stderr, "%s has a new at job = %s\n", 1049 u->name, name); 1050 #endif 1051 add_atevent(u, name, tim, jobtype); 1052 } 1053 } 1054 1055 static char line[CTLINESIZE]; /* holds a line from a crontab file */ 1056 static int cursor; /* cursor for the above line */ 1057 1058 static void 1059 readcron(struct usr *u, time_t reftime) 1060 { 1061 /* 1062 * readcron reads in a crontab file for a user (u). The list of 1063 * events for user u is built, and u->events is made to point to 1064 * this list. Each event is also entered into the main event 1065 * list. 1066 */ 1067 FILE *cf; /* cf will be a user's crontab file */ 1068 struct event *e; 1069 int start; 1070 unsigned int i; 1071 char namebuf[PATH_MAX]; 1072 char *pname; 1073 int lineno = 0; 1074 1075 /* read the crontab file */ 1076 cte_init(); /* Init error handling */ 1077 if (cwd != CRON) { 1078 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", 1079 CRONDIR, u->name) >= sizeof (namebuf)) { 1080 return; 1081 } 1082 pname = namebuf; 1083 } else { 1084 pname = u->name; 1085 } 1086 if ((cf = fopen(pname, "r")) == NULL) { 1087 mail(u->name, NOREAD, ERR_UNIXERR); 1088 return; 1089 } 1090 while (fgets(line, CTLINESIZE, cf) != NULL) { 1091 /* process a line of a crontab file */ 1092 lineno++; 1093 if (cte_istoomany()) 1094 break; 1095 cursor = 0; 1096 while (line[cursor] == ' ' || line[cursor] == '\t') 1097 cursor++; 1098 if (line[cursor] == '#' || line[cursor] == '\n') 1099 continue; 1100 e = xmalloc(sizeof (struct event)); 1101 e->etype = CRONEVENT; 1102 if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) && 1103 ((e->of.ct.hour = next_field(0, 23)) != NULL) && 1104 ((e->of.ct.daymon = next_field(1, 31)) != NULL) && 1105 ((e->of.ct.month = next_field(1, 12)) != NULL) && 1106 ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) { 1107 free(e); 1108 cte_add(lineno, line); 1109 continue; 1110 } 1111 while (line[cursor] == ' ' || line[cursor] == '\t') 1112 cursor++; 1113 if (line[cursor] == '\n' || line[cursor] == '\0') 1114 continue; 1115 /* get the command to execute */ 1116 start = cursor; 1117 again: 1118 while ((line[cursor] != '%') && 1119 (line[cursor] != '\n') && 1120 (line[cursor] != '\0') && 1121 (line[cursor] != '\\')) 1122 cursor++; 1123 if (line[cursor] == '\\') { 1124 cursor += 2; 1125 goto again; 1126 } 1127 e->cmd = xmalloc(cursor-start + 1); 1128 (void) strncpy(e->cmd, line + start, cursor-start); 1129 e->cmd[cursor-start] = '\0'; 1130 /* see if there is any standard input */ 1131 if (line[cursor] == '%') { 1132 e->of.ct.input = xmalloc(strlen(line)-cursor + 1); 1133 (void) strcpy(e->of.ct.input, line + cursor + 1); 1134 for (i = 0; i < strlen(e->of.ct.input); i++) { 1135 if (e->of.ct.input[i] == '%') 1136 e->of.ct.input[i] = '\n'; 1137 } 1138 } else { 1139 e->of.ct.input = NULL; 1140 } 1141 /* have the event point to it's owner */ 1142 e->u = u; 1143 /* insert this event at the front of this user's event list */ 1144 e->link = u->ctevents; 1145 u->ctevents = e; 1146 /* set the time for the first occurance of this event */ 1147 e->time = next_time(e, reftime); 1148 /* finally, add this event to the main event list */ 1149 el_add(e, e->time, u->ctid); 1150 cte_valid(); 1151 #ifdef DEBUG 1152 cftime(timebuf, "%C", &e->time); 1153 (void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n", 1154 e->cmd, e->time, timebuf); 1155 #endif 1156 } 1157 cte_sendmail(u->name); /* mail errors if any to user */ 1158 (void) fclose(cf); 1159 } 1160 1161 /* 1162 * Below are the functions for handling of errors in crontabs. Concept is to 1163 * collect faulty lines and send one email at the end of the crontab 1164 * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation 1165 * of crontab is aborted. Otherwise reading of crontab is continued to the end 1166 * of the file but no further error logging appears. 1167 */ 1168 static void 1169 cte_init() 1170 { 1171 if (cte_text == NULL) 1172 cte_text = xmalloc(MAILBUFLEN); 1173 (void) strlcpy(cte_text, cte_intro, MAILBUFLEN); 1174 cte_lp = cte_text + sizeof (cte_intro) - 1; 1175 cte_free = MAILBINITFREE; 1176 cte_nvalid = 0; 1177 } 1178 1179 static void 1180 cte_add(int lineno, char *ctline) 1181 { 1182 int len; 1183 char *p; 1184 1185 if (cte_free >= LINELIMIT) { 1186 (void) sprintf(cte_lp, "%4d: ", lineno); 1187 (void) strlcat(cte_lp, ctline, LINELIMIT - 1); 1188 len = strlen(cte_lp); 1189 if (cte_lp[len - 1] != '\n') { 1190 cte_lp[len++] = '\n'; 1191 cte_lp[len] = '\0'; 1192 } 1193 for (p = cte_lp; *p; p++) { 1194 if (isprint(*p) || *p == '\n' || *p == '\t') 1195 continue; 1196 *p = '.'; 1197 } 1198 cte_lp += len; 1199 cte_free -= len; 1200 if (cte_free < LINELIMIT) { 1201 size_t buflen = MAILBUFLEN - (cte_lp - cte_text); 1202 (void) strlcpy(cte_lp, cte_trail1, buflen); 1203 if (cte_nvalid == 0) 1204 (void) strlcat(cte_lp, cte_trail2, buflen); 1205 } 1206 } 1207 } 1208 1209 static void 1210 cte_valid() 1211 { 1212 cte_nvalid++; 1213 } 1214 1215 static int 1216 cte_istoomany() 1217 { 1218 /* 1219 * Return TRUE only if all lines are faulty. So evaluation of 1220 * a crontab is not aborted if at least one valid line was found. 1221 */ 1222 return (cte_nvalid == 0 && cte_free < LINELIMIT); 1223 } 1224 1225 static void 1226 cte_sendmail(char *username) 1227 { 1228 if (cte_free < MAILBINITFREE) 1229 mail(username, cte_text, ERR_CRONTABENT); 1230 } 1231 1232 /* 1233 * Send mail with error message to a user 1234 */ 1235 static void 1236 mail(char *usrname, char *mesg, int format) 1237 { 1238 /* mail mails a user a message. */ 1239 FILE *pipe; 1240 char *temp; 1241 struct passwd *ruser_ids; 1242 pid_t fork_val; 1243 int saveerrno = errno; 1244 struct utsname name; 1245 1246 #ifdef TESTING 1247 return; 1248 #endif 1249 (void) uname(&name); 1250 if ((fork_val = fork()) == (pid_t)-1) { 1251 msg("cron cannot fork\n"); 1252 return; 1253 } 1254 if (fork_val == 0) { 1255 child_sigreset(); 1256 contract_clear_template(); 1257 if ((ruser_ids = getpwnam(usrname)) == NULL) 1258 exit(0); 1259 (void) setuid(ruser_ids->pw_uid); 1260 temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2); 1261 (void) sprintf(temp, "%s %s", MAIL, usrname); 1262 pipe = popen(temp, "w"); 1263 if (pipe != NULL) { 1264 (void) fprintf(pipe, "To: %s\n", usrname); 1265 switch (format) { 1266 case ERR_CRONTABENT: 1267 (void) fprintf(pipe, CRONTABERR); 1268 (void) fprintf(pipe, "Your \"crontab\" on %s\n", 1269 name.nodename); 1270 (void) fprintf(pipe, mesg); 1271 (void) fprintf(pipe, 1272 "\nEntries or crontab have been ignored\n"); 1273 break; 1274 case ERR_UNIXERR: 1275 (void) fprintf(pipe, "Subject: %s\n\n", mesg); 1276 (void) fprintf(pipe, 1277 "The error on %s was \"%s\"\n", 1278 name.nodename, errmsg(saveerrno)); 1279 break; 1280 1281 case ERR_CANTEXECCRON: 1282 (void) fprintf(pipe, 1283 "Subject: Couldn't run your \"cron\" job\n\n"); 1284 (void) fprintf(pipe, 1285 "Your \"cron\" job on %s ", name.nodename); 1286 (void) fprintf(pipe, "couldn't be run\n"); 1287 (void) fprintf(pipe, "%s\n", mesg); 1288 (void) fprintf(pipe, 1289 "The error was \"%s\"\n", errmsg(saveerrno)); 1290 break; 1291 1292 case ERR_CANTEXECAT: 1293 (void) fprintf(pipe, 1294 "Subject: Couldn't run your \"at\" job\n\n"); 1295 (void) fprintf(pipe, "Your \"at\" job on %s ", 1296 name.nodename); 1297 (void) fprintf(pipe, "couldn't be run\n"); 1298 (void) fprintf(pipe, "%s\n", mesg); 1299 (void) fprintf(pipe, 1300 "The error was \"%s\"\n", errmsg(saveerrno)); 1301 break; 1302 1303 default: 1304 break; 1305 } 1306 (void) pclose(pipe); 1307 } 1308 free(temp); 1309 exit(0); 1310 } 1311 1312 contract_abandon_latest(fork_val); 1313 1314 if (cron_pid == getpid()) { 1315 miscpid_insert(fork_val); 1316 } 1317 } 1318 1319 static char * 1320 next_field(int lower, int upper) 1321 { 1322 /* 1323 * next_field returns a pointer to a string which holds the next 1324 * field of a line of a crontab file. 1325 * if (numbers in this field are out of range (lower..upper), 1326 * or there is a syntax error) then 1327 * NULL is returned, and a mail message is sent to the 1328 * user telling him which line the error was in. 1329 */ 1330 1331 char *s; 1332 int num, num2, start; 1333 1334 while ((line[cursor] == ' ') || (line[cursor] == '\t')) 1335 cursor++; 1336 start = cursor; 1337 if (line[cursor] == '\0') { 1338 return (NULL); 1339 } 1340 if (line[cursor] == '*') { 1341 cursor++; 1342 if ((line[cursor] != ' ') && (line[cursor] != '\t')) 1343 return (NULL); 1344 s = xmalloc(2); 1345 (void) strcpy(s, "*"); 1346 return (s); 1347 } 1348 for (;;) { 1349 if (!isdigit(line[cursor])) 1350 return (NULL); 1351 num = 0; 1352 do { 1353 num = num*10 + (line[cursor]-'0'); 1354 } while (isdigit(line[++cursor])); 1355 if ((num < lower) || (num > upper)) 1356 return (NULL); 1357 if (line[cursor] == '-') { 1358 if (!isdigit(line[++cursor])) 1359 return (NULL); 1360 num2 = 0; 1361 do { 1362 num2 = num2*10 + (line[cursor]-'0'); 1363 } while (isdigit(line[++cursor])); 1364 if ((num2 < lower) || (num2 > upper)) 1365 return (NULL); 1366 } 1367 if ((line[cursor] == ' ') || (line[cursor] == '\t')) 1368 break; 1369 if (line[cursor] == '\0') 1370 return (NULL); 1371 if (line[cursor++] != ',') 1372 return (NULL); 1373 } 1374 s = xmalloc(cursor-start + 1); 1375 (void) strncpy(s, line + start, cursor-start); 1376 s[cursor-start] = '\0'; 1377 return (s); 1378 } 1379 1380 #define tm_cmp(t1, t2) (\ 1381 (t1)->tm_year == (t2)->tm_year && \ 1382 (t1)->tm_mon == (t2)->tm_mon && \ 1383 (t1)->tm_mday == (t2)->tm_mday && \ 1384 (t1)->tm_hour == (t2)->tm_hour && \ 1385 (t1)->tm_min == (t2)->tm_min) 1386 1387 #define tm_setup(tp, yr, mon, dy, hr, min, dst) \ 1388 (tp)->tm_year = yr; \ 1389 (tp)->tm_mon = mon; \ 1390 (tp)->tm_mday = dy; \ 1391 (tp)->tm_hour = hr; \ 1392 (tp)->tm_min = min; \ 1393 (tp)->tm_isdst = dst; \ 1394 (tp)->tm_sec = 0; \ 1395 (tp)->tm_wday = 0; \ 1396 (tp)->tm_yday = 0; 1397 1398 /* 1399 * modification for bugid 1104537. the second argument to next_time is 1400 * now the value of time(2) to be used. if this is 0, then use the 1401 * current time. otherwise, the second argument is the time from which to 1402 * calculate things. this is useful to correct situations where you've 1403 * gone backwards in time (I.e. the system's internal clock is correcting 1404 * itself backwards). 1405 */ 1406 1407 static time_t 1408 next_time(struct event *e, time_t tflag) 1409 { 1410 /* 1411 * returns the integer time for the next occurance of event e. 1412 * the following fields have ranges as indicated: 1413 * PRGM | min hour day of month mon day of week 1414 * ------|------------------------------------------------------- 1415 * cron | 0-59 0-23 1-31 1-12 0-6 (0=sunday) 1416 * time | 0-59 0-23 1-31 0-11 0-6 (0=sunday) 1417 * NOTE: this routine is hard to understand. 1418 */ 1419 1420 struct tm *tm, ref_tm, tmp, tmp1, tmp2; 1421 int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days, 1422 d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd, 1423 today; 1424 1425 time_t t, ref_t, t1, t2, zone_start; 1426 int fallback; 1427 extern int days_btwn(int, int, int, int, int, int); 1428 1429 if (tflag == 0) { 1430 t = time(NULL); /* original way of doing things */ 1431 } else { 1432 t = tflag; 1433 } 1434 1435 tm = &ref_tm; /* use a local variable and call localtime_r() */ 1436 ref_t = t; /* keep a copy of the reference time */ 1437 1438 recalc: 1439 fallback = 0; 1440 1441 (void) localtime_r(&t, tm); 1442 1443 if (daylight) { 1444 tmp = *tm; 1445 tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1); 1446 t1 = xmktime(&tmp); 1447 /* 1448 * see if we will have timezone switch over, and clock will 1449 * fall back. zone_start will hold the time when it happens 1450 * (ie time of PST -> PDT switch over). 1451 */ 1452 if (tm->tm_isdst != tmp.tm_isdst && 1453 (t1 - t) == (timezone - altzone) && 1454 tm_cmp(tm, &tmp)) { 1455 zone_start = get_switching_time(tmp.tm_isdst, t); 1456 fallback = 1; 1457 } 1458 } 1459 1460 tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1; /* 0-11 */ 1461 tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon); /* 1-31 */ 1462 tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek); /* 0-6 */ 1463 today = TRUE; 1464 if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) || 1465 (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) || 1466 (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) || 1467 (tm->tm_mon != tm_mon)) { 1468 today = FALSE; 1469 } 1470 m = tm->tm_min + (t == ref_t ? 1 : 0); 1471 if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) { 1472 m = 0; 1473 } 1474 min = next_ge(m%60, e->of.ct.minute); 1475 carry = (min < m) ? 1 : 0; 1476 h = tm->tm_hour + carry; 1477 hr = next_ge(h%24, e->of.ct.hour); 1478 carry = (hr < h) ? 1 : 0; 1479 1480 if (carry == 0 && today) { 1481 /* this event must occur today */ 1482 tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday, 1483 hr, min, tm->tm_isdst); 1484 tmp1 = tmp; 1485 if ((t1 = xmktime(&tmp1)) == (time_t)-1) { 1486 return (0); 1487 } 1488 if (daylight && tmp.tm_isdst != tmp1.tm_isdst) { 1489 /* In case we are falling back */ 1490 if (fallback) { 1491 /* we may need to run the job once more. */ 1492 t = zone_start; 1493 goto recalc; 1494 } 1495 1496 /* 1497 * In case we are not in falling back period, 1498 * calculate the time assuming the DST. If the 1499 * date/time is not altered by mktime, it is the 1500 * time to execute the job. 1501 */ 1502 tmp2 = tmp; 1503 tmp2.tm_isdst = tmp1.tm_isdst; 1504 if ((t1 = xmktime(&tmp2)) == (time_t)-1) { 1505 return (0); 1506 } 1507 if (tmp1.tm_isdst == tmp2.tm_isdst && 1508 tm_cmp(&tmp, &tmp2)) { 1509 /* 1510 * We got a valid time. 1511 */ 1512 return (t1); 1513 } else { 1514 /* 1515 * If the date does not match even if 1516 * we assume the alternate timezone, then 1517 * it must be the invalid time. eg 1518 * 2am while switching 1:59am to 3am. 1519 * t1 should point the time before the 1520 * switching over as we've calculate the 1521 * time with assuming alternate zone. 1522 */ 1523 if (tmp1.tm_isdst != tmp2.tm_isdst) { 1524 t = get_switching_time(tmp1.tm_isdst, 1525 t1); 1526 } else { 1527 /* does this really happen? */ 1528 t = get_switching_time(tmp1.tm_isdst, 1529 t1 - abs(timezone - altzone)); 1530 } 1531 if (t == (time_t)-1) 1532 return (0); 1533 } 1534 goto recalc; 1535 } 1536 if (tm_cmp(&tmp, &tmp1)) { 1537 /* got valid time */ 1538 return (t1); 1539 } else { 1540 /* 1541 * This should never happen, but just in 1542 * case, we fall back to the old code. 1543 */ 1544 if (tm->tm_min > min) { 1545 t += (time_t)(hr-tm->tm_hour-1) * HOUR + 1546 (time_t)(60-tm->tm_min + min) * MINUTE; 1547 } else { 1548 t += (time_t)(hr-tm->tm_hour) * HOUR + 1549 (time_t)(min-tm->tm_min) * MINUTE; 1550 } 1551 t1 = t; 1552 t -= (time_t)tm->tm_sec; 1553 (void) localtime_r(&t, &tmp); 1554 if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0)) 1555 t -= (timezone - altzone); 1556 return ((t <= ref_t) ? t1 : t); 1557 } 1558 } 1559 1560 /* 1561 * Job won't run today, however if we have a switch over within 1562 * one hour and we will have one hour time drifting back in this 1563 * period, we may need to run the job one more time if the job was 1564 * set to run on this hour of clock. 1565 */ 1566 if (fallback) { 1567 t = zone_start; 1568 goto recalc; 1569 } 1570 1571 min = next_ge(0, e->of.ct.minute); 1572 hr = next_ge(0, e->of.ct.hour); 1573 1574 /* 1575 * calculate the date of the next occurance of this event, which 1576 * will be on a different day than the current 1577 */ 1578 1579 /* check monthly day specification */ 1580 d1 = tm->tm_mday + 1; 1581 day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1, 1582 e->of.ct.daymon); 1583 carry1 = (day1 < d1) ? 1 : 0; 1584 1585 /* check weekly day specification */ 1586 d2 = tm->tm_wday + 1; 1587 wday = next_ge(d2%7, e->of.ct.dayweek); 1588 if (wday < d2) 1589 daysahead = 7 - d2 + wday; 1590 else 1591 daysahead = wday - d2; 1592 day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1; 1593 carry2 = (day2 < d1) ? 1 : 0; 1594 1595 /* 1596 * based on their respective specifications, day1, and day2 give 1597 * the day of the month for the next occurance of this event. 1598 */ 1599 if ((strcmp(e->of.ct.daymon, "*") == 0) && 1600 (strcmp(e->of.ct.dayweek, "*") != 0)) { 1601 day1 = day2; 1602 carry1 = carry2; 1603 } 1604 if ((strcmp(e->of.ct.daymon, "*") != 0) && 1605 (strcmp(e->of.ct.dayweek, "*") == 0)) { 1606 day2 = day1; 1607 carry2 = carry1; 1608 } 1609 1610 yr = tm->tm_year; 1611 if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) { 1612 /* event does not occur in this month */ 1613 m = tm->tm_mon + 1; 1614 mon = next_ge(m%12 + 1, e->of.ct.month) - 1; /* 0..11 */ 1615 carry = (mon < m) ? 1 : 0; 1616 yr += carry; 1617 /* recompute day1 and day2 */ 1618 day1 = next_ge(1, e->of.ct.daymon); 1619 db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon, 1620 1, yr) + 1; 1621 wd = (tm->tm_wday + db)%7; 1622 /* wd is the day of the week of the first of month mon */ 1623 wday = next_ge(wd, e->of.ct.dayweek); 1624 if (wday < wd) 1625 day2 = 1 + 7 - wd + wday; 1626 else 1627 day2 = 1 + wday - wd; 1628 if ((strcmp(e->of.ct.daymon, "*") != 0) && 1629 (strcmp(e->of.ct.dayweek, "*") == 0)) 1630 day2 = day1; 1631 if ((strcmp(e->of.ct.daymon, "*") == 0) && 1632 (strcmp(e->of.ct.dayweek, "*") != 0)) 1633 day1 = day2; 1634 day = (day1 < day2) ? day1 : day2; 1635 } else { /* event occurs in this month */ 1636 mon = tm->tm_mon; 1637 if (!carry1 && !carry2) 1638 day = (day1 < day2) ? day1 : day2; 1639 else if (!carry1) 1640 day = day1; 1641 else 1642 day = day2; 1643 } 1644 1645 /* 1646 * now that we have the min, hr, day, mon, yr of the next event, 1647 * figure out what time that turns out to be. 1648 */ 1649 tm_setup(&tmp, yr, mon, day, hr, min, -1); 1650 tmp2 = tmp; 1651 if ((t1 = xmktime(&tmp2)) == (time_t)-1) { 1652 return (0); 1653 } 1654 if (tm_cmp(&tmp, &tmp2)) { 1655 /* 1656 * mktime returns clock for the current time zone. If the 1657 * target date was in fallback period, it needs to be adjusted 1658 * to the time comes first. 1659 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03. 1660 * mktime returns the time in PST, but 1:30am in PDT comes 1661 * first. So reverse the tm_isdst, and see if we have such 1662 * time/date. 1663 */ 1664 if (daylight) { 1665 int dst = tmp2.tm_isdst; 1666 1667 tmp2 = tmp; 1668 tmp2.tm_isdst = (dst > 0 ? 0 : 1); 1669 if ((t2 = xmktime(&tmp2)) == (time_t)-1) { 1670 return (0); 1671 } 1672 if (tm_cmp(&tmp, &tmp2)) { 1673 /* 1674 * same time/date found in the opposite zone. 1675 * check the clock to see which comes early. 1676 */ 1677 if (t2 > ref_t && t2 < t1) { 1678 t1 = t2; 1679 } 1680 } 1681 } 1682 return (t1); 1683 } else { 1684 /* 1685 * mktime has set different time/date for the given date. 1686 * This means that the next job is scheduled to be run on the 1687 * invalid time. There are three possible invalid date/time. 1688 * 1. Non existing day of the month. such as April 31th. 1689 * 2. Feb 29th in the non-leap year. 1690 * 3. Time gap during the DST switch over. 1691 */ 1692 d1 = days_in_mon(mon, yr); 1693 if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) { 1694 /* 1695 * see if we have got a specific date which 1696 * is invalid. 1697 */ 1698 if (strcmp(e->of.ct.dayweek, "*") == 0 && 1699 mon == (next_ge((mon + 1)%12 + 1, 1700 e->of.ct.month) - 1) && 1701 day <= next_ge(1, e->of.ct.daymon)) { 1702 /* job never run */ 1703 return (0); 1704 } 1705 /* 1706 * Since the day has gone invalid, we need to go to 1707 * next month, and recalcuate the first occurrence. 1708 * eg the cron tab such as: 1709 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin.... 1710 * 2/31 is invalid, so the next job is 3/1. 1711 */ 1712 tmp2 = tmp; 1713 tmp2.tm_min = 0; 1714 tmp2.tm_hour = 0; 1715 tmp2.tm_mday = 1; /* 1st day of the month */ 1716 if (mon == 11) { 1717 tmp2.tm_mon = 0; 1718 tmp2.tm_year = yr + 1; 1719 } else { 1720 tmp2.tm_mon = mon + 1; 1721 } 1722 if ((t = xmktime(&tmp2)) == (time_t)-1) { 1723 return (0); 1724 } 1725 } else if (mon == 1 && day > d1) { 1726 /* 1727 * ie 29th in the non-leap year. Forwarding the 1728 * clock to Feb 29th 00:00 (March 1st), and recalculate 1729 * the next time. 1730 */ 1731 tmp2 = tmp; 1732 tmp2.tm_min = 0; 1733 tmp2.tm_hour = 0; 1734 if ((t = xmktime(&tmp2)) == (time_t)-1) { 1735 return (0); 1736 } 1737 } else if (daylight) { 1738 /* 1739 * Non existing time, eg 2am PST during summer time 1740 * switch. 1741 * We need to get the correct isdst which we are 1742 * swithing to, by adding time difference to make sure 1743 * that t2 is in the zone being switched. 1744 */ 1745 t2 = t1; 1746 t2 += abs(timezone - altzone); 1747 (void) localtime_r(&t2, &tmp2); 1748 zone_start = get_switching_time(tmp2.tm_isdst, 1749 t1 - abs(timezone - altzone)); 1750 if (zone_start == (time_t)-1) { 1751 return (0); 1752 } 1753 t = zone_start; 1754 } else { 1755 /* 1756 * This should never happen, but fall back to the 1757 * old code. 1758 */ 1759 days = days_btwn(tm->tm_mon, 1760 tm->tm_mday, tm->tm_year, mon, day, yr); 1761 t += (time_t)(23-tm->tm_hour)*HOUR 1762 + (time_t)(60-tm->tm_min)*MINUTE 1763 + (time_t)hr*HOUR + (time_t)min*MINUTE 1764 + (time_t)days*DAY; 1765 t1 = t; 1766 t -= (time_t)tm->tm_sec; 1767 (void) localtime_r(&t, &tmp); 1768 if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0)) 1769 t -= (timezone - altzone); 1770 return (t <= ref_t ? t1 : t); 1771 } 1772 goto recalc; 1773 } 1774 /*NOTREACHED*/ 1775 } 1776 1777 /* 1778 * This returns TOD in time_t that zone switch will happen, and this 1779 * will be called when clock fallback is about to happen. 1780 * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST 1781 * will fall back to 1:00 PDT. So this function will be called only 1782 * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)). 1783 * First goes through the common time differences to see if zone 1784 * switch happens at those minutes later. If not, check every minutes 1785 * until 6 hours ahead see if it happens(We might have 45minutes 1786 * fallback). 1787 */ 1788 static time_t 1789 get_switching_time(int to_dst, time_t t_ref) 1790 { 1791 time_t t, t1; 1792 struct tm tmp, tmp1; 1793 int hints[] = { 60, 120, 30, 90, 0}; /* minutes */ 1794 int i; 1795 1796 (void) localtime_r(&t_ref, &tmp); 1797 tmp1 = tmp; 1798 tmp1.tm_sec = 0; 1799 tmp1.tm_min = 0; 1800 if ((t = xmktime(&tmp1)) == (time_t)-1) 1801 return ((time_t)-1); 1802 1803 /* fast path */ 1804 for (i = 0; hints[i] != 0; i++) { 1805 t1 = t + hints[i] * 60; 1806 (void) localtime_r(&t1, &tmp1); 1807 if (tmp1.tm_isdst == to_dst) { 1808 t1--; 1809 (void) localtime_r(&t1, &tmp1); 1810 if (tmp1.tm_isdst != to_dst) { 1811 return (t1 + 1); 1812 } 1813 } 1814 } 1815 1816 /* ugly, but don't know other than this. */ 1817 tmp1 = tmp; 1818 tmp1.tm_sec = 0; 1819 if ((t = xmktime(&tmp1)) == (time_t)-1) 1820 return ((time_t)-1); 1821 while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */ 1822 t += 60; /* at least one minute, I assume */ 1823 (void) localtime_r(&t, &tmp); 1824 if (tmp.tm_isdst == to_dst) 1825 return (t); 1826 } 1827 return ((time_t)-1); 1828 } 1829 1830 static time_t 1831 xmktime(struct tm *tmp) 1832 { 1833 time_t ret; 1834 1835 if ((ret = mktime(tmp)) == (time_t)-1) { 1836 if (errno == EOVERFLOW) { 1837 return ((time_t)-1); 1838 } 1839 crabort("internal error: mktime failed", 1840 REMOVE_FIFO|CONSOLE_MSG); 1841 } 1842 return (ret); 1843 } 1844 1845 #define DUMMY 100 1846 1847 static int 1848 next_ge(int current, char *list) 1849 { 1850 /* 1851 * list is a character field as in a crontab file; 1852 * for example: "40, 20, 50-10" 1853 * next_ge returns the next number in the list that is 1854 * greater than or equal to current. if no numbers of list 1855 * are >= current, the smallest element of list is returned. 1856 * NOTE: current must be in the appropriate range. 1857 */ 1858 1859 char *ptr; 1860 int n, n2, min, min_gt; 1861 1862 if (strcmp(list, "*") == 0) 1863 return (current); 1864 ptr = list; 1865 min = DUMMY; 1866 min_gt = DUMMY; 1867 for (;;) { 1868 if ((n = (int)num(&ptr)) == current) 1869 return (current); 1870 if (n < min) 1871 min = n; 1872 if ((n > current) && (n < min_gt)) 1873 min_gt = n; 1874 if (*ptr == '-') { 1875 ptr++; 1876 if ((n2 = (int)num(&ptr)) > n) { 1877 if ((current > n) && (current <= n2)) 1878 return (current); 1879 } else { /* range that wraps around */ 1880 if (current > n) 1881 return (current); 1882 if (current <= n2) 1883 return (current); 1884 } 1885 } 1886 if (*ptr == '\0') 1887 break; 1888 ptr += 1; 1889 } 1890 if (min_gt != DUMMY) 1891 return (min_gt); 1892 else 1893 return (min); 1894 } 1895 1896 static void 1897 free_if_unused(struct usr *u) 1898 { 1899 struct usr *cur, *prev; 1900 /* 1901 * To make sure a usr structure is idle we must check that 1902 * there are no at jobs queued for the user; the user does 1903 * not have a crontab, and also that there are no running at 1904 * or cron jobs (since the runinfo structure also has a 1905 * pointer to the usr structure). 1906 */ 1907 if (!u->ctexists && u->atevents == NULL && 1908 u->cruncnt == 0 && u->aruncnt == 0) { 1909 #ifdef DEBUG 1910 (void) fprintf(stderr, "%s removed from usr list\n", u->name); 1911 #endif 1912 for (cur = uhead, prev = NULL; 1913 cur != u; 1914 prev = cur, cur = cur->nextusr) { 1915 if (cur == NULL) { 1916 return; 1917 } 1918 } 1919 1920 if (prev == NULL) 1921 uhead = u->nextusr; 1922 else 1923 prev->nextusr = u->nextusr; 1924 free(u->name); 1925 free(u->home); 1926 free(u); 1927 } 1928 } 1929 1930 static void 1931 del_atjob(char *name, char *usrname) 1932 { 1933 1934 struct event *e, *eprev; 1935 struct usr *u; 1936 1937 if ((u = find_usr(usrname)) == NULL) 1938 return; 1939 e = u->atevents; 1940 eprev = NULL; 1941 while (e != NULL) { 1942 if (strcmp(name, e->cmd) == 0) { 1943 if (next_event == e) 1944 next_event = NULL; 1945 if (eprev == NULL) 1946 u->atevents = e->link; 1947 else 1948 eprev->link = e->link; 1949 el_remove(e->of.at.eventid, 1); 1950 free(e->cmd); 1951 free(e); 1952 break; 1953 } else { 1954 eprev = e; 1955 e = e->link; 1956 } 1957 } 1958 1959 free_if_unused(u); 1960 } 1961 1962 static void 1963 del_ctab(char *name) 1964 { 1965 1966 struct usr *u; 1967 1968 if ((u = find_usr(name)) == NULL) 1969 return; 1970 rm_ctevents(u); 1971 el_remove(u->ctid, 0); 1972 u->ctid = 0; 1973 u->ctexists = 0; 1974 1975 free_if_unused(u); 1976 } 1977 1978 static void 1979 rm_ctevents(struct usr *u) 1980 { 1981 struct event *e2, *e3; 1982 1983 /* 1984 * see if the next event (to be run by cron) is a cronevent 1985 * owned by this user. 1986 */ 1987 1988 if ((next_event != NULL) && 1989 (next_event->etype == CRONEVENT) && 1990 (next_event->u == u)) { 1991 next_event = NULL; 1992 } 1993 e2 = u->ctevents; 1994 while (e2 != NULL) { 1995 free(e2->cmd); 1996 free(e2->of.ct.minute); 1997 free(e2->of.ct.hour); 1998 free(e2->of.ct.daymon); 1999 free(e2->of.ct.month); 2000 free(e2->of.ct.dayweek); 2001 if (e2->of.ct.input != NULL) 2002 free(e2->of.ct.input); 2003 e3 = e2->link; 2004 free(e2); 2005 e2 = e3; 2006 } 2007 u->ctevents = NULL; 2008 } 2009 2010 2011 static struct usr * 2012 find_usr(char *uname) 2013 { 2014 struct usr *u; 2015 2016 u = uhead; 2017 while (u != NULL) { 2018 if (strcmp(u->name, uname) == 0) 2019 return (u); 2020 u = u->nextusr; 2021 } 2022 return (NULL); 2023 } 2024 2025 /* 2026 * Execute cron command or at/batch job. 2027 * If ever a premature return is added to this function pay attention to 2028 * free at_cmdfile and outfile plus jobname buffers of the runinfo structure. 2029 */ 2030 static int 2031 ex(struct event *e) 2032 { 2033 int r; 2034 int fd; 2035 pid_t rfork; 2036 FILE *atcmdfp; 2037 char mailvar[4]; 2038 char *at_cmdfile = NULL; 2039 struct stat buf; 2040 struct queue *qp; 2041 struct runinfo *rp; 2042 struct project proj, *pproj = NULL; 2043 char mybuf[PROJECT_BUFSZ]; 2044 char mybuf2[PROJECT_BUFSZ]; 2045 char *tmpfile; 2046 FILE *fptr; 2047 time_t dhltime; 2048 projid_t projid; 2049 int projflag = 0; 2050 2051 qp = &qt[e->etype]; /* set pointer to queue defs */ 2052 if (qp->nrun >= qp->njob) { 2053 msg("%c queue max run limit reached", e->etype + 'a'); 2054 resched(qp->nwait); 2055 return (0); 2056 } 2057 rp = rinfo_get(0); /* allocating a new runinfo struct */ 2058 2059 #ifdef ATLIMIT 2060 if ((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) { 2061 msg("ATLIMIT (%d) reached for uid %d", 2062 ATLIMIT, (e->u)->uid); 2063 rinfo_free(rp); 2064 resched(qp->nwait); 2065 return (0); 2066 } 2067 #endif 2068 #ifdef CRONLIMIT 2069 if ((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) { 2070 msg("CRONLIMIT (%d) reached for uid %d", 2071 CRONLIMIT, (e->u)->uid); 2072 rinfo_free(rp); 2073 resched(qp->nwait); 2074 return (0); 2075 } 2076 #endif 2077 if ((e->u)->uid == 0) { /* set default path */ 2078 /* path settable in defaults file */ 2079 envinit[2] = supath; 2080 } else { 2081 envinit[2] = path; 2082 } 2083 2084 /* 2085 * the tempnam() function uses malloc(3C) to allocate space for the 2086 * constructed file name, and returns a pointer to this area, which 2087 * is assigned to rp->outfile. Here rp->outfile is not overwritten. 2088 */ 2089 2090 rp->outfile = tempnam(TMPDIR, PFX); 2091 rp->jobtype = e->etype; 2092 if (e->etype == CRONEVENT) { 2093 rp->jobname = xmalloc(strlen(e->cmd) + 1); 2094 (void) strcpy(rp->jobname, e->cmd); 2095 /* "cron" jobs only produce mail if there's output */ 2096 rp->mailwhendone = 0; 2097 } else { 2098 at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2); 2099 (void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd); 2100 if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) { 2101 if (errno == ENAMETOOLONG) { 2102 if (chdir(ATDIR) == 0) 2103 cron_unlink(e->cmd); 2104 } else { 2105 cron_unlink(at_cmdfile); 2106 } 2107 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT); 2108 free(at_cmdfile); 2109 rinfo_free(rp); 2110 return (0); 2111 } 2112 rp->jobname = xmalloc(strlen(at_cmdfile) + 1); 2113 (void) strcpy(rp->jobname, at_cmdfile); 2114 2115 /* 2116 * Skip over the first two lines. 2117 */ 2118 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2119 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2120 if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n", 2121 mailvar) == 1) { 2122 /* 2123 * Check to see if we should always send mail 2124 * to the owner. 2125 */ 2126 rp->mailwhendone = (strcmp(mailvar, "yes") == 0); 2127 } else { 2128 rp->mailwhendone = 0; 2129 } 2130 2131 if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) { 2132 projflag = 1; 2133 } 2134 (void) fclose(atcmdfp); 2135 } 2136 2137 /* 2138 * we make sure that the system time 2139 * hasn't drifted backwards. if it has, el_add() is now 2140 * called, to make sure that the event queue is back in order, 2141 * and we set the delayed flag. cron will pick up the request 2142 * later on at the proper time. 2143 */ 2144 dhltime = time(NULL); 2145 if ((dhltime - e->time) < 0) { 2146 msg("clock time drifted backwards!\n"); 2147 if (next_event->etype == CRONEVENT) { 2148 msg("correcting cron event\n"); 2149 next_event->time = next_time(next_event, dhltime); 2150 el_add(next_event, next_event->time, 2151 (next_event->u)->ctid); 2152 } else { /* etype == ATEVENT */ 2153 msg("correcting batch event\n"); 2154 el_add(next_event, next_event->time, 2155 next_event->of.at.eventid); 2156 } 2157 delayed++; 2158 t_old = time(NULL); 2159 free(at_cmdfile); 2160 rinfo_free(rp); 2161 return (0); 2162 } 2163 2164 if ((rfork = fork()) == (pid_t)-1) { 2165 reap_child(); 2166 if ((rfork = fork()) == (pid_t)-1) { 2167 msg("cannot fork"); 2168 free(at_cmdfile); 2169 rinfo_free(rp); 2170 resched(60); 2171 (void) sleep(30); 2172 return (0); 2173 } 2174 } 2175 if (rfork) { /* parent process */ 2176 contract_abandon_latest(rfork); 2177 2178 ++qp->nrun; 2179 rp->pid = rfork; 2180 rp->que = e->etype; 2181 if (e->etype != CRONEVENT) 2182 (e->u)->aruncnt++; 2183 else 2184 (e->u)->cruncnt++; 2185 rp->rusr = (e->u); 2186 logit(BCHAR, rp, 0); 2187 free(at_cmdfile); 2188 2189 return (0); 2190 } 2191 2192 child_sigreset(); 2193 contract_clear_template(); 2194 2195 if (e->etype != CRONEVENT) { 2196 /* open jobfile as stdin to shell */ 2197 if (stat(at_cmdfile, &buf)) { 2198 if (errno == ENAMETOOLONG) { 2199 if (chdir(ATDIR) == 0) 2200 cron_unlink(e->cmd); 2201 } else 2202 cron_unlink(at_cmdfile); 2203 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2204 exit(1); 2205 } 2206 if (!(buf.st_mode&ISUID)) { 2207 /* 2208 * if setuid bit off, original owner has 2209 * given this file to someone else 2210 */ 2211 cron_unlink(at_cmdfile); 2212 exit(1); 2213 } 2214 if ((fd = open(at_cmdfile, O_RDONLY)) == -1) { 2215 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2216 cron_unlink(at_cmdfile); 2217 exit(1); 2218 } 2219 if (fd != 0) { 2220 (void) dup2(fd, 0); 2221 (void) close(fd); 2222 } 2223 /* 2224 * retrieve the project id of the at job and convert it 2225 * to a project name. fail if it's not a valid project 2226 * or if the user isn't a member of the project. 2227 */ 2228 if (projflag == 1) { 2229 if ((pproj = getprojbyid(projid, &proj, 2230 (void *)&mybuf, sizeof (mybuf))) == NULL || 2231 !inproj(e->u->name, pproj->pj_name, 2232 mybuf2, sizeof (mybuf2))) { 2233 cron_unlink(at_cmdfile); 2234 mail((e->u)->name, BADPROJID, ERR_CANTEXECAT); 2235 exit(1); 2236 } 2237 } 2238 } 2239 2240 /* 2241 * Put process in a new session, and create a new task. 2242 */ 2243 if (setsid() < 0) { 2244 msg("setsid failed with errno = %d. job failed (%s)" 2245 " for user %s", errno, e->cmd, e->u->name); 2246 if (e->etype != CRONEVENT) 2247 cron_unlink(at_cmdfile); 2248 exit(1); 2249 } 2250 2251 /* 2252 * set correct user identification and check his account 2253 */ 2254 r = set_user_cred(e->u, pproj); 2255 if (r == VUC_EXPIRED) { 2256 msg("user (%s) account is expired", e->u->name); 2257 audit_cron_user_acct_expired(e->u->name); 2258 clean_out_user(e->u); 2259 exit(1); 2260 } 2261 if (r == VUC_NEW_AUTH) { 2262 msg("user (%s) password has expired", e->u->name); 2263 audit_cron_user_acct_expired(e->u->name); 2264 clean_out_user(e->u); 2265 exit(1); 2266 } 2267 if (r != VUC_OK) { 2268 msg("bad user (%s)", e->u->name); 2269 audit_cron_bad_user(e->u->name); 2270 clean_out_user(e->u); 2271 exit(1); 2272 } 2273 /* 2274 * check user and initialize the supplementary group access list. 2275 * bugid 1230784: deleted from parent to avoid cron hang. Now 2276 * only child handles the call. 2277 */ 2278 2279 if (verify_user_cred(e->u) != VUC_OK || 2280 setgid(e->u->gid) == -1 || 2281 initgroups(e->u->name, e->u->gid) == -1) { 2282 msg("bad user (%s) or setgid failed (%s)", 2283 e->u->name, e->u->name); 2284 audit_cron_bad_user(e->u->name); 2285 clean_out_user(e->u); 2286 exit(1); 2287 } 2288 2289 if (e->etype != CRONEVENT) { 2290 r = audit_cron_session(e->u->name, NULL, 2291 e->u->uid, e->u->gid, at_cmdfile); 2292 cron_unlink(at_cmdfile); 2293 } else { 2294 r = audit_cron_session(e->u->name, CRONDIR, 2295 e->u->uid, e->u->gid, NULL); 2296 } 2297 if (r != 0) { 2298 msg("cron audit problem. job failed (%s) for user %s", 2299 e->cmd, e->u->name); 2300 exit(1); 2301 } 2302 2303 audit_cron_new_job(e->cmd, e->etype, (void *)e); 2304 2305 if (setuid(e->u->uid) == -1) { 2306 msg("setuid failed (%s)", e->u->name); 2307 clean_out_user(e->u); 2308 exit(1); 2309 } 2310 2311 if (e->etype == CRONEVENT) { 2312 /* check for standard input to command */ 2313 if (e->of.ct.input != NULL) { 2314 if ((tmpfile = strdup(TMPINFILE)) == NULL) { 2315 mail((e->u)->name, MALLOCERR, 2316 ERR_CANTEXECCRON); 2317 exit(1); 2318 } 2319 if ((fd = mkstemp(tmpfile)) == -1 || 2320 (fptr = fdopen(fd, "w")) == NULL) { 2321 mail((e->u)->name, NOSTDIN, 2322 ERR_CANTEXECCRON); 2323 cron_unlink(tmpfile); 2324 free(tmpfile); 2325 exit(1); 2326 } 2327 if ((fwrite(e->of.ct.input, sizeof (char), 2328 strlen(e->of.ct.input), fptr)) != 2329 strlen(e->of.ct.input)) { 2330 mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON); 2331 cron_unlink(tmpfile); 2332 free(tmpfile); 2333 (void) close(fd); 2334 (void) fclose(fptr); 2335 exit(1); 2336 } 2337 if (fseek(fptr, (off_t)0, SEEK_SET) != -1) { 2338 if (fd != 0) { 2339 (void) dup2(fd, 0); 2340 (void) close(fd); 2341 } 2342 } 2343 cron_unlink(tmpfile); 2344 free(tmpfile); 2345 (void) fclose(fptr); 2346 } else if ((fd = open("/dev/null", O_RDONLY)) > 0) { 2347 (void) dup2(fd, 0); 2348 (void) close(fd); 2349 } 2350 } 2351 2352 /* redirect stdout and stderr for the shell */ 2353 if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1) 2354 fd = open("/dev/null", O_WRONLY); 2355 2356 if (fd >= 0 && fd != 1) 2357 (void) dup2(fd, 1); 2358 2359 if (fd >= 0 && fd != 2) { 2360 (void) dup2(fd, 2); 2361 if (fd != 1) 2362 (void) close(fd); 2363 } 2364 2365 (void) strlcat(homedir, (e->u)->home, sizeof (homedir)); 2366 (void) strlcat(logname, (e->u)->name, sizeof (logname)); 2367 environ = envinit; 2368 if (chdir((e->u)->home) == -1) { 2369 mail((e->u)->name, CANTCDHOME, 2370 e->etype == CRONEVENT ? ERR_CANTEXECCRON : 2371 ERR_CANTEXECAT); 2372 exit(1); 2373 } 2374 #ifdef TESTING 2375 exit(1); 2376 #endif 2377 /* 2378 * make sure that all file descriptors EXCEPT 0, 1 and 2 2379 * will be closed. 2380 */ 2381 closefrom(3); 2382 2383 if ((e->u)->uid != 0) 2384 (void) nice(qp->nice); 2385 if (e->etype == CRONEVENT) 2386 (void) execl(SHELL, "sh", "-c", e->cmd, 0); 2387 else /* type == ATEVENT */ 2388 (void) execl(SHELL, "sh", 0); 2389 mail((e->u)->name, CANTEXECSH, 2390 e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT); 2391 exit(1); 2392 /*NOTREACHED*/ 2393 } 2394 2395 static int 2396 idle(long t) 2397 { 2398 time_t now; 2399 2400 while (t > 0L) { 2401 2402 if (msg_wait(t) != 0) { 2403 /* we need to run next job immediately */ 2404 return (0); 2405 } 2406 2407 reap_child(); 2408 2409 now = time(NULL); 2410 if (last_time > now) { 2411 /* clock has been reset */ 2412 return (1); 2413 } 2414 2415 if (next_event == NULL && !el_empty()) { 2416 next_event = (struct event *)el_first(); 2417 } 2418 if (next_event == NULL) 2419 t = INFINITY; 2420 else 2421 t = (long)next_event->time - now; 2422 } 2423 return (0); 2424 } 2425 2426 /* 2427 * This used to be in the idle(), but moved to the separate function. 2428 * This called from various place when cron needs to reap the 2429 * child. It includes the situation that cron hit maxrun, and needs 2430 * to reschedule the job. 2431 */ 2432 static void 2433 reap_child() 2434 { 2435 pid_t pid; 2436 int prc; 2437 struct runinfo *rp; 2438 2439 for (;;) { 2440 pid = waitpid((pid_t)-1, &prc, WNOHANG); 2441 if (pid <= 0) 2442 break; 2443 #ifdef DEBUG 2444 fprintf(stderr, 2445 "wait returned %x for process %d\n", prc, pid); 2446 #endif 2447 if ((rp = rinfo_get(pid)) == NULL) { 2448 if (miscpid_delete(pid) == 0) { 2449 /* not found in anywhere */ 2450 msg(PIDERR, pid); 2451 } 2452 } else if (rp->que == ZOMB) { 2453 (void) unlink(rp->outfile); 2454 rinfo_free(rp); 2455 } else { 2456 cleanup(rp, prc); 2457 } 2458 } 2459 } 2460 2461 static void 2462 cleanup(struct runinfo *pr, int rc) 2463 { 2464 int nextfork = 1; 2465 struct usr *p; 2466 struct stat buf; 2467 2468 logit(ECHAR, pr, rc); 2469 --qt[pr->que].nrun; 2470 p = pr->rusr; 2471 if (pr->que != CRONEVENT) 2472 --p->aruncnt; 2473 else 2474 --p->cruncnt; 2475 2476 if (lstat(pr->outfile, &buf) == 0) { 2477 if (!S_ISLNK(buf.st_mode) && 2478 (buf.st_size > 0 || pr->mailwhendone)) { 2479 /* mail user stdout and stderr */ 2480 for (;;) { 2481 if ((pr->pid = fork()) < 0) { 2482 /* 2483 * if fork fails try forever in doubling 2484 * retry times, up to 16 seconds 2485 */ 2486 (void) sleep(nextfork); 2487 if (nextfork < 16) 2488 nextfork += nextfork; 2489 continue; 2490 } else if (pr->pid == 0) { 2491 child_sigreset(); 2492 contract_clear_template(); 2493 2494 mail_result(p, pr, buf.st_size); 2495 /* NOTREACHED */ 2496 } else { 2497 contract_abandon_latest(pr->pid); 2498 pr->que = ZOMB; 2499 break; 2500 } 2501 } 2502 } else { 2503 (void) unlink(pr->outfile); 2504 rinfo_free(pr); 2505 } 2506 } else { 2507 rinfo_free(pr); 2508 } 2509 2510 free_if_unused(p); 2511 } 2512 2513 /* 2514 * Mail stdout and stderr of a job to user. Get uid for real user and become 2515 * that person. We do this so that mail won't come from root since this 2516 * could be a security hole. If failure, quit - don't send mail as root. 2517 */ 2518 static void 2519 mail_result(struct usr *p, struct runinfo *pr, size_t filesize) 2520 { 2521 struct passwd *ruser_ids; 2522 FILE *mailpipe; 2523 FILE *st; 2524 struct utsname name; 2525 int nbytes; 2526 char iobuf[BUFSIZ]; 2527 char *cmd; 2528 2529 (void) uname(&name); 2530 if ((ruser_ids = getpwnam(p->name)) == NULL) 2531 exit(0); 2532 (void) setuid(ruser_ids->pw_uid); 2533 2534 cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2); 2535 (void) sprintf(cmd, "%s %s", MAIL, p->name); 2536 mailpipe = popen(cmd, "w"); 2537 free(cmd); 2538 if (mailpipe == NULL) 2539 exit(127); 2540 (void) fprintf(mailpipe, "To: %s\n", p->name); 2541 if (pr->jobtype == CRONEVENT) { 2542 (void) fprintf(mailpipe, CRONOUT); 2543 (void) fprintf(mailpipe, "Your \"cron\" job on %s\n", 2544 name.nodename); 2545 if (pr->jobname != NULL) { 2546 (void) fprintf(mailpipe, "%s\n\n", pr->jobname); 2547 } 2548 } else { 2549 (void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n"); 2550 (void) fprintf(mailpipe, "Your \"at\" job on %s\n", 2551 name.nodename); 2552 if (pr->jobname != NULL) { 2553 (void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname); 2554 } 2555 } 2556 /* Tmp. file is fopen'ed w/ "r", secure open */ 2557 if (filesize > 0 && 2558 (st = fopen(pr->outfile, "r")) != NULL) { 2559 (void) fprintf(mailpipe, 2560 "produced the following output:\n\n"); 2561 while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0) 2562 (void) fwrite(iobuf, sizeof (char), nbytes, mailpipe); 2563 (void) fclose(st); 2564 } else { 2565 (void) fprintf(mailpipe, "completed.\n"); 2566 } 2567 (void) pclose(mailpipe); 2568 exit(0); 2569 } 2570 2571 static int 2572 msg_wait(long tim) 2573 { 2574 struct message msg; 2575 int cnt; 2576 time_t reftime; 2577 struct pollfd pfd[2]; 2578 int64_t tl; 2579 int timeout; 2580 static int pending_msg; 2581 static time_t pending_reftime; 2582 2583 if (pending_msg) { 2584 process_msg(&msgbuf, pending_reftime); 2585 pending_msg = 0; 2586 return (0); 2587 } 2588 2589 /* 2590 * We are opening the signal mask to receive SIGCLD. The notifypipe 2591 * is used to avoid race condition between SIGCLD and poll system 2592 * call. 2593 * If SIGCLD is delivered in poll(), poll will be interrupted, and 2594 * we will return to idle() to reap the dead children. 2595 * If SIGCLD is delivered between sigprocmask() below and poll(), 2596 * there is no way we can detect the SIGCLD because poll() won't 2597 * be interrupted. In such case, the dead children can't be wait'ed 2598 * until poll returns by timeout or a new job. To avoid this race 2599 * condition, child_handler write to the notifypipe, so that 2600 * poll() will be able to return with POLLIN which indicates that 2601 * we have received SIGCLD. 2602 * 2603 * Since the notifypipe is used to just let poll return from 2604 * system call, the data in the pipe won't be read. Therefore, 2605 * any data in the pipe needs to be flushed before opening signal 2606 * mask. 2607 * 2608 * Note that we can probably re-write this code with pselect() 2609 * which can handle this situation easily. 2610 */ 2611 (void) ioctl(notifypipe[0], I_FLUSH, FLUSHW); 2612 2613 pfd[0].fd = msgfd; 2614 pfd[0].events = POLLIN; 2615 pfd[1].fd = notifypipe[1]; 2616 pfd[1].events = POLLIN; 2617 2618 #ifdef CRON_MAXSLEEP 2619 /* 2620 * CRON_MAXSLEEP can be defined to have cron periodically wake 2621 * up, so that cron can detect a change of TOD and adjust the 2622 * sleep time accordingly. 2623 */ 2624 tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim; 2625 #endif 2626 tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000; 2627 2628 accept_sigcld = 1; 2629 (void) sigprocmask(SIG_UNBLOCK, &childmask, NULL); 2630 do { 2631 timeout = (tl > INT_MAX ? INT_MAX : (int)tl); 2632 tl -= timeout; 2633 cnt = poll(pfd, 2, timeout); 2634 if (cnt == -1 && errno != EINTR) { 2635 perror("! poll"); 2636 } 2637 } while (tl > 0 && cnt == 0); 2638 (void) sigprocmask(SIG_BLOCK, &childmask, NULL); 2639 accept_sigcld = 0; 2640 2641 /* 2642 * poll timeout or interrupted. 2643 */ 2644 if (cnt <= 0) 2645 return (0); 2646 2647 /* 2648 * Not the timeout or new job, but a SIGCLD has been delivered. 2649 */ 2650 if ((pfd[0].revents & POLLIN) == 0) 2651 return (0); 2652 2653 errno = 0; 2654 if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) { 2655 if (cnt != -1 || errno != EAGAIN) 2656 perror("! read"); 2657 return (0); 2658 } 2659 reftime = time(NULL); 2660 if (next_event != NULL && reftime >= next_event->time) { 2661 /* 2662 * we need to run the job before reloading crontab. 2663 */ 2664 (void) memcpy(&msgbuf, &msg, sizeof (msg)); 2665 pending_msg = 1; 2666 pending_reftime = reftime; 2667 return (1); 2668 } 2669 process_msg(&msg, reftime); 2670 return (0); 2671 } 2672 2673 /* 2674 * process the message supplied via pipe. This will be called either 2675 * immediately after cron read the message from pipe, or idle time 2676 * if the message was pending due to the job execution. 2677 */ 2678 static void 2679 process_msg(struct message *pmsg, time_t reftime) 2680 { 2681 if (pmsg->etype == NULL) 2682 return; 2683 2684 switch (pmsg->etype) { 2685 case AT: 2686 if (pmsg->action == DELETE) 2687 del_atjob(pmsg->fname, pmsg->logname); 2688 else 2689 mod_atjob(pmsg->fname, (time_t)0); 2690 break; 2691 case CRON: 2692 if (pmsg->action == DELETE) 2693 del_ctab(pmsg->fname); 2694 else 2695 mod_ctab(pmsg->fname, reftime); 2696 break; 2697 default: 2698 msg("message received - bad format"); 2699 break; 2700 } 2701 if (next_event != NULL) { 2702 if (next_event->etype == CRONEVENT) 2703 el_add(next_event, next_event->time, 2704 (next_event->u)->ctid); 2705 else /* etype == ATEVENT */ 2706 el_add(next_event, next_event->time, 2707 next_event->of.at.eventid); 2708 next_event = NULL; 2709 } 2710 (void) fflush(stdout); 2711 pmsg->etype = NULL; 2712 } 2713 2714 /* 2715 * Allocate a new or find an existing runinfo structure 2716 */ 2717 static struct runinfo * 2718 rinfo_get(pid_t pid) 2719 { 2720 struct runinfo *rp; 2721 2722 if (pid == 0) { /* allocate a new entry */ 2723 rp = xcalloc(1, sizeof (struct runinfo)); 2724 rp->next = rthead; /* link the entry into the list */ 2725 rthead = rp; 2726 return (rp); 2727 } 2728 /* search the list for an existing entry */ 2729 for (rp = rthead; rp != NULL; rp = rp->next) { 2730 if (rp->pid == pid) 2731 break; 2732 } 2733 return (rp); 2734 } 2735 2736 /* 2737 * Free a runinfo structure and its associated memory 2738 */ 2739 static void 2740 rinfo_free(struct runinfo *entry) 2741 { 2742 struct runinfo **rpp; 2743 struct runinfo *rp; 2744 2745 #ifdef DEBUG 2746 (void) fprintf(stderr, "freeing job %s\n", entry->jobname); 2747 #endif 2748 for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) { 2749 if (rp == entry) { 2750 *rpp = rp->next; /* unlink the entry */ 2751 free(rp->outfile); 2752 free(rp->jobname); 2753 free(rp); 2754 break; 2755 } 2756 } 2757 } 2758 2759 /* ARGSUSED */ 2760 static void 2761 thaw_handler(int sig) 2762 { 2763 ; 2764 } 2765 2766 2767 /* ARGSUSED */ 2768 static void 2769 cronend(int sig) 2770 { 2771 crabort("SIGTERM", REMOVE_FIFO); 2772 } 2773 2774 /*ARGSUSED*/ 2775 static void 2776 child_handler(int sig) 2777 { 2778 /* 2779 * Just in case someone changes the signal mask. 2780 * we don't want to notify the SIGCLD. 2781 */ 2782 if (accept_sigcld) { 2783 (void) write(notifypipe[0], &sig, 1); 2784 } 2785 } 2786 2787 static void 2788 child_sigreset(void) 2789 { 2790 (void) signal(SIGCLD, SIG_DFL); 2791 (void) sigprocmask(SIG_SETMASK, &defmask, NULL); 2792 } 2793 2794 /* 2795 * crabort() - handle exits out of cron 2796 */ 2797 static void 2798 crabort(char *mssg, int action) 2799 { 2800 int c; 2801 2802 if (action & REMOVE_FIFO) { 2803 /* FIFO vanishes when cron finishes */ 2804 if (unlink(FIFO) < 0) 2805 perror("cron could not unlink FIFO"); 2806 } 2807 2808 if (action & CONSOLE_MSG) { 2809 /* write error msg to console */ 2810 if ((c = open(CONSOLE, O_WRONLY)) >= 0) { 2811 (void) write(c, "cron aborted: ", 14); 2812 (void) write(c, mssg, strlen(mssg)); 2813 (void) write(c, "\n", 1); 2814 (void) close(c); 2815 } 2816 } 2817 2818 /* always log the message */ 2819 msg(mssg); 2820 msg("******* CRON ABORTED ********"); 2821 exit(1); 2822 } 2823 2824 /* 2825 * msg() - time-stamped error reporting function 2826 */ 2827 /*PRINTFLIKE1*/ 2828 static void 2829 msg(char *fmt, ...) 2830 { 2831 va_list args; 2832 time_t t; 2833 2834 t = time(NULL); 2835 2836 (void) fflush(stdout); 2837 2838 (void) fprintf(stderr, "! "); 2839 2840 va_start(args, fmt); 2841 (void) vfprintf(stderr, fmt, args); 2842 va_end(args); 2843 2844 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2845 (void) fprintf(stderr, " %s\n", timebuf); 2846 2847 (void) fflush(stderr); 2848 } 2849 2850 static void 2851 logit(int cc, struct runinfo *rp, int rc) 2852 { 2853 time_t t; 2854 int ret; 2855 2856 if (!log) 2857 return; 2858 2859 t = time(NULL); 2860 if (cc == BCHAR) 2861 (void) printf("%c CMD: %s\n", cc, next_event->cmd); 2862 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2863 (void) printf("%c %.8s %u %c %s", 2864 cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf); 2865 if ((ret = TSTAT(rc)) != 0) 2866 (void) printf(" ts=%d", ret); 2867 if ((ret = RCODE(rc)) != 0) 2868 (void) printf(" rc=%d", ret); 2869 (void) putchar('\n'); 2870 (void) fflush(stdout); 2871 } 2872 2873 static void 2874 resched(int delay) 2875 { 2876 time_t nt; 2877 2878 /* run job at a later time */ 2879 nt = next_event->time + delay; 2880 if (next_event->etype == CRONEVENT) { 2881 next_event->time = next_time(next_event, (time_t)0); 2882 if (nt < next_event->time) 2883 next_event->time = nt; 2884 el_add(next_event, next_event->time, (next_event->u)->ctid); 2885 delayed = 1; 2886 msg("rescheduling a cron job"); 2887 return; 2888 } 2889 add_atevent(next_event->u, next_event->cmd, nt, next_event->etype); 2890 msg("rescheduling at job"); 2891 } 2892 2893 static void 2894 quedefs(int action) 2895 { 2896 int i; 2897 int j; 2898 char qbuf[QBUFSIZ]; 2899 FILE *fd; 2900 2901 /* set up default queue definitions */ 2902 for (i = 0; i < NQUEUE; i++) { 2903 qt[i].njob = qd.njob; 2904 qt[i].nice = qd.nice; 2905 qt[i].nwait = qd.nwait; 2906 } 2907 if (action == DEFAULT) 2908 return; 2909 if ((fd = fopen(QUEDEFS, "r")) == NULL) { 2910 msg("cannot open quedefs file"); 2911 msg("using default queue definitions"); 2912 return; 2913 } 2914 while (fgets(qbuf, QBUFSIZ, fd) != NULL) { 2915 if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.') 2916 continue; 2917 parsqdef(&qbuf[2]); 2918 qt[j].njob = qq.njob; 2919 qt[j].nice = qq.nice; 2920 qt[j].nwait = qq.nwait; 2921 } 2922 (void) fclose(fd); 2923 } 2924 2925 static void 2926 parsqdef(char *name) 2927 { 2928 int i; 2929 2930 qq = qd; 2931 while (*name) { 2932 i = 0; 2933 while (isdigit(*name)) { 2934 i *= 10; 2935 i += *name++ - '0'; 2936 } 2937 switch (*name++) { 2938 case JOBF: 2939 qq.njob = i; 2940 break; 2941 case NICEF: 2942 qq.nice = i; 2943 break; 2944 case WAITF: 2945 qq.nwait = i; 2946 break; 2947 } 2948 } 2949 } 2950 2951 /* 2952 * defaults - read defaults from /etc/default/cron 2953 */ 2954 static void 2955 defaults() 2956 { 2957 int flags; 2958 char *deflog; 2959 char *hz, *tz; 2960 2961 /* 2962 * get HZ value for environment 2963 */ 2964 if ((hz = getenv("HZ")) == (char *)NULL) 2965 (void) sprintf(hzname, "HZ=%d", HZ); 2966 else 2967 (void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz); 2968 /* 2969 * get TZ value for environment 2970 */ 2971 (void) snprintf(tzone, sizeof (tzone), "TZ=%s", 2972 ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ); 2973 2974 if (defopen(DEFFILE) == 0) { 2975 /* ignore case */ 2976 flags = defcntl(DC_GETFLAGS, 0); 2977 TURNOFF(flags, DC_CASE); 2978 (void) defcntl(DC_SETFLAGS, flags); 2979 2980 if (((deflog = defread("CRONLOG=")) == NULL) || 2981 (*deflog == 'N') || (*deflog == 'n')) 2982 log = 0; 2983 else 2984 log = 1; 2985 /* fix for 1087611 - allow paths to be set in defaults file */ 2986 if ((Def_path = defread("PATH=")) != NULL) { 2987 (void) strlcat(path, Def_path, LINE_MAX); 2988 } else { 2989 (void) strlcpy(path, NONROOTPATH, LINE_MAX); 2990 } 2991 if ((Def_supath = defread("SUPATH=")) != NULL) { 2992 (void) strlcat(supath, Def_supath, LINE_MAX); 2993 } else { 2994 (void) strlcpy(supath, ROOTPATH, LINE_MAX); 2995 } 2996 (void) defopen(NULL); 2997 } 2998 } 2999 3000 /* 3001 * Determine if a user entry for a job is still ok. The method used here 3002 * is a lot (about 75x) faster than using setgrent() / getgrent() 3003 * endgrent(). It should be safe because we use the sysconf to determine 3004 * the max, and it tolerates the max being 0. 3005 */ 3006 3007 static int 3008 verify_user_cred(struct usr *u) 3009 { 3010 struct passwd *pw; 3011 size_t numUsrGrps = 0; 3012 size_t numOrigGrps = 0; 3013 size_t i; 3014 int retval; 3015 3016 /* 3017 * Maximum number of groups a user may be in concurrently. This 3018 * is a value which we obtain at runtime through a sysconf() 3019 * call. 3020 */ 3021 3022 static size_t nGroupsMax = (size_t)-1; 3023 3024 /* 3025 * Arrays for cron user's group list, constructed at startup to 3026 * be nGroupsMax elements long, used for verifying user 3027 * credentials prior to execution. 3028 */ 3029 3030 static gid_t *UsrGrps; 3031 static gid_t *OrigGrps; 3032 3033 if ((pw = getpwnam(u->name)) == NULL) 3034 return (VUC_BADUSER); 3035 if (u->home != NULL) { 3036 if (strcmp(u->home, pw->pw_dir) != 0) { 3037 free(u->home); 3038 u->home = xmalloc(strlen(pw->pw_dir) + 1); 3039 (void) strcpy(u->home, pw->pw_dir); 3040 } 3041 } else { 3042 u->home = xmalloc(strlen(pw->pw_dir) + 1); 3043 (void) strcpy(u->home, pw->pw_dir); 3044 } 3045 if (u->uid != pw->pw_uid) 3046 u->uid = pw->pw_uid; 3047 if (u->gid != pw->pw_gid) 3048 u->gid = pw->pw_gid; 3049 3050 /* 3051 * Create the group id lists needed for job credential 3052 * verification. 3053 */ 3054 3055 if (nGroupsMax == (size_t)-1) { 3056 if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) { 3057 UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 3058 OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 3059 } 3060 3061 #ifdef DEBUG 3062 (void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax); 3063 #endif 3064 } 3065 3066 #ifdef DEBUG 3067 (void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name, 3068 pw->pw_uid); 3069 (void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, " 3070 "u->gid = %d\n", pw->pw_gid, u->gid); 3071 #endif 3072 3073 retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP; 3074 3075 if (nGroupsMax > 0) { 3076 numOrigGrps = getgroups(nGroupsMax, OrigGrps); 3077 3078 (void) initgroups(pw->pw_name, pw->pw_gid); 3079 numUsrGrps = getgroups(nGroupsMax, UsrGrps); 3080 3081 for (i = 0; i < numUsrGrps; i++) { 3082 if (UsrGrps[i] == u->gid) { 3083 retval = VUC_OK; 3084 break; 3085 } 3086 } 3087 3088 if (OrigGrps) { 3089 (void) setgroups(numOrigGrps, OrigGrps); 3090 } 3091 } 3092 3093 #ifdef DEBUG 3094 (void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval); 3095 #endif 3096 3097 return (retval); 3098 } 3099 3100 static int 3101 set_user_cred(const struct usr *u, struct project *pproj) 3102 { 3103 static char *progname = "cron"; 3104 int r = 0, rval = 0; 3105 3106 if ((r = pam_start(progname, u->name, &pam_conv, &pamh)) 3107 != PAM_SUCCESS) { 3108 #ifdef DEBUG 3109 msg("pam_start returns %d\n", r); 3110 #endif 3111 rval = VUC_BADUSER; 3112 goto set_eser_cred_exit; 3113 } 3114 3115 r = pam_acct_mgmt(pamh, 0); 3116 #ifdef DEBUG 3117 msg("pam_acc_mgmt returns %d\n", r); 3118 #endif 3119 if (r == PAM_ACCT_EXPIRED) { 3120 rval = VUC_EXPIRED; 3121 goto set_eser_cred_exit; 3122 } 3123 if (r == PAM_NEW_AUTHTOK_REQD) { 3124 rval = VUC_NEW_AUTH; 3125 goto set_eser_cred_exit; 3126 } 3127 if (r != PAM_SUCCESS) { 3128 rval = VUC_BADUSER; 3129 goto set_eser_cred_exit; 3130 } 3131 3132 if (pproj != NULL) { 3133 size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name); 3134 char *buf = alloca(sz); 3135 3136 (void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name); 3137 (void) pam_set_item(pamh, PAM_RESOURCE, buf); 3138 } 3139 3140 r = pam_setcred(pamh, PAM_ESTABLISH_CRED); 3141 if (r != PAM_SUCCESS) 3142 rval = VUC_BADUSER; 3143 3144 set_eser_cred_exit: 3145 (void) pam_end(pamh, r); 3146 return (rval); 3147 } 3148 3149 static void 3150 clean_out_user(struct usr *u) 3151 { 3152 if (next_event->u == u) { 3153 next_event = NULL; 3154 } 3155 3156 clean_out_ctab(u); 3157 clean_out_atjobs(u); 3158 free_if_unused(u); 3159 } 3160 3161 static void 3162 clean_out_atjobs(struct usr *u) 3163 { 3164 struct event *ev, *pv; 3165 3166 for (pv = NULL, ev = u->atevents; 3167 ev != NULL; 3168 pv = ev, ev = ev->link, free(pv)) { 3169 el_remove(ev->of.at.eventid, 1); 3170 if (cwd == AT) 3171 cron_unlink(ev->cmd); 3172 else { 3173 char buf[PATH_MAX]; 3174 if (strlen(ATDIR) + strlen(ev->cmd) + 2 3175 < PATH_MAX) { 3176 (void) sprintf(buf, "%s/%s", ATDIR, ev->cmd); 3177 cron_unlink(buf); 3178 } 3179 } 3180 free(ev->cmd); 3181 } 3182 3183 u->atevents = NULL; 3184 } 3185 3186 static void 3187 clean_out_ctab(struct usr *u) 3188 { 3189 rm_ctevents(u); 3190 el_remove(u->ctid, 0); 3191 u->ctid = 0; 3192 u->ctexists = 0; 3193 } 3194 3195 static void 3196 cron_unlink(char *name) 3197 { 3198 int r; 3199 3200 r = unlink(name); 3201 if (r == 0 || (r == -1 && errno == ENOENT)) { 3202 (void) audit_cron_delete_anc_file(name, NULL); 3203 } 3204 } 3205 3206 static void 3207 create_anc_ctab(struct event *e) 3208 { 3209 if (audit_cron_create_anc_file(e->u->name, 3210 (cwd == CRON) ? NULL:CRONDIR, 3211 e->u->name, e->u->uid) == -1) { 3212 process_anc_files(CRON_ANC_DELETE); 3213 crabort("cannot create ancillary files for crontabs", 3214 REMOVE_FIFO|CONSOLE_MSG); 3215 } 3216 } 3217 3218 static void 3219 delete_anc_ctab(struct event *e) 3220 { 3221 (void) audit_cron_delete_anc_file(e->u->name, 3222 (cwd == CRON) ? NULL:CRONDIR); 3223 } 3224 3225 static void 3226 create_anc_atjob(struct event *e) 3227 { 3228 if (!e->of.at.exists) 3229 return; 3230 3231 if (audit_cron_create_anc_file(e->cmd, 3232 (cwd == AT) ? NULL:ATDIR, 3233 e->u->name, e->u->uid) == -1) { 3234 process_anc_files(CRON_ANC_DELETE); 3235 crabort("cannot create ancillary files for atjobs", 3236 REMOVE_FIFO|CONSOLE_MSG); 3237 } 3238 } 3239 3240 static void 3241 delete_anc_atjob(struct event *e) 3242 { 3243 if (!e->of.at.exists) 3244 return; 3245 3246 (void) audit_cron_delete_anc_file(e->cmd, 3247 (cwd == AT) ? NULL:ATDIR); 3248 } 3249 3250 3251 static void 3252 process_anc_files(int del) 3253 { 3254 struct usr *u = uhead; 3255 struct event *e; 3256 3257 if (!audit_cron_mode()) 3258 return; 3259 3260 for (;;) { 3261 if (u->ctexists && u->ctevents != NULL) { 3262 e = u->ctevents; 3263 for (;;) { 3264 if (del) 3265 delete_anc_ctab(e); 3266 else 3267 create_anc_ctab(e); 3268 if ((e = e->link) == NULL) 3269 break; 3270 } 3271 } 3272 3273 if (u->atevents != NULL) { 3274 e = u->atevents; 3275 for (;;) { 3276 if (del) 3277 delete_anc_atjob(e); 3278 else 3279 create_anc_atjob(e); 3280 if ((e = e->link) == NULL) 3281 break; 3282 } 3283 } 3284 3285 if ((u = u->nextusr) == NULL) 3286 break; 3287 } 3288 } 3289 3290 /*ARGSUSED*/ 3291 static int 3292 cron_conv(int num_msg, struct pam_message **msgs, 3293 struct pam_response **response, void *appdata_ptr) 3294 { 3295 struct pam_message **m = msgs; 3296 int i; 3297 3298 for (i = 0; i < num_msg; i++) { 3299 switch (m[i]->msg_style) { 3300 case PAM_ERROR_MSG: 3301 case PAM_TEXT_INFO: 3302 if (m[i]->msg != NULL) { 3303 (void) msg("%s\n", m[i]->msg); 3304 } 3305 break; 3306 3307 default: 3308 break; 3309 } 3310 } 3311 return (0); 3312 } 3313 3314 /* 3315 * Cron creates process for other than job. Mail process is the 3316 * one which rinfo does not cover. Therefore, miscpid will keep 3317 * track of the pids executed from cron. Otherwise, we will see 3318 * "unexpected pid returned.." messages appear in the log file. 3319 */ 3320 static void 3321 miscpid_insert(pid_t pid) 3322 { 3323 struct miscpid *mp; 3324 3325 mp = xmalloc(sizeof (*mp)); 3326 mp->pid = pid; 3327 mp->next = miscpid_head; 3328 miscpid_head = mp; 3329 } 3330 3331 static int 3332 miscpid_delete(pid_t pid) 3333 { 3334 struct miscpid *mp, *omp; 3335 int found = 0; 3336 3337 omp = NULL; 3338 for (mp = miscpid_head; mp != NULL; mp = mp->next) { 3339 if (mp->pid == pid) { 3340 found = 1; 3341 break; 3342 } 3343 omp = mp; 3344 } 3345 if (found) { 3346 if (omp != NULL) 3347 omp->next = mp->next; 3348 else 3349 miscpid_head = NULL; 3350 free(mp); 3351 } 3352 return (found); 3353 } 3354 3355 /* 3356 * Establish contract terms such that all children are in abandoned 3357 * process contracts. 3358 */ 3359 static void 3360 contract_set_template(void) 3361 { 3362 int fd; 3363 3364 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3365 crabort("cannot open process contract template", 3366 REMOVE_FIFO | CONSOLE_MSG); 3367 3368 if (ct_pr_tmpl_set_param(fd, 0) || 3369 ct_tmpl_set_informative(fd, 0) || 3370 ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR)) 3371 crabort("cannot establish contract template terms", 3372 REMOVE_FIFO | CONSOLE_MSG); 3373 3374 if (ct_tmpl_activate(fd)) 3375 crabort("cannot activate contract template", 3376 REMOVE_FIFO | CONSOLE_MSG); 3377 3378 (void) close(fd); 3379 } 3380 3381 /* 3382 * Clear active process contract template. 3383 */ 3384 static void 3385 contract_clear_template(void) 3386 { 3387 int fd; 3388 3389 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3390 crabort("cannot open process contract template", 3391 REMOVE_FIFO | CONSOLE_MSG); 3392 3393 if (ct_tmpl_clear(fd)) 3394 crabort("cannot clear contract template", 3395 REMOVE_FIFO | CONSOLE_MSG); 3396 3397 (void) close(fd); 3398 } 3399 3400 /* 3401 * Abandon latest process contract unconditionally. If we have leaked [some 3402 * critical amount], exit such that the kernel reaps our contracts. 3403 */ 3404 static void 3405 contract_abandon_latest(pid_t pid) 3406 { 3407 int r; 3408 ctid_t id; 3409 static uint_t cts_lost; 3410 3411 if (cts_lost > MAX_LOST_CONTRACTS) 3412 crabort("repeated failure to abandon contracts", 3413 REMOVE_FIFO | CONSOLE_MSG); 3414 3415 if (r = contract_latest(&id)) { 3416 msg("could not obtain latest contract for " 3417 "PID %ld: %s", pid, strerror(r)); 3418 cts_lost++; 3419 return; 3420 } 3421 3422 if (r = contract_abandon_id(id)) { 3423 msg("could not abandon latest contract %ld: %s", id, 3424 strerror(r)); 3425 cts_lost++; 3426 return; 3427 } 3428 } 3429