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