1 /* $OpenBSD: message.c,v 1.18 2009/10/27 23:59:42 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 "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(char *); 82 static void _fatalerr(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 = (char *) 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 #if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS 556 /* 557 * Varargs front-end to _message() 558 */ 559 void 560 message(va_alist) 561 va_dcl 562 { 563 static char buf[MSGBUFSIZ]; 564 va_list args; 565 char *fmt; 566 int lvl; 567 568 va_start(args); 569 lvl = (int) va_arg(args, int); 570 fmt = (char *) va_arg(args, char *); 571 va_end(args); 572 573 (void) vsnprintf(buf, sizeof(buf), fmt, args); 574 575 _message(lvl, buf); 576 } 577 #endif /* ARG_VARARGS */ 578 579 #if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG 580 /* 581 * Stdarg front-end to _message() 582 */ 583 void 584 message(int lvl, char *fmt, ...) 585 { 586 static char buf[MSGBUFSIZ]; 587 va_list args; 588 589 va_start(args, fmt); 590 (void) vsnprintf(buf, sizeof(buf), fmt, args); 591 va_end(args); 592 593 _message(lvl, buf); 594 } 595 #endif /* ARG_STDARG */ 596 597 /* 598 * Display a debugging message 599 */ 600 static void 601 _debugmsg(int lvl, char *buf) 602 { 603 if (IS_ON(debug, lvl)) 604 _message(MT_DEBUG, buf); 605 } 606 607 #if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS 608 /* 609 * Varargs front-end to _debugmsg() 610 */ 611 void 612 debugmsg(va_alist) 613 va_dcl 614 { 615 static char buf[MSGBUFSIZ]; 616 va_list args; 617 char *fmt; 618 int lvl; 619 620 va_start(args); 621 lvl = (int) va_arg(args, int); 622 fmt = (char *) va_arg(args, char *); 623 va_end(args); 624 625 (void) vsnprintf(buf, sizeof(buf), fmt, args); 626 627 _debugmsg(lvl, buf); 628 } 629 #endif /* ARG_VARARGS */ 630 631 #if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG 632 /* 633 * Stdarg front-end to _debugmsg() 634 */ 635 void 636 debugmsg(int lvl, char *fmt, ...) 637 { 638 static char buf[MSGBUFSIZ]; 639 va_list args; 640 641 va_start(args, fmt); 642 (void) vsnprintf(buf, sizeof(buf), fmt, args); 643 va_end(args); 644 645 _debugmsg(lvl, buf); 646 } 647 #endif /* ARG_STDARG */ 648 649 /* 650 * Print an error message 651 */ 652 static void 653 _error(char *msg) 654 { 655 static char buf[MSGBUFSIZ]; 656 657 nerrs++; 658 buf[0] = CNULL; 659 660 if (msg) { 661 if (isserver) 662 (void) snprintf(buf, sizeof(buf), 663 "REMOTE ERROR: %s", msg); 664 else 665 (void) snprintf(buf, sizeof(buf), 666 "LOCAL ERROR: %s", msg); 667 } 668 669 _message(MT_NERROR, (buf[0]) ? buf : NULL); 670 } 671 672 #if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS 673 /* 674 * Varargs frontend to _error() 675 */ 676 void 677 error(va_alist) 678 va_dcl 679 { 680 static char buf[MSGBUFSIZ]; 681 va_list args; 682 char *fmt; 683 684 buf[0] = CNULL; 685 va_start(args); 686 fmt = (char *) va_arg(args, char *); 687 if (fmt) 688 (void) vsnprintf(buf, sizeof(buf), fmt, args); 689 va_end(args); 690 691 _error((buf[0]) ? buf : NULL); 692 } 693 #endif /* ARG_VARARGS */ 694 695 #if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG 696 /* 697 * Stdarg frontend to _error() 698 */ 699 void 700 error(char *fmt, ...) 701 { 702 static char buf[MSGBUFSIZ]; 703 va_list args; 704 705 buf[0] = CNULL; 706 va_start(args, fmt); 707 if (fmt) 708 (void) vsnprintf(buf, sizeof(buf), fmt, args); 709 va_end(args); 710 711 _error((buf[0]) ? buf : NULL); 712 } 713 #endif /* ARG_STDARG */ 714 715 /* 716 * Display a fatal message 717 */ 718 static void 719 _fatalerr(char *msg) 720 { 721 static char buf[MSGBUFSIZ]; 722 723 ++nerrs; 724 725 if (isserver) 726 (void) snprintf(buf, sizeof(buf), "REMOTE ERROR: %s", msg); 727 else 728 (void) snprintf(buf, sizeof(buf), "LOCAL ERROR: %s", msg); 729 730 _message(MT_FERROR, buf); 731 732 exit(nerrs); 733 } 734 735 #if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS 736 /* 737 * Varargs front-end to _fatalerr() 738 */ 739 void 740 fatalerr(va_alist) 741 va_dcl 742 { 743 static char buf[MSGBUFSIZ]; 744 va_list args; 745 char *fmt; 746 747 va_start(args); 748 fmt = (char *) va_arg(args, char *); 749 (void) vsnprintf(buf, sizeof(buf), fmt, args); 750 va_end(args); 751 752 _fatalerr(buf); 753 } 754 #endif /* ARG_VARARGS */ 755 756 #if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG 757 /* 758 * Stdarg front-end to _fatalerr() 759 */ 760 void 761 fatalerr(char *fmt, ...) 762 { 763 static char buf[MSGBUFSIZ]; 764 va_list args; 765 766 va_start(args, fmt); 767 (void) vsnprintf(buf, sizeof(buf), fmt, args); 768 va_end(args); 769 770 _fatalerr(buf); 771 } 772 #endif /* ARG_STDARG */ 773 774 /* 775 * Get the name of the file used for notify. 776 * A side effect is that the file pointer to the file 777 * is closed. We assume this function is only called when 778 * we are ready to read the file. 779 */ 780 char * 781 getnotifyfile(void) 782 { 783 int i; 784 785 for (i = 0; msgfacility[i].mf_name; i++) 786 if (msgfacility[i].mf_msgfac == MF_NOTIFY && 787 msgfacility[i].mf_fptr) { 788 (void) fclose(msgfacility[i].mf_fptr); 789 msgfacility[i].mf_fptr = NULL; 790 return(msgfacility[i].mf_filename); 791 } 792 793 return(NULL); 794 } 795