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