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