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