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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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