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