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