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