dhcpcd: update README.DRAGONFLY
[dragonfly.git] / usr.sbin / cron / lib / misc.c
blob77c0c7a7d48c8a2ae9f2285c85f0cb295cfce60c
1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
2 * All rights reserved
4 * Distribute freely, except: don't remove my name from the source or
5 * documentation (don't take credit for my work), mark your changes (don't
6 * get me blamed for your possible bugs), don't alter or remove this
7 * notice. May be sold if buildable source is provided to buyer. No
8 * warrantee of any kind, express or implied, is included with this
9 * software; use at your own risk, responsibility for damages (if any) to
10 * anyone resulting from the use of this software rests entirely with the
11 * user.
13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
14 * I'll try to keep a version up to date. I can be reached as follows:
15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
17 * $FreeBSD: src/usr.sbin/cron/lib/misc.c,v 1.8.2.2 2002/04/28 22:45:53 dwmalone Exp $
20 /* vix 26jan87 [RCS has the rest of the log]
21 * vix 30dec86 [written]
25 #include "cron.h"
26 #if SYS_TIME_H
27 # include <sys/time.h>
28 #else
29 # include <time.h>
30 #endif
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #if defined(SYSLOG)
38 # include <syslog.h>
39 #endif
42 #if defined(LOG_DAEMON) && !defined(LOG_CRON)
43 #define LOG_CRON LOG_DAEMON
44 #endif
47 static int LogFD = ERR;
50 int
51 strcmp_until(char *left, char *right, int until)
53 int diff;
55 while (*left && *left != until && *left == *right) {
56 left++;
57 right++;
60 if ((*left=='\0' || *left == until) &&
61 (*right=='\0' || *right == until)) {
62 diff = 0;
63 } else {
64 diff = *left - *right;
67 return diff;
71 /* strdtb(s) - delete trailing blanks in string 's' and return new length
73 int
74 strdtb(char *s)
76 char *x = s;
78 /* scan forward to the null
80 while (*x)
81 x++;
83 /* scan backward to either the first character before the string,
84 * or the last non-blank in the string, whichever comes first.
86 do {x--;}
87 while (x >= s && isspace(*x));
89 /* one character beyond where we stopped above is where the null
90 * goes.
92 *++x = '\0';
94 /* the difference between the position of the null character and
95 * the position of the first character of the string is the length.
97 return x - s;
102 set_debug_flags(char *flags)
104 /* debug flags are of the form flag[,flag ...]
106 * if an error occurs, print a message to stdout and return FALSE.
107 * otherwise return TRUE after setting ERROR_FLAGS.
110 #if !DEBUGGING
112 printf("this program was compiled without debugging enabled\n");
113 return FALSE;
115 #else /* DEBUGGING */
117 char *pc = flags;
119 DebugFlags = 0;
121 while (*pc) {
122 char **test;
123 int mask;
125 /* try to find debug flag name in our list.
127 for ( test = DebugFlagNames, mask = 1;
128 *test && strcmp_until(*test, pc, ',');
129 test++, mask <<= 1
133 if (!*test) {
134 fprintf(stderr,
135 "unrecognized debug flag <%s> <%s>\n",
136 flags, pc);
137 return FALSE;
140 DebugFlags |= mask;
142 /* skip to the next flag
144 while (*pc && *pc != ',')
145 pc++;
146 if (*pc == ',')
147 pc++;
150 if (DebugFlags) {
151 int flag;
153 fprintf(stderr, "debug flags enabled:");
155 for (flag = 0; DebugFlagNames[flag]; flag++)
156 if (DebugFlags & (1 << flag))
157 fprintf(stderr, " %s", DebugFlagNames[flag]);
158 fprintf(stderr, "\n");
161 return TRUE;
163 #endif /* DEBUGGING */
167 void
168 set_cron_uid(void)
170 #if defined(BSD) || defined(POSIX)
171 if (seteuid(ROOT_UID) < OK)
172 err(ERROR_EXIT, "seteuid");
173 #else
174 if (setuid(ROOT_UID) < OK)
175 err(ERROR_EXIT, "setuid");
176 #endif
180 void
181 set_cron_cwd(void)
183 struct stat sb;
185 /* first check for CRONDIR ("/var/cron" or some such)
187 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
188 warn("%s", CRONDIR);
189 if (OK == mkdir(CRONDIR, 0700)) {
190 warnx("%s: created", CRONDIR);
191 stat(CRONDIR, &sb);
192 } else {
193 err(ERROR_EXIT, "%s: mkdir", CRONDIR);
196 if (!(sb.st_mode & S_IFDIR))
197 err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR);
198 if (chdir(CRONDIR) < OK)
199 err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR);
201 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
203 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
204 warn("%s", SPOOL_DIR);
205 if (OK == mkdir(SPOOL_DIR, 0700)) {
206 warnx("%s: created", SPOOL_DIR);
207 stat(SPOOL_DIR, &sb);
208 } else {
209 err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR);
212 if (!(sb.st_mode & S_IFDIR))
213 err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR);
217 /* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
218 * another daemon is already running, which we detect here.
220 * note: main() calls us twice; once before forking, once after.
221 * we maintain static storage of the file pointer so that we
222 * can rewrite our PID into the PIDFILE after the fork.
224 * it would be great if fflush() disassociated the file buffer.
226 void
227 acquire_daemonlock(int closeflag)
229 static FILE *fp = NULL;
231 if (closeflag && fp) {
232 fclose(fp);
233 fp = NULL;
234 return;
237 if (!fp) {
238 char pidfile[MAX_FNAME];
239 char buf[MAX_TEMPSTR];
240 int fd, otherpid;
242 sprintf(pidfile, PIDFILE, PIDDIR);
243 if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
244 || (NULL == (fp = fdopen(fd, "r+")))
246 sprintf(buf, "can't open or create %.64s: %s",
247 pidfile, strerror(errno));
248 log_it("CRON", getpid(), "DEATH", buf);
249 errx(ERROR_EXIT, "%s", buf);
252 if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
253 int save_errno = errno;
255 fscanf(fp, "%d", &otherpid);
256 sprintf(buf, "can't lock %.64s, otherpid may be %d: %s",
257 pidfile, otherpid, strerror(save_errno));
258 log_it("CRON", getpid(), "DEATH", buf);
259 errx(ERROR_EXIT, "%s", buf);
262 fcntl(fd, F_SETFD, 1);
265 rewind(fp);
266 fprintf(fp, "%d\n", getpid());
267 fflush(fp);
268 ftruncate(fileno(fp), ftell(fp));
270 /* abandon fd and fp even though the file is open. we need to
271 * keep it open and locked, but we don't need the handles elsewhere.
275 /* get_char(file) : like getc() but increment LineNumber on newlines
278 get_char(FILE *file)
280 int ch;
282 ch = getc(file);
283 if (ch == '\n')
284 Set_LineNum(LineNumber + 1)
285 return ch;
289 /* unget_char(ch, file) : like ungetc but do LineNumber processing
291 void
292 unget_char(int ch, FILE *file)
294 ungetc(ch, file);
295 if (ch == '\n')
296 Set_LineNum(LineNumber - 1)
300 /* get_string(str, max, file, termstr) : like fgets() but
301 * (1) has terminator string which should include \n
302 * (2) will always leave room for the null
303 * (3) uses get_char() so LineNumber will be accurate
304 * (4) returns EOF or terminating character, whichever
307 get_string(char *string, int size, FILE *file, char *terms)
309 int ch;
311 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
312 if (size > 1) {
313 *string++ = (char) ch;
314 size--;
318 if (size > 0)
319 *string = '\0';
321 return ch;
325 /* skip_comments(file) : read past comment (if any)
327 void
328 skip_comments(FILE *file)
330 int ch;
332 while (EOF != (ch = get_char(file))) {
333 /* ch is now the first character of a line.
336 while (ch == ' ' || ch == '\t')
337 ch = get_char(file);
339 if (ch == EOF)
340 break;
342 /* ch is now the first non-blank character of a line.
345 if (ch != '\n' && ch != '#')
346 break;
348 /* ch must be a newline or comment as first non-blank
349 * character on a line.
352 while (ch != '\n' && ch != EOF)
353 ch = get_char(file);
355 /* ch is now the newline of a line which we're going to
356 * ignore.
359 if (ch != EOF)
360 unget_char(ch, file);
364 /* int in_file(char *string, FILE *file)
365 * return TRUE if one of the lines in file matches string exactly,
366 * FALSE otherwise.
368 static int
369 in_file(char *string, FILE *file)
371 char line[MAX_TEMPSTR];
373 rewind(file);
374 while (fgets(line, MAX_TEMPSTR, file)) {
375 if (line[0] != '\0')
376 if (line[strlen(line)-1] == '\n')
377 line[strlen(line)-1] = '\0';
378 if (0 == strcmp(line, string))
379 return TRUE;
381 return FALSE;
385 /* int allowed(char *username)
386 * returns TRUE if (ALLOW_FILE exists and user is listed)
387 * or (DENY_FILE exists and user is NOT listed)
388 * or (neither file exists but user=="root" so it's okay)
391 allowed(char *username)
393 static int init = FALSE;
394 static FILE *allow, *deny;
396 if (!init) {
397 init = TRUE;
398 #if defined(ALLOW_FILE) && defined(DENY_FILE)
399 allow = fopen(ALLOW_FILE, "r");
400 deny = fopen(DENY_FILE, "r");
401 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
402 #else
403 allow = NULL;
404 deny = NULL;
405 #endif
408 if (allow)
409 return (in_file(username, allow));
410 if (deny)
411 return (!in_file(username, deny));
413 #if defined(ALLOW_ONLY_ROOT)
414 return (strcmp(username, ROOT_USER) == 0);
415 #else
416 return TRUE;
417 #endif
421 void
422 log_it(char *username, int xpid, char *event, const char *detail)
424 PID_T pid = xpid;
425 #if defined(LOG_FILE)
426 char *msg;
427 TIME_T now;
428 struct tm *t;
429 #endif /*LOG_FILE*/
431 #if defined(SYSLOG)
432 static int syslog_open = 0;
433 #endif
435 #if defined(LOG_FILE)
436 now = time((TIME_T)0);
437 t = localtime(&now);
438 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
440 msg = malloc(strlen(username)
441 + strlen(event)
442 + strlen(detail)
443 + MAX_TEMPSTR);
445 if (msg == NULL)
446 warnx("failed to allocate memory for log message");
447 else {
448 if (LogFD < OK) {
449 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
450 if (LogFD < OK) {
451 warn("can't open log file %s", LOG_FILE);
452 } else {
453 fcntl(LogFD, F_SETFD, 1);
457 /* we have to sprintf() it because fprintf() doesn't always
458 * write everything out in one chunk and this has to be
459 * atomically appended to the log file.
461 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
462 username,
463 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min,
464 t->tm_sec, pid, event, detail);
466 /* we have to run strlen() because sprintf() returns (char*)
467 * on old BSD.
469 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
470 if (LogFD >= OK)
471 warn("%s", LOG_FILE);
472 warnx("can't write to log file");
473 write(STDERR, msg, strlen(msg));
476 free(msg);
478 #endif /*LOG_FILE*/
480 #if defined(SYSLOG)
481 if (!syslog_open) {
482 /* we don't use LOG_PID since the pid passed to us by
483 * our client may not be our own. therefore we want to
484 * print the pid ourselves.
486 # ifdef LOG_DAEMON
487 openlog(ProgramName, LOG_PID, LOG_CRON);
488 # else
489 openlog(ProgramName, LOG_PID);
490 # endif
491 syslog_open = TRUE; /* assume openlog success */
494 syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
496 #endif /*SYSLOG*/
498 #if DEBUGGING
499 if (DebugFlags) {
500 fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
501 username, pid, event, detail);
503 #endif
507 void
508 log_close(void) {
509 if (LogFD != ERR) {
510 close(LogFD);
511 LogFD = ERR;
516 /* two warnings:
517 * (1) this routine is fairly slow
518 * (2) it returns a pointer to static storage
520 * s string we want the first word of
521 * t terminators, implicitly including \0
523 char *
524 first_word(char *s, char *t)
526 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
527 static int retsel = 0;
528 char *rb, *rp;
530 /* select a return buffer */
531 retsel = 1-retsel;
532 rb = &retbuf[retsel][0];
533 rp = rb;
535 /* skip any leading terminators */
536 while (*s && (NULL != strchr(t, *s))) {
537 s++;
540 /* copy until next terminator or full buffer */
541 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
542 *rp++ = *s++;
545 /* finish the return-string and return it */
546 *rp = '\0';
547 return rb;
551 /* warning:
552 * heavily ascii-dependent.
554 void
555 mkprint(char *dst, unsigned char *src, int len)
557 while (len-- > 0)
559 unsigned char ch = *src++;
561 if (ch < ' ') { /* control character */
562 *dst++ = '^';
563 *dst++ = ch + '@';
564 } else if (ch < 0177) { /* printable */
565 *dst++ = ch;
566 } else if (ch == 0177) { /* delete/rubout */
567 *dst++ = '^';
568 *dst++ = '?';
569 } else { /* parity character */
570 sprintf(dst, "\\%03o", ch);
571 dst += 4;
574 *dst = '\0';
578 /* warning:
579 * returns a pointer to malloc'd storage, you must call free yourself.
581 char *
582 mkprints(unsigned char *src, unsigned int len)
584 char *dst;
586 dst = malloc(len * 4 + 1);
587 if (dst != NULL)
588 mkprint(dst, src, len);
590 return dst;
594 #ifdef MAIL_DATE
595 /* Sat, 27 Feb 93 11:44:51 CST
596 * 123456789012345678901234567
598 char *
599 arpadate(time_t *clock)
601 time_t t = clock ?*clock :time(0L);
602 struct tm *tm = localtime(&t);
603 static char ret[32]; /* zone name might be >3 chars */
605 if (tm->tm_year >= 100)
606 tm->tm_year += 1900;
608 snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s",
609 DowNames[tm->tm_wday],
610 tm->tm_mday,
611 MonthNames[tm->tm_mon],
612 tm->tm_year,
613 tm->tm_hour,
614 tm->tm_min,
615 tm->tm_sec,
616 TZONE(*tm));
617 return ret;
619 #endif /*MAIL_DATE*/
622 #ifdef HAVE_SAVED_UIDS
623 static int save_euid;
625 swap_uids(void)
627 save_euid = geteuid();
628 return seteuid(getuid());
631 swap_uids_back(void)
634 return seteuid(save_euid);
636 #else /*HAVE_SAVED_UIDS*/
638 swap_uids(void)
640 return setreuid(geteuid(), getuid());
643 swap_uids_back(void)
645 return swap_uids();
647 #endif /*HAVE_SAVED_UIDS*/