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