386BSD 0.1 development
[unix-history] / usr / src / libexec / crond / misc.c
CommitLineData
6a550a97
WJ
1#if !defined(lint) && !defined(LINT)
2static char rcsid[] = "$Header: misc.c,v 2.1 90/07/18 00:24:33 vixie Exp $";
3#endif
4
5/* vix 26jan87 [RCS has the rest of the log]
6 * vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid]
7 * vix 30dec86 [written]
8 */
9
10/* Copyright 1988,1990 by Paul Vixie
11 * All rights reserved
12 *
13 * Distribute freely, except: don't remove my name from the source or
14 * documentation (don't take credit for my work), mark your changes (don't
15 * get me blamed for your possible bugs), don't alter or remove this
16 * notice. May be sold if buildable source is provided to buyer. No
17 * warrantee of any kind, express or implied, is included with this
18 * software; use at your own risk, responsibility for damages (if any) to
19 * anyone resulting from the use of this software rests entirely with the
20 * user.
21 *
22 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
23 * I'll try to keep a version up to date. I can be reached as follows:
24 * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
25 * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
26 */
27
28
29#include "cron.h"
30#include <sys/time.h>
31#include <sys/resource.h>
32#include <sys/ioctl.h>
33#include <sys/file.h>
34#include <errno.h>
35#if defined(ATT)
36# include <fcntl.h>
37#endif
38
39
40void log_it(), be_different(), acquire_daemonlock();
41
42
43char *
44savestr(str)
45 char *str;
46{
47 extern int strlen();
48 extern char *malloc(), *strcpy();
49 /**/ char *temp;
50
51 temp = malloc((unsigned) (strlen(str) + 1));
52 (void) strcpy(temp, str);
53 return temp;
54}
55
56
57int
58nocase_strcmp(left, right)
59 char *left;
60 char *right;
61{
62 while (*left && (MkLower(*left) == MkLower(*right)))
63 {
64 left++;
65 right++;
66 }
67 return MkLower(*left) - MkLower(*right);
68}
69
70
71int
72strcmp_until(left, right, until)
73 char *left;
74 char *right;
75 char until;
76{
77 register int diff;
78
79 Debug(DPARS|DEXT, ("strcmp_until(%s,%s,%c) ... ", left, right, until))
80
81 while (*left && *left != until && *left == *right)
82 {
83 left++;
84 right++;
85 }
86
87 if ( (*left=='\0' || *left == until)
88 && (*right=='\0' || *right == until)
89 )
90 diff = 0;
91 else
92 diff = *left - *right;
93
94 Debug(DPARS|DEXT, ("%d\n", diff))
95
96 return diff;
97}
98
99
100/* strdtb(s) - delete trailing blanks in string 's' and return new length
101 */
102int
103strdtb(s)
104 register char *s;
105{
106 register char *x = s;
107
108 /* scan forward to the null
109 */
110 while (*x)
111 x++;
112
113 /* scan backward to either the first character before the string,
114 * or the last non-blank in the string, whichever comes first.
115 */
116 do {x--;}
117 while (x >= s && isspace(*x));
118
119 /* one character beyond where we stopped above is where the null
120 * goes.
121 */
122 *++x = '\0';
123
124 /* the difference between the position of the null character and
125 * the position of the first character of the string is the length.
126 */
127 return x - s;
128}
129
130
131int
132set_debug_flags(flags)
133 char *flags;
134{
135 /* debug flags are of the form flag[,flag ...]
136 *
137 * if an error occurs, print a message to stdout and return FALSE.
138 * otherwise return TRUE after setting ERROR_FLAGS.
139 */
140
141#if !DEBUGGING
142
143 printf("this program was compiled without debugging enabled\n");
144 return FALSE;
145
146#else /* DEBUGGING */
147
148 char *pc = flags;
149
150 DebugFlags = 0;
151
152 while (*pc)
153 {
154 char **test;
155 int mask;
156
157 /* try to find debug flag name in our list.
158 */
159 for ( test = DebugFlagNames, mask = 1;
160 *test && strcmp_until(*test, pc, ',');
161 test++, mask <<= 1
162 )
163 ;
164
165 if (!*test)
166 {
167 fprintf(stderr,
168 "unrecognized debug flag <%s> <%s>\n",
169 flags, pc);
170 return FALSE;
171 }
172
173 DebugFlags |= mask;
174
175 /* skip to the next flag
176 */
177 while (*pc && *pc != ',')
178 pc++;
179 if (*pc == ',')
180 pc++;
181 }
182
183 if (DebugFlags)
184 {
185 int flag;
186
187 fprintf(stderr, "debug flags enabled:");
188
189 for (flag = 0; DebugFlagNames[flag]; flag++)
190 if (DebugFlags & (1 << flag))
191 fprintf(stderr, " %s", DebugFlagNames[flag]);
192 fprintf(stderr, "\n");
193 }
194
195 return TRUE;
196
197#endif /* DEBUGGING */
198}
199
200
201#if defined(BSD)
202void
203set_cron_uid()
204{
205 int seteuid();
206
207 if (seteuid(ROOT_UID) < OK)
208 {
209 perror("seteuid");
210 exit(ERROR_EXIT);
211 }
212}
213#endif
214
215#if defined(ATT)
216void
217set_cron_uid()
218{
219 int setuid();
220
221 if (setuid(ROOT_UID) < OK)
222 {
223 perror("setuid");
224 exit(ERROR_EXIT);
225 }
226}
227#endif
228
229void
230set_cron_cwd()
231{
232 extern int errno;
233 struct stat sb;
234
235 /* first check for CRONDIR ("/var/cron" or some such)
236 */
237 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
238 perror(CRONDIR);
239 if (OK == mkdir(CRONDIR, 0700)) {
240 fprintf(stderr, "%s: created\n", CRONDIR);
241 stat(CRONDIR, &sb);
242 } else {
243 fprintf(stderr, "%s: ", CRONDIR);
244 perror("mkdir");
245 exit(ERROR_EXIT);
246 }
247 }
248 if (!(sb.st_mode & S_IFDIR)) {
249 fprintf(stderr, "'%s' is not a directory, bailing out.\n",
250 CRONDIR);
251 exit(ERROR_EXIT);
252 }
253 if (chdir(CRONDIR) < OK) {
254 fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
255 perror(CRONDIR);
256 exit(ERROR_EXIT);
257 }
258
259 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
260 */
261 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
262 perror(SPOOL_DIR);
263 if (OK == mkdir(SPOOL_DIR, 0700)) {
264 fprintf(stderr, "%s: created\n", SPOOL_DIR);
265 stat(SPOOL_DIR, &sb);
266 } else {
267 fprintf(stderr, "%s: ", SPOOL_DIR);
268 perror("mkdir");
269 exit(ERROR_EXIT);
270 }
271 }
272 if (!(sb.st_mode & S_IFDIR)) {
273 fprintf(stderr, "'%s' is not a directory, bailing out.\n",
274 SPOOL_DIR);
275 exit(ERROR_EXIT);
276 }
277}
278
279
280#if defined(BSD)
281void
282be_different()
283{
284 /* release the control terminal:
285 * get new pgrp (name after our PID)
286 * do an IOCTL to void tty association
287 */
288
289 extern int getpid(), setpgrp(), open(), ioctl(), close();
290 auto int fd;
291
292 (void) setpgrp(0, getpid());
293
294 if ((fd = open("/dev/tty", 2)) >= 0)
295 {
296 (void) ioctl(fd, TIOCNOTTY, (char*)0);
297 (void) close(fd);
298 }
299}
300#endif /*BSD*/
301
302#if defined(ATT)
303void
304be_different()
305{
306 /* not being a system V wiz, I don't know if this is what you have
307 * to do to release your control terminal. what I want to accomplish
308 * is to keep this process from getting any signals from the tty.
309 *
310 * some system V person should let me know if this works... (vixie)
311 */
312 int setpgrp(), close(), open();
313
314 (void) setpgrp();
315
316 (void) close(STDIN); (void) open("/dev/null", 0);
317 (void) close(STDOUT); (void) open("/dev/null", 1);
318 (void) close(STDERR); (void) open("/dev/null", 2);
319}
320#endif /*ATT*/
321
322
323/* acquire_daemonlock() - write our PID into /etc/crond.pid, unless
324 * another daemon is already running, which we detect here.
325 */
326void
327acquire_daemonlock()
328{
329 int fd = open(PIDFILE, O_RDWR|O_CREAT, 0644);
330 FILE *fp = fdopen(fd, "r+");
331 int pid = getpid(), otherpid;
332 char buf[MAX_TEMPSTR];
333
334 if (fd < 0 || fp == NULL) {
335 sprintf(buf, "can't open or create %s, errno %d", PIDFILE, errno);
336 log_it("CROND", pid, "DEATH", buf);
337 exit(ERROR_EXIT);
338 }
339
340 if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
341 int save_errno = errno;
342
343 fscanf(fp, "%d", &otherpid);
344 sprintf(buf, "can't lock %s, otherpid may be %d, errno %d",
345 PIDFILE, otherpid, save_errno);
346 log_it("CROND", pid, "DEATH", buf);
347 exit(ERROR_EXIT);
348 }
349
350 rewind(fp);
351 fprintf(fp, "%d\n", pid);
352 fflush(fp);
353 ftruncate(fd, ftell(fp));
354
355 /* abandon fd and fp even though the file is open. we need to
356 * keep it open and locked, but we don't need the handles elsewhere.
357 */
358}
359
360/* get_char(file) : like getc() but increment LineNumber on newlines
361 */
362int
363get_char(file)
364 FILE *file;
365{
366 int ch;
367
368 ch = getc(file);
369 if (ch == '\n')
370 Set_LineNum(LineNumber + 1)
371 return ch;
372}
373
374
375/* unget_char(ch, file) : like ungetc but do LineNumber processing
376 */
377void
378unget_char(ch, file)
379 int ch;
380 FILE *file;
381{
382 ungetc(ch, file);
383 if (ch == '\n')
384 Set_LineNum(LineNumber - 1)
385}
386
387
388/* get_string(str, max, file, termstr) : like fgets() but
389 * (1) has terminator string which should include \n
390 * (2) will always leave room for the null
391 * (3) uses get_char() so LineNumber will be accurate
392 * (4) returns EOF or terminating character, whichever
393 */
394int
395get_string(string, size, file, terms)
396 char *string;
397 int size;
398 FILE *file;
399 char *terms;
400{
401 int ch;
402 char *index();
403
404 while (EOF != (ch = get_char(file)) && !index(terms, ch))
405 if (size > 1)
406 {
407 *string++ = (char) ch;
408 size--;
409 }
410
411 if (size > 0)
412 *string = '\0';
413
414 return ch;
415}
416
417
418/* skip_comments(file) : read past comment (if any)
419 */
420void
421skip_comments(file)
422 FILE *file;
423{
424 int ch;
425
426 while (EOF != (ch = get_char(file)))
427 {
428 /* ch is now the first character of a line.
429 */
430
431 while (ch == ' ' || ch == '\t')
432 ch = get_char(file);
433
434 if (ch == EOF)
435 break;
436
437 /* ch is now the first non-blank character of a line.
438 */
439
440 if (ch != '\n' && ch != '#')
441 break;
442
443 /* ch must be a newline or comment as first non-blank
444 * character on a line.
445 */
446
447 while (ch != '\n' && ch != EOF)
448 ch = get_char(file);
449
450 /* ch is now the newline of a line which we're going to
451 * ignore.
452 */
453 }
454 unget_char(ch, file);
455}
456
457/* int in_file(char *string, FILE *file)
458 * return TRUE if one of the lines in file matches string exactly,
459 * FALSE otherwise.
460 */
461int
462in_file(string, file)
463 char *string;
464 FILE *file;
465{
466 char line[MAX_TEMPSTR];
467
468 /* let's be persnickety today.
469 */
470 if (!file) {
471 if (!string)
472 string = "0x0";
473 fprintf(stderr,
474 "in_file(\"%s\", 0x%x): called with NULL file--botch",
475 string, file);
476 exit(ERROR_EXIT);
477 }
478
479 rewind(file);
480 while (fgets(line, MAX_TEMPSTR, file)) {
481 if (line[0] != '\0')
482 line[strlen(line)-1] = '\0';
483 if (0 == strcmp(line, string))
484 return TRUE;
485 }
486 return FALSE;
487}
488
489
490/* int allowed(char *username)
491 * returns TRUE if (ALLOW_FILE exists and user is listed)
492 * or (DENY_FILE exists and user is NOT listed)
493 * or (neither file exists but user=="root" so it's okay)
494 */
495int
496allowed(username)
497 char *username;
498{
499 static int init = FALSE;
500 static FILE *allow, *deny;
501
502 if (!init) {
503 init = TRUE;
504#if defined(ALLOW_FILE) && defined(DENY_FILE)
505 allow = fopen(ALLOW_FILE, "r");
506 deny = fopen(DENY_FILE, "r");
507 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
508#else
509 allow = NULL;
510 deny = NULL;
511#endif
512 }
513
514 if (allow)
515 return (in_file(username, allow));
516 if (deny)
517 return (!in_file(username, deny));
518
519#if defined(ALLOW_ONLY_ROOT)
520 return (strcmp(username, ROOT_USER) == 0);
521#else
522 return TRUE;
523#endif
524}
525
526
527#if defined(LOG_FILE) || defined(SYSLOG)
528void
529log_it(username, pid, event, detail)
530 char *username;
531 int pid;
532 char *event;
533 char *detail;
534{
535#if defined(LOG_FILE)
536 extern struct tm *localtime();
537 extern long time();
538 extern char *malloc();
539 auto char *msg;
540 auto long now = time((long *) 0);
541 register struct tm *t = localtime(&now);
542 static int log_fd = -1;
543#endif /*LOG_FILE*/
544
545#if defined(SYSLOG)
546 static int syslog_open = 0;
547#endif
548
549
550#if defined(LOG_FILE)
551 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
552 */
553 msg = malloc(strlen(username)
554 + strlen(event)
555 + strlen(detail)
556 + MAX_TEMPSTR);
557
558 if (log_fd < OK) {
559 log_fd = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
560 if (log_fd < OK) {
561 fprintf(stderr, "%s: can't open log file\n", ProgramName);
562 perror(LOG_FILE);
563 }
564 }
565
566 /* we have to sprintf() it because fprintf() doesn't always write
567 * everything out in one chunk and this has to be atomically appended
568 * to the log file.
569 */
570 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
571 username,
572 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
573 event, detail);
574
575 /* we have to run strlen() because sprintf() returns (char*) on BSD
576 */
577 if (log_fd < OK || write(log_fd, msg, strlen(msg)) < OK) {
578 fprintf(stderr, "%s: can't write to log file\n", ProgramName);
579 if (log_fd >= OK)
580 perror(LOG_FILE);
581 write(STDERR, msg, strlen(msg));
582 }
583
584 /* I suppose we could use alloca()...
585 */
586 free(msg);
587#endif /*LOG_FILE*/
588
589#if defined(SYSLOG)
590 if (!syslog_open) {
591 /* we don't use LOG_PID since the pid passed to us by
592 * our client may not be our own. therefore we want to
593 * print the pid ourselves.
594 */
595# ifdef LOG_CRON
596 openlog(ProgramName, 0, LOG_CRON);
597# else
598# ifdef LOG_DAEMON
599 openlog(ProgramName, 0, LOG_DAEMON);
600# else
601 openlog(ProgramName, 0);
602# endif /*LOG_DAEMON*/
603# endif /*LOG_CRON*/
604 syslog_open = TRUE; /* assume openlog success */
605 }
606
607 syslog(LOG_INFO, "(%s %d) %s (%s)\n",
608 username, pid, event, detail);
609
610#endif /*SYSLOG*/
611
612 if (DebugFlags) {
613 fprintf(stderr, "log_it: (%s %d) %s (%s)",
614 username, pid, event, detail);
615 }
616}
617#endif /*LOG_FILE || SYSLOG */
618
619
620/* two warnings:
621 * (1) this routine is fairly slow
622 * (2) it returns a pointer to static storage
623 */
624char *
625first_word(s, t)
626 local char *s; /* string we want the first word of */
627 local char *t; /* terminators, implicitly including \0 */
628{
629 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish I had GC */
630 static int retsel = 0;
631 local char *rb, *rp;
632 extern char *index();
633
634 /* select a return buffer */
635 retsel = 1-retsel;
636 rb = &retbuf[retsel][0];
637 rp = rb;
638
639 /* skip any leading terminators */
640 while (*s && (NULL != index(t, *s))) {s++;}
641
642 /* copy until next terminator or full buffer */
643 while (*s && (NULL == index(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
644 *rp++ = *s++;
645 }
646
647 /* finish the return-string and return it */
648 *rp = '\0';
649 return rb;
650}
651
652
653/* warning:
654 * heavily ascii-dependent.
655 */
656
657void
658mkprint(dst, src, len)
659 register char *dst;
660 register unsigned char *src;
661 register int len;
662{
663 while (len-- > 0)
664 {
665 register unsigned char ch = *src++;
666
667 if (ch < ' ') { /* control character */
668 *dst++ = '^';
669 *dst++ = ch + '@';
670 } else if (ch < 0177) { /* printable */
671 *dst++ = ch;
672 } else if (ch == 0177) { /* delete/rubout */
673 *dst++ = '^';
674 *dst++ = '?';
675 } else { /* parity character */
676 sprintf(dst, "\\%03o", ch);
677 dst += 4;
678 }
679 }
680 *dst = NULL;
681}
682
683
684/* warning:
685 * returns a pointer to malloc'd storage, you must call free yourself.
686 */
687
688char *
689mkprints(src, len)
690 register unsigned char *src;
691 register unsigned int len;
692{
693 extern char *malloc();
694 register char *dst = malloc(len*4 + 1);
695
696 mkprint(dst, src, len);
697
698 return dst;
699}