142994Swollman/*
242994Swollman * Copyright (c) 1997-2006 Erez Zadok
342994Swollman * Copyright (c) 1990 Jan-Simon Pendry
442994Swollman * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
542994Swollman * Copyright (c) 1990 The Regents of the University of California.
642994Swollman * All rights reserved.
742994Swollman *
842994Swollman * This code is derived from software contributed to Berkeley by
942994Swollman * Jan-Simon Pendry at Imperial College, London.
1042994Swollman *
1142997Swollman * Redistribution and use in source and binary forms, with or without
1242997Swollman * modification, are permitted provided that the following conditions
1342997Swollman * are met:
1442997Swollman * 1. Redistributions of source code must retain the above copyright
1542997Swollman *    notice, this list of conditions and the following disclaimer.
1650479Speter * 2. Redistributions in binary form must reproduce the above copyright
1742997Swollman *    notice, this list of conditions and the following disclaimer in the
1842997Swollman *    documentation and/or other materials provided with the distribution.
1942997Swollman * 3. All advertising materials mentioning features or use of this software
2042994Swollman *    must display the following acknowledgment:
2142994Swollman *      This product includes software developed by the University of
2242994Swollman *      California, Berkeley and its contributors.
2342994Swollman * 4. Neither the name of the University nor the names of its contributors
2442994Swollman *    may be used to endorse or promote products derived from this software
2542994Swollman *    without specific prior written permission.
2642994Swollman *
2742994Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2842994Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2942994Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3042994Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3142994Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3242994Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33130819Sstefanf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3442994Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3542994Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3642994Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3742994Swollman * SUCH DAMAGE.
3842994Swollman *
3942994Swollman *
4042994Swollman * File: am-utils/libamu/xutil.c
4142994Swollman *
4242994Swollman */
4342994Swollman
4442994Swollman/*
4542994Swollman * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
4642994Swollman */
4742997Swollman
4842994Swollman#ifdef HAVE_CONFIG_H
4942994Swollman# include <config.h>
5042994Swollman#endif /* HAVE_CONFIG_H */
5142994Swollman#include <am_defs.h>
5242994Swollman#include <amu.h>
5342994Swollman
54130819Sstefanf/*
55130819Sstefanf * Logfp is the default logging device, and is initialized to stderr by
56130819Sstefanf * default in dplog/plog below, and in
57130819Sstefanf * amd/amfs_program.c:amfs_program_exec().
58130819Sstefanf */
59130819SstefanfFILE *logfp = NULL;
60130819Sstefanf
61130819Sstefanfstatic char *am_progname = "unknown";	/* "amd" */
6242994Swollmanstatic char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */
6342994Swollmanpid_t am_mypid = -1;		/* process ID */
6442994Swollmanserv_state amd_state;		/* amd's state */
6542994Swollmanint foreground = 1;		/* 1 == this is the top-level server */
6642994Swollmanint debug_flags = 0;
6742994Swollman
6842994Swollman#ifdef HAVE_SYSLOG
6942994Swollmanint syslogging;
7042994Swollman#endif /* HAVE_SYSLOG */
7142994Swollmanint xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
7242994Swollmanint xlog_level_init = ~0;
7342994Swollmanstatic int amd_program_number = AMQ_PROGRAM;
7442994Swollman
7542994Swollman#ifdef DEBUG_MEM
7642994Swollman# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
7742994Swollmanstatic int mem_bytes;
7842994Swollmanstatic int orig_mem_bytes;
7942994Swollman# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
8042994Swollman#endif /* DEBUG_MEM */
8142994Swollman
82130819Sstefanf/* forward definitions */
83130819Sstefanf/* for GCC format string auditing */
84130819Sstefanfstatic void real_plog(int lvl, const char *fmt, va_list vargs)
85130819Sstefanf     __attribute__((__format__(__printf__, 2, 0)));
8642994Swollman
8742994Swollman
8842994Swollman#ifdef DEBUG
8942994Swollman/*
9042994Swollman * List of debug options.
9142994Swollman */
9242994Swollmanstruct opt_tab dbg_opt[] =
9342994Swollman{
9442994Swollman  {"all", D_ALL},		/* All non-disruptive options */
9542994Swollman  {"amq", D_AMQ},		/* Don't register for AMQ program */
9642994Swollman  {"daemon", D_DAEMON},		/* Don't enter daemon mode */
9742994Swollman  {"fork", D_FORK},		/* Don't fork server */
9842994Swollman  {"full", D_FULL},		/* Program trace */
9942994Swollman#ifdef HAVE_CLOCK_GETTIME
10042994Swollman  {"hrtime", D_HRTIME},		/* Print high resolution time stamps */
10142994Swollman#endif /* HAVE_CLOCK_GETTIME */
10242997Swollman  /* info service specific debugging (hesiod, nis, etc) */
10342994Swollman  {"info", D_INFO},
10442994Swollman  {"mem", D_MEM},		/* Trace memory allocations */
10542994Swollman  {"mtab", D_MTAB},		/* Use local mtab file */
10642994Swollman  {"readdir", D_READDIR},	/* Check on browsable_dirs progress */
10742994Swollman  {"str", D_STR},		/* Debug string munging */
10842994Swollman  {"test", D_TEST},		/* Full debug - no daemon, no amq, local mtab */
10942994Swollman  {"trace", D_TRACE},		/* Protocol trace */
11042994Swollman  {"xdrtrace", D_XDRTRACE},	/* Trace xdr routines */
11142994Swollman  {0, 0}
11242994Swollman};
11342994Swollman#endif /* DEBUG */
11479858Sdd
11542994Swollman/*
11642994Swollman * List of log options
11742994Swollman */
11842994Swollmanstruct opt_tab xlog_opt[] =
11942994Swollman{
12042994Swollman  {"all", XLOG_ALL},		/* All messages */
12142994Swollman#ifdef DEBUG
12242994Swollman  {"debug", XLOG_DEBUG},	/* Debug messages */
12342994Swollman#endif /* DEBUG */		/* DEBUG */
12442994Swollman  {"error", XLOG_ERROR},	/* Non-fatal system errors */
12542994Swollman  {"fatal", XLOG_FATAL},	/* Fatal errors */
12642994Swollman  {"info", XLOG_INFO},		/* Information */
12742994Swollman  {"map", XLOG_MAP},		/* Map errors */
12842994Swollman  {"stats", XLOG_STATS},	/* Additional statistical information */
12942994Swollman  {"user", XLOG_USER},		/* Non-fatal user errors */
13042994Swollman  {"warn", XLOG_WARNING},	/* Warnings */
13142994Swollman  {"warning", XLOG_WARNING},	/* Warnings */
13242994Swollman  {0, 0}
13342994Swollman};
13442994Swollman
13542994Swollman
13642994Swollmanvoid
13742994Swollmanam_set_progname(char *pn)
13842994Swollman{
13942994Swollman  am_progname = pn;
14042994Swollman}
14142994Swollman
14242994Swollman
14342994Swollmanconst char *
14442994Swollmanam_get_progname(void)
14542994Swollman{
14642994Swollman  return am_progname;
14742994Swollman}
14842994Swollman
14942994Swollman
15042994Swollmanvoid
15142994Swollmanam_set_hostname(char *hn)
15242994Swollman{
15342994Swollman  xstrlcpy(am_hostname, hn, MAXHOSTNAMELEN);
15442994Swollman}
15542994Swollman
15642994Swollman
15742994Swollmanconst char *
15842994Swollmanam_get_hostname(void)
15942994Swollman{
16042994Swollman  return am_hostname;
16142994Swollman}
16242994Swollman
16342994Swollman
16442994Swollmanpid_t
16542994Swollmanam_set_mypid(void)
16642994Swollman{
16742994Swollman  am_mypid = getpid();
16842994Swollman  return am_mypid;
16942994Swollman}
17042994Swollman
17142994Swollman
17242994Swollmanlong
17342994Swollmanget_server_pid()
17442994Swollman{
17542994Swollman  return (long) (foreground ? am_mypid : getppid());
17642994Swollman}
17742994Swollman
17842994Swollman
17942994Swollmanvoidp
18042994Swollmanxmalloc(int len)
18142994Swollman{
18242994Swollman  voidp p;
18342994Swollman  int retries = 600;
18442994Swollman
18542994Swollman  /*
18642994Swollman   * Avoid malloc's which return NULL for malloc(0)
18742994Swollman   */
18842994Swollman  if (len == 0)
18942994Swollman    len = 1;
190130819Sstefanf
19142994Swollman  do {
19242994Swollman    p = (voidp) malloc((unsigned) len);
19342994Swollman    if (p) {
194      if (amuDebug(D_MEM))
195	plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p);
196      return p;
197    }
198    if (retries > 0) {
199      plog(XLOG_ERROR, "Retrying memory allocation");
200      sleep(1);
201    }
202  } while (--retries);
203
204  plog(XLOG_FATAL, "Out of memory");
205  going_down(1);
206
207  abort();
208
209  return 0;
210}
211
212
213/* like xmalloc, but zeros out the bytes */
214voidp
215xzalloc(int len)
216{
217  voidp p = xmalloc(len);
218
219  if (p)
220    memset(p, 0, len);
221  return p;
222}
223
224
225voidp
226xrealloc(voidp ptr, int len)
227{
228  if (amuDebug(D_MEM))
229    plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
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#ifdef DEBUG_MEM
249void
250dxfree(char *file, int line, voidp ptr)
251{
252  if (amuDebug(D_MEM))
253    plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr);
254  /* this is the only place that must NOT use XFREE()!!! */
255  free(ptr);
256  ptr = NULL;			/* paranoid */
257}
258
259
260# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
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 /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
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, size_t maxlen)
294{
295  const char *p;
296  char *q;
297  int error = errno;
298  int len = 0;
299
300  for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) {
301    if (p[0] == '%' && p[1] == 'm') {
302      xstrlcpy(q, strerror(error), maxlen);
303      len += strlen(q) - 1;
304      q += strlen(q) - 1;
305      p++;
306    }
307  }
308  e[maxlen-1] = '\0';		/* null terminate, to be sure */
309  return e;
310}
311
312
313/*
314 * Output the time of day and hostname to the logfile
315 */
316static void
317show_time_host_and_name(int lvl)
318{
319  static time_t last_t = 0;
320  static char *last_ctime = 0;
321  time_t t;
322#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
323  struct timespec ts;
324#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
325  char nsecs[11];		/* '.' + 9 digits + '\0' */
326  char *sev;
327
328  nsecs[0] = '\0';
329
330#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
331  /*
332   * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
333   * returning ENOSYS.
334   */
335  if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
336    t = ts.tv_sec;
337    if (amuDebug(D_HRTIME))
338      xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec);
339  }
340  else
341#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
342    t = clocktime(NULL);
343
344  if (t != last_t) {
345    last_ctime = ctime(&t);
346    last_t = t;
347  }
348
349  switch (lvl) {
350  case XLOG_FATAL:
351    sev = "fatal:";
352    break;
353  case XLOG_ERROR:
354    sev = "error:";
355    break;
356  case XLOG_USER:
357    sev = "user: ";
358    break;
359  case XLOG_WARNING:
360    sev = "warn: ";
361    break;
362  case XLOG_INFO:
363    sev = "info: ";
364    break;
365  case XLOG_DEBUG:
366    sev = "debug:";
367    break;
368  case XLOG_MAP:
369    sev = "map:  ";
370    break;
371  case XLOG_STATS:
372    sev = "stats:";
373    break;
374  default:
375    sev = "hmm:  ";
376    break;
377  }
378  fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
379	  last_ctime + 4, nsecs, am_get_hostname(),
380	  am_get_progname(),
381	  (long) am_mypid,
382	  sev);
383}
384
385
386#ifdef DEBUG
387/*
388 * Switch on/off debug options
389 */
390int
391debug_option(char *opt)
392{
393  return cmdoption(opt, dbg_opt, &debug_flags);
394}
395
396
397void
398dplog(const char *fmt, ...)
399{
400  va_list ap;
401
402  if (!logfp)
403    logfp = stderr;		/* initialize before possible first use */
404
405  va_start(ap, fmt);
406  real_plog(XLOG_DEBUG, fmt, ap);
407  va_end(ap);
408}
409#endif /* DEBUG */
410
411
412void
413plog(int lvl, const char *fmt, ...)
414{
415  va_list ap;
416
417  if (!logfp)
418    logfp = stderr;		/* initialize before possible first use */
419
420  va_start(ap, fmt);
421  real_plog(lvl, fmt, ap);
422  va_end(ap);
423}
424
425
426static void
427real_plog(int lvl, const char *fmt, va_list vargs)
428{
429  char msg[1024];
430  char efmt[1024];
431  char *ptr = msg;
432  static char last_msg[1024];
433  static int last_count = 0, last_lvl = 0;
434
435  if (!(xlog_level & lvl))
436    return;
437
438#ifdef DEBUG_MEM
439# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
440  checkup_mem();
441# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
442#endif /* DEBUG_MEM */
443
444  /*
445   * Note: xvsnprintf() may call plog() if a truncation happened, but the
446   * latter has some code to break out of an infinite loop.  See comment in
447   * xsnprintf() below.
448   */
449  xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
450
451  ptr += strlen(ptr);
452  if (*(ptr-1) == '\n')
453    *--ptr = '\0';
454
455#ifdef HAVE_SYSLOG
456  if (syslogging) {
457    switch (lvl) {		/* from mike <mcooper@usc.edu> */
458    case XLOG_FATAL:
459      lvl = LOG_CRIT;
460      break;
461    case XLOG_ERROR:
462      lvl = LOG_ERR;
463      break;
464    case XLOG_USER:
465      lvl = LOG_WARNING;
466      break;
467    case XLOG_WARNING:
468      lvl = LOG_WARNING;
469      break;
470    case XLOG_INFO:
471      lvl = LOG_INFO;
472      break;
473    case XLOG_DEBUG:
474      lvl = LOG_DEBUG;
475      break;
476    case XLOG_MAP:
477      lvl = LOG_DEBUG;
478      break;
479    case XLOG_STATS:
480      lvl = LOG_INFO;
481      break;
482    default:
483      lvl = LOG_ERR;
484      break;
485    }
486    syslog(lvl, "%s", msg);
487    return;
488  }
489#endif /* HAVE_SYSLOG */
490
491  *ptr++ = '\n';
492  *ptr = '\0';
493
494  /*
495   * mimic syslog behavior: only write repeated strings if they differ
496   */
497  switch (last_count) {
498  case 0:			/* never printed at all */
499    last_count = 1;
500    if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
501      fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
502    last_lvl = lvl;
503    show_time_host_and_name(lvl); /* mimic syslog header */
504    fwrite(msg, ptr - msg, 1, logfp);
505    fflush(logfp);
506    break;
507
508  case 1:			/* item printed once, if same, don't repeat */
509    if (STREQ(last_msg, msg)) {
510      last_count++;
511    } else {			/* last msg printed once, new one differs */
512      /* last_count remains at 1 */
513      if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
514	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
515      last_lvl = lvl;
516      show_time_host_and_name(lvl); /* mimic syslog header */
517      fwrite(msg, ptr - msg, 1, logfp);
518      fflush(logfp);
519    }
520    break;
521
522  case 100:
523    /*
524     * Don't allow repetitions longer than 100, so you can see when something
525     * cycles like crazy.
526     */
527    show_time_host_and_name(last_lvl);
528    xsnprintf(last_msg, sizeof(last_msg),
529	      "last message repeated %d times\n", last_count);
530    fwrite(last_msg, strlen(last_msg), 1, logfp);
531    fflush(logfp);
532    last_count = 0;		/* start from scratch */
533    break;
534
535  default:			/* item repeated multiple times */
536    if (STREQ(last_msg, msg)) {
537      last_count++;
538    } else {		/* last msg repeated+skipped, new one differs */
539      show_time_host_and_name(last_lvl);
540      xsnprintf(last_msg, sizeof(last_msg),
541		"last message repeated %d times\n", last_count);
542      fwrite(last_msg, strlen(last_msg), 1, logfp);
543      if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
544	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
545      last_count = 1;
546      last_lvl = lvl;
547      show_time_host_and_name(lvl); /* mimic syslog header */
548      fwrite(msg, ptr - msg, 1, logfp);
549      fflush(logfp);
550    }
551    break;
552  }
553
554}
555
556
557/*
558 * Display current debug options
559 */
560void
561show_opts(int ch, struct opt_tab *opts)
562{
563  int i;
564  int s = '{';
565
566  fprintf(stderr, "\t[-%c {no}", ch);
567  for (i = 0; opts[i].opt; i++) {
568    fprintf(stderr, "%c%s", s, opts[i].opt);
569    s = ',';
570  }
571  fputs("}]\n", stderr);
572}
573
574
575int
576cmdoption(char *s, struct opt_tab *optb, int *flags)
577{
578  char *p = s;
579  int errs = 0;
580
581  while (p && *p) {
582    int neg;
583    char *opt;
584    struct opt_tab *dp, *dpn = 0;
585
586    s = p;
587    p = strchr(p, ',');
588    if (p)
589      *p = '\0';
590
591    /* check for "no" prefix to options */
592    if (s[0] == 'n' && s[1] == 'o') {
593      opt = s + 2;
594      neg = 1;
595    } else {
596      opt = s;
597      neg = 0;
598    }
599
600    /*
601     * Scan the array of debug options to find the
602     * corresponding flag value.  If it is found
603     * then set (or clear) the flag (depending on
604     * whether the option was prefixed with "no").
605     */
606    for (dp = optb; dp->opt; dp++) {
607      if (STREQ(opt, dp->opt))
608	break;
609      if (opt != s && !dpn && STREQ(s, dp->opt))
610	dpn = dp;
611    }
612
613    if (dp->opt || dpn) {
614      if (!dp->opt) {
615	dp = dpn;
616	neg = !neg;
617      }
618      if (neg)
619	*flags &= ~dp->flag;
620      else
621	*flags |= dp->flag;
622    } else {
623      /*
624       * This will log to stderr when parsing the command line
625       * since any -l option will not yet have taken effect.
626       */
627      plog(XLOG_USER, "option \"%s\" not recognized", s);
628      errs++;
629    }
630
631    /*
632     * Put the comma back
633     */
634    if (p)
635      *p++ = ',';
636  }
637
638  return errs;
639}
640
641
642/*
643 * Switch on/off logging options
644 */
645int
646switch_option(char *opt)
647{
648  int xl = xlog_level;
649  int rc = cmdoption(opt, xlog_opt, &xl);
650
651  if (rc) {
652    rc = EINVAL;
653  } else {
654    /*
655     * Keep track of initial log level, and
656     * don't allow options to be turned off.
657     */
658    if (xlog_level_init == ~0)
659      xlog_level_init = xl;
660    else
661      xl |= xlog_level_init;
662    xlog_level = xl;
663  }
664  return rc;
665}
666
667
668#ifdef LOG_DAEMON
669/*
670 * get syslog facility to use.
671 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
672 */
673static int
674get_syslog_facility(const char *logfile)
675{
676  char *facstr;
677
678  /* parse facility string */
679  facstr = strchr(logfile, ':');
680  if (!facstr)			/* log file was "syslog" */
681    return LOG_DAEMON;
682  facstr++;
683  if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
684    plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
685    return LOG_DAEMON;
686  }
687
688#ifdef LOG_KERN
689  if (STREQ(facstr, "kern"))
690      return LOG_KERN;
691#endif /* not LOG_KERN */
692#ifdef LOG_USER
693  if (STREQ(facstr, "user"))
694      return LOG_USER;
695#endif /* not LOG_USER */
696#ifdef LOG_MAIL
697  if (STREQ(facstr, "mail"))
698      return LOG_MAIL;
699#endif /* not LOG_MAIL */
700
701  if (STREQ(facstr, "daemon"))
702      return LOG_DAEMON;
703
704#ifdef LOG_AUTH
705  if (STREQ(facstr, "auth"))
706      return LOG_AUTH;
707#endif /* not LOG_AUTH */
708#ifdef LOG_SYSLOG
709  if (STREQ(facstr, "syslog"))
710      return LOG_SYSLOG;
711#endif /* not LOG_SYSLOG */
712#ifdef LOG_LPR
713  if (STREQ(facstr, "lpr"))
714      return LOG_LPR;
715#endif /* not LOG_LPR */
716#ifdef LOG_NEWS
717  if (STREQ(facstr, "news"))
718      return LOG_NEWS;
719#endif /* not LOG_NEWS */
720#ifdef LOG_UUCP
721  if (STREQ(facstr, "uucp"))
722      return LOG_UUCP;
723#endif /* not LOG_UUCP */
724#ifdef LOG_CRON
725  if (STREQ(facstr, "cron"))
726      return LOG_CRON;
727#endif /* not LOG_CRON */
728#ifdef LOG_LOCAL0
729  if (STREQ(facstr, "local0"))
730      return LOG_LOCAL0;
731#endif /* not LOG_LOCAL0 */
732#ifdef LOG_LOCAL1
733  if (STREQ(facstr, "local1"))
734      return LOG_LOCAL1;
735#endif /* not LOG_LOCAL1 */
736#ifdef LOG_LOCAL2
737  if (STREQ(facstr, "local2"))
738      return LOG_LOCAL2;
739#endif /* not LOG_LOCAL2 */
740#ifdef LOG_LOCAL3
741  if (STREQ(facstr, "local3"))
742      return LOG_LOCAL3;
743#endif /* not LOG_LOCAL3 */
744#ifdef LOG_LOCAL4
745  if (STREQ(facstr, "local4"))
746      return LOG_LOCAL4;
747#endif /* not LOG_LOCAL4 */
748#ifdef LOG_LOCAL5
749  if (STREQ(facstr, "local5"))
750      return LOG_LOCAL5;
751#endif /* not LOG_LOCAL5 */
752#ifdef LOG_LOCAL6
753  if (STREQ(facstr, "local6"))
754      return LOG_LOCAL6;
755#endif /* not LOG_LOCAL6 */
756#ifdef LOG_LOCAL7
757  if (STREQ(facstr, "local7"))
758      return LOG_LOCAL7;
759#endif /* not LOG_LOCAL7 */
760
761  /* didn't match anything else */
762  plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
763  return LOG_DAEMON;
764}
765#endif /* not LOG_DAEMON */
766
767
768/*
769 * Change current logfile
770 */
771int
772switch_to_logfile(char *logfile, int old_umask, int truncate_log)
773{
774  FILE *new_logfp = stderr;
775
776  if (logfile) {
777#ifdef HAVE_SYSLOG
778    syslogging = 0;
779#endif /* HAVE_SYSLOG */
780
781    if (STREQ(logfile, "/dev/stderr"))
782      new_logfp = stderr;
783    else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
784
785#ifdef HAVE_SYSLOG
786      syslogging = 1;
787      new_logfp = stderr;
788      openlog(am_get_progname(),
789	      LOG_PID
790# ifdef LOG_NOWAIT
791	      | LOG_NOWAIT
792# endif /* LOG_NOWAIT */
793# ifdef LOG_DAEMON
794	      , get_syslog_facility(logfile)
795# endif /* LOG_DAEMON */
796	      );
797#else /* not HAVE_SYSLOG */
798      plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
799#endif /* not HAVE_SYSLOG */
800
801    } else {			/* regular log file */
802      (void) umask(old_umask);
803      if (truncate_log)
804	truncate(logfile, 0);
805      new_logfp = fopen(logfile, "a");
806      umask(0);
807    }
808  }
809
810  /*
811   * If we couldn't open a new file, then continue using the old.
812   */
813  if (!new_logfp && logfile) {
814    plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
815    return 1;
816  }
817
818  /*
819   * Close the previous file
820   */
821  if (logfp && logfp != stderr)
822    (void) fclose(logfp);
823  logfp = new_logfp;
824
825  if (logfile)
826    plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
827  else
828    plog(XLOG_INFO, "no logfile defined; using stderr");
829
830  return 0;
831}
832
833
834void
835unregister_amq(void)
836{
837  if (!amuDebug(D_AMQ)) {
838    /* find which instance of amd to unregister */
839    u_long amd_prognum = get_amd_program_number();
840
841    if (pmap_unset(amd_prognum, AMQ_VERSION) != 1)
842      dlog("failed to de-register Amd program %lu, version %lu",
843	   amd_prognum, AMQ_VERSION);
844  }
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
859#ifdef MOUNT_TABLE_ON_FILE
860  /*
861   * Call unlock_mntlist to free any important resources such as an on-disk
862   * lock file (/etc/mtab~).
863   */
864  unlock_mntlist();
865#endif /* MOUNT_TABLE_ON_FILE */
866
867  if (foreground) {
868    plog(XLOG_INFO, "Finishing with status %d", rc);
869  } else {
870    dlog("background process exiting with status %d", rc);
871  }
872  /* bye bye... */
873  exit(rc);
874}
875
876
877/* return the rpc program number under which amd was used */
878int
879get_amd_program_number(void)
880{
881  return amd_program_number;
882}
883
884
885/* set the rpc program number used for amd */
886void
887set_amd_program_number(int program)
888{
889  amd_program_number = program;
890}
891
892
893/*
894 * Release the controlling tty of the process pid.
895 *
896 * Algorithm: try these in order, if available, until one of them
897 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
898 * Do not use setpgid(): on some OSs it may release the controlling tty,
899 * even if the man page does not mention it, but on other OSs it does not.
900 * Also avoid setpgrp(): it works on some systems, and on others it is
901 * identical to setpgid().
902 */
903void
904amu_release_controlling_tty(void)
905{
906  int fd;
907
908  /*
909   * In daemon mode, leaving open file descriptors to terminals or pipes
910   * can be a really bad idea.
911   * Case in point: the redhat startup script calls us through their 'initlog'
912   * program, which exits as soon as the original amd process exits. If,
913   * at some point, a misbehaved library function decides to print something
914   * to the screen, we get a SIGPIPE and die.
915   * And guess what: NIS glibc functions will attempt to print to stderr
916   * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
917   * a ypserver.
918   *
919   * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
920   * reopen them as /dev/null.
921   *
922   * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
923   */
924  fd = open("/dev/null", O_RDWR);
925  if (fd < 0) {
926    plog(XLOG_WARNING, "Could not open /dev/null for rw: %m");
927  } else {
928    fflush(stdin);  close(0); dup2(fd, 0);
929    fflush(stdout); close(1); dup2(fd, 1);
930    fflush(stderr); close(2); dup2(fd, 2);
931    close(fd);
932  }
933
934#ifdef HAVE_SETSID
935  /* XXX: one day maybe use vhangup(2) */
936  if (setsid() < 0) {
937    plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
938  } else {
939    plog(XLOG_INFO, "released controlling tty using setsid()");
940    return;
941  }
942#endif /* HAVE_SETSID */
943
944#ifdef TIOCNOTTY
945  fd = open("/dev/tty", O_RDWR);
946  if (fd < 0) {
947    /* not an error if already no controlling tty */
948    if (errno != ENXIO)
949      plog(XLOG_WARNING, "Could not open controlling tty: %m");
950  } else {
951    if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
952      plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
953    else
954      plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
955    close(fd);
956  }
957  return;
958#endif /* not TIOCNOTTY */
959
960  plog(XLOG_ERROR, "unable to release controlling tty");
961}
962
963
964/* setup a single signal handler */
965void
966setup_sighandler(int signum, void (*handler)(int))
967{
968#ifdef HAVE_SIGACTION
969  struct sigaction sa;
970  memset(&sa, 0, sizeof(sa));
971  sa.sa_flags = 0;		/* unnecessary */
972  sa.sa_handler = handler;
973  sigemptyset(&(sa.sa_mask));	/* probably unnecessary too */
974  sigaddset(&(sa.sa_mask), signum);
975  sigaction(signum, &sa, NULL);
976#else /* not HAVE_SIGACTION */
977  (void) signal(signum, handler);
978#endif /* not HAVE_SIGACTION */
979}
980
981
982/*
983 * Return current time in seconds.  If passed a non-null argyument, then
984 * fill it in with the current time in seconds and microseconds (useful
985 * for mtime updates).
986 */
987time_t
988clocktime(nfstime *nt)
989{
990  static struct timeval now;	/* keep last time, as default */
991
992  if (gettimeofday(&now, NULL) < 0) {
993    plog(XLOG_ERROR, "clocktime: gettimeofday: %m");
994    /* hack: force time to have incremented by at least 1 second */
995    now.tv_sec++;
996  }
997  /* copy seconds and microseconds. may demote a long to an int */
998  if (nt) {
999    nt->nt_seconds = (u_int) now.tv_sec;
1000    nt->nt_useconds = (u_int) now.tv_usec;
1001  }
1002  return (time_t) now.tv_sec;
1003}
1004
1005
1006/*
1007 * Make all the directories in the path.
1008 */
1009int
1010mkdirs(char *path, int mode)
1011{
1012  /*
1013   * take a copy in case path is in readonly store
1014   */
1015  char *p2 = strdup(path);
1016  char *sp = p2;
1017  struct stat stb;
1018  int error_so_far = 0;
1019
1020  /*
1021   * Skip through the string make the directories.
1022   * Mostly ignore errors - the result is tested at the end.
1023   *
1024   * This assumes we are root so that we can do mkdir in a
1025   * mode 555 directory...
1026   */
1027  while ((sp = strchr(sp + 1, '/'))) {
1028    *sp = '\0';
1029    if (mkdir(p2, mode) < 0) {
1030      error_so_far = errno;
1031    } else {
1032      dlog("mkdir(%s)", p2);
1033    }
1034    *sp = '/';
1035  }
1036
1037  if (mkdir(p2, mode) < 0) {
1038    error_so_far = errno;
1039  } else {
1040    dlog("mkdir(%s)", p2);
1041  }
1042
1043  XFREE(p2);
1044
1045  return stat(path, &stb) == 0 &&
1046    (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1047}
1048
1049
1050/*
1051 * Remove as many directories in the path as possible.
1052 * Give up if the directory doesn't appear to have
1053 * been created by Amd (not mode dr-x) or an rmdir
1054 * fails for any reason.
1055 */
1056void
1057rmdirs(char *dir)
1058{
1059  char *xdp = strdup(dir);
1060  char *dp;
1061
1062  do {
1063    struct stat stb;
1064    /*
1065     * Try to find out whether this was
1066     * created by amd.  Do this by checking
1067     * for owner write permission.
1068     */
1069    if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
1070      if (rmdir(xdp) < 0) {
1071	if (errno != ENOTEMPTY &&
1072	    errno != EBUSY &&
1073	    errno != EEXIST &&
1074	    errno != EROFS &&
1075	    errno != EINVAL)
1076	  plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
1077	break;
1078      } else {
1079	dlog("rmdir(%s)", xdp);
1080      }
1081    } else {
1082      break;
1083    }
1084
1085    dp = strrchr(xdp, '/');
1086    if (dp)
1087      *dp = '\0';
1088  } while (dp && dp > xdp);
1089
1090  XFREE(xdp);
1091}
1092