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 ZOMB 100 /* proc slot used for mailing output */ 97 98 #define JOBF 'j' 99 #define NICEF 'n' 100 #define USERF 'u' 101 #define WAITF 'w' 102 103 #define BCHAR '>' 104 #define ECHAR '<' 105 106 #define DEFAULT 0 107 #define LOAD 1 108 #define QBUFSIZ 80 109 110 /* Defined actions for crabort() routine */ 111 #define NO_ACTION 000 112 #define REMOVE_FIFO 001 113 #define CONSOLE_MSG 002 114 115 #define BADCD "can't change directory to the crontab directory." 116 #define NOREADDIR "can't read the crontab directory." 117 118 #define BADJOBOPEN "unable to read your at job." 119 #define BADSHELL "because your login shell \ 120 isn't /usr/bin/sh, you can't use cron." 121 122 #define BADSTAT "can't access your crontab file. Resubmit it." 123 #define BADPROJID "can't set project id for your job." 124 #define CANTCDHOME "can't change directory to your home directory.\ 125 \nYour commands will not be executed." 126 #define CANTEXECSH "unable to exec the shell for one of your commands." 127 #define NOREAD "can't read your crontab file. Resubmit it." 128 #define BADTYPE "crontab is not a regular file.\n" 129 #define NOSTDIN "unable to create a standard input file for \ 130 one of your crontab commands. \ 131 \nThat command was not executed." 132 133 #define NOTALLOWED "you are not authorized to use cron. Sorry." 134 #define STDERRMSG "\n\n********************************************\ 135 *****\nCron: The previous message is the \ 136 standard output and standard error \ 137 \nof one of your cron commands.\n" 138 139 #define STDOUTERR "one of your commands generated output or errors, \ 140 but cron was unable to mail you this output.\ 141 \nRemember to redirect standard output and standard \ 142 error for each of your commands." 143 144 #define CLOCK_DRIFT "clock time drifted backwards after event!\n" 145 #define PIDERR "unexpected pid returned %d (ignored)" 146 #define CRONTABERR "Subject: Your crontab file has an error in it\n\n" 147 #define CRONOUT "Subject: Output from \"cron\" command\n\n" 148 #define MALLOCERR "out of space, cannot create new string\n" 149 150 #define DIDFORK didfork 151 #define NOFORK !didfork 152 153 #define MAILBUFLEN (8*1024) 154 #define LINELIMIT 80 155 #define MAILBINITFREE (MAILBUFLEN - (sizeof (cte_intro) - 1) \ 156 - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1) 157 158 #define ERR_CRONTABENT 0 /* error in crontab file entry */ 159 #define ERR_UNIXERR 1 /* error in some system call */ 160 #define ERR_CANTEXECCRON 2 /* error setting up "cron" job environment */ 161 #define ERR_CANTEXECAT 3 /* error setting up "at" job environment */ 162 163 #define PROJECT "project=" 164 165 #define MAX_LOST_CONTRACTS 2048 /* reset if this many failed abandons */ 166 167 #define FORMAT "%a %b %e %H:%M:%S %Y" 168 static char timebuf[80]; 169 170 struct event { 171 time_t time; /* time of the event */ 172 short etype; /* what type of event; 0=cron, 1=at */ 173 char *cmd; /* command for cron, job name for at */ 174 struct usr *u; /* ptr to the owner (usr) of this event */ 175 struct event *link; /* ptr to another event for this user */ 176 union { 177 struct { /* for crontab events */ 178 char *minute; /* (these */ 179 char *hour; /* fields */ 180 char *daymon; /* are */ 181 char *month; /* from */ 182 char *dayweek; /* crontab) */ 183 char *input; /* ptr to stdin */ 184 } ct; 185 struct { /* for at events */ 186 short exists; /* for revising at events */ 187 int eventid; /* for el_remove-ing at events */ 188 } at; 189 } of; 190 }; 191 192 struct usr { 193 char *name; /* name of user (e.g. "root") */ 194 char *home; /* home directory for user */ 195 uid_t uid; /* user id */ 196 gid_t gid; /* group id */ 197 int aruncnt; /* counter for running jobs per uid */ 198 int cruncnt; /* counter for running cron jobs per uid */ 199 int ctid; /* for el_remove-ing crontab events */ 200 short ctexists; /* for revising crontab events */ 201 struct event *ctevents; /* list of this usr's crontab events */ 202 struct event *atevents; /* list of this usr's at events */ 203 struct usr *nextusr; 204 }; /* ptr to next user */ 205 206 static struct queue 207 { 208 int njob; /* limit */ 209 int nice; /* nice for execution */ 210 int nwait; /* wait time to next execution attempt */ 211 int nrun; /* number running */ 212 } 213 qd = {100, 2, 60}, /* default values for queue defs */ 214 qt[NQUEUE]; 215 static struct queue qq; 216 217 static struct runinfo 218 { 219 pid_t pid; 220 short que; 221 struct usr *rusr; /* pointer to usr struct */ 222 char *outfile; /* file where stdout & stderr are trapped */ 223 short jobtype; /* what type of event: 0=cron, 1=at */ 224 char *jobname; /* command for "cron", jobname for "at" */ 225 int mailwhendone; /* 1 = send mail even if no ouptut */ 226 struct runinfo *next; 227 } *rthead; 228 229 static struct miscpid { 230 pid_t pid; 231 struct miscpid *next; 232 } *miscpid_head; 233 234 static pid_t cron_pid; /* own pid */ 235 static char didfork = 0; /* flag to see if I'm process group leader */ 236 static int msgfd; /* file descriptor for fifo queue */ 237 static int ecid = 1; /* event class id for el_remove(); MUST be set to 1 */ 238 static int delayed; /* is job being rescheduled or did it run first time */ 239 static int cwd; /* current working directory */ 240 static struct event *next_event; /* the next event to execute */ 241 static struct usr *uhead; /* ptr to the list of users */ 242 243 /* Variables for error handling at reading crontabs. */ 244 static char cte_intro[] = "Line(s) with errors:\n\n"; 245 static char cte_trail1[] = "\nMax number of errors encountered."; 246 static char cte_trail2[] = " Evaluation of crontab aborted.\n"; 247 static int cte_free = MAILBINITFREE; /* Free buffer space */ 248 static char *cte_text = NULL; /* Text buffer pointer */ 249 static char *cte_lp; /* Next free line in cte_text */ 250 static int cte_nvalid; /* Valid lines found */ 251 252 /* user's default environment for the shell */ 253 #define ROOTPATH "PATH=/usr/sbin:/usr/bin" 254 #define NONROOTPATH "PATH=/usr/bin:" 255 256 static char *Def_supath = NULL; 257 static char *Def_path = NULL; 258 static char path[LINE_MAX] = "PATH="; 259 static char supath[LINE_MAX] = "PATH="; 260 static char homedir[LINE_MAX] = "HOME="; 261 static char logname[LINE_MAX] = "LOGNAME="; 262 static char tzone[LINE_MAX] = "TZ="; 263 static char *envinit[] = { 264 homedir, 265 logname, 266 ROOTPATH, 267 "SHELL=/usr/bin/sh", 268 tzone, 269 NULL 270 }; 271 272 extern char **environ; 273 274 #define DEFTZ "GMT" 275 static int log = 0; 276 static char hzname[10]; 277 278 static void cronend(int); 279 static void thaw_handler(int); 280 static void child_handler(int); 281 static void child_sigreset(void); 282 283 static void 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 rp = rinfo_get(0); /* allocating a new runinfo struct */ 1959 1960 #ifdef ATLIMIT 1961 if ((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) { 1962 msg("ATLIMIT (%d) reached for uid %d", 1963 ATLIMIT, (e->u)->uid); 1964 rinfo_free(rp); 1965 resched(qp->nwait); 1966 return (0); 1967 } 1968 #endif 1969 #ifdef CRONLIMIT 1970 if ((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) { 1971 msg("CRONLIMIT (%d) reached for uid %d", 1972 CRONLIMIT, (e->u)->uid); 1973 rinfo_free(rp); 1974 resched(qp->nwait); 1975 return (0); 1976 } 1977 #endif 1978 if ((e->u)->uid == 0) { /* set default path */ 1979 /* path settable in defaults file */ 1980 envinit[2] = supath; 1981 } else { 1982 envinit[2] = path; 1983 } 1984 1985 /* 1986 * the tempnam() function uses malloc(3C) to allocate space for the 1987 * constructed file name, and returns a pointer to this area, which 1988 * is assigned to rp->outfile. Here rp->outfile is not overwritten. 1989 */ 1990 1991 rp->outfile = tempnam(TMPDIR, PFX); 1992 rp->jobtype = e->etype; 1993 if (e->etype == CRONEVENT) { 1994 rp->jobname = xmalloc(strlen(e->cmd)+1); 1995 (void) strcpy(rp->jobname, e->cmd); 1996 /* "cron" jobs only produce mail if there's output */ 1997 rp->mailwhendone = 0; 1998 } else { 1999 at_cmdfile = xmalloc(strlen(ATDIR)+strlen(e->cmd)+2); 2000 (void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd); 2001 if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) { 2002 if (errno == ENAMETOOLONG) { 2003 if (chdir(ATDIR) == 0) 2004 cron_unlink(e->cmd); 2005 } else { 2006 cron_unlink(at_cmdfile); 2007 } 2008 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT); 2009 free(at_cmdfile); 2010 rinfo_free(rp); 2011 return (0); 2012 } 2013 rp->jobname = xmalloc(strlen(at_cmdfile)+1); 2014 (void) strcpy(rp->jobname, at_cmdfile); 2015 2016 /* 2017 * Skip over the first two lines. 2018 */ 2019 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2020 (void) fscanf(atcmdfp, "%*[^\n]\n"); 2021 if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n", 2022 mailvar) == 1) { 2023 /* 2024 * Check to see if we should always send mail 2025 * to the owner. 2026 */ 2027 rp->mailwhendone = (strcmp(mailvar, "yes") == 0); 2028 } else { 2029 rp->mailwhendone = 0; 2030 } 2031 2032 if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) { 2033 projflag = 1; 2034 } 2035 (void) fclose(atcmdfp); 2036 } 2037 2038 /* 2039 * we make sure that the system time 2040 * hasn't drifted backwards. if it has, el_add() is now 2041 * called, to make sure that the event queue is back in order, 2042 * and we set the delayed flag. cron will pick up the request 2043 * later on at the proper time. 2044 */ 2045 dhltime = time(NULL); 2046 if ((dhltime - e->time) < 0) { 2047 msg("clock time drifted backwards!\n"); 2048 if (next_event->etype == CRONEVENT) { 2049 msg("correcting cron event\n"); 2050 next_event->time = next_time(next_event, dhltime); 2051 el_add(next_event, next_event->time, 2052 (next_event->u)->ctid); 2053 } else { /* etype == ATEVENT */ 2054 msg("correcting batch event\n"); 2055 el_add(next_event, next_event->time, 2056 next_event->of.at.eventid); 2057 } 2058 delayed++; 2059 t_old = time(NULL); 2060 free(at_cmdfile); 2061 rinfo_free(rp); 2062 return (0); 2063 } 2064 2065 if ((rfork = fork()) == (pid_t)-1) { 2066 reap_child(); 2067 if ((rfork = fork()) == (pid_t)-1) { 2068 msg("cannot fork"); 2069 free(at_cmdfile); 2070 rinfo_free(rp); 2071 resched(60); 2072 (void) sleep(30); 2073 return (0); 2074 } 2075 } 2076 if (rfork) { /* parent process */ 2077 contract_abandon_latest(rfork); 2078 2079 ++qp->nrun; 2080 rp->pid = rfork; 2081 rp->que = e->etype; 2082 if (e->etype != CRONEVENT) 2083 (e->u)->aruncnt++; 2084 else 2085 (e->u)->cruncnt++; 2086 rp->rusr = (e->u); 2087 logit(BCHAR, rp, 0); 2088 free(at_cmdfile); 2089 2090 return (0); 2091 } 2092 2093 child_sigreset(); 2094 contract_clear_template(); 2095 2096 if (e->etype != CRONEVENT) { 2097 /* open jobfile as stdin to shell */ 2098 if (stat(at_cmdfile, &buf)) { 2099 if (errno == ENAMETOOLONG) { 2100 if (chdir(ATDIR) == 0) 2101 cron_unlink(e->cmd); 2102 } else 2103 cron_unlink(at_cmdfile); 2104 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2105 exit(1); 2106 } 2107 if (!(buf.st_mode&ISUID)) { 2108 /* 2109 * if setuid bit off, original owner has 2110 * given this file to someone else 2111 */ 2112 cron_unlink(at_cmdfile); 2113 exit(1); 2114 } 2115 if ((fd = open(at_cmdfile, O_RDONLY)) == -1) { 2116 mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON); 2117 cron_unlink(at_cmdfile); 2118 exit(1); 2119 } 2120 if (fd != 0) { 2121 (void) dup2(fd, 0); 2122 (void) close(fd); 2123 } 2124 /* 2125 * retrieve the project id of the at job and convert it 2126 * to a project name. fail if it's not a valid project 2127 * or if the user isn't a member of the project. 2128 */ 2129 if (projflag == 1) { 2130 if ((pproj = getprojbyid(projid, &proj, 2131 (void *)&mybuf, sizeof (mybuf))) == NULL || 2132 !inproj(e->u->name, pproj->pj_name, 2133 mybuf2, sizeof (mybuf2))) { 2134 cron_unlink(at_cmdfile); 2135 mail((e->u)->name, BADPROJID, ERR_CANTEXECAT); 2136 exit(1); 2137 } 2138 } 2139 } 2140 2141 /* 2142 * Put process in a new session, and create a new task. 2143 */ 2144 if (setsid() < 0) { 2145 msg("setsid failed with errno = %d. job failed (%s)" 2146 " for user %s", errno, e->cmd, e->u->name); 2147 if (e->etype != CRONEVENT) 2148 cron_unlink(at_cmdfile); 2149 exit(1); 2150 } 2151 2152 /* 2153 * set correct user identification and check his account 2154 */ 2155 r = set_user_cred(e->u, pproj); 2156 if (r == VUC_EXPIRED) { 2157 msg("user (%s) account is expired", e->u->name); 2158 audit_cron_user_acct_expired(e->u->name); 2159 clean_out_user(e->u); 2160 exit(1); 2161 } 2162 if (r == VUC_NEW_AUTH) { 2163 msg("user (%s) password has expired", e->u->name); 2164 audit_cron_user_acct_expired(e->u->name); 2165 clean_out_user(e->u); 2166 exit(1); 2167 } 2168 if (r != VUC_OK) { 2169 msg("bad user (%s)", e->u->name); 2170 audit_cron_bad_user(e->u->name); 2171 clean_out_user(e->u); 2172 exit(1); 2173 } 2174 /* 2175 * check user and initialize the supplementary group access list. 2176 * bugid 1230784: deleted from parent to avoid cron hang. Now 2177 * only child handles the call. 2178 */ 2179 2180 if (verify_user_cred(e->u) != VUC_OK || 2181 setgid(e->u->gid) == -1 || 2182 initgroups(e->u->name, e->u->gid) == -1) { 2183 msg("bad user (%s) or setgid failed (%s)", 2184 e->u->name, e->u->name); 2185 audit_cron_bad_user(e->u->name); 2186 clean_out_user(e->u); 2187 exit(1); 2188 } 2189 2190 if (e->etype != CRONEVENT) { 2191 r = audit_cron_session(e->u->name, NULL, 2192 e->u->uid, e->u->gid, 2193 at_cmdfile); 2194 cron_unlink(at_cmdfile); 2195 } else { 2196 r = audit_cron_session(e->u->name, CRONDIR, 2197 e->u->uid, e->u->gid, 2198 NULL); 2199 } 2200 if (r != 0) { 2201 msg("cron audit problem. job failed (%s) for user %s", 2202 e->cmd, e->u->name); 2203 exit(1); 2204 } 2205 2206 audit_cron_new_job(e->cmd, e->etype, (void *)e); 2207 2208 if (setuid(e->u->uid) == -1) { 2209 msg("setuid failed (%s)", e->u->name); 2210 clean_out_user(e->u); 2211 exit(1); 2212 } 2213 2214 if (e->etype == CRONEVENT) { 2215 /* check for standard input to command */ 2216 if (e->of.ct.input != NULL) { 2217 if ((tmpfile = strdup(TMPINFILE)) == NULL) { 2218 mail((e->u)->name, MALLOCERR, 2219 ERR_CANTEXECCRON); 2220 exit(1); 2221 } 2222 if ((fd = mkstemp(tmpfile)) == -1 || 2223 (fptr = fdopen(fd, "w")) == NULL) { 2224 mail((e->u)->name, NOSTDIN, 2225 ERR_CANTEXECCRON); 2226 cron_unlink(tmpfile); 2227 free(tmpfile); 2228 exit(1); 2229 } 2230 if ((fwrite(e->of.ct.input, sizeof (char), 2231 strlen(e->of.ct.input), fptr)) != 2232 strlen(e->of.ct.input)) { 2233 mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON); 2234 cron_unlink(tmpfile); 2235 free(tmpfile); 2236 (void) close(fd); 2237 (void) fclose(fptr); 2238 exit(1); 2239 } 2240 if (fseek(fptr, (off_t)0, SEEK_SET) != -1) { 2241 if (fd != 0) { 2242 (void) dup2(fd, 0); 2243 (void) close(fd); 2244 } 2245 } 2246 cron_unlink(tmpfile); 2247 free(tmpfile); 2248 (void) fclose(fptr); 2249 } else if ((fd = open("/dev/null", O_RDONLY)) > 0) { 2250 (void) dup2(fd, 0); 2251 (void) close(fd); 2252 } 2253 } 2254 2255 /* redirect stdout and stderr for the shell */ 2256 if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1) 2257 fd = open("/dev/null", O_WRONLY); 2258 2259 if (fd >= 0 && fd != 1) 2260 (void) dup2(fd, 1); 2261 2262 if (fd >= 0 && fd != 2) { 2263 (void) dup2(fd, 2); 2264 if (fd != 1) 2265 (void) close(fd); 2266 } 2267 2268 (void) strlcat(homedir, (e->u)->home, sizeof (homedir)); 2269 (void) strlcat(logname, (e->u)->name, sizeof (logname)); 2270 environ = envinit; 2271 if (chdir((e->u)->home) == -1) { 2272 mail((e->u)->name, CANTCDHOME, 2273 e->etype == CRONEVENT ? ERR_CANTEXECCRON : 2274 ERR_CANTEXECAT); 2275 exit(1); 2276 } 2277 #ifdef TESTING 2278 exit(1); 2279 #endif 2280 /* 2281 * make sure that all file descriptors EXCEPT 0, 1 and 2 2282 * will be closed. 2283 */ 2284 closefrom(3); 2285 2286 if ((e->u)->uid != 0) 2287 (void) nice(qp->nice); 2288 if (e->etype == CRONEVENT) 2289 (void) execl(SHELL, "sh", "-c", e->cmd, 0); 2290 else /* type == ATEVENT */ 2291 (void) execl(SHELL, "sh", 0); 2292 mail((e->u)->name, CANTEXECSH, 2293 e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT); 2294 exit(1); 2295 /*NOTREACHED*/ 2296 } 2297 2298 static int 2299 idle(long t) 2300 { 2301 time_t now; 2302 2303 while (t > 0L) { 2304 2305 if (msg_wait(t) != 0) { 2306 /* we need to run next job immediately */ 2307 return (0); 2308 } 2309 2310 reap_child(); 2311 2312 now = time(NULL); 2313 if (last_time > now) { 2314 /* clock has been reset */ 2315 return (1); 2316 } 2317 2318 if (next_event == NULL && !el_empty()) { 2319 next_event = (struct event *)el_first(); 2320 } 2321 if (next_event == NULL) 2322 t = INFINITY; 2323 else 2324 t = (long)next_event->time - now; 2325 } 2326 return (0); 2327 } 2328 2329 /* 2330 * This used to be in the idle(), but moved to the separate function. 2331 * This called from various place when cron needs to reap the 2332 * child. It includes the situation that cron hit maxrun, and needs 2333 * to reschedule the job. 2334 */ 2335 static void 2336 reap_child() 2337 { 2338 pid_t pid; 2339 int prc; 2340 struct runinfo *rp; 2341 2342 for (;;) { 2343 pid = waitpid((pid_t)-1, &prc, WNOHANG); 2344 if (pid <= 0) 2345 break; 2346 #ifdef DEBUG 2347 fprintf(stderr, 2348 "wait returned %x for process %d\n", prc, pid); 2349 #endif 2350 if ((rp = rinfo_get(pid)) == NULL) { 2351 if (miscpid_delete(pid) == 0) { 2352 /* not found in anywhere */ 2353 msg(PIDERR, pid); 2354 } 2355 } else if (rp->que == ZOMB) { 2356 (void) unlink(rp->outfile); 2357 rinfo_free(rp); 2358 } else { 2359 cleanup(rp, prc); 2360 } 2361 } 2362 } 2363 2364 static void 2365 cleanup(struct runinfo *pr, int rc) 2366 { 2367 int nextfork = 1; 2368 struct usr *p; 2369 struct stat buf; 2370 2371 logit(ECHAR, pr, rc); 2372 --qt[pr->que].nrun; 2373 p = pr->rusr; 2374 if (pr->que != CRONEVENT) 2375 --p->aruncnt; 2376 else 2377 --p->cruncnt; 2378 2379 if (!lstat(pr->outfile, &buf)) { 2380 if ((buf.st_mode != S_IFLNK) && 2381 (buf.st_size > 0 || pr->mailwhendone)) { 2382 /* mail user stdout and stderr */ 2383 for (;;) { 2384 if ((pr->pid = fork()) < 0) { 2385 /* 2386 * if fork fails try forever in doubling 2387 * retry times, up to 16 seconds 2388 */ 2389 (void) sleep(nextfork); 2390 if (nextfork < 16) 2391 nextfork += nextfork; 2392 continue; 2393 } else if (pr->pid == 0) { 2394 child_sigreset(); 2395 contract_clear_template(); 2396 2397 mail_result(p, pr, buf.st_size); 2398 /* NOTREACHED */ 2399 } else { 2400 contract_abandon_latest(pr->pid); 2401 pr->que = ZOMB; 2402 break; 2403 } 2404 } 2405 } else { 2406 (void) unlink(pr->outfile); 2407 rinfo_free(pr); 2408 } 2409 } else { 2410 rinfo_free(pr); 2411 } 2412 2413 free_if_unused(p); 2414 } 2415 2416 /* 2417 * Mail stdout and stderr of a job to user. Get uid for real user and become 2418 * that person. We do this so that mail won't come from root since this 2419 * could be a security hole. If failure, quit - don't send mail as root. 2420 */ 2421 static void 2422 mail_result(struct usr *p, struct runinfo *pr, size_t filesize) 2423 { 2424 struct passwd *ruser_ids; 2425 FILE *mailpipe; 2426 FILE *st; 2427 struct utsname name; 2428 int nbytes; 2429 char iobuf[BUFSIZ]; 2430 char *cmd; 2431 2432 (void) uname(&name); 2433 if ((ruser_ids = getpwnam(p->name)) == NULL) 2434 exit(0); 2435 (void) setuid(ruser_ids->pw_uid); 2436 2437 cmd = xmalloc(strlen(MAIL)+strlen(p->name)+2); 2438 (void) sprintf(cmd, "%s %s", MAIL, p->name); 2439 mailpipe = popen(cmd, "w"); 2440 contract_abandon_latest(0); 2441 free(cmd); 2442 if (mailpipe == NULL) 2443 exit(127); 2444 (void) fprintf(mailpipe, "To: %s\n", p->name); 2445 if (pr->jobtype == CRONEVENT) { 2446 (void) fprintf(mailpipe, CRONOUT); 2447 (void) fprintf(mailpipe, "Your \"cron\" job on %s\n", 2448 name.nodename); 2449 if (pr->jobname != NULL) { 2450 (void) fprintf(mailpipe, "%s\n\n", pr->jobname); 2451 } 2452 } else { 2453 (void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n"); 2454 (void) fprintf(mailpipe, "Your \"at\" job on %s\n", 2455 name.nodename); 2456 if (pr->jobname != NULL) { 2457 (void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname); 2458 } 2459 } 2460 /* Tmp. file is fopen'ed w/ "r", secure open */ 2461 if (filesize > 0 && 2462 (st = fopen(pr->outfile, "r")) != NULL) { 2463 (void) fprintf(mailpipe, 2464 "produced the following output:\n\n"); 2465 while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0) 2466 (void) fwrite(iobuf, sizeof (char), nbytes, mailpipe); 2467 (void) fclose(st); 2468 } else { 2469 (void) fprintf(mailpipe, "completed.\n"); 2470 } 2471 (void) pclose(mailpipe); 2472 exit(0); 2473 } 2474 2475 static int 2476 msg_wait(long tim) 2477 { 2478 struct message msg; 2479 int cnt; 2480 time_t reftime; 2481 struct pollfd pfd[2]; 2482 int64_t tl; 2483 int timeout; 2484 static int pending_msg; 2485 static time_t pending_reftime; 2486 2487 if (pending_msg) { 2488 process_msg(&msgbuf, pending_reftime); 2489 pending_msg = 0; 2490 return (0); 2491 } 2492 2493 /* 2494 * We are opening the signal mask to receive SIGCLD. The notifypipe 2495 * is used to avoid race condition between SIGCLD and poll system 2496 * call. 2497 * If SIGCLD is delivered in poll(), poll will be interrupted, and 2498 * we will return to idle() to reap the dead children. 2499 * If SIGCLD is delivered between sigprocmask() below and poll(), 2500 * there is no way we can detect the SIGCLD because poll() won't 2501 * be interrupted. In such case, the dead children can't be wait'ed 2502 * until poll returns by timeout or a new job. To avoid this race 2503 * condition, child_handler write to the notifypipe, so that 2504 * poll() will be able to return with POLLIN which indicates that 2505 * we have received SIGCLD. 2506 * 2507 * Since the notifypipe is used to just let poll return from 2508 * system call, the data in the pipe won't be read. Therefore, 2509 * any data in the pipe needs to be flushed before opening signal 2510 * mask. 2511 * 2512 * Note that we can probably re-write this code with pselect() 2513 * which can handle this situation easily. 2514 */ 2515 (void) ioctl(notifypipe[0], I_FLUSH, FLUSHW); 2516 2517 pfd[0].fd = msgfd; 2518 pfd[0].events = POLLIN; 2519 pfd[1].fd = notifypipe[1]; 2520 pfd[1].events = POLLIN; 2521 2522 #ifdef CRON_MAXSLEEP 2523 /* 2524 * CRON_MAXSLEEP can be defined to have cron periodically wake 2525 * up, so that cron can detect a change of TOD and adjust the 2526 * sleep time accordingly. 2527 */ 2528 tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim; 2529 #endif 2530 tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000; 2531 2532 accept_sigcld = 1; 2533 (void) sigprocmask(SIG_UNBLOCK, &childmask, NULL); 2534 do { 2535 timeout = (tl > INT_MAX ? INT_MAX : (int)tl); 2536 tl -= timeout; 2537 cnt = poll(pfd, 2, timeout); 2538 if (cnt == -1 && errno != EINTR) { 2539 perror("! poll"); 2540 } 2541 } while (tl > 0 && cnt == 0); 2542 (void) sigprocmask(SIG_BLOCK, &childmask, NULL); 2543 accept_sigcld = 0; 2544 2545 /* 2546 * poll timeout or interrupted. 2547 */ 2548 if (cnt <= 0) 2549 return (0); 2550 2551 /* 2552 * Not the timeout or new job, but a SIGCLD has been delivered. 2553 */ 2554 if ((pfd[0].revents & POLLIN) == 0) 2555 return (0); 2556 2557 errno = 0; 2558 if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) { 2559 if (cnt != -1 || errno != EAGAIN) 2560 perror("! read"); 2561 return (0); 2562 } 2563 reftime = time(NULL); 2564 if (next_event != NULL && reftime >= next_event->time) { 2565 /* 2566 * we need to run the job before reloading crontab. 2567 */ 2568 (void) memcpy(&msgbuf, &msg, sizeof (msg)); 2569 pending_msg = 1; 2570 pending_reftime = reftime; 2571 return (1); 2572 } 2573 process_msg(&msg, reftime); 2574 return (0); 2575 } 2576 2577 /* 2578 * process the message supplied via pipe. This will be called either 2579 * immediately after cron read the message from pipe, or idle time 2580 * if the message was pending due to the job execution. 2581 */ 2582 static void 2583 process_msg(struct message *pmsg, time_t reftime) 2584 { 2585 if (pmsg->etype == NULL) 2586 return; 2587 2588 switch (pmsg->etype) { 2589 case AT: 2590 if (pmsg->action == DELETE) 2591 del_atjob(pmsg->fname, pmsg->logname); 2592 else 2593 mod_atjob(pmsg->fname, (time_t)0); 2594 break; 2595 case CRON: 2596 if (pmsg->action == DELETE) 2597 del_ctab(pmsg->fname); 2598 else 2599 mod_ctab(pmsg->fname, reftime); 2600 break; 2601 default: 2602 msg("message received - bad format"); 2603 break; 2604 } 2605 if (next_event != NULL) { 2606 if (next_event->etype == CRONEVENT) 2607 el_add(next_event, next_event->time, 2608 (next_event->u)->ctid); 2609 else /* etype == ATEVENT */ 2610 el_add(next_event, next_event->time, 2611 next_event->of.at.eventid); 2612 next_event = NULL; 2613 } 2614 (void) fflush(stdout); 2615 pmsg->etype = NULL; 2616 } 2617 2618 /* 2619 * Allocate a new or find an existing runinfo structure 2620 */ 2621 static struct runinfo * 2622 rinfo_get(pid_t pid) 2623 { 2624 struct runinfo *rp; 2625 2626 if (pid == 0) { /* allocate a new entry */ 2627 rp = xcalloc(1, sizeof (struct runinfo)); 2628 rp->next = rthead; /* link the entry into the list */ 2629 rthead = rp; 2630 return (rp); 2631 } 2632 /* search the list for an existing entry */ 2633 for (rp = rthead; rp != NULL; rp = rp->next) { 2634 if (rp->pid == pid) 2635 break; 2636 } 2637 return (rp); 2638 } 2639 2640 /* 2641 * Free a runinfo structure and its associated memory 2642 */ 2643 static void 2644 rinfo_free(struct runinfo *entry) 2645 { 2646 struct runinfo **rpp; 2647 struct runinfo *rp; 2648 2649 #ifdef DEBUG 2650 (void) fprintf(stderr, "freeing job %s\n", entry->jobname); 2651 #endif 2652 for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) { 2653 if (rp == entry) { 2654 *rpp = rp->next; /* unlink the entry */ 2655 free(rp->outfile); 2656 free(rp->jobname); 2657 free(rp); 2658 break; 2659 } 2660 } 2661 } 2662 2663 /* ARGSUSED */ 2664 static void 2665 thaw_handler(int sig) 2666 { 2667 ; 2668 } 2669 2670 2671 /* ARGSUSED */ 2672 static void 2673 cronend(int sig) 2674 { 2675 crabort("SIGTERM", REMOVE_FIFO); 2676 } 2677 2678 /*ARGSUSED*/ 2679 static void 2680 child_handler(int sig) 2681 { 2682 /* 2683 * Just in case someone changes the signal mask. 2684 * we don't want to notify the SIGCLD. 2685 */ 2686 if (accept_sigcld) { 2687 (void) write(notifypipe[0], &sig, 1); 2688 } 2689 } 2690 2691 static void 2692 child_sigreset(void) 2693 { 2694 (void) signal(SIGCLD, SIG_DFL); 2695 (void) sigprocmask(SIG_SETMASK, &defmask, NULL); 2696 } 2697 2698 /* 2699 * crabort() - handle exits out of cron 2700 */ 2701 static void 2702 crabort(char *mssg, int action) 2703 { 2704 int c; 2705 2706 if (action & REMOVE_FIFO) { 2707 /* FIFO vanishes when cron finishes */ 2708 if (unlink(FIFO) < 0) 2709 perror("cron could not unlink FIFO"); 2710 } 2711 2712 if (action & CONSOLE_MSG) { 2713 /* write error msg to console */ 2714 if ((c = open(CONSOLE, O_WRONLY)) >= 0) { 2715 (void) write(c, "cron aborted: ", 14); 2716 (void) write(c, mssg, strlen(mssg)); 2717 (void) write(c, "\n", 1); 2718 (void) close(c); 2719 } 2720 } 2721 2722 /* always log the message */ 2723 msg(mssg); 2724 msg("******* CRON ABORTED ********"); 2725 exit(1); 2726 } 2727 2728 /* 2729 * msg() - time-stamped error reporting function 2730 */ 2731 /*PRINTFLIKE1*/ 2732 static void 2733 msg(char *fmt, ...) 2734 { 2735 va_list args; 2736 time_t t; 2737 2738 t = time(NULL); 2739 2740 (void) fflush(stdout); 2741 2742 (void) fprintf(stderr, "! "); 2743 2744 va_start(args, fmt); 2745 (void) vfprintf(stderr, fmt, args); 2746 va_end(args); 2747 2748 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2749 (void) fprintf(stderr, " %s\n", timebuf); 2750 2751 (void) fflush(stderr); 2752 } 2753 2754 static void 2755 logit(int cc, struct runinfo *rp, int rc) 2756 { 2757 time_t t; 2758 int ret; 2759 2760 if (!log) 2761 return; 2762 2763 t = time(NULL); 2764 if (cc == BCHAR) 2765 (void) printf("%c CMD: %s\n", cc, next_event->cmd); 2766 (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t)); 2767 (void) printf("%c %.8s %u %c %s", 2768 cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf); 2769 if ((ret = TSTAT(rc)) != 0) 2770 (void) printf(" ts=%d", ret); 2771 if ((ret = RCODE(rc)) != 0) 2772 (void) printf(" rc=%d", ret); 2773 (void) putchar('\n'); 2774 (void) fflush(stdout); 2775 } 2776 2777 static void 2778 resched(int delay) 2779 { 2780 time_t nt; 2781 2782 /* run job at a later time */ 2783 nt = next_event->time + delay; 2784 if (next_event->etype == CRONEVENT) { 2785 next_event->time = next_time(next_event, (time_t)0); 2786 if (nt < next_event->time) 2787 next_event->time = nt; 2788 el_add(next_event, next_event->time, (next_event->u)->ctid); 2789 delayed = 1; 2790 msg("rescheduling a cron job"); 2791 return; 2792 } 2793 add_atevent(next_event->u, next_event->cmd, nt, next_event->etype); 2794 msg("rescheduling at job"); 2795 } 2796 2797 static void 2798 quedefs(int action) 2799 { 2800 int i; 2801 int j; 2802 char qbuf[QBUFSIZ]; 2803 FILE *fd; 2804 2805 /* set up default queue definitions */ 2806 for (i = 0; i < NQUEUE; i++) { 2807 qt[i].njob = qd.njob; 2808 qt[i].nice = qd.nice; 2809 qt[i].nwait = qd.nwait; 2810 } 2811 if (action == DEFAULT) 2812 return; 2813 if ((fd = fopen(QUEDEFS, "r")) == NULL) { 2814 msg("cannot open quedefs file"); 2815 msg("using default queue definitions"); 2816 return; 2817 } 2818 while (fgets(qbuf, QBUFSIZ, fd) != NULL) { 2819 if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.') 2820 continue; 2821 parsqdef(&qbuf[2]); 2822 qt[j].njob = qq.njob; 2823 qt[j].nice = qq.nice; 2824 qt[j].nwait = qq.nwait; 2825 } 2826 (void) fclose(fd); 2827 } 2828 2829 static void 2830 parsqdef(char *name) 2831 { 2832 int i; 2833 2834 qq = qd; 2835 while (*name) { 2836 i = 0; 2837 while (isdigit(*name)) { 2838 i *= 10; 2839 i += *name++ - '0'; 2840 } 2841 switch (*name++) { 2842 case JOBF: 2843 qq.njob = i; 2844 break; 2845 case NICEF: 2846 qq.nice = i; 2847 break; 2848 case WAITF: 2849 qq.nwait = i; 2850 break; 2851 } 2852 } 2853 } 2854 2855 /* 2856 * defaults - read defaults from /etc/default/cron 2857 */ 2858 static void 2859 defaults() 2860 { 2861 int flags; 2862 char *deflog; 2863 char *hz, *tz; 2864 2865 /* 2866 * get HZ value for environment 2867 */ 2868 if ((hz = getenv("HZ")) == (char *)NULL) 2869 (void) sprintf(hzname, "HZ=%d", HZ); 2870 else 2871 (void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz); 2872 /* 2873 * get TZ value for environment 2874 */ 2875 (void) snprintf(tzone, sizeof (tzone), "TZ=%s", 2876 ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ); 2877 2878 if (defopen(DEFFILE) == 0) { 2879 /* ignore case */ 2880 flags = defcntl(DC_GETFLAGS, 0); 2881 TURNOFF(flags, DC_CASE); 2882 (void) defcntl(DC_SETFLAGS, flags); 2883 2884 if (((deflog = defread("CRONLOG=")) == NULL) || 2885 (*deflog == 'N') || (*deflog == 'n')) 2886 log = 0; 2887 else 2888 log = 1; 2889 /* fix for 1087611 - allow paths to be set in defaults file */ 2890 if ((Def_path = defread("PATH=")) != NULL) { 2891 (void) strlcat(path, Def_path, LINE_MAX); 2892 } else { 2893 (void) strlcpy(path, NONROOTPATH, LINE_MAX); 2894 } 2895 if ((Def_supath = defread("SUPATH=")) != NULL) { 2896 (void) strlcat(supath, Def_supath, LINE_MAX); 2897 } else { 2898 (void) strlcpy(supath, ROOTPATH, LINE_MAX); 2899 } 2900 (void) defopen(NULL); 2901 } 2902 } 2903 2904 /* 2905 * Determine if a user entry for a job is still ok. The method used here 2906 * is a lot (about 75x) faster than using setgrent() / getgrent() 2907 * endgrent(). It should be safe because we use the sysconf to determine 2908 * the max, and it tolerates the max being 0. 2909 */ 2910 2911 static int 2912 verify_user_cred(const struct usr *u) 2913 { 2914 struct passwd *pw; 2915 size_t numUsrGrps = 0; 2916 size_t numOrigGrps = 0; 2917 size_t i; 2918 int retval; 2919 2920 /* 2921 * Maximum number of groups a user may be in concurrently. This 2922 * is a value which we obtain at runtime through a sysconf() 2923 * call. 2924 */ 2925 2926 static size_t nGroupsMax = (size_t)-1; 2927 2928 /* 2929 * Arrays for cron user's group list, constructed at startup to 2930 * be nGroupsMax elements long, used for verifying user 2931 * credentials prior to execution. 2932 */ 2933 2934 static gid_t *UsrGrps; 2935 static gid_t *OrigGrps; 2936 2937 if (((pw = getpwnam(u->name)) == NULL) || 2938 (pw->pw_uid != u->uid)) { 2939 return (VUC_BADUSER); 2940 } 2941 2942 /* 2943 * Create the group id lists needed for job credential 2944 * verification. 2945 */ 2946 2947 if (nGroupsMax == (size_t)-1) { 2948 if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) { 2949 UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 2950 OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t)); 2951 } 2952 2953 #ifdef DEBUG 2954 (void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax); 2955 #endif 2956 } 2957 2958 #ifdef DEBUG 2959 (void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name, 2960 pw->pw_uid); 2961 (void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, " 2962 "u->gid = %d\n", pw->pw_gid, u->gid); 2963 #endif 2964 2965 retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP; 2966 2967 if (nGroupsMax > 0) { 2968 numOrigGrps = getgroups(nGroupsMax, OrigGrps); 2969 2970 (void) initgroups(pw->pw_name, pw->pw_gid); 2971 numUsrGrps = getgroups(nGroupsMax, UsrGrps); 2972 2973 for (i = 0; i < numUsrGrps; i++) { 2974 if (UsrGrps[i] == u->gid) { 2975 retval = VUC_OK; 2976 break; 2977 } 2978 } 2979 2980 if (OrigGrps) { 2981 (void) setgroups(numOrigGrps, OrigGrps); 2982 } 2983 } 2984 2985 #ifdef DEBUG 2986 (void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval); 2987 #endif 2988 2989 return (retval); 2990 } 2991 2992 static int 2993 set_user_cred(const struct usr *u, struct project *pproj) 2994 { 2995 static char *progname = "cron"; 2996 int r = 0, rval = 0; 2997 2998 if ((r = pam_start(progname, u->name, &pam_conv, &pamh)) 2999 != PAM_SUCCESS) { 3000 #ifdef DEBUG 3001 msg("pam_start returns %d\n", r); 3002 #endif 3003 rval = VUC_BADUSER; 3004 goto set_eser_cred_exit; 3005 } 3006 3007 r = pam_acct_mgmt(pamh, 0); 3008 #ifdef DEBUG 3009 msg("pam_acc_mgmt returns %d\n", r); 3010 #endif 3011 if (r == PAM_ACCT_EXPIRED) { 3012 rval = VUC_EXPIRED; 3013 goto set_eser_cred_exit; 3014 } 3015 if (r == PAM_NEW_AUTHTOK_REQD) { 3016 rval = VUC_NEW_AUTH; 3017 goto set_eser_cred_exit; 3018 } 3019 if (r != PAM_SUCCESS) { 3020 rval = VUC_BADUSER; 3021 goto set_eser_cred_exit; 3022 } 3023 3024 if (pproj != NULL) { 3025 size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name); 3026 char *buf = alloca(sz); 3027 3028 (void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name); 3029 (void) pam_set_item(pamh, PAM_RESOURCE, buf); 3030 } 3031 3032 r = pam_setcred(pamh, PAM_ESTABLISH_CRED); 3033 if (r != PAM_SUCCESS) 3034 rval = VUC_BADUSER; 3035 3036 set_eser_cred_exit: 3037 (void) pam_end(pamh, r); 3038 return (rval); 3039 } 3040 3041 static void 3042 clean_out_user(struct usr *u) 3043 { 3044 if (next_event->u == u) { 3045 next_event = NULL; 3046 } 3047 3048 clean_out_ctab(u); 3049 clean_out_atjobs(u); 3050 free_if_unused(u); 3051 } 3052 3053 static void 3054 clean_out_atjobs(struct usr *u) 3055 { 3056 struct event *ev, *pv; 3057 3058 for (pv = NULL, ev = u->atevents; 3059 ev != NULL; 3060 pv = ev, ev = ev->link, free(pv)) { 3061 el_remove(ev->of.at.eventid, 1); 3062 if (cwd == AT) 3063 cron_unlink(ev->cmd); 3064 else { 3065 char buf[PATH_MAX]; 3066 if (strlen(ATDIR) + strlen(ev->cmd) + 2 3067 < PATH_MAX) { 3068 (void) sprintf(buf, "%s/%s", ATDIR, ev->cmd); 3069 cron_unlink(buf); 3070 } 3071 } 3072 free(ev->cmd); 3073 } 3074 3075 u->atevents = NULL; 3076 } 3077 3078 static void 3079 clean_out_ctab(struct usr *u) 3080 { 3081 rm_ctevents(u); 3082 el_remove(u->ctid, 0); 3083 u->ctid = 0; 3084 u->ctexists = 0; 3085 } 3086 3087 static void 3088 cron_unlink(char *name) 3089 { 3090 int r; 3091 3092 r = unlink(name); 3093 if (r == 0 || (r == -1 && errno == ENOENT)) { 3094 (void) audit_cron_delete_anc_file(name, NULL); 3095 } 3096 } 3097 3098 static void 3099 create_anc_ctab(struct event *e) 3100 { 3101 if (audit_cron_create_anc_file(e->u->name, 3102 (cwd == CRON) ? NULL:CRONDIR, 3103 e->u->name, 3104 e->u->uid) == -1) { 3105 process_anc_files(CRON_ANC_DELETE); 3106 crabort("cannot create ancillary files for crontabs", 3107 REMOVE_FIFO|CONSOLE_MSG); 3108 } 3109 } 3110 3111 static void 3112 delete_anc_ctab(struct event *e) 3113 { 3114 (void) audit_cron_delete_anc_file(e->u->name, 3115 (cwd == CRON) ? NULL:CRONDIR); 3116 } 3117 3118 static void 3119 create_anc_atjob(struct event *e) 3120 { 3121 if (!e->of.at.exists) 3122 return; 3123 3124 if (audit_cron_create_anc_file(e->cmd, 3125 (cwd == AT) ? NULL:ATDIR, 3126 e->u->name, 3127 e->u->uid) == -1) { 3128 process_anc_files(CRON_ANC_DELETE); 3129 crabort("cannot create ancillary files for atjobs", 3130 REMOVE_FIFO|CONSOLE_MSG); 3131 } 3132 } 3133 3134 static void 3135 delete_anc_atjob(struct event *e) 3136 { 3137 if (!e->of.at.exists) 3138 return; 3139 3140 (void) audit_cron_delete_anc_file(e->cmd, 3141 (cwd == AT) ? NULL:ATDIR); 3142 } 3143 3144 3145 static void 3146 process_anc_files(int del) 3147 { 3148 struct usr *u = uhead; 3149 struct event *e; 3150 3151 if (!audit_cron_mode()) 3152 return; 3153 3154 for (;;) { 3155 if (u->ctexists && u->ctevents != NULL) { 3156 e = u->ctevents; 3157 for (;;) { 3158 if (del) 3159 delete_anc_ctab(e); 3160 else 3161 create_anc_ctab(e); 3162 if ((e = e->link) == NULL) 3163 break; 3164 } 3165 } 3166 3167 if (u->atevents != NULL) { 3168 e = u->atevents; 3169 for (;;) { 3170 if (del) 3171 delete_anc_atjob(e); 3172 else 3173 create_anc_atjob(e); 3174 if ((e = e->link) == NULL) 3175 break; 3176 } 3177 } 3178 3179 if ((u = u->nextusr) == NULL) 3180 break; 3181 } 3182 } 3183 3184 /*ARGSUSED*/ 3185 static int 3186 cron_conv(int num_msg, struct pam_message **msgs, 3187 struct pam_response **response, void *appdata_ptr) 3188 { 3189 struct pam_message **m = msgs; 3190 int i; 3191 3192 for (i = 0; i < num_msg; i++) { 3193 switch (m[i]->msg_style) { 3194 case PAM_ERROR_MSG: 3195 case PAM_TEXT_INFO: 3196 if (m[i]->msg != NULL) { 3197 (void) msg("%s\n", m[i]->msg); 3198 } 3199 break; 3200 3201 default: 3202 break; 3203 } 3204 } 3205 return (0); 3206 } 3207 3208 /* 3209 * Cron creates process for other than job. Mail process is the 3210 * one which rinfo does not cover. Therefore, miscpid will keep 3211 * track of the pids executed from cron. Otherwise, we will see 3212 * "unexpected pid returned.." messages appear in the log file. 3213 */ 3214 static void 3215 miscpid_insert(pid_t pid) 3216 { 3217 struct miscpid *mp; 3218 3219 mp = xmalloc(sizeof (*mp)); 3220 mp->pid = pid; 3221 mp->next = miscpid_head; 3222 miscpid_head = mp; 3223 } 3224 3225 static int 3226 miscpid_delete(pid_t pid) 3227 { 3228 struct miscpid *mp, *omp; 3229 int found = 0; 3230 3231 omp = NULL; 3232 for (mp = miscpid_head; mp != NULL; mp = mp->next) { 3233 if (mp->pid == pid) { 3234 found = 1; 3235 break; 3236 } 3237 omp = mp; 3238 } 3239 if (found) { 3240 if (omp != NULL) 3241 omp->next = mp->next; 3242 else 3243 miscpid_head = NULL; 3244 free(mp); 3245 } 3246 return (found); 3247 } 3248 3249 /* 3250 * Establish contract terms such that all children are in abandoned 3251 * process contracts. 3252 */ 3253 static void 3254 contract_set_template(void) 3255 { 3256 int fd; 3257 3258 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3259 crabort("cannot open process contract template", 3260 REMOVE_FIFO | CONSOLE_MSG); 3261 3262 if (ct_pr_tmpl_set_param(fd, 0) || 3263 ct_tmpl_set_informative(fd, 0) || 3264 ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR)) 3265 crabort("cannot establish contract template terms", 3266 REMOVE_FIFO | CONSOLE_MSG); 3267 3268 if (ct_tmpl_activate(fd)) 3269 crabort("cannot activate contract template", 3270 REMOVE_FIFO | CONSOLE_MSG); 3271 3272 (void) close(fd); 3273 } 3274 3275 /* 3276 * Clear active process contract template. 3277 */ 3278 static void 3279 contract_clear_template(void) 3280 { 3281 int fd; 3282 3283 if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0) 3284 crabort("cannot open process contract template", 3285 REMOVE_FIFO | CONSOLE_MSG); 3286 3287 if (ct_tmpl_clear(fd)) 3288 crabort("cannot clear contract template", 3289 REMOVE_FIFO | CONSOLE_MSG); 3290 3291 (void) close(fd); 3292 } 3293 3294 /* 3295 * Abandon latest process contract unconditionally. If we have leaked [some 3296 * critical amount], exit such that the kernel reaps our contracts. 3297 */ 3298 static void 3299 contract_abandon_latest(pid_t pid) 3300 { 3301 int r; 3302 ctid_t id; 3303 static uint_t cts_lost; 3304 3305 if (cts_lost > MAX_LOST_CONTRACTS) 3306 crabort("repeated failure to abandon contracts", 3307 REMOVE_FIFO | CONSOLE_MSG); 3308 3309 if (r = contract_latest(&id)) { 3310 if (pid == 0) 3311 msg("could not obtain latest contract from " 3312 "popen(3C): %s", strerror(r)); 3313 else 3314 msg("could not obtain latest contract for " 3315 "PID %ld: %s", pid, strerror(r)); 3316 cts_lost++; 3317 return; 3318 } 3319 3320 if (r = contract_abandon_id(id)) { 3321 msg("could not abandon latest contract %ld: %s", id, 3322 strerror(r)); 3323 cts_lost++; 3324 return; 3325 } 3326 } 3327