xref: /openbsd-src/usr.bin/rdist/message.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: message.c,v 1.15 2003/06/03 02:56:14 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "defs.h"
33 
34 #ifndef lint
35 #if 0
36 static char RCSid[] __attribute__((__unused__)) =
37 "$From: message.c,v 1.5 1999/11/01 00:21:39 christos Exp $";
38 #else
39 static char RCSid[] __attribute__((__unused__)) =
40 "$OpenBSD: message.c,v 1.15 2003/06/03 02:56:14 millert Exp $";
41 #endif
42 
43 static char sccsid[] __attribute__((__unused__)) =
44 "@(#)common.c";
45 
46 static char copyright[] __attribute__((__unused__)) =
47 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
48  All rights reserved.\n";
49 #endif /* !lint */
50 
51 /*
52  * Message handling functions for both rdist and rdistd.
53  */
54 
55 
56 #define MSGBUFSIZ	32*1024
57 
58 int			debug = 0;		/* Debugging level */
59 int			nerrs = 0;		/* Number of errors */
60 
61 /*
62  * Message Types
63  */
64 MSGTYPE msgtypes[] = {
65 	{ MT_CHANGE,	"change" },
66 	{ MT_INFO,	"info" },
67 	{ MT_NOTICE,	"notice" },
68 	{ MT_NERROR,	"nerror" },
69 	{ MT_FERROR,	"ferror" },
70 	{ MT_WARNING,	"warning" },
71 	{ MT_VERBOSE,	"verbose" },
72 	{ MT_ALL,	"all" },
73 	{ MT_DEBUG,	"debug" },
74 	{ 0 },
75 };
76 
77 static void msgsendstdout(MSGFACILITY *, int, int, char *);
78 static void msgsendsyslog(MSGFACILITY *, int, int, char *);
79 static void msgsendfile(MSGFACILITY *, int, int, char *);
80 static void msgsendnotify(MSGFACILITY *, int, int, char *);
81 
82 /*
83  * Message Facilities
84  */
85 MSGFACILITY msgfacility[] = {
86 	{ MF_STDOUT,	"stdout",	msgsendstdout },
87 	{ MF_FILE,	"file",		msgsendfile },
88 	{ MF_SYSLOG,	"syslog",	msgsendsyslog },
89 	{ MF_NOTIFY,	"notify",	msgsendnotify },
90 	{ 0 },
91 };
92 
93 static MSGFACILITY *getmsgfac(char *);
94 static MSGTYPE *getmsgtype(char *);
95 static char *setmsgtypes(MSGFACILITY *, char *);
96 static void _message(int, char *);
97 static void _debugmsg(int, char *);
98 static void _error(char *);
99 static void _fatalerr(char *);
100 
101 /*
102  * Print message logging usage message
103  */
104 void
105 msgprusage(void)
106 {
107 	int i, x;
108 
109 	(void) fprintf(stderr, "\nWhere <msgopt> is of form\n");
110 	(void) fprintf(stderr,
111        "\t<facility1>=<type1>,<type2>,...:<facility2>=<type1>,<type2>...\n");
112 
113 	(void) fprintf(stderr, "Valid <facility> names:");
114 
115 	for (i = 0; msgfacility[i].mf_name; ++i)
116 		(void) fprintf(stderr, " %s", msgfacility[i].mf_name);
117 
118 	(void) fprintf(stderr, "\nValid <type> names:");
119 	for (x = 0; msgtypes[x].mt_name; ++x)
120 		(void) fprintf(stderr, " %s", msgtypes[x].mt_name);
121 
122 	(void) fprintf(stderr, "\n");
123 }
124 
125 /*
126  * Print enabled message logging info
127  */
128 void
129 msgprconfig(void)
130 {
131 	int i, x;
132 	static char buf[MSGBUFSIZ];
133 
134 	debugmsg(DM_MISC, "Current message logging config:");
135 	for (i = 0; msgfacility[i].mf_name; ++i) {
136 		(void) snprintf(buf, sizeof(buf), "    %.*s=",
137 			       (int)(sizeof(buf) - 7), msgfacility[i].mf_name);
138 		for (x = 0; msgtypes[x].mt_name; ++x)
139 			if (IS_ON(msgfacility[i].mf_msgtypes,
140 				  msgtypes[x].mt_type)) {
141 				if (x > 0)
142 					(void) strlcat(buf, ",", sizeof(buf));
143 				(void) strlcat(buf, msgtypes[x].mt_name,
144 				    sizeof(buf));
145 			}
146 		debugmsg(DM_MISC, "%s", buf);
147 	}
148 
149 }
150 
151 /*
152  * Get the Message Facility entry "name"
153  */
154 static MSGFACILITY *
155 getmsgfac(char *name)
156 {
157 	int i;
158 
159 	for (i = 0; msgfacility[i].mf_name; ++i)
160 		if (strcasecmp(name, msgfacility[i].mf_name) == 0)
161 			return(&msgfacility[i]);
162 
163 	return(NULL);
164 }
165 
166 /*
167  * Get the Message Type entry named "name"
168  */
169 static MSGTYPE *
170 getmsgtype(char *name)
171 {
172 	int i;
173 
174 	for (i = 0; msgtypes[i].mt_name; ++i)
175 		if (strcasecmp(name, msgtypes[i].mt_name) == 0)
176 			return(&msgtypes[i]);
177 
178 	return(NULL);
179 }
180 
181 /*
182  * Set Message Type information for Message Facility "msgfac" as
183  * indicated by string "str".
184  */
185 static char *
186 setmsgtypes(MSGFACILITY *msgfac, char *str)
187 {
188 	static char ebuf[BUFSIZ];
189 	char *cp;
190 	char *strptr, *word;
191 	MSGTYPE *mtp;
192 
193 	/*
194 	 * MF_SYSLOG is the only supported message facility for the server
195 	 */
196 	if (isserver && (msgfac->mf_msgfac != MF_SYSLOG &&
197 			 msgfac->mf_msgfac != MF_FILE)) {
198 		(void) snprintf(ebuf, sizeof(ebuf),
199 		"The \"%.*s\" message facility cannot be used by the server.",
200 			        100, msgfac->mf_name);
201 		return(ebuf);
202 	}
203 
204 	strptr = str;
205 
206 	/*
207 	 * Do any necessary Message Facility preparation
208 	 */
209 	switch(msgfac->mf_msgfac) {
210 	case MF_FILE:
211 		/*
212 		 * The MF_FILE string should look like "<file>=<types>".
213 		 */
214 		if ((cp = strchr(strptr, '=')) == NULL)
215 			return(
216 			   "No file name found for \"file\" message facility");
217 		*cp++ = CNULL;
218 
219 		if ((msgfac->mf_fptr = fopen(strptr, "w")) == NULL)
220 			fatalerr("Cannot open log file for writing: %s: %s.",
221 				 strptr, SYSERR);
222 		msgfac->mf_filename = xstrdup(strptr);
223 
224 		strptr = cp;
225 		break;
226 
227 	case MF_NOTIFY:
228 		break;
229 
230 	case MF_STDOUT:
231 		msgfac->mf_fptr = stdout;
232 		break;
233 
234 	case MF_SYSLOG:
235 #if defined(LOG_OPTS)
236 #if	defined(LOG_FACILITY)
237 		openlog(progname, LOG_OPTS, LOG_FACILITY);
238 #else
239 		openlog(progname, LOG_OPTS);
240 #endif	/* LOG_FACILITY */
241 #endif	/* LOG_OPTS */
242 		break;
243 	}
244 
245 	/*
246 	 * Parse each type word
247 	 */
248 	msgfac->mf_msgtypes = 0;	/* Start from scratch */
249 	while (strptr) {
250 		word = strptr;
251 		if ((cp = strchr(strptr, ',')) != NULL)
252 			*cp++ = CNULL;
253 		strptr = cp;
254 
255 		if ((mtp = getmsgtype(word)) != NULL) {
256 			msgfac->mf_msgtypes |= mtp->mt_type;
257 			/*
258 			 * XXX This is really a kludge until we add real
259 			 * control over debugging.
260 			 */
261 			if (!debug && isserver &&
262 			    strcasecmp(word, "debug") == 0)
263 				debug = DM_ALL;
264 		} else {
265 			(void) snprintf(ebuf, sizeof(ebuf),
266 				        "Message type \"%.*s\" is invalid.",
267 				        100, word);
268 			return(ebuf);
269 		}
270 	}
271 
272 	return(NULL);
273 }
274 
275 /*
276  * Parse a message logging option string
277  */
278 char *
279 msgparseopts(char *msgstr, int doset)
280 {
281 	static char ebuf[BUFSIZ], msgbuf[MSGBUFSIZ];
282 	char *cp, *optstr;
283 	char *word;
284 	MSGFACILITY *msgfac;
285 
286 	if (msgstr == NULL)
287 		return("NULL message string");
288 
289 	/* strtok() is harmful */
290 	(void) strlcpy(msgbuf, msgstr, sizeof(msgbuf));
291 
292 	/*
293 	 * Each <facility>=<types> list is separated by ":".
294 	 */
295 	for (optstr = strtok(msgbuf, ":"); optstr;
296 	     optstr = strtok(NULL, ":")) {
297 
298 		if ((cp = strchr(optstr, '=')) == NULL)
299 			return("No '=' found");
300 
301 		*cp++ = CNULL;
302 		word = optstr;
303 		if ((int)strlen(word) <= 0)
304 			return("No message facility specified");
305 		if ((int)strlen(cp) <= 0)
306 			return("No message type specified");
307 
308 		if ((msgfac = getmsgfac(word)) == NULL) {
309 			(void) snprintf(ebuf, sizeof(ebuf),
310 				        "%.*s is not a valid message facility",
311 				        100, word);
312 			return(ebuf);
313 		}
314 
315 		if (doset) {
316 			char *mcp;
317 
318 			if ((mcp = setmsgtypes(msgfac, cp)) != NULL)
319 				return(mcp);
320 		}
321 	}
322 
323 	if (isserver && debug) {
324 		debugmsg(DM_MISC, "%s", getversion());
325 		msgprconfig();
326 	}
327 
328 	return(NULL);
329 }
330 
331 /*
332  * Send a message to facility "stdout".
333  * For rdistd, this is really the rdist client.
334  */
335 static void
336 msgsendstdout(MSGFACILITY *msgfac, int mtype, int flags, char *msgbuf)
337 {
338 	char cmd;
339 
340 	if (isserver) {
341 		if (rem_w < 0 || IS_ON(flags, MT_NOREMOTE))
342 			return;
343 
344 		cmd = CNULL;
345 
346 		switch(mtype) {
347 		case MT_NERROR:		cmd = C_ERRMSG;		break;
348 		case MT_FERROR:		cmd = C_FERRMSG;	break;
349 		case MT_NOTICE:		cmd = C_NOTEMSG;	break;
350 		case MT_REMOTE:		cmd = C_LOGMSG;		break;
351 		}
352 
353 		if (cmd != CNULL)
354 			(void) sendcmd(cmd, "%s", msgbuf);
355 	} else {
356 		switch(mtype) {
357 		case MT_FERROR:
358 		case MT_NERROR:
359 			if (msgbuf && *msgbuf) {
360 				(void) fprintf(stderr, "%s\n", msgbuf);
361 				(void) fflush(stderr);
362 			}
363 			break;
364 
365 		case MT_DEBUG:
366 			/*
367 			 * Only things that are strictly MT_DEBUG should
368 			 * be shown.
369 			 */
370 			if (flags != MT_DEBUG)
371 				return;
372 		case MT_NOTICE:
373 		case MT_CHANGE:
374 		case MT_INFO:
375 		case MT_VERBOSE:
376 		case MT_WARNING:
377 			if (msgbuf && *msgbuf) {
378 				(void) printf("%s\n", msgbuf);
379 				(void) fflush(stdout);
380 			}
381 			break;
382 		}
383 	}
384 }
385 
386 /*
387  * Send a message to facility "syslog"
388  */
389 static void
390 msgsendsyslog(MSGFACILITY *msgfac, int mtype, int flags, char *msgbuf)
391 {
392 	int syslvl = 0;
393 
394 	if (!msgbuf || !*msgbuf)
395 		return;
396 
397 	switch(mtype) {
398 #if	defined(SL_NERROR)
399 	case MT_NERROR:		syslvl = SL_NERROR;	break;
400 #endif
401 #if	defined(SL_FERROR)
402 	case MT_FERROR:		syslvl = SL_FERROR;	break;
403 #endif
404 #if	defined(SL_WARNING)
405 	case MT_WARNING:	syslvl = SL_WARNING;	break;
406 #endif
407 #if	defined(SL_CHANGE)
408 	case MT_CHANGE:		syslvl = SL_CHANGE;	break;
409 #endif
410 #if	defined(SL_INFO)
411 	case MT_SYSLOG:
412 	case MT_VERBOSE:
413 	case MT_INFO:		syslvl = SL_INFO;	break;
414 #endif
415 #if	defined(SL_NOTICE)
416 	case MT_NOTICE:		syslvl = SL_NOTICE;	break;
417 #endif
418 #if	defined(SL_DEBUG)
419 	case MT_DEBUG:		syslvl = SL_DEBUG;	break;
420 #endif
421 	}
422 
423 	if (syslvl)
424 		syslog(syslvl, "%s", msgbuf);
425 }
426 
427 /*
428  * Send a message to a "file" facility.
429  */
430 static void
431 msgsendfile(MSGFACILITY *msgfac, int mtype, int flags, char *msgbuf)
432 {
433 	if (msgfac->mf_fptr == NULL)
434 		return;
435 
436 	if (!msgbuf || !*msgbuf)
437 		return;
438 
439 	(void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
440 	(void) fflush(msgfac->mf_fptr);
441 }
442 
443 /*
444  * Same method as msgsendfile()
445  */
446 static void
447 msgsendnotify(MSGFACILITY *msgfac, int mtype, int flags, char *msgbuf)
448 {
449 	char *tempfile;
450 
451 	if (IS_ON(flags, MT_DEBUG))
452 		return;
453 
454 	if (!msgbuf || !*msgbuf)
455 		return;
456 
457 	if (!msgfac->mf_fptr) {
458 		char *cp;
459 		int fd;
460 		size_t len;
461 
462 		/*
463 		 * Create and open a new temporary file
464 		 */
465 		if ((cp = getenv("TMPDIR")) == NULL || *cp == '\0')
466 			cp = _PATH_TMP;
467 		len = strlen(cp) + 1 + sizeof(_RDIST_TMP);
468 		tempfile = (char *) xmalloc(len);
469 		(void) snprintf(tempfile, len, "%s/%s", cp, _RDIST_TMP);
470 
471 		msgfac->mf_filename = tempfile;
472 		if ((fd = mkstemp(msgfac->mf_filename)) < 0 ||
473 		    (msgfac->mf_fptr = fdopen(fd, "w")) == NULL)
474 		    fatalerr("Cannot open notify file for writing: %s: %s.",
475 			msgfac->mf_filename, SYSERR);
476 		debugmsg(DM_MISC, "Created notify temp file '%s'",
477 			 msgfac->mf_filename);
478 	}
479 
480 	if (msgfac->mf_fptr == NULL)
481 		return;
482 
483 	(void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
484 	(void) fflush(msgfac->mf_fptr);
485 }
486 
487 /*
488  * Insure currenthost is set to something reasonable.
489  */
490 void
491 checkhostname(void)
492 {
493 	static char mbuf[MAXHOSTNAMELEN];
494 	char *cp;
495 
496 	if (!currenthost) {
497 		if (gethostname(mbuf, sizeof(mbuf)) == 0) {
498 			if ((cp = strchr(mbuf, '.')) != NULL)
499 				*cp = CNULL;
500 			currenthost = xstrdup(mbuf);
501 		} else
502 			currenthost = "(unknown)";
503 	}
504 }
505 
506 /*
507  * Print a message contained in "msgbuf" if a level "lvl" is set.
508  */
509 static void
510 _message(int flags, char *msgbuf)
511 {
512 	int i, x;
513 	char *cp;
514 	static char mbuf[2048];
515 
516 	if (msgbuf && *msgbuf) {
517 		/*
518 		 * Ensure no stray newlines are present
519 		 */
520 		if ((cp = strchr(msgbuf, '\n')) != NULL)
521 			*cp = CNULL;
522 
523 		checkhostname();
524 		if (strncmp(currenthost, msgbuf, strlen(currenthost)) == 0)
525 			(void) strlcpy(mbuf, msgbuf, sizeof(mbuf));
526 		else
527 			(void) snprintf(mbuf, sizeof(mbuf),
528 					"%s: %s", currenthost, msgbuf);
529 	} else
530 		mbuf[0] = '\0';
531 
532 	/*
533 	 * Special case for messages that only get
534 	 * logged to the system log facility
535 	 */
536 	if (IS_ON(flags, MT_SYSLOG)) {
537 		msgsendsyslog(NULL, MT_SYSLOG, flags, mbuf);
538 		return;
539 	}
540 
541 	/*
542 	 * Special cases
543 	 */
544 	if (isserver && IS_ON(flags, MT_NOTICE)) {
545 		msgsendstdout(NULL, MT_NOTICE, flags, mbuf);
546 		return;
547 	} else if (isserver && IS_ON(flags, MT_REMOTE))
548 		msgsendstdout(NULL, MT_REMOTE, flags, mbuf);
549 	else if (isserver && IS_ON(flags, MT_NERROR))
550 		msgsendstdout(NULL, MT_NERROR, flags, mbuf);
551 	else if (isserver && IS_ON(flags, MT_FERROR))
552 		msgsendstdout(NULL, MT_FERROR, flags, mbuf);
553 
554 	/*
555 	 * For each Message Facility, check each Message Type to see
556 	 * if the bits in "flags" are set.  If so, call the appropriate
557 	 * Message Facility to dispatch the message.
558 	 */
559 	for (i = 0; msgfacility[i].mf_name; ++i)
560 		for (x = 0; msgtypes[x].mt_name; ++x)
561 			/*
562 			 * XXX MT_ALL should not be used directly
563 			 */
564 			if (msgtypes[x].mt_type != MT_ALL &&
565 			    IS_ON(flags, msgtypes[x].mt_type) &&
566 			    IS_ON(msgfacility[i].mf_msgtypes,
567 				  msgtypes[x].mt_type))
568 				(*msgfacility[i].mf_sendfunc)(&msgfacility[i],
569 							   msgtypes[x].mt_type,
570 							      flags,
571 							      mbuf);
572 }
573 
574 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
575 /*
576  * Varargs front-end to _message()
577  */
578 void
579 message(va_alist)
580 	va_dcl
581 {
582 	static char buf[MSGBUFSIZ];
583 	va_list args;
584 	char *fmt;
585 	int lvl;
586 
587 	va_start(args);
588 	lvl = (int) va_arg(args, int);
589 	fmt = (char *) va_arg(args, char *);
590 	va_end(args);
591 
592 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
593 
594 	_message(lvl, buf);
595 }
596 #endif	/* ARG_VARARGS */
597 
598 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
599 /*
600  * Stdarg front-end to _message()
601  */
602 void
603 message(int lvl, char *fmt, ...)
604 {
605 	static char buf[MSGBUFSIZ];
606 	va_list args;
607 
608 	va_start(args, fmt);
609 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
610 	va_end(args);
611 
612 	_message(lvl, buf);
613 }
614 #endif	/* ARG_STDARG */
615 
616 /*
617  * Display a debugging message
618  */
619 static void
620 _debugmsg(int lvl, char *buf)
621 {
622 	if (IS_ON(debug, lvl))
623 		_message(MT_DEBUG, buf);
624 }
625 
626 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
627 /*
628  * Varargs front-end to _debugmsg()
629  */
630 void
631 debugmsg(va_alist)
632 	va_dcl
633 {
634 	static char buf[MSGBUFSIZ];
635 	va_list args;
636 	char *fmt;
637 	int lvl;
638 
639 	va_start(args);
640 	lvl = (int) va_arg(args, int);
641 	fmt = (char *) va_arg(args, char *);
642 	va_end(args);
643 
644 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
645 
646 	_debugmsg(lvl, buf);
647 }
648 #endif	/* ARG_VARARGS */
649 
650 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
651 /*
652  * Stdarg front-end to _debugmsg()
653  */
654 void
655 debugmsg(int lvl, char *fmt, ...)
656 {
657 	static char buf[MSGBUFSIZ];
658 	va_list args;
659 
660 	va_start(args, fmt);
661 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
662 	va_end(args);
663 
664 	_debugmsg(lvl, buf);
665 }
666 #endif	/* ARG_STDARG */
667 
668 /*
669  * Print an error message
670  */
671 static void
672 _error(char *msg)
673 {
674 	static char buf[MSGBUFSIZ];
675 
676 	nerrs++;
677 	buf[0] = CNULL;
678 
679 	if (msg) {
680 		if (isserver)
681 			(void) snprintf(buf, sizeof(buf),
682 					"REMOTE ERROR: %s", msg);
683 		else
684 			(void) snprintf(buf, sizeof(buf),
685 					"LOCAL ERROR: %s", msg);
686 	}
687 
688 	_message(MT_NERROR, (buf[0]) ? buf : NULL);
689 }
690 
691 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
692 /*
693  * Varargs frontend to _error()
694  */
695 void
696 error(va_alist)
697 	va_dcl
698 {
699 	static char buf[MSGBUFSIZ];
700 	va_list args;
701 	char *fmt;
702 
703 	buf[0] = CNULL;
704 	va_start(args);
705 	fmt = (char *) va_arg(args, char *);
706 	if (fmt)
707 		(void) vsnprintf(buf, sizeof(buf), fmt, args);
708 	va_end(args);
709 
710 	_error((buf[0]) ? buf : NULL);
711 }
712 #endif	/* ARG_VARARGS */
713 
714 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
715 /*
716  * Stdarg frontend to _error()
717  */
718 void
719 error(char *fmt, ...)
720 {
721 	static char buf[MSGBUFSIZ];
722 	va_list args;
723 
724 	buf[0] = CNULL;
725 	va_start(args, fmt);
726 	if (fmt)
727 		(void) vsnprintf(buf, sizeof(buf), fmt, args);
728 	va_end(args);
729 
730 	_error((buf[0]) ? buf : NULL);
731 }
732 #endif	/* ARG_STDARG */
733 
734 /*
735  * Display a fatal message
736  */
737 static void
738 _fatalerr(char *msg)
739 {
740 	static char buf[MSGBUFSIZ];
741 
742 	++nerrs;
743 
744 	if (isserver)
745 		(void) snprintf(buf, sizeof(buf), "REMOTE ERROR: %s", msg);
746 	else
747 		(void) snprintf(buf, sizeof(buf), "LOCAL ERROR: %s", msg);
748 
749 	_message(MT_FERROR, buf);
750 
751 	exit(nerrs);
752 }
753 
754 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
755 /*
756  * Varargs front-end to _fatalerr()
757  */
758 void
759 fatalerr(va_alist)
760 	va_dcl
761 {
762 	static char buf[MSGBUFSIZ];
763 	va_list args;
764 	char *fmt;
765 
766 	va_start(args);
767 	fmt = (char *) va_arg(args, char *);
768 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
769 	va_end(args);
770 
771 	_fatalerr(buf);
772 }
773 #endif	/* ARG_VARARGS */
774 
775 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
776 /*
777  * Stdarg front-end to _fatalerr()
778  */
779 void
780 fatalerr(char *fmt, ...)
781 {
782 	static char buf[MSGBUFSIZ];
783 	va_list args;
784 
785 	va_start(args, fmt);
786 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
787 	va_end(args);
788 
789 	_fatalerr(buf);
790 }
791 #endif	/* ARG_STDARG */
792 
793 /*
794  * Get the name of the file used for notify.
795  * A side effect is that the file pointer to the file
796  * is closed.  We assume this function is only called when
797  * we are ready to read the file.
798  */
799 char *
800 getnotifyfile(void)
801 {
802 	int i;
803 
804 	for (i = 0; msgfacility[i].mf_name; i++)
805 		if (msgfacility[i].mf_msgfac == MF_NOTIFY &&
806 		    msgfacility[i].mf_fptr) {
807 			(void) fclose(msgfacility[i].mf_fptr);
808 			msgfacility[i].mf_fptr = NULL;
809 			return(msgfacility[i].mf_filename);
810 		}
811 
812 	return(NULL);
813 }
814