]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/amd/libamu/xutil.c
MFC r308493, r308619: Update amd from am-utils 6.1.5 to 6.2.
[FreeBSD/stable/10.git] / contrib / amd / libamu / xutil.c
1 /*
2  * Copyright (c) 1997-2014 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *
36  * File: am-utils/libamu/xutil.c
37  *
38  */
39
40 /*
41  * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
42  */
43
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif /* HAVE_CONFIG_H */
47 #include <am_defs.h>
48 #include <amu.h>
49
50 /*
51  * Logfp is the default logging device, and is initialized to stderr by
52  * default in dplog/plog below, and in
53  * amd/amfs_program.c:amfs_program_exec().
54  */
55 FILE *logfp = NULL;
56
57 static char *am_progname = "unknown";   /* "amd" */
58 static char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */
59 pid_t am_mypid = -1;            /* process ID */
60 serv_state amd_state;           /* amd's state */
61 int foreground = 1;             /* 1 == this is the top-level server */
62 u_int debug_flags = D_CONTROL;  /* set regardless if compiled with debugging */
63
64 #ifdef HAVE_SYSLOG
65 int syslogging;
66 #endif /* HAVE_SYSLOG */
67 static u_int xlog_level = XLOG_DEFAULT;
68 static u_long amd_program_number = AMQ_PROGRAM;
69
70 #ifdef DEBUG_MEM
71 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
72 static int mem_bytes;
73 static int orig_mem_bytes;
74 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
75 #endif /* DEBUG_MEM */
76
77 /* forward definitions */
78 /* for GCC format string auditing */
79 static void real_plog(int lvl, const char *fmt, va_list vargs)
80      __attribute__((__format__(__printf__, 2, 0)));
81
82
83 #ifdef DEBUG
84 /*
85  * List of debug options.
86  */
87 struct opt_tab dbg_opt[] =
88 {
89   {"all", D_ALL},               /* All non-disruptive options */
90   {"defaults", D_DEFAULT},      /* Default options */
91   {"test", D_TEST},             /* Full debug - no daemon, no fork, no amq, local mtab */
92   {"amq", D_AMQ},               /* Register for AMQ program */
93   {"daemon", D_DAEMON},         /* Enter daemon mode */
94   {"fork", D_FORK},             /* Fork server (hlfsd only) */
95   {"full", D_FULL},             /* Program trace */
96 #ifdef HAVE_CLOCK_GETTIME
97   {"hrtime", D_HRTIME},         /* Print high resolution time stamps */
98 #endif /* HAVE_CLOCK_GETTIME */
99   {"info", D_INFO},             /* info service specific debugging (hesiod, nis, etc) */
100   {"mem", D_MEM},               /* Trace memory allocations */
101   {"mtab", D_MTAB},             /* Use local mtab file */
102   {"readdir", D_READDIR},       /* Check on browsable_dirs progress */
103   {"str", D_STR},               /* Debug string munging */
104   {"trace", D_TRACE},           /* Protocol trace */
105   {"xdrtrace", D_XDRTRACE},     /* Trace xdr routines */
106   {NULL, 0}
107 };
108 #endif /* DEBUG */
109
110 /*
111  * List of log options
112  */
113 struct opt_tab xlog_opt[] =
114 {
115   {"all", XLOG_ALL},            /* All messages */
116   {"defaults", XLOG_DEFAULT},   /* Default messages */
117 #ifdef DEBUG
118   {"debug", XLOG_DEBUG},        /* Debug messages */
119 #endif /* DEBUG */              /* DEBUG */
120   {"error", XLOG_ERROR},        /* Non-fatal system errors */
121   {"fatal", XLOG_FATAL},        /* Fatal errors */
122   {"info", XLOG_INFO},          /* Information */
123   {"map", XLOG_MAP},            /* Map errors */
124   {"stats", XLOG_STATS},        /* Additional statistical information */
125   {"user", XLOG_USER},          /* Non-fatal user errors */
126   {"warn", XLOG_WARNING},       /* Warnings */
127   {"warning", XLOG_WARNING},    /* Warnings */
128   {NULL, 0}
129 };
130
131
132 void
133 am_set_progname(char *pn)
134 {
135   am_progname = pn;
136 }
137
138
139 const char *
140 am_get_progname(void)
141 {
142   return am_progname;
143 }
144
145
146 void
147 am_set_hostname(char *hn)
148 {
149   xstrlcpy(am_hostname, hn, sizeof(am_hostname));
150 }
151
152
153 const char *
154 am_get_hostname(void)
155 {
156   return am_hostname;
157 }
158
159
160 pid_t
161 am_set_mypid(void)
162 {
163   am_mypid = getpid();
164   return am_mypid;
165 }
166
167
168 long
169 get_server_pid()
170 {
171   return (long) (foreground ? am_mypid : getppid());
172 }
173
174
175 voidp
176 xmalloc(int len)
177 {
178   voidp p;
179   int retries = 600;
180
181   /*
182    * Avoid malloc's which return NULL for malloc(0)
183    */
184   if (len == 0)
185     len = 1;
186
187   do {
188     p = (voidp) malloc((unsigned) len);
189     if (p) {
190       if (amuDebug(D_MEM))
191         plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p);
192       return p;
193     }
194     if (retries > 0) {
195       plog(XLOG_ERROR, "Retrying memory allocation");
196       sleep(1);
197     }
198   } while (--retries);
199
200   plog(XLOG_FATAL, "Out of memory");
201   going_down(1);
202
203   abort();
204
205   return 0;
206 }
207
208
209 /* like xmalloc, but zeros out the bytes */
210 voidp
211 xzalloc(int len)
212 {
213   voidp p = xmalloc(len);
214
215   if (p)
216     memset(p, 0, len);
217   return p;
218 }
219
220
221 voidp
222 xrealloc(voidp ptr, int len)
223 {
224   if (amuDebug(D_MEM))
225     plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
226
227   if (len == 0)
228     len = 1;
229
230   if (ptr)
231     ptr = (voidp) realloc(ptr, (unsigned) len);
232   else
233     ptr = (voidp) xmalloc((unsigned) len);
234
235   if (!ptr) {
236     plog(XLOG_FATAL, "Out of memory in realloc");
237     going_down(1);
238     abort();
239   }
240   return ptr;
241 }
242
243
244 #ifdef DEBUG_MEM
245 void
246 dxfree(char *file, int line, voidp ptr)
247 {
248   if (amuDebug(D_MEM))
249     plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr);
250   /* this is the only place that must NOT use XFREE()!!! */
251   free(ptr);
252   ptr = NULL;                   /* paranoid */
253 }
254
255
256 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
257 static void
258 checkup_mem(void)
259 {
260   struct mallinfo mi = mallinfo();
261   u_long uordbytes = mi.uordblks * 4096;
262
263   if (mem_bytes != uordbytes) {
264     if (orig_mem_bytes == 0)
265       mem_bytes = orig_mem_bytes = uordbytes;
266     else {
267       fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid);
268       if (mem_bytes < uordbytes) {
269         fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
270       } else {
271         fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
272       }
273       mem_bytes = uordbytes;
274       fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
275     }
276   }
277   malloc_verify();
278 }
279 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
280 #endif /* DEBUG_MEM */
281
282
283 /*
284  * Take a log format string and expand occurrences of %m
285  * with the current error code taken from errno.  Make sure
286  * 'e' never gets longer than maxlen characters.
287  */
288 static const char *
289 expand_error(const char *f, char *e, size_t maxlen)
290 {
291   const char *p;
292   char *q;
293   int error = errno;
294   size_t len = 0, l;
295
296   *e = '\0';
297   for (p = f, q = e; len < maxlen && (*q = *p); len++, q++, p++) {
298     if (p[0] == '%' && p[1] == 'm') {
299       if (len >= maxlen)
300         break;
301       xstrlcpy(q, strerror(error), maxlen - len);
302       l = strlen(q);
303       if (l != 0)
304           l--;
305       len += l;
306       q += l;
307       p++;
308     }
309   }
310   e[maxlen - 1] = '\0';         /* null terminate, to be sure */
311   return e;
312 }
313
314
315 /*
316  * Output the time of day and hostname to the logfile
317  */
318 static void
319 show_time_host_and_name(int lvl)
320 {
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;
329
330   nsecs[0] = '\0';
331
332 #if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
333   /*
334    * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
335    * returning ENOSYS.
336    */
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);
341   }
342   else
343 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
344     t = clocktime(NULL);
345
346   if (t != last_t) {
347     last_ctime = ctime(&t);
348     last_t = t;
349   }
350
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;
379   }
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);
385 }
386
387
388 #ifdef DEBUG
389 /*
390  * Switch on/off debug options
391  */
392 int
393 debug_option(char *opt)
394 {
395   u_int dl = debug_flags;
396   static int initialized_debug_flags = 0;
397   int rc = cmdoption(opt, dbg_opt, &dl);
398
399   if (rc)                   /* if got any error, don't update debug flags */
400     return EINVAL;
401
402   /*
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.
407    */
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);
414   }
415   initialized_debug_flags = 1;
416   debug_flags = dl;
417
418   return rc;
419 }
420
421
422 void
423 dplog(const char *fmt, ...)
424 {
425 #ifdef HAVE_SIGACTION
426   sigset_t old, chld;
427 #else /* not HAVE_SIGACTION */
428   int mask;
429 #endif /* not HAVE_SIGACTION */
430   va_list ap;
431
432 #ifdef HAVE_SIGACTION
433   sigemptyset(&chld);
434   sigaddset(&chld, SIGCHLD);
435 #else /* not HAVE_SIGACTION */
436   mask = sigblock(sigmask(SIGCHLD));
437 #endif /* not HAVE_SIGACTION */
438
439   sigprocmask(SIG_BLOCK, &chld, &old);
440   if (!logfp)
441     logfp = stderr;             /* initialize before possible first use */
442
443   va_start(ap, fmt);
444   real_plog(XLOG_DEBUG, fmt, ap);
445   va_end(ap);
446
447 #ifdef HAVE_SIGACTION
448   sigprocmask(SIG_SETMASK, &old, NULL);
449 #else /* not HAVE_SIGACTION */
450   mask = sigblock(sigmask(SIGCHLD));
451 #endif /* not HAVE_SIGACTION */
452 }
453 #endif /* DEBUG */
454
455
456 void
457 plog(int lvl, const char *fmt, ...)
458 {
459 #ifdef HAVE_SIGACTION
460   sigset_t old, chld;
461 #else /* not HAVE_SIGACTION */
462   int mask;
463 #endif /* not HAVE_SIGACTION */
464   va_list ap;
465
466 #ifdef HAVE_SIGACTION
467   sigemptyset(&chld);
468   sigaddset(&chld, SIGCHLD);
469   sigprocmask(SIG_BLOCK, &chld, &old);
470 #else /* not HAVE_SIGACTION */
471   mask = sigblock(sigmask(SIGCHLD));
472 #endif /* not HAVE_SIGACTION */
473
474   if (!logfp)
475     logfp = stderr;             /* initialize before possible first use */
476
477   va_start(ap, fmt);
478   real_plog(lvl, fmt, ap);
479   va_end(ap);
480
481 #ifdef HAVE_SIGACTION
482   sigprocmask(SIG_SETMASK, &old, NULL);
483 #else /* not HAVE_SIGACTION */
484   sigsetmask(mask);
485 #endif /* not HAVE_SIGACTION */
486 }
487
488
489 static void
490 real_plog(int lvl, const char *fmt, va_list vargs)
491 {
492   char msg[1024];
493   char efmt[1024];
494   char *ptr = msg;
495   static char last_msg[1024];
496   static int last_count = 0, last_lvl = 0;
497
498   if (!(xlog_level & lvl))
499     return;
500
501 #ifdef DEBUG_MEM
502 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
503   checkup_mem();
504 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
505 #endif /* DEBUG_MEM */
506
507   /*
508    * Note: xvsnprintf() may call plog() if a truncation happened, but the
509    * latter has some code to break out of an infinite loop.  See comment in
510    * xsnprintf() below.
511    */
512   xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
513
514   ptr += strlen(ptr);
515   if (*(ptr-1) == '\n')
516     *--ptr = '\0';
517
518 #ifdef HAVE_SYSLOG
519   if (syslogging) {
520     switch (lvl) {              /* from mike <mcooper@usc.edu> */
521     case XLOG_FATAL:
522       lvl = LOG_CRIT;
523       break;
524     case XLOG_ERROR:
525       lvl = LOG_ERR;
526       break;
527     case XLOG_USER:
528       lvl = LOG_WARNING;
529       break;
530     case XLOG_WARNING:
531       lvl = LOG_WARNING;
532       break;
533     case XLOG_INFO:
534       lvl = LOG_INFO;
535       break;
536     case XLOG_DEBUG:
537       lvl = LOG_DEBUG;
538       break;
539     case XLOG_MAP:
540       lvl = LOG_DEBUG;
541       break;
542     case XLOG_STATS:
543       lvl = LOG_INFO;
544       break;
545     default:
546       lvl = LOG_ERR;
547       break;
548     }
549     syslog(lvl, "%s", msg);
550     return;
551   }
552 #endif /* HAVE_SYSLOG */
553
554   *ptr++ = '\n';
555   *ptr = '\0';
556
557   /*
558    * mimic syslog behavior: only write repeated strings if they differ
559    */
560   switch (last_count) {
561   case 0:                       /* never printed at all */
562     last_count = 1;
563     if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
564       fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
565     last_lvl = lvl;
566     show_time_host_and_name(lvl); /* mimic syslog header */
567     __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
568     fflush(logfp);
569     break;
570
571   case 1:                       /* item printed once, if same, don't repeat */
572     if (STREQ(last_msg, msg)) {
573       last_count++;
574     } else {                    /* last msg printed once, new one differs */
575       /* last_count remains at 1 */
576       if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
577         fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
578       last_lvl = lvl;
579       show_time_host_and_name(lvl); /* mimic syslog header */
580       __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
581       fflush(logfp);
582     }
583     break;
584
585   case 100:
586     /*
587      * Don't allow repetitions longer than 100, so you can see when something
588      * cycles like crazy.
589      */
590     show_time_host_and_name(last_lvl);
591     xsnprintf(last_msg, sizeof(last_msg),
592               "last message repeated %d times\n", last_count);
593     __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp));
594     fflush(logfp);
595     last_count = 0;             /* start from scratch */
596     break;
597
598   default:                      /* item repeated multiple times */
599     if (STREQ(last_msg, msg)) {
600       last_count++;
601     } else {            /* last msg repeated+skipped, new one differs */
602       show_time_host_and_name(last_lvl);
603       xsnprintf(last_msg, sizeof(last_msg),
604                 "last message repeated %d times\n", last_count);
605       __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp));
606       if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
607         fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
608       last_count = 1;
609       last_lvl = lvl;
610       show_time_host_and_name(lvl); /* mimic syslog header */
611       __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
612       fflush(logfp);
613     }
614     break;
615   }
616
617 }
618
619
620 /*
621  * Display current debug options
622  */
623 void
624 show_opts(int ch, struct opt_tab *opts)
625 {
626   int i;
627   int s = '{';
628
629   fprintf(stderr, "\t[-%c {no}", ch);
630   for (i = 0; opts[i].opt; i++) {
631     fprintf(stderr, "%c%s", s, opts[i].opt);
632     s = ',';
633   }
634   fputs("}]\n", stderr);
635 }
636
637
638 int
639 cmdoption(char *s, struct opt_tab *optb, u_int *flags)
640 {
641   char *p = s;
642   int errs = 0;
643
644   while (p && *p) {
645     int neg;
646     char *opt;
647     struct opt_tab *dp, *dpn = NULL;
648
649     s = p;
650     p = strchr(p, ',');
651     if (p)
652       *p = '\0';
653
654     /* check for "no" prefix to options */
655     if (s[0] == 'n' && s[1] == 'o') {
656       opt = s + 2;
657       neg = 1;
658     } else {
659       opt = s;
660       neg = 0;
661     }
662
663     /*
664      * Scan the array of debug options to find the
665      * corresponding flag value.  If it is found
666      * then set (or clear) the flag (depending on
667      * whether the option was prefixed with "no").
668      */
669     for (dp = optb; dp->opt; dp++) {
670       if (STREQ(opt, dp->opt))
671         break;
672       if (opt != s && !dpn && STREQ(s, dp->opt))
673         dpn = dp;
674     }
675
676     if (dp->opt || dpn) {
677       if (!dp->opt) {
678         dp = dpn;
679         neg = !neg;
680       }
681       if (neg)
682         *flags &= ~dp->flag;
683       else
684         *flags |= dp->flag;
685     } else {
686       /*
687        * This will log to stderr when parsing the command line
688        * since any -l option will not yet have taken effect.
689        */
690       plog(XLOG_ERROR, "option \"%s\" not recognized", s);
691       errs++;
692     }
693
694     /*
695      * Put the comma back
696      */
697     if (p)
698       *p++ = ',';
699   }
700
701   return errs;
702 }
703
704
705 /*
706  * Switch on/off logging options
707  */
708 int
709 switch_option(char *opt)
710 {
711   u_int xl = xlog_level;
712   int rc = cmdoption(opt, xlog_opt, &xl);
713
714   if (rc)                       /* if got any error, don't update flags */
715     return EINVAL;
716
717   /*
718    * Don't allow "mandatory" flags to be turned off, because
719    * we must always be able to report on flag re/setting errors.
720    */
721   if ((xl & XLOG_MANDATORY) != XLOG_MANDATORY) {
722     plog(XLOG_ERROR, "cannot turn off mandatory logging options");
723     xl |= XLOG_MANDATORY;
724   }
725   if (xlog_level != xl)
726     xlog_level = xl;            /* set new flags */
727   return rc;
728 }
729
730
731 #ifdef LOG_DAEMON
732 /*
733  * get syslog facility to use.
734  * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
735  */
736 static int
737 get_syslog_facility(const char *logfile)
738 {
739   char *facstr;
740
741   /* parse facility string */
742   facstr = strchr(logfile, ':');
743   if (!facstr)                  /* log file was "syslog" */
744     return LOG_DAEMON;
745   facstr++;
746   if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
747     plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
748     return LOG_DAEMON;
749   }
750
751 #ifdef LOG_KERN
752   if (STREQ(facstr, "kern"))
753       return LOG_KERN;
754 #endif /* not LOG_KERN */
755 #ifdef LOG_USER
756   if (STREQ(facstr, "user"))
757       return LOG_USER;
758 #endif /* not LOG_USER */
759 #ifdef LOG_MAIL
760   if (STREQ(facstr, "mail"))
761       return LOG_MAIL;
762 #endif /* not LOG_MAIL */
763
764   if (STREQ(facstr, "daemon"))
765       return LOG_DAEMON;
766
767 #ifdef LOG_AUTH
768   if (STREQ(facstr, "auth"))
769       return LOG_AUTH;
770 #endif /* not LOG_AUTH */
771 #ifdef LOG_SYSLOG
772   if (STREQ(facstr, "syslog"))
773       return LOG_SYSLOG;
774 #endif /* not LOG_SYSLOG */
775 #ifdef LOG_LPR
776   if (STREQ(facstr, "lpr"))
777       return LOG_LPR;
778 #endif /* not LOG_LPR */
779 #ifdef LOG_NEWS
780   if (STREQ(facstr, "news"))
781       return LOG_NEWS;
782 #endif /* not LOG_NEWS */
783 #ifdef LOG_UUCP
784   if (STREQ(facstr, "uucp"))
785       return LOG_UUCP;
786 #endif /* not LOG_UUCP */
787 #ifdef LOG_CRON
788   if (STREQ(facstr, "cron"))
789       return LOG_CRON;
790 #endif /* not LOG_CRON */
791 #ifdef LOG_LOCAL0
792   if (STREQ(facstr, "local0"))
793       return LOG_LOCAL0;
794 #endif /* not LOG_LOCAL0 */
795 #ifdef LOG_LOCAL1
796   if (STREQ(facstr, "local1"))
797       return LOG_LOCAL1;
798 #endif /* not LOG_LOCAL1 */
799 #ifdef LOG_LOCAL2
800   if (STREQ(facstr, "local2"))
801       return LOG_LOCAL2;
802 #endif /* not LOG_LOCAL2 */
803 #ifdef LOG_LOCAL3
804   if (STREQ(facstr, "local3"))
805       return LOG_LOCAL3;
806 #endif /* not LOG_LOCAL3 */
807 #ifdef LOG_LOCAL4
808   if (STREQ(facstr, "local4"))
809       return LOG_LOCAL4;
810 #endif /* not LOG_LOCAL4 */
811 #ifdef LOG_LOCAL5
812   if (STREQ(facstr, "local5"))
813       return LOG_LOCAL5;
814 #endif /* not LOG_LOCAL5 */
815 #ifdef LOG_LOCAL6
816   if (STREQ(facstr, "local6"))
817       return LOG_LOCAL6;
818 #endif /* not LOG_LOCAL6 */
819 #ifdef LOG_LOCAL7
820   if (STREQ(facstr, "local7"))
821       return LOG_LOCAL7;
822 #endif /* not LOG_LOCAL7 */
823
824   /* didn't match anything else */
825   plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
826   return LOG_DAEMON;
827 }
828 #endif /* not LOG_DAEMON */
829
830
831 /*
832  * Change current logfile
833  */
834 int
835 switch_to_logfile(char *logfile, int old_umask, int truncate_log)
836 {
837   FILE *new_logfp = stderr;
838
839   if (logfile) {
840 #ifdef HAVE_SYSLOG
841     syslogging = 0;
842 #endif /* HAVE_SYSLOG */
843
844     if (STREQ(logfile, "/dev/stderr"))
845       new_logfp = stderr;
846     else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
847
848 #ifdef HAVE_SYSLOG
849       syslogging = 1;
850       new_logfp = stderr;
851       openlog(am_get_progname(),
852               LOG_PID
853 # ifdef LOG_NOWAIT
854               | LOG_NOWAIT
855 # endif /* LOG_NOWAIT */
856 # ifdef LOG_DAEMON
857               , get_syslog_facility(logfile)
858 # endif /* LOG_DAEMON */
859               );
860 #else /* not HAVE_SYSLOG */
861       plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
862 #endif /* not HAVE_SYSLOG */
863
864     } else {                    /* regular log file */
865       (void) umask(old_umask);
866       if (truncate_log)
867         __IGNORE(truncate(logfile, 0));
868       new_logfp = fopen(logfile, "a");
869       umask(0);
870     }
871   }
872
873   /*
874    * If we couldn't open a new file, then continue using the old.
875    */
876   if (!new_logfp && logfile) {
877     plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
878     return 1;
879   }
880
881   /*
882    * Close the previous file
883    */
884   if (logfp && logfp != stderr)
885     (void) fclose(logfp);
886   logfp = new_logfp;
887
888   if (logfile)
889     plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
890   else
891     plog(XLOG_INFO, "no logfile defined; using stderr");
892
893   return 0;
894 }
895
896
897 void
898 unregister_amq(void)
899 {
900
901   if (amuDebug(D_AMQ)) {
902     /* find which instance of amd to unregister */
903     u_long amd_prognum = get_amd_program_number();
904
905     if (pmap_unset(amd_prognum, AMQ_VERSION) != 1)
906       dlog("failed to de-register Amd program %lu, version %lu",
907            amd_prognum, AMQ_VERSION);
908   }
909 }
910
911
912 void
913 going_down(int rc)
914 {
915   if (foreground) {
916     if (amd_state != Start) {
917       if (amd_state != Done)
918         return;
919       unregister_amq();
920     }
921   }
922
923 #ifdef MOUNT_TABLE_ON_FILE
924   /*
925    * Call unlock_mntlist to free any important resources such as an on-disk
926    * lock file (/etc/mtab~).
927    */
928   unlock_mntlist();
929 #endif /* MOUNT_TABLE_ON_FILE */
930
931   if (foreground) {
932     plog(XLOG_INFO, "Finishing with status %d", rc);
933   } else {
934     dlog("background process exiting with status %d", rc);
935   }
936   /* bye bye... */
937   exit(rc);
938 }
939
940
941 /* return the rpc program number under which amd was used */
942 u_long
943 get_amd_program_number(void)
944 {
945   return amd_program_number;
946 }
947
948
949 /* set the rpc program number used for amd */
950 void
951 set_amd_program_number(u_long program)
952 {
953   amd_program_number = program;
954 }
955
956
957 /*
958  * Release the controlling tty of the process pid.
959  *
960  * Algorithm: try these in order, if available, until one of them
961  * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
962  * Do not use setpgid(): on some OSs it may release the controlling tty,
963  * even if the man page does not mention it, but on other OSs it does not.
964  * Also avoid setpgrp(): it works on some systems, and on others it is
965  * identical to setpgid().
966  */
967 void
968 amu_release_controlling_tty(void)
969 {
970   int fd;
971
972   /*
973    * In daemon mode, leaving open file descriptors to terminals or pipes
974    * can be a really bad idea.
975    * Case in point: the redhat startup script calls us through their 'initlog'
976    * program, which exits as soon as the original amd process exits. If,
977    * at some point, a misbehaved library function decides to print something
978    * to the screen, we get a SIGPIPE and die.
979    * And guess what: NIS glibc functions will attempt to print to stderr
980    * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
981    * a ypserver.
982    *
983    * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
984    * reopen them as /dev/null.
985    *
986    * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
987    */
988   fd = open("/dev/null", O_RDWR);
989   if (fd < 0) {
990     plog(XLOG_WARNING, "Could not open /dev/null for rw: %m");
991   } else {
992     fflush(stdin);  close(0); dup2(fd, 0);
993     fflush(stdout); close(1); dup2(fd, 1);
994     fflush(stderr); close(2); dup2(fd, 2);
995     close(fd);
996   }
997
998 #ifdef HAVE_SETSID
999   /* XXX: one day maybe use vhangup(2) */
1000   if (setsid() < 0) {
1001     plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
1002   } else {
1003     plog(XLOG_INFO, "released controlling tty using setsid()");
1004     return;
1005   }
1006 #endif /* HAVE_SETSID */
1007
1008 #ifdef TIOCNOTTY
1009   fd = open("/dev/tty", O_RDWR);
1010   if (fd < 0) {
1011     /* not an error if already no controlling tty */
1012     if (errno != ENXIO)
1013       plog(XLOG_WARNING, "Could not open controlling tty: %m");
1014   } else {
1015     if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
1016       plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
1017     else
1018       plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
1019     close(fd);
1020   }
1021   return;
1022 #else
1023   plog(XLOG_ERROR, "unable to release controlling tty");
1024 #endif /* not TIOCNOTTY */
1025 }
1026
1027
1028 /* setup a single signal handler */
1029 void
1030 setup_sighandler(int signum, void (*handler)(int))
1031 {
1032 #ifdef HAVE_SIGACTION
1033   struct sigaction sa;
1034   memset(&sa, 0, sizeof(sa));
1035   sa.sa_flags = 0;              /* unnecessary */
1036   sa.sa_handler = handler;
1037   sigemptyset(&(sa.sa_mask));   /* probably unnecessary too */
1038   sigaddset(&(sa.sa_mask), signum);
1039   sigaction(signum, &sa, NULL);
1040 #else /* not HAVE_SIGACTION */
1041   (void) signal(signum, handler);
1042 #endif /* not HAVE_SIGACTION */
1043 }
1044
1045
1046 /*
1047  * Return current time in seconds.  If passed a non-null argyument, then
1048  * fill it in with the current time in seconds and microseconds (useful
1049  * for mtime updates).
1050  */
1051 time_t
1052 clocktime(nfstime *nt)
1053 {
1054   static struct timeval now;    /* keep last time, as default */
1055
1056   if (gettimeofday(&now, NULL) < 0) {
1057     plog(XLOG_ERROR, "clocktime: gettimeofday: %m");
1058     /* hack: force time to have incremented by at least 1 second */
1059     now.tv_sec++;
1060   }
1061   /* copy seconds and microseconds. may demote a long to an int */
1062   if (nt) {
1063     nt->nt_seconds = (u_int) now.tv_sec;
1064     nt->nt_useconds = (u_int) now.tv_usec;
1065   }
1066   return (time_t) now.tv_sec;
1067 }
1068
1069
1070 /*
1071  * Make all the directories in the path.
1072  */
1073 int
1074 mkdirs(char *path, int mode)
1075 {
1076   /*
1077    * take a copy in case path is in readonly store
1078    */
1079   char *p2 = xstrdup(path);
1080   char *sp = p2;
1081   struct stat stb;
1082   int error_so_far = 0;
1083
1084   /*
1085    * Skip through the string make the directories.
1086    * Mostly ignore errors - the result is tested at the end.
1087    *
1088    * This assumes we are root so that we can do mkdir in a
1089    * mode 555 directory...
1090    */
1091   while ((sp = strchr(sp + 1, '/'))) {
1092     *sp = '\0';
1093     if (mkdir(p2, mode) < 0) {
1094       error_so_far = errno;
1095     } else {
1096       dlog("mkdir(%s)", p2);
1097     }
1098     *sp = '/';
1099   }
1100
1101   if (mkdir(p2, mode) < 0) {
1102     error_so_far = errno;
1103   } else {
1104     dlog("mkdir(%s)", p2);
1105   }
1106
1107   XFREE(p2);
1108
1109   return stat(path, &stb) == 0 &&
1110     (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1111 }
1112
1113
1114 /*
1115  * Remove as many directories in the path as possible.
1116  * Give up if the directory doesn't appear to have
1117  * been created by Amd (not mode dr-x) or an rmdir
1118  * fails for any reason.
1119  */
1120 void
1121 rmdirs(char *dir)
1122 {
1123   char *xdp = xstrdup(dir);
1124   char *dp;
1125
1126   do {
1127     struct stat stb;
1128     /*
1129      * Try to find out whether this was
1130      * created by amd.  Do this by checking
1131      * for owner write permission.
1132      */
1133     if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
1134       if (rmdir(xdp) < 0) {
1135         if (errno != ENOTEMPTY &&
1136             errno != EBUSY &&
1137             errno != EEXIST &&
1138             errno != EROFS &&
1139             errno != EINVAL)
1140           plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
1141         break;
1142       } else {
1143         dlog("rmdir(%s)", xdp);
1144       }
1145     } else {
1146       break;
1147     }
1148
1149     dp = strrchr(xdp, '/');
1150     if (dp)
1151       *dp = '\0';
1152   } while (dp && dp > xdp);
1153
1154   XFREE(xdp);
1155 }
1156
1157 /*
1158  * Dup a string
1159  */
1160 char *
1161 xstrdup(const char *s)
1162 {
1163   size_t len = strlen(s);
1164   char *sp = xmalloc(len + 1);
1165   memcpy(sp, s, len + 1);
1166   return sp;
1167 }