Merge from vendor branch AWK:
[dragonfly.git] / contrib / amd / libamu / xutil.c
1 /*
2  * Copyright (c) 1997-1999 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.8 1999/09/30 21:01:42 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 static void real_plog(int lvl, char *fmt, va_list vargs);
84
85 #ifdef DEBUG
86 /*
87  * List of debug options.
88  */
89 struct opt_tab dbg_opt[] =
90 {
91   {"all", D_ALL},               /* All */
92   {"amq", D_AMQ},               /* Register for AMQ program */
93   {"daemon", D_DAEMON},         /* Enter daemon mode */
94   {"fork", D_FORK},             /* Fork server (nofork = don't fork) */
95   {"full", D_FULL},             /* Program trace */
96   /* info service specific debugging (hesiod, nis, etc) */
97   {"info", D_INFO},
98 # ifdef DEBUG_MEM
99   {"mem", D_MEM},               /* Trace memory allocations */
100 # endif /* DEBUG_MEM */
101   {"mtab", D_MTAB},             /* Use local mtab file */
102   {"str", D_STR},               /* Debug string munging */
103   {"test", D_TEST},             /* Full debug - but no daemon */
104   {"trace", D_TRACE},           /* Protocol trace */
105   {0, 0}
106 };
107 #endif /* DEBUG */
108
109 /*
110  * List of log options
111  */
112 struct opt_tab xlog_opt[] =
113 {
114   {"all", XLOG_ALL},            /* All messages */
115 #ifdef DEBUG
116   {"debug", XLOG_DEBUG},        /* Debug messages */
117 #endif /* DEBUG */              /* DEBUG */
118   {"error", XLOG_ERROR},        /* Non-fatal system errors */
119   {"fatal", XLOG_FATAL},        /* Fatal errors */
120   {"info", XLOG_INFO},          /* Information */
121   {"map", XLOG_MAP},            /* Map errors */
122   {"stats", XLOG_STATS},        /* Additional statistical information */
123   {"user", XLOG_USER},          /* Non-fatal user errors */
124   {"warn", XLOG_WARNING},       /* Warnings */
125   {"warning", XLOG_WARNING},    /* Warnings */
126   {0, 0}
127 };
128
129
130 void
131 am_set_progname(char *pn)
132 {
133   am_progname = pn;
134 }
135
136
137 const char *
138 am_get_progname(void)
139 {
140   return am_progname;
141 }
142
143
144 void
145 am_set_hostname(char *hn)
146 {
147   strncpy(am_hostname, hn, MAXHOSTNAMELEN);
148   am_hostname[MAXHOSTNAMELEN] = '\0';
149 }
150
151
152 const char *
153 am_get_hostname(void)
154 {
155   return am_hostname;
156 }
157
158
159 pid_t
160 am_set_mypid(void)
161 {
162   am_mypid = getpid();
163   return am_mypid;
164 }
165
166
167 voidp
168 xmalloc(int len)
169 {
170   voidp p;
171   int retries = 600;
172
173   /*
174    * Avoid malloc's which return NULL for malloc(0)
175    */
176   if (len == 0)
177     len = 1;
178
179   do {
180     p = (voidp) malloc((unsigned) len);
181     if (p) {
182 #if defined(DEBUG) && defined(DEBUG_MEM)
183       amuDebug(D_MEM)
184         plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p);
185 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
186       return p;
187     }
188     if (retries > 0) {
189       plog(XLOG_ERROR, "Retrying memory allocation");
190       sleep(1);
191     }
192   } while (--retries);
193
194   plog(XLOG_FATAL, "Out of memory");
195   going_down(1);
196
197   abort();
198
199   return 0;
200 }
201
202
203 /* like xmalloc, but zeros out the bytes */
204 voidp
205 xzalloc(int len)
206 {
207   voidp p = xmalloc(len);
208
209   if (p)
210     memset(p, 0, len);
211   return p;
212 }
213
214
215 voidp
216 xrealloc(voidp ptr, int len)
217 {
218 #if defined(DEBUG) && defined(DEBUG_MEM)
219   amuDebug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr);
220 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
221
222   if (len == 0)
223     len = 1;
224
225   if (ptr)
226     ptr = (voidp) realloc(ptr, (unsigned) len);
227   else
228     ptr = (voidp) xmalloc((unsigned) len);
229
230   if (!ptr) {
231     plog(XLOG_FATAL, "Out of memory in realloc");
232     going_down(1);
233     abort();
234   }
235   return ptr;
236 }
237
238
239 #if defined(DEBUG) && defined(DEBUG_MEM)
240 void
241 dxfree(char *file, int line, voidp ptr)
242 {
243   amuDebug(D_MEM)
244     plog(XLOG_DEBUG, "Free in %s:%d: block %#x", file, line, ptr);
245   /* this is the only place that must NOT use XFREE()!!! */
246   free(ptr);
247   ptr = NULL;                   /* paranoid */
248 }
249 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
250
251
252 #ifdef DEBUG_MEM
253 static void
254 checkup_mem(void)
255 {
256   struct mallinfo mi = mallinfo();
257   u_long uordbytes = mi.uordblks * 4096;
258
259   if (mem_bytes != uordbytes) {
260     if (orig_mem_bytes == 0)
261       mem_bytes = orig_mem_bytes = uordbytes;
262     else {
263       fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid);
264       if (mem_bytes < uordbytes) {
265         fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
266       } else {
267         fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
268       }
269       mem_bytes = uordbytes;
270       fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
271     }
272   }
273   malloc_verify();
274 }
275 #endif /* DEBUG_MEM */
276
277
278 /*
279  * Take a log format string and expand occurrences of %m
280  * with the current error code taken from errno.  Make sure
281  * 'e' never gets longer than maxlen characters.
282  */
283 static void
284 expand_error(char *f, char *e, int maxlen)
285 {
286   char *p, *q;
287   int error = errno;
288   int len = 0;
289
290   for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
291     if (p[0] == '%' && p[1] == 'm') {
292       const char *errstr;
293       if (error < 0 || error >= sys_nerr)
294         errstr = NULL;
295       else
296 #ifdef HAVE_STRERROR
297         errstr = strerror(error);
298 #else /* not HAVE_STRERROR */
299         errstr = sys_errlist[error];
300 #endif /* not HAVE_STRERROR */
301       if (errstr)
302         strcpy(q, errstr);
303       else
304         sprintf(q, "Error %d", error);
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 }
312
313
314 /*
315  * Output the time of day and hostname to the logfile
316  */
317 static void
318 show_time_host_and_name(int lvl)
319 {
320   static time_t last_t = 0;
321   static char *last_ctime = 0;
322   time_t t = clocktime();
323   char *sev;
324
325   if (t != last_t) {
326     last_ctime = ctime(&t);
327     last_t = t;
328   }
329   switch (lvl) {
330   case XLOG_FATAL:
331     sev = "fatal:";
332     break;
333   case XLOG_ERROR:
334     sev = "error:";
335     break;
336   case XLOG_USER:
337     sev = "user: ";
338     break;
339   case XLOG_WARNING:
340     sev = "warn: ";
341     break;
342   case XLOG_INFO:
343     sev = "info: ";
344     break;
345   case XLOG_DEBUG:
346     sev = "debug:";
347     break;
348   case XLOG_MAP:
349     sev = "map:  ";
350     break;
351   case XLOG_STATS:
352     sev = "stats:";
353     break;
354   default:
355     sev = "hmm:  ";
356     break;
357   }
358   fprintf(logfp, "%15.15s %s %s[%ld]/%s ",
359           last_ctime + 4, am_get_hostname(),
360           am_get_progname(),
361           (long) am_mypid,
362           sev);
363 }
364
365
366 #ifdef DEBUG
367 /*
368  * Switch on/off debug options
369  */
370 int
371 debug_option(char *opt)
372 {
373   return cmdoption(opt, dbg_opt, &debug_flags);
374 }
375
376
377 void
378 dplog(char *fmt, ...)
379 {
380   va_list ap;
381
382   if (!logfp)
383     logfp = stderr;             /* initialize before possible first use */
384
385   va_start(ap, fmt);
386   real_plog(XLOG_DEBUG, fmt, ap);
387   va_end(ap);
388 }
389 #endif /* DEBUG */
390
391
392 void
393 plog(int lvl, char *fmt, ...)
394 {
395   va_list ap;
396
397   if (!logfp)
398     logfp = stderr;             /* initialize before possible first use */
399
400   va_start(ap, fmt);
401   real_plog(lvl, fmt, ap);
402   va_end(ap);
403 }
404
405
406 static void
407 real_plog(int lvl, char *fmt, va_list vargs)
408 {
409   char msg[1024];
410   char efmt[1024];
411   char *ptr = msg;
412   static char last_msg[1024];
413   static int last_count = 0, last_lvl = 0;
414
415   if (!(xlog_level & lvl))
416     return;
417
418 #ifdef DEBUG_MEM
419   checkup_mem();
420 #endif /* DEBUG_MEM */
421
422   expand_error(fmt, efmt, 1024);
423
424 #ifdef HAVE_VSNPRINTF
425   vsnprintf(ptr, 1024, efmt, vargs);
426 #else /* not HAVE_VSNPRINTF */
427   /*
428    * XXX: ptr is 1024 bytes long.  It is possible to write into it
429    * more than 1024 bytes, if efmt is already large, and vargs expand
430    * as well.  This is not as safe as using vsnprintf().
431    */
432   vsprintf(ptr, efmt, vargs);
433   msg[1023] = '\0';             /* null terminate, to be sure */
434 #endif /* not HAVE_VSNPRINTF */
435
436   ptr += strlen(ptr);
437   if (ptr[-1] == '\n')
438     *--ptr = '\0';
439
440 #ifdef HAVE_SYSLOG
441   if (syslogging) {
442     switch (lvl) {              /* from mike <mcooper@usc.edu> */
443     case XLOG_FATAL:
444       lvl = LOG_CRIT;
445       break;
446     case XLOG_ERROR:
447       lvl = LOG_ERR;
448       break;
449     case XLOG_USER:
450       lvl = LOG_WARNING;
451       break;
452     case XLOG_WARNING:
453       lvl = LOG_WARNING;
454       break;
455     case XLOG_INFO:
456       lvl = LOG_INFO;
457       break;
458     case XLOG_DEBUG:
459       lvl = LOG_DEBUG;
460       break;
461     case XLOG_MAP:
462       lvl = LOG_DEBUG;
463       break;
464     case XLOG_STATS:
465       lvl = LOG_INFO;
466       break;
467     default:
468       lvl = LOG_ERR;
469       break;
470     }
471     syslog(lvl, "%s", msg);
472     return;
473   }
474 #endif /* HAVE_SYSLOG */
475
476   *ptr++ = '\n';
477   *ptr = '\0';
478
479   /*
480    * mimic syslog behavior: only write repeated strings if they differ
481    */
482   switch (last_count) {
483   case 0:                       /* never printed at all */
484     last_count = 1;
485     strncpy(last_msg, msg, 1024);
486     last_lvl = lvl;
487     show_time_host_and_name(lvl); /* mimic syslog header */
488     fwrite(msg, ptr - msg, 1, logfp);
489     fflush(logfp);
490     break;
491
492   case 1:                       /* item printed once, if same, don't repeat */
493     if (STREQ(last_msg, msg)) {
494       last_count++;
495     } else {                    /* last msg printed once, new one differs */
496       /* last_count remains at 1 */
497       strncpy(last_msg, msg, 1024);
498       last_lvl = lvl;
499       show_time_host_and_name(lvl); /* mimic syslog header */
500       fwrite(msg, ptr - msg, 1, logfp);
501       fflush(logfp);
502     }
503     break;
504
505   case 100:
506     /*
507      * Don't allow repetitions longer than 100, so you can see when something
508      * cycles like crazy.
509      */
510     show_time_host_and_name(last_lvl);
511     sprintf(last_msg, "last message repeated %d times\n", last_count);
512     fwrite(last_msg, strlen(last_msg), 1, logfp);
513     fflush(logfp);
514     last_count = 0;             /* start from scratch */
515     break;
516
517   default:                      /* item repeated multiple times */
518     if (STREQ(last_msg, msg)) {
519       last_count++;
520     } else {            /* last msg repeated+skipped, new one differs */
521       show_time_host_and_name(last_lvl);
522       sprintf(last_msg, "last message repeated %d times\n", last_count);
523       fwrite(last_msg, strlen(last_msg), 1, logfp);
524       strncpy(last_msg, msg, 1024);
525       last_count = 1;
526       last_lvl = lvl;
527       show_time_host_and_name(lvl); /* mimic syslog header */
528       fwrite(msg, ptr - msg, 1, logfp);
529       fflush(logfp);
530     }
531     break;
532   }
533
534 }
535
536
537 /*
538  * Display current debug options
539  */
540 void
541 show_opts(int ch, struct opt_tab *opts)
542 {
543   int i;
544   int s = '{';
545
546   fprintf(stderr, "\t[-%c {no}", ch);
547   for (i = 0; opts[i].opt; i++) {
548     fprintf(stderr, "%c%s", s, opts[i].opt);
549     s = ',';
550   }
551   fputs("}]\n", stderr);
552 }
553
554
555 int
556 cmdoption(char *s, struct opt_tab *optb, int *flags)
557 {
558   char *p = s;
559   int errs = 0;
560
561   while (p && *p) {
562     int neg;
563     char *opt;
564     struct opt_tab *dp, *dpn = 0;
565
566     s = p;
567     p = strchr(p, ',');
568     if (p)
569       *p = '\0';
570
571     /* check for "no" prefix to options */
572     if (s[0] == 'n' && s[1] == 'o') {
573       opt = s + 2;
574       neg = 1;
575     } else {
576       opt = s;
577       neg = 0;
578     }
579
580     /*
581      * Scan the array of debug options to find the
582      * corresponding flag value.  If it is found
583      * then set (or clear) the flag (depending on
584      * whether the option was prefixed with "no").
585      */
586     for (dp = optb; dp->opt; dp++) {
587       if (STREQ(opt, dp->opt))
588         break;
589       if (opt != s && !dpn && STREQ(s, dp->opt))
590         dpn = dp;
591     }
592
593     if (dp->opt || dpn) {
594       if (!dp->opt) {
595         dp = dpn;
596         neg = !neg;
597       }
598       if (neg)
599         *flags &= ~dp->flag;
600       else
601         *flags |= dp->flag;
602     } else {
603       /*
604        * This will log to stderr when parsing the command line
605        * since any -l option will not yet have taken effect.
606        */
607       plog(XLOG_USER, "option \"%s\" not recognized", s);
608       errs++;
609     }
610
611     /*
612      * Put the comma back
613      */
614     if (p)
615       *p++ = ',';
616   }
617
618   return errs;
619 }
620
621
622 /*
623  * Switch on/off logging options
624  */
625 int
626 switch_option(char *opt)
627 {
628   int xl = xlog_level;
629   int rc = cmdoption(opt, xlog_opt, &xl);
630
631   if (rc) {
632     rc = EINVAL;
633   } else {
634     /*
635      * Keep track of initial log level, and
636      * don't allow options to be turned off.
637      */
638     if (xlog_level_init == ~0)
639       xlog_level_init = xl;
640     else
641       xl |= xlog_level_init;
642     xlog_level = xl;
643   }
644   return rc;
645 }
646
647 #ifdef LOG_DAEMON
648 /*
649  * get syslog facility to use.
650  * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
651  */
652 static int
653 get_syslog_facility(const char *logfile)
654 {
655   char *facstr;
656
657   /* parse facility string */
658   facstr = strchr(logfile, ':');
659   if (!facstr)                  /* log file was "syslog" */
660     return LOG_DAEMON;
661   facstr++;
662   if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
663     plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
664     return LOG_DAEMON;
665   }
666
667 #ifdef LOG_KERN
668   if (STREQ(facstr, "kern"))
669       return LOG_KERN;
670 #endif /* not LOG_KERN */
671 #ifdef LOG_USER
672   if (STREQ(facstr, "user"))
673       return LOG_USER;
674 #endif /* not LOG_USER */
675 #ifdef LOG_MAIL
676   if (STREQ(facstr, "mail"))
677       return LOG_MAIL;
678 #endif /* not LOG_MAIL */
679
680   if (STREQ(facstr, "daemon"))
681       return LOG_DAEMON;
682
683 #ifdef LOG_AUTH
684   if (STREQ(facstr, "auth"))
685       return LOG_AUTH;
686 #endif /* not LOG_AUTH */
687 #ifdef LOG_SYSLOG
688   if (STREQ(facstr, "syslog"))
689       return LOG_SYSLOG;
690 #endif /* not LOG_SYSLOG */
691 #ifdef LOG_LPR
692   if (STREQ(facstr, "lpr"))
693       return LOG_LPR;
694 #endif /* not LOG_LPR */
695 #ifdef LOG_NEWS
696   if (STREQ(facstr, "news"))
697       return LOG_NEWS;
698 #endif /* not LOG_NEWS */
699 #ifdef LOG_UUCP
700   if (STREQ(facstr, "uucp"))
701       return LOG_UUCP;
702 #endif /* not LOG_UUCP */
703 #ifdef LOG_CRON
704   if (STREQ(facstr, "cron"))
705       return LOG_CRON;
706 #endif /* not LOG_CRON */
707 #ifdef LOG_LOCAL0
708   if (STREQ(facstr, "local0"))
709       return LOG_LOCAL0;
710 #endif /* not LOG_LOCAL0 */
711 #ifdef LOG_LOCAL1
712   if (STREQ(facstr, "local1"))
713       return LOG_LOCAL1;
714 #endif /* not LOG_LOCAL1 */
715 #ifdef LOG_LOCAL2
716   if (STREQ(facstr, "local2"))
717       return LOG_LOCAL2;
718 #endif /* not LOG_LOCAL2 */
719 #ifdef LOG_LOCAL3
720   if (STREQ(facstr, "local3"))
721       return LOG_LOCAL3;
722 #endif /* not LOG_LOCAL3 */
723 #ifdef LOG_LOCAL4
724   if (STREQ(facstr, "local4"))
725       return LOG_LOCAL4;
726 #endif /* not LOG_LOCAL4 */
727 #ifdef LOG_LOCAL5
728   if (STREQ(facstr, "local5"))
729       return LOG_LOCAL5;
730 #endif /* not LOG_LOCAL5 */
731 #ifdef LOG_LOCAL6
732   if (STREQ(facstr, "local6"))
733       return LOG_LOCAL6;
734 #endif /* not LOG_LOCAL6 */
735 #ifdef LOG_LOCAL7
736   if (STREQ(facstr, "local7"))
737       return LOG_LOCAL7;
738 #endif /* not LOG_LOCAL7 */
739
740   /* didn't match anything else */
741   plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
742   return LOG_DAEMON;
743 }
744 #endif /* not LOG_DAEMON */
745
746
747 /*
748  * Change current logfile
749  */
750 int
751 switch_to_logfile(char *logfile, int old_umask)
752 {
753   FILE *new_logfp = stderr;
754
755   if (logfile) {
756 #ifdef HAVE_SYSLOG
757     syslogging = 0;
758 #endif /* HAVE_SYSLOG */
759
760     if (STREQ(logfile, "/dev/stderr"))
761       new_logfp = stderr;
762     else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
763
764 #ifdef HAVE_SYSLOG
765       syslogging = 1;
766       new_logfp = stderr;
767       openlog(am_get_progname(),
768               LOG_PID
769 # ifdef LOG_CONS
770               | LOG_CONS
771 # endif /* LOG_CONS */
772 # ifdef LOG_NOWAIT
773               | LOG_NOWAIT
774 # endif /* LOG_NOWAIT */
775 # ifdef LOG_DAEMON
776               , get_syslog_facility(logfile)
777 # endif /* LOG_DAEMON */
778               );
779 #else /* not HAVE_SYSLOG */
780       plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
781 #endif /* not HAVE_SYSLOG */
782
783     } else {
784       (void) umask(old_umask);
785       new_logfp = fopen(logfile, "a");
786       umask(0);
787     }
788   }
789
790   /*
791    * If we couldn't open a new file, then continue using the old.
792    */
793   if (!new_logfp && logfile) {
794     plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
795     return 1;
796   }
797
798   /*
799    * Close the previous file
800    */
801   if (logfp && logfp != stderr)
802     (void) fclose(logfp);
803   logfp = new_logfp;
804
805   plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
806   return 0;
807 }
808
809
810 void
811 unregister_amq(void)
812 {
813 #ifdef DEBUG
814   amuDebug(D_AMQ)
815 #endif /* DEBUG */
816     /* find which instance of amd to unregister */
817     pmap_unset(get_amd_program_number(), AMQ_VERSION);
818 }
819
820
821 void
822 going_down(int rc)
823 {
824   if (foreground) {
825     if (amd_state != Start) {
826       if (amd_state != Done)
827         return;
828       unregister_amq();
829     }
830   }
831   if (foreground) {
832     plog(XLOG_INFO, "Finishing with status %d", rc);
833   } else {
834 #ifdef DEBUG
835     dlog("background process exiting with status %d", rc);
836 #endif /* DEBUG */
837   }
838
839   exit(rc);
840 }
841
842
843 /* return the rpc program number under which amd was used */
844 int
845 get_amd_program_number(void)
846 {
847   return amd_program_number;
848 }
849
850
851 /* set the rpc program number used for amd */
852 void
853 set_amd_program_number(int program)
854 {
855   amd_program_number = program;
856 }
857
858
859 /*
860  * Release the controlling tty of the process pid.
861  *
862  * Algorithm: try these in order, if available, until one of them
863  * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
864  * Do not use setpgid(): on some OSs it may release the controlling tty,
865  * even if the man page does not mention it, but on other OSs it does not.
866  * Also avoid setpgrp(): it works on some systems, and on others it is
867  * identical to setpgid().
868  */
869 void
870 amu_release_controlling_tty(void)
871 {
872 #ifdef TIOCNOTTY
873   int fd;
874 #endif /* TIOCNOTTY */
875
876 #ifdef HAVE_SETSID
877   /* XXX: one day maybe use vhangup(2) */
878   if (setsid() < 0) {
879     plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
880   } else {
881     plog(XLOG_INFO, "released controlling tty using setsid()");
882     return;
883   }
884 #endif /* HAVE_SETSID */
885
886 #ifdef TIOCNOTTY
887   fd = open("/dev/tty", O_RDWR);
888   if (fd < 0) {
889     /* not an error if already no controlling tty */
890     if (errno != ENXIO)
891       plog(XLOG_WARNING, "Could not open controlling tty: %m");
892   } else {
893     if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
894       plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
895     else
896       plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
897     close(fd);
898   }
899   return;
900 #endif /* not TIOCNOTTY */
901
902   plog(XLOG_ERROR, "unable to release controlling tty");
903 }