xref: /netbsd/external/bsd/am-utils/dist/libamu/xutil.c (revision 31bdb48a)
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
am_set_progname(char * pn)135 am_set_progname(char *pn)
136 {
137   am_progname = pn;
138 }
139 
140 
141 const char *
am_get_progname(void)142 am_get_progname(void)
143 {
144   return am_progname;
145 }
146 
147 
148 void
am_set_hostname(char * hn)149 am_set_hostname(char *hn)
150 {
151   xstrlcpy(am_hostname, hn, sizeof(am_hostname));
152 }
153 
154 
155 const char *
am_get_hostname(void)156 am_get_hostname(void)
157 {
158   return am_hostname;
159 }
160 
161 
162 pid_t
am_set_mypid(void)163 am_set_mypid(void)
164 {
165   am_mypid = getpid();
166   return am_mypid;
167 }
168 
169 
170 long
get_server_pid()171 get_server_pid()
172 {
173   return (long) (foreground ? am_mypid : getppid());
174 }
175 
176 
177 voidp
xmalloc(int len)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
xzalloc(int len)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
xrealloc(voidp ptr,int len)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
dxfree(char * file,int line,voidp ptr)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
checkup_mem(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 *
expand_error(const char * f,char * e,size_t maxlen)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
show_time_host_and_name(int lvl)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
debug_option(char * opt)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
dplog(const char * fmt,...)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
plog(int lvl,const char * fmt,...)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
real_plog(int lvl,const char * fmt,va_list vargs)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@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
show_opts(int ch,struct opt_tab * opts)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
cmdoption(char * s,struct opt_tab * optb,u_int * flags)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
switch_option(char * opt)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
get_syslog_facility(const char * logfile)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
switch_to_logfile(char * logfile,int old_umask,int truncate_log)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
unregister_amq(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
going_down(int rc)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
get_amd_program_number(void)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
set_amd_program_number(u_long program)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
amu_release_controlling_tty(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
setup_sighandler(int signum,void (* handler)(int))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
clocktime(nfstime * nt)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
mkdirs(char * path,int mode)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
rmdirs(char * dir)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 *
xstrdup(const char * s)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