xutil.c revision 131702
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 */
56FILE *logfp = NULL;
57
58static char *am_progname = "unknown";	/* "amd" */
59static char am_hostname[MAXHOSTNAMELEN + 1] = "unknown"; /* Hostname */
60pid_t am_mypid = -1;		/* process ID */
61serv_state amd_state;		/* amd's state */
62int foreground = 1;		/* 1 == this is the top-level server */
63#ifdef DEBUG
64int debug_flags = 0;
65#endif /* DEBUG */
66
67#ifdef HAVE_SYSLOG
68int syslogging;
69#endif /* HAVE_SYSLOG */
70int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
71int xlog_level_init = ~0;
72static int amd_program_number = AMQ_PROGRAM;
73
74time_t clock_valid = 0;
75time_t xclock_valid = 0;
76
77#ifdef DEBUG_MEM
78static int mem_bytes;
79static int orig_mem_bytes;
80#endif /* DEBUG_MEM */
81
82/* forward definitions */
83/* for GCC format string auditing */
84static 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 */
92struct 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 */
120struct 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
138void
139am_set_progname(char *pn)
140{
141  am_progname = pn;
142}
143
144
145const char *
146am_get_progname(void)
147{
148  return am_progname;
149}
150
151
152void
153am_set_hostname(char *hn)
154{
155  strncpy(am_hostname, hn, MAXHOSTNAMELEN);
156  am_hostname[MAXHOSTNAMELEN] = '\0';
157}
158
159
160const char *
161am_get_hostname(void)
162{
163  return am_hostname;
164}
165
166
167pid_t
168am_set_mypid(void)
169{
170  am_mypid = getpid();
171  return am_mypid;
172}
173
174
175voidp
176xmalloc(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 */
212voidp
213xzalloc(int len)
214{
215  voidp p = xmalloc(len);
216
217  if (p)
218    memset(p, 0, len);
219  return p;
220}
221
222
223voidp
224xrealloc(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)
248void
249dxfree(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
261static void
262checkup_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 */
291static const char *
292expand_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 */
315static void
316show_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 */
389int
390debug_option(char *opt)
391{
392  return cmdoption(opt, dbg_opt, &debug_flags);
393}
394
395
396void
397dplog(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
411void
412plog(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
425static void
426real_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 */
563void
564show_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
578int
579cmdoption(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 */
648int
649switch_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 */
675static int
676get_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 */
773int
774switch_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
837void
838unregister_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
848void
849going_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 */
871int
872get_amd_program_number(void)
873{
874  return amd_program_number;
875}
876
877
878/* set the rpc program number used for amd */
879void
880set_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 */
896void
897amu_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}
954