No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / libamu / xutil.c
blob4624bfa35cdf32accb3bdfe0d06c252ff232dc8f
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/libamu/xutil.c
47 * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amu.h>
57 * Logfp is the default logging device, and is initialized to stderr by
58 * default in dplog/plog below, and in
59 * amd/amfs_program.c:amfs_program_exec().
61 FILE *logfp = NULL;
63 static char *am_progname = "unknown"; /* "amd" */
64 static char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */
65 pid_t am_mypid = -1; /* process ID */
66 serv_state amd_state; /* amd's state */
67 int foreground = 1; /* 1 == this is the top-level server */
68 u_int debug_flags = D_CONTROL; /* set regardless if compiled with debugging */
70 #ifdef HAVE_SYSLOG
71 int syslogging;
72 #endif /* HAVE_SYSLOG */
73 static u_int xlog_level = XLOG_DEFAULT;
74 static u_long amd_program_number = AMQ_PROGRAM;
76 #ifdef DEBUG_MEM
77 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
78 static int mem_bytes;
79 static int orig_mem_bytes;
80 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
81 #endif /* DEBUG_MEM */
83 /* forward definitions */
84 /* for GCC format string auditing */
85 static void real_plog(int lvl, const char *fmt, va_list vargs)
86 __attribute__((__format__(__printf__, 2, 0)));
89 #ifdef DEBUG
91 * List of debug options.
93 struct opt_tab dbg_opt[] =
95 {"all", D_ALL}, /* All non-disruptive options */
96 {"defaults", D_DEFAULT}, /* Default options */
97 {"test", D_TEST}, /* Full debug - no daemon, no fork, no amq, local mtab */
98 {"amq", D_AMQ}, /* Register for AMQ program */
99 {"daemon", D_DAEMON}, /* Enter daemon mode */
100 {"fork", D_FORK}, /* Fork server (hlfsd only) */
101 {"full", D_FULL}, /* Program trace */
102 #ifdef HAVE_CLOCK_GETTIME
103 {"hrtime", D_HRTIME}, /* Print high resolution time stamps */
104 #endif /* HAVE_CLOCK_GETTIME */
105 {"info", D_INFO}, /* info service specific debugging (hesiod, nis, etc) */
106 {"mem", D_MEM}, /* Trace memory allocations */
107 {"mtab", D_MTAB}, /* Use local mtab file */
108 {"readdir", D_READDIR}, /* Check on browsable_dirs progress */
109 {"str", D_STR}, /* Debug string munging */
110 {"trace", D_TRACE}, /* Protocol trace */
111 {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */
112 {NULL, 0}
114 #endif /* DEBUG */
117 * List of log options
119 struct opt_tab xlog_opt[] =
121 {"all", XLOG_ALL}, /* All messages */
122 {"defaults", XLOG_DEFAULT}, /* Default messages */
123 #ifdef DEBUG
124 {"debug", XLOG_DEBUG}, /* Debug messages */
125 #endif /* DEBUG */ /* DEBUG */
126 {"error", XLOG_ERROR}, /* Non-fatal system errors */
127 {"fatal", XLOG_FATAL}, /* Fatal errors */
128 {"info", XLOG_INFO}, /* Information */
129 {"map", XLOG_MAP}, /* Map errors */
130 {"stats", XLOG_STATS}, /* Additional statistical information */
131 {"user", XLOG_USER}, /* Non-fatal user errors */
132 {"warn", XLOG_WARNING}, /* Warnings */
133 {"warning", XLOG_WARNING}, /* Warnings */
134 {NULL, 0}
138 void
139 am_set_progname(char *pn)
141 am_progname = pn;
145 const char *
146 am_get_progname(void)
148 return am_progname;
152 void
153 am_set_hostname(char *hn)
155 xstrlcpy(am_hostname, hn, sizeof(am_hostname));
159 const char *
160 am_get_hostname(void)
162 return am_hostname;
166 pid_t
167 am_set_mypid(void)
169 am_mypid = getpid();
170 return am_mypid;
174 long
175 get_server_pid()
177 return (long) (foreground ? am_mypid : getppid());
181 voidp
182 xmalloc(int len)
184 voidp p;
185 int retries = 600;
188 * Avoid malloc's which return NULL for malloc(0)
190 if (len == 0)
191 len = 1;
193 do {
194 p = (voidp) malloc((unsigned) len);
195 if (p) {
196 if (amuDebug(D_MEM))
197 plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p);
198 return p;
200 if (retries > 0) {
201 plog(XLOG_ERROR, "Retrying memory allocation");
202 sleep(1);
204 } while (--retries);
206 plog(XLOG_FATAL, "Out of memory");
207 going_down(1);
209 abort();
211 return 0;
215 /* like xmalloc, but zeros out the bytes */
216 voidp
217 xzalloc(int len)
219 voidp p = xmalloc(len);
221 if (p)
222 memset(p, 0, len);
223 return p;
227 voidp
228 xrealloc(voidp ptr, int len)
230 if (amuDebug(D_MEM))
231 plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
233 if (len == 0)
234 len = 1;
236 if (ptr)
237 ptr = (voidp) realloc(ptr, (unsigned) len);
238 else
239 ptr = (voidp) xmalloc((unsigned) len);
241 if (!ptr) {
242 plog(XLOG_FATAL, "Out of memory in realloc");
243 going_down(1);
244 abort();
246 return ptr;
250 #ifdef DEBUG_MEM
251 void
252 dxfree(char *file, int line, voidp ptr)
254 if (amuDebug(D_MEM))
255 plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr);
256 /* this is the only place that must NOT use XFREE()!!! */
257 free(ptr);
258 ptr = NULL; /* paranoid */
262 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
263 static void
264 checkup_mem(void)
266 struct mallinfo mi = mallinfo();
267 u_long uordbytes = mi.uordblks * 4096;
269 if (mem_bytes != uordbytes) {
270 if (orig_mem_bytes == 0)
271 mem_bytes = orig_mem_bytes = uordbytes;
272 else {
273 fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid);
274 if (mem_bytes < uordbytes) {
275 fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
276 } else {
277 fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
279 mem_bytes = uordbytes;
280 fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
283 malloc_verify();
285 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
286 #endif /* DEBUG_MEM */
290 * Take a log format string and expand occurrences of %m
291 * with the current error code taken from errno. Make sure
292 * 'e' never gets longer than maxlen characters.
294 static const char *
295 expand_error(const char *f, char *e, size_t maxlen)
297 const char *p;
298 char *q;
299 int error = errno;
300 int len = 0;
302 for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) {
303 if (p[0] == '%' && p[1] == 'm') {
304 xstrlcpy(q, strerror(error), maxlen);
305 len += strlen(q) - 1;
306 q += strlen(q) - 1;
307 p++;
310 e[maxlen-1] = '\0'; /* null terminate, to be sure */
311 return e;
316 * Output the time of day and hostname to the logfile
318 static void
319 show_time_host_and_name(int lvl)
321 static time_t last_t = 0;
322 static char *last_ctime = NULL;
323 time_t t;
324 #if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
325 struct timespec ts;
326 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
327 char nsecs[11]; /* '.' + 9 digits + '\0' */
328 char *sev;
330 nsecs[0] = '\0';
332 #if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
334 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
335 * returning ENOSYS.
337 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
338 t = ts.tv_sec;
339 if (amuDebug(D_HRTIME))
340 xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec);
342 else
343 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
344 t = clocktime(NULL);
346 if (t != last_t) {
347 last_ctime = ctime(&t);
348 last_t = t;
351 switch (lvl) {
352 case XLOG_FATAL:
353 sev = "fatal:";
354 break;
355 case XLOG_ERROR:
356 sev = "error:";
357 break;
358 case XLOG_USER:
359 sev = "user: ";
360 break;
361 case XLOG_WARNING:
362 sev = "warn: ";
363 break;
364 case XLOG_INFO:
365 sev = "info: ";
366 break;
367 case XLOG_DEBUG:
368 sev = "debug:";
369 break;
370 case XLOG_MAP:
371 sev = "map: ";
372 break;
373 case XLOG_STATS:
374 sev = "stats:";
375 break;
376 default:
377 sev = "hmm: ";
378 break;
380 fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
381 last_ctime + 4, nsecs, am_get_hostname(),
382 am_get_progname(),
383 (long) am_mypid,
384 sev);
388 #ifdef DEBUG
390 * Switch on/off debug options
393 debug_option(char *opt)
395 u_int dl = debug_flags;
396 static int initialized_debug_flags = 0;
397 int rc = cmdoption(opt, dbg_opt, &dl);
399 if (rc) /* if got any error, don't update debug flags */
400 return EINVAL;
403 * If we already initialized the debugging flags once (via amd.conf), then
404 * don't allow "immutable" flags to be changed again (via amq -D), because
405 * they could mess Amd's state and only make sense to be set once when Amd
406 * starts.
408 if (initialized_debug_flags &&
409 debug_flags != 0 &&
410 (dl & D_IMMUTABLE) != (debug_flags & D_IMMUTABLE)) {
411 plog(XLOG_ERROR, "cannot change immutable debug flags");
412 /* undo any attempted change to an immutable flag */
413 dl = (dl & ~D_IMMUTABLE) | (debug_flags & D_IMMUTABLE);
415 initialized_debug_flags = 1;
416 debug_flags = dl;
418 return rc;
422 void
423 dplog(const char *fmt, ...)
425 va_list ap;
427 if (!logfp)
428 logfp = stderr; /* initialize before possible first use */
430 va_start(ap, fmt);
431 real_plog(XLOG_DEBUG, fmt, ap);
432 va_end(ap);
434 #endif /* DEBUG */
437 void
438 plog(int lvl, const char *fmt, ...)
440 va_list ap;
442 if (!logfp)
443 logfp = stderr; /* initialize before possible first use */
445 va_start(ap, fmt);
446 real_plog(lvl, fmt, ap);
447 va_end(ap);
451 static void
452 real_plog(int lvl, const char *fmt, va_list vargs)
454 char msg[1024];
455 char efmt[1024];
456 char *ptr = msg;
457 static char last_msg[1024];
458 static int last_count = 0, last_lvl = 0;
460 if (!(xlog_level & lvl))
461 return;
463 #ifdef DEBUG_MEM
464 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
465 checkup_mem();
466 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
467 #endif /* DEBUG_MEM */
470 * Note: xvsnprintf() may call plog() if a truncation happened, but the
471 * latter has some code to break out of an infinite loop. See comment in
472 * xsnprintf() below.
474 xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
476 ptr += strlen(ptr);
477 if (*(ptr-1) == '\n')
478 *--ptr = '\0';
480 #ifdef HAVE_SYSLOG
481 if (syslogging) {
482 switch (lvl) { /* from mike <mcooper@usc.edu> */
483 case XLOG_FATAL:
484 lvl = LOG_CRIT;
485 break;
486 case XLOG_ERROR:
487 lvl = LOG_ERR;
488 break;
489 case XLOG_USER:
490 lvl = LOG_WARNING;
491 break;
492 case XLOG_WARNING:
493 lvl = LOG_WARNING;
494 break;
495 case XLOG_INFO:
496 lvl = LOG_INFO;
497 break;
498 case XLOG_DEBUG:
499 lvl = LOG_DEBUG;
500 break;
501 case XLOG_MAP:
502 lvl = LOG_DEBUG;
503 break;
504 case XLOG_STATS:
505 lvl = LOG_INFO;
506 break;
507 default:
508 lvl = LOG_ERR;
509 break;
511 syslog(lvl, "%s", msg);
512 return;
514 #endif /* HAVE_SYSLOG */
516 *ptr++ = '\n';
517 *ptr = '\0';
520 * mimic syslog behavior: only write repeated strings if they differ
522 switch (last_count) {
523 case 0: /* never printed at all */
524 last_count = 1;
525 if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
526 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
527 last_lvl = lvl;
528 show_time_host_and_name(lvl); /* mimic syslog header */
529 fwrite(msg, ptr - msg, 1, logfp);
530 fflush(logfp);
531 break;
533 case 1: /* item printed once, if same, don't repeat */
534 if (STREQ(last_msg, msg)) {
535 last_count++;
536 } else { /* last msg printed once, new one differs */
537 /* last_count remains at 1 */
538 if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
539 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
540 last_lvl = lvl;
541 show_time_host_and_name(lvl); /* mimic syslog header */
542 fwrite(msg, ptr - msg, 1, logfp);
543 fflush(logfp);
545 break;
547 case 100:
549 * Don't allow repetitions longer than 100, so you can see when something
550 * cycles like crazy.
552 show_time_host_and_name(last_lvl);
553 xsnprintf(last_msg, sizeof(last_msg),
554 "last message repeated %d times\n", last_count);
555 fwrite(last_msg, strlen(last_msg), 1, logfp);
556 fflush(logfp);
557 last_count = 0; /* start from scratch */
558 break;
560 default: /* item repeated multiple times */
561 if (STREQ(last_msg, msg)) {
562 last_count++;
563 } else { /* last msg repeated+skipped, new one differs */
564 show_time_host_and_name(last_lvl);
565 xsnprintf(last_msg, sizeof(last_msg),
566 "last message repeated %d times\n", last_count);
567 fwrite(last_msg, strlen(last_msg), 1, logfp);
568 if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
569 fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
570 last_count = 1;
571 last_lvl = lvl;
572 show_time_host_and_name(lvl); /* mimic syslog header */
573 fwrite(msg, ptr - msg, 1, logfp);
574 fflush(logfp);
576 break;
583 * Display current debug options
585 void
586 show_opts(int ch, struct opt_tab *opts)
588 int i;
589 int s = '{';
591 fprintf(stderr, "\t[-%c {no}", ch);
592 for (i = 0; opts[i].opt; i++) {
593 fprintf(stderr, "%c%s", s, opts[i].opt);
594 s = ',';
596 fputs("}]\n", stderr);
601 cmdoption(char *s, struct opt_tab *optb, u_int *flags)
603 char *p = s;
604 int errs = 0;
606 while (p && *p) {
607 int neg;
608 char *opt;
609 struct opt_tab *dp, *dpn = NULL;
611 s = p;
612 p = strchr(p, ',');
613 if (p)
614 *p = '\0';
616 /* check for "no" prefix to options */
617 if (s[0] == 'n' && s[1] == 'o') {
618 opt = s + 2;
619 neg = 1;
620 } else {
621 opt = s;
622 neg = 0;
626 * Scan the array of debug options to find the
627 * corresponding flag value. If it is found
628 * then set (or clear) the flag (depending on
629 * whether the option was prefixed with "no").
631 for (dp = optb; dp->opt; dp++) {
632 if (STREQ(opt, dp->opt))
633 break;
634 if (opt != s && !dpn && STREQ(s, dp->opt))
635 dpn = dp;
638 if (dp->opt || dpn) {
639 if (!dp->opt) {
640 dp = dpn;
641 neg = !neg;
643 if (neg)
644 *flags &= ~dp->flag;
645 else
646 *flags |= dp->flag;
647 } else {
649 * This will log to stderr when parsing the command line
650 * since any -l option will not yet have taken effect.
652 plog(XLOG_ERROR, "option \"%s\" not recognized", s);
653 errs++;
657 * Put the comma back
659 if (p)
660 *p++ = ',';
663 return errs;
668 * Switch on/off logging options
671 switch_option(char *opt)
673 u_int xl = xlog_level;
674 int rc = cmdoption(opt, xlog_opt, &xl);
676 if (rc) /* if got any error, don't update flags */
677 return EINVAL;
680 * Don't allow "mandatory" flags to be turned off, because
681 * we must always be able to report on flag re/setting errors.
683 if ((xl & XLOG_MANDATORY) != XLOG_MANDATORY) {
684 plog(XLOG_ERROR, "cannot turn off mandatory logging options");
685 xl |= XLOG_MANDATORY;
687 if (xlog_level != xl)
688 xlog_level = xl; /* set new flags */
689 return rc;
693 #ifdef LOG_DAEMON
695 * get syslog facility to use.
696 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
698 static int
699 get_syslog_facility(const char *logfile)
701 char *facstr;
703 /* parse facility string */
704 facstr = strchr(logfile, ':');
705 if (!facstr) /* log file was "syslog" */
706 return LOG_DAEMON;
707 facstr++;
708 if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
709 plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
710 return LOG_DAEMON;
713 #ifdef LOG_KERN
714 if (STREQ(facstr, "kern"))
715 return LOG_KERN;
716 #endif /* not LOG_KERN */
717 #ifdef LOG_USER
718 if (STREQ(facstr, "user"))
719 return LOG_USER;
720 #endif /* not LOG_USER */
721 #ifdef LOG_MAIL
722 if (STREQ(facstr, "mail"))
723 return LOG_MAIL;
724 #endif /* not LOG_MAIL */
726 if (STREQ(facstr, "daemon"))
727 return LOG_DAEMON;
729 #ifdef LOG_AUTH
730 if (STREQ(facstr, "auth"))
731 return LOG_AUTH;
732 #endif /* not LOG_AUTH */
733 #ifdef LOG_SYSLOG
734 if (STREQ(facstr, "syslog"))
735 return LOG_SYSLOG;
736 #endif /* not LOG_SYSLOG */
737 #ifdef LOG_LPR
738 if (STREQ(facstr, "lpr"))
739 return LOG_LPR;
740 #endif /* not LOG_LPR */
741 #ifdef LOG_NEWS
742 if (STREQ(facstr, "news"))
743 return LOG_NEWS;
744 #endif /* not LOG_NEWS */
745 #ifdef LOG_UUCP
746 if (STREQ(facstr, "uucp"))
747 return LOG_UUCP;
748 #endif /* not LOG_UUCP */
749 #ifdef LOG_CRON
750 if (STREQ(facstr, "cron"))
751 return LOG_CRON;
752 #endif /* not LOG_CRON */
753 #ifdef LOG_LOCAL0
754 if (STREQ(facstr, "local0"))
755 return LOG_LOCAL0;
756 #endif /* not LOG_LOCAL0 */
757 #ifdef LOG_LOCAL1
758 if (STREQ(facstr, "local1"))
759 return LOG_LOCAL1;
760 #endif /* not LOG_LOCAL1 */
761 #ifdef LOG_LOCAL2
762 if (STREQ(facstr, "local2"))
763 return LOG_LOCAL2;
764 #endif /* not LOG_LOCAL2 */
765 #ifdef LOG_LOCAL3
766 if (STREQ(facstr, "local3"))
767 return LOG_LOCAL3;
768 #endif /* not LOG_LOCAL3 */
769 #ifdef LOG_LOCAL4
770 if (STREQ(facstr, "local4"))
771 return LOG_LOCAL4;
772 #endif /* not LOG_LOCAL4 */
773 #ifdef LOG_LOCAL5
774 if (STREQ(facstr, "local5"))
775 return LOG_LOCAL5;
776 #endif /* not LOG_LOCAL5 */
777 #ifdef LOG_LOCAL6
778 if (STREQ(facstr, "local6"))
779 return LOG_LOCAL6;
780 #endif /* not LOG_LOCAL6 */
781 #ifdef LOG_LOCAL7
782 if (STREQ(facstr, "local7"))
783 return LOG_LOCAL7;
784 #endif /* not LOG_LOCAL7 */
786 /* didn't match anything else */
787 plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
788 return LOG_DAEMON;
790 #endif /* not LOG_DAEMON */
794 * Change current logfile
797 switch_to_logfile(char *logfile, int old_umask, int truncate_log)
799 FILE *new_logfp = stderr;
801 if (logfile) {
802 #ifdef HAVE_SYSLOG
803 syslogging = 0;
804 #endif /* HAVE_SYSLOG */
806 if (STREQ(logfile, "/dev/stderr"))
807 new_logfp = stderr;
808 else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
810 #ifdef HAVE_SYSLOG
811 syslogging = 1;
812 new_logfp = stderr;
813 openlog(am_get_progname(),
814 LOG_PID
815 # ifdef LOG_NOWAIT
816 | LOG_NOWAIT
817 # endif /* LOG_NOWAIT */
818 # ifdef LOG_DAEMON
819 , get_syslog_facility(logfile)
820 # endif /* LOG_DAEMON */
822 #else /* not HAVE_SYSLOG */
823 plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
824 #endif /* not HAVE_SYSLOG */
826 } else { /* regular log file */
827 (void) umask(old_umask);
828 if (truncate_log)
829 truncate(logfile, 0);
830 new_logfp = fopen(logfile, "a");
831 umask(0);
836 * If we couldn't open a new file, then continue using the old.
838 if (!new_logfp && logfile) {
839 plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
840 return 1;
844 * Close the previous file
846 if (logfp && logfp != stderr)
847 (void) fclose(logfp);
848 logfp = new_logfp;
850 if (logfile)
851 plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
852 else
853 plog(XLOG_INFO, "no logfile defined; using stderr");
855 return 0;
859 void
860 unregister_amq(void)
863 if (amuDebug(D_AMQ)) {
864 /* find which instance of amd to unregister */
865 u_long amd_prognum = get_amd_program_number();
867 if (pmap_unset(amd_prognum, AMQ_VERSION) != 1)
868 dlog("failed to de-register Amd program %lu, version %lu",
869 amd_prognum, AMQ_VERSION);
874 void
875 going_down(int rc)
877 if (foreground) {
878 if (amd_state != Start) {
879 if (amd_state != Done)
880 return;
881 unregister_amq();
885 #ifdef MOUNT_TABLE_ON_FILE
887 * Call unlock_mntlist to free any important resources such as an on-disk
888 * lock file (/etc/mtab~).
890 unlock_mntlist();
891 #endif /* MOUNT_TABLE_ON_FILE */
893 if (foreground) {
894 plog(XLOG_INFO, "Finishing with status %d", rc);
895 } else {
896 dlog("background process exiting with status %d", rc);
898 /* bye bye... */
899 exit(rc);
903 /* return the rpc program number under which amd was used */
904 u_long
905 get_amd_program_number(void)
907 return amd_program_number;
911 /* set the rpc program number used for amd */
912 void
913 set_amd_program_number(u_long program)
915 amd_program_number = program;
920 * Release the controlling tty of the process pid.
922 * Algorithm: try these in order, if available, until one of them
923 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
924 * Do not use setpgid(): on some OSs it may release the controlling tty,
925 * even if the man page does not mention it, but on other OSs it does not.
926 * Also avoid setpgrp(): it works on some systems, and on others it is
927 * identical to setpgid().
929 void
930 amu_release_controlling_tty(void)
932 int fd;
935 * In daemon mode, leaving open file descriptors to terminals or pipes
936 * can be a really bad idea.
937 * Case in point: the redhat startup script calls us through their 'initlog'
938 * program, which exits as soon as the original amd process exits. If,
939 * at some point, a misbehaved library function decides to print something
940 * to the screen, we get a SIGPIPE and die.
941 * And guess what: NIS glibc functions will attempt to print to stderr
942 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
943 * a ypserver.
945 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
946 * reopen them as /dev/null.
948 * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
950 fd = open("/dev/null", O_RDWR);
951 if (fd < 0) {
952 plog(XLOG_WARNING, "Could not open /dev/null for rw: %m");
953 } else {
954 fflush(stdin); close(0); dup2(fd, 0);
955 fflush(stdout); close(1); dup2(fd, 1);
956 fflush(stderr); close(2); dup2(fd, 2);
957 close(fd);
960 #ifdef HAVE_SETSID
961 /* XXX: one day maybe use vhangup(2) */
962 if (setsid() < 0) {
963 plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
964 } else {
965 plog(XLOG_INFO, "released controlling tty using setsid()");
966 return;
968 #endif /* HAVE_SETSID */
970 #ifdef TIOCNOTTY
971 fd = open("/dev/tty", O_RDWR);
972 if (fd < 0) {
973 /* not an error if already no controlling tty */
974 if (errno != ENXIO)
975 plog(XLOG_WARNING, "Could not open controlling tty: %m");
976 } else {
977 if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
978 plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
979 else
980 plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
981 close(fd);
983 return;
984 #endif /* not TIOCNOTTY */
986 plog(XLOG_ERROR, "unable to release controlling tty");
990 /* setup a single signal handler */
991 void
992 setup_sighandler(int signum, void (*handler)(int))
994 #ifdef HAVE_SIGACTION
995 struct sigaction sa;
996 memset(&sa, 0, sizeof(sa));
997 sa.sa_flags = 0; /* unnecessary */
998 sa.sa_handler = handler;
999 sigemptyset(&(sa.sa_mask)); /* probably unnecessary too */
1000 sigaddset(&(sa.sa_mask), signum);
1001 sigaction(signum, &sa, NULL);
1002 #else /* not HAVE_SIGACTION */
1003 (void) signal(signum, handler);
1004 #endif /* not HAVE_SIGACTION */
1009 * Return current time in seconds. If passed a non-null argyument, then
1010 * fill it in with the current time in seconds and microseconds (useful
1011 * for mtime updates).
1013 time_t
1014 clocktime(nfstime *nt)
1016 static struct timeval now; /* keep last time, as default */
1018 if (gettimeofday(&now, NULL) < 0) {
1019 plog(XLOG_ERROR, "clocktime: gettimeofday: %m");
1020 /* hack: force time to have incremented by at least 1 second */
1021 now.tv_sec++;
1023 /* copy seconds and microseconds. may demote a long to an int */
1024 if (nt) {
1025 nt->nt_seconds = (u_int) now.tv_sec;
1026 nt->nt_useconds = (u_int) now.tv_usec;
1028 return (time_t) now.tv_sec;
1033 * Make all the directories in the path.
1036 mkdirs(char *path, int mode)
1039 * take a copy in case path is in readonly store
1041 char *p2 = strdup(path);
1042 char *sp = p2;
1043 struct stat stb;
1044 int error_so_far = 0;
1047 * Skip through the string make the directories.
1048 * Mostly ignore errors - the result is tested at the end.
1050 * This assumes we are root so that we can do mkdir in a
1051 * mode 555 directory...
1053 while ((sp = strchr(sp + 1, '/'))) {
1054 *sp = '\0';
1055 if (mkdir(p2, mode) < 0) {
1056 error_so_far = errno;
1057 } else {
1058 dlog("mkdir(%s)", p2);
1060 *sp = '/';
1063 if (mkdir(p2, mode) < 0) {
1064 error_so_far = errno;
1065 } else {
1066 dlog("mkdir(%s)", p2);
1069 XFREE(p2);
1071 return stat(path, &stb) == 0 &&
1072 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1077 * Remove as many directories in the path as possible.
1078 * Give up if the directory doesn't appear to have
1079 * been created by Amd (not mode dr-x) or an rmdir
1080 * fails for any reason.
1082 void
1083 rmdirs(char *dir)
1085 char *xdp = strdup(dir);
1086 char *dp;
1088 do {
1089 struct stat stb;
1091 * Try to find out whether this was
1092 * created by amd. Do this by checking
1093 * for owner write permission.
1095 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
1096 if (rmdir(xdp) < 0) {
1097 if (errno != ENOTEMPTY &&
1098 errno != EBUSY &&
1099 errno != EEXIST &&
1100 errno != EROFS &&
1101 errno != EINVAL)
1102 plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
1103 break;
1104 } else {
1105 dlog("rmdir(%s)", xdp);
1107 } else {
1108 break;
1111 dp = strrchr(xdp, '/');
1112 if (dp)
1113 *dp = '\0';
1114 } while (dp && dp > xdp);
1116 XFREE(xdp);