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