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