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 (u->home != NULL) { 902 if (strcmp(u->home, pw->pw_dir) != 0) { 903 free(u->home); 904 u->home = xmalloc(strlen(pw->pw_dir) + 1); 905 (void) strcpy(u->home, pw->pw_dir); 906 } 907 } else { 908 u->home = xmalloc(strlen(pw->pw_dir) + 1); 909 (void) strcpy(u->home, pw->pw_dir); 910 } 911 u->ctexists = TRUE; 912 if (u->ctid == 0) { 913 #ifdef DEBUG 914 (void) fprintf(stderr, "%s now has a crontab\n", 915 u->name); 916 #endif 917 /* user didnt have a crontab last time */ 918 u->ctid = ecid++; 919 u->ctevents = NULL; 920 readcron(u, reftime); 921 return; 922 } 923 #ifdef DEBUG 924 (void) fprintf(stderr, "%s has revised his crontab\n", u->name); 925 #endif 926 rm_ctevents(u); 927 el_remove(u->ctid, 0); 928 readcron(u, reftime); 929 } 930 } 931 932 /* ARGSUSED */ 933 static void 934 mod_atjob(char *name, time_t reftime) 935 { 936 char *ptr; 937 time_t tim; 938 struct passwd *pw; 939 struct stat buf; 940 struct usr *u; 941 char namebuf[PATH_MAX]; 942 char *pname; 943 int jobtype; 944 945 ptr = name; 946 if (((tim = num(&ptr)) == 0) || (*ptr != '.')) 947 return; 948 ptr++; 949 if (!isalpha(*ptr)) 950 return; 951 jobtype = *ptr - 'a'; 952 953 /* check for audit ancillary file */ 954 if (audit_cron_is_anc_name(name)) 955 return; 956 957 if (cwd != AT) { 958 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name) 959 >= sizeof (namebuf)) { 960 return; 961 } 962 pname = namebuf; 963 } else { 964 pname = name; 965 } 966 if (stat(pname, &buf) || jobtype >= NQUEUE) { 967 cron_unlink(pname); 968 return; 969 } 970 if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) { 971 cron_unlink(pname); 972 return; 973 } 974 if ((pw = getpwuid(buf.st_uid)) == NULL) { 975 cron_unlink(pname); 976 return; 977 } 978 /* 979 * a warning message is given by the at command so there is no 980 * need to give one here......use this code if you only want 981 * users with a login shell of /usr/bin/sh to use cron 982 */ 983 #ifdef BOURNESHELLONLY 984 if ((strcmp(pw->pw_shell, "") != 0) && 985 (strcmp(pw->pw_shell, SHELL) != 0)) { 986 mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT); 987 cron_unlink(pname); 988 return; 989 } 990 #endif 991 if ((u = find_usr(pw->pw_name)) == NULL) { 992 #ifdef DEBUG 993 (void) fprintf(stderr, "new user (%s) with an at job = %s\n", 994 pw->pw_name, name); 995 #endif 996 u = create_ulist(pw->pw_name, ATEVENT); 997 u->home = xmalloc(strlen(pw->pw_dir) + 1); 998 (void) strcpy(u->home, pw->pw_dir); 999 u->uid = pw->pw_uid; 1000 u->gid = pw->pw_gid; 1001 add_atevent(u, name, tim, jobtype); 1002 } else { 1003 u->uid = pw->pw_uid; 1004 u->gid = pw->pw_gid; 1005 free(u->home); 1006 u->home = xmalloc(strlen(pw->pw_dir) + 1); 1007 (void) strcpy(u->home, pw->pw_dir); 1008 update_atevent(u, name, tim, jobtype); 1009 } 1010 } 1011 1012 static void 1013 add_atevent(struct usr *u, char *job, time_t tim, int jobtype) 1014 { 1015 struct event *e; 1016 1017 e = xmalloc(sizeof (struct event)); 1018 e->etype = jobtype; 1019 e->cmd = xmalloc(strlen(job) + 1); 1020 (void) strcpy(e->cmd, job); 1021 e->u = u; 1022 e->link = u->atevents; 1023 u->atevents = e; 1024 e->of.at.exists = TRUE; 1025 e->of.at.eventid = ecid++; 1026 if (tim < init_time) /* old job */ 1027 e->time = init_time; 1028 else 1029 e->time = tim; 1030 #ifdef DEBUG 1031 (void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n", 1032 u->name, e->cmd, e->time); 1033 #endif 1034 el_add(e, e->time, e->of.at.eventid); 1035 } 1036 1037 void 1038 update_atevent(struct usr *u, char *name, time_t tim, int jobtype) 1039 { 1040 struct event *e; 1041 1042 e = u->atevents; 1043 while (e != NULL) { 1044 if (strcmp(e->cmd, name) == 0) { 1045 e->of.at.exists = TRUE; 1046 break; 1047 } else { 1048 e = e->link; 1049 } 1050 } 1051 if (e == NULL) { 1052 #ifdef DEBUG 1053 (void) fprintf(stderr, "%s has a new at job = %s\n", 1054 u->name, name); 1055 #endif 1056 add_atevent(u, name, tim, jobtype); 1057 } 1058 } 1059 1060 static char line[CTLINESIZE]; /* holds a line from a crontab file */ 1061 static int cursor; /* cursor for the above line */ 1062 1063 static void 1064 readcron(struct usr *u, time_t reftime) 1065 { 1066 /* 1067 * readcron reads in a crontab file for a user (u). The list of 1068 * events for user u is built, and u->events is made to point to 1069 * this list. Each event is also entered into the main event 1070 * list. 1071 */ 1072 FILE *cf; /* cf will be a user's crontab file */ 1073 struct event *e; 1074 int start; 1075 unsigned int i; 1076 char namebuf[PATH_MAX]; 1077 char *pname; 1078 int lineno = 0; 1079 1080 /* read the crontab file */ 1081 cte_init(); /* Init error handling */ 1082 if (cwd != CRON) { 1083 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", 1084 CRONDIR, u->name) >= sizeof (namebuf)) { 1085 return; 1086 } 1087 pname = namebuf; 1088 } else { 1089 pname = u->name; 1090 } 1091 if ((cf = fopen(pname, "r")) == NULL) { 1092 mail(u->name, NOREAD, ERR_UNIXERR); 1093 return; 1094 } 1095 while (fgets(line, CTLINESIZE, cf) != NULL) { 1096 /* process a line of a crontab file */ 1097 lineno++; 1098 if (cte_istoomany()) 1099 break; 1100 cursor = 0; 1101 while (line[cursor] == ' ' || line[cursor] == '\t') 1102 cursor++; 1103 if (line[cursor] == '#' || line[cursor] == '\n') 1104 continue; 1105 e = xmalloc(sizeof (struct event)); 1106 e->etype = CRONEVENT; 1107 if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) && 1108 ((e->of.ct.hour = next_field(0, 23)) != NULL) && 1109 ((e->of.ct.daymon = next_field(1, 31)) != NULL) && 1110 ((e->of.ct.month = next_field(1, 12)) != NULL) && 1111 ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) { 1112 free(e); 1113 cte_add(lineno, line); 1114 continue; 1115 } 1116 while (line[cursor] == ' ' || line[cursor] == '\t') 1117 cursor++; 1118 if (line[cursor] == '\n' || line[cursor] == '\0') 1119 continue; 1120 /* get the command to execute */ 1121 start = cursor; 1122 again: 1123 while ((line[cursor] != '%') && 1124 (line[cursor] != '\n') && 1125 (line[cursor] != '\0') && 1126 (line[cursor] != '\\')) 1127 cursor++; 1128 if (line[cursor] == '\\') { 1129 cursor += 2; 1130 goto again; 1131 } 1132 e->cmd = xmalloc(cursor-start + 1); 1133 (void) strncpy(e->cmd, line + start, cursor-start); 1134 e->cmd[cursor-start] = '\0'; 1135 /* see if there is any standard input */ 1136 if (line[cursor] == '%') { 1137 e->of.ct.input = xmalloc(strlen(line)-cursor + 1); 1138 (void) strcpy(e->of.ct.input, line + cursor + 1); 1139 for (i = 0; i < strlen(e->of.ct.input); i++) { 1140 if (e->of.ct.input[i] == '%') 1141 e->of.ct.input[i] = '\n'; 1142 } 1143 } else { 1144 e->of.ct.input = NULL; 1145 } 1146 /* have the event point to it's owner */ 1147 e->u = u; 1148 /* insert this event at the front of this user's event list */ 1149 e->link = u->ctevents; 1150 u->ctevents = e; 1151 /* set the time for the first occurance of this event */ 1152 e->time = next_time(e, reftime); 1153 /* finally, add this event to the main event list */ 1154 el_add(e, e->time, u->ctid); 1155 cte_valid(); 1156 #ifdef DEBUG 1157 cftime(timebuf, "%C", &e->time); 1158 (void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n", 1159 e->cmd, e->time, timebuf); 1160 #endif 1161 } 1162 cte_sendmail(u->name); /* mail errors if any to user */ 1163 (void) fclose(cf); 1164 } 1165 1166 /* 1167 * Below are the functions for handling of errors in crontabs. Concept is to 1168 * collect faulty lines and send one email at the end of the crontab 1169 * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation 1170 * of crontab is aborted. Otherwise reading of crontab is continued to the end 1171 * of the file but no further error logging appears. 1172 */ 1173 static void 1174 cte_init() 1175 { 1176 if (cte_text == NULL) 1177 cte_text = xmalloc(MAILBUFLEN); 1178 (void) strlcpy(cte_text, cte_intro, MAILBUFLEN); 1179 cte_lp = cte_text + sizeof (cte_intro) - 1; 1180 cte_free = MAILBINITFREE; 1181 cte_nvalid = 0; 1182 } 1183 1184 static void 1185 cte_add(int lineno, char *ctline) 1186 { 1187 int len; 1188 char *p; 1189 1190 if (cte_free >= LINELIMIT) { 1191 (void) sprintf(cte_lp, "%4d: ", lineno); 1192 (void) strlcat(cte_lp, ctline, LINELIMIT - 1); 1193 len = strlen(cte_lp); 1194 if (cte_lp[len - 1] != '\n') { 1195 cte_lp[len++] = '\n'; 1196 cte_lp[len] = '\0'; 1197 } 1198 for (p = cte_lp; *p; p++) { 1199 if (isprint(*p) || *p == '\n' || *p == '\t') 1200 continue; 1201 *p = '.'; 1202 } 1203 cte_lp += len; 1204 cte_free -= len; 1205 if (cte_free < LINELIMIT) { 1206 size_t buflen = MAILBUFLEN - (cte_lp - cte_text); 1207 (void) strlcpy(cte_lp, cte_trail1, buflen); 1208 if (cte_nvalid == 0) 1209 (void) strlcat(cte_lp, cte_trail2, buflen); 1210 } 1211 } 1212 } 1213 1214 static void 1215 cte_valid() 1216 { 1217 cte_nvalid++; 1218 } 1219 1220 static int 1221 cte_istoomany() 1222 { 1223 /* 1224 * Return TRUE only if all lines are faulty. So evaluation of 1225 * a crontab is not aborted if at least one valid line was found. 1226 */ 1227 return (cte_nvalid == 0 && cte_free < LINELIMIT); 1228 } 1229 1230 static void 1231 cte_sendmail(char *username) 1232 { 1233 if (cte_free < MAILBINITFREE) 1234 mail(username, cte_text, ERR_CRONTABENT); 1235 } 1236 1237 /* 1238 * Send mail with error message to a user 1239 */ 1240 static void 1241 mail(char *usrname, char *mesg, int format) 1242 { 1243 /* mail mails a user a message. */ 1244 FILE *pipe; 1245 char *temp; 1246 struct passwd *ruser_ids; 1247 pid_t fork_val; 1248 int saveerrno = errno; 1249 struct utsname name; 1250 1251 #ifdef TESTING 1252 return; 1253 #endif 1254 (void) uname(&name); 1255 if ((fork_val = fork()) == (pid_t)-1) { 1256 msg("cron cannot fork\n"); 1257 return; 1258 } 1259 if (fork_val == 0) { 1260 child_sigreset(); 1261 contract_clear_template(); 1262 if ((ruser_ids = getpwnam(usrname)) == NULL) 1263 exit(0); 1264 (void) setuid(ruser_ids->pw_uid); 1265 temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2); 1266 (void) sprintf(temp, "%s %s", MAIL, usrname); 1267 pipe = popen(temp, "w"); 1268 if (pipe != NULL) { 1269 (void) fprintf(pipe, "To: %s\n", usrname); 1270 switch (format) { 1271 case ERR_CRONTABENT: 1272 (void) fprintf(pipe, CRONTABERR); 1273 (void) fprintf(pipe, "Your \"crontab\" on %s\n", 1274 name.nodename); 1275 (void) fprintf(pipe, mesg); 1276 (void) fprintf(pipe, 1277 "\nEntries or crontab have been ignored\n"); 1278 break; 1279 case ERR_UNIXERR: 1280 (void) fprintf(pipe, "Subject: %s\n\n", mesg); 1281 (void) fprintf(pipe, 1282 "The error on %s was \"%s\"\n", 1283 name.nodename, errmsg(saveerrno)); 1284 break; 1285 1286 case ERR_CANTEXECCRON: 1287 (void) fprintf(pipe, 1288 "Subject: Couldn't run your \"cron\" job\n\n"); 1289 (void) fprintf(pipe, 1290 "Your \"cron\" job on %s ", name.nodename); 1291 (void) fprintf(pipe, "couldn't be run\n"); 1292 (void) fprintf(pipe, "%s\n", mesg); 1293 (void) fprintf(pipe, 1294 "The error was \"%s\"\n", errmsg(saveerrno)); 1295 break; 1296 1297 case ERR_CANTEXECAT: 1298 (void) fprintf(pipe, 1299 "Subject: Couldn't run your \"at\" job\n\n"); 1300 (void) fprintf(pipe, "Your \"at\" job on %s ", 1301 name.nodename); 1302 (void) fprintf(pipe, "couldn't be run\n"); 1303 (void) fprintf(pipe, "%s\n", mesg); 1304 (void) fprintf(pipe, 1305 "The error was \"%s\"\n", errmsg(saveerrno)); 1306 break; 1307 1308 default: 1309 break; 1310 } 1311 (void) pclose(pipe); 1312 } 1313 free(temp); 1314 exit(0); 1315 } 1316 1317 contract_abandon_latest(fork_val); 1318 1319 if (cron_pid == getpid()) { 1320 miscpid_insert(fork_val); 1321 } 1322 } 1323 1324 static char * 1325 next_field(int lower, int upper) 1326 { 1327 /* 1328 * next_field returns a pointer to a string which holds the next 1329 * field of a line of a crontab file. 1330 * if (numbers in this field are out of range (lower..upper), 1331 * or there is a syntax error) then 1332 * NULL is returned, and a mail message is sent to the 1333 * user telling him which line the error was in. 1334 */ 1335 1336 char *s; 1337 int num, num2, start; 1338 1339 while ((line[cursor] == ' ') || (line[cursor] == '\t')) 1340 cursor++; 1341 start = cursor; 1342 if (line[cursor] == '\0') { 1343 return (NULL); 1344 } 1345 if (line[cursor] == '*') { 1346 cursor++; 1347 if ((line[cursor] != ' ') && (line[cursor] != '\t')) 1348 return (NULL); 1349 s = xmalloc(2); 1350 (void) strcpy(s, "*"); 1351 return (s); 1352 } 1353 for (;;) { 1354 if (!isdigit(line[cursor])) 1355 return (NULL); 1356 num = 0; 1357 do { 1358 num = num*10 + (line[cursor]-'0'); 1359 } while (isdigit(line[++cursor])); 1360 if ((num < lower) || (num > upper)) 1361 return (NULL); 1362 if (line[cursor] == '-') { 1363 if (!isdigit(line[++cursor])) 1364 return (NULL); 1365 num2 = 0; 1366 do { 1367 num2 = num2*10 + (line[cursor]-'0'); 1368 } while (isdigit(line[++cursor])); 1369 if ((num2 < lower) || (num2 > upper)) 1370 return (NULL); 1371 } 1372 if ((line[cursor] == ' ') || (line[cursor] == '\t')) 1373 break; 1374 if (line[cursor] == '\0') 1375 return (NULL); 1376 if (line[cursor++] != ',') 1377 return (NULL); 1378 } 1379 s = xmalloc(cursor-start + 1); 1380 (void) strncpy(s, line + start, cursor-start); 1381 s[cursor-start] = '\0'; 1382 return (s); 1383 } 1384 1385 #define tm_cmp(t1, t2) (\ 1386 (t1)->tm_year == (t2)->tm_year && \ 1387 (t1)->tm_mon == (t2)->tm_mon && \ 1388 (t1)->tm_mday == (t2)->tm_mday && \ 1389 (t1)->tm_hour == (t2)->tm_hour && \ 1390 (t1)->tm_min == (t2)->tm_min) 1391 1392 #define tm_setup(tp, yr, mon, dy, hr, min, dst) \ 1393 (tp)->tm_year = yr; \ 1394 (tp)->tm_mon = mon; \ 1395 (tp)->tm_mday = dy; \ 1396 (tp)->tm_hour = hr; \ 1397 (tp)->tm_min = min; \ 1398 (tp)->tm_isdst = dst; \ 1399 (tp)->tm_sec = 0; \ 1400 (tp)->tm_wday = 0; \ 1401 (tp)->tm_yday = 0; 1402 1403 /* 1404 * modification for bugid 1104537. the second argument to next_time is 1405 * now the value of time(2) to be used. if this is 0, then use the 1406 * current time. otherwise, the second argument is the time from which to 1407 * calculate things. this is useful to correct situations where you've 1408 * gone backwards in time (I.e. the system's internal clock is correcting 1409 * itself backwards). 1410 */ 1411 1412 static time_t 1413 next_time(struct event *e, time_t tflag) 1414 { 1415 /* 1416 * returns the integer time for the next occurance of event e. 1417 * the following fields have ranges as indicated: 1418 * PRGM | min hour day of month mon day of week 1419 * ------|------------------------------------------------------- 1420 * cron | 0-59 0-23 1-31 1-12 0-6 (0=sunday) 1421 * time | 0-59 0-23 1-31 0-11 0-6 (0=sunday) 1422 * NOTE: this routine is hard to understand. 1423 */ 1424 1425 struct tm *tm, ref_tm, tmp, tmp1, tmp2; 1426 int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days, 1427 d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd, 1428 today; 1429 1430 time_t t, ref_t, t1, t2, zone_start; 1431 int fallback; 1432 extern int days_btwn(int, int, int, int, int, int); 1433 1434 if (tflag == 0) { 1435 t = time(NULL); /* original way of doing things */ 1436 } else { 1437 t = tflag; 1438 } 1439 1440 tm = &ref_tm; /* use a local variable and call localtime_r() */ 1441 ref_t = t; /* keep a copy of the reference time */ 1442 1443 recalc: 1444 fallback = 0; 1445 1446 (void) localtime_r(&t, tm); 1447 1448 if (daylight) { 1449 tmp = *tm; 1450 tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1); 1451 t1 = xmktime(&tmp); 1452 /* 1453 * see if we will have timezone switch over, and clock will 1454 * fall back. zone_start will hold the time when it happens 1455 * (ie time of PST -> PDT switch over). 1456 */ 1457 if (tm->tm_isdst != tmp.tm_isdst && 1458 (t1 - t) == (timezone - altzone) && 1459 tm_cmp(tm, &tmp)) { 1460 zone_start = get_switching_time(tmp.tm_isdst, t); 1461 fallback = 1; 1462 } 1463 } 1464 1465 tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1; /* 0-11 */ 1466 tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon); /* 1-31 */ 1467 tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek); /* 0-6 */ 1468 today = TRUE; 1469 if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) || 1470 (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) || 1471 (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) || 1472 (tm->tm_mon != tm_mon)) { 1473 today = FALSE; 1474 } 1475 m = tm->tm_min + (t == ref_t ? 1 : 0); 1476 if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) { 1477 m = 0; 1478 } 1479 min = next_ge(m%60, e->of.ct.minute); 1480 carry = (min < m) ? 1 : 0; 1481 h = tm->tm_hour + carry; 1482 hr = next_ge(h%24, e->of.ct.hour); 1483 carry = (hr < h) ? 1 : 0; 1484 1485 if (carry == 0 && today) { 1486 /* this event must occur today */ 1487 tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday, 1488 hr, min, tm->tm_isdst); 1489 tmp1 = tmp; 1490 if ((t1 = xmktime(&tmp1)) == (time_t)-1) { 1491 return (0); 1492 } 1493 if (daylight && tmp.tm_isdst != tmp1.tm_isdst) { 1494 /* In case we are falling back */ 1495 if (fallback) { 1496 /* we may need to run the job once more. */ 1497 t = zone_start; 1498 goto recalc; 1499 } 1500 1501 /* 1502 * In case we are not in falling back period, 1503 * calculate the time assuming the DST. If the 1504 * date/time is not altered by mktime, it is the 1505 * time to execute the job. 1506 */ 1507 tmp2 = tmp; 1508 tmp2.tm_isdst = tmp1.tm_isdst; 1509 if ((t1 = xmktime(&tmp2)) == (time_t)-1) { 1510 return (0); 1511 } 1512 if (tmp1.tm_isdst == tmp2.tm_isdst && 1513 tm_cmp(&tmp, &tmp2)) { 1514 /* 1515 * We got a valid time. 1516 */ 1517 return (t1); 1518 } else { 1519 /* 1520 * If the date does not match even if 1521 * we assume the alternate timezone, then 1522 * it must be the invalid time. eg 1523 * 2am while switching 1:59am to 3am. 1524 * t1 should point the time before the 1525 * switching over as we've calculate the 1526 * time with assuming alternate zone. 1527 */ 1528 if (tmp1.tm_isdst != tmp2.tm_isdst) { 1529 t = get_switching_time(tmp1.tm_isdst, 1530 t1); 1531 } else { 1532 /* does this really happen? */ 1533 t = get_switching_time(tmp1.tm_isdst, 1534 t1 - abs(timezone - altzone)); 1535 } 1536 if (t == (time_t)-1) 1537 return (0); 1538 } 1539 goto recalc; 1540 } 1541 if (tm_cmp(&tmp, &tmp1)) { 1542 /* got valid time */ 1543 return (t1); 1544 } else { 1545 /* 1546 * This should never happen, but just in 1547 * case, we fall back to the old code. 1548 */ 1549 if (tm->tm_min > min) { 1550 t += (time_t)(hr-tm->tm_hour-1) * HOUR + 1551 (time_t)(60-tm->tm_min + min) * MINUTE; 1552 } else { 1553 t += (time_t)(hr-tm->tm_hour) * HOUR + 1554 (time_t)(min-tm->tm_min) * MINUTE; 1555 } 1556 t1 = t; 1557 t -= (time_t)tm->tm_sec; 1558 (void) localtime_r(&t, &tmp); 1559 if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0)) 1560 t -= (timezone - altzone); 1561 return ((t <= ref_t) ? t1 : t); 1562 } 1563 } 1564 1565 /* 1566 * Job won't run today, however if we have a switch over within 1567 * one hour and we will have one hour time drifting back in this 1568 * period, we may need to run the job one more time if the job was 1569 * set to run on this hour of clock. 1570 */ 1571 if (fallback) { 1572 t = zone_start; 1573 goto recalc; 1574 } 1575 1576 min = next_ge(0, e->of.ct.minute); 1577 hr = next_ge(0, e->of.ct.hour); 1578 1579 /* 1580 * calculate the date of the next occurance of this event, which 1581 * will be on a different day than the current 1582 */ 1583 1584 /* check monthly day specification */ 1585 d1 = tm->tm_mday + 1; 1586 day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1, 1587 e->of.ct.daymon); 1588 carry1 = (day1 < d1) ? 1 : 0; 1589 1590 /* check weekly day specification */ 1591 d2 = tm->tm_wday + 1; 1592 wday = next_ge(d2%7, e->of.ct.dayweek); 1593 if (wday < d2) 1594 daysahead = 7 - d2 + wday; 1595 else 1596 daysahead = wday - d2; 1597 day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1; 1598 carry2 = (day2 < d1) ? 1 : 0; 1599 1600 /* 1601 * based on their respective specifications, day1, and day2 give 1602 * the day of the month for the next occurance of this event. 1603 */ 1604 if ((strcmp(e->of.ct.daymon, "*") == 0) && 1605 (strcmp(e->of.ct.dayweek, "*") != 0)) { 1606 day1 = day2; 1607 carry1 = carry2; 1608 } 1609 if ((strcmp(e->of.ct.daymon, "*") != 0) && 1610 (strcmp(e->of.ct.dayweek, "*") == 0)) { 1611 day2 = day1; 1612 carry2 = carry1; 1613 } 1614 1615 yr = tm->tm_year; 1616 if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) { 1617 /* event does not occur in this month */ 1618 m = tm->tm_mon + 1; 1619 mon = next_ge(m%12 + 1, e->of.ct.month) - 1; /* 0..11 */ 1620 carry = (mon < m) ? 1 : 0; 1621 yr += carry; 1622 /* recompute day1 and day2 */ 1623 day1 = next_ge(1, e->of.ct.daymon); 1624 db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon, 1625 1, yr) + 1; 1626 wd = (tm->tm_wday + db)%7; 1627 /* wd is the day of the week of the first of month mon */ 1628 wday = next_ge(wd, e->of.ct.dayweek); 1629 if (wday < wd) 1630 day2 = 1 + 7 - wd + wday; 1631 else 1632 day2 = 1 + wday - wd; 1633 if ((strcmp(e->of.ct.daymon, "*") != 0) && 1634 (strcmp(e->of.ct.dayweek, "*") == 0)) 1635 day2 = day1; 1636 if ((strcmp(e->of.ct.daymon, "*") == 0) && 1637 (strcmp(e->of.ct.dayweek, "*") != 0)) 1638 day1 = day2; 1639 day = (day1 < day2) ? day1 : day2; 1640 } else { /* event occurs in this month */ 1641 mon = tm->tm_mon; 1642 if (!carry1 && !carry2) 1643 day = (day1 < day2) ? day1 : day2; 1644 else if (!carry1) 1645 day = day1; 1646 else 1647 day = day2; 1648 } 1649 1650 /* 1651 * now that we have the min, hr, day, mon, yr of the next event, 1652 * figure out what time that turns out to be. 1653 */ 1654 tm_setup(&tmp, yr, mon, day, hr, min, -1); 1655 tmp2 = tmp; 1656 if ((t1 = xmktime(&tmp2)) == (time_t)-1) { 1657 return (0); 1658 } 1659 if (tm_cmp(&tmp, &tmp2)) { 1660 /* 1661 * mktime returns clock for the current time zone. If the 1662 * target date was in fallback period, it needs to be adjusted 1663 * to the time comes first. 1664 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03. 1665 * mktime returns the time in PST, but 1:30am in PDT comes 1666 * first. So reverse the tm_isdst, and see if we have such 1667 * time/date. 1668 */ 1669 if (daylight) { 1670 int dst = tmp2.tm_isdst; 1671 1672 tmp2 = tmp; 1673 tmp2.tm_isdst = (dst > 0 ? 0 : 1); 1674 if ((t2 = xmktime(&tmp2)) == (time_t)-1) { 1675 return (0); 1676 } 1677 if (tm_cmp(&tmp, &tmp2)) { 1678 /* 1679 * same time/date found in the opposite zone. 1680 * check the clock to see which comes early. 1681 */ 1682 if (t2 > ref_t && t2 < t1) { 1683 t1 = t2; 1684 } 1685 } 1686 } 1687 return (t1); 1688 } else { 1689 /* 1690 * mktime has set different time/date for the given date. 1691 * This means that the next job is scheduled to be run on the 1692 * invalid time. There are three possible invalid date/time. 1693 * 1. Non existing day of the month. such as April 31th. 1694 * 2. Feb 29th in the non-leap year. 1695 * 3. Time gap during the DST switch over. 1696 */ 1697 d1 = days_in_mon(mon, yr); 1698 if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) { 1699 /* 1700 * see if we have got a specific date which 1701 * is invalid. 1702 */ 1703 if (strcmp(e->of.ct.dayweek, "*") == 0 && 1704 mon == (next_ge((mon + 1)%12 + 1, 1705 e->of.ct.month) - 1) && 1706 day <= next_ge(1, e->of.ct.daymon)) { 1707 /* job never run */ 1708 return (0); 1709 } 1710 /* 1711 * Since the day has gone invalid, we need to go to 1712 * next month, and recalcuate the first occurrence. 1713 * eg the cron tab such as: 1714 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin.... 1715 * 2/31 is invalid, so the next job is 3/1. 1716 */ 1717 tmp2 = tmp; 1718 tmp2.tm_min = 0; 1719 tmp2.tm_hour = 0; 1720 tmp2.tm_mday = 1; /* 1st day of the month */ 1721 if (mon == 11) { 1722 tmp2.tm_mon = 0; 1723 tmp2.tm_year = yr + 1; 1724 } else { 1725 tmp2.tm_mon = mon + 1; 1726 } 1727 if ((t = xmktime(&tmp2)) == (time_t)-1) { 1728 return (0); 1729 } 1730 } else if (mon == 1 && day > d1) { 1731 /* 1732 * ie 29th in the non-leap year. Forwarding the 1733 * clock to Feb 29th 00:00 (March 1st), and recalculate 1734 * the next time. 1735 */ 1736 tmp2 = tmp; 1737 tmp2.tm_min = 0; 1738 tmp2.tm_hour = 0; 1739 if ((t = xmktime(&tmp2)) == (time_t)-1) { 1740 return (0); 1741 } 1742 } else if (daylight) { 1743 /* 1744 * Non existing time, eg 2am PST during summer time 1745 * switch. 1746 * We need to get the correct isdst which we are 1747 * swithing to, by adding time difference to make sure 1748 * that t2 is in the zone being switched. 1749 */ 1750 t2 = t1; 1751 t2 += abs(timezone - altzone); 1752 (void) localtime_r(&t2, &tmp2); 1753 zone_start = get_switching_time(tmp2.tm_isdst, 1754 t1 - abs(timezone - altzone)); 1755 if (zone_start == (time_t)-1) { 1756 return (0); 1757 } 1758 t = zone_start; 1759 } else { 1760 /* 1761 * This should never happen, but fall back to the 1762 * old code. 1763 */ 1764 days = days_btwn(tm->tm_mon, 1765 tm->tm_mday, tm->tm_year, mon, day, yr); 1766 t += (time_t)(23-tm->tm_hour)*HOUR 1767 + (time_t)(60-tm->tm_min)*MINUTE 1768 + (time_t)hr*HOUR + (time_t)min*MINUTE 1769 + (time_t)days*DAY; 1770 t1 = t; 1771 t -= (time_t)tm->tm_sec; 1772 (void) localtime_r(&t, &tmp); 1773 if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0)) 1774 t -= (timezone - altzone); 1775 return (t <= ref_t ? t1 : t); 1776 } 1777 goto recalc; 1778 } 1779 /*NOTREACHED*/ 1780 } 1781 1782 /* 1783 * This returns TOD in time_t that zone switch will happen, and this 1784 * will be called when clock fallback is about to happen. 1785 * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST 1786 * will fall back to 1:00 PDT. So this function will be called only 1787 * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)). 1788 * First goes through the common time differences to see if zone 1789 * switch happens at those minutes later. If not, check every minutes 1790 * until 6 hours ahead see if it happens(We might have 45minutes 1791 * fallback). 1792 */ 1793 static time_t 1794 get_switching_time(int to_dst, time_t t_ref) 1795 { 1796 time_t t, t1; 1797 struct tm tmp, tmp1; 1798 int hints[] = { 60, 120, 30, 90, 0}; /* minutes */ 1799 int i; 1800 1801 (void) localtime_r(&t_ref, &tmp); 1802 tmp1 = tmp; 1803 tmp1.tm_sec = 0; 1804 tmp1.tm_min = 0; 1805 if ((t = xmktime(&tmp1)) == (time_t)-1) 1806 return ((time_t)-1); 1807 1808 /* fast path */ 1809 for (i = 0; hints[i] != 0; i++) { 1810 t1 = t + hints[i] * 60; 1811 (void) localtime_r(&t1, &tmp1); 1812 if (tmp1.tm_isdst == to_dst) { 1813 t1--; 1814 (void) localtime_r(&t1, &tmp1); 1815 if (tmp1.tm_isdst != to_dst) { 1816 return (t1 + 1); 1817 } 1818 } 1819 } 1820 1821 /* ugly, but don't know other than this. */ 1822 tmp1 = tmp; 1823 tmp1.tm_sec = 0; 1824 if ((t = xmktime(&tmp1)) == (time_t)-1) 1825 return ((time_t)-1); 1826 while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */ 1827 t += 60; /* at least one minute, I assume */ 1828 (void) localtime_r(&t, &tmp); 1829 if (tmp.tm_isdst == to_dst) 1830 return (t); 1831 } 1832 return ((time_t)-1); 1833 } 1834 1835 static time_t 1836 xmktime(struct tm *tmp) 1837 { 1838 time_t ret; 1839 1840 if ((ret = mktime(tmp)) == (time_t)-1) { 1841 if (errno == EOVERFLOW) { 1842 return ((time_t)-1); 1843 } 1844 crabort("internal error: mktime failed", 1845 REMOVE_FIFO|CONSOLE_MSG); 1846 } 1847 return (ret); 1848 } 1849 1850 #define DUMMY 100 1851 1852 static int 1853 next_ge(int current, char *list) 1854 { 1855 /* 1856 * list is a character field as in a crontab file; 1857 * for example: "40, 20, 50-10" 1858 * next_ge returns the next number in the list that is 1859 * greater than or equal to current. if no numbers of list 1860 * are >= current, the smallest element of list is returned. 1861 * NOTE: current must be in the appropriate range. 1862 */ 1863 1864 char *ptr; 1865 int n, n2, min, min_gt; 1866 1867 if (strcmp(list, "*") == 0) 1868 return (current); 1869 ptr = list; 1870 min = DUMMY; 1871 min_gt = DUMMY; 1872 for (;;) { 1873 if ((n = (int)num(&ptr)) == current) 1874 return (current); 1875 if (n < min) 1876 min = n; 1877 if ((n > current) && (n < min_gt)) 1878 min_gt = n; 1879 if (*ptr == '-') { 1880 ptr++; 1881 if ((n2 = (int)num(&ptr)) > n) { 1882 if ((current > n) && (current <= n2)) 1883 return (current); 1884 } else { /* range that wraps around */ 1885 if (current > n) 1886 return (current); 1887 if (current <= n2) 1888 return (current); 1889 } 1890 } 1891 if (*ptr == '\0') 1892 break; 1893 ptr += 1; 1894 } 1895 if (min_gt != DUMMY) 1896 return (min_gt); 1897 else 1898 return (min); 1899 } 1900 1901 static void 1902 free_if_unused(struct usr *u) 1903 { 1904 struct usr *cur, *prev; 1905 /* 1906 * To make sure a usr structure is idle we must check that 1907 * there are no at jobs queued for the user; the user does 1908 * not have a crontab, and also that there are no running at 1909 * or cron jobs (since the runinfo structure also has a 1910 * pointer to the usr structure). 1911 */ 1912 if (!u->ctexists && u->atevents == NULL && 1913 u->cruncnt == 0 && u->aruncnt == 0) { 1914 #ifdef DEBUG 1915 (void) fprintf(stderr, "%s removed from usr list\n", u->name); 1916 #endif 1917 for (cur = uhead, prev = NULL; 1918 cur != u; 1919 prev = cur, cur = cur->nextusr) { 1920 if (cur == NULL) { 1921 return; 1922 } 1923 } 1924 1925 if (prev == NULL) 1926 uhead = u->nextusr; 1927 else 1928 prev->nextusr = u->nextusr; 1929 free(u->name); 1930 free(u->home); 1931 free(u); 1932 } 1933 } 1934 1935 static void 1936 del_atjob(char *name, char *usrname) 1937 { 1938 1939 struct event *e, *eprev; 1940 struct usr *u; 1941 1942 if ((u = find_usr(usrname)) == NULL) 1943 return; 1944 e = u->atevents; 1945 eprev = NULL; 1946 while (e != NULL) { 1947 if (strcmp(name, e->cmd) == 0) { 1948 if (next_event == e) 1949 next_event = NULL; 1950 if (eprev == NULL) 1951 u->atevents = e->link; 1952 else 1953 eprev->link = e->link; 1954 el_remove(e->of.at.eventid, 1); 1955 free(e->cmd); 1956 free(e); 1957 break; 1958 } else { 1959 eprev = e; 1960 e = e->link; 1961 } 1962 } 1963 1964 free_if_unused(u); 1965 } 1966 1967 static void 1968 del_ctab(char *name) 1969 { 1970 1971 struct usr *u; 1972 1973 if ((u = find_usr(name)) == NULL) 1974 return; 1975 rm_ctevents(u); 1976 el_remove(u->ctid, 0); 1977 u->ctid = 0; 1978 u->ctexists = 0; 1979 1980 free_if_unused(u); 1981 } 1982 1983 static void 1984 rm_ctevents(struct usr *u) 1985 { 1986 struct event *e2, *e3; 1987 1988 /* 1989 * see if the next event (to be run by cron) is a cronevent 1990 * owned by this user. 1991 */ 1992 1993 if ((next_event != NULL) && 1994 (next_event->etype == CRONEVENT) && 1995 (next_event->u == u)) { 1996 next_event = NULL; 1997 } 1998 e2 = u->ctevents; 1999 while (e2 != NULL) { 2000 free(e2->cmd); 2001 free(e2->of.ct.minute); 2002 free(e2->of.ct.hour); 2003 free(e2->of.ct.daymon); 2004 free(e2->of.ct.month); 2005 free(e2->of.ct.dayweek); 2006 if (e2->of.ct.input != NULL) 2007 free(e2->of.ct.input); 2008 e3 = e2->link; 2009 free(e2); 2010 e2 = e3; 2011 } 2012 u->ctevents = NULL; 2013 } 2014 2015 2016 static struct usr * 2017 find_usr(char *uname) 2018 { 2019 struct usr *u; 2020 2021 u = uhead; 2022 while (u != NULL) { 2023 if (strcmp(u->name, uname) == 0) 2024 return (u); 2025 u = u->nextusr; 2026 } 2027 return (NULL); 2028 } 2029 2030 /* 2031 * Execute cron command or at/batch job. 2032 * If ever a premature return is added to this function pay attention to 2033 * free at_cmdfile and outfile plus jobname buffers of the runinfo structure. 2034 */ 2035 static int 2036 ex(struct event *e) 2037 { 2038 int r; 2039 int fd; 2040 pid_t rfork; 2041 FILE *atcmdfp; 2042 char mailvar[4]; 2043 char *at_cmdfile = NULL; 2044 struct stat buf; 2045 struct queue *qp; 2046 struct runinfo *rp; 2047 struct project proj, *pproj = NULL; 2048 char mybuf[PROJECT_BUFSZ]; 2049 char mybuf2[PROJECT_BUFSZ]; 2050 char *tmpfile; 2051 FILE *fptr; 2052 time_t dhltime; 2053 projid_t projid; 2054 int projflag = 0; 2055 2056 qp = &qt[e->etype]; /* set pointer to queue defs */ 2057 if (qp->nrun >= qp->njob) { 2058 msg("%c queue max run limit reached", e->etype + 'a'); 2059 resched(qp->nwait); 2060 return (0); 2061 } 2062 rp = rinfo_get(0); /* allocating a new runinfo struct */ 2063 2064 #ifdef ATLIMIT 2065 if ((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) { 2066 msg("ATLIMIT (%d) reached for uid %d", 2067 ATLIMIT, (e->u)->uid); 2068 rinfo_free(rp); 2069 resched(qp->nwait); 2070 return (0); 2071 } 2072 #endif 2073 #ifdef CRONLIMIT 2074 if ((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) { 2075 msg("CRONLIMIT (%d) reached for uid %d", 2076 CRONLIMIT, (e->u)->uid); 2077 rinfo_free(rp); 2078 resched(qp->nwait); 2079 return (0); 2080 } 2081 #endif 2082 if ((e->u)->uid == 0) { /* set default path */ 2083 /* path settable in defaults file */ 2084 envinit[2] = supath; 2085 } else { 2086 envinit[2] = path; 2087 } 2088 2089 /* 2090 * the tempnam() function uses malloc(3C) to allocate space for the 2091 * constructed file name, and returns a pointer to this area, which 2092 * is assigned to rp->outfile. Here rp->outfile is not overwritten. 2093 */ 2094 2095 rp->outfile = tempnam(TMPDIR, PFX); 2096 rp->jobtype = e->etype; 2097 if (e->etype == CRONEVENT) { 2098 rp->jobname = xmalloc(strlen(e->cmd) + 1); 2099 (void) strcpy(rp->jobname, e->cmd); 2100 /* "cron" jobs only produce mail if there's output */ 2101 rp->mailwhendone = 0; 2102 } else { 2103 at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2); 2104 (void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd); 2105 if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) { 2106 if (errno == ENAMETOOLONG) { 2107 if (chdir(ATDIR) == 0) 2108 cron_unlink(e->cmd); 2109 } else { 2110 cron_unlink(at_cmdfile); 2111 } 2112 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT); 2113 free(at_cmdfile); 2114 rinfo_free(rp); 2115 return (0); 2116 } 2117 rp->jobname = xmalloc(strlen(at_cmdfile) + 1); 2118 (void) strcpy(rp->jobname, at_cmdfile); 2119 2120 /* 2121 * Skip over the first two lines. 2122 */ 2123 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2124 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2125 if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n", 2126 mailvar) == 1) { 2127 /* 2128 * Check to see if we should always send mail 2129 * to the owner. 2130 */ 2131 rp->mailwhendone = (strcmp(mailvar, "yes") == 0); 2132 } else { 2133 rp->mailwhendone = 0; 2134 } 2135 2136 if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) { 2137 projflag = 1; 2138 } 2139 (void) fclose(atcmdfp); 2140 } 2141 2142 /* 2143 * we make sure that the system time 2144 * hasn't drifted backwards. if it has, el_add() is now 2145 * called, to make sure that the event queue is back in order, 2146 * and we set the delayed flag. cron will pick up the request 2147 * later on at the proper time. 2148 */ 2149 dhltime = time(NULL); 2150 if ((dhltime - e->time) < 0) { 2151 msg("clock time drifted backwards!\n"); 2152 if (next_event->etype == CRONEVENT) { 2153 msg("correcting cron event\n"); 2154 next_event->time = next_time(next_event, dhltime); 2155 el_add(next_event, next_event->time, 2156 (next_event->u)->ctid); 2157 } else { /* etype == ATEVENT */ 2158 msg("correcting batch event\n"); 2159 el_add(next_event, next_event->time, 2160 next_event->of.at.eventid); 2161 } 2162 delayed++; 2163 t_old = time(NULL); 2164 free(at_cmdfile); 2165 rinfo_free(rp); 2166 return (0); 2167 } 2168 2169 if ((rfork = fork()) == (pid_t)-1) { 2170 reap_child(); 2171 if ((rfork = fork()) == (pid_t)-1) { 2172 msg("cannot fork"); 2173 free(at_cmdfile); 2174 rinfo_free(rp); 2175 resched(60); 2176 (void) sleep(30); 2177 return (0); 2178 } 2179 } 2180 if (rfork) { /* parent process */ 2181 contract_abandon_latest(rfork); 2182 2183 ++qp->nrun; 2184 rp->pid = rfork; 2185 rp->que = e->etype; 2186 if (e->etype != CRONEVENT) 2187 (e->u)->aruncnt++; 2188 else 2189 (e->u)->cruncnt++; 2190 rp->rusr = (e->u); 2191 logit(BCHAR, rp, 0); 2192 free(at_cmdfile); 2193 2194 return (0); 2195 } 2196 2197 child_sigreset(); 2198 contract_clear_template(); 2199 2200 if (e->etype != CRONEVENT) { 2201 /* open jobfile as stdin to shell */ 2202 if (stat(at_cmdfile, &buf)) { 2203 if (errno == ENAMETOOLONG) { 2204 if (chdir(ATDIR) == 0) 2205 cron_unlink(e->cmd); 2206 } else 2207 cron_unlink(at_cmdfile); 2208 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2209 exit(1); 2210 } 2211 if (!(buf.st_mode&ISUID)) { 2212 /* 2213 * if setuid bit off, original owner has 2214 * given this file to someone else 2215 */ 2216 cron_unlink(at_cmdfile); 2217 exit(1); 2218 } 2219 if ((fd = open(at_cmdfile, O_RDONLY)) == -1) { 2220 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2221 cron_unlink(at_cmdfile); 2222 exit(1); 2223 } 2224 if (fd != 0) { 2225 (void) dup2(fd, 0); 2226 (void) close(fd); 2227 } 2228 /* 2229 * retrieve the project id of the at job and convert it 2230 * to a project name. fail if it's not a valid project 2231 * or if the user isn't a member of the project. 2232 */ 2233 if (projflag == 1) { 2234 if ((pproj = getprojbyid(projid, &proj, 2235 (void *)&mybuf, sizeof (mybuf))) == NULL || 2236 !inproj(e->u->name, pproj->pj_name, 2237 mybuf2, sizeof (mybuf2))) { 2238 cron_unlink(at_cmdfile); 2239 mail((e->u)->name, BADPROJID, ERR_CANTEXECAT); 2240 exit(1); 2241 } 2242 } 2243 } 2244 2245 /* 2246 * Put process in a new session, and create a new task. 2247 */ 2248 if (setsid() < 0) { 2249 msg("setsid failed with errno = %d. job failed (%s)" 2250 " for user %s", errno, e->cmd, e->u->name); 2251 if (e->etype != CRONEVENT) 2252 cron_unlink(at_cmdfile); 2253 exit(1); 2254 } 2255 2256 /* 2257 * set correct user identification and check his account 2258 */ 2259 r = set_user_cred(e->u, pproj); 2260 if (r == VUC_EXPIRED) { 2261 msg("user (%s) account is expired", e->u->name); 2262 audit_cron_user_acct_expired(e->u->name); 2263 clean_out_user(e->u); 2264 exit(1); 2265 } 2266 if (r == VUC_NEW_AUTH) { 2267 msg("user (%s) password has expired", e->u->name); 2268 audit_cron_user_acct_expired(e->u->name); 2269 clean_out_user(e->u); 2270 exit(1); 2271 } 2272 if (r != VUC_OK) { 2273 msg("bad user (%s)", e->u->name); 2274 audit_cron_bad_user(e->u->name); 2275 clean_out_user(e->u); 2276 exit(1); 2277 } 2278 /* 2279 * check user and initialize the supplementary group access list. 2280 * bugid 1230784: deleted from parent to avoid cron hang. Now 2281 * only child handles the call. 2282 */ 2283 2284 if (verify_user_cred(e->u) != VUC_OK || 2285 setgid(e->u->gid) == -1 || 2286 initgroups(e->u->name, e->u->gid) == -1) { 2287 msg("bad user (%s) or setgid failed (%s)", 2288 e->u->name, e->u->name); 2289 audit_cron_bad_user(e->u->name); 2290 clean_out_user(e->u); 2291 exit(1); 2292 } 2293 2294 if (e->etype != CRONEVENT) { 2295 r = audit_cron_session(e->u->name, NULL, 2296 e->u->uid, e->u->gid, at_cmdfile); 2297 cron_unlink(at_cmdfile); 2298 } else { 2299 r = audit_cron_session(e->u->name, CRONDIR, 2300 e->u->uid, e->u->gid, NULL); 2301 } 2302 if (r != 0) { 2303 msg("cron audit problem. job failed (%s) for user %s", 2304 e->cmd, e->u->name); 2305 exit(1); 2306 } 2307 2308 audit_cron_new_job(e->cmd, e->etype, (void *)e); 2309 2310 if (setuid(e->u->uid) == -1) { 2311 msg("setuid failed (%s)", e->u->name); 2312 clean_out_user(e->u); 2313 exit(1); 2314 } 2315 2316 if (e->etype == CRONEVENT) { 2317 /* check for standard input to command */ 2318 if (e->of.ct.input != NULL) { 2319 if ((tmpfile = strdup(TMPINFILE)) == NULL) { 2320 mail((e->u)->name, MALLOCERR, 2321 ERR_CANTEXECCRON); 2322 exit(1); 2323 } 2324 if ((fd = mkstemp(tmpfile)) == -1 || 2325 (fptr = fdopen(fd, "w")) == NULL) { 2326 mail((e->u)->name, NOSTDIN, 2327 ERR_CANTEXECCRON); 2328 cron_unlink(tmpfile); 2329 free(tmpfile); 2330 exit(1); 2331 } 2332 if ((fwrite(e->of.ct.input, sizeof (char), 2333 strlen(e->of.ct.input), fptr)) != 2334 strlen(e->of.ct.input)) { 2335 mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON); 2336 cron_unlink(tmpfile); 2337 free(tmpfile); 2338 (void) close(fd); 2339 (void) fclose(fptr); 2340 exit(1); 2341 } 2342 if (fseek(fptr, (off_t)0, SEEK_SET) != -1) { 2343 if (fd != 0) { 2344 (void) dup2(fd, 0); 2345 (void) close(fd); 2346 } 2347 } 2348 cron_unlink(tmpfile); 2349 free(tmpfile); 2350 (void) fclose(fptr); 2351 } else if ((fd = open("/dev/null", O_RDONLY)) > 0) { 2352 (void) dup2(fd, 0); 2353 (void) close(fd); 2354 } 2355 } 2356 2357 /* redirect stdout and stderr for the shell */ 2358 if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1) 2359 fd = open("/dev/null", O_WRONLY); 2360 2361 if (fd >= 0 && fd != 1) 2362 (void) dup2(fd, 1); 2363 2364 if (fd >= 0 && fd != 2) { 2365 (void) dup2(fd, 2); 2366 if (fd != 1) 2367 (void) close(fd); 2368 } 2369 2370 (void) strlcat(homedir, (e->u)->home, sizeof (homedir)); 2371 (void) strlcat(logname, (e->u)->name, sizeof (logname)); 2372 environ = envinit; 2373 if (chdir((e->u)->home) == -1) { 2374 mail((e->u)->name, CANTCDHOME, 2375 e->etype == CRONEVENT ? ERR_CANTEXECCRON : 2376 ERR_CANTEXECAT); 2377 exit(1); 2378 } 2379 #ifdef TESTING 2380 exit(1); 2381 #endif 2382 /* 2383 * make sure that all file descriptors EXCEPT 0, 1 and 2 2384 * will be closed. 2385 */ 2386 closefrom(3); 2387 2388 if ((e->u)->uid != 0) 2389 (void) nice(qp->nice); 2390 if (e->etype == CRONEVENT) 2391 (void) execl(SHELL, "sh", "-c", e->cmd, 0); 2392 else /* type == ATEVENT */ 2393 (void) execl(SHELL, "sh", 0); 2394 mail((e->u)->name, CANTEXECSH, 2395 e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT); 2396 exit(1); 2397 /*NOTREACHED*/ 2398 } 2399 2400 static int 2401 idle(long t) 2402 { 2403 time_t now; 2404 2405 while (t > 0L) { 2406 2407 if (msg_wait(t) != 0) { 2408 /* we need to run next job immediately */ 2409 return (0); 2410 } 2411 2412 reap_child(); 2413 2414 now = time(NULL); 2415 if (last_time > now) { 2416 /* clock has been reset */ 2417 return (1); 2418 } 2419 2420 if (next_event == NULL && !el_empty()) { 2421 next_event = (struct event *)el_first(); 2422 } 2423 if (next_event == NULL) 2424 t = INFINITY; 2425 else 2426 t = (long)next_event->time - now; 2427 } 2428 return (0); 2429 } 2430 2431 /* 2432 * This used to be in the idle(), but moved to the separate function. 2433 * This called from various place when cron needs to reap the 2434 * child. It includes the situation that cron hit maxrun, and needs 2435 * to reschedule the job. 2436 */ 2437 static void 2438 reap_child() 2439 { 2440 pid_t pid; 2441 int prc; 2442 struct runinfo *rp; 2443 2444 for (;;) { 2445 pid = waitpid((pid_t)-1, &prc, WNOHANG); 2446 if (pid <= 0) 2447 break; 2448 #ifdef DEBUG 2449 fprintf(stderr, 2450 "wait returned %x for process %d\n", prc, pid); 2451 #endif 2452 if ((rp = rinfo_get(pid)) == NULL) { 2453 if (miscpid_delete(pid) == 0) { 2454 /* not found in anywhere */ 2455 msg(PIDERR, pid); 2456 } 2457 } else if (rp->que == ZOMB) { 2458 (void) unlink(rp->outfile); 2459 rinfo_free(rp); 2460 } else { 2461 cleanup(rp, prc); 2462 } 2463 } 2464 } 2465 2466 static void 2467 cleanup(struct runinfo *pr, int rc) 2468 { 2469 int nextfork = 1; 2470 struct usr *p; 2471 struct stat buf; 2472 2473 logit(ECHAR, pr, rc); 2474 --qt[pr->que].nrun; 2475 p = pr->rusr; 2476 if (pr->que != CRONEVENT) 2477 --p->aruncnt; 2478 else 2479 --p->cruncnt; 2480 2481 if (lstat(pr->outfile, &buf) == 0) { 2482 if (!S_ISLNK(buf.st_mode) && 2483 (buf.st_size > 0 || pr->mailwhendone)) { 2484 /* mail user stdout and stderr */ 2485 for (;;) { 2486 if ((pr->pid = fork()) < 0) { 2487 /* 2488 * if fork fails try forever in doubling 2489 * retry times, up to 16 seconds 2490 */ 2491 (void) sleep(nextfork); 2492 if (nextfork < 16) 2493 nextfork += nextfork; 2494 continue; 2495 } else if (pr->pid == 0) { 2496 child_sigreset(); 2497 contract_clear_template(); 2498 2499 mail_result(p, pr, buf.st_size); 2500 /* NOTREACHED */ 2501 } else { 2502 contract_abandon_latest(pr->pid); 2503 pr->que = ZOMB; 2504 break; 2505 } 2506 } 2507 } else { 2508 (void) unlink(pr->outfile); 2509 rinfo_free(pr); 2510 } 2511 } else { 2512 rinfo_free(pr); 2513 } 2514 2515 free_if_unused(p); 2516 } 2517 2518 /* 2519 * Mail stdout and stderr of a job to user. Get uid for real user and become 2520 * that person. We do this so that mail won't come from root since this 2521 * could be a security hole. If failure, quit - don't send mail as root. 2522 */ 2523 static void 2524 mail_result(struct usr *p, struct runinfo *pr, size_t filesize) 2525 { 2526 struct passwd *ruser_ids; 2527 FILE *mailpipe; 2528 FILE *st; 2529 struct utsname name; 2530 int nbytes; 2531 char iobuf[BUFSIZ]; 2532 char *cmd; 2533 2534 (void) uname(&name); 2535 if ((ruser_ids = getpwnam(p->name)) == NULL) 2536 exit(0); 2537 (void) setuid(ruser_ids->pw_uid); 2538 2539 cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2); 2540 (void) sprintf(cmd, "%s %s", MAIL, p->name); 2541 mailpipe = popen(cmd, "w"); 2542 free(cmd); 2543 if (mailpipe == NULL) 2544 exit(127); 2545 (void) fprintf(mailpipe, "To: %s\n", p->name); 2546 if (pr->jobtype == CRONEVENT) { 2547 (void) fprintf(mailpipe, CRONOUT); 2548 (void) fprintf(mailpipe, "Your \"cron\" job on %s\n", 2549 name.nodename); 2550 if (pr->jobname != NULL) { 2551 (void) fprintf(mailpipe, "%s\n\n", pr->jobname); 2552 } 2553 } else { 2554 (void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n"); 2555 (void) fprintf(mailpipe, "Your \"at\" job on %s\n", 2556 name.nodename); 2557 if (pr->jobname != NULL) { 2558 (void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname); 2559 } 2560 } 2561 /* Tmp. file is fopen'ed w/ "r", secure open */ 2562 if (filesize > 0 && 2563 (st = fopen(pr->outfile, "r")) != NULL) { 2564 (void) fprintf(mailpipe, 2565 "produced the following output:\n\n"); 2566 while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0) 2567 (void) fwrite(iobuf, sizeof (char), nbytes, mailpipe); 2568 (void) fclose(st); 2569 } else { 2570 (void) fprintf(mailpipe, "completed.\n"); 2571 } 2572 (void) pclose(mailpipe); 2573 exit(0); 2574 } 2575 2576 static int 2577 msg_wait(long tim) 2578 { 2579 struct message msg; 2580 int cnt; 2581 time_t reftime; 2582 struct pollfd pfd[2]; 2583 int64_t tl; 2584 int timeout; 2585 static int pending_msg; 2586 static time_t pending_reftime; 2587 2588 if (pending_msg) { 2589 process_msg(&msgbuf, pending_reftime); 2590 pending_msg = 0; 2591 return (0); 2592 } 2593 2594 /* 2595 * We are opening the signal mask to receive SIGCLD. The notifypipe 2596 * is used to avoid race condition between SIGCLD and poll system 2597 * call. 2598 * If SIGCLD is delivered in poll(), poll will be interrupted, and 2599 * we will return to idle() to reap the dead children. 2600 * If SIGCLD is delivered between sigprocmask() below and poll(), 2601 * there is no way we can detect the SIGCLD because poll() won't 2602 * be interrupted. In such case, the dead children can't be wait'ed 2603 * until poll returns by timeout or a new job. To avoid this race 2604 * condition, child_handler write to the notifypipe, so that 2605 * poll() will be able to return with POLLIN which indicates that 2606 * we have received SIGCLD. 2607 * 2608 * Since the notifypipe is used to just let poll return from 2609 * system call, the data in the pipe won't be read. Therefore, 2610 * any data in the pipe needs to be flushed before opening signal 2611 * mask. 2612 * 2613 * Note that we can probably re-write this code with pselect() 2614 * which can handle this situation easily. 2615 */ 2616 (void) ioctl(notifypipe[0], I_FLUSH, FLUSHW); 2617 2618 pfd[0].fd = msgfd; 2619 pfd[0].events = POLLIN; 2620 pfd[1].fd = notifypipe[1]; 2621 pfd[1].events = POLLIN; 2622 2623 #ifdef CRON_MAXSLEEP 2624 /* 2625 * CRON_MAXSLEEP can be defined to have cron periodically wake 2626 * up, so that cron can detect a change of TOD and adjust the 2627 * sleep time accordingly. 2628 */ 2629 tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim; 2630 #endif 2631 tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000; 2632 2633 accept_sigcld = 1; 2634 (void) sigprocmask(SIG_UNBLOCK, &childmask, NULL); 2635 do { 2636 timeout = (tl > INT_MAX ? INT_MAX : (int)tl); 2637 tl -= timeout; 2638 cnt = poll(pfd, 2, timeout); 2639 if (cnt == -1 && errno != EINTR) { 2640 perror("! poll"); 2641 } 2642 } while (tl > 0 && cnt == 0); 2643 (void) sigprocmask(SIG_BLOCK, &childmask, NULL); 2644 accept_sigcld = 0; 2645 2646 /* 2647 * poll timeout or interrupted. 2648 */ 2649 if (cnt <= 0) 2650 return (0); 2651 2652 /* 2653 * Not the timeout or new job, but a SIGCLD has been delivered. 2654 */ 2655 if ((pfd[0].revents & POLLIN) == 0) 2656 return (0); 2657 2658 errno = 0; 2659 if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) { 2660 if (cnt != -1 || errno != EAGAIN) 2661 perror("! read"); 2662 return (0); 2663 } 2664 reftime = time(NULL); 2665 if (next_event != NULL && reftime >= next_event->time) { 2666 /* 2667 * we need to run the job before reloading crontab. 2668 */ 2669 (void) memcpy(&msgbuf, &msg, sizeof (msg)); 2670 pending_msg = 1; 2671 pending_reftime = reftime; 2672 return (1); 2673 } 2674 process_msg(&msg, reftime); 2675 return (0); 2676 } 2677 2678 /* 2679 * process the message supplied via pipe. This will be called either 2680 * immediately after cron read the message from pipe, or idle time 2681 * if the message was pending due to the job execution. 2682 */ 2683 static void 2684 process_msg(struct message *pmsg, time_t reftime) 2685 { 2686 if (pmsg->etype == NULL) 2687 return; 2688 2689 switch (pmsg->etype) { 2690 case AT: 2691 if (pmsg->action == DELETE) 2692 del_atjob(pmsg->fname, pmsg->logname); 2693 else 2694 mod_atjob(pmsg->fname, (time_t)0); 2695 break; 2696 case CRON: 2697 if (pmsg->action == DELETE) 2698 del_ctab(pmsg->fname); 2699 else 2700 mod_ctab(pmsg->fname, reftime); 2701 break; 2702 default: 2703 msg("message received - bad format"); 2704 break; 2705 } 2706 if (next_event != NULL) { 2707 if (next_event->etype == CRONEVENT) 2708 el_add(next_event, next_event->time, 2709 (next_event->u)->ctid); 2710 else /* etype == ATEVENT */ 2711 el_add(next_event, next_event->time, 2712 next_event->of.at.eventid); 2713 next_event = NULL; 2714 } 2715 (void) fflush(stdout); 2716 pmsg->etype = NULL; 2717 } 2718 2719 /* 2720 * Allocate a new or find an existing runinfo structure 2721 */ 2722 static struct runinfo * 2723 rinfo_get(pid_t pid) 2724 { 2725 struct runinfo *rp; 2726 2727 if (pid == 0) { /* allocate a new entry */ 2728 rp = xcalloc(1, sizeof (struct runinfo)); 2729 rp->next = rthead; /* link the entry into the list */ 2730 rthead = rp; 2731 return (rp); 2732 } 2733 /* search the list for an existing entry */ 2734 for (rp = rthead; rp != NULL; rp = rp->next) { 2735 if (rp->pid == pid) 2736 break; 2737 } 2738 return (rp); 2739 } 2740 2741 /* 2742 * Free a runinfo structure and its associated memory 2743 */ 2744 static void 2745 rinfo_free(struct runinfo *entry) 2746 { 2747 struct runinfo **rpp; 2748 struct runinfo *rp; 2749 2750 #ifdef DEBUG 2751 (void) fprintf(stderr, "freeing job %s\n", entry->jobname); 2752 #endif 2753 for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) { 2754 if (rp == entry) { 2755 *rpp = rp->next; /* unlink the entry */ 2756 free(rp->outfile); 2757 free(rp->jobname); 2758 free(rp); 2759 break; 2760 } 2761 } 2762 } 2763 2764 /* ARGSUSED */ 2765 static void 2766 thaw_handler(int sig) 2767 { 2768 ; 2769 } 2770 2771 2772 /* ARGSUSED */ 2773 static void 2774 cronend(int sig) 2775 { 2776 crabort("SIGTERM", REMOVE_FIFO); 2777 } 2778 2779 /*ARGSUSED*/ 2780 static void 2781 child_handler(int sig) 2782 { 2783 /* 2784 * Just in case someone changes the signal mask. 2785 * we don't want to notify the SIGCLD. 2786 */ 2787 if (accept_sigcld) { 2788 (void) write(notifypipe[0], &sig, 1); 2789 } 2790 } 2791 2792 static void 2793 child_sigreset(void) 2794 { 2795 (void) signal(SIGCLD, SIG_DFL); 2796 (void) sigprocmask(SIG_SETMASK, &defmask, NULL); 2797 } 2798 2799 /* 2800 * crabort() - handle exits out of cron 2801 */ 2802 static void 2803 crabort(char *mssg, int action) 2804 { 2805 int c; 2806 2807 if (action & REMOVE_FIFO) { 2808 /* FIFO vanishes when cron finishes */ 2809 if (unlink(FIFO) < 0) 2810 perror("cron could not unlink FIFO"); 2811 } 2812 2813 if (action & CONSOLE_MSG) { 2814 /* write error msg to console */ 2815 if ((c = open(CONSOLE, O_WRONLY)) >= 0) { 2816 (void) write(c, "cron aborted: ", 14); 2817 (void) write(c, mssg, strlen(mssg)); 2818 (void) write(c, "\n", 1); 2819 (void) close(c); 2820 } 2821 } 2822 2823 /* always log the message */ 2824 msg(mssg); 2825 msg("******* CRON ABORTED ********"); 2826 exit(1); 2827 } 2828 2829 /* 2830 * msg() - time-stamped error reporting function 2831 */ 2832 /*PRINTFLIKE1*/ 2833 static void 2834 msg(char *fmt, ...) 2835 { 2836 va_list args; 2837 time_t t; 2838 2839 t = time(NULL); 2840 2841 (void) fflush(stdout); 2842 2843 (void) fprintf(stderr, "! "); 2844 2845 va_start(args, fmt); 2846 (void) vfprintf(stderr, fmt, args); 2847 va_end(args); 2848 2849 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2850 (void) fprintf(stderr, " %s\n", timebuf); 2851 2852 (void) fflush(stderr); 2853 } 2854 2855 static void 2856 logit(int cc, struct runinfo *rp, int rc) 2857 { 2858 time_t t; 2859 int ret; 2860 2861 if (!log) 2862 return; 2863 2864 t = time(NULL); 2865 if (cc == BCHAR) 2866 (void) printf("%c CMD: %s\n", cc, next_event->cmd); 2867 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2868 (void) printf("%c %.8s %u %c %s", 2869 cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf); 2870 if ((ret = TSTAT(rc)) != 0) 2871 (void) printf(" ts=%d", ret); 2872 if ((ret = RCODE(rc)) != 0) 2873 (void) printf(" rc=%d", ret); 2874 (void) putchar('\n'); 2875 (void) fflush(stdout); 2876 } 2877 2878 static void 2879 resched(int delay) 2880 { 2881 time_t nt; 2882 2883 /* run job at a later time */ 2884 nt = next_event->time + delay; 2885 if (next_event->etype == CRONEVENT) { 2886 next_event->time = next_time(next_event, (time_t)0); 2887 if (nt < next_event->time) 2888 next_event->time = nt; 2889 el_add(next_event, next_event->time, (next_event->u)->ctid); 2890 delayed = 1; 2891 msg("rescheduling a cron job"); 2892 return; 2893 } 2894 add_atevent(next_event->u, next_event->cmd, nt, next_event->etype); 2895 msg("rescheduling at job"); 2896 } 2897 2898 static void 2899 quedefs(int action) 2900 { 2901 int i; 2902 int j; 2903 char qbuf[QBUFSIZ]; 2904 FILE *fd; 2905 2906 /* set up default queue definitions */ 2907 for (i = 0; i < NQUEUE; i++) { 2908 qt[i].njob = qd.njob; 2909 qt[i].nice = qd.nice; 2910 qt[i].nwait = qd.nwait; 2911 } 2912 if (action == DEFAULT) 2913 return; 2914 if ((fd = fopen(QUEDEFS, "r")) == NULL) { 2915 msg("cannot open quedefs file"); 2916 msg("using default queue definitions"); 2917 return; 2918 } 2919 while (fgets(qbuf, QBUFSIZ, fd) != NULL) { 2920 if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.') 2921 continue; 2922 parsqdef(&qbuf[2]); 2923 qt[j].njob = qq.njob; 2924 qt[j].nice = qq.nice; 2925 qt[j].nwait = qq.nwait; 2926 } 2927 (void) fclose(fd); 2928 } 2929 2930 static void 2931 parsqdef(char *name) 2932 { 2933 int i; 2934 2935 qq = qd; 2936 while (*name) { 2937 i = 0; 2938 while (isdigit(*name)) { 2939 i *= 10; 2940 i += *name++ - '0'; 2941 } 2942 switch (*name++) { 2943 case JOBF: 2944 qq.njob = i; 2945 break; 2946 case NICEF: 2947 qq.nice = i; 2948 break; 2949 case WAITF: 2950 qq.nwait = i; 2951 break; 2952 } 2953 } 2954 } 2955 2956 /* 2957 * defaults - read defaults from /etc/default/cron 2958 */ 2959 static void 2960 defaults() 2961 { 2962 int flags; 2963 char *deflog; 2964 char *hz, *tz; 2965 2966 /* 2967 * get HZ value for environment 2968 */ 2969 if ((hz = getenv("HZ")) == (char *)NULL) 2970 (void) sprintf(hzname, "HZ=%d", HZ); 2971 else 2972 (void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz); 2973 /* 2974 * get TZ value for environment 2975 */ 2976 (void) snprintf(tzone, sizeof (tzone), "TZ=%s", 2977 ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ); 2978 2979 if (defopen(DEFFILE) == 0) { 2980 /* ignore case */ 2981 flags = defcntl(DC_GETFLAGS, 0); 2982 TURNOFF(flags, DC_CASE); 2983 (void) defcntl(DC_SETFLAGS, flags); 2984 2985 if (((deflog = defread("CRONLOG=")) == NULL) || 2986 (*deflog == 'N') || (*deflog == 'n')) 2987 log = 0; 2988 else 2989 log = 1; 2990 /* fix for 1087611 - allow paths to be set in defaults file */ 2991 if ((Def_path = defread("PATH=")) != NULL) { 2992 (void) strlcat(path, Def_path, LINE_MAX); 2993 } else { 2994 (void) strlcpy(path, NONROOTPATH, LINE_MAX); 2995 } 2996 if ((Def_supath = defread("SUPATH=")) != NULL) { 2997 (void) strlcat(supath, Def_supath, LINE_MAX); 2998 } else { 2999 (void) strlcpy(supath, ROOTPATH, LINE_MAX); 3000 } 3001 (void) defopen(NULL); 3002 } 3003 } 3004 3005 /* 3006 * Determine if a user entry for a job is still ok. The method used here 3007 * is a lot (about 75x) faster than using setgrent() / getgrent() 3008 * endgrent(). It should be safe because we use the sysconf to determine 3009 * the max, and it tolerates the max being 0. 3010 */ 3011 3012 static int 3013 verify_user_cred(struct usr *u) 3014 { 3015 struct passwd *pw; 3016 size_t numUsrGrps = 0; 3017 size_t numOrigGrps = 0; 3018 size_t i; 3019 int retval; 3020 3021 /* 3022 * Maximum number of groups a user may be in concurrently. This 3023 * is a value which we obtain at runtime through a sysconf() 3024 * call. 3025 */ 3026 3027 static size_t nGroupsMax = (size_t)-1; 3028 3029 /* 3030 * Arrays for cron user's group list, constructed at startup to 3031 * be nGroupsMax elements long, used for verifying user 3032 * credentials prior to execution. 3033 */ 3034 3035 static gid_t *UsrGrps; 3036 static gid_t *OrigGrps; 3037 3038 if ((pw = getpwnam(u->name)) == NULL) 3039 return (VUC_BADUSER); 3040 if (u->home != NULL) { 3041 if (strcmp(u->home, pw->pw_dir) != 0) { 3042 free(u->home); 3043 u->home = xmalloc(strlen(pw->pw_dir) + 1); 3044 (void) strcpy(u->home, pw->pw_dir); 3045 } 3046 } else { 3047 u->home = xmalloc(strlen(pw->pw_dir) + 1); 3048 (void) strcpy(u->home, pw->pw_dir); 3049 } 3050 if (u->uid != pw->pw_uid) 3051 u->uid = pw->pw_uid; 3052 if (u->gid != pw->pw_gid) 3053 u->gid = pw->pw_gid; 3054 3055 /* 3056 * Create the group id lists needed for job credential 3057 * verification. 3058 */ 3059 3060 if (nGroupsMax == (size_t)-1) { 3061 if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) { 3062 UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 3063 OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 3064 } 3065 3066 #ifdef DEBUG 3067 (void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax); 3068 #endif 3069 } 3070 3071 #ifdef DEBUG 3072 (void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name, 3073 pw->pw_uid); 3074 (void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, " 3075 "u->gid = %d\n", pw->pw_gid, u->gid); 3076 #endif 3077 3078 retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP; 3079 3080 if (nGroupsMax > 0) { 3081 numOrigGrps = getgroups(nGroupsMax, OrigGrps); 3082 3083 (void) initgroups(pw->pw_name, pw->pw_gid); 3084 numUsrGrps = getgroups(nGroupsMax, UsrGrps); 3085 3086 for (i = 0; i < numUsrGrps; i++) { 3087 if (UsrGrps[i] == u->gid) { 3088 retval = VUC_OK; 3089 break; 3090 } 3091 } 3092 3093 if (OrigGrps) { 3094 (void) setgroups(numOrigGrps, OrigGrps); 3095 } 3096 } 3097 3098 #ifdef DEBUG 3099 (void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval); 3100 #endif 3101 3102 return (retval); 3103 } 3104 3105 static int 3106 set_user_cred(const struct usr *u, struct project *pproj) 3107 { 3108 static char *progname = "cron"; 3109 int r = 0, rval = 0; 3110 3111 if ((r = pam_start(progname, u->name, &pam_conv, &pamh)) 3112 != PAM_SUCCESS) { 3113 #ifdef DEBUG 3114 msg("pam_start returns %d\n", r); 3115 #endif 3116 rval = VUC_BADUSER; 3117 goto set_eser_cred_exit; 3118 } 3119 3120 r = pam_acct_mgmt(pamh, 0); 3121 #ifdef DEBUG 3122 msg("pam_acc_mgmt returns %d\n", r); 3123 #endif 3124 if (r == PAM_ACCT_EXPIRED) { 3125 rval = VUC_EXPIRED; 3126 goto set_eser_cred_exit; 3127 } 3128 if (r == PAM_NEW_AUTHTOK_REQD) { 3129 rval = VUC_NEW_AUTH; 3130 goto set_eser_cred_exit; 3131 } 3132 if (r != PAM_SUCCESS) { 3133 rval = VUC_BADUSER; 3134 goto set_eser_cred_exit; 3135 } 3136 3137 if (pproj != NULL) { 3138 size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name); 3139 char *buf = alloca(sz); 3140 3141 (void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name); 3142 (void) pam_set_item(pamh, PAM_RESOURCE, buf); 3143 } 3144 3145 r = pam_setcred(pamh, PAM_ESTABLISH_CRED); 3146 if (r != PAM_SUCCESS) 3147 rval = VUC_BADUSER; 3148 3149 set_eser_cred_exit: 3150 (void) pam_end(pamh, r); 3151 return (rval); 3152 } 3153 3154 static void 3155 clean_out_user(struct usr *u) 3156 { 3157 if (next_event->u == u) { 3158 next_event = NULL; 3159 } 3160 3161 clean_out_ctab(u); 3162 clean_out_atjobs(u); 3163 free_if_unused(u); 3164 } 3165 3166 static void 3167 clean_out_atjobs(struct usr *u) 3168 { 3169 struct event *ev, *pv; 3170 3171 for (pv = NULL, ev = u->atevents; 3172 ev != NULL; 3173 pv = ev, ev = ev->link, free(pv)) { 3174 el_remove(ev->of.at.eventid, 1); 3175 if (cwd == AT) 3176 cron_unlink(ev->cmd); 3177 else { 3178 char buf[PATH_MAX]; 3179 if (strlen(ATDIR) + strlen(ev->cmd) + 2 3180 < PATH_MAX) { 3181 (void) sprintf(buf, "%s/%s", ATDIR, ev->cmd); 3182 cron_unlink(buf); 3183 } 3184 } 3185 free(ev->cmd); 3186 } 3187 3188 u->atevents = NULL; 3189 } 3190 3191 static void 3192 clean_out_ctab(struct usr *u) 3193 { 3194 rm_ctevents(u); 3195 el_remove(u->ctid, 0); 3196 u->ctid = 0; 3197 u->ctexists = 0; 3198 } 3199 3200 static void 3201 cron_unlink(char *name) 3202 { 3203 int r; 3204 3205 r = unlink(name); 3206 if (r == 0 || (r == -1 && errno == ENOENT)) { 3207 (void) audit_cron_delete_anc_file(name, NULL); 3208 } 3209 } 3210 3211 static void 3212 create_anc_ctab(struct event *e) 3213 { 3214 if (audit_cron_create_anc_file(e->u->name, 3215 (cwd == CRON) ? NULL:CRONDIR, 3216 e->u->name, e->u->uid) == -1) { 3217 process_anc_files(CRON_ANC_DELETE); 3218 crabort("cannot create ancillary files for crontabs", 3219 REMOVE_FIFO|CONSOLE_MSG); 3220 } 3221 } 3222 3223 static void 3224 delete_anc_ctab(struct event *e) 3225 { 3226 (void) audit_cron_delete_anc_file(e->u->name, 3227 (cwd == CRON) ? NULL:CRONDIR); 3228 } 3229 3230 static void 3231 create_anc_atjob(struct event *e) 3232 { 3233 if (!e->of.at.exists) 3234 return; 3235 3236 if (audit_cron_create_anc_file(e->cmd, 3237 (cwd == AT) ? NULL:ATDIR, 3238 e->u->name, e->u->uid) == -1) { 3239 process_anc_files(CRON_ANC_DELETE); 3240 crabort("cannot create ancillary files for atjobs", 3241 REMOVE_FIFO|CONSOLE_MSG); 3242 } 3243 } 3244 3245 static void 3246 delete_anc_atjob(struct event *e) 3247 { 3248 if (!e->of.at.exists) 3249 return; 3250 3251 (void) audit_cron_delete_anc_file(e->cmd, 3252 (cwd == AT) ? NULL:ATDIR); 3253 } 3254 3255 3256 static void 3257 process_anc_files(int del) 3258 { 3259 struct usr *u = uhead; 3260 struct event *e; 3261 3262 if (!audit_cron_mode()) 3263 return; 3264 3265 for (;;) { 3266 if (u->ctexists && u->ctevents != NULL) { 3267 e = u->ctevents; 3268 for (;;) { 3269 if (del) 3270 delete_anc_ctab(e); 3271 else 3272 create_anc_ctab(e); 3273 if ((e = e->link) == NULL) 3274 break; 3275 } 3276 } 3277 3278 if (u->atevents != NULL) { 3279 e = u->atevents; 3280 for (;;) { 3281 if (del) 3282 delete_anc_atjob(e); 3283 else 3284 create_anc_atjob(e); 3285 if ((e = e->link) == NULL) 3286 break; 3287 } 3288 } 3289 3290 if ((u = u->nextusr) == NULL) 3291 break; 3292 } 3293 } 3294 3295 /*ARGSUSED*/ 3296 static int 3297 cron_conv(int num_msg, struct pam_message **msgs, 3298 struct pam_response **response, void *appdata_ptr) 3299 { 3300 struct pam_message **m = msgs; 3301 int i; 3302 3303 for (i = 0; i < num_msg; i++) { 3304 switch (m[i]->msg_style) { 3305 case PAM_ERROR_MSG: 3306 case PAM_TEXT_INFO: 3307 if (m[i]->msg != NULL) { 3308 (void) msg("%s\n", m[i]->msg); 3309 } 3310 break; 3311 3312 default: 3313 break; 3314 } 3315 } 3316 return (0); 3317 } 3318 3319 /* 3320 * Cron creates process for other than job. Mail process is the 3321 * one which rinfo does not cover. Therefore, miscpid will keep 3322 * track of the pids executed from cron. Otherwise, we will see 3323 * "unexpected pid returned.." messages appear in the log file. 3324 */ 3325 static void 3326 miscpid_insert(pid_t pid) 3327 { 3328 struct miscpid *mp; 3329 3330 mp = xmalloc(sizeof (*mp)); 3331 mp->pid = pid; 3332 mp->next = miscpid_head; 3333 miscpid_head = mp; 3334 } 3335 3336 static int 3337 miscpid_delete(pid_t pid) 3338 { 3339 struct miscpid *mp, *omp; 3340 int found = 0; 3341 3342 omp = NULL; 3343 for (mp = miscpid_head; mp != NULL; mp = mp->next) { 3344 if (mp->pid == pid) { 3345 found = 1; 3346 break; 3347 } 3348 omp = mp; 3349 } 3350 if (found) { 3351 if (omp != NULL) 3352 omp->next = mp->next; 3353 else 3354 miscpid_head = NULL; 3355 free(mp); 3356 } 3357 return (found); 3358 } 3359 3360 /* 3361 * Establish contract terms such that all children are in abandoned 3362 * process contracts. 3363 */ 3364 static void 3365 contract_set_template(void) 3366 { 3367 int fd; 3368 3369 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3370 crabort("cannot open process contract template", 3371 REMOVE_FIFO | CONSOLE_MSG); 3372 3373 if (ct_pr_tmpl_set_param(fd, 0) || 3374 ct_tmpl_set_informative(fd, 0) || 3375 ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR)) 3376 crabort("cannot establish contract template terms", 3377 REMOVE_FIFO | CONSOLE_MSG); 3378 3379 if (ct_tmpl_activate(fd)) 3380 crabort("cannot activate contract template", 3381 REMOVE_FIFO | CONSOLE_MSG); 3382 3383 (void) close(fd); 3384 } 3385 3386 /* 3387 * Clear active process contract template. 3388 */ 3389 static void 3390 contract_clear_template(void) 3391 { 3392 int fd; 3393 3394 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3395 crabort("cannot open process contract template", 3396 REMOVE_FIFO | CONSOLE_MSG); 3397 3398 if (ct_tmpl_clear(fd)) 3399 crabort("cannot clear contract template", 3400 REMOVE_FIFO | CONSOLE_MSG); 3401 3402 (void) close(fd); 3403 } 3404 3405 /* 3406 * Abandon latest process contract unconditionally. If we have leaked [some 3407 * critical amount], exit such that the kernel reaps our contracts. 3408 */ 3409 static void 3410 contract_abandon_latest(pid_t pid) 3411 { 3412 int r; 3413 ctid_t id; 3414 static uint_t cts_lost; 3415 3416 if (cts_lost > MAX_LOST_CONTRACTS) 3417 crabort("repeated failure to abandon contracts", 3418 REMOVE_FIFO | CONSOLE_MSG); 3419 3420 if (r = contract_latest(&id)) { 3421 msg("could not obtain latest contract for " 3422 "PID %ld: %s", pid, strerror(r)); 3423 cts_lost++; 3424 return; 3425 } 3426 3427 if (r = contract_abandon_id(id)) { 3428 msg("could not abandon latest contract %ld: %s", id, 3429 strerror(r)); 3430 cts_lost++; 3431 return; 3432 } 3433 } 3434