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 51int debug = 0; /* Debugging level */ 52int nerrs = 0; /* Number of errors */ 53 54/* 55 * Message Types 56 */ 57struct 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 */ 76struct 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 96static void msgsendstdout(struct msgfacility *, int, int, char *); 97static void msgsendsyslog(struct msgfacility *, int, int, char *); 98static void msgsendfile(struct msgfacility *, int, int, char *); 99static void msgsendnotify(struct msgfacility *, int, int, char *); 100 101/* 102 * Message Facilities 103 */ 104struct 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 112static struct msgfacility *getmsgfac(char *); 113static struct msgtype *getmsgtype(char *); 114static char *setmsgtypes(struct msgfacility *, char *); 115static void _message(int, char *); 116static void _debugmsg(int, char *); 117static void _error(const char *); 118static void _fatalerr(const char *); 119 120/* 121 * Print enabled message logging info 122 */ 123void 124msgprconfig(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 */ 149static struct msgfacility * 150getmsgfac(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 */ 164static struct msgtype * 165getmsgtype(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 */ 180static char * 181setmsgtypes(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 */ 267char * 268msgparseopts(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 */ 324static void 325msgsendstdout(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 */ 378static void 379msgsendsyslog(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 */ 419static void 420msgsendfile(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 */ 435static void 436msgsendnotify(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 */ 479void 480checkhostname(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 */ 498static void 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 */ 564void 565message(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 */ 582static void 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 */ 592void 593debugmsg(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 */ 608static void 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 */ 631void 632error(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 */ 649static void 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 */ 669void 670fatalerr(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 */ 688char * 689getnotifyfile(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