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