xref: /openbsd-src/usr.bin/rdist/message.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: message.c,v 1.17 2007/09/14 14:29:20 chl 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.17 2007/09/14 14:29:20 chl 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 	static char mbuf[2048];
514 
515 	if (msgbuf && *msgbuf) {
516 		/*
517 		 * Ensure no stray newlines are present
518 		 */
519 		msgbuf[strcspn(msgbuf, "\n")] = CNULL;
520 
521 		checkhostname();
522 		if (strncmp(currenthost, msgbuf, strlen(currenthost)) == 0)
523 			(void) strlcpy(mbuf, msgbuf, sizeof(mbuf));
524 		else
525 			(void) snprintf(mbuf, sizeof(mbuf),
526 					"%s: %s", currenthost, msgbuf);
527 	} else
528 		mbuf[0] = '\0';
529 
530 	/*
531 	 * Special case for messages that only get
532 	 * logged to the system log facility
533 	 */
534 	if (IS_ON(flags, MT_SYSLOG)) {
535 		msgsendsyslog(NULL, MT_SYSLOG, flags, mbuf);
536 		return;
537 	}
538 
539 	/*
540 	 * Special cases
541 	 */
542 	if (isserver && IS_ON(flags, MT_NOTICE)) {
543 		msgsendstdout(NULL, MT_NOTICE, flags, mbuf);
544 		return;
545 	} else if (isserver && IS_ON(flags, MT_REMOTE))
546 		msgsendstdout(NULL, MT_REMOTE, flags, mbuf);
547 	else if (isserver && IS_ON(flags, MT_NERROR))
548 		msgsendstdout(NULL, MT_NERROR, flags, mbuf);
549 	else if (isserver && IS_ON(flags, MT_FERROR))
550 		msgsendstdout(NULL, MT_FERROR, flags, mbuf);
551 
552 	/*
553 	 * For each Message Facility, check each Message Type to see
554 	 * if the bits in "flags" are set.  If so, call the appropriate
555 	 * Message Facility to dispatch the message.
556 	 */
557 	for (i = 0; msgfacility[i].mf_name; ++i)
558 		for (x = 0; msgtypes[x].mt_name; ++x)
559 			/*
560 			 * XXX MT_ALL should not be used directly
561 			 */
562 			if (msgtypes[x].mt_type != MT_ALL &&
563 			    IS_ON(flags, msgtypes[x].mt_type) &&
564 			    IS_ON(msgfacility[i].mf_msgtypes,
565 				  msgtypes[x].mt_type))
566 				(*msgfacility[i].mf_sendfunc)(&msgfacility[i],
567 							   msgtypes[x].mt_type,
568 							      flags,
569 							      mbuf);
570 }
571 
572 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
573 /*
574  * Varargs front-end to _message()
575  */
576 void
577 message(va_alist)
578 	va_dcl
579 {
580 	static char buf[MSGBUFSIZ];
581 	va_list args;
582 	char *fmt;
583 	int lvl;
584 
585 	va_start(args);
586 	lvl = (int) va_arg(args, int);
587 	fmt = (char *) va_arg(args, char *);
588 	va_end(args);
589 
590 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
591 
592 	_message(lvl, buf);
593 }
594 #endif	/* ARG_VARARGS */
595 
596 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
597 /*
598  * Stdarg front-end to _message()
599  */
600 void
601 message(int lvl, char *fmt, ...)
602 {
603 	static char buf[MSGBUFSIZ];
604 	va_list args;
605 
606 	va_start(args, fmt);
607 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
608 	va_end(args);
609 
610 	_message(lvl, buf);
611 }
612 #endif	/* ARG_STDARG */
613 
614 /*
615  * Display a debugging message
616  */
617 static void
618 _debugmsg(int lvl, char *buf)
619 {
620 	if (IS_ON(debug, lvl))
621 		_message(MT_DEBUG, buf);
622 }
623 
624 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
625 /*
626  * Varargs front-end to _debugmsg()
627  */
628 void
629 debugmsg(va_alist)
630 	va_dcl
631 {
632 	static char buf[MSGBUFSIZ];
633 	va_list args;
634 	char *fmt;
635 	int lvl;
636 
637 	va_start(args);
638 	lvl = (int) va_arg(args, int);
639 	fmt = (char *) va_arg(args, char *);
640 	va_end(args);
641 
642 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
643 
644 	_debugmsg(lvl, buf);
645 }
646 #endif	/* ARG_VARARGS */
647 
648 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
649 /*
650  * Stdarg front-end to _debugmsg()
651  */
652 void
653 debugmsg(int lvl, char *fmt, ...)
654 {
655 	static char buf[MSGBUFSIZ];
656 	va_list args;
657 
658 	va_start(args, fmt);
659 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
660 	va_end(args);
661 
662 	_debugmsg(lvl, buf);
663 }
664 #endif	/* ARG_STDARG */
665 
666 /*
667  * Print an error message
668  */
669 static void
670 _error(char *msg)
671 {
672 	static char buf[MSGBUFSIZ];
673 
674 	nerrs++;
675 	buf[0] = CNULL;
676 
677 	if (msg) {
678 		if (isserver)
679 			(void) snprintf(buf, sizeof(buf),
680 					"REMOTE ERROR: %s", msg);
681 		else
682 			(void) snprintf(buf, sizeof(buf),
683 					"LOCAL ERROR: %s", msg);
684 	}
685 
686 	_message(MT_NERROR, (buf[0]) ? buf : NULL);
687 }
688 
689 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
690 /*
691  * Varargs frontend to _error()
692  */
693 void
694 error(va_alist)
695 	va_dcl
696 {
697 	static char buf[MSGBUFSIZ];
698 	va_list args;
699 	char *fmt;
700 
701 	buf[0] = CNULL;
702 	va_start(args);
703 	fmt = (char *) va_arg(args, char *);
704 	if (fmt)
705 		(void) vsnprintf(buf, sizeof(buf), fmt, args);
706 	va_end(args);
707 
708 	_error((buf[0]) ? buf : NULL);
709 }
710 #endif	/* ARG_VARARGS */
711 
712 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
713 /*
714  * Stdarg frontend to _error()
715  */
716 void
717 error(char *fmt, ...)
718 {
719 	static char buf[MSGBUFSIZ];
720 	va_list args;
721 
722 	buf[0] = CNULL;
723 	va_start(args, fmt);
724 	if (fmt)
725 		(void) vsnprintf(buf, sizeof(buf), fmt, args);
726 	va_end(args);
727 
728 	_error((buf[0]) ? buf : NULL);
729 }
730 #endif	/* ARG_STDARG */
731 
732 /*
733  * Display a fatal message
734  */
735 static void
736 _fatalerr(char *msg)
737 {
738 	static char buf[MSGBUFSIZ];
739 
740 	++nerrs;
741 
742 	if (isserver)
743 		(void) snprintf(buf, sizeof(buf), "REMOTE ERROR: %s", msg);
744 	else
745 		(void) snprintf(buf, sizeof(buf), "LOCAL ERROR: %s", msg);
746 
747 	_message(MT_FERROR, buf);
748 
749 	exit(nerrs);
750 }
751 
752 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
753 /*
754  * Varargs front-end to _fatalerr()
755  */
756 void
757 fatalerr(va_alist)
758 	va_dcl
759 {
760 	static char buf[MSGBUFSIZ];
761 	va_list args;
762 	char *fmt;
763 
764 	va_start(args);
765 	fmt = (char *) va_arg(args, char *);
766 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
767 	va_end(args);
768 
769 	_fatalerr(buf);
770 }
771 #endif	/* ARG_VARARGS */
772 
773 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
774 /*
775  * Stdarg front-end to _fatalerr()
776  */
777 void
778 fatalerr(char *fmt, ...)
779 {
780 	static char buf[MSGBUFSIZ];
781 	va_list args;
782 
783 	va_start(args, fmt);
784 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
785 	va_end(args);
786 
787 	_fatalerr(buf);
788 }
789 #endif	/* ARG_STDARG */
790 
791 /*
792  * Get the name of the file used for notify.
793  * A side effect is that the file pointer to the file
794  * is closed.  We assume this function is only called when
795  * we are ready to read the file.
796  */
797 char *
798 getnotifyfile(void)
799 {
800 	int i;
801 
802 	for (i = 0; msgfacility[i].mf_name; i++)
803 		if (msgfacility[i].mf_msgfac == MF_NOTIFY &&
804 		    msgfacility[i].mf_fptr) {
805 			(void) fclose(msgfacility[i].mf_fptr);
806 			msgfacility[i].mf_fptr = NULL;
807 			return(msgfacility[i].mf_filename);
808 		}
809 
810 	return(NULL);
811 }
812