xref: /netbsd-src/external/bsd/am-utils/dist/libamu/xutil.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: xutil.c,v 1.1.1.2 2009/03/20 20:26:55 christos Exp $	*/
2 
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.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
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.
28  *
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.
40  *
41  *
42  * File: am-utils/libamu/xutil.c
43  *
44  */
45 
46 /*
47  * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
48  */
49 
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amu.h>
55 
56 /*
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().
60  */
61 FILE *logfp = NULL;
62 
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 */
69 
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;
75 
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 */
82 
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)));
87 
88 
89 #ifdef DEBUG
90 /*
91  * List of debug options.
92  */
93 struct opt_tab dbg_opt[] =
94 {
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}
113 };
114 #endif /* DEBUG */
115 
116 /*
117  * List of log options
118  */
119 struct opt_tab xlog_opt[] =
120 {
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}
135 };
136 
137 
138 void
139 am_set_progname(char *pn)
140 {
141   am_progname = pn;
142 }
143 
144 
145 const char *
146 am_get_progname(void)
147 {
148   return am_progname;
149 }
150 
151 
152 void
153 am_set_hostname(char *hn)
154 {
155   xstrlcpy(am_hostname, hn, sizeof(am_hostname));
156 }
157 
158 
159 const char *
160 am_get_hostname(void)
161 {
162   return am_hostname;
163 }
164 
165 
166 pid_t
167 am_set_mypid(void)
168 {
169   am_mypid = getpid();
170   return am_mypid;
171 }
172 
173 
174 long
175 get_server_pid()
176 {
177   return (long) (foreground ? am_mypid : getppid());
178 }
179 
180 
181 voidp
182 xmalloc(int len)
183 {
184   voidp p;
185   int retries = 600;
186 
187   /*
188    * Avoid malloc's which return NULL for malloc(0)
189    */
190   if (len == 0)
191     len = 1;
192 
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;
199     }
200     if (retries > 0) {
201       plog(XLOG_ERROR, "Retrying memory allocation");
202       sleep(1);
203     }
204   } while (--retries);
205 
206   plog(XLOG_FATAL, "Out of memory");
207   going_down(1);
208 
209   abort();
210 
211   return 0;
212 }
213 
214 
215 /* like xmalloc, but zeros out the bytes */
216 voidp
217 xzalloc(int len)
218 {
219   voidp p = xmalloc(len);
220 
221   if (p)
222     memset(p, 0, len);
223   return p;
224 }
225 
226 
227 voidp
228 xrealloc(voidp ptr, int len)
229 {
230   if (amuDebug(D_MEM))
231     plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
232 
233   if (len == 0)
234     len = 1;
235 
236   if (ptr)
237     ptr = (voidp) realloc(ptr, (unsigned) len);
238   else
239     ptr = (voidp) xmalloc((unsigned) len);
240 
241   if (!ptr) {
242     plog(XLOG_FATAL, "Out of memory in realloc");
243     going_down(1);
244     abort();
245   }
246   return ptr;
247 }
248 
249 
250 #ifdef DEBUG_MEM
251 void
252 dxfree(char *file, int line, voidp ptr)
253 {
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 */
259 }
260 
261 
262 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
263 static void
264 checkup_mem(void)
265 {
266   struct mallinfo mi = mallinfo();
267   u_long uordbytes = mi.uordblks * 4096;
268 
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);
278       }
279       mem_bytes = uordbytes;
280       fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
281     }
282   }
283   malloc_verify();
284 }
285 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
286 #endif /* DEBUG_MEM */
287 
288 
289 /*
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.
293  */
294 static const char *
295 expand_error(const char *f, char *e, size_t maxlen)
296 {
297   const char *p;
298   char *q;
299   int error = errno;
300   int len = 0;
301 
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++;
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   va_list ap;
426 
427   if (!logfp)
428     logfp = stderr;		/* initialize before possible first use */
429 
430   va_start(ap, fmt);
431   real_plog(XLOG_DEBUG, fmt, ap);
432   va_end(ap);
433 }
434 #endif /* DEBUG */
435 
436 
437 void
438 plog(int lvl, const char *fmt, ...)
439 {
440   va_list ap;
441 
442   if (!logfp)
443     logfp = stderr;		/* initialize before possible first use */
444 
445   va_start(ap, fmt);
446   real_plog(lvl, fmt, ap);
447   va_end(ap);
448 }
449 
450 
451 static void
452 real_plog(int lvl, const char *fmt, va_list vargs)
453 {
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;
459 
460   if (!(xlog_level & lvl))
461     return;
462 
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 */
468 
469   /*
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.
473    */
474   xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
475 
476   ptr += strlen(ptr);
477   if (*(ptr-1) == '\n')
478     *--ptr = '\0';
479 
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;
510     }
511     syslog(lvl, "%s", msg);
512     return;
513   }
514 #endif /* HAVE_SYSLOG */
515 
516   *ptr++ = '\n';
517   *ptr = '\0';
518 
519   /*
520    * mimic syslog behavior: only write repeated strings if they differ
521    */
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;
532 
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);
544     }
545     break;
546 
547   case 100:
548     /*
549      * Don't allow repetitions longer than 100, so you can see when something
550      * cycles like crazy.
551      */
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;
559 
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);
575     }
576     break;
577   }
578 
579 }
580 
581 
582 /*
583  * Display current debug options
584  */
585 void
586 show_opts(int ch, struct opt_tab *opts)
587 {
588   int i;
589   int s = '{';
590 
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 = ',';
595   }
596   fputs("}]\n", stderr);
597 }
598 
599 
600 int
601 cmdoption(char *s, struct opt_tab *optb, u_int *flags)
602 {
603   char *p = s;
604   int errs = 0;
605 
606   while (p && *p) {
607     int neg;
608     char *opt;
609     struct opt_tab *dp, *dpn = NULL;
610 
611     s = p;
612     p = strchr(p, ',');
613     if (p)
614       *p = '\0';
615 
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;
623     }
624 
625     /*
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").
630      */
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;
636     }
637 
638     if (dp->opt || dpn) {
639       if (!dp->opt) {
640 	dp = dpn;
641 	neg = !neg;
642       }
643       if (neg)
644 	*flags &= ~dp->flag;
645       else
646 	*flags |= dp->flag;
647     } else {
648       /*
649        * This will log to stderr when parsing the command line
650        * since any -l option will not yet have taken effect.
651        */
652       plog(XLOG_ERROR, "option \"%s\" not recognized", s);
653       errs++;
654     }
655 
656     /*
657      * Put the comma back
658      */
659     if (p)
660       *p++ = ',';
661   }
662 
663   return errs;
664 }
665 
666 
667 /*
668  * Switch on/off logging options
669  */
670 int
671 switch_option(char *opt)
672 {
673   u_int xl = xlog_level;
674   int rc = cmdoption(opt, xlog_opt, &xl);
675 
676   if (rc)			/* if got any error, don't update flags */
677     return EINVAL;
678 
679   /*
680    * Don't allow "mandatory" flags to be turned off, because
681    * we must always be able to report on flag re/setting errors.
682    */
683   if ((xl & XLOG_MANDATORY) != XLOG_MANDATORY) {
684     plog(XLOG_ERROR, "cannot turn off mandatory logging options");
685     xl |= XLOG_MANDATORY;
686   }
687   if (xlog_level != xl)
688     xlog_level = xl;		/* set new flags */
689   return rc;
690 }
691 
692 
693 #ifdef LOG_DAEMON
694 /*
695  * get syslog facility to use.
696  * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
697  */
698 static int
699 get_syslog_facility(const char *logfile)
700 {
701   char *facstr;
702 
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;
711   }
712 
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 */
725 
726   if (STREQ(facstr, "daemon"))
727       return LOG_DAEMON;
728 
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 */
785 
786   /* didn't match anything else */
787   plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
788   return LOG_DAEMON;
789 }
790 #endif /* not LOG_DAEMON */
791 
792 
793 /*
794  * Change current logfile
795  */
796 int
797 switch_to_logfile(char *logfile, int old_umask, int truncate_log)
798 {
799   FILE *new_logfp = stderr;
800 
801   if (logfile) {
802 #ifdef HAVE_SYSLOG
803     syslogging = 0;
804 #endif /* HAVE_SYSLOG */
805 
806     if (STREQ(logfile, "/dev/stderr"))
807       new_logfp = stderr;
808     else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
809 
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 */
821 	      );
822 #else /* not HAVE_SYSLOG */
823       plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
824 #endif /* not HAVE_SYSLOG */
825 
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);
832     }
833   }
834 
835   /*
836    * If we couldn't open a new file, then continue using the old.
837    */
838   if (!new_logfp && logfile) {
839     plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
840     return 1;
841   }
842 
843   /*
844    * Close the previous file
845    */
846   if (logfp && logfp != stderr)
847     (void) fclose(logfp);
848   logfp = new_logfp;
849 
850   if (logfile)
851     plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
852   else
853     plog(XLOG_INFO, "no logfile defined; using stderr");
854 
855   return 0;
856 }
857 
858 
859 void
860 unregister_amq(void)
861 {
862 
863   if (amuDebug(D_AMQ)) {
864     /* find which instance of amd to unregister */
865     u_long amd_prognum = get_amd_program_number();
866 
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);
870   }
871 }
872 
873 
874 void
875 going_down(int rc)
876 {
877   if (foreground) {
878     if (amd_state != Start) {
879       if (amd_state != Done)
880 	return;
881       unregister_amq();
882     }
883   }
884 
885 #ifdef MOUNT_TABLE_ON_FILE
886   /*
887    * Call unlock_mntlist to free any important resources such as an on-disk
888    * lock file (/etc/mtab~).
889    */
890   unlock_mntlist();
891 #endif /* MOUNT_TABLE_ON_FILE */
892 
893   if (foreground) {
894     plog(XLOG_INFO, "Finishing with status %d", rc);
895   } else {
896     dlog("background process exiting with status %d", rc);
897   }
898   /* bye bye... */
899   exit(rc);
900 }
901 
902 
903 /* return the rpc program number under which amd was used */
904 u_long
905 get_amd_program_number(void)
906 {
907   return amd_program_number;
908 }
909 
910 
911 /* set the rpc program number used for amd */
912 void
913 set_amd_program_number(u_long program)
914 {
915   amd_program_number = program;
916 }
917 
918 
919 /*
920  * Release the controlling tty of the process pid.
921  *
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().
928  */
929 void
930 amu_release_controlling_tty(void)
931 {
932   int fd;
933 
934   /*
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.
944    *
945    * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
946    * reopen them as /dev/null.
947    *
948    * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
949    */
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);
958   }
959 
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;
967   }
968 #endif /* HAVE_SETSID */
969 
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);
982   }
983   return;
984 #endif /* not TIOCNOTTY */
985 
986   plog(XLOG_ERROR, "unable to release controlling tty");
987 }
988 
989 
990 /* setup a single signal handler */
991 void
992 setup_sighandler(int signum, void (*handler)(int))
993 {
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 */
1005 }
1006 
1007 
1008 /*
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).
1012  */
1013 time_t
1014 clocktime(nfstime *nt)
1015 {
1016   static struct timeval now;	/* keep last time, as default */
1017 
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++;
1022   }
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;
1027   }
1028   return (time_t) now.tv_sec;
1029 }
1030 
1031 
1032 /*
1033  * Make all the directories in the path.
1034  */
1035 int
1036 mkdirs(char *path, int mode)
1037 {
1038   /*
1039    * take a copy in case path is in readonly store
1040    */
1041   char *p2 = strdup(path);
1042   char *sp = p2;
1043   struct stat stb;
1044   int error_so_far = 0;
1045 
1046   /*
1047    * Skip through the string make the directories.
1048    * Mostly ignore errors - the result is tested at the end.
1049    *
1050    * This assumes we are root so that we can do mkdir in a
1051    * mode 555 directory...
1052    */
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);
1059     }
1060     *sp = '/';
1061   }
1062 
1063   if (mkdir(p2, mode) < 0) {
1064     error_so_far = errno;
1065   } else {
1066     dlog("mkdir(%s)", p2);
1067   }
1068 
1069   XFREE(p2);
1070 
1071   return stat(path, &stb) == 0 &&
1072     (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1073 }
1074 
1075 
1076 /*
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.
1081  */
1082 void
1083 rmdirs(char *dir)
1084 {
1085   char *xdp = strdup(dir);
1086   char *dp;
1087 
1088   do {
1089     struct stat stb;
1090     /*
1091      * Try to find out whether this was
1092      * created by amd.  Do this by checking
1093      * for owner write permission.
1094      */
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);
1106       }
1107     } else {
1108       break;
1109     }
1110 
1111     dp = strrchr(xdp, '/');
1112     if (dp)
1113       *dp = '\0';
1114   } while (dp && dp > xdp);
1115 
1116   XFREE(xdp);
1117 }
1118 
1119