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