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