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