2 * Copyright (c) 1997-2004 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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
41 * $Id: xutil.c,v 1.11.2.13 2004/01/06 03:15:24 ezk Exp $
47 #endif /* HAVE_CONFIG_H */
52 * Logfp is the default logging device, and is initialized to stderr by
53 * default in dplog/plog below, and in
54 * amd/amfs_program.c:amfs_program_exec().
58 static char *am_progname = "unknown"; /* "amd" */
59 static char am_hostname[MAXHOSTNAMELEN + 1] = "unknown"; /* Hostname */
60 pid_t am_mypid = -1; /* process ID */
61 serv_state amd_state; /* amd's state */
62 int foreground = 1; /* 1 == this is the top-level server */
69 #endif /* HAVE_SYSLOG */
70 int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
71 int xlog_level_init = ~0;
72 static int amd_program_number = AMQ_PROGRAM;
74 time_t clock_valid = 0;
75 time_t xclock_valid = 0;
79 static int orig_mem_bytes;
80 #endif /* DEBUG_MEM */
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)));
90 * List of debug options.
92 struct opt_tab dbg_opt[] =
94 {"all", D_ALL}, /* All */
95 {"amq", D_AMQ}, /* Register for AMQ program */
96 {"daemon", D_DAEMON}, /* Enter daemon mode */
97 {"fork", D_FORK}, /* Fork server (nofork = don't fork) */
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) */
105 {"mem", D_MEM}, /* Trace memory allocations */
106 # endif /* DEBUG_MEM */
107 {"mtab", D_MTAB}, /* Use local mtab file */
108 {"readdir", D_READDIR}, /* check on browsable_dirs progress */
109 {"str", D_STR}, /* Debug string munging */
110 {"test", D_TEST}, /* Full debug - but no daemon */
111 {"trace", D_TRACE}, /* Protocol trace */
112 {"xdrtrace", D_XDRTRACE}, /* Trace xdr routines */
118 * List of log options
120 struct opt_tab xlog_opt[] =
122 {"all", XLOG_ALL}, /* All messages */
124 {"debug", XLOG_DEBUG}, /* Debug messages */
125 #endif /* DEBUG */ /* DEBUG */
126 {"error", XLOG_ERROR}, /* Non-fatal system errors */
127 {"fatal", XLOG_FATAL}, /* Fatal errors */
128 {"info", XLOG_INFO}, /* Information */
129 {"map", XLOG_MAP}, /* Map errors */
130 {"stats", XLOG_STATS}, /* Additional statistical information */
131 {"user", XLOG_USER}, /* Non-fatal user errors */
132 {"warn", XLOG_WARNING}, /* Warnings */
133 {"warning", XLOG_WARNING}, /* Warnings */
139 am_set_progname(char *pn)
146 am_get_progname(void)
153 am_set_hostname(char *hn)
155 strncpy(am_hostname, hn, MAXHOSTNAMELEN);
156 am_hostname[MAXHOSTNAMELEN] = '\0';
161 am_get_hostname(void)
182 * Avoid malloc's which return NULL for malloc(0)
188 p = (voidp) malloc((unsigned) len);
190 #if defined(DEBUG) && defined(DEBUG_MEM)
192 plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p);
193 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
197 plog(XLOG_ERROR, "Retrying memory allocation");
202 plog(XLOG_FATAL, "Out of memory");
211 /* like xmalloc, but zeros out the bytes */
215 voidp p = xmalloc(len);
224 xrealloc(voidp ptr, int len)
226 #if defined(DEBUG) && defined(DEBUG_MEM)
227 amuDebug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr);
228 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
234 ptr = (voidp) realloc(ptr, (unsigned) len);
236 ptr = (voidp) xmalloc((unsigned) len);
239 plog(XLOG_FATAL, "Out of memory in realloc");
247 #if defined(DEBUG) && defined(DEBUG_MEM)
249 dxfree(char *file, int line, voidp ptr)
252 plog(XLOG_DEBUG, "Free in %s:%d: block %#x", file, line, ptr);
253 /* this is the only place that must NOT use XFREE()!!! */
255 ptr = NULL; /* paranoid */
257 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
264 struct mallinfo mi = mallinfo();
265 u_long uordbytes = mi.uordblks * 4096;
267 if (mem_bytes != uordbytes) {
268 if (orig_mem_bytes == 0)
269 mem_bytes = orig_mem_bytes = uordbytes;
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);
275 fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
277 mem_bytes = uordbytes;
278 fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
283 #endif /* DEBUG_MEM */
287 * Take a log format string and expand occurrences of %m
288 * with the current error code taken from errno. Make sure
289 * 'e' never gets longer than maxlen characters.
292 expand_error(const char *f, char *e, int maxlen)
299 for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
300 if (p[0] == '%' && p[1] == 'm') {
301 strcpy(q, strerror(error));
302 len += strlen(q) - 1;
307 e[maxlen-1] = '\0'; /* null terminate, to be sure */
313 * Output the time of day and hostname to the logfile
316 show_time_host_and_name(int lvl)
318 static time_t last_t = 0;
319 static char *last_ctime = 0;
321 #ifdef HAVE_CLOCK_GETTIME
323 #endif /* HAVE_CLOCK_GETTIME */
324 char nsecs[11] = ""; /* '.' + 9 digits + '\0' */
327 #ifdef HAVE_CLOCK_GETTIME
329 * Some systems (AIX 4.3) seem to implement clock_gettime() as stub
332 if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
336 sprintf(nsecs, ".%09ld", ts.tv_nsec);
340 #endif /* HAVE_CLOCK_GETTIME */
344 last_ctime = ctime(&t);
377 fprintf(logfp, "%15.15s%s %s %s[%ld]/%s ",
378 last_ctime + 4, nsecs, am_get_hostname(),
387 * Switch on/off debug options
390 debug_option(char *opt)
392 return cmdoption(opt, dbg_opt, &debug_flags);
397 dplog(const char *fmt, ...)
402 logfp = stderr; /* initialize before possible first use */
405 real_plog(XLOG_DEBUG, fmt, ap);
412 plog(int lvl, const char *fmt, ...)
417 logfp = stderr; /* initialize before possible first use */
420 real_plog(lvl, fmt, ap);
426 real_plog(int lvl, const char *fmt, va_list vargs)
431 static char last_msg[1024];
432 static int last_count = 0, last_lvl = 0;
434 if (!(xlog_level & lvl))
439 #endif /* DEBUG_MEM */
441 #ifdef HAVE_VSNPRINTF
443 * XXX: ptr is 1024 bytes long, but we may write to ptr[strlen(ptr) + 2]
444 * (to add an '\n', see code below) so we have to limit the string copy
445 * to 1023 (including the '\0').
447 vsnprintf(ptr, 1023, expand_error(fmt, efmt, 1024), vargs);
448 msg[1022] = '\0'; /* null terminate, to be sure */
449 #else /* not HAVE_VSNPRINTF */
451 * XXX: ptr is 1024 bytes long. It is possible to write into it
452 * more than 1024 bytes, if efmt is already large, and vargs expand
453 * as well. This is not as safe as using vsnprintf().
455 vsprintf(ptr, expand_error(fmt, efmt, 1023), vargs);
456 msg[1023] = '\0'; /* null terminate, to be sure */
457 #endif /* not HAVE_VSNPRINTF */
465 switch (lvl) { /* from mike <mcooper@usc.edu> */
494 syslog(lvl, "%s", msg);
497 #endif /* HAVE_SYSLOG */
503 * mimic syslog behavior: only write repeated strings if they differ
505 switch (last_count) {
506 case 0: /* never printed at all */
508 strncpy(last_msg, msg, 1024);
510 show_time_host_and_name(lvl); /* mimic syslog header */
511 fwrite(msg, ptr - msg, 1, logfp);
515 case 1: /* item printed once, if same, don't repeat */
516 if (STREQ(last_msg, msg)) {
518 } else { /* last msg printed once, new one differs */
519 /* last_count remains at 1 */
520 strncpy(last_msg, msg, 1024);
522 show_time_host_and_name(lvl); /* mimic syslog header */
523 fwrite(msg, ptr - msg, 1, logfp);
530 * Don't allow repetitions longer than 100, so you can see when something
533 show_time_host_and_name(last_lvl);
534 sprintf(last_msg, "last message repeated %d times\n", last_count);
535 fwrite(last_msg, strlen(last_msg), 1, logfp);
537 last_count = 0; /* start from scratch */
540 default: /* item repeated multiple times */
541 if (STREQ(last_msg, msg)) {
543 } else { /* last msg repeated+skipped, new one differs */
544 show_time_host_and_name(last_lvl);
545 sprintf(last_msg, "last message repeated %d times\n", last_count);
546 fwrite(last_msg, strlen(last_msg), 1, logfp);
547 strncpy(last_msg, msg, 1024);
550 show_time_host_and_name(lvl); /* mimic syslog header */
551 fwrite(msg, ptr - msg, 1, logfp);
561 * Display current debug options
564 show_opts(int ch, struct opt_tab *opts)
569 fprintf(stderr, "\t[-%c {no}", ch);
570 for (i = 0; opts[i].opt; i++) {
571 fprintf(stderr, "%c%s", s, opts[i].opt);
574 fputs("}]\n", stderr);
579 cmdoption(char *s, struct opt_tab *optb, int *flags)
587 struct opt_tab *dp, *dpn = 0;
594 /* check for "no" prefix to options */
595 if (s[0] == 'n' && s[1] == 'o') {
604 * Scan the array of debug options to find the
605 * corresponding flag value. If it is found
606 * then set (or clear) the flag (depending on
607 * whether the option was prefixed with "no").
609 for (dp = optb; dp->opt; dp++) {
610 if (STREQ(opt, dp->opt))
612 if (opt != s && !dpn && STREQ(s, dp->opt))
616 if (dp->opt || dpn) {
627 * This will log to stderr when parsing the command line
628 * since any -l option will not yet have taken effect.
630 plog(XLOG_USER, "option \"%s\" not recognized", s);
646 * Switch on/off logging options
649 switch_option(char *opt)
652 int rc = cmdoption(opt, xlog_opt, &xl);
658 * Keep track of initial log level, and
659 * don't allow options to be turned off.
661 if (xlog_level_init == ~0)
662 xlog_level_init = xl;
664 xl |= xlog_level_init;
672 * get syslog facility to use.
673 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
676 get_syslog_facility(const char *logfile)
680 /* parse facility string */
681 facstr = strchr(logfile, ':');
682 if (!facstr) /* log file was "syslog" */
685 if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
686 plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
691 if (STREQ(facstr, "kern"))
693 #endif /* not LOG_KERN */
695 if (STREQ(facstr, "user"))
697 #endif /* not LOG_USER */
699 if (STREQ(facstr, "mail"))
701 #endif /* not LOG_MAIL */
703 if (STREQ(facstr, "daemon"))
707 if (STREQ(facstr, "auth"))
709 #endif /* not LOG_AUTH */
711 if (STREQ(facstr, "syslog"))
713 #endif /* not LOG_SYSLOG */
715 if (STREQ(facstr, "lpr"))
717 #endif /* not LOG_LPR */
719 if (STREQ(facstr, "news"))
721 #endif /* not LOG_NEWS */
723 if (STREQ(facstr, "uucp"))
725 #endif /* not LOG_UUCP */
727 if (STREQ(facstr, "cron"))
729 #endif /* not LOG_CRON */
731 if (STREQ(facstr, "local0"))
733 #endif /* not LOG_LOCAL0 */
735 if (STREQ(facstr, "local1"))
737 #endif /* not LOG_LOCAL1 */
739 if (STREQ(facstr, "local2"))
741 #endif /* not LOG_LOCAL2 */
743 if (STREQ(facstr, "local3"))
745 #endif /* not LOG_LOCAL3 */
747 if (STREQ(facstr, "local4"))
749 #endif /* not LOG_LOCAL4 */
751 if (STREQ(facstr, "local5"))
753 #endif /* not LOG_LOCAL5 */
755 if (STREQ(facstr, "local6"))
757 #endif /* not LOG_LOCAL6 */
759 if (STREQ(facstr, "local7"))
761 #endif /* not LOG_LOCAL7 */
763 /* didn't match anything else */
764 plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
767 #endif /* not LOG_DAEMON */
771 * Change current logfile
774 switch_to_logfile(char *logfile, int old_umask)
776 FILE *new_logfp = stderr;
781 #endif /* HAVE_SYSLOG */
783 if (STREQ(logfile, "/dev/stderr"))
785 else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
790 openlog(am_get_progname(),
794 # endif /* LOG_CONS */
797 # endif /* LOG_NOWAIT */
799 , get_syslog_facility(logfile)
800 # endif /* LOG_DAEMON */
802 #else /* not HAVE_SYSLOG */
803 plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
804 #endif /* not HAVE_SYSLOG */
807 (void) umask(old_umask);
808 new_logfp = fopen(logfile, "a");
814 * If we couldn't open a new file, then continue using the old.
816 if (!new_logfp && logfile) {
817 plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
822 * Close the previous file
824 if (logfp && logfp != stderr)
825 (void) fclose(logfp);
829 plog(XLOG_INFO, "switched to logfile \"%s\"", logfile);
831 plog(XLOG_INFO, "no logfile defined; using stderr");
843 /* find which instance of amd to unregister */
844 pmap_unset(get_amd_program_number(), AMQ_VERSION);
852 if (amd_state != Start) {
853 if (amd_state != Done)
859 plog(XLOG_INFO, "Finishing with status %d", rc);
862 dlog("background process exiting with status %d", rc);
870 /* return the rpc program number under which amd was used */
872 get_amd_program_number(void)
874 return amd_program_number;
878 /* set the rpc program number used for amd */
880 set_amd_program_number(int program)
882 amd_program_number = program;
887 * Release the controlling tty of the process pid.
889 * Algorithm: try these in order, if available, until one of them
890 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
891 * Do not use setpgid(): on some OSs it may release the controlling tty,
892 * even if the man page does not mention it, but on other OSs it does not.
893 * Also avoid setpgrp(): it works on some systems, and on others it is
894 * identical to setpgid().
897 amu_release_controlling_tty(void)
901 #endif /* TIOCNOTTY */
905 * In daemon mode, leaving open file descriptors to terminals or pipes
906 * can be a really bad idea.
907 * Case in point: the redhat startup script calls us through their 'initlog'
908 * program, which exits as soon as the original amd process exits. If,
909 * at some point, a misbehaved library function decides to print something
910 * to the screen, we get a SIGPIPE and die.
911 * And guess what: NIS glibc functions will attempt to print to stderr
912 * "YPBINDPROC_DOMAIN: Domain not bound" if ypbind is running but can't find
915 * So we close all of our "terminal" filedescriptors, i.e. 0, 1 and 2, then
916 * reopen them as /dev/null.
918 * XXX We should also probably set the SIGPIPE handler to SIG_IGN.
920 tempfd = open("/dev/null", O_RDWR);
921 fflush(stdin); close(0); dup2(tempfd, 0);
922 fflush(stdout); close(1); dup2(tempfd, 1);
923 fflush(stderr); close(2); dup2(tempfd, 2);
927 /* XXX: one day maybe use vhangup(2) */
929 plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
931 plog(XLOG_INFO, "released controlling tty using setsid()");
934 #endif /* HAVE_SETSID */
937 fd = open("/dev/tty", O_RDWR);
939 /* not an error if already no controlling tty */
941 plog(XLOG_WARNING, "Could not open controlling tty: %m");
943 if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
944 plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
946 plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
950 #endif /* not TIOCNOTTY */
952 plog(XLOG_ERROR, "unable to release controlling tty");