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