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