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