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