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