2 * Copyright (c) 1997-1999 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.8 1999/09/30 21:01:42 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 static void real_plog(int lvl
, char *fmt
, va_list vargs
);
87 * List of debug options.
89 struct opt_tab dbg_opt
[] =
91 {"all", D_ALL
}, /* All */
92 {"amq", D_AMQ
}, /* Register for AMQ program */
93 {"daemon", D_DAEMON
}, /* Enter daemon mode */
94 {"fork", D_FORK
}, /* Fork server (nofork = don't fork) */
95 {"full", D_FULL
}, /* Program trace */
96 /* info service specific debugging (hesiod, nis, etc) */
99 {"mem", D_MEM
}, /* Trace memory allocations */
100 # endif /* DEBUG_MEM */
101 {"mtab", D_MTAB
}, /* Use local mtab file */
102 {"str", D_STR
}, /* Debug string munging */
103 {"test", D_TEST
}, /* Full debug - but no daemon */
104 {"trace", D_TRACE
}, /* Protocol trace */
110 * List of log options
112 struct opt_tab xlog_opt
[] =
114 {"all", XLOG_ALL
}, /* All messages */
116 {"debug", XLOG_DEBUG
}, /* Debug messages */
117 #endif /* DEBUG */ /* DEBUG */
118 {"error", XLOG_ERROR
}, /* Non-fatal system errors */
119 {"fatal", XLOG_FATAL
}, /* Fatal errors */
120 {"info", XLOG_INFO
}, /* Information */
121 {"map", XLOG_MAP
}, /* Map errors */
122 {"stats", XLOG_STATS
}, /* Additional statistical information */
123 {"user", XLOG_USER
}, /* Non-fatal user errors */
124 {"warn", XLOG_WARNING
}, /* Warnings */
125 {"warning", XLOG_WARNING
}, /* Warnings */
131 am_set_progname(char *pn
)
138 am_get_progname(void)
145 am_set_hostname(char *hn
)
147 strncpy(am_hostname
, hn
, MAXHOSTNAMELEN
);
148 am_hostname
[MAXHOSTNAMELEN
] = '\0';
153 am_get_hostname(void)
174 * Avoid malloc's which return NULL for malloc(0)
180 p
= (voidp
) malloc((unsigned) len
);
182 #if defined(DEBUG) && defined(DEBUG_MEM)
184 plog(XLOG_DEBUG
, "Allocated size %d; block %#x", len
, p
);
185 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
189 plog(XLOG_ERROR
, "Retrying memory allocation");
194 plog(XLOG_FATAL
, "Out of memory");
203 /* like xmalloc, but zeros out the bytes */
207 voidp p
= xmalloc(len
);
216 xrealloc(voidp ptr
, int len
)
218 #if defined(DEBUG) && defined(DEBUG_MEM)
219 amuDebug(D_MEM
) plog(XLOG_DEBUG
, "Reallocated size %d; block %#x", len
, ptr
);
220 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
226 ptr
= (voidp
) realloc(ptr
, (unsigned) len
);
228 ptr
= (voidp
) xmalloc((unsigned) len
);
231 plog(XLOG_FATAL
, "Out of memory in realloc");
239 #if defined(DEBUG) && defined(DEBUG_MEM)
241 dxfree(char *file
, int line
, voidp ptr
)
244 plog(XLOG_DEBUG
, "Free in %s:%d: block %#x", file
, line
, ptr
);
245 /* this is the only place that must NOT use XFREE()!!! */
247 ptr
= NULL
; /* paranoid */
249 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */
256 struct mallinfo mi
= mallinfo();
257 u_long uordbytes
= mi
.uordblks
* 4096;
259 if (mem_bytes
!= uordbytes
) {
260 if (orig_mem_bytes
== 0)
261 mem_bytes
= orig_mem_bytes
= uordbytes
;
263 fprintf(logfp
, "%s[%ld]: ", am_get_progname(), (long) am_mypid
);
264 if (mem_bytes
< uordbytes
) {
265 fprintf(logfp
, "ALLOC: %ld bytes", uordbytes
- mem_bytes
);
267 fprintf(logfp
, "FREE: %ld bytes", mem_bytes
- uordbytes
);
269 mem_bytes
= uordbytes
;
270 fprintf(logfp
, ", making %d missing\n", mem_bytes
- orig_mem_bytes
);
275 #endif /* DEBUG_MEM */
279 * Take a log format string and expand occurrences of %m
280 * with the current error code taken from errno. Make sure
281 * 'e' never gets longer than maxlen characters.
284 expand_error(char *f
, char *e
, int maxlen
)
290 for (p
= f
, q
= e
; (*q
= *p
) && len
< maxlen
; len
++, q
++, p
++) {
291 if (p
[0] == '%' && p
[1] == 'm') {
293 if (error
< 0 || error
>= sys_nerr
)
297 errstr
= strerror(error
);
298 #else /* not HAVE_STRERROR */
299 errstr
= sys_errlist
[error
];
300 #endif /* not HAVE_STRERROR */
304 sprintf(q
, "Error %d", error
);
305 len
+= strlen(q
) - 1;
310 e
[maxlen
-1] = '\0'; /* null terminate, to be sure */
315 * Output the time of day and hostname to the logfile
318 show_time_host_and_name(int lvl
)
320 static time_t last_t
= 0;
321 static char *last_ctime
= 0;
322 time_t t
= clocktime();
326 last_ctime
= ctime(&t
);
358 fprintf(logfp
, "%15.15s %s %s[%ld]/%s ",
359 last_ctime
+ 4, am_get_hostname(),
368 * Switch on/off debug options
371 debug_option(char *opt
)
373 return cmdoption(opt
, dbg_opt
, &debug_flags
);
378 dplog(char *fmt
, ...)
383 logfp
= stderr
; /* initialize before possible first use */
386 real_plog(XLOG_DEBUG
, fmt
, ap
);
393 plog(int lvl
, char *fmt
, ...)
398 logfp
= stderr
; /* initialize before possible first use */
401 real_plog(lvl
, fmt
, ap
);
407 real_plog(int lvl
, char *fmt
, va_list vargs
)
412 static char last_msg
[1024];
413 static int last_count
= 0, last_lvl
= 0;
415 if (!(xlog_level
& lvl
))
420 #endif /* DEBUG_MEM */
422 expand_error(fmt
, efmt
, 1024);
424 #ifdef HAVE_VSNPRINTF
425 vsnprintf(ptr
, 1024, efmt
, vargs
);
426 #else /* not HAVE_VSNPRINTF */
428 * XXX: ptr is 1024 bytes long. It is possible to write into it
429 * more than 1024 bytes, if efmt is already large, and vargs expand
430 * as well. This is not as safe as using vsnprintf().
432 vsprintf(ptr
, efmt
, vargs
);
433 msg
[1023] = '\0'; /* null terminate, to be sure */
434 #endif /* not HAVE_VSNPRINTF */
442 switch (lvl
) { /* from mike <mcooper@usc.edu> */
471 syslog(lvl
, "%s", msg
);
474 #endif /* HAVE_SYSLOG */
480 * mimic syslog behavior: only write repeated strings if they differ
482 switch (last_count
) {
483 case 0: /* never printed at all */
485 strncpy(last_msg
, msg
, 1024);
487 show_time_host_and_name(lvl
); /* mimic syslog header */
488 fwrite(msg
, ptr
- msg
, 1, logfp
);
492 case 1: /* item printed once, if same, don't repeat */
493 if (STREQ(last_msg
, msg
)) {
495 } else { /* last msg printed once, new one differs */
496 /* last_count remains at 1 */
497 strncpy(last_msg
, msg
, 1024);
499 show_time_host_and_name(lvl
); /* mimic syslog header */
500 fwrite(msg
, ptr
- msg
, 1, logfp
);
507 * Don't allow repetitions longer than 100, so you can see when something
510 show_time_host_and_name(last_lvl
);
511 sprintf(last_msg
, "last message repeated %d times\n", last_count
);
512 fwrite(last_msg
, strlen(last_msg
), 1, logfp
);
514 last_count
= 0; /* start from scratch */
517 default: /* item repeated multiple times */
518 if (STREQ(last_msg
, msg
)) {
520 } else { /* last msg repeated+skipped, new one differs */
521 show_time_host_and_name(last_lvl
);
522 sprintf(last_msg
, "last message repeated %d times\n", last_count
);
523 fwrite(last_msg
, strlen(last_msg
), 1, logfp
);
524 strncpy(last_msg
, msg
, 1024);
527 show_time_host_and_name(lvl
); /* mimic syslog header */
528 fwrite(msg
, ptr
- msg
, 1, logfp
);
538 * Display current debug options
541 show_opts(int ch
, struct opt_tab
*opts
)
546 fprintf(stderr
, "\t[-%c {no}", ch
);
547 for (i
= 0; opts
[i
].opt
; i
++) {
548 fprintf(stderr
, "%c%s", s
, opts
[i
].opt
);
551 fputs("}]\n", stderr
);
556 cmdoption(char *s
, struct opt_tab
*optb
, int *flags
)
564 struct opt_tab
*dp
, *dpn
= 0;
571 /* check for "no" prefix to options */
572 if (s
[0] == 'n' && s
[1] == 'o') {
581 * Scan the array of debug options to find the
582 * corresponding flag value. If it is found
583 * then set (or clear) the flag (depending on
584 * whether the option was prefixed with "no").
586 for (dp
= optb
; dp
->opt
; dp
++) {
587 if (STREQ(opt
, dp
->opt
))
589 if (opt
!= s
&& !dpn
&& STREQ(s
, dp
->opt
))
593 if (dp
->opt
|| dpn
) {
604 * This will log to stderr when parsing the command line
605 * since any -l option will not yet have taken effect.
607 plog(XLOG_USER
, "option \"%s\" not recognized", s
);
623 * Switch on/off logging options
626 switch_option(char *opt
)
629 int rc
= cmdoption(opt
, xlog_opt
, &xl
);
635 * Keep track of initial log level, and
636 * don't allow options to be turned off.
638 if (xlog_level_init
== ~0)
639 xlog_level_init
= xl
;
641 xl
|= xlog_level_init
;
649 * get syslog facility to use.
650 * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
653 get_syslog_facility(const char *logfile
)
657 /* parse facility string */
658 facstr
= strchr(logfile
, ':');
659 if (!facstr
) /* log file was "syslog" */
662 if (!facstr
|| facstr
[0] == '\0') { /* log file was "syslog:" */
663 plog(XLOG_WARNING
, "null syslog facility, using LOG_DAEMON");
668 if (STREQ(facstr
, "kern"))
670 #endif /* not LOG_KERN */
672 if (STREQ(facstr
, "user"))
674 #endif /* not LOG_USER */
676 if (STREQ(facstr
, "mail"))
678 #endif /* not LOG_MAIL */
680 if (STREQ(facstr
, "daemon"))
684 if (STREQ(facstr
, "auth"))
686 #endif /* not LOG_AUTH */
688 if (STREQ(facstr
, "syslog"))
690 #endif /* not LOG_SYSLOG */
692 if (STREQ(facstr
, "lpr"))
694 #endif /* not LOG_LPR */
696 if (STREQ(facstr
, "news"))
698 #endif /* not LOG_NEWS */
700 if (STREQ(facstr
, "uucp"))
702 #endif /* not LOG_UUCP */
704 if (STREQ(facstr
, "cron"))
706 #endif /* not LOG_CRON */
708 if (STREQ(facstr
, "local0"))
710 #endif /* not LOG_LOCAL0 */
712 if (STREQ(facstr
, "local1"))
714 #endif /* not LOG_LOCAL1 */
716 if (STREQ(facstr
, "local2"))
718 #endif /* not LOG_LOCAL2 */
720 if (STREQ(facstr
, "local3"))
722 #endif /* not LOG_LOCAL3 */
724 if (STREQ(facstr
, "local4"))
726 #endif /* not LOG_LOCAL4 */
728 if (STREQ(facstr
, "local5"))
730 #endif /* not LOG_LOCAL5 */
732 if (STREQ(facstr
, "local6"))
734 #endif /* not LOG_LOCAL6 */
736 if (STREQ(facstr
, "local7"))
738 #endif /* not LOG_LOCAL7 */
740 /* didn't match anything else */
741 plog(XLOG_WARNING
, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr
);
744 #endif /* not LOG_DAEMON */
748 * Change current logfile
751 switch_to_logfile(char *logfile
, int old_umask
)
753 FILE *new_logfp
= stderr
;
758 #endif /* HAVE_SYSLOG */
760 if (STREQ(logfile
, "/dev/stderr"))
762 else if (NSTREQ(logfile
, "syslog", strlen("syslog"))) {
767 openlog(am_get_progname(),
771 # endif /* LOG_CONS */
774 # endif /* LOG_NOWAIT */
776 , get_syslog_facility(logfile
)
777 # endif /* LOG_DAEMON */
779 #else /* not HAVE_SYSLOG */
780 plog(XLOG_WARNING
, "syslog option not supported, logging unchanged");
781 #endif /* not HAVE_SYSLOG */
784 (void) umask(old_umask
);
785 new_logfp
= fopen(logfile
, "a");
791 * If we couldn't open a new file, then continue using the old.
793 if (!new_logfp
&& logfile
) {
794 plog(XLOG_USER
, "%s: Can't open logfile: %m", logfile
);
799 * Close the previous file
801 if (logfp
&& logfp
!= stderr
)
802 (void) fclose(logfp
);
805 plog(XLOG_INFO
, "switched to logfile \"%s\"", logfile
);
816 /* find which instance of amd to unregister */
817 pmap_unset(get_amd_program_number(), AMQ_VERSION
);
825 if (amd_state
!= Start
) {
826 if (amd_state
!= Done
)
832 plog(XLOG_INFO
, "Finishing with status %d", rc
);
835 dlog("background process exiting with status %d", rc
);
843 /* return the rpc program number under which amd was used */
845 get_amd_program_number(void)
847 return amd_program_number
;
851 /* set the rpc program number used for amd */
853 set_amd_program_number(int program
)
855 amd_program_number
= program
;
860 * Release the controlling tty of the process pid.
862 * Algorithm: try these in order, if available, until one of them
863 * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
864 * Do not use setpgid(): on some OSs it may release the controlling tty,
865 * even if the man page does not mention it, but on other OSs it does not.
866 * Also avoid setpgrp(): it works on some systems, and on others it is
867 * identical to setpgid().
870 amu_release_controlling_tty(void)
874 #endif /* TIOCNOTTY */
877 /* XXX: one day maybe use vhangup(2) */
879 plog(XLOG_WARNING
, "Could not release controlling tty using setsid(): %m");
881 plog(XLOG_INFO
, "released controlling tty using setsid()");
884 #endif /* HAVE_SETSID */
887 fd
= open("/dev/tty", O_RDWR
);
889 /* not an error if already no controlling tty */
891 plog(XLOG_WARNING
, "Could not open controlling tty: %m");
893 if (ioctl(fd
, TIOCNOTTY
, 0) < 0 && errno
!= ENOTTY
)
894 plog(XLOG_WARNING
, "Could not disassociate tty (TIOCNOTTY): %m");
896 plog(XLOG_INFO
, "released controlling tty using ioctl(TIOCNOTTY)");
900 #endif /* not TIOCNOTTY */
902 plog(XLOG_ERROR
, "unable to release controlling tty");