1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 30 /* All Rights Reserved */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #ifdef lint 35 /* make lint happy */ 36 #define __EXTENSIONS__ 37 #endif 38 39 #include <sys/contract/process.h> 40 #include <sys/ctfs.h> 41 #include <sys/param.h> 42 #include <sys/resource.h> 43 #include <sys/stat.h> 44 #include <sys/task.h> 45 #include <sys/time.h> 46 #include <sys/types.h> 47 #include <sys/utsname.h> 48 #include <sys/wait.h> 49 50 #include <security/pam_appl.h> 51 52 #include <alloca.h> 53 #include <ctype.h> 54 #include <deflt.h> 55 #include <dirent.h> 56 #include <errno.h> 57 #include <fcntl.h> 58 #include <grp.h> 59 #include <libcontract.h> 60 #include <libcontract_priv.h> 61 #include <limits.h> 62 #include <locale.h> 63 #include <poll.h> 64 #include <project.h> 65 #include <pwd.h> 66 #include <signal.h> 67 #include <stdarg.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <stropts.h> 72 #include <time.h> 73 #include <unistd.h> 74 75 #include "cron.h" 76 77 /* 78 * #define DEBUG 79 */ 80 81 #define MAIL "/usr/bin/mail" /* mail program to use */ 82 #define CONSOLE "/dev/console" /* where messages go when cron dies */ 83 84 #define TMPINFILE "/tmp/crinXXXXXX" /* file to put stdin in for cmd */ 85 #define TMPDIR "/tmp" 86 #define PFX "crout" 87 #define TMPOUTFILE "/tmp/croutXXXXXX" /* file to place stdout, stderr */ 88 89 #define INMODE 00400 /* mode for stdin file */ 90 #define OUTMODE 00600 /* mode for stdout file */ 91 #define ISUID S_ISUID /* mode for verifing at jobs */ 92 93 #define INFINITY 2147483647L /* upper bound on time */ 94 #define CUSHION 180L 95 #define ZOMB 100 /* proc slot used for mailing output */ 96 97 #define JOBF 'j' 98 #define NICEF 'n' 99 #define USERF 'u' 100 #define WAITF 'w' 101 102 #define BCHAR '>' 103 #define ECHAR '<' 104 105 #define DEFAULT 0 106 #define LOAD 1 107 #define QBUFSIZ 80 108 109 /* Defined actions for crabort() routine */ 110 #define NO_ACTION 000 111 #define REMOVE_FIFO 001 112 #define CONSOLE_MSG 002 113 114 #define BADCD "can't change directory to the crontab directory." 115 #define NOREADDIR "can't read the crontab directory." 116 117 #define BADJOBOPEN "unable to read your at job." 118 #define BADSHELL "because your login shell \ 119 isn't /usr/bin/sh, you can't use cron." 120 121 #define BADSTAT "can't access your crontab or at-job file. Resubmit it." 122 #define BADPROJID "can't set project id for your job." 123 #define CANTCDHOME "can't change directory to your home directory.\ 124 \nYour commands will not be executed." 125 #define CANTEXECSH "unable to exec the shell for one of your commands." 126 #define NOREAD "can't read your crontab file. Resubmit it." 127 #define BADTYPE "crontab or at-job file is not a regular file.\n" 128 #define NOSTDIN "unable to create a standard input file for \ 129 one of your crontab commands. \ 130 \nThat command was not executed." 131 132 #define NOTALLOWED "you are not authorized to use cron. Sorry." 133 #define STDERRMSG "\n\n********************************************\ 134 *****\nCron: The previous message is the \ 135 standard output and standard error \ 136 \nof one of your cron commands.\n" 137 138 #define STDOUTERR "one of your commands generated output or errors, \ 139 but cron was unable to mail you this output.\ 140 \nRemember to redirect standard output and standard \ 141 error for each of your commands." 142 143 #define CLOCK_DRIFT "clock time drifted backwards after event!\n" 144 #define PIDERR "unexpected pid returned %d (ignored)" 145 #define CRONTABERR "Subject: Your crontab file has an error in it\n\n" 146 #define CRONOUT "Subject: Output from \"cron\" command\n\n" 147 #define MALLOCERR "out of space, cannot create new string\n" 148 149 #define DIDFORK didfork 150 #define NOFORK !didfork 151 152 #define MAILBUFLEN (8*1024) 153 #define LINELIMIT 80 154 #define MAILBINITFREE (MAILBUFLEN - (sizeof (cte_intro) - 1) \ 155 - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1) 156 157 #define ERR_CRONTABENT 0 /* error in crontab file entry */ 158 #define ERR_UNIXERR 1 /* error in some system call */ 159 #define ERR_CANTEXECCRON 2 /* error setting up "cron" job environment */ 160 #define ERR_CANTEXECAT 3 /* error setting up "at" job environment */ 161 #define ERR_NOTREG 4 /* error not a regular file */ 162 163 #define PROJECT "project=" 164 165 #define MAX_LOST_CONTRACTS 2048 /* reset if this many failed abandons */ 166 167 #define FORMAT "%a %b %e %H:%M:%S %Y" 168 static char timebuf[80]; 169 170 struct event { 171 time_t time; /* time of the event */ 172 short etype; /* what type of event; 0=cron, 1=at */ 173 char *cmd; /* command for cron, job name for at */ 174 struct usr *u; /* ptr to the owner (usr) of this event */ 175 struct event *link; /* ptr to another event for this user */ 176 union { 177 struct { /* for crontab events */ 178 char *minute; /* (these */ 179 char *hour; /* fields */ 180 char *daymon; /* are */ 181 char *month; /* from */ 182 char *dayweek; /* crontab) */ 183 char *input; /* ptr to stdin */ 184 } ct; 185 struct { /* for at events */ 186 short exists; /* for revising at events */ 187 int eventid; /* for el_remove-ing at events */ 188 } at; 189 } of; 190 }; 191 192 struct usr { 193 char *name; /* name of user (e.g. "root") */ 194 char *home; /* home directory for user */ 195 uid_t uid; /* user id */ 196 gid_t gid; /* group id */ 197 int aruncnt; /* counter for running jobs per uid */ 198 int cruncnt; /* counter for running cron jobs per uid */ 199 int ctid; /* for el_remove-ing crontab events */ 200 short ctexists; /* for revising crontab events */ 201 struct event *ctevents; /* list of this usr's crontab events */ 202 struct event *atevents; /* list of this usr's at events */ 203 struct usr *nextusr; 204 }; /* ptr to next user */ 205 206 static struct queue 207 { 208 int njob; /* limit */ 209 int nice; /* nice for execution */ 210 int nwait; /* wait time to next execution attempt */ 211 int nrun; /* number running */ 212 } 213 qd = {100, 2, 60}, /* default values for queue defs */ 214 qt[NQUEUE]; 215 static struct queue qq; 216 217 static struct runinfo 218 { 219 pid_t pid; 220 short que; 221 struct usr *rusr; /* pointer to usr struct */ 222 char *outfile; /* file where stdout & stderr are trapped */ 223 short jobtype; /* what type of event: 0=cron, 1=at */ 224 char *jobname; /* command for "cron", jobname for "at" */ 225 int mailwhendone; /* 1 = send mail even if no ouptut */ 226 struct runinfo *next; 227 } *rthead; 228 229 static struct miscpid { 230 pid_t pid; 231 struct miscpid *next; 232 } *miscpid_head; 233 234 static pid_t cron_pid; /* own pid */ 235 static char didfork = 0; /* flag to see if I'm process group leader */ 236 static int msgfd; /* file descriptor for fifo queue */ 237 static int ecid = 1; /* event class id for el_remove(); MUST be set to 1 */ 238 static int delayed; /* is job being rescheduled or did it run first time */ 239 static int cwd; /* current working directory */ 240 static struct event *next_event; /* the next event to execute */ 241 static struct usr *uhead; /* ptr to the list of users */ 242 243 /* Variables for error handling at reading crontabs. */ 244 static char cte_intro[] = "Line(s) with errors:\n\n"; 245 static char cte_trail1[] = "\nMax number of errors encountered."; 246 static char cte_trail2[] = " Evaluation of crontab aborted.\n"; 247 static int cte_free = MAILBINITFREE; /* Free buffer space */ 248 static char *cte_text = NULL; /* Text buffer pointer */ 249 static char *cte_lp; /* Next free line in cte_text */ 250 static int cte_nvalid; /* Valid lines found */ 251 252 /* user's default environment for the shell */ 253 #define ROOTPATH "PATH=/usr/sbin:/usr/bin" 254 #define NONROOTPATH "PATH=/usr/bin:" 255 256 static char *Def_supath = NULL; 257 static char *Def_path = NULL; 258 static char path[LINE_MAX] = "PATH="; 259 static char supath[LINE_MAX] = "PATH="; 260 static char homedir[LINE_MAX] = "HOME="; 261 static char logname[LINE_MAX] = "LOGNAME="; 262 static char tzone[LINE_MAX] = "TZ="; 263 static char *envinit[] = { 264 homedir, 265 logname, 266 ROOTPATH, 267 "SHELL=/usr/bin/sh", 268 tzone, 269 NULL 270 }; 271 272 extern char **environ; 273 274 #define DEFTZ "GMT" 275 static int log = 0; 276 static char hzname[10]; 277 278 static void cronend(int); 279 static void thaw_handler(int); 280 static void child_handler(int); 281 static void child_sigreset(void); 282 283 static void mod_ctab(char *, time_t); 284 static void mod_atjob(char *, time_t); 285 static void add_atevent(struct usr *, char *, time_t, int); 286 static void rm_ctevents(struct usr *); 287 static void cleanup(struct runinfo *rn, int r); 288 static void crabort(char *, int); 289 static void msg(char *fmt, ...); 290 static void logit(int, struct runinfo *, int); 291 static void parsqdef(char *); 292 static void defaults(); 293 static void initialize(int); 294 static void quedefs(int); 295 static int idle(long); 296 static struct usr *find_usr(char *); 297 static int ex(struct event *e); 298 static void read_dirs(int); 299 static void mail(char *, char *, int); 300 static char *next_field(int, int); 301 static void readcron(struct usr *, time_t); 302 static int next_ge(int, char *); 303 static void free_if_unused(struct usr *); 304 static void del_atjob(char *, char *); 305 static void del_ctab(char *); 306 static void resched(int); 307 static int msg_wait(long); 308 static struct runinfo *rinfo_get(pid_t); 309 static void rinfo_free(struct runinfo *rp); 310 static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize); 311 static time_t next_time(struct event *, time_t); 312 static time_t get_switching_time(int, time_t); 313 static time_t xmktime(struct tm *); 314 static void process_msg(struct message *, time_t); 315 static void reap_child(void); 316 static void miscpid_insert(pid_t); 317 static int miscpid_delete(pid_t); 318 static void contract_set_template(void); 319 static void contract_clear_template(void); 320 static void contract_abandon_latest(pid_t); 321 322 static void cte_init(void); 323 static void cte_add(int, char *); 324 static void cte_valid(void); 325 static int cte_istoomany(void); 326 static void cte_sendmail(char *); 327 328 static int set_user_cred(const struct usr *, struct project *); 329 330 /* 331 * last_time is set immediately prior to exection of an event (via ex()) 332 * to indicate the last time an event was executed. This was (surely) 333 * it's original intended use. 334 */ 335 static time_t last_time, init_time, t_old; 336 337 static int accept_sigcld, notifypipe[2]; 338 static sigset_t defmask, childmask; 339 340 /* 341 * BSM hooks 342 */ 343 extern int audit_cron_session(char *, char *, uid_t, gid_t, char *); 344 extern void audit_cron_new_job(char *, int, void *); 345 extern void audit_cron_bad_user(char *); 346 extern void audit_cron_user_acct_expired(char *); 347 extern int audit_cron_create_anc_file(char *, char *, char *, uid_t); 348 extern int audit_cron_delete_anc_file(char *, char *); 349 extern int audit_cron_is_anc_name(char *); 350 extern int audit_cron_mode(); 351 352 static int cron_conv(int, struct pam_message **, 353 struct pam_response **, void *); 354 355 static struct pam_conv pam_conv = {cron_conv, NULL}; 356 static pam_handle_t *pamh; /* Authentication handle */ 357 358 /* 359 * Function to help check a user's credentials. 360 */ 361 362 static int verify_user_cred(struct usr *u); 363 364 /* 365 * Values returned by verify_user_cred and set_user_cred: 366 */ 367 368 #define VUC_OK 0 369 #define VUC_BADUSER 1 370 #define VUC_NOTINGROUP 2 371 #define VUC_EXPIRED 3 372 #define VUC_NEW_AUTH 4 373 374 /* 375 * Modes of process_anc_files function 376 */ 377 #define CRON_ANC_DELETE 1 378 #define CRON_ANC_CREATE 0 379 380 /* 381 * Functions to remove a user or job completely from the running database. 382 */ 383 static void clean_out_atjobs(struct usr *u); 384 static void clean_out_ctab(struct usr *u); 385 static void clean_out_user(struct usr *u); 386 static void cron_unlink(char *name); 387 static void process_anc_files(int); 388 389 /* 390 * functions in elm.c 391 */ 392 extern void el_init(int, time_t, time_t, int); 393 extern void el_add(void *, time_t, int); 394 extern void el_remove(int, int); 395 extern int el_empty(void); 396 extern void *el_first(void); 397 extern void el_delete(void); 398 399 static int valid_entry(char *, int); 400 static struct usr *create_ulist(char *, int); 401 static void init_cronevent(char *, int); 402 static void init_atevent(char *, time_t, int, int); 403 static void update_atevent(struct usr *, char *, time_t, int); 404 405 int 406 main(int argc, char *argv[]) 407 { 408 time_t t; 409 time_t ne_time; /* amt of time until next event execution */ 410 time_t newtime, lastmtime = 0L; 411 struct usr *u; 412 struct event *e, *e2, *eprev; 413 struct stat buf; 414 pid_t rfork; 415 struct sigaction act; 416 417 /* 418 * reset is set to 1 via the return from ex() should ex() find 419 * that the event to be executed is being run at the wrong time. 420 * We immediately return to the top of the while (TRUE) loop in 421 * main() where the event list is cleared and rebuilt, and reset 422 * is set back to 0. 423 */ 424 int reset = 0; 425 426 /* 427 * Only the privileged user can run this command. 428 */ 429 if (getuid() != 0) 430 crabort(NOTALLOWED, 0); 431 432 begin: 433 (void) setlocale(LC_ALL, ""); 434 /* fork unless 'nofork' is specified */ 435 if ((argc <= 1) || (strcmp(argv[1], "nofork"))) { 436 if (rfork = fork()) { 437 if (rfork == (pid_t)-1) { 438 (void) sleep(30); 439 goto begin; 440 } 441 return (0); 442 } 443 didfork++; 444 (void) setpgrp(); /* detach cron from console */ 445 } 446 447 (void) umask(022); 448 (void) signal(SIGHUP, SIG_IGN); 449 (void) signal(SIGINT, SIG_IGN); 450 (void) signal(SIGQUIT, SIG_IGN); 451 (void) signal(SIGTERM, cronend); 452 453 defaults(); 454 initialize(1); 455 quedefs(DEFAULT); /* load default queue definitions */ 456 cron_pid = getpid(); 457 msg("*** cron started *** pid = %d", cron_pid); 458 (void) sigset(SIGTHAW, thaw_handler); 459 /* 460 * setup SIGCLD handler/mask 461 */ 462 act.sa_handler = child_handler; 463 act.sa_flags = 0; 464 (void) sigemptyset(&act.sa_mask); 465 (void) sigaddset(&act.sa_mask, SIGCLD); 466 (void) sigaction(SIGCLD, &act, NULL); 467 (void) sigemptyset(&childmask); 468 (void) sigaddset(&childmask, SIGCLD); 469 (void) sigprocmask(SIG_BLOCK, &childmask, &defmask); 470 471 if (pipe(notifypipe) != 0) { 472 crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG); 473 } 474 /* 475 * will set O_NONBLOCK, so that the write() from child_handler 476 * never be blocked. 477 */ 478 (void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK); 479 480 t_old = time(NULL); 481 last_time = t_old; 482 for (;;) { /* MAIN LOOP */ 483 t = time(NULL); 484 if ((t_old > t) || (t-last_time > CUSHION) || reset) { 485 reset = 0; 486 /* the time was set backwards or forward */ 487 el_delete(); 488 u = uhead; 489 while (u != NULL) { 490 rm_ctevents(u); 491 e = u->atevents; 492 while (e != NULL) { 493 free(e->cmd); 494 e2 = e->link; 495 free(e); 496 e = e2; 497 } 498 u->atevents = NULL; 499 u = u->nextusr; 500 } 501 (void) close(msgfd); 502 initialize(0); 503 t = time(NULL); 504 last_time = t; 505 } 506 t_old = t; 507 508 if (next_event == NULL && !el_empty()) { 509 next_event = (struct event *)el_first(); 510 } 511 if (next_event == NULL) { 512 ne_time = INFINITY; 513 } else { 514 ne_time = next_event->time - t; 515 #ifdef DEBUG 516 cftime(timebuf, "%C", &next_event->time); 517 (void) fprintf(stderr, "next_time=%ld %s\n", 518 next_event->time, timebuf); 519 #endif 520 } 521 if (ne_time > 0) { 522 if ((reset = idle(ne_time)) != 0) 523 continue; 524 } 525 526 if (stat(QUEDEFS, &buf)) { 527 msg("cannot stat QUEDEFS file"); 528 } else if (lastmtime != buf.st_mtime) { 529 quedefs(LOAD); 530 lastmtime = buf.st_mtime; 531 } 532 533 last_time = next_event->time; /* save execution time */ 534 535 if (reset = ex(next_event)) 536 continue; 537 538 switch (next_event->etype) { 539 case CRONEVENT: 540 /* add cronevent back into the main event list */ 541 if (delayed) { 542 delayed = 0; 543 break; 544 } 545 546 /* 547 * check if time(0)< last_time. if so, then the 548 * system clock has gone backwards. to prevent this 549 * job from being started twice, we reschedule this 550 * job for the >>next time after last_time<<, and 551 * then set next_event->time to this. note that 552 * crontab's resolution is 1 minute. 553 */ 554 555 if (last_time > time(NULL)) { 556 msg(CLOCK_DRIFT); 557 /* 558 * bump up to next 30 second 559 * increment 560 * 1 <= newtime <= 30 561 */ 562 newtime = 30 - (last_time % 30); 563 newtime += last_time; 564 565 /* 566 * get the next scheduled event, 567 * not the one that we just 568 * kicked off! 569 */ 570 next_event->time = 571 next_time(next_event, newtime); 572 t_old = time(NULL); 573 } else { 574 next_event->time = 575 next_time(next_event, (time_t)0); 576 } 577 #ifdef DEBUG 578 cftime(timebuf, "%C", &next_event->time); 579 (void) fprintf(stderr, 580 "pushing back cron event %s at %ld (%s)\n", 581 next_event->cmd, next_event->time, timebuf); 582 #endif 583 584 el_add(next_event, next_event->time, 585 (next_event->u)->ctid); 586 break; 587 default: 588 /* remove at or batch job from system */ 589 if (delayed) { 590 delayed = 0; 591 break; 592 } 593 eprev = NULL; 594 e = (next_event->u)->atevents; 595 while (e != NULL) { 596 if (e == next_event) { 597 if (eprev == NULL) 598 (e->u)->atevents = e->link; 599 else 600 eprev->link = e->link; 601 free(e->cmd); 602 free(e); 603 break; 604 } else { 605 eprev = e; 606 e = e->link; 607 } 608 } 609 break; 610 } 611 next_event = NULL; 612 } 613 614 /*NOTREACHED*/ 615 } 616 617 static void 618 initialize(int firstpass) 619 { 620 #ifdef DEBUG 621 (void) fprintf(stderr, "in initialize\n"); 622 #endif 623 init_time = time(NULL); 624 el_init(8, init_time, (time_t)(60*60*24), 10); 625 if (firstpass) { 626 /* for mail(1), make sure messages come from root */ 627 if (putenv("LOGNAME=root") != 0) { 628 crabort("cannot expand env variable", 629 REMOVE_FIFO|CONSOLE_MSG); 630 } 631 if (access(FIFO, R_OK) == -1) { 632 if (errno == ENOENT) { 633 if (mknod(FIFO, S_IFIFO|0600, 0) != 0) 634 crabort("cannot create fifo queue", 635 REMOVE_FIFO|CONSOLE_MSG); 636 } else { 637 if (NOFORK) { 638 /* didn't fork... init(1M) is waiting */ 639 (void) sleep(60); 640 } 641 perror("FIFO"); 642 crabort("cannot access fifo queue", 643 REMOVE_FIFO|CONSOLE_MSG); 644 } 645 } else { 646 if (NOFORK) { 647 /* didn't fork... init(1M) is waiting */ 648 (void) sleep(60); 649 /* 650 * the wait is painful, but we don't want 651 * init respawning this quickly 652 */ 653 } 654 crabort("cannot start cron; FIFO exists", CONSOLE_MSG); 655 } 656 } 657 658 if ((msgfd = open(FIFO, O_RDWR)) < 0) { 659 perror("! open"); 660 crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG); 661 } 662 663 /* 664 * read directories, create users list, and add events to the 665 * main event list. Only zero user list on firstpass. 666 */ 667 if (firstpass) 668 uhead = NULL; 669 read_dirs(firstpass); 670 next_event = NULL; 671 672 if (!firstpass) 673 return; 674 675 /* stdout is log file */ 676 if (freopen(ACCTFILE, "a", stdout) == NULL) 677 (void) fprintf(stderr, "cannot open %s\n", ACCTFILE); 678 679 /* log should be root-only */ 680 (void) fchmod(1, S_IRUSR|S_IWUSR); 681 682 /* stderr also goes to ACCTFILE */ 683 (void) close(fileno(stderr)); 684 (void) dup(1); 685 686 /* null for stdin */ 687 (void) freopen("/dev/null", "r", stdin); 688 689 contract_set_template(); 690 } 691 692 static void 693 read_dirs(int first) 694 { 695 DIR *dir; 696 struct dirent *dp; 697 char *ptr; 698 int jobtype; 699 time_t tim; 700 701 702 if (chdir(CRONDIR) == -1) 703 crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG); 704 cwd = CRON; 705 if ((dir = opendir(".")) == NULL) 706 crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG); 707 while ((dp = readdir(dir)) != NULL) { 708 if (!valid_entry(dp->d_name, CRONEVENT)) 709 continue; 710 init_cronevent(dp->d_name, first); 711 } 712 (void) closedir(dir); 713 714 if (chdir(ATDIR) == -1) { 715 msg("cannot chdir to at directory"); 716 return; 717 } 718 if ((dir = opendir(".")) == NULL) { 719 msg("cannot read at at directory"); 720 return; 721 } 722 cwd = AT; 723 while ((dp = readdir(dir)) != NULL) { 724 if (!valid_entry(dp->d_name, ATEVENT)) 725 continue; 726 ptr = dp->d_name; 727 if (((tim = num(&ptr)) == 0) || (*ptr != '.')) 728 continue; 729 ptr++; 730 if (!isalpha(*ptr)) 731 continue; 732 jobtype = *ptr - 'a'; 733 if (jobtype >= NQUEUE) { 734 cron_unlink(dp->d_name); 735 continue; 736 } 737 init_atevent(dp->d_name, tim, jobtype, first); 738 } 739 (void) closedir(dir); 740 } 741 742 static int 743 valid_entry(char *name, int type) 744 { 745 struct stat buf; 746 747 if (strcmp(name, ".") == 0 || 748 strcmp(name, "..") == 0) 749 return (0); 750 751 /* skip over ancillary file names */ 752 if (audit_cron_is_anc_name(name)) 753 return (0); 754 755 if (stat(name, &buf)) { 756 mail(name, BADSTAT, ERR_UNIXERR); 757 cron_unlink(name); 758 return (0); 759 } 760 if (!S_ISREG(buf.st_mode)) { 761 mail(name, BADTYPE, ERR_NOTREG); 762 cron_unlink(name); 763 return (0); 764 } 765 if (type == ATEVENT) { 766 if (!(buf.st_mode & ISUID)) { 767 cron_unlink(name); 768 return (0); 769 } 770 } 771 return (1); 772 } 773 774 struct usr * 775 create_ulist(char *name, int type) 776 { 777 struct usr *u; 778 779 u = xmalloc(sizeof (struct usr)); 780 u->name = xmalloc(strlen(name) + 1); 781 (void) strcpy(u->name, name); 782 u->home = NULL; 783 if (type == CRONEVENT) { 784 u->ctexists = TRUE; 785 u->ctid = ecid++; 786 } else { 787 u->ctexists = FALSE; 788 u->ctid = 0; 789 } 790 u->ctevents = NULL; 791 u->atevents = NULL; 792 u->aruncnt = 0; 793 u->cruncnt = 0; 794 u->nextusr = uhead; 795 uhead = u; 796 return (u); 797 } 798 799 void 800 init_cronevent(char *name, int first) 801 { 802 struct usr *u; 803 804 if (first) { 805 u = create_ulist(name, CRONEVENT); 806 readcron(u, 0); 807 } else { 808 if ((u = find_usr(name)) == NULL) { 809 u = create_ulist(name, CRONEVENT); 810 readcron(u, 0); 811 } else { 812 u->ctexists = TRUE; 813 rm_ctevents(u); 814 el_remove(u->ctid, 0); 815 readcron(u, 0); 816 } 817 } 818 } 819 820 void 821 init_atevent(char *name, time_t tim, int jobtype, int first) 822 { 823 struct usr *u; 824 825 if (first) { 826 u = create_ulist(name, ATEVENT); 827 add_atevent(u, name, tim, jobtype); 828 } else { 829 if ((u = find_usr(name)) == NULL) { 830 u = create_ulist(name, ATEVENT); 831 add_atevent(u, name, tim, jobtype); 832 } else { 833 update_atevent(u, name, tim, jobtype); 834 } 835 } 836 } 837 838 static void 839 mod_ctab(char *name, time_t reftime) 840 { 841 struct passwd *pw; 842 struct stat buf; 843 struct usr *u; 844 char namebuf[PATH_MAX]; 845 char *pname; 846 847 /* skip over ancillary file names */ 848 if (audit_cron_is_anc_name(name)) 849 return; 850 851 if ((pw = getpwnam(name)) == NULL) { 852 msg("No such user as %s - cron entries not created", name); 853 return; 854 } 855 if (cwd != CRON) { 856 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", 857 CRONDIR, name) >= sizeof (namebuf)) { 858 msg("Too long path name %s - cron entries not created", 859 namebuf); 860 return; 861 } 862 pname = namebuf; 863 } else { 864 pname = name; 865 } 866 /* 867 * a warning message is given by the crontab command so there is 868 * no need to give one here...... use this code if you only want 869 * users with a login shell of /usr/bin/sh to use cron 870 */ 871 #ifdef BOURNESHELLONLY 872 if ((strcmp(pw->pw_shell, "") != 0) && 873 (strcmp(pw->pw_shell, SHELL) != 0)) { 874 mail(name, BADSHELL, ERR_CANTEXECCRON); 875 cron_unlink(pname); 876 return; 877 } 878 #endif 879 if (stat(pname, &buf)) { 880 mail(name, BADSTAT, ERR_UNIXERR); 881 cron_unlink(pname); 882 return; 883 } 884 if (!S_ISREG(buf.st_mode)) { 885 mail(name, BADTYPE, ERR_CRONTABENT); 886 return; 887 } 888 if ((u = find_usr(name)) == NULL) { 889 #ifdef DEBUG 890 (void) fprintf(stderr, "new user (%s) with a crontab\n", name); 891 #endif 892 u = create_ulist(name, CRONEVENT); 893 u->home = xmalloc(strlen(pw->pw_dir) + 1); 894 (void) strcpy(u->home, pw->pw_dir); 895 u->uid = pw->pw_uid; 896 u->gid = pw->pw_gid; 897 readcron(u, reftime); 898 } else { 899 u->uid = pw->pw_uid; 900 u->gid = pw->pw_gid; 901 if (u->home != NULL) { 902 if (strcmp(u->home, pw->pw_dir) != 0) { 903 free(u->home); 904 u->home = xmalloc(strlen(pw->pw_dir) + 1); 905 (void) strcpy(u->home, pw->pw_dir); 906 } 907 } else { 908 u->home = xmalloc(strlen(pw->pw_dir) + 1); 909 (void) strcpy(u->home, pw->pw_dir); 910 } 911 u->ctexists = TRUE; 912 if (u->ctid == 0) { 913 #ifdef DEBUG 914 (void) fprintf(stderr, "%s now has a crontab\n", 915 u->name); 916 #endif 917 /* user didnt have a crontab last time */ 918 u->ctid = ecid++; 919 u->ctevents = NULL; 920 readcron(u, reftime); 921 return; 922 } 923 #ifdef DEBUG 924 (void) fprintf(stderr, "%s has revised his crontab\n", u->name); 925 #endif 926 rm_ctevents(u); 927 el_remove(u->ctid, 0); 928 readcron(u, reftime); 929 } 930 } 931 932 /* ARGSUSED */ 933 static void 934 mod_atjob(char *name, time_t reftime) 935 { 936 char *ptr; 937 time_t tim; 938 struct passwd *pw; 939 struct stat buf; 940 struct usr *u; 941 char namebuf[PATH_MAX]; 942 char *pname; 943 int jobtype; 944 945 ptr = name; 946 if (((tim = num(&ptr)) == 0) || (*ptr != '.')) 947 return; 948 ptr++; 949 if (!isalpha(*ptr)) 950 return; 951 jobtype = *ptr - 'a'; 952 953 /* check for audit ancillary file */ 954 if (audit_cron_is_anc_name(name)) 955 return; 956 957 if (cwd != AT) { 958 if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name) 959 >= sizeof (namebuf)) { 960 return; 961 } 962 pname = namebuf; 963 } else { 964 pname = name; 965 } 966 if (stat(pname, &buf) || jobtype >= NQUEUE) { 967 cron_unlink(pname); 968 return; 969 } 970 if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) { 971 cron_unlink(pname); 972 return; 973 } 974 if ((pw = getpwuid(buf.st_uid)) == NULL) { 975 cron_unlink(pname); 976 return; 977 } 978 /* 979 * a warning message is given by the at command so there is no 980 * need to give one here......use this code if you only want 981 * users with a login shell of /usr/bin/sh to use cron 982 */ 983 #ifdef BOURNESHELLONLY 984 if ((strcmp(pw->pw_shell, "") != 0) && 985 (strcmp(pw->pw_shell, SHELL) != 0)) { 986 mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT); 987 cron_unlink(pname); 988 return; 989 } 990 #endif 991 if ((u = find_usr(pw->pw_name)) == NULL) { 992 #ifdef DEBUG 993 (void) fprintf(stderr, "new user (%s) with an at job = %s\n", 994 pw->pw_name, name); 995 #endif 996 u = create_ulist(pw->pw_name, ATEVENT); 997 u->home = xmalloc(strlen(pw->pw_dir) + 1); 998 (void) strcpy(u->home, pw->pw_dir); 999 u->uid = pw->pw_uid; 1000 u->gid = pw->pw_gid; 1001 add_atevent(u, name, tim, jobtype); 1002 } else { 1003 u->uid = pw->pw_uid; 1004 u->gid = pw->pw_gid; 1005 free(u->home); 1006 u->home = xmalloc(strlen(pw->pw_dir) + 1); 1007 (void) strcpy(u->home, pw->pw_dir); 1008 update_atevent(u, name, tim, jobtype); 1009 } 1010 } 1011 1012 static void 1013 add_atevent(struct usr *u, char *job, time_t tim, int jobtype) 1014 { 1015 struct event *e; 1016 1017 e = xmalloc(sizeof (struct event)); 1018 e->etype = jobtype; 1019 e->cmd = xmalloc(strlen(job) + 1); 1020 (void) strcpy(e->cmd, job); 1021 e->u = u; 1022 e->link = u->atevents; 1023 u->atevents = e; 1024 e->of.at.exists = TRUE; 1025 e->of.at.eventid = ecid++; 1026 if (tim < init_time) /* old job */ 1027 e->time = init_time; 1028 else 1029 e->time = tim; 1030 #ifdef DEBUG 1031 (void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n", 1032 u->name, e->cmd, e->time); 1033 #endif 1034 el_add(e, e->time, e->of.at.eventid); 1035 } 1036 1037 void 1038 update_atevent(struct usr *u, char *name, time_t tim, int jobtype) 1039 { 1040 struct event *e; 1041 1042 e = u->atevents; 1043 while (e != NULL) { 1044 if (strcmp(e->cmd, name) == 0) { 1045 e->of.at.exists = TRUE; 1046 break; 1047 } else { 1048 e = e->link; 1049 } 1050 } 1051 if (e == NULL) { 1052 #if