1 /* $OpenBSD: syslogd.c,v 1.43 2001/08/03 20:24:16 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1988, 1993, 1994 5 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 static char copyright[] = 38 "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94"; 45 #else 46 static char rcsid[] = "$OpenBSD: syslogd.c,v 1.43 2001/08/03 20:24:16 millert Exp $"; 47 #endif 48 #endif /* not lint */ 49 50 /* 51 * syslogd -- log system messages 52 * 53 * This program implements a system log. It takes a series of lines. 54 * Each line may have a priority, signified as "<n>" as 55 * the first characters of the line. If this is 56 * not present, a default priority is used. 57 * 58 * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will 59 * cause it to reread its configuration file. 60 * 61 * Defined Constants: 62 * 63 * MAXLINE -- the maximimum line length that can be handled. 64 * DEFUPRI -- the default priority for user messages 65 * DEFSPRI -- the default priority for kernel messages 66 * 67 * Author: Eric Allman 68 * extensive changes by Ralph Campbell 69 * more extensive changes by Eric Allman (again) 70 */ 71 72 #define MAXLINE 1024 /* maximum line length */ 73 #define MAXSVLINE 120 /* maximum saved line length */ 74 #define DEFUPRI (LOG_USER|LOG_NOTICE) 75 #define DEFSPRI (LOG_KERN|LOG_CRIT) 76 #define TIMERINTVL 30 /* interval for checking flush, mark */ 77 #define TTYMSGTIME 1 /* timeout passed to ttymsg */ 78 79 #include <sys/param.h> 80 #include <sys/ioctl.h> 81 #include <sys/stat.h> 82 #include <sys/wait.h> 83 #include <sys/socket.h> 84 #include <sys/msgbuf.h> 85 #include <sys/uio.h> 86 #include <sys/sysctl.h> 87 #include <sys/un.h> 88 #include <sys/time.h> 89 #include <sys/resource.h> 90 91 #include <netinet/in.h> 92 #include <netdb.h> 93 #include <arpa/inet.h> 94 95 #include <ctype.h> 96 #include <errno.h> 97 #include <err.h> 98 #include <fcntl.h> 99 #include <paths.h> 100 #include <setjmp.h> 101 #include <signal.h> 102 #include <stdio.h> 103 #include <stdlib.h> 104 #include <string.h> 105 #include <unistd.h> 106 #include <utmp.h> 107 #include <vis.h> 108 109 #define SYSLOG_NAMES 110 #include <sys/syslog.h> 111 112 char *ConfFile = _PATH_LOGCONF; 113 char *PidFile = _PATH_LOGPID; 114 char ctty[] = _PATH_CONSOLE; 115 116 #define dprintf if (Debug) printf 117 118 #define MAXUNAMES 20 /* maximum number of user names */ 119 120 /* 121 * Flags to logmsg(). 122 */ 123 124 #define IGN_CONS 0x001 /* don't print on console */ 125 #define SYNC_FILE 0x002 /* do fsync on file after printing */ 126 #define ADDDATE 0x004 /* add a date to the message */ 127 #define MARK 0x008 /* this message is a mark */ 128 129 /* 130 * This structure represents the files that will have log 131 * copies printed. 132 */ 133 134 struct filed { 135 struct filed *f_next; /* next in linked list */ 136 short f_type; /* entry type, see below */ 137 short f_file; /* file descriptor */ 138 time_t f_time; /* time this was last written */ 139 u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */ 140 char *f_program; /* program this applies to */ 141 union { 142 char f_uname[MAXUNAMES][UT_NAMESIZE+1]; 143 struct { 144 char f_hname[MAXHOSTNAMELEN]; 145 struct sockaddr_in f_addr; 146 } f_forw; /* forwarding address */ 147 char f_fname[MAXPATHLEN]; 148 } f_un; 149 char f_prevline[MAXSVLINE]; /* last message logged */ 150 char f_lasttime[16]; /* time of last occurrence */ 151 char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */ 152 int f_prevpri; /* pri of f_prevline */ 153 int f_prevlen; /* length of f_prevline */ 154 int f_prevcount; /* repetition cnt of prevline */ 155 int f_repeatcount; /* number of "repeated" msgs */ 156 }; 157 158 /* 159 * Intervals at which we flush out "message repeated" messages, 160 * in seconds after previous message is logged. After each flush, 161 * we move to the next interval until we reach the largest. 162 */ 163 int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */ 164 #define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1) 165 #define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) 166 #define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ 167 (f)->f_repeatcount = MAXREPEAT; \ 168 } 169 170 /* values for f_type */ 171 #define F_UNUSED 0 /* unused entry */ 172 #define F_FILE 1 /* regular file */ 173 #define F_TTY 2 /* terminal */ 174 #define F_CONSOLE 3 /* console terminal */ 175 #define F_FORW 4 /* remote machine */ 176 #define F_USERS 5 /* list of users */ 177 #define F_WALL 6 /* everyone logged on */ 178 179 char *TypeNames[7] = { 180 "UNUSED", "FILE", "TTY", "CONSOLE", 181 "FORW", "USERS", "WALL" 182 }; 183 184 struct filed *Files; 185 struct filed consfile; 186 187 int Debug; /* debug flag */ 188 char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */ 189 char *LocalDomain; /* our local domain name */ 190 int InetInuse = 0; /* non-zero if INET sockets are being used */ 191 int finet; /* Internet datagram socket */ 192 int LogPort; /* port number for INET connections */ 193 int Initialized = 0; /* set when we have initialized ourselves */ 194 195 int MarkInterval = 20 * 60; /* interval between marks in seconds */ 196 int MarkSeq = 0; /* mark sequence number */ 197 198 sig_atomic_t MarkSet; 199 sig_atomic_t WantDie; 200 201 int SecureMode = 1; /* when true, speak only unix domain socks */ 202 203 void cfline __P((char *, struct filed *, char *)); 204 char *cvthname __P((struct sockaddr_in *)); 205 int decode __P((const char *, CODE *)); 206 void dodie __P((int)); 207 void die __P((int)); 208 void domark __P((int)); 209 void markit __P((void)); 210 void fprintlog __P((struct filed *, int, char *)); 211 void init __P((int)); 212 void logerror __P((char *)); 213 void logmsg __P((int, char *, char *, int)); 214 void printline __P((char *, char *)); 215 void printsys __P((char *)); 216 void reapchild __P((int)); 217 char *ttymsg __P((struct iovec *, int, char *, int)); 218 void usage __P((void)); 219 void wallmsg __P((struct filed *, struct iovec *)); 220 221 #define MAXFUNIX 21 222 223 int nfunix = 1; 224 char *funixn[MAXFUNIX] = { _PATH_LOG }; 225 int funix[MAXFUNIX]; 226 227 int 228 main(argc, argv) 229 int argc; 230 char *argv[]; 231 { 232 int ch, i, fklog, len, linesize, fdsrmax = 0; 233 struct sockaddr_un sunx, fromunix; 234 struct sockaddr_in sin, frominet; 235 fd_set *fdsr = NULL; 236 char *p, *line; 237 FILE *fp; 238 239 while ((ch = getopt(argc, argv, "duf:m:p:a:")) != -1) 240 switch (ch) { 241 case 'd': /* debug */ 242 Debug++; 243 break; 244 case 'f': /* configuration file */ 245 ConfFile = optarg; 246 break; 247 case 'm': /* mark interval */ 248 MarkInterval = atoi(optarg) * 60; 249 break; 250 case 'p': /* path */ 251 funixn[0] = optarg; 252 break; 253 case 'u': /* allow udp input port */ 254 SecureMode = 0; 255 break; 256 case 'a': 257 if (nfunix >= MAXFUNIX) 258 fprintf(stderr, 259 "syslogd: out of descriptors, ignoring %s\n", 260 optarg); 261 else if (strlen(optarg) >= sizeof(sunx.sun_path)) 262 fprintf(stderr, 263 "syslogd: path too long, ignoring %s\n", 264 optarg); 265 else 266 funixn[nfunix++] = optarg; 267 break; 268 case '?': 269 default: 270 usage(); 271 } 272 if ((argc -= optind) != 0) 273 usage(); 274 275 if (!Debug) 276 (void)daemon(0, 0); 277 else 278 setlinebuf(stdout); 279 280 consfile.f_type = F_CONSOLE; 281 (void)strlcpy(consfile.f_un.f_fname, ctty, 282 sizeof(consfile.f_un.f_fname)); 283 (void)gethostname(LocalHostName, sizeof(LocalHostName)); 284 if ((p = strchr(LocalHostName, '.')) != NULL) { 285 *p++ = '\0'; 286 LocalDomain = p; 287 } else 288 LocalDomain = ""; 289 290 linesize = getmsgbufsize(); 291 if (linesize < MAXLINE) 292 linesize = MAXLINE; 293 linesize++; 294 line = malloc(linesize); 295 296 (void)signal(SIGTERM, dodie); 297 (void)signal(SIGINT, Debug ? dodie : SIG_IGN); 298 (void)signal(SIGQUIT, Debug ? dodie : SIG_IGN); 299 (void)signal(SIGCHLD, reapchild); 300 (void)signal(SIGALRM, domark); 301 (void)alarm(TIMERINTVL); 302 303 #ifndef SUN_LEN 304 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 305 #endif 306 for (i = 0; i < nfunix; i++) { 307 (void)unlink(funixn[i]); 308 309 memset(&sunx, 0, sizeof(sunx)); 310 sunx.sun_family = AF_UNIX; 311 (void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path)); 312 funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0); 313 if (funix[i] < 0 || 314 bind(funix[i], (struct sockaddr *)&sunx, 315 SUN_LEN(&sunx)) < 0 || 316 chmod(funixn[i], 0666) < 0) { 317 (void) snprintf(line, sizeof line, "cannot create %s", 318 funixn[i]); 319 logerror(line); 320 dprintf("cannot create %s (%d)\n", funixn[i], errno); 321 if (i == 0) 322 die(0); 323 } 324 } 325 finet = socket(AF_INET, SOCK_DGRAM, 0); 326 if (finet >= 0) { 327 struct servent *sp; 328 329 sp = getservbyname("syslog", "udp"); 330 if (sp == NULL) { 331 errno = 0; 332 logerror("syslog/udp: unknown service"); 333 die(0); 334 } 335 memset(&sin, 0, sizeof(sin)); 336 sin.sin_len = sizeof(sin); 337 sin.sin_family = AF_INET; 338 sin.sin_port = LogPort = sp->s_port; 339 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 340 logerror("bind"); 341 if (!Debug) 342 die(0); 343 } else { 344 InetInuse = 1; 345 } 346 } 347 if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0) 348 dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); 349 350 /* tuck my process id away */ 351 if (!Debug) { 352 fp = fopen(PidFile, "w"); 353 if (fp != NULL) { 354 fprintf(fp, "%d\n", getpid()); 355 (void) fclose(fp); 356 } 357 } 358 359 dprintf("off & running....\n"); 360 361 init(0); 362 (void)signal(SIGHUP, init); 363 364 if (fklog != -1 && fklog > fdsrmax) 365 fdsrmax = fklog; 366 if (finet != -1 && finet > fdsrmax) 367 fdsrmax = finet; 368 for (i = 0; i < nfunix; i++) { 369 if (funix[i] != -1 && funix[i] > fdsrmax) 370 fdsrmax = funix[i]; 371 } 372 373 fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS), 374 sizeof(fd_mask)); 375 if (fdsr == NULL) 376 errx(1, "calloc fd_set"); 377 378 for (;;) { 379 if (MarkSet) 380 markit(); 381 if (WantDie) 382 die(WantDie); 383 384 bzero(fdsr, howmany(fdsrmax+1, NFDBITS) * 385 sizeof(fd_mask)); 386 387 if (fklog != -1) 388 FD_SET(fklog, fdsr); 389 if (finet != -1) 390 FD_SET(finet, fdsr); 391 for (i = 0; i < nfunix; i++) { 392 if (funix[i] != -1) 393 FD_SET(funix[i], fdsr); 394 } 395 396 switch (select(fdsrmax+1, fdsr, NULL, NULL, NULL)) { 397 case 0: 398 continue; 399 case -1: 400 if (errno != EINTR) 401 logerror("select"); 402 continue; 403 } 404 405 if (fklog != -1 && FD_ISSET(fklog, fdsr)) { 406 i = read(fklog, line, linesize - 1); 407 if (i > 0) { 408 line[i] = '\0'; 409 printsys(line); 410 } else if (i < 0 && errno != EINTR) { 411 logerror("klog"); 412 fklog = -1; 413 } 414 } 415 if (finet != -1 && FD_ISSET(finet, fdsr)) { 416 len = sizeof(frominet); 417 i = recvfrom(finet, line, MAXLINE, 0, 418 (struct sockaddr *)&frominet, &len); 419 if (SecureMode) { 420 /* silently drop it */ 421 } else { 422 if (i > 0) { 423 line[i] = '\0'; 424 printline(cvthname(&frominet), line); 425 } else if (i < 0 && errno != EINTR) 426 logerror("recvfrom inet"); 427 } 428 } 429 for (i = 0; i < nfunix; i++) { 430 if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) { 431 len = sizeof(fromunix); 432 len = recvfrom(funix[i], line, MAXLINE, 0, 433 (struct sockaddr *)&fromunix, &len); 434 if (len > 0) { 435 line[len] = '\0'; 436 printline(LocalHostName, line); 437 } else if (len < 0 && errno != EINTR) 438 logerror("recvfrom unix"); 439 } 440 } 441 } 442 if (fdsr) 443 free(fdsr); 444 } 445 446 void 447 usage() 448 { 449 450 (void)fprintf(stderr, 451 "usage: syslogd [-u] [-f conffile] [-m markinterval] [-p logpath] [-a logpath]\n"); 452 exit(1); 453 } 454 455 /* 456 * Take a raw input line, decode the message, and print the message 457 * on the appropriate log files. 458 */ 459 void 460 printline(hname, msg) 461 char *hname; 462 char *msg; 463 { 464 int pri; 465 char *p, *q, line[MAXLINE + 1]; 466 467 /* test for special codes */ 468 pri = DEFUPRI; 469 p = msg; 470 if (*p == '<') { 471 pri = 0; 472 while (isdigit(*++p)) 473 pri = 10 * pri + (*p - '0'); 474 if (*p == '>') 475 ++p; 476 } 477 if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) 478 pri = DEFUPRI; 479 480 /* don't allow users to log kernel messages */ 481 if (LOG_FAC(pri) == LOG_KERN) 482 pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); 483 484 for (q = line; *p && q < &line[sizeof(line) - 4]; p++) { 485 if (*p == '\n') 486 *q++ = ' '; 487 else 488 q = vis(q, *p, 0, 0); 489 } 490 *q = '\0'; 491 492 logmsg(pri, line, hname, 0); 493 } 494 495 /* 496 * Take a raw input line from /dev/klog, split and format similar to syslog(). 497 */ 498 void 499 printsys(msg) 500 char *msg; 501 { 502 int c, pri, flags; 503 char *lp, *p, *q, line[MAXLINE + 1]; 504 505 snprintf(line, sizeof line, "%s: ", _PATH_UNIX); 506 lp = line + strlen(line); 507 for (p = msg; *p != '\0'; ) { 508 flags = SYNC_FILE | ADDDATE; /* fsync file after write */ 509 pri = DEFSPRI; 510 if (*p == '<') { 511 pri = 0; 512 while (isdigit(*++p)) 513 pri = 10 * pri + (*p - '0'); 514 if (*p == '>') 515 ++p; 516 } else { 517 /* kernel printf's come out on console */ 518 flags |= IGN_CONS; 519 } 520 if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) 521 pri = DEFSPRI; 522 523 q = lp; 524 while (*p && (c = *p++) != '\n' && q < &line[sizeof(line) - 4]) 525 q = vis(q, c, 0, 0); 526 527 logmsg(pri, line, LocalHostName, flags); 528 } 529 } 530 531 time_t now; 532 533 /* 534 * Log a message to the appropriate log files, users, etc. based on 535 * the priority. 536 */ 537 void 538 logmsg(pri, msg, from, flags) 539 int pri; 540 char *msg, *from; 541 int flags; 542 { 543 struct filed *f; 544 int fac, msglen, prilev, i; 545 sigset_t mask, omask; 546 char *timestamp; 547 char prog[NAME_MAX+1]; 548 549 dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n", 550 pri, flags, from, msg); 551 552 sigemptyset(&mask); 553 sigaddset(&mask, SIGALRM); 554 sigaddset(&mask, SIGHUP); 555 sigprocmask(SIG_BLOCK, &mask, &omask); 556 557 /* 558 * Check to see if msg looks non-standard. 559 */ 560 msglen = strlen(msg); 561 if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' || 562 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') 563 flags |= ADDDATE; 564 565 (void)time(&now); 566 if (flags & ADDDATE) 567 timestamp = ctime(&now) + 4; 568 else { 569 timestamp = msg; 570 msg += 16; 571 msglen -= 16; 572 } 573 574 /* extract facility and priority level */ 575 if (flags & MARK) 576 fac = LOG_NFACILITIES; 577 else 578 fac = LOG_FAC(pri); 579 prilev = LOG_PRI(pri); 580 581 /* extract program name */ 582 for(i = 0; i < NAME_MAX; i++) { 583 if (!isalnum(msg[i])) 584 break; 585 prog[i] = msg[i]; 586 } 587 prog[i] = 0; 588 589 /* log the message to the particular outputs */ 590 if (!Initialized) { 591 f = &consfile; 592 f->f_file = open(ctty, O_WRONLY|O_NONBLOCK, 0); 593 594 if (f->f_file >= 0) { 595 fprintlog(f, flags, msg); 596 (void)close(f->f_file); 597 f->f_file = -1; 598 } 599 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 600 return; 601 } 602 for (f = Files; f; f = f->f_next) { 603 /* skip messages that are incorrect priority */ 604 if (f->f_pmask[fac] < prilev || 605 f->f_pmask[fac] == INTERNAL_NOPRI) 606 continue; 607 608 /* skip messages with the incorrect program name */ 609 if (f->f_program) 610 if (strcmp(prog, f->f_program) != 0) 611 continue; 612 613 if (f->f_type == F_CONSOLE && (flags & IGN_CONS)) 614 continue; 615 616 /* don't output marks to recently written files */ 617 if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) 618 continue; 619 620 /* 621 * suppress duplicate lines to this file 622 */ 623 if ((flags & MARK) == 0 && msglen == f->f_prevlen && 624 !strcmp(msg, f->f_prevline) && 625 !strcmp(from, f->f_prevhost)) { 626 strlcpy(f->f_lasttime, timestamp, 16); 627 f->f_prevcount++; 628 dprintf("msg repeated %d times, %ld sec of %d\n", 629 f->f_prevcount, (long)(now - f->f_time), 630 repeatinterval[f->f_repeatcount]); 631 /* 632 * If domark would have logged this by now, 633 * flush it now (so we don't hold isolated messages), 634 * but back off so we'll flush less often 635 * in the future. 636 */ 637 if (now > REPEATTIME(f)) { 638 fprintlog(f, flags, (char *)NULL); 639 BACKOFF(f); 640 } 641 } else { 642 /* new line, save it */ 643 if (f->f_prevcount) 644 fprintlog(f, 0, (char *)NULL); 645 f->f_repeatcount = 0; 646 f->f_prevpri = pri; 647 strlcpy(f->f_lasttime, timestamp, 16); 648 strlcpy(f->f_prevhost, from, 649 sizeof(f->f_prevhost)); 650 if (msglen < MAXSVLINE) { 651 f->f_prevlen = msglen; 652 strlcpy(f->f_prevline, msg, sizeof(f->f_prevline)); 653 fprintlog(f, flags, (char *)NULL); 654 } else { 655 f->f_prevline[0] = 0; 656 f->f_prevlen = 0; 657 fprintlog(f, flags, msg); 658 } 659 } 660 } 661 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 662 } 663 664 void 665 fprintlog(f, flags, msg) 666 struct filed *f; 667 int flags; 668 char *msg; 669 { 670 struct iovec iov[6]; 671 struct iovec *v; 672 int l; 673 char line[MAXLINE + 1], repbuf[80], greetings[500]; 674 675 v = iov; 676 if (f->f_type == F_WALL) { 677 v->iov_base = greetings; 678 v->iov_len = snprintf(greetings, sizeof(greetings), 679 "\r\n\7Message from syslogd@%s at %.24s ...\r\n", 680 f->f_prevhost, ctime(&now)); 681 if (v->iov_len >= sizeof(greetings)) 682 v->iov_len = sizeof(greetings) - 1; 683 v++; 684 v->iov_base = ""; 685 v->iov_len = 0; 686 v++; 687 } else { 688 v->iov_base = f->f_lasttime; 689 v->iov_len = 15; 690 v++; 691 v->iov_base = " "; 692 v->iov_len = 1; 693 v++; 694 } 695 v->iov_base = f->f_prevhost; 696 v->iov_len = strlen(v->iov_base); 697 v++; 698 v->iov_base = " "; 699 v->iov_len = 1; 700 v++; 701 702 if (msg) { 703 v->iov_base = msg; 704 v->iov_len = strlen(msg); 705 } else if (f->f_prevcount > 1) { 706 v->iov_base = repbuf; 707 v->iov_len = snprintf(repbuf, sizeof repbuf, 708 "last message repeated %d times", f->f_prevcount); 709 } else { 710 v->iov_base = f->f_prevline; 711 v->iov_len = f->f_prevlen; 712 } 713 v++; 714 715 dprintf("Logging to %s", TypeNames[f->f_type]); 716 f->f_time = now; 717 718 switch (f->f_type) { 719 case F_UNUSED: 720 dprintf("\n"); 721 break; 722 723 case F_FORW: 724 dprintf(" %s\n", f->f_un.f_forw.f_hname); 725 l = snprintf(line, sizeof(line) - 1, "<%d>%.15s %s", f->f_prevpri, 726 (char *)iov[0].iov_base, (char *)iov[4].iov_base); 727 if (l > MAXLINE) 728 l = MAXLINE; 729 if (sendto(finet, line, l, 0, 730 (struct sockaddr *)&f->f_un.f_forw.f_addr, 731 sizeof(f->f_un.f_forw.f_addr)) != l) { 732 f->f_type = F_UNUSED; 733 logerror("sendto"); 734 } 735 break; 736 737 case F_CONSOLE: 738 if (flags & IGN_CONS) { 739 dprintf(" (ignored)\n"); 740 break; 741 } 742 /* FALLTHROUGH */ 743 744 case F_TTY: 745 case F_FILE: 746 dprintf(" %s\n", f->f_un.f_fname); 747 if (f->f_type != F_FILE) { 748 v->iov_base = "\r\n"; 749 v->iov_len = 2; 750 } else { 751 v->iov_base = "\n"; 752 v->iov_len = 1; 753 } 754 again: 755 if (writev(f->f_file, iov, 6) < 0) { 756 int e = errno; 757 (void)close(f->f_file); 758 /* 759 * Check for errors on TTY's due to loss of tty 760 */ 761 if (e == EAGAIN) { 762 /* 763 * Silently drop messages on blocked write. 764 * This can happen when logging to a locked tty. 765 */ 766 break; 767 } else if ((e == EIO || e == EBADF) && 768 f->f_type != F_FILE) { 769 f->f_file = open(f->f_un.f_fname, 770 O_WRONLY|O_APPEND|O_NONBLOCK, 0); 771 if (f->f_file < 0) { 772 f->f_type = F_UNUSED; 773 logerror(f->f_un.f_fname); 774 } else 775 goto again; 776 } else { 777 f->f_type = F_UNUSED; 778 f->f_file = -1; 779 errno = e; 780 logerror(f->f_un.f_fname); 781 } 782 } else if (flags & SYNC_FILE) 783 (void)fsync(f->f_file); 784 break; 785 786 case F_USERS: 787 case F_WALL: 788 dprintf("\n"); 789 v->iov_base = "\r\n"; 790 v->iov_len = 2; 791 wallmsg(f, iov); 792 break; 793 } 794 f->f_prevcount = 0; 795 } 796 797 /* 798 * WALLMSG -- Write a message to the world at large 799 * 800 * Write the specified message to either the entire 801 * world, or a list of approved users. 802 */ 803 void 804 wallmsg(f, iov) 805 struct filed *f; 806 struct iovec *iov; 807 { 808 static int reenter; /* avoid calling ourselves */ 809 FILE *uf; 810 struct utmp ut; 811 int i; 812 char *p; 813 char line[sizeof(ut.ut_line) + 1]; 814 815 if (reenter++) 816 return; 817 if ((uf = fopen(_PATH_UTMP, "r")) == NULL) { 818 logerror(_PATH_UTMP); 819 reenter = 0; 820 return; 821 } 822 /* NOSTRICT */ 823 while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) { 824 if (ut.ut_name[0] == '\0') 825 continue; 826 strlcpy(line, ut.ut_line, sizeof(line)); 827 if (f->f_type == F_WALL) { 828 if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) { 829 errno = 0; /* already in msg */ 830 logerror(p); 831 } 832 continue; 833 } 834 /* should we send the message to this user? */ 835 for (i = 0; i < MAXUNAMES; i++) { 836 if (!f->f_un.f_uname[i][0]) 837 break; 838 if (!strncmp(f->f_un.f_uname[i], ut.ut_name, 839 UT_NAMESIZE)) { 840 if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) 841 != NULL) { 842 errno = 0; /* already in msg */ 843 logerror(p); 844 } 845 break; 846 } 847 } 848 } 849 (void)fclose(uf); 850 reenter = 0; 851 } 852 853 void 854 reapchild(signo) 855 int signo; 856 { 857 int status; 858 int save_errno = errno; 859 860 while (waitpid(-1, &status, WNOHANG) > 0) 861 ; 862 errno = save_errno; 863 } 864 865 /* 866 * Return a printable representation of a host address. 867 */ 868 char * 869 cvthname(f) 870 struct sockaddr_in *f; 871 { 872 struct hostent *hp; 873 sigset_t omask, nmask; 874 char *p; 875 876 dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr)); 877 878 if (f->sin_family != AF_INET) { 879 dprintf("Malformed from address\n"); 880 return ("???"); 881 } 882 sigemptyset(&nmask); 883 sigaddset(&nmask, SIGHUP); 884 sigprocmask(SIG_BLOCK, &nmask, &omask); 885 hp = gethostbyaddr((char *)&f->sin_addr, 886 sizeof(struct in_addr), f->sin_family); 887 sigprocmask(SIG_SETMASK, &omask, NULL); 888 if (hp == 0) { 889 dprintf("Host name for your address (%s) unknown\n", 890 inet_ntoa(f->sin_addr)); 891 return (inet_ntoa(f->sin_addr)); 892 } 893 if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0) 894 *p = '\0'; 895 return (hp->h_name); 896 } 897 898 void 899 dodie(signo) 900 int signo; 901 { 902 WantDie = signo; 903 } 904 905 void 906 domark(signo) 907 int signo; 908 { 909 MarkSet = 1; 910 } 911 912 /* 913 * Print syslogd errors some place. 914 */ 915 void 916 logerror(type) 917 char *type; 918 { 919 char buf[100]; 920 921 if (errno) 922 (void)snprintf(buf, sizeof(buf), "syslogd: %s: %s", 923 type, strerror(errno)); 924 else 925 (void)snprintf(buf, sizeof(buf), "syslogd: %s", type); 926 errno = 0; 927 dprintf("%s\n", buf); 928 logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE); 929 } 930 931 void 932 die(signo) 933 int signo; 934 { 935 struct filed *f; 936 int was_initialized = Initialized; 937 char buf[100]; 938 int i; 939 940 Initialized = 0; /* Don't log SIGCHLDs */ 941 alarm(0); 942 for (f = Files; f != NULL; f = f->f_next) { 943 /* flush any pending output */ 944 if (f->f_prevcount) 945 fprintlog(f, 0, (char *)NULL); 946 } 947 Initialized = was_initialized; 948 if (signo) { 949 dprintf("syslogd: exiting on signal %d\n", signo); 950 (void)snprintf(buf, sizeof buf, "exiting on signal %d", signo); 951 errno = 0; 952 logerror(buf); 953 } 954 for (i = 0; i < nfunix; i++) 955 if (funixn[i] && funix[i] != -1) 956 (void)unlink(funixn[i]); 957 exit(0); 958 } 959 960 /* 961 * INIT -- Initialize syslogd from configuration table 962 */ 963 void 964 init(signo) 965 int signo; 966 { 967 int i; 968 FILE *cf; 969 struct filed *f, *next, **nextp; 970 char *p; 971 char cline[LINE_MAX]; 972 char prog[NAME_MAX+1]; 973 int save_errno = errno; 974 975 dprintf("init\n"); 976 977 /* 978 * Close all open log files. 979 */ 980 Initialized = 0; 981 for (f = Files; f != NULL; f = next) { 982 /* flush any pending output */ 983 if (f->f_prevcount) 984 fprintlog(f, 0, (char *)NULL); 985 986 switch (f->f_type) { 987 case F_FILE: 988 case F_TTY: 989 case F_CONSOLE: 990 (void)close(f->f_file); 991 break; 992 case F_FORW: 993 break; 994 } 995 next = f->f_next; 996 if (f->f_program) 997 free(f->f_program); 998 free((char *)f); 999 } 1000 Files = NULL; 1001 nextp = &Files; 1002 1003 /* open the configuration file */ 1004 if ((cf = fopen(ConfFile, "r")) == NULL) { 1005 dprintf("cannot open %s\n", ConfFile); 1006 *nextp = (struct filed *)calloc(1, sizeof(*f)); 1007 cfline("*.ERR\t/dev/console", *nextp, "*"); 1008 (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f)); 1009 cfline("*.PANIC\t*", (*nextp)->f_next, "*"); 1010 Initialized = 1; 1011 errno = save_errno; 1012 return; 1013 } 1014 1015 /* 1016 * Foreach line in the conf table, open that file. 1017 */ 1018 f = NULL; 1019 strlcpy(prog, "*", sizeof(prog)); 1020 while (fgets(cline, sizeof(cline), cf) != NULL) { 1021 /* 1022 * check for end-of-section, comments, strip off trailing 1023 * spaces and newline character. !prog is treated 1024 * specially: the following lines apply only to that program. 1025 */ 1026 for (p = cline; isspace(*p); ++p) 1027 continue; 1028 if (*p == '\0' || *p == '#') 1029 continue; 1030 if (*p == '!') { 1031 p++; 1032 while (isspace(*p)) 1033 p++; 1034 if (!*p) { 1035 strlcpy(prog, "*", sizeof(prog)); 1036 continue; 1037 } 1038 for (i = 0; i < NAME_MAX; i++) { 1039 if (!isalnum(p[i])) 1040 break; 1041 prog[i] = p[i]; 1042 } 1043 prog[i] = 0; 1044 continue; 1045 } 1046 p = cline + strlen(cline); 1047 while (p > cline) 1048 if (!isspace(*--p)) { 1049 p++; 1050 break; 1051 } 1052 *p = '\0'; 1053 f = (struct filed *)calloc(1, sizeof(*f)); 1054 *nextp = f; 1055 nextp = &f->f_next; 1056 cfline(cline, f, prog); 1057 } 1058 1059 /* close the configuration file */ 1060 (void)fclose(cf); 1061 1062 Initialized = 1; 1063 1064 if (Debug) { 1065 for (f = Files; f; f = f->f_next) { 1066 for (i = 0; i <= LOG_NFACILITIES; i++) 1067 if (f->f_pmask[i] == INTERNAL_NOPRI) 1068 printf("X "); 1069 else 1070 printf("%d ", f->f_pmask[i]); 1071 printf("%s: ", TypeNames[f->f_type]); 1072 switch (f->f_type) { 1073 case F_FILE: 1074 case F_TTY: 1075 case F_CONSOLE: 1076 printf("%s", f->f_un.f_fname); 1077 break; 1078 1079 case F_FORW: 1080 printf("%s", f->f_un.f_forw.f_hname); 1081 break; 1082 1083 case F_USERS: 1084 for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++) 1085 printf("%s, ", f->f_un.f_uname[i]); 1086 break; 1087 } 1088 if (f->f_program) 1089 printf(" (%s)", f->f_program); 1090 printf("\n"); 1091 } 1092 } 1093 1094 logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, 1095 ADDDATE); 1096 dprintf("syslogd: restarted\n"); 1097 errno = save_errno; 1098 } 1099 1100 /* 1101 * Crack a configuration file line 1102 */ 1103 void 1104 cfline(line, f, prog) 1105 char *line; 1106 struct filed *f; 1107 char *prog; 1108 { 1109 struct hostent *hp; 1110 int i, pri; 1111 char *bp, *p, *q; 1112 char buf[MAXLINE], ebuf[100]; 1113 1114 dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog); 1115 1116 errno = 0; /* keep strerror() stuff out of logerror messages */ 1117 1118 /* clear out file entry */ 1119 memset(f, 0, sizeof(*f)); 1120 for (i = 0; i <= LOG_NFACILITIES; i++) 1121 f->f_pmask[i] = INTERNAL_NOPRI; 1122 1123 /* save program name if any */ 1124 if (!strcmp(prog, "*")) 1125 prog = NULL; 1126 else { 1127 f->f_program = calloc(1, strlen(prog)+1); 1128 if (f->f_program) 1129 strlcpy(f->f_program, prog, strlen(prog)+1); 1130 } 1131 1132 /* scan through the list of selectors */ 1133 for (p = line; *p && *p != '\t';) { 1134 1135 /* find the end of this facility name list */ 1136 for (q = p; *q && *q != '\t' && *q++ != '.'; ) 1137 continue; 1138 1139 /* collect priority name */ 1140 for (bp = buf; *q && !strchr("\t,;", *q); ) 1141 *bp++ = *q++; 1142 *bp = '\0'; 1143 1144 /* skip cruft */ 1145 while (strchr(", ;", *q)) 1146 q++; 1147 1148 /* decode priority name */ 1149 if (*buf == '*') 1150 pri = LOG_PRIMASK + 1; 1151 else { 1152 /* ignore trailing spaces */ 1153 int i; 1154 for (i=strlen(buf)-1; i >= 0 && buf[i] == ' '; i--) { 1155 buf[i]='\0'; 1156 } 1157 1158 pri = decode(buf, prioritynames); 1159 if (pri < 0) { 1160 (void)snprintf(ebuf, sizeof ebuf, 1161 "unknown priority name \"%s\"", buf); 1162 logerror(ebuf); 1163 return; 1164 } 1165 } 1166 1167 /* scan facilities */ 1168 while (*p && !strchr("\t.;", *p)) { 1169 for (bp = buf; *p && !strchr("\t,;.", *p); ) 1170 *bp++ = *p++; 1171 *bp = '\0'; 1172 if (*buf == '*') 1173 for (i = 0; i < LOG_NFACILITIES; i++) 1174 f->f_pmask[i] = pri; 1175 else { 1176 i = decode(buf, facilitynames); 1177 if (i < 0) { 1178 (void)snprintf(ebuf, sizeof(ebuf), 1179 "unknown facility name \"%s\"", 1180 buf); 1181 logerror(ebuf); 1182 return; 1183 } 1184 f->f_pmask[i >> 3] = pri; 1185 } 1186 while (*p == ',' || *p == ' ') 1187 p++; 1188 } 1189 1190 p = q; 1191 } 1192 1193 /* skip to action part */ 1194 while (*p == '\t') 1195 p++; 1196 1197 switch (*p) 1198 { 1199 case '@': 1200 if (!InetInuse) 1201 break; 1202 (void)strlcpy(f->f_un.f_forw.f_hname, ++p, 1203 sizeof(f->f_un.f_forw.f_hname)); 1204 hp = gethostbyname(f->f_un.f_forw.f_hname); 1205 if (hp == NULL) { 1206 extern int h_errno; 1207 1208 logerror((char *)hstrerror(h_errno)); 1209 break; 1210 } 1211 memset(&f->f_un.f_forw.f_addr, 0, 1212 sizeof(f->f_un.f_forw.f_addr)); 1213 f->f_un.f_forw.f_addr.sin_len = sizeof(f->f_un.f_forw.f_addr); 1214 f->f_un.f_forw.f_addr.sin_family = AF_INET; 1215 f->f_un.f_forw.f_addr.sin_port = LogPort; 1216 memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, 1217 hp->h_length); 1218 f->f_type = F_FORW; 1219 break; 1220 1221 case '/': 1222 (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname)); 1223 f->f_file = open(p, O_WRONLY|O_APPEND|O_NONBLOCK, 0); 1224 if (f->f_file < 0) { 1225 f->f_type = F_UNUSED; 1226 logerror(p); 1227 break; 1228 } 1229 if (isatty(f->f_file)) { 1230 if (strcmp(p, ctty) == 0) 1231 f->f_type = F_CONSOLE; 1232 else 1233 f->f_type = F_TTY; 1234 } else { 1235 f->f_type = F_FILE; 1236 /* Clear O_NONBLOCK flag on f->f_file */ 1237 if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) { 1238 i &= ~O_NONBLOCK; 1239 fcntl(f->f_file, F_SETFL, i); 1240 } 1241 } 1242 break; 1243 1244 case '*': 1245 f->f_type = F_WALL; 1246 break; 1247 1248 default: 1249 for (i = 0; i < MAXUNAMES && *p; i++) { 1250 for (q = p; *q && *q != ','; ) 1251 q++; 1252 (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE); 1253 if ((q - p) > UT_NAMESIZE) 1254 f->f_un.f_uname[i][UT_NAMESIZE] = '\0'; 1255 else 1256 f->f_un.f_uname[i][q - p] = '\0'; 1257 while (*q == ',' || *q == ' ') 1258 q++; 1259 p = q; 1260 } 1261 f->f_type = F_USERS; 1262 break; 1263 } 1264 } 1265 1266 1267 /* 1268 * Retrieve the size of the kernel message buffer, via sysctl. 1269 */ 1270 int 1271 getmsgbufsize() 1272 { 1273 int msgbufsize, mib[2]; 1274 size_t size; 1275 1276 mib[0] = CTL_KERN; 1277 mib[1] = KERN_MSGBUFSIZE; 1278 size = sizeof msgbufsize; 1279 if (sysctl(mib, 2, &msgbufsize, &size, NULL, 0) == -1) { 1280 dprintf("couldn't get kern.msgbufsize\n"); 1281 return (0); 1282 } 1283 return (msgbufsize); 1284 } 1285 1286 /* 1287 * Decode a symbolic name to a numeric value 1288 */ 1289 int 1290 decode(name, codetab) 1291 const char *name; 1292 CODE *codetab; 1293 { 1294 CODE *c; 1295 char *p, buf[40]; 1296 1297 if (isdigit(*name)) 1298 return (atoi(name)); 1299 1300 for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) { 1301 if (isupper(*name)) 1302 *p = tolower(*name); 1303 else 1304 *p = *name; 1305 } 1306 *p = '\0'; 1307 for (c = codetab; c->c_name; c++) 1308 if (!strcmp(buf, c->c_name)) 1309 return (c->c_val); 1310 1311 return (-1); 1312 } 1313 1314 void 1315 markit(void) 1316 { 1317 struct filed *f; 1318 1319 now = time((time_t *)NULL); 1320 MarkSeq += TIMERINTVL; 1321 if (MarkSeq >= MarkInterval) { 1322 logmsg(LOG_INFO, "-- MARK --", 1323 LocalHostName, ADDDATE|MARK); 1324 MarkSeq = 0; 1325 } 1326 1327 for (f = Files; f; f = f->f_next) { 1328 if (f->f_prevcount && now >= REPEATTIME(f)) { 1329 dprintf("flush %s: repeated %d times, %d sec.\n", 1330 TypeNames[f->f_type], f->f_prevcount, 1331 repeatinterval[f->f_repeatcount]); 1332 fprintlog(f, 0, (char *)NULL); 1333 BACKOFF(f); 1334 } 1335 } 1336 MarkSet = 0; 1337 (void)alarm(TIMERINTVL); 1338 } 1339