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