138494Sobrien/*
2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174294Sobrien * File: am-utils/libamu/xutil.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
44174294Sobrien/*
45174294Sobrien * Miscellaneous Utilities: Logging, TTY, timers, signals, RPC, memory, etc.
46174294Sobrien */
47174294Sobrien
4838494Sobrien#ifdef HAVE_CONFIG_H
4938494Sobrien# include <config.h>
5038494Sobrien#endif /* HAVE_CONFIG_H */
5138494Sobrien#include <am_defs.h>
5238494Sobrien#include <amu.h>
5338494Sobrien
5451292Sobrien/*
5551292Sobrien * Logfp is the default logging device, and is initialized to stderr by
5651292Sobrien * default in dplog/plog below, and in
5751292Sobrien * amd/amfs_program.c:amfs_program_exec().
5851292Sobrien */
5951292SobrienFILE *logfp = NULL;
6038494Sobrien
6142629Sobrienstatic char *am_progname = "unknown";	/* "amd" */
62174294Sobrienstatic char am_hostname[MAXHOSTNAMELEN] = "unknown"; /* Hostname */
6342629Sobrienpid_t am_mypid = -1;		/* process ID */
6442629Sobrienserv_state amd_state;		/* amd's state */
6542629Sobrienint foreground = 1;		/* 1 == this is the top-level server */
6642629Sobrienint debug_flags = 0;
6742629Sobrien
6838494Sobrien#ifdef HAVE_SYSLOG
6938494Sobrienint syslogging;
7038494Sobrien#endif /* HAVE_SYSLOG */
7138494Sobrienint xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
7238494Sobrienint xlog_level_init = ~0;
7338494Sobrienstatic int amd_program_number = AMQ_PROGRAM;
7438494Sobrien
7538494Sobrien#ifdef DEBUG_MEM
76174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
7738494Sobrienstatic int mem_bytes;
7838494Sobrienstatic int orig_mem_bytes;
79174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
8038494Sobrien#endif /* DEBUG_MEM */
8138494Sobrien
8238494Sobrien/* forward definitions */
83119679Smbr/* for GCC format string auditing */
8482794Sobrienstatic void real_plog(int lvl, const char *fmt, va_list vargs)
8582794Sobrien     __attribute__((__format__(__printf__, 2, 0)));
8638494Sobrien
87119679Smbr
8838494Sobrien#ifdef DEBUG
8938494Sobrien/*
9038494Sobrien * List of debug options.
9138494Sobrien */
9238494Sobrienstruct opt_tab dbg_opt[] =
9338494Sobrien{
94174294Sobrien  {"all", D_ALL},		/* All non-disruptive options */
95174294Sobrien  {"amq", D_AMQ},		/* Don't register for AMQ program */
96174294Sobrien  {"daemon", D_DAEMON},		/* Don't enter daemon mode */
97174294Sobrien  {"fork", D_FORK},		/* Don't fork server */
9838494Sobrien  {"full", D_FULL},		/* Program trace */
9982794Sobrien#ifdef HAVE_CLOCK_GETTIME
10082794Sobrien  {"hrtime", D_HRTIME},		/* Print high resolution time stamps */
10182794Sobrien#endif /* HAVE_CLOCK_GETTIME */
10238494Sobrien  /* info service specific debugging (hesiod, nis, etc) */
10338494Sobrien  {"info", D_INFO},
10438494Sobrien  {"mem", D_MEM},		/* Trace memory allocations */
10538494Sobrien  {"mtab", D_MTAB},		/* Use local mtab file */
106174294Sobrien  {"readdir", D_READDIR},	/* Check on browsable_dirs progress */
10738494Sobrien  {"str", D_STR},		/* Debug string munging */
108174294Sobrien  {"test", D_TEST},		/* Full debug - no daemon, no amq, local mtab */
10938494Sobrien  {"trace", D_TRACE},		/* Protocol trace */
11082794Sobrien  {"xdrtrace", D_XDRTRACE},	/* Trace xdr routines */
11138494Sobrien  {0, 0}
11238494Sobrien};
11338494Sobrien#endif /* DEBUG */
11438494Sobrien
11538494Sobrien/*
11638494Sobrien * List of log options
11738494Sobrien */
11838494Sobrienstruct opt_tab xlog_opt[] =
11938494Sobrien{
12038494Sobrien  {"all", XLOG_ALL},		/* All messages */
12138494Sobrien#ifdef DEBUG
12238494Sobrien  {"debug", XLOG_DEBUG},	/* Debug messages */
12338494Sobrien#endif /* DEBUG */		/* DEBUG */
12438494Sobrien  {"error", XLOG_ERROR},	/* Non-fatal system errors */
12538494Sobrien  {"fatal", XLOG_FATAL},	/* Fatal errors */
12638494Sobrien  {"info", XLOG_INFO},		/* Information */
12738494Sobrien  {"map", XLOG_MAP},		/* Map errors */
12838494Sobrien  {"stats", XLOG_STATS},	/* Additional statistical information */
12938494Sobrien  {"user", XLOG_USER},		/* Non-fatal user errors */
13038494Sobrien  {"warn", XLOG_WARNING},	/* Warnings */
13138494Sobrien  {"warning", XLOG_WARNING},	/* Warnings */
13238494Sobrien  {0, 0}
13338494Sobrien};
13438494Sobrien
13538494Sobrien
13642629Sobrienvoid
13742629Sobrienam_set_progname(char *pn)
13842629Sobrien{
13942629Sobrien  am_progname = pn;
14042629Sobrien}
14142629Sobrien
14242629Sobrien
14342629Sobrienconst char *
14442629Sobrienam_get_progname(void)
14542629Sobrien{
14642629Sobrien  return am_progname;
14742629Sobrien}
14842629Sobrien
14942629Sobrien
15042629Sobrienvoid
15142629Sobrienam_set_hostname(char *hn)
15242629Sobrien{
153174294Sobrien  xstrlcpy(am_hostname, hn, MAXHOSTNAMELEN);
15442629Sobrien}
15542629Sobrien
15642629Sobrien
15742629Sobrienconst char *
15842629Sobrienam_get_hostname(void)
15942629Sobrien{
16042629Sobrien  return am_hostname;
16142629Sobrien}
16242629Sobrien
16342629Sobrien
16442629Sobrienpid_t
16542629Sobrienam_set_mypid(void)
16642629Sobrien{
16742629Sobrien  am_mypid = getpid();
16842629Sobrien  return am_mypid;
16942629Sobrien}
17042629Sobrien
17142629Sobrien
172174294Sobrienlong
173174294Sobrienget_server_pid()
174174294Sobrien{
175174294Sobrien  return (long) (foreground ? am_mypid : getppid());
176174294Sobrien}
177174294Sobrien
178174294Sobrien
17938494Sobrienvoidp
18038494Sobrienxmalloc(int len)
18138494Sobrien{
18238494Sobrien  voidp p;
18338494Sobrien  int retries = 600;
18438494Sobrien
18538494Sobrien  /*
18638494Sobrien   * Avoid malloc's which return NULL for malloc(0)
18738494Sobrien   */
18838494Sobrien  if (len == 0)
18938494Sobrien    len = 1;
19038494Sobrien
19138494Sobrien  do {
19238494Sobrien    p = (voidp) malloc((unsigned) len);
19338494Sobrien    if (p) {
194174294Sobrien      if (amuDebug(D_MEM))
195174294Sobrien	plog(XLOG_DEBUG, "Allocated size %d; block %p", len, p);
19638494Sobrien      return p;
19738494Sobrien    }
19838494Sobrien    if (retries > 0) {
19938494Sobrien      plog(XLOG_ERROR, "Retrying memory allocation");
20038494Sobrien      sleep(1);
20138494Sobrien    }
20238494Sobrien  } while (--retries);
20338494Sobrien
20438494Sobrien  plog(XLOG_FATAL, "Out of memory");
20538494Sobrien  going_down(1);
20638494Sobrien
20738494Sobrien  abort();
20838494Sobrien
20938494Sobrien  return 0;
21038494Sobrien}
21138494Sobrien
21238494Sobrien
21338494Sobrien/* like xmalloc, but zeros out the bytes */
21438494Sobrienvoidp
21538494Sobrienxzalloc(int len)
21638494Sobrien{
21738494Sobrien  voidp p = xmalloc(len);
21838494Sobrien
21938494Sobrien  if (p)
22038494Sobrien    memset(p, 0, len);
22138494Sobrien  return p;
22238494Sobrien}
22338494Sobrien
22438494Sobrien
22538494Sobrienvoidp
22638494Sobrienxrealloc(voidp ptr, int len)
22738494Sobrien{
228174294Sobrien  if (amuDebug(D_MEM))
229174294Sobrien    plog(XLOG_DEBUG, "Reallocated size %d; block %p", len, ptr);
23038494Sobrien
23138494Sobrien  if (len == 0)
23238494Sobrien    len = 1;
23338494Sobrien
23438494Sobrien  if (ptr)
23538494Sobrien    ptr = (voidp) realloc(ptr, (unsigned) len);
23638494Sobrien  else
23738494Sobrien    ptr = (voidp) xmalloc((unsigned) len);
23838494Sobrien
23938494Sobrien  if (!ptr) {
24038494Sobrien    plog(XLOG_FATAL, "Out of memory in realloc");
24138494Sobrien    going_down(1);
24238494Sobrien    abort();
24338494Sobrien  }
24438494Sobrien  return ptr;
24538494Sobrien}
24638494Sobrien
24738494Sobrien
248174294Sobrien#ifdef DEBUG_MEM
24938494Sobrienvoid
25038494Sobriendxfree(char *file, int line, voidp ptr)
25138494Sobrien{
252174294Sobrien  if (amuDebug(D_MEM))
253174294Sobrien    plog(XLOG_DEBUG, "Free in %s:%d: block %p", file, line, ptr);
25438494Sobrien  /* this is the only place that must NOT use XFREE()!!! */
25538494Sobrien  free(ptr);
25638494Sobrien  ptr = NULL;			/* paranoid */
25738494Sobrien}
25838494Sobrien
25938494Sobrien
260174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
26138494Sobrienstatic void
26238494Sobriencheckup_mem(void)
26338494Sobrien{
26438494Sobrien  struct mallinfo mi = mallinfo();
26538494Sobrien  u_long uordbytes = mi.uordblks * 4096;
26638494Sobrien
26738494Sobrien  if (mem_bytes != uordbytes) {
26838494Sobrien    if (orig_mem_bytes == 0)
26938494Sobrien      mem_bytes = orig_mem_bytes = uordbytes;
27038494Sobrien    else {
27142629Sobrien      fprintf(logfp, "%s[%ld]: ", am_get_progname(), (long) am_mypid);
27238494Sobrien      if (mem_bytes < uordbytes) {
27338494Sobrien	fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
27438494Sobrien      } else {
27538494Sobrien	fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
27638494Sobrien      }
27738494Sobrien      mem_bytes = uordbytes;
27838494Sobrien      fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
27938494Sobrien    }
28038494Sobrien  }
28138494Sobrien  malloc_verify();
28238494Sobrien}
283174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
28438494Sobrien#endif /* DEBUG_MEM */
28538494Sobrien
28638494Sobrien
28738494Sobrien/*
28842629Sobrien * Take a log format string and expand occurrences of %m
28951292Sobrien * with the current error code taken from errno.  Make sure
29051292Sobrien * 'e' never gets longer than maxlen characters.
29138494Sobrien */
29282794Sobrienstatic const char *
293174294Sobrienexpand_error(const char *f, char *e, size_t maxlen)
29438494Sobrien{
29582794Sobrien  const char *p;
29682794Sobrien  char *q;
29738494Sobrien  int error = errno;
29851292Sobrien  int len = 0;
29938494Sobrien
300174294Sobrien  for (p = f, q = e; (*q = *p) && (size_t) len < maxlen; len++, q++, p++) {
30138494Sobrien    if (p[0] == '%' && p[1] == 'm') {
302174294Sobrien      xstrlcpy(q, strerror(error), maxlen);
30351292Sobrien      len += strlen(q) - 1;
30451292Sobrien      q += strlen(q) - 1;
30538494Sobrien      p++;
30638494Sobrien    }
30738494Sobrien  }
30851292Sobrien  e[maxlen-1] = '\0';		/* null terminate, to be sure */
30982794Sobrien  return e;
31038494Sobrien}
31138494Sobrien
31238494Sobrien
31338494Sobrien/*
31438494Sobrien * Output the time of day and hostname to the logfile
31538494Sobrien */
31638494Sobrienstatic void
31738494Sobrienshow_time_host_and_name(int lvl)
31838494Sobrien{
31938494Sobrien  static time_t last_t = 0;
32038494Sobrien  static char *last_ctime = 0;
32182794Sobrien  time_t t;
322174294Sobrien#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
32382794Sobrien  struct timespec ts;
324174294Sobrien#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
325174294Sobrien  char nsecs[11];		/* '.' + 9 digits + '\0' */
32638494Sobrien  char *sev;
32738494Sobrien
328174294Sobrien  nsecs[0] = '\0';
329174294Sobrien
330174294Sobrien#if defined(HAVE_CLOCK_GETTIME) && defined(DEBUG)
33182794Sobrien  /*
33282794Sobrien   * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
33382794Sobrien   * returning ENOSYS.
33482794Sobrien   */
33582794Sobrien  if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
33682794Sobrien    t = ts.tv_sec;
337174294Sobrien    if (amuDebug(D_HRTIME))
338174294Sobrien      xsnprintf(nsecs, sizeof(nsecs), ".%09ld", ts.tv_nsec);
33982794Sobrien  }
34082794Sobrien  else
341174294Sobrien#endif /* defined(HAVE_CLOCK_GETTIME) && defined(DEBUG) */
342174294Sobrien    t = clocktime(NULL);
34382794Sobrien
34438494Sobrien  if (t != last_t) {
34538494Sobrien    last_ctime = ctime(&t);
34638494Sobrien    last_t = t;
34738494Sobrien  }
34882794Sobrien
34938494Sobrien  switch (lvl) {
35038494Sobrien  case XLOG_FATAL:
35138494Sobrien    sev = "fatal:";
35238494Sobrien    break;
35338494Sobrien  case XLOG_ERROR:
35438494Sobrien    sev = "error:";
35538494Sobrien    break;
35638494Sobrien  case XLOG_USER:
35738494Sobrien    sev = "user: ";
35838494Sobrien    break;
35938494Sobrien  case XLOG_WARNING:
36038494Sobrien    sev = "warn: ";
36138494Sobrien    break;
36238494Sobrien  case XLOG_INFO:
36338494Sobrien    sev = "info: ";
36438494Sobrien    break;
36538494Sobrien  case XLOG_DEBUG:
36638494Sobrien    sev = "debug:";
36738494Sobrien    break;
36838494Sobrien  case XLOG_MAP:
36938494Sobrien    sev = "map:  ";
37038494Sobrien    break;
37138494Sobrien  case XLOG_STATS:
37238494Sobrien    sev = "stats:";
37338494Sobrien    break;
37438494Sobrien  default:
37538494Sobrien    sev = "hmm:  ";
37638494Sobrien    break;
37738494Sobrien  }
37882794Sobrien  fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
37982794Sobrien	  last_ctime + 4, nsecs, am_get_hostname(),
38042629Sobrien	  am_get_progname(),
38142629Sobrien	  (long) am_mypid,
38238494Sobrien	  sev);
38338494Sobrien}
38438494Sobrien
38538494Sobrien
38638494Sobrien#ifdef DEBUG
38738494Sobrien/*
38838494Sobrien * Switch on/off debug options
38938494Sobrien */
39038494Sobrienint
39138494Sobriendebug_option(char *opt)
39238494Sobrien{
39338494Sobrien  return cmdoption(opt, dbg_opt, &debug_flags);
39438494Sobrien}
39538494Sobrien
39638494Sobrien
39738494Sobrienvoid
39882794Sobriendplog(const char *fmt, ...)
39938494Sobrien{
40038494Sobrien  va_list ap;
40138494Sobrien
40251292Sobrien  if (!logfp)
40351292Sobrien    logfp = stderr;		/* initialize before possible first use */
40451292Sobrien
40538494Sobrien  va_start(ap, fmt);
40638494Sobrien  real_plog(XLOG_DEBUG, fmt, ap);
40738494Sobrien  va_end(ap);
40838494Sobrien}
40938494Sobrien#endif /* DEBUG */
41038494Sobrien
41138494Sobrien
41238494Sobrienvoid
41382794Sobrienplog(int lvl, const char *fmt, ...)
41438494Sobrien{
41538494Sobrien  va_list ap;
41638494Sobrien
41751292Sobrien  if (!logfp)
41851292Sobrien    logfp = stderr;		/* initialize before possible first use */
41951292Sobrien
42038494Sobrien  va_start(ap, fmt);
42138494Sobrien  real_plog(lvl, fmt, ap);
42238494Sobrien  va_end(ap);
42338494Sobrien}
42438494Sobrien
42538494Sobrien
42638494Sobrienstatic void
42782794Sobrienreal_plog(int lvl, const char *fmt, va_list vargs)
42838494Sobrien{
42938494Sobrien  char msg[1024];
43038494Sobrien  char efmt[1024];
43138494Sobrien  char *ptr = msg;
43238494Sobrien  static char last_msg[1024];
43338494Sobrien  static int last_count = 0, last_lvl = 0;
43438494Sobrien
43538494Sobrien  if (!(xlog_level & lvl))
43638494Sobrien    return;
43738494Sobrien
43838494Sobrien#ifdef DEBUG_MEM
439174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
44038494Sobrien  checkup_mem();
441174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
44238494Sobrien#endif /* DEBUG_MEM */
44338494Sobrien
44482794Sobrien  /*
445174294Sobrien   * Note: xvsnprintf() may call plog() if a truncation happened, but the
446174294Sobrien   * latter has some code to break out of an infinite loop.  See comment in
447174294Sobrien   * xsnprintf() below.
44882794Sobrien   */
449174294Sobrien  xvsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
45038494Sobrien
45138494Sobrien  ptr += strlen(ptr);
452174294Sobrien  if (*(ptr-1) == '\n')
45338494Sobrien    *--ptr = '\0';
45438494Sobrien
45538494Sobrien#ifdef HAVE_SYSLOG
45638494Sobrien  if (syslogging) {
45738494Sobrien    switch (lvl) {		/* from mike <mcooper@usc.edu> */
45838494Sobrien    case XLOG_FATAL:
45938494Sobrien      lvl = LOG_CRIT;
46038494Sobrien      break;
46138494Sobrien    case XLOG_ERROR:
46238494Sobrien      lvl = LOG_ERR;
46338494Sobrien      break;
46438494Sobrien    case XLOG_USER:
46538494Sobrien      lvl = LOG_WARNING;
46638494Sobrien      break;
46738494Sobrien    case XLOG_WARNING:
46838494Sobrien      lvl = LOG_WARNING;
46938494Sobrien      break;
47038494Sobrien    case XLOG_INFO:
47138494Sobrien      lvl = LOG_INFO;
47238494Sobrien      break;
47338494Sobrien    case XLOG_DEBUG:
47438494Sobrien      lvl = LOG_DEBUG;
47538494Sobrien      break;
47638494Sobrien    case XLOG_MAP:
47738494Sobrien      lvl = LOG_DEBUG;
47838494Sobrien      break;
47938494Sobrien    case XLOG_STATS:
48038494Sobrien      lvl = LOG_INFO;
48138494Sobrien      break;
48238494Sobrien    default:
48338494Sobrien      lvl = LOG_ERR;
48438494Sobrien      break;
48538494Sobrien    }
48638494Sobrien    syslog(lvl, "%s", msg);
48738494Sobrien    return;
48838494Sobrien  }
48938494Sobrien#endif /* HAVE_SYSLOG */
49038494Sobrien
49138494Sobrien  *ptr++ = '\n';
49238494Sobrien  *ptr = '\0';
49338494Sobrien
49438494Sobrien  /*
49538494Sobrien   * mimic syslog behavior: only write repeated strings if they differ
49638494Sobrien   */
49738494Sobrien  switch (last_count) {
49838494Sobrien  case 0:			/* never printed at all */
49938494Sobrien    last_count = 1;
500174294Sobrien    if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
501174294Sobrien      fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
50238494Sobrien    last_lvl = lvl;
50338494Sobrien    show_time_host_and_name(lvl); /* mimic syslog header */
50438494Sobrien    fwrite(msg, ptr - msg, 1, logfp);
50538494Sobrien    fflush(logfp);
50638494Sobrien    break;
50738494Sobrien
50838494Sobrien  case 1:			/* item printed once, if same, don't repeat */
50938494Sobrien    if (STREQ(last_msg, msg)) {
51038494Sobrien      last_count++;
51138494Sobrien    } else {			/* last msg printed once, new one differs */
51238494Sobrien      /* last_count remains at 1 */
513174294Sobrien      if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
514174294Sobrien	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
51538494Sobrien      last_lvl = lvl;
51638494Sobrien      show_time_host_and_name(lvl); /* mimic syslog header */
51738494Sobrien      fwrite(msg, ptr - msg, 1, logfp);
51838494Sobrien      fflush(logfp);
51938494Sobrien    }
52038494Sobrien    break;
52138494Sobrien
52238494Sobrien  case 100:
52338494Sobrien    /*
52438494Sobrien     * Don't allow repetitions longer than 100, so you can see when something
52538494Sobrien     * cycles like crazy.
52638494Sobrien     */
52738494Sobrien    show_time_host_and_name(last_lvl);
528174294Sobrien    xsnprintf(last_msg, sizeof(last_msg),
529174294Sobrien	      "last message repeated %d times\n", last_count);
53038494Sobrien    fwrite(last_msg, strlen(last_msg), 1, logfp);
53138494Sobrien    fflush(logfp);
53238494Sobrien    last_count = 0;		/* start from scratch */
53338494Sobrien    break;
53438494Sobrien
53538494Sobrien  default:			/* item repeated multiple times */
53638494Sobrien    if (STREQ(last_msg, msg)) {
53738494Sobrien      last_count++;
53838494Sobrien    } else {		/* last msg repeated+skipped, new one differs */
53938494Sobrien      show_time_host_and_name(last_lvl);
540174294Sobrien      xsnprintf(last_msg, sizeof(last_msg),
541174294Sobrien		"last message repeated %d times\n", last_count);
54238494Sobrien      fwrite(last_msg, strlen(last_msg), 1, logfp);
543174294Sobrien      if (strlcpy(last_msg, msg, 1024) >= 1024) /* don't use xstrlcpy here (recursive!) */
544174294Sobrien	fprintf(stderr, "real_plog: string \"%s\" truncated to \"%s\"\n", last_msg, msg);
54538494Sobrien      last_count = 1;
54638494Sobrien      last_lvl = lvl;
54738494Sobrien      show_time_host_and_name(lvl); /* mimic syslog header */
54838494Sobrien      fwrite(msg, ptr - msg, 1, logfp);
54938494Sobrien      fflush(logfp);
55038494Sobrien    }
55138494Sobrien    break;
55238494Sobrien  }
55338494Sobrien
55438494Sobrien}
55538494Sobrien
55638494Sobrien
55738494Sobrien/*
55838494Sobrien * Display current debug options
55938494Sobrien */
56038494Sobrienvoid
56138494Sobrienshow_opts(int ch, struct opt_tab *opts)
56238494Sobrien{
56338494Sobrien  int i;
56438494Sobrien  int s = '{';
56538494Sobrien
56638494Sobrien  fprintf(stderr, "\t[-%c {no}", ch);
56738494Sobrien  for (i = 0; opts[i].opt; i++) {
56838494Sobrien    fprintf(stderr, "%c%s", s, opts[i].opt);
56938494Sobrien    s = ',';
57038494Sobrien  }
57138494Sobrien  fputs("}]\n", stderr);
57238494Sobrien}
57338494Sobrien
57438494Sobrien
57538494Sobrienint
57638494Sobriencmdoption(char *s, struct opt_tab *optb, int *flags)
57738494Sobrien{
57838494Sobrien  char *p = s;
57938494Sobrien  int errs = 0;
58038494Sobrien
58138494Sobrien  while (p && *p) {
58238494Sobrien    int neg;
58338494Sobrien    char *opt;
58438494Sobrien    struct opt_tab *dp, *dpn = 0;
58538494Sobrien
58638494Sobrien    s = p;
58738494Sobrien    p = strchr(p, ',');
58838494Sobrien    if (p)
58938494Sobrien      *p = '\0';
59038494Sobrien
59138494Sobrien    /* check for "no" prefix to options */
59238494Sobrien    if (s[0] == 'n' && s[1] == 'o') {
59338494Sobrien      opt = s + 2;
59438494Sobrien      neg = 1;
59538494Sobrien    } else {
59638494Sobrien      opt = s;
59738494Sobrien      neg = 0;
59838494Sobrien    }
59938494Sobrien
60038494Sobrien    /*
60138494Sobrien     * Scan the array of debug options to find the
60238494Sobrien     * corresponding flag value.  If it is found
60338494Sobrien     * then set (or clear) the flag (depending on
60438494Sobrien     * whether the option was prefixed with "no").
60538494Sobrien     */
60638494Sobrien    for (dp = optb; dp->opt; dp++) {
60738494Sobrien      if (STREQ(opt, dp->opt))
60838494Sobrien	break;
60938494Sobrien      if (opt != s && !dpn && STREQ(s, dp->opt))
61038494Sobrien	dpn = dp;
61138494Sobrien    }
61238494Sobrien
61338494Sobrien    if (dp->opt || dpn) {
61438494Sobrien      if (!dp->opt) {
61538494Sobrien	dp = dpn;
61638494Sobrien	neg = !neg;
61738494Sobrien      }
61838494Sobrien      if (neg)
61938494Sobrien	*flags &= ~dp->flag;
62038494Sobrien      else
62138494Sobrien	*flags |= dp->flag;
62238494Sobrien    } else {
62338494Sobrien      /*
62438494Sobrien       * This will log to stderr when parsing the command line
62538494Sobrien       * since any -l option will not yet have taken effect.
62638494Sobrien       */
62742629Sobrien      plog(XLOG_USER, "option \"%s\" not recognized", s);
62838494Sobrien      errs++;
62938494Sobrien    }
63038494Sobrien
63138494Sobrien    /*
63238494Sobrien     * Put the comma back
63338494Sobrien     */
63438494Sobrien    if (p)
63538494Sobrien      *p++ = ',';
63638494Sobrien  }
63738494Sobrien
63838494Sobrien  return errs;
63938494Sobrien}
64038494Sobrien
64138494Sobrien
64238494Sobrien/*
64338494Sobrien * Switch on/off logging options
64438494Sobrien */
64538494Sobrienint
64638494Sobrienswitch_option(char *opt)
64738494Sobrien{
64838494Sobrien  int xl = xlog_level;
64938494Sobrien  int rc = cmdoption(opt, xlog_opt, &xl);
65038494Sobrien
65138494Sobrien  if (rc) {
65238494Sobrien    rc = EINVAL;
65338494Sobrien  } else {
65438494Sobrien    /*
65538494Sobrien     * Keep track of initial log level, and
65638494Sobrien     * don't allow options to be turned off.
65738494Sobrien     */
65838494Sobrien    if (xlog_level_init == ~0)
65938494Sobrien      xlog_level_init = xl;
66038494Sobrien    else
66138494Sobrien      xl |= xlog_level_init;
66238494Sobrien    xlog_level = xl;
66338494Sobrien  }
66438494Sobrien  return rc;
66538494Sobrien}
66638494Sobrien
667174294Sobrien
66851292Sobrien#ifdef LOG_DAEMON
66938494Sobrien/*
67038494Sobrien * get syslog facility to use.
67138494Sobrien * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
67238494Sobrien */
67338494Sobrienstatic int
67438494Sobrienget_syslog_facility(const char *logfile)
67538494Sobrien{
67638494Sobrien  char *facstr;
67738494Sobrien
67838494Sobrien  /* parse facility string */
67938494Sobrien  facstr = strchr(logfile, ':');
68038494Sobrien  if (!facstr)			/* log file was "syslog" */
68138494Sobrien    return LOG_DAEMON;
68238494Sobrien  facstr++;
68338494Sobrien  if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
68438494Sobrien    plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
68538494Sobrien    return LOG_DAEMON;
68638494Sobrien  }
68738494Sobrien
68838494Sobrien#ifdef LOG_KERN
68938494Sobrien  if (STREQ(facstr, "kern"))
69038494Sobrien      return LOG_KERN;
69138494Sobrien#endif /* not LOG_KERN */
69238494Sobrien#ifdef LOG_USER
69338494Sobrien  if (STREQ(facstr, "user"))
69438494Sobrien      return LOG_USER;
69538494Sobrien#endif /* not LOG_USER */
69638494Sobrien#ifdef LOG_MAIL
69738494Sobrien  if (STREQ(facstr, "mail"))
69838494Sobrien      return LOG_MAIL;
69938494Sobrien#endif /* not LOG_MAIL */
70051292Sobrien
70138494Sobrien  if (STREQ(facstr, "daemon"))
70238494Sobrien      return LOG_DAEMON;
70351292Sobrien
70438494Sobrien#ifdef LOG_AUTH
70538494Sobrien  if (STREQ(facstr, "auth"))
70638494Sobrien      return LOG_AUTH;
70738494Sobrien#endif /* not LOG_AUTH */
70838494Sobrien#ifdef LOG_SYSLOG
70938494Sobrien  if (STREQ(facstr, "syslog"))
71038494Sobrien      return LOG_SYSLOG;
71138494Sobrien#endif /* not LOG_SYSLOG */
71238494Sobrien#ifdef LOG_LPR
71338494Sobrien  if (STREQ(facstr, "lpr"))
71438494Sobrien      return LOG_LPR;
71538494Sobrien#endif /* not LOG_LPR */
71638494Sobrien#ifdef LOG_NEWS
71738494Sobrien  if (STREQ(facstr, "news"))
71838494Sobrien      return LOG_NEWS;
71938494Sobrien#endif /* not LOG_NEWS */
72038494Sobrien#ifdef LOG_UUCP
72138494Sobrien  if (STREQ(facstr, "uucp"))
72238494Sobrien      return LOG_UUCP;
72338494Sobrien#endif /* not LOG_UUCP */
72438494Sobrien#ifdef LOG_CRON
72538494Sobrien  if (STREQ(facstr, "cron"))
72638494Sobrien      return LOG_CRON;
72738494Sobrien#endif /* not LOG_CRON */
72838494Sobrien#ifdef LOG_LOCAL0
72938494Sobrien  if (STREQ(facstr, "local0"))
73038494Sobrien      return LOG_LOCAL0;
73138494Sobrien#endif /* not LOG_LOCAL0 */
73238494Sobrien#ifdef LOG_LOCAL1
73338494Sobrien  if (STREQ(facstr, "local1"))
73438494Sobrien      return LOG_LOCAL1;
73538494Sobrien#endif /* not LOG_LOCAL1 */
73638494Sobrien#ifdef LOG_LOCAL2
73738494Sobrien  if (STREQ(facstr, "local2"))
73838494Sobrien      return LOG_LOCAL2;
73938494Sobrien#endif /* not LOG_LOCAL2 */
74038494Sobrien#ifdef LOG_LOCAL3
74138494Sobrien  if (STREQ(facstr, "local3"))
74238494Sobrien      return LOG_LOCAL3;
74338494Sobrien#endif /* not LOG_LOCAL3 */
74438494Sobrien#ifdef LOG_LOCAL4
74538494Sobrien  if (STREQ(facstr, "local4"))
74638494Sobrien      return LOG_LOCAL4;
74738494Sobrien#endif /* not LOG_LOCAL4 */
74838494Sobrien#ifdef LOG_LOCAL5
74938494Sobrien  if (STREQ(facstr, "local5"))
75038494Sobrien      return LOG_LOCAL5;
75138494Sobrien#endif /* not LOG_LOCAL5 */
75238494Sobrien#ifdef LOG_LOCAL6
75338494Sobrien  if (STREQ(facstr, "local6"))
75438494Sobrien      return LOG_LOCAL6;
75538494Sobrien#endif /* not LOG_LOCAL6 */
75638494Sobrien#ifdef LOG_LOCAL7
75738494Sobrien  if (STREQ(facstr, "local7"))
75838494Sobrien      return LOG_LOCAL7;
75938494Sobrien#endif /* not LOG_LOCAL7 */
76038494Sobrien
76138494Sobrien  /* didn't match anything else */
76238494Sobrien  plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
76338494Sobrien  return LOG_DAEMON;
76438494Sobrien}
76551292Sobrien#endif /* not LOG_DAEMON */
76638494Sobrien
76738494Sobrien
76838494Sobrien/*
76938494Sobrien * Change current logfile
77038494Sobrien */
77138494Sobrienint
772174294Sobrienswitch_to_logfile(char *logfile, int old_umask, int truncate_log)
77338494Sobrien{
77438494Sobrien  FILE *new_logfp = stderr;
77538494Sobrien
77638494Sobrien  if (logfile) {
77738494Sobrien#ifdef HAVE_SYSLOG
77838494Sobrien    syslogging = 0;
77938494Sobrien#endif /* HAVE_SYSLOG */
78038494Sobrien
78138494Sobrien    if (STREQ(logfile, "/dev/stderr"))
78238494Sobrien      new_logfp = stderr;
78338494Sobrien    else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
78438494Sobrien
78538494Sobrien#ifdef HAVE_SYSLOG
78638494Sobrien      syslogging = 1;
78738494Sobrien      new_logfp = stderr;
78842629Sobrien      openlog(am_get_progname(),
78938494Sobrien	      LOG_PID
79038494Sobrien# ifdef LOG_NOWAIT
79138494Sobrien	      | LOG_NOWAIT
79238494Sobrien# endif /* LOG_NOWAIT */
79338494Sobrien# ifdef LOG_DAEMON
79438494Sobrien	      , get_syslog_facility(logfile)
79538494Sobrien# endif /* LOG_DAEMON */
79638494Sobrien	      );
79738494Sobrien#else /* not HAVE_SYSLOG */
79838494Sobrien      plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
79938494Sobrien#endif /* not HAVE_SYSLOG */
80038494Sobrien
801174294Sobrien    } else {			/* regular log file */
80242629Sobrien      (void) umask(old_umask);
803174294Sobrien      if (truncate_log)
804174294Sobrien	truncate(logfile, 0);
80538494Sobrien      new_logfp = fopen(logfile, "a");
80638494Sobrien      umask(0);
80738494Sobrien    }
80838494Sobrien  }
80938494Sobrien
81038494Sobrien  /*
81138494Sobrien   * If we couldn't open a new file, then continue using the old.
81238494Sobrien   */
81338494Sobrien  if (!new_logfp && logfile) {
81438494Sobrien    plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
81538494Sobrien    return 1;
81638494Sobrien  }
81738494Sobrien
81838494Sobrien  /*
81938494Sobrien   * Close the previous file
82038494Sobrien   */
82138494Sobrien  if (logfp && logfp != stderr)
82238494Sobrien    (void) fclose(logfp);
82338494Sobrien  logfp = new_logfp;
82438494Sobrien
825119679Smbr  if (logfile)
826119679Smbr    plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
827119679Smbr  else
828119679Smbr    plog(XLOG_INFO, "no logfile defined; using stderr");
829119679Smbr
83038494Sobrien  return 0;
83138494Sobrien}
83238494Sobrien
83338494Sobrien
83438494Sobrienvoid
83538494Sobrienunregister_amq(void)
83638494Sobrien{
837174294Sobrien  if (!amuDebug(D_AMQ)) {
83838494Sobrien    /* find which instance of amd to unregister */
839174294Sobrien    u_long amd_prognum = get_amd_program_number();
840174294Sobrien
841174294Sobrien    if (pmap_unset(amd_prognum, AMQ_VERSION) != 1)
842174294Sobrien      dlog("failed to de-register Amd program %lu, version %lu",
843174294Sobrien	   amd_prognum, AMQ_VERSION);
844174294Sobrien  }
84538494Sobrien}
84638494Sobrien
84738494Sobrien
84838494Sobrienvoid
84938494Sobriengoing_down(int rc)
85038494Sobrien{
85138494Sobrien  if (foreground) {
85238494Sobrien    if (amd_state != Start) {
85338494Sobrien      if (amd_state != Done)
85438494Sobrien	return;
85538494Sobrien      unregister_amq();
85638494Sobrien    }
85738494Sobrien  }
858174294Sobrien
859174294Sobrien#ifdef MOUNT_TABLE_ON_FILE
860174294Sobrien  /*
861174294Sobrien   * Call unlock_mntlist to free any important resources such as an on-disk
862174294Sobrien   * lock file (/etc/mtab~).
863174294Sobrien   */
864174294Sobrien  unlock_mntlist();
865174294Sobrien#endif /* MOUNT_TABLE_ON_FILE */
866174294Sobrien
86738494Sobrien  if (foreground) {
86838494Sobrien    plog(XLOG_INFO, "Finishing with status %d", rc);
86938494Sobrien  } else {
87038494Sobrien    dlog("background process exiting with status %d", rc);
87138494Sobrien  }
872174294Sobrien  /* bye bye... */
87338494Sobrien  exit(rc);
87438494Sobrien}
87538494Sobrien
87638494Sobrien
87738494Sobrien/* return the rpc program number under which amd was used */
87838494Sobrienint
87938494Sobrienget_amd_program_number(void)
88038494Sobrien{
88138494Sobrien  return amd_program_number;
88238494Sobrien}
88338494Sobrien
88438494Sobrien
88538494Sobrien/* set the rpc program number used for amd */
88638494Sobrienvoid
88738494Sobrienset_amd_program_number(int program)
88838494Sobrien{
88938494Sobrien  amd_program_number = program;
89038494Sobrien}
89138494Sobrien
89238494Sobrien
89338494Sobrien/*
89438494Sobrien * Release the controlling tty of the process pid.
89538494Sobrien *
89638494Sobrien * Algorithm: try these in order, if available, until one of them
89738494Sobrien * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
89838494Sobrien * Do not use setpgid(): on some OSs it may release the controlling tty,
89938494Sobrien * even if the man page does not mention it, but on other OSs it does not.
90038494Sobrien * Also avoid setpgrp(): it works on some systems, and on others it is
90138494Sobrien * identical to setpgid().
90238494Sobrien */
90338494Sobrienvoid
90438494Sobrienamu_release_controlling_tty(void)
90538494Sobrien{
90638494Sobrien  int fd;
90738494Sobrien
90882794Sobrien  /*
90982794Sobrien   * In daemon mode, leaving open file descriptors to terminals or pipes
91082794Sobrien   * can be a really bad idea.
91182794Sobrien   * Case in point: the redhat startup script calls us through their 'initlog'
912119679Smbr   * program, which exits as soon as the original amd process exits. If,
913119679Smbr   * at some point, a misbehaved library function decides to print something
914119679Smbr   * to the screen, we get a SIGPIPE and die.
915119679Smbr   * And guess what: NIS glibc functions will attempt to print to stderr
91682794Sobrien   * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
91782794Sobrien   * a ypserver.
91882794Sobrien   *
91982794Sobrien   * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
92082794Sobrien   * reopen them as /dev/null.
92182794Sobrien   *
92282794Sobrien   * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
92382794Sobrien   */
924174294Sobrien  fd = open("/dev/null", O_RDWR);
925174294Sobrien  if (fd < 0) {
926174294Sobrien    plog(XLOG_WARNING, "Could not open /dev/null for rw: %m");
927174294Sobrien  } else {
928174294Sobrien    fflush(stdin);  close(0); dup2(fd, 0);
929174294Sobrien    fflush(stdout); close(1); dup2(fd, 1);
930174294Sobrien    fflush(stderr); close(2); dup2(fd, 2);
931174294Sobrien    close(fd);
932174294Sobrien  }
93382794Sobrien
934119679Smbr#ifdef HAVE_SETSID
935119679Smbr  /* XXX: one day maybe use vhangup(2) */
936119679Smbr  if (setsid() < 0) {
937119679Smbr    plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
938119679Smbr  } else {
939119679Smbr    plog(XLOG_INFO, "released controlling tty using setsid()");
940119679Smbr    return;
941119679Smbr  }
942119679Smbr#endif /* HAVE_SETSID */
943119679Smbr
94438494Sobrien#ifdef TIOCNOTTY
94538494Sobrien  fd = open("/dev/tty", O_RDWR);
94638494Sobrien  if (fd < 0) {
94738494Sobrien    /* not an error if already no controlling tty */
94838494Sobrien    if (errno != ENXIO)
94938494Sobrien      plog(XLOG_WARNING, "Could not open controlling tty: %m");
95038494Sobrien  } else {
95138494Sobrien    if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
95238494Sobrien      plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
95338494Sobrien    else
95438494Sobrien      plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
95538494Sobrien    close(fd);
95638494Sobrien  }
95738494Sobrien  return;
95838494Sobrien#endif /* not TIOCNOTTY */
95938494Sobrien
96038494Sobrien  plog(XLOG_ERROR, "unable to release controlling tty");
96138494Sobrien}
962174294Sobrien
963174294Sobrien
964174294Sobrien/* setup a single signal handler */
965174294Sobrienvoid
966174294Sobriensetup_sighandler(int signum, void (*handler)(int))
967174294Sobrien{
968174294Sobrien#ifdef HAVE_SIGACTION
969174294Sobrien  struct sigaction sa;
970174294Sobrien  memset(&sa, 0, sizeof(sa));
971174294Sobrien  sa.sa_flags = 0;		/* unnecessary */
972174294Sobrien  sa.sa_handler = handler;
973174294Sobrien  sigemptyset(&(sa.sa_mask));	/* probably unnecessary too */
974174294Sobrien  sigaddset(&(sa.sa_mask), signum);
975174294Sobrien  sigaction(signum, &sa, NULL);
976174294Sobrien#else /* not HAVE_SIGACTION */
977174294Sobrien  (void) signal(signum, handler);
978174294Sobrien#endif /* not HAVE_SIGACTION */
979174294Sobrien}
980174294Sobrien
981174294Sobrien
982174294Sobrien/*
983174294Sobrien * Return current time in seconds.  If passed a non-null argyument, then
984174294Sobrien * fill it in with the current time in seconds and microseconds (useful
985174294Sobrien * for mtime updates).
986174294Sobrien */
987174294Sobrientime_t
988174294Sobrienclocktime(nfstime *nt)
989174294Sobrien{
990174294Sobrien  static struct timeval now;	/* keep last time, as default */
991174294Sobrien
992174294Sobrien  if (gettimeofday(&now, NULL) < 0) {
993174294Sobrien    plog(XLOG_ERROR, "clocktime: gettimeofday: %m");
994174294Sobrien    /* hack: force time to have incremented by at least 1 second */
995174294Sobrien    now.tv_sec++;
996174294Sobrien  }
997174294Sobrien  /* copy seconds and microseconds. may demote a long to an int */
998174294Sobrien  if (nt) {
999174294Sobrien    nt->nt_seconds = (u_int) now.tv_sec;
1000174294Sobrien    nt->nt_useconds = (u_int) now.tv_usec;
1001174294Sobrien  }
1002174294Sobrien  return (time_t) now.tv_sec;
1003174294Sobrien}
1004174294Sobrien
1005174294Sobrien
1006174294Sobrien/*
1007174294Sobrien * Make all the directories in the path.
1008174294Sobrien */
1009174294Sobrienint
1010174294Sobrienmkdirs(char *path, int mode)
1011174294Sobrien{
1012174294Sobrien  /*
1013174294Sobrien   * take a copy in case path is in readonly store
1014174294Sobrien   */
1015174294Sobrien  char *p2 = strdup(path);
1016174294Sobrien  char *sp = p2;
1017174294Sobrien  struct stat stb;
1018174294Sobrien  int error_so_far = 0;
1019174294Sobrien
1020174294Sobrien  /*
1021174294Sobrien   * Skip through the string make the directories.
1022174294Sobrien   * Mostly ignore errors - the result is tested at the end.
1023174294Sobrien   *
1024174294Sobrien   * This assumes we are root so that we can do mkdir in a
1025174294Sobrien   * mode 555 directory...
1026174294Sobrien   */
1027174294Sobrien  while ((sp = strchr(sp + 1, '/'))) {
1028174294Sobrien    *sp = '\0';
1029174294Sobrien    if (mkdir(p2, mode) < 0) {
1030174294Sobrien      error_so_far = errno;
1031174294Sobrien    } else {
1032174294Sobrien      dlog("mkdir(%s)", p2);
1033174294Sobrien    }
1034174294Sobrien    *sp = '/';
1035174294Sobrien  }
1036174294Sobrien
1037174294Sobrien  if (mkdir(p2, mode) < 0) {
1038174294Sobrien    error_so_far = errno;
1039174294Sobrien  } else {
1040174294Sobrien    dlog("mkdir(%s)", p2);
1041174294Sobrien  }
1042174294Sobrien
1043174294Sobrien  XFREE(p2);
1044174294Sobrien
1045174294Sobrien  return stat(path, &stb) == 0 &&
1046174294Sobrien    (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
1047174294Sobrien}
1048174294Sobrien
1049174294Sobrien
1050174294Sobrien/*
1051174294Sobrien * Remove as many directories in the path as possible.
1052174294Sobrien * Give up if the directory doesn't appear to have
1053174294Sobrien * been created by Amd (not mode dr-x) or an rmdir
1054174294Sobrien * fails for any reason.
1055174294Sobrien */
1056174294Sobrienvoid
1057174294Sobrienrmdirs(char *dir)
1058174294Sobrien{
1059174294Sobrien  char *xdp = strdup(dir);
1060174294Sobrien  char *dp;
1061174294Sobrien
1062174294Sobrien  do {
1063174294Sobrien    struct stat stb;
1064174294Sobrien    /*
1065174294Sobrien     * Try to find out whether this was
1066174294Sobrien     * created by amd.  Do this by checking
1067174294Sobrien     * for owner write permission.
1068174294Sobrien     */
1069174294Sobrien    if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
1070174294Sobrien      if (rmdir(xdp) < 0) {
1071174294Sobrien	if (errno != ENOTEMPTY &&
1072174294Sobrien	    errno != EBUSY &&
1073174294Sobrien	    errno != EEXIST &&
1074174294Sobrien	    errno != EROFS &&
1075174294Sobrien	    errno != EINVAL)
1076174294Sobrien	  plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
1077174294Sobrien	break;
1078174294Sobrien      } else {
1079174294Sobrien	dlog("rmdir(%s)", xdp);
1080174294Sobrien      }
1081174294Sobrien    } else {
1082174294Sobrien      break;
1083174294Sobrien    }
1084174294Sobrien
1085174294Sobrien    dp = strrchr(xdp, '/');
1086174294Sobrien    if (dp)
1087174294Sobrien      *dp = '\0';
1088174294Sobrien  } while (dp && dp > xdp);
1089174294Sobrien
1090174294Sobrien  XFREE(xdp);
1091174294Sobrien}
1092