1/*
2 * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *
36 * File: am-utils/libamu/xutil.c
37 *
38 */
39
40/*
41 * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
42 */
43
44#ifdef HAVE_CONFIG_H
45# include <config.h>
46#endif /* HAVE_CONFIG_H */
47#include <am_defs.h>
48#include <amu.h>
49
50/*
51 * Logfp is the default logging device, and is initialized to stderr by
52 * default in dplog/plog below, and in
53 * amd/amfs_program.c:amfs_program_exec().
54 */
55FILE *logfp = NULL;
56
57static char *am_progname = "unknown";	/* "amd" */
58static char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */
59pid_t am_mypid = -1;		/* process ID */
60serv_state amd_state;		/* amd's state */
61int foreground = 1;		/* 1 == this is the top-level server */
62u_int debug_flags = D_CONTROL;	/* set regardless if compiled with debugging */
63
64#ifdef HAVE_SYSLOG
65int syslogging;
66#endif /* HAVE_SYSLOG */
67static u_int xlog_level = XLOG_DEFAULT;
68static u_long amd_program_number = AMQ_PROGRAM;
69
70#ifdef DEBUG_MEM
71# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
72static int mem_bytes;
73static int orig_mem_bytes;
74# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
75#endif /* DEBUG_MEM */
76
77/* forward definitions */
78/* for GCC format string auditing */
79static void real_plog(int lvl, const char *fmt, va_list vargs)
80     __attribute__((__format__(__printf__, 2, 0)));
81
82
83#ifdef DEBUG
84/*
85 * List of debug options.
86 */
87struct opt_tab dbg_opt[] =
88{
89  {"all", D_ALL},		/* All non-disruptive options */
90  {"defaults", D_DEFAULT},	/* Default options */
91  {"test", D_TEST},		/* Full debug - no daemon, no fork, no amq, local mtab */
92  {"amq", D_AMQ},		/* Register for AMQ program */
93  {"daemon", D_DAEMON},		/* Enter daemon mode */
94  {"fork", D_FORK},		/* Fork server (hlfsd only) */
95  {"full", D_FULL},		/* Program trace */
96#ifdef HAVE_CLOCK_GETTIME
97  {"hrtime", D_HRTIME},		/* Print high resolution time stamps */
98#endif /* HAVE_CLOCK_GETTIME */
99  {"info", D_INFO},		/* info service specific debugging (hesiod, nis, etc) */
100  {"mem", D_MEM},		/* Trace memory allocations */
101  {"mtab", D_MTAB},		/* Use local mtab file */
102  {"readdir", D_READDIR},	/* Check on browsable_dirs progress */
103  {"str", D_STR},		/* Debug string munging */
104  {"trace", D_TRACE},		/* Protocol trace */
105  {"xdrtrace", D_XDRTRACE},	/* Trace xdr routines */
106  {NULL, 0}
107};
108#endif /* DEBUG */
109
110/*
111 * List of log options
112 */
113struct opt_tab xlog_opt[] =
114{
115  {"all", XLOG_ALL},		/* All messages */
116  {"defaults", XLOG_DEFAULT},	/* Default messages */
117#ifdef DEBUG
118  {"debug", XLOG_DEBUG},	/* Debug messages */
119#endif /* DEBUG */		/* DEBUG */
120  {"error", XLOG_ERROR},	/* Non-fatal system errors */
121  {"fatal", XLOG_FATAL},	/* Fatal errors */
122  {"info", XLOG_INFO},		/* Information */
123  {"map", XLOG_MAP},		/* Map errors */
124  {"stats", XLOG_STATS},	/* Additional statistical information */
125  {"user", XLOG_USER},		/* Non-fatal user errors */
126  {"warn", XLOG_WARNING},	/* Warnings */
127  {"warning", XLOG_WARNING},	/* Warnings */
128  {NULL, 0}
129};
130
131
132void
133am_set_progname(char *pn)
134{
135  am_progname = pn;
136}
137
138
139const char *
140am_get_progname(void)
141{
142  return am_progname;
143}
144
145
146void
147am_set_hostname(char *hn)
148{
149  xstrlcpy(am_hostname, hn, sizeof(am_hostname));
150}
151
152
153const char *
154am_get_hostname(void)
155{
156  return am_hostname;
157}
158
159
160pid_t
161am_set_mypid(void)
162{
163  am_mypid = getpid();
164  return am_mypid;
165}
166
167
168long
169get_server_pid()
170{
171  return (long) (foreground ? am_mypid : getppid());
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 (amuDebug(D_MEM))
191	plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p);
192      return p;
193    }
194    if (retries > 0) {
195      plog(XLOG_ERROR, "Retrying memory allocation");
196      sleep(1);
197    }
198  } while (--retries);
199
200  plog(XLOG_FATAL, "Out of memory");
201  going_down(1);
202
203  abort();
204
205  return 0;
206}
207
208
209/* like xmalloc, but zeros out the bytes */
210voidp
211xzalloc(int len)
212{
213  voidp p = xmalloc(len);
214
215  if (p)
216    memset(p, 0, len);
217  return p;
218}
219
220
221voidp
222xrealloc(voidp ptr, int len)
223{
224  if (amuDebug(D_MEM))
225    plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
226
227  if (len == 0)
228    len = 1;
229
230  if (ptr)
231    ptr = (voidp) realloc(ptr, (unsigned) len);
232  else
233    ptr = (voidp) xmalloc((unsigned) len);
234
235  if (!ptr) {
236    plog(XLOG_FATAL, "Out of memory in realloc");
237    going_down(1);
238    abort();
239  }
240  return ptr;
241}
242
243
244#ifdef DEBUG_MEM
245void
246dxfree(char *file, int line, voidp ptr)
247{
248  if (amuDebug(D_MEM))
249    plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr);
250  /* this is the only place that must NOT use XFREE()!!! */
251  free(ptr);
252  ptr = NULL;			/* paranoid */
253}
254
255
256# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
257static void
258checkup_mem(void)
259{
260  struct mallinfo mi = mallinfo();
261  u_long uordbytes = mi.uordblks * 4096;
262
263  if (mem_bytes != uordbytes) {
264    if (orig_mem_bytes == 0)
265      mem_bytes = orig_mem_bytes = uordbytes;
266    else {
267      fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid);
268      if (mem_bytes < uordbytes) {
269	fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
270      } else {
271	fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
272      }
273      mem_bytes = uordbytes;
274      fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
275    }
276  }
277  malloc_verify();
278}
279# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
280#endif /* DEBUG_MEM */
281
282
283/*
284 * Take a log format string and expand occurrences of %m
285 * with the current error code taken from errno.  Make sure
286 * 'e' never gets longer than maxlen characters.
287 */
288static const char *
289expand_error(const char *f, char *e, size_t maxlen)
290{
291  const char *p;
292  char *q;
293  int error = errno;
294  size_t len = 0, l;
295
296  *e = '\0';
297  for (p = f, q = e; len < maxlen && (*q = *p); len++, q++, p++) {
298    if (p[0] == '%' && p[1] == 'm') {
299      if (len >= maxlen)
300	break;
301      xstrlcpy(q, strerror(error), maxlen - len);
302      l = strlen(q);
303      if (l != 0)
304	  l--;
305      len += l;
306      q += l;
307      p++;
308    }
309  }
310  e[maxlen - 1] = '\0';		/* null terminate, to be sure */
311  return e;
312}
313
314
315/*
316 * Output the time of day and hostname to the logfile
317 */
318static void
319show_time_host_and_name(int lvl)
320{
321  static time_t last_t = 0;
322  static char *last_ctime = NULL;
323  time_t t;
324#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
325  struct timespec ts;
326#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
327  char nsecs[11];		/* '.' + 9 digits + '\0' */
328  char *sev;
329
330  nsecs[0] = '\0';
331
332#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
333  /*
334   * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
335   * returning ENOSYS.
336   */
337  if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
338    t = ts.tv_sec;
339    if (amuDebug(D_HRTIME))
340      xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec);
341  }
342  else
343#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
344    t = clocktime(NULL);
345
346  if (t != last_t) {
347    last_ctime = ctime(&t);
348    last_t = t;
349  }
350
351  switch (lvl) {
352  case XLOG_FATAL:
353    sev = "fatal:";
354    break;
355  case XLOG_ERROR:
356    sev = "error:";
357    break;
358  case XLOG_USER:
359    sev = "user: ";
360    break;
361  case XLOG_WARNING:
362    sev = "warn: ";
363    break;
364  case XLOG_INFO:
365    sev = "info: ";
366    break;
367  case XLOG_DEBUG:
368    sev = "debug:";
369    break;
370  case XLOG_MAP:
371    sev = "map:  ";
372    break;
373  case XLOG_STATS:
374    sev = "stats:";
375    break;
376  default:
377    sev = "hmm:  ";
378    break;
379  }
380  fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
381	  last_ctime + 4, nsecs, am_get_hostname(),
382	  am_get_progname(),
383	  (long) am_mypid,
384	  sev);
385}
386
387
388#ifdef DEBUG
389/*
390 * Switch on/off debug options
391 */
392int
393debug_option(char *opt)
394{
395  u_int dl = debug_flags;
396  static int initialized_debug_flags = 0;
397  int rc = cmdoption(opt, dbg_opt, &dl);
398
399  if (rc)		    /* if got any error, don't update debug flags */
400    return EINVAL;
401
402  /*
403   * If we already initialized the debugging flags once (via amd.conf), then
404   * don't allow "immutable" flags to be changed again (via amq -D), because
405   * they could mess Amd's state and only make sense to be set once when Amd
406   * starts.
407   */
408  if (initialized_debug_flags &&
409      debug_flags != 0 &&
410      (dl & D_IMMUTABLE) != (debug_flags & D_IMMUTABLE)) {
411    plog(XLOG_ERROR, "cannot change immutable debug flags");
412    /* undo any attempted change to an immutable flag */
413    dl = (dl & ~D_IMMUTABLE) | (debug_flags & D_IMMUTABLE);
414  }
415  initialized_debug_flags = 1;
416  debug_flags = dl;
417
418  return rc;
419}
420
421
422void
423dplog(const char *fmt, ...)
424{
425#ifdef HAVE_SIGACTION
426  sigset_t old, chld;
427#else /* not HAVE_SIGACTION */
428  int mask;
429#endif /* not HAVE_SIGACTION */
430  va_list ap;
431
432#ifdef HAVE_SIGACTION
433  sigemptyset(&chld);
434  sigaddset(&chld, SIGCHLD);
435#else /* not HAVE_SIGACTION */
436  mask = sigblock(sigmask(SIGCHLD));
437#endif /* not HAVE_SIGACTION */
438
439  sigprocmask(SIG_BLOCK, &chld, &old);
440  if (!logfp)
441    logfp = stderr;		/* initialize before possible first use */
442
443  va_start(ap, fmt);
444  real_plog(XLOG_DEBUG, fmt, ap);
445  va_end(ap);
446
447#ifdef HAVE_SIGACTION
448  sigprocmask(SIG_SETMASK, &old, NULL);
449#else /* not HAVE_SIGACTION */
450  mask = sigblock(sigmask(SIGCHLD));
451#endif /* not HAVE_SIGACTION */
452}
453#endif /* DEBUG */
454
455
456void
457plog(int lvl, const char *fmt, ...)
458{
459#ifdef HAVE_SIGACTION
460  sigset_t old, chld;
461#else /* not HAVE_SIGACTION */
462  int mask;
463#endif /* not HAVE_SIGACTION */
464  va_list ap;
465
466#ifdef HAVE_SIGACTION
467  sigemptyset(&chld);
468  sigaddset(&chld, SIGCHLD);
469  sigprocmask(SIG_BLOCK, &chld, &old);
470#else /* not HAVE_SIGACTION */
471  mask = sigblock(sigmask(SIGCHLD));
472#endif /* not HAVE_SIGACTION */
473
474  if (!logfp)
475    logfp = stderr;		/* initialize before possible first use */
476
477  va_start(ap, fmt);
478  real_plog(lvl, fmt, ap);
479  va_end(ap);
480
481#ifdef HAVE_SIGACTION
482  sigprocmask(SIG_SETMASK, &old, NULL);
483#else /* not HAVE_SIGACTION */
484  sigsetmask(mask);
485#endif /* not HAVE_SIGACTION */
486}
487
488
489static void
490real_plog(int lvl, const char *fmt, va_list vargs)
491{
492  char msg[1024];
493  char efmt[1024];
494  char *ptr = msg;
495  static char last_msg[1024];
496  static int last_count = 0, last_lvl = 0;
497
498  if (!(xlog_level & lvl))
499    return;
500
501#ifdef DEBUG_MEM
502# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
503  checkup_mem();
504# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
505#endif /* DEBUG_MEM */
506
507  /*
508   * Note: xvsnprintf() may call plog() if a truncation happened, but the
509   * latter has some code to break out of an infinite loop.  See comment in
510   * xsnprintf() below.
511   */
512  xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
513
514  ptr += strlen(ptr);
515  if (*(ptr-1) == '\n')
516    *--ptr = '\0';
517
518#ifdef HAVE_SYSLOG
519  if (syslogging) {
520    switch (lvl) {		/* from mike <mcooper@usc.edu> */
521    case XLOG_FATAL:
522      lvl = LOG_CRIT;
523      break;
524    case XLOG_ERROR:
525      lvl = LOG_ERR;
526      break;
527    case XLOG_USER:
528      lvl = LOG_WARNING;
529      break;
530    case XLOG_WARNING:
531      lvl = LOG_WARNING;
532      break;
533    case XLOG_INFO:
534      lvl = LOG_INFO;
535      break;
536    case XLOG_DEBUG:
537      lvl = LOG_DEBUG;
538      break;
539    case XLOG_MAP:
540      lvl = LOG_DEBUG;
541      break;
542    case XLOG_STATS:
543      lvl = LOG_INFO;
544      break;
545    default:
546      lvl = LOG_ERR;
547      break;
548    }
549    syslog(lvl, "%s", msg);
550    return;
551  }
552#endif /* HAVE_SYSLOG */
553
554  *ptr++ = '\n';
555  *ptr = '\0';
556
557  /*
558   * mimic syslog behavior: only write repeated strings if they differ
559   */
560  switch (last_count) {
561  case 0:			/* never printed at all */
562    last_count = 1;
563    if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
564      fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
565    last_lvl = lvl;
566    show_time_host_and_name(lvl); /* mimic syslog header */
567    __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
568    fflush(logfp);
569    break;
570
571  case 1:			/* item printed once, if same, don't repeat */
572    if (STREQ(last_msg, msg)) {
573      last_count++;
574    } else {			/* last msg printed once, new one differs */
575      /* last_count remains at 1 */
576      if (strlcpy(last_msg, msg, sizeof(last_msg)) >= sizeof(last_msg)) /* don't use xstrlcpy here (recursive!) */
577	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
578      last_lvl = lvl;
579      show_time_host_and_name(lvl); /* mimic syslog header */
580      __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
581      fflush(logfp);
582    }
583    break;
584
585  case 100:
586    /*
587     * Don't allow repetitions longer than 100, so you can see when something
588     * cycles like crazy.
589     */
590    show_time_host_and_name(last_lvl);
591    xsnprintf(last_msg, sizeof(last_msg),
592	      "last message repeated %d times\n", last_count);
593    __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp));
594    fflush(logfp);
595    last_count = 0;		/* start from scratch */
596    break;
597
598  default:			/* item repeated multiple times */
599    if (STREQ(last_msg, msg)) {
600      last_count++;
601    } else {		/* last msg repeated+skipped, new one differs */
602      show_time_host_and_name(last_lvl);
603      xsnprintf(last_msg, sizeof(last_msg),
604		"last message repeated %d times\n", last_count);
605      __IGNORE(fwrite(last_msg, strlen(last_msg), 1, logfp));
606      if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
607	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
608      last_count = 1;
609      last_lvl = lvl;
610      show_time_host_and_name(lvl); /* mimic syslog header */
611      __IGNORE(fwrite(msg, ptr - msg, 1, logfp));
612      fflush(logfp);
613    }
614    break;
615  }
616
617}
618
619
620/*
621 * Display current debug options
622 */
623void
624show_opts(int ch, struct opt_tab *opts)
625{
626  int i;
627  int s = '{';
628
629  fprintf(stderr, "\t[-%c {no}", ch);
630  for (i = 0; opts[i].opt; i++) {
631    fprintf(stderr, "%c%s", s, opts[i].opt);
632    s = ',';
633  }
634  fputs("}]\n", stderr);
635}
636
637
638int
639cmdoption(char *s, struct opt_tab *optb, u_int *flags)
640{
641  char *p = s;
642  int errs = 0;
643
644  while (p && *p) {
645    int neg;
646    char *opt;
647    struct opt_tab *dp, *dpn = NULL;
648
649    s = p;
650    p = strchr(p, ',');
651    if (p)
652      *p = '\0';
653
654    /* check for "no" prefix to options */
655    if (s[0] == 'n' && s[1] == 'o') {
656      opt = s + 2;
657      neg = 1;
658    } else {
659      opt = s;
660      neg = 0;
661    }
662
663    /*
664     * Scan the array of debug options to find the
665     * corresponding flag value.  If it is found
666     * then set (or clear) the flag (depending on
667     * whether the option was prefixed with "no").
668     */
669    for (dp = optb; dp->opt; dp++) {
670      if (STREQ(opt, dp->opt))
671	break;
672      if (opt != s && !dpn && STREQ(s, dp->opt))
673	dpn = dp;
674    }
675
676    if (dp->opt || dpn) {
677      if (!dp->opt) {
678	dp = dpn;
679	neg = !neg;
680      }
681      if (neg)
682	*flags &= ~dp->flag;
683      else
684	*flags |= dp->flag;
685    } else {
686      /*
687       * This will log to stderr when parsing the command line
688       * since any -l option will not yet have taken effect.
689       */
690      plog(XLOG_ERROR, "option \"%s\" not recognized", s);
691      errs++;
692    }
693
694    /*
695     * Put the comma back
696     */
697    if (p)
698      *p++ = ',';
699  }
700
701  return errs;
702}
703
704
705/*
706 * Switch on/off logging options
707 */
708int
709switch_option(char *opt)
710{
711  u_int xl = xlog_level;
712  int rc = cmdoption(opt, xlog_opt, &xl);
713
714  if (rc)			/* if got any error, don't update flags */
715    return EINVAL;
716
717  /*
718   * Don't allow "mandatory" flags to be turned off, because
719   * we must always be able to report on flag re/setting errors.
720   */
721  if ((xl & XLOG_MANDATORY) != XLOG_MANDATORY) {
722    plog(XLOG_ERROR, "cannot turn off mandatory logging options");
723    xl |= XLOG_MANDATORY;
724  }
725  if (xlog_level != xl)
726    xlog_level = xl;		/* set new flags */
727  return rc;
728}
729
730
731#ifdef LOG_DAEMON
732/*
733 * get syslog facility to use.
734 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
735 */
736static int
737get_syslog_facility(const char *logfile)
738{
739  char *facstr;
740
741  /* parse facility string */
742  facstr = strchr(logfile, ':');
743  if (!facstr)			/* log file was "syslog" */
744    return LOG_DAEMON;
745  facstr++;
746  if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
747    plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
748    return LOG_DAEMON;
749  }
750
751#ifdef LOG_KERN
752  if (STREQ(facstr, "kern"))
753      return LOG_KERN;
754#endif /* not LOG_KERN */
755#ifdef LOG_USER
756  if (STREQ(facstr, "user"))
757      return LOG_USER;
758#endif /* not LOG_USER */
759#ifdef LOG_MAIL
760  if (STREQ(facstr, "mail"))
761      return LOG_MAIL;
762#endif /* not LOG_MAIL */
763
764  if (STREQ(facstr, "daemon"))
765      return LOG_DAEMON;
766
767#ifdef LOG_AUTH
768  if (STREQ(facstr, "auth"))
769      return LOG_AUTH;
770#endif /* not LOG_AUTH */
771#ifdef LOG_SYSLOG
772  if (STREQ(facstr, "syslog"))
773      return LOG_SYSLOG;
774#endif /* not LOG_SYSLOG */
775#ifdef LOG_LPR
776  if (STREQ(facstr, "lpr"))
777      return LOG_LPR;
778#endif /* not LOG_LPR */
779#ifdef LOG_NEWS
780  if (STREQ(facstr, "news"))
781      return LOG_NEWS;
782#endif /* not LOG_NEWS */
783#ifdef LOG_UUCP
784  if (STREQ(facstr, "uucp"))
785      return LOG_UUCP;
786#endif /* not LOG_UUCP */
787#ifdef LOG_CRON
788  if (STREQ(facstr, "cron"))
789      return LOG_CRON;
790#endif /* not LOG_CRON */
791#ifdef LOG_LOCAL0
792  if (STREQ(facstr, "local0"))
793      return LOG_LOCAL0;
794#endif /* not LOG_LOCAL0 */
795#ifdef LOG_LOCAL1
796  if (STREQ(facstr, "local1"))
797      return LOG_LOCAL1;
798#endif /* not LOG_LOCAL1 */
799#ifdef LOG_LOCAL2
800  if (STREQ(facstr, "local2"))
801      return LOG_LOCAL2;
802#endif /* not LOG_LOCAL2 */
803#ifdef LOG_LOCAL3
804  if (STREQ(facstr, "local3"))
805      return LOG_LOCAL3;
806#endif /* not LOG_LOCAL3 */
807#ifdef LOG_LOCAL4
808  if (STREQ(facstr, "local4"))
809      return LOG_LOCAL4;
810#endif /* not LOG_LOCAL4 */
811#ifdef LOG_LOCAL5
812  if (STREQ(facstr, "local5"))
813      return LOG_LOCAL5;
814#endif /* not LOG_LOCAL5 */
815#ifdef LOG_LOCAL6
816  if (STREQ(facstr, "local6"))
817      return LOG_LOCAL6;
818#endif /* not LOG_LOCAL6 */
819#ifdef LOG_LOCAL7
820  if (STREQ(facstr, "local7"))
821      return LOG_LOCAL7;
822#endif /* not LOG_LOCAL7 */
823
824  /* didn't match anything else */
825  plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
826  return LOG_DAEMON;
827}
828#endif /* not LOG_DAEMON */
829
830
831/*
832 * Change current logfile
833 */
834int
835switch_to_logfile(char *logfile, int old_umask, int truncate_log)
836{
837  FILE *new_logfp = stderr;
838
839  if (logfile) {
840#ifdef HAVE_SYSLOG
841    syslogging = 0;
842#endif /* HAVE_SYSLOG */
843
844    if (STREQ(logfile, "/dev/stderr"))
845      new_logfp = stderr;
846    else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
847
848#ifdef HAVE_SYSLOG
849      syslogging = 1;
850      new_logfp = stderr;
851      openlog(am_get_progname(),
852	      LOG_PID
853# ifdef LOG_NOWAIT
854	      | LOG_NOWAIT
855# endif /* LOG_NOWAIT */
856# ifdef LOG_DAEMON
857	      , get_syslog_facility(logfile)
858# endif /* LOG_DAEMON */
859	      );
860#else /* not HAVE_SYSLOG */
861      plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
862#endif /* not HAVE_SYSLOG */
863
864    } else {			/* regular log file */
865      (void) umask(old_umask);
866      if (truncate_log)
867	__IGNORE(truncate(logfile, 0));
868      new_logfp = fopen(logfile, "a");
869      umask(0);
870    }
871  }
872
873  /*
874   * If we couldn't open a new file, then continue using the old.
875   */
876  if (!new_logfp && logfile) {
877    plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
878    return 1;
879  }
880
881  /*
882   * Close the previous file
883   */
884  if (logfp && logfp != stderr)
885    (void) fclose(logfp);
886  logfp = new_logfp;
887
888  if (logfile)
889    plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
890  else
891    plog(XLOG_INFO, "no logfile defined; using stderr");
892
893  return 0;
894}
895
896
897void
898unregister_amq(void)
899{
900
901  if (amuDebug(D_AMQ)) {
902    /* find which instance of amd to unregister */
903    u_long amd_prognum = get_amd_program_number();
904
905    if (pmap_unset(amd_prognum, AMQ_VERSION) != 1)
906      dlog("failed to de-register Amd program %lu, version %lu",
907	   amd_prognum, AMQ_VERSION);
908  }
909}
910
911
912void
913going_down(int rc)
914{
915  if (foreground) {
916    if (amd_state != Start) {
917      if (amd_state != Done)
918	return;
919      unregister_amq();
920    }
921  }
922
923#ifdef MOUNT_TABLE_ON_FILE
924  /*
925   * Call unlock_mntlist to free any important resources such as an on-disk
926   * lock file (/etc/mtab~).
927   */
928  unlock_mntlist();
929#endif /* MOUNT_TABLE_ON_FILE */
930
931  if (foreground) {
932    plog(XLOG_INFO, "Finishing with status %d", rc);
933  } else {
934    dlog("background process exiting with status %d", rc);
935  }
936  /* bye bye... */
937  exit(rc);
938}
939
940
941/* return the rpc program number under which amd was used */
942u_long
943get_amd_program_number(void)
944{
945  return amd_program_number;
946}
947
948
949/* set the rpc program number used for amd */
950void
951set_amd_program_number(u_long program)
952{
953  amd_program_number = program;
954}
955
956
957/*
958 * Release the controlling tty of the process pid.
959 *
960 * Algorithm: try these in order, if available, until one of them
961 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
962 * Do not use setpgid(): on some OSs it may release the controlling tty,
963 * even if the man page does not mention it, but on other OSs it does not.
964 * Also avoid setpgrp(): it works on some systems, and on others it is
965 * identical to setpgid().
966 */
967void
968amu_release_controlling_tty(void)
969{
970  int fd;
971
972  /*
973   * In daemon mode, leaving open file descriptors to terminals or pipes
974   * can be a really bad idea.
975   * Case in point: the redhat startup script calls us through their 'initlog'
976   * program, which exits as soon as the original amd process exits. If,
977   * at some point, a misbehaved library function decides to print something
978   * to the screen, we get a SIGPIPE and die.
979   * And guess what: NIS glibc functions will attempt to print to stderr
980   * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
981   * a ypserver.
982   *
983   * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
984   * reopen them as /dev/null.
985   *
986   * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
987   */
988  fd = open("/dev/null", O_RDWR);
989  if (fd < 0) {
990    plog(XLOG_WARNING, "Could not open /dev/null for rw: %m");
991  } else {
992    fflush(stdin);  close(0); dup2(fd, 0);
993    fflush(stdout); close(1); dup2(fd, 1);
994    fflush(stderr); close(2); dup2(fd, 2);
995    close(fd);
996  }
997
998#ifdef HAVE_SETSID
999  /* XXX: one day maybe use vhangup(2) */
1000  if (setsid() < 0) {
1001    plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
1002  } else {
1003    plog(XLOG_INFO, "released controlling tty using setsid()");
1004    return;
1005  }
1006#endif /* HAVE_SETSID */
1007
1008#ifdef TIOCNOTTY
1009  fd = open("/dev/tty", O_RDWR);
1010  if (fd < 0) {
1011    /* not an error if already no controlling tty */
1012    if (errno != ENXIO)
1013      plog(XLOG_WARNING, "Could not open controlling tty: %m");
1014  } else {
1015    if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
1016      plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
1017    else
1018      plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
1019    close(fd);
1020  }
1021  return;
1022#else
1023  plog(XLOG_ERROR, "unable to release controlling tty");
1024#endif /* not TIOCNOTTY */
1025}
1026
1027
1028/* setup a single signal handler */
1029void
1030setup_sighandler(int signum, void (*handler)(int))
1031{
1032#ifdef HAVE_SIGACTION
1033  struct sigaction sa;
1034  memset(&sa, 0, sizeof(sa));
1035  sa.sa_flags = 0;		/* unnecessary */
1036  sa.sa_handler = handler;
1037  sigemptyset(&(sa.sa_mask));	/* probably unnecessary too */
1038  sigaddset(&(sa.sa_mask), signum);
1039  sigaction(signum, &sa, NULL);
1040#else /* not HAVE_SIGACTION */
1041  (void) signal(signum, handler);
1042#endif /* not HAVE_SIGACTION */
1043}
1044
1045
1046/*
1047 * Return current time in seconds.  If passed a non-null argyument, then
1048 * fill it in with the current time in seconds and microseconds (useful
1049 * for mtime updates).
1050 */
1051time_t
1052clocktime(nfstime *nt)
1053{
1054  static struct timeval now;	/* keep last time, as default */
1055
1056  if (gettimeofday(&now, NULL) < 0) {
1057    plog(XLOG_ERROR, "clocktime: gettimeofday: %m");
1058    /* hack: force time to have incremented by at least 1 second */
1059    now.tv_sec++;
1060  }
1061  /* copy seconds and microseconds. may demote a long to an int */
1062  if (nt) {
1063    nt->nt_seconds = (u_int) now.tv_sec;
1064    nt->nt_useconds = (u_int) now.tv_usec;
1065  }
1066  return (time_t) now.tv_sec;
1067}
1068
1069
1070/*
1071 * Make all the directories in the path.
1072 */
1073int
1074mkdirs(char *path, int mode)
1075{
1076  /*
1077   * take a copy in case path is in readonly store
1078   */
1079  char *p2 = xstrdup(path);
1080  char *sp = p2;
1081  struct stat stb;
1082  int error_so_far = 0;
1083
1084  /*
1085   * Skip through the string make the directories.
1086   * Mostly ignore errors - the result is tested at the end.
1087   *
1088   * This assumes we are root so that we can do mkdir in a
1089   * mode 555 directory...
1090   */
1091  while ((sp = strchr(sp + 1, '/'))) {
1092    *sp = '\0';
1093    if (mkdir(p2, mode) < 0) {
1094      error_so_far = errno;
1095    } else {
1096      dlog("mkdir(%s)", p2);
1097    }
1098    *sp = '/';
1099  }
1100
1101  if (mkdir(p2, mode) < 0) {
1102    error_so_far = errno;
1103  } else {
1104    dlog("mkdir(%s)", p2);
1105  }
1106
1107  XFREE(p2);
1108
1109  return stat(path, &stb) == 0 &&
1110    (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1111}
1112
1113
1114/*
1115 * Remove as many directories in the path as possible.
1116 * Give up if the directory doesn't appear to have
1117 * been created by Amd (not mode dr-x) or an rmdir
1118 * fails for any reason.
1119 */
1120void
1121rmdirs(char *dir)
1122{
1123  char *xdp = xstrdup(dir);
1124  char *dp;
1125
1126  do {
1127    struct stat stb;
1128    /*
1129     * Try to find out whether this was
1130     * created by amd.  Do this by checking
1131     * for owner write permission.
1132     */
1133    if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
1134      if (rmdir(xdp) < 0) {
1135	if (errno != ENOTEMPTY &&
1136	    errno != EBUSY &&
1137	    errno != EEXIST &&
1138	    errno != EROFS &&
1139	    errno != EINVAL)
1140	  plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
1141	break;
1142      } else {
1143	dlog("rmdir(%s)", xdp);
1144      }
1145    } else {
1146      break;
1147    }
1148
1149    dp = strrchr(xdp, '/');
1150    if (dp)
1151      *dp = '\0';
1152  } while (dp && dp > xdp);
1153
1154  XFREE(xdp);
1155}
1156
1157/*
1158 * Dup a string
1159 */
1160char *
1161xstrdup(const char *s)
1162{
1163  size_t len = strlen(s);
1164  char *sp = xmalloc(len + 1);
1165  memcpy(sp, s, len + 1);
1166  return sp;
1167}
1168