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