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