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