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