]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/libamu/xutil.c
This commit was generated by cvs2svn to compensate for changes in r167805,
[FreeBSD/FreeBSD.git] / contrib / amd / libamu / xutil.c
1 /*
2  * Copyright (c) 1997-2004 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  *      %W% (Berkeley) %G%
40  *
41  * $Id: xutil.c,v 1.11.2.13 2004/01/06 03:15:24 ezk Exp $
42  *
43  */
44
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif /* HAVE_CONFIG_H */
48 #include <am_defs.h>
49 #include <amu.h>
50
51 /*
52  * Logfp is the default logging device, and is initialized to stderr by
53  * default in dplog/plog below, and in
54  * amd/amfs_program.c:amfs_program_exec().
55  */
56 FILE *logfp = NULL;
57
58 static char *am_progname = "unknown";   /* "amd" */
59 static char am_hostname[MAXHOSTNAMELEN + 1] = "unknown"; /* Hostname */
60 pid_t am_mypid = -1;            /* process ID */
61 serv_state amd_state;           /* amd's state */
62 int foreground = 1;             /* 1 == this is the top-level server */
63 #ifdef DEBUG
64 int debug_flags = 0;
65 #endif /* DEBUG */
66
67 #ifdef HAVE_SYSLOG
68 int syslogging;
69 #endif /* HAVE_SYSLOG */
70 int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
71 int xlog_level_init = ~0;
72 static int amd_program_number = AMQ_PROGRAM;
73
74 time_t clock_valid = 0;
75 time_t xclock_valid = 0;
76
77 #ifdef DEBUG_MEM
78 static int mem_bytes;
79 static int orig_mem_bytes;
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 */
95   {"amq", D_AMQ},               /* Register for AMQ program */
96   {"daemon", D_DAEMON},         /* Enter daemon mode */
97   {"fork", D_FORK},             /* Fork server (nofork = don't fork) */
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 # ifdef DEBUG_MEM
105   {"mem", D_MEM},               /* Trace memory allocations */
106 # endif /* DEBUG_MEM */
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   {"test", D_TEST},             /* Full debug - but no daemon */
111   {"trace", D_TRACE},           /* Protocol trace */
112   {"xdrtrace", D_XDRTRACE},     /* Trace xdr routines */
113   {0, 0}
114 };
115 #endif /* DEBUG */
116
117 /*
118  * List of log options
119  */
120 struct opt_tab xlog_opt[] =
121 {
122   {"all", XLOG_ALL},            /* All 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   {0, 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   strncpy(am_hostname, hn, MAXHOSTNAMELEN);
156   am_hostname[MAXHOSTNAMELEN] = '\0';
157 }
158
159
160 const char *
161 am_get_hostname(void)
162 {
163   return am_hostname;
164 }
165
166
167 pid_t
168 am_set_mypid(void)
169 {
170   am_mypid = getpid();
171   return am_mypid;
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 defined(DEBUG) && defined(DEBUG_MEM)
191       amuDebug(D_MEM)
192         plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p);
193 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
194       return p;
195     }
196     if (retries > 0) {
197       plog(XLOG_ERROR, "Retrying memory allocation");
198       sleep(1);
199     }
200   } while (--retries);
201
202   plog(XLOG_FATAL, "Out of memory");
203   going_down(1);
204
205   abort();
206
207   return 0;
208 }
209
210
211 /* like xmalloc, but zeros out the bytes */
212 voidp
213 xzalloc(int len)
214 {
215   voidp p = xmalloc(len);
216
217   if (p)
218     memset(p, 0, len);
219   return p;
220 }
221
222
223 voidp
224 xrealloc(voidp ptr, int len)
225 {
226 #if defined(DEBUG) && defined(DEBUG_MEM)
227   amuDebug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr);
228 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
229
230   if (len == 0)
231     len = 1;
232
233   if (ptr)
234     ptr = (voidp) realloc(ptr, (unsigned) len);
235   else
236     ptr = (voidp) xmalloc((unsigned) len);
237
238   if (!ptr) {
239     plog(XLOG_FATAL, "Out of memory in realloc");
240     going_down(1);
241     abort();
242   }
243   return ptr;
244 }
245
246
247 #if defined(DEBUG) && defined(DEBUG_MEM)
248 void
249 dxfree(char *file, int line, voidp ptr)
250 {
251   amuDebug(D_MEM)
252     plog(XLOG_DEBUG, "Free in %s:%d: block %#x", file, line, ptr);
253   /* this is the only place that must NOT use XFREE()!!! */
254   free(ptr);
255   ptr = NULL;                   /* paranoid */
256 }
257 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
258
259
260 #ifdef DEBUG_MEM
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 /* DEBUG_MEM */
284
285
286 /*
287  * Take a log format string and expand occurrences of %m
288  * with the current error code taken from errno.  Make sure
289  * 'e' never gets longer than maxlen characters.
290  */
291 static const char *
292 expand_error(const char *f, char *e, int maxlen)
293 {
294   const char *p;
295   char *q;
296   int error = errno;
297   int len = 0;
298
299   for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
300     if (p[0] == '%' && p[1] == 'm') {
301       strcpy(q, strerror(error));
302       len += strlen(q) - 1;
303       q += strlen(q) - 1;
304       p++;
305     }
306   }
307   e[maxlen-1] = '\0';           /* null terminate, to be sure */
308   return e;
309 }
310
311
312 /*
313  * Output the time of day and hostname to the logfile
314  */
315 static void
316 show_time_host_and_name(int lvl)
317 {
318   static time_t last_t = 0;
319   static char *last_ctime = 0;
320   time_t t;
321 #ifdef HAVE_CLOCK_GETTIME
322   struct timespec ts;
323 #endif /* HAVE_CLOCK_GETTIME */
324   char nsecs[11] = "";  /* '.' + 9 digits + '\0' */
325   char *sev;
326
327 #ifdef HAVE_CLOCK_GETTIME
328   /*
329    * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
330    * returning ENOSYS.
331    */
332   if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
333     t = ts.tv_sec;
334 #ifdef DEBUG
335     amuDebug(D_HRTIME)
336       sprintf(nsecs, ".%09ld", ts.tv_nsec);
337 #endif /* DEBUG */
338   }
339   else
340 #endif /* HAVE_CLOCK_GETTIME */
341     t = clocktime();
342
343   if (t != last_t) {
344     last_ctime = ctime(&t);
345     last_t = t;
346   }
347
348   switch (lvl) {
349   case XLOG_FATAL:
350     sev = "fatal:";
351     break;
352   case XLOG_ERROR:
353     sev = "error:";
354     break;
355   case XLOG_USER:
356     sev = "user: ";
357     break;
358   case XLOG_WARNING:
359     sev = "warn: ";
360     break;
361   case XLOG_INFO:
362     sev = "info: ";
363     break;
364   case XLOG_DEBUG:
365     sev = "debug:";
366     break;
367   case XLOG_MAP:
368     sev = "map:  ";
369     break;
370   case XLOG_STATS:
371     sev = "stats:";
372     break;
373   default:
374     sev = "hmm:  ";
375     break;
376   }
377   fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
378           last_ctime + 4, nsecs, am_get_hostname(),
379           am_get_progname(),
380           (long) am_mypid,
381           sev);
382 }
383
384
385 #ifdef DEBUG
386 /*
387  * Switch on/off debug options
388  */
389 int
390 debug_option(char *opt)
391 {
392   return cmdoption(opt, dbg_opt, &debug_flags);
393 }
394
395
396 void
397 dplog(const char *fmt, ...)
398 {
399   va_list ap;
400
401   if (!logfp)
402     logfp = stderr;             /* initialize before possible first use */
403
404   va_start(ap, fmt);
405   real_plog(XLOG_DEBUG, fmt, ap);
406   va_end(ap);
407 }
408 #endif /* DEBUG */
409
410
411 void
412 plog(int lvl, const char *fmt, ...)
413 {
414   va_list ap;
415
416   if (!logfp)
417     logfp = stderr;             /* initialize before possible first use */
418
419   va_start(ap, fmt);
420   real_plog(lvl, fmt, ap);
421   va_end(ap);
422 }
423
424
425 static void
426 real_plog(int lvl, const char *fmt, va_list vargs)
427 {
428   char msg[1024];
429   char efmt[1024];
430   char *ptr = msg;
431   static char last_msg[1024];
432   static int last_count = 0, last_lvl = 0;
433
434   if (!(xlog_level & lvl))
435     return;
436
437 #ifdef DEBUG_MEM
438   checkup_mem();
439 #endif /* DEBUG_MEM */
440
441 #ifdef HAVE_VSNPRINTF
442   /*
443    * XXX: ptr is 1024 bytes long, but we may write to ptr[strlen(ptr) + 2]
444    * (to add an '\n', see code below) so we have to limit the string copy
445    * to 1023 (including the '\0').
446    */
447   vsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
448   msg[1022] = '\0';             /* null terminate, to be sure */
449 #else /* not HAVE_VSNPRINTF */
450   /*
451    * XXX: ptr is 1024 bytes long.  It is possible to write into it
452    * more than 1024 bytes, if efmt is already large, and vargs expand
453    * as well.  This is not as safe as using vsnprintf().
454    */
455   vsprintf(ptr, expand_error(fmt, efmt, 1023), vargs);
456   msg[1023] = '\0';             /* null terminate, to be sure */
457 #endif /* not HAVE_VSNPRINTF */
458
459   ptr += strlen(ptr);
460   if (ptr[-1] == '\n')
461     *--ptr = '\0';
462
463 #ifdef HAVE_SYSLOG
464   if (syslogging) {
465     switch (lvl) {              /* from mike <mcooper@usc.edu> */
466     case XLOG_FATAL:
467       lvl = LOG_CRIT;
468       break;
469     case XLOG_ERROR:
470       lvl = LOG_ERR;
471       break;
472     case XLOG_USER:
473       lvl = LOG_WARNING;
474       break;
475     case XLOG_WARNING:
476       lvl = LOG_WARNING;
477       break;
478     case XLOG_INFO:
479       lvl = LOG_INFO;
480       break;
481     case XLOG_DEBUG:
482       lvl = LOG_DEBUG;
483       break;
484     case XLOG_MAP:
485       lvl = LOG_DEBUG;
486       break;
487     case XLOG_STATS:
488       lvl = LOG_INFO;
489       break;
490     default:
491       lvl = LOG_ERR;
492       break;
493     }
494     syslog(lvl, "%s", msg);
495     return;
496   }
497 #endif /* HAVE_SYSLOG */
498
499   *ptr++ = '\n';
500   *ptr = '\0';
501
502   /*
503    * mimic syslog behavior: only write repeated strings if they differ
504    */
505   switch (last_count) {
506   case 0:                       /* never printed at all */
507     last_count = 1;
508     strncpy(last_msg, msg, 1024);
509     last_lvl = lvl;
510     show_time_host_and_name(lvl); /* mimic syslog header */
511     fwrite(msg, ptr - msg, 1, logfp);
512     fflush(logfp);
513     break;
514
515   case 1:                       /* item printed once, if same, don't repeat */
516     if (STREQ(last_msg, msg)) {
517       last_count++;
518     } else {                    /* last msg printed once, new one differs */
519       /* last_count remains at 1 */
520       strncpy(last_msg, msg, 1024);
521       last_lvl = lvl;
522       show_time_host_and_name(lvl); /* mimic syslog header */
523       fwrite(msg, ptr - msg, 1, logfp);
524       fflush(logfp);
525     }
526     break;
527
528   case 100:
529     /*
530      * Don't allow repetitions longer than 100, so you can see when something
531      * cycles like crazy.
532      */
533     show_time_host_and_name(last_lvl);
534     sprintf(last_msg, "last message repeated %d times\n", last_count);
535     fwrite(last_msg, strlen(last_msg), 1, logfp);
536     fflush(logfp);
537     last_count = 0;             /* start from scratch */
538     break;
539
540   default:                      /* item repeated multiple times */
541     if (STREQ(last_msg, msg)) {
542       last_count++;
543     } else {            /* last msg repeated+skipped, new one differs */
544       show_time_host_and_name(last_lvl);
545       sprintf(last_msg, "last message repeated %d times\n", last_count);
546       fwrite(last_msg, strlen(last_msg), 1, logfp);
547       strncpy(last_msg, msg, 1024);
548       last_count = 1;
549       last_lvl = lvl;
550       show_time_host_and_name(lvl); /* mimic syslog header */
551       fwrite(msg, ptr - msg, 1, logfp);
552       fflush(logfp);
553     }
554     break;
555   }
556
557 }
558
559
560 /*
561  * Display current debug options
562  */
563 void
564 show_opts(int ch, struct opt_tab *opts)
565 {
566   int i;
567   int s = '{';
568
569   fprintf(stderr, "\t[-%c {no}", ch);
570   for (i = 0; opts[i].opt; i++) {
571     fprintf(stderr, "%c%s", s, opts[i].opt);
572     s = ',';
573   }
574   fputs("}]\n", stderr);
575 }
576
577
578 int
579 cmdoption(char *s, struct opt_tab *optb, int *flags)
580 {
581   char *p = s;
582   int errs = 0;
583
584   while (p && *p) {
585     int neg;
586     char *opt;
587     struct opt_tab *dp, *dpn = 0;
588
589     s = p;
590     p = strchr(p, ',');
591     if (p)
592       *p = '\0';
593
594     /* check for "no" prefix to options */
595     if (s[0] == 'n' && s[1] == 'o') {
596       opt = s + 2;
597       neg = 1;
598     } else {
599       opt = s;
600       neg = 0;
601     }
602
603     /*
604      * Scan the array of debug options to find the
605      * corresponding flag value.  If it is found
606      * then set (or clear) the flag (depending on
607      * whether the option was prefixed with "no").
608      */
609     for (dp = optb; dp->opt; dp++) {
610       if (STREQ(opt, dp->opt))
611         break;
612       if (opt != s && !dpn && STREQ(s, dp->opt))
613         dpn = dp;
614     }
615
616     if (dp->opt || dpn) {
617       if (!dp->opt) {
618         dp = dpn;
619         neg = !neg;
620       }
621       if (neg)
622         *flags &= ~dp->flag;
623       else
624         *flags |= dp->flag;
625     } else {
626       /*
627        * This will log to stderr when parsing the command line
628        * since any -l option will not yet have taken effect.
629        */
630       plog(XLOG_USER, "option \"%s\" not recognized", s);
631       errs++;
632     }
633
634     /*
635      * Put the comma back
636      */
637     if (p)
638       *p++ = ',';
639   }
640
641   return errs;
642 }
643
644
645 /*
646  * Switch on/off logging options
647  */
648 int
649 switch_option(char *opt)
650 {
651   int xl = xlog_level;
652   int rc = cmdoption(opt, xlog_opt, &xl);
653
654   if (rc) {
655     rc = EINVAL;
656   } else {
657     /*
658      * Keep track of initial log level, and
659      * don't allow options to be turned off.
660      */
661     if (xlog_level_init == ~0)
662       xlog_level_init = xl;
663     else
664       xl |= xlog_level_init;
665     xlog_level = xl;
666   }
667   return rc;
668 }
669
670 #ifdef LOG_DAEMON
671 /*
672  * get syslog facility to use.
673  * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
674  */
675 static int
676 get_syslog_facility(const char *logfile)
677 {
678   char *facstr;
679
680   /* parse facility string */
681   facstr = strchr(logfile, ':');
682   if (!facstr)                  /* log file was "syslog" */
683     return LOG_DAEMON;
684   facstr++;
685   if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
686     plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
687     return LOG_DAEMON;
688   }
689
690 #ifdef LOG_KERN
691   if (STREQ(facstr, "kern"))
692       return LOG_KERN;
693 #endif /* not LOG_KERN */
694 #ifdef LOG_USER
695   if (STREQ(facstr, "user"))
696       return LOG_USER;
697 #endif /* not LOG_USER */
698 #ifdef LOG_MAIL
699   if (STREQ(facstr, "mail"))
700       return LOG_MAIL;
701 #endif /* not LOG_MAIL */
702
703   if (STREQ(facstr, "daemon"))
704       return LOG_DAEMON;
705
706 #ifdef LOG_AUTH
707   if (STREQ(facstr, "auth"))
708       return LOG_AUTH;
709 #endif /* not LOG_AUTH */
710 #ifdef LOG_SYSLOG
711   if (STREQ(facstr, "syslog"))
712       return LOG_SYSLOG;
713 #endif /* not LOG_SYSLOG */
714 #ifdef LOG_LPR
715   if (STREQ(facstr, "lpr"))
716       return LOG_LPR;
717 #endif /* not LOG_LPR */
718 #ifdef LOG_NEWS
719   if (STREQ(facstr, "news"))
720       return LOG_NEWS;
721 #endif /* not LOG_NEWS */
722 #ifdef LOG_UUCP
723   if (STREQ(facstr, "uucp"))
724       return LOG_UUCP;
725 #endif /* not LOG_UUCP */
726 #ifdef LOG_CRON
727   if (STREQ(facstr, "cron"))
728       return LOG_CRON;
729 #endif /* not LOG_CRON */
730 #ifdef LOG_LOCAL0
731   if (STREQ(facstr, "local0"))
732       return LOG_LOCAL0;
733 #endif /* not LOG_LOCAL0 */
734 #ifdef LOG_LOCAL1
735   if (STREQ(facstr, "local1"))
736       return LOG_LOCAL1;
737 #endif /* not LOG_LOCAL1 */
738 #ifdef LOG_LOCAL2
739   if (STREQ(facstr, "local2"))
740       return LOG_LOCAL2;
741 #endif /* not LOG_LOCAL2 */
742 #ifdef LOG_LOCAL3
743   if (STREQ(facstr, "local3"))
744       return LOG_LOCAL3;
745 #endif /* not LOG_LOCAL3 */
746 #ifdef LOG_LOCAL4
747   if (STREQ(facstr, "local4"))
748       return LOG_LOCAL4;
749 #endif /* not LOG_LOCAL4 */
750 #ifdef LOG_LOCAL5
751   if (STREQ(facstr, "local5"))
752       return LOG_LOCAL5;
753 #endif /* not LOG_LOCAL5 */
754 #ifdef LOG_LOCAL6
755   if (STREQ(facstr, "local6"))
756       return LOG_LOCAL6;
757 #endif /* not LOG_LOCAL6 */
758 #ifdef LOG_LOCAL7
759   if (STREQ(facstr, "local7"))
760       return LOG_LOCAL7;
761 #endif /* not LOG_LOCAL7 */
762
763   /* didn't match anything else */
764   plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
765   return LOG_DAEMON;
766 }
767 #endif /* not LOG_DAEMON */
768
769
770 /*
771  * Change current logfile
772  */
773 int
774 switch_to_logfile(char *logfile, int old_umask)
775 {
776   FILE *new_logfp = stderr;
777
778   if (logfile) {
779 #ifdef HAVE_SYSLOG
780     syslogging = 0;
781 #endif /* HAVE_SYSLOG */
782
783     if (STREQ(logfile, "/dev/stderr"))
784       new_logfp = stderr;
785     else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
786
787 #ifdef HAVE_SYSLOG
788       syslogging = 1;
789       new_logfp = stderr;
790       openlog(am_get_progname(),
791               LOG_PID
792 # ifdef LOG_CONS
793               | LOG_CONS
794 # endif /* LOG_CONS */
795 # ifdef LOG_NOWAIT
796               | LOG_NOWAIT
797 # endif /* LOG_NOWAIT */
798 # ifdef LOG_DAEMON
799               , get_syslog_facility(logfile)
800 # endif /* LOG_DAEMON */
801               );
802 #else /* not HAVE_SYSLOG */
803       plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
804 #endif /* not HAVE_SYSLOG */
805
806     } else {
807       (void) umask(old_umask);
808       new_logfp = fopen(logfile, "a");
809       umask(0);
810     }
811   }
812
813   /*
814    * If we couldn't open a new file, then continue using the old.
815    */
816   if (!new_logfp && logfile) {
817     plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
818     return 1;
819   }
820
821   /*
822    * Close the previous file
823    */
824   if (logfp && logfp != stderr)
825     (void) fclose(logfp);
826   logfp = new_logfp;
827
828   if (logfile)
829     plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
830   else
831     plog(XLOG_INFO, "no logfile defined; using stderr");
832
833   return 0;
834 }
835
836
837 void
838 unregister_amq(void)
839 {
840 #ifdef DEBUG
841   amuDebug(D_AMQ)
842 #endif /* DEBUG */
843     /* find which instance of amd to unregister */
844     pmap_unset(get_amd_program_number(), AMQ_VERSION);
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   if (foreground) {
859     plog(XLOG_INFO, "Finishing with status %d", rc);
860   } else {
861 #ifdef DEBUG
862     dlog("background process exiting with status %d", rc);
863 #endif /* DEBUG */
864   }
865
866   exit(rc);
867 }
868
869
870 /* return the rpc program number under which amd was used */
871 int
872 get_amd_program_number(void)
873 {
874   return amd_program_number;
875 }
876
877
878 /* set the rpc program number used for amd */
879 void
880 set_amd_program_number(int program)
881 {
882   amd_program_number = program;
883 }
884
885
886 /*
887  * Release the controlling tty of the process pid.
888  *
889  * Algorithm: try these in order, if available, until one of them
890  * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
891  * Do not use setpgid(): on some OSs it may release the controlling tty,
892  * even if the man page does not mention it, but on other OSs it does not.
893  * Also avoid setpgrp(): it works on some systems, and on others it is
894  * identical to setpgid().
895  */
896 void
897 amu_release_controlling_tty(void)
898 {
899 #ifdef TIOCNOTTY
900   int fd;
901 #endif /* TIOCNOTTY */
902   int tempfd;
903
904   /*
905    * In daemon mode, leaving open file descriptors to terminals or pipes
906    * can be a really bad idea.
907    * Case in point: the redhat startup script calls us through their 'initlog'
908    * program, which exits as soon as the original amd process exits. If,
909    * at some point, a misbehaved library function decides to print something
910    * to the screen, we get a SIGPIPE and die.
911    * And guess what: NIS glibc functions will attempt to print to stderr
912    * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
913    * a ypserver.
914    *
915    * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
916    * reopen them as /dev/null.
917    *
918    * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
919    */
920   tempfd = open("/dev/null", O_RDWR);
921   fflush(stdin);  close(0); dup2(tempfd, 0);
922   fflush(stdout); close(1); dup2(tempfd, 1);
923   fflush(stderr); close(2); dup2(tempfd, 2);
924   close(tempfd);
925
926 #ifdef HAVE_SETSID
927   /* XXX: one day maybe use vhangup(2) */
928   if (setsid() < 0) {
929     plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
930   } else {
931     plog(XLOG_INFO, "released controlling tty using setsid()");
932     return;
933   }
934 #endif /* HAVE_SETSID */
935
936 #ifdef TIOCNOTTY
937   fd = open("/dev/tty", O_RDWR);
938   if (fd < 0) {
939     /* not an error if already no controlling tty */
940     if (errno != ENXIO)
941       plog(XLOG_WARNING, "Could not open controlling tty: %m");
942   } else {
943     if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
944       plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
945     else
946       plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
947     close(fd);
948   }
949   return;
950 #endif /* not TIOCNOTTY */
951
952   plog(XLOG_ERROR, "unable to release controlling tty");
953 }