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