Initial import from FreeBSD RELENG_4:
[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   extern int sys_nerr;
287   char *p, *q;
288   int error = errno;
289   int len = 0;
290
291   for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
292     if (p[0] == '%' && p[1] == 'm') {
293       const char *errstr;
294       if (error < 0 || error >= sys_nerr)
295         errstr = NULL;
296       else
297 #ifdef HAVE_STRERROR
298         errstr = strerror(error);
299 #else /* not HAVE_STRERROR */
300         errstr = sys_errlist[error];
301 #endif /* not HAVE_STRERROR */
302       if (errstr)
303         strcpy(q, errstr);
304       else
305         sprintf(q, "Error %d", error);
306       len += strlen(q) - 1;
307       q += strlen(q) - 1;
308       p++;
309     }
310   }
311   e[maxlen-1] = '\0';           /* null terminate, to be sure */
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 = 0;
323   time_t t = clocktime();
324   char *sev;
325
326   if (t != last_t) {
327     last_ctime = ctime(&t);
328     last_t = t;
329   }
330   switch (lvl) {
331   case XLOG_FATAL:
332     sev = "fatal:";
333     break;
334   case XLOG_ERROR:
335     sev = "error:";
336     break;
337   case XLOG_USER:
338     sev = "user: ";
339     break;
340   case XLOG_WARNING:
341     sev = "warn: ";
342     break;
343   case XLOG_INFO:
344     sev = "info: ";
345     break;
346   case XLOG_DEBUG:
347     sev = "debug:";
348     break;
349   case XLOG_MAP:
350     sev = "map:  ";
351     break;
352   case XLOG_STATS:
353     sev = "stats:";
354     break;
355   default:
356     sev = "hmm:  ";
357     break;
358   }
359   fprintf(logfp, "%15.15s %s %s[%ld]/%s ",
360           last_ctime + 4, am_get_hostname(),
361           am_get_progname(),
362           (long) am_mypid,
363           sev);
364 }
365
366
367 #ifdef DEBUG
368 /*
369  * Switch on/off debug options
370  */
371 int
372 debug_option(char *opt)
373 {
374   return cmdoption(opt, dbg_opt, &debug_flags);
375 }
376
377
378 void
379 dplog(char *fmt, ...)
380 {
381   va_list ap;
382
383   if (!logfp)
384     logfp = stderr;             /* initialize before possible first use */
385
386   va_start(ap, fmt);
387   real_plog(XLOG_DEBUG, fmt, ap);
388   va_end(ap);
389 }
390 #endif /* DEBUG */
391
392
393 void
394 plog(int lvl, char *fmt, ...)
395 {
396   va_list ap;
397
398   if (!logfp)
399     logfp = stderr;             /* initialize before possible first use */
400
401   va_start(ap, fmt);
402   real_plog(lvl, fmt, ap);
403   va_end(ap);
404 }
405
406
407 static void
408 real_plog(int lvl, char *fmt, va_list vargs)
409 {
410   char msg[1024];
411   char efmt[1024];
412   char *ptr = msg;
413   static char last_msg[1024];
414   static int last_count = 0, last_lvl = 0;
415
416   if (!(xlog_level & lvl))
417     return;
418
419 #ifdef DEBUG_MEM
420   checkup_mem();
421 #endif /* DEBUG_MEM */
422
423   expand_error(fmt, efmt, 1024);
424
425 #ifdef HAVE_VSNPRINTF
426   vsnprintf(ptr, 1024, efmt, vargs);
427 #else /* not HAVE_VSNPRINTF */
428   /*
429    * XXX: ptr is 1024 bytes long.  It is possible to write into it
430    * more than 1024 bytes, if efmt is already large, and vargs expand
431    * as well.  This is not as safe as using vsnprintf().
432    */
433   vsprintf(ptr, efmt, vargs);
434   msg[1023] = '\0';             /* null terminate, to be sure */
435 #endif /* not HAVE_VSNPRINTF */
436
437   ptr += strlen(ptr);
438   if (ptr[-1] == '\n')
439     *--ptr = '\0';
440
441 #ifdef HAVE_SYSLOG
442   if (syslogging) {
443     switch (lvl) {              /* from mike <mcooper@usc.edu> */
444     case XLOG_FATAL:
445       lvl = LOG_CRIT;
446       break;
447     case XLOG_ERROR:
448       lvl = LOG_ERR;
449       break;
450     case XLOG_USER:
451       lvl = LOG_WARNING;
452       break;
453     case XLOG_WARNING:
454       lvl = LOG_WARNING;
455       break;
456     case XLOG_INFO:
457       lvl = LOG_INFO;
458       break;
459     case XLOG_DEBUG:
460       lvl = LOG_DEBUG;
461       break;
462     case XLOG_MAP:
463       lvl = LOG_DEBUG;
464       break;
465     case XLOG_STATS:
466       lvl = LOG_INFO;
467       break;
468     default:
469       lvl = LOG_ERR;
470       break;
471     }
472     syslog(lvl, "%s", msg);
473     return;
474   }
475 #endif /* HAVE_SYSLOG */
476
477   *ptr++ = '\n';
478   *ptr = '\0';
479
480   /*
481    * mimic syslog behavior: only write repeated strings if they differ
482    */
483   switch (last_count) {
484   case 0:                       /* never printed at all */
485     last_count = 1;
486     strncpy(last_msg, msg, 1024);
487     last_lvl = lvl;
488     show_time_host_and_name(lvl); /* mimic syslog header */
489     fwrite(msg, ptr - msg, 1, logfp);
490     fflush(logfp);
491     break;
492
493   case 1:                       /* item printed once, if same, don't repeat */
494     if (STREQ(last_msg, msg)) {
495       last_count++;
496     } else {                    /* last msg printed once, new one differs */
497       /* last_count remains at 1 */
498       strncpy(last_msg, msg, 1024);
499       last_lvl = lvl;
500       show_time_host_and_name(lvl); /* mimic syslog header */
501       fwrite(msg, ptr - msg, 1, logfp);
502       fflush(logfp);
503     }
504     break;
505
506   case 100:
507     /*
508      * Don't allow repetitions longer than 100, so you can see when something
509      * cycles like crazy.
510      */
511     show_time_host_and_name(last_lvl);
512     sprintf(last_msg, "last message repeated %d times\n", last_count);
513     fwrite(last_msg, strlen(last_msg), 1, logfp);
514     fflush(logfp);
515     last_count = 0;             /* start from scratch */
516     break;
517
518   default:                      /* item repeated multiple times */
519     if (STREQ(last_msg, msg)) {
520       last_count++;
521     } else {            /* last msg repeated+skipped, new one differs */
522       show_time_host_and_name(last_lvl);
523       sprintf(last_msg, "last message repeated %d times\n", last_count);
524       fwrite(last_msg, strlen(last_msg), 1, logfp);
525       strncpy(last_msg, msg, 1024);
526       last_count = 1;
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     }
532     break;
533   }
534
535 }
536
537
538 /*
539  * Display current debug options
540  */
541 void
542 show_opts(int ch, struct opt_tab *opts)
543 {
544   int i;
545   int s = '{';
546
547   fprintf(stderr, "\t[-%c {no}", ch);
548   for (i = 0; opts[i].opt; i++) {
549     fprintf(stderr, "%c%s", s, opts[i].opt);
550     s = ',';
551   }
552   fputs("}]\n", stderr);
553 }
554
555
556 int
557 cmdoption(char *s, struct opt_tab *optb, int *flags)
558 {
559   char *p = s;
560   int errs = 0;
561
562   while (p && *p) {
563     int neg;
564     char *opt;
565     struct opt_tab *dp, *dpn = 0;
566
567     s = p;
568     p = strchr(p, ',');
569     if (p)
570       *p = '\0';
571
572     /* check for "no" prefix to options */
573     if (s[0] == 'n' && s[1] == 'o') {
574       opt = s + 2;
575       neg = 1;
576     } else {
577       opt = s;
578       neg = 0;
579     }
580
581     /*
582      * Scan the array of debug options to find the
583      * corresponding flag value.  If it is found
584      * then set (or clear) the flag (depending on
585      * whether the option was prefixed with "no").
586      */
587     for (dp = optb; dp->opt; dp++) {
588       if (STREQ(opt, dp->opt))
589         break;
590       if (opt != s && !dpn && STREQ(s, dp->opt))
591         dpn = dp;
592     }
593
594     if (dp->opt || dpn) {
595       if (!dp->opt) {
596         dp = dpn;
597         neg = !neg;
598       }
599       if (neg)
600         *flags &= ~dp->flag;
601       else
602         *flags |= dp->flag;
603     } else {
604       /*
605        * This will log to stderr when parsing the command line
606        * since any -l option will not yet have taken effect.
607        */
608       plog(XLOG_USER, "option \"%s\" not recognized", s);
609       errs++;
610     }
611
612     /*
613      * Put the comma back
614      */
615     if (p)
616       *p++ = ',';
617   }
618
619   return errs;
620 }
621
622
623 /*
624  * Switch on/off logging options
625  */
626 int
627 switch_option(char *opt)
628 {
629   int xl = xlog_level;
630   int rc = cmdoption(opt, xlog_opt, &xl);
631
632   if (rc) {
633     rc = EINVAL;
634   } else {
635     /*
636      * Keep track of initial log level, and
637      * don't allow options to be turned off.
638      */
639     if (xlog_level_init == ~0)
640       xlog_level_init = xl;
641     else
642       xl |= xlog_level_init;
643     xlog_level = xl;
644   }
645   return rc;
646 }
647
648 #ifdef LOG_DAEMON
649 /*
650  * get syslog facility to use.
651  * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
652  */
653 static int
654 get_syslog_facility(const char *logfile)
655 {
656   char *facstr;
657
658   /* parse facility string */
659   facstr = strchr(logfile, ':');
660   if (!facstr)                  /* log file was "syslog" */
661     return LOG_DAEMON;
662   facstr++;
663   if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
664     plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
665     return LOG_DAEMON;
666   }
667
668 #ifdef LOG_KERN
669   if (STREQ(facstr, "kern"))
670       return LOG_KERN;
671 #endif /* not LOG_KERN */
672 #ifdef LOG_USER
673   if (STREQ(facstr, "user"))
674       return LOG_USER;
675 #endif /* not LOG_USER */
676 #ifdef LOG_MAIL
677   if (STREQ(facstr, "mail"))
678       return LOG_MAIL;
679 #endif /* not LOG_MAIL */
680
681   if (STREQ(facstr, "daemon"))
682       return LOG_DAEMON;
683
684 #ifdef LOG_AUTH
685   if (STREQ(facstr, "auth"))
686       return LOG_AUTH;
687 #endif /* not LOG_AUTH */
688 #ifdef LOG_SYSLOG
689   if (STREQ(facstr, "syslog"))
690       return LOG_SYSLOG;
691 #endif /* not LOG_SYSLOG */
692 #ifdef LOG_LPR
693   if (STREQ(facstr, "lpr"))
694       return LOG_LPR;
695 #endif /* not LOG_LPR */
696 #ifdef LOG_NEWS
697   if (STREQ(facstr, "news"))
698       return LOG_NEWS;
699 #endif /* not LOG_NEWS */
700 #ifdef LOG_UUCP
701   if (STREQ(facstr, "uucp"))
702       return LOG_UUCP;
703 #endif /* not LOG_UUCP */
704 #ifdef LOG_CRON
705   if (STREQ(facstr, "cron"))
706       return LOG_CRON;
707 #endif /* not LOG_CRON */
708 #ifdef LOG_LOCAL0
709   if (STREQ(facstr, "local0"))
710       return LOG_LOCAL0;
711 #endif /* not LOG_LOCAL0 */
712 #ifdef LOG_LOCAL1
713   if (STREQ(facstr, "local1"))
714       return LOG_LOCAL1;
715 #endif /* not LOG_LOCAL1 */
716 #ifdef LOG_LOCAL2
717   if (STREQ(facstr, "local2"))
718       return LOG_LOCAL2;
719 #endif /* not LOG_LOCAL2 */
720 #ifdef LOG_LOCAL3
721   if (STREQ(facstr, "local3"))
722       return LOG_LOCAL3;
723 #endif /* not LOG_LOCAL3 */
724 #ifdef LOG_LOCAL4
725   if (STREQ(facstr, "local4"))
726       return LOG_LOCAL4;
727 #endif /* not LOG_LOCAL4 */
728 #ifdef LOG_LOCAL5
729   if (STREQ(facstr, "local5"))
730       return LOG_LOCAL5;
731 #endif /* not LOG_LOCAL5 */
732 #ifdef LOG_LOCAL6
733   if (STREQ(facstr, "local6"))
734       return LOG_LOCAL6;
735 #endif /* not LOG_LOCAL6 */
736 #ifdef LOG_LOCAL7
737   if (STREQ(facstr, "local7"))
738       return LOG_LOCAL7;
739 #endif /* not LOG_LOCAL7 */
740
741   /* didn't match anything else */
742   plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
743   return LOG_DAEMON;
744 }
745 #endif /* not LOG_DAEMON */
746
747
748 /*
749  * Change current logfile
750  */
751 int
752 switch_to_logfile(char *logfile, int old_umask)
753 {
754   FILE *new_logfp = stderr;
755
756   if (logfile) {
757 #ifdef HAVE_SYSLOG
758     syslogging = 0;
759 #endif /* HAVE_SYSLOG */
760
761     if (STREQ(logfile, "/dev/stderr"))
762       new_logfp = stderr;
763     else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
764
765 #ifdef HAVE_SYSLOG
766       syslogging = 1;
767       new_logfp = stderr;
768       openlog(am_get_progname(),
769               LOG_PID
770 # ifdef LOG_CONS
771               | LOG_CONS
772 # endif /* LOG_CONS */
773 # ifdef LOG_NOWAIT
774               | LOG_NOWAIT
775 # endif /* LOG_NOWAIT */
776 # ifdef LOG_DAEMON
777               , get_syslog_facility(logfile)
778 # endif /* LOG_DAEMON */
779               );
780 #else /* not HAVE_SYSLOG */
781       plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
782 #endif /* not HAVE_SYSLOG */
783
784     } else {
785       (void) umask(old_umask);
786       new_logfp = fopen(logfile, "a");
787       umask(0);
788     }
789   }
790
791   /*
792    * If we couldn't open a new file, then continue using the old.
793    */
794   if (!new_logfp && logfile) {
795     plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
796     return 1;
797   }
798
799   /*
800    * Close the previous file
801    */
802   if (logfp && logfp != stderr)
803     (void) fclose(logfp);
804   logfp = new_logfp;
805
806   plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
807   return 0;
808 }
809
810
811 void
812 unregister_amq(void)
813 {
814 #ifdef DEBUG
815   amuDebug(D_AMQ)
816 #endif /* DEBUG */
817     /* find which instance of amd to unregister */
818     pmap_unset(get_amd_program_number(), AMQ_VERSION);
819 }
820
821
822 void
823 going_down(int rc)
824 {
825   if (foreground) {
826     if (amd_state != Start) {
827       if (amd_state != Done)
828         return;
829       unregister_amq();
830     }
831   }
832   if (foreground) {
833     plog(XLOG_INFO, "Finishing with status %d", rc);
834   } else {
835 #ifdef DEBUG
836     dlog("background process exiting with status %d", rc);
837 #endif /* DEBUG */
838   }
839
840   exit(rc);
841 }
842
843
844 /* return the rpc program number under which amd was used */
845 int
846 get_amd_program_number(void)
847 {
848   return amd_program_number;
849 }
850
851
852 /* set the rpc program number used for amd */
853 void
854 set_amd_program_number(int program)
855 {
856   amd_program_number = program;
857 }
858
859
860 /*
861  * Release the controlling tty of the process pid.
862  *
863  * Algorithm: try these in order, if available, until one of them
864  * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
865  * Do not use setpgid(): on some OSs it may release the controlling tty,
866  * even if the man page does not mention it, but on other OSs it does not.
867  * Also avoid setpgrp(): it works on some systems, and on others it is
868  * identical to setpgid().
869  */
870 void
871 amu_release_controlling_tty(void)
872 {
873 #ifdef TIOCNOTTY
874   int fd;
875 #endif /* TIOCNOTTY */
876
877 #ifdef HAVE_SETSID
878   /* XXX: one day maybe use vhangup(2) */
879   if (setsid() < 0) {
880     plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
881   } else {
882     plog(XLOG_INFO, "released controlling tty using setsid()");
883     return;
884   }
885 #endif /* HAVE_SETSID */
886
887 #ifdef TIOCNOTTY
888   fd = open("/dev/tty", O_RDWR);
889   if (fd < 0) {
890     /* not an error if already no controlling tty */
891     if (errno != ENXIO)
892       plog(XLOG_WARNING, "Could not open controlling tty: %m");
893   } else {
894     if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
895       plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
896     else
897       plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
898     close(fd);
899   }
900   return;
901 #endif /* not TIOCNOTTY */
902
903   plog(XLOG_ERROR, "unable to release controlling tty");
904 }