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