]> git.saurik.com Git - apple/network_cmds.git/blob - syslogd.tproj/syslogd.c
network_cmds-85.tar.gz
[apple/network_cmds.git] / syslogd.tproj / syslogd.c
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
58 static 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
64 static 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
125 char *LogName = _PATH_LOG;
126 char *ConfFile = _PATH_LOGCONF;
127 char *PidFile = _PATH_LOGPID;
128 char 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
150 struct 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 */
178 int 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
194 char *TypeNames[7] = {
195 "UNUSED", "FILE", "TTY", "CONSOLE",
196 "FORW", "USERS", "WALL"
197 };
198
199 struct filed *Files;
200 struct filed consfile;
201
202 int Debug = 0; /* debug flag */
203 int Insecure = 0; /* insecure flag */
204 char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
205 char *LocalDomain; /* our local domain name */
206 int InetInuse = 0; /* non-zero if INET sockets are being used */
207 int finet; /* Internet datagram socket */
208 int LogPort; /* port number for INET connections */
209 int Initialized = 0; /* set when we have initialized ourselves */
210 int MarkInterval = 20 * 60; /* interval between marks in seconds */
211 int MarkSeq = 0; /* mark sequence number */
212
213 void cfline __P((char *, struct filed *));
214 char *cvthname __P((struct sockaddr_in *));
215 int decode __P((const char *, CODE *));
216 void die __P((int));
217 void domark __P((int));
218 void fprintlog __P((struct filed *, int, char *));
219 void init __P((int));
220 void logerror __P((char *));
221 void logmsg __P((int, char *, char *, int));
222 void printline __P((char *, char *));
223 void printsys __P((char *));
224 void reapchild __P((int));
225 char *ttymsg __P((struct iovec *, int, char *, int));
226 void usage __P((void));
227 void wallmsg __P((struct filed *, struct iovec *));
228
229 int
230 main(argc, argv)
231 int argc;
232 char *argv[];
233 {
234 int ch, funix, i, inetm, fklog, klogm, len;
235 struct sockaddr_un sunx, fromunix;
236 struct sockaddr_in sin, frominet;
237 FILE *fp;
238 char *p, line[MSG_BSIZE + 1];
239
240 while ((ch = getopt(argc, argv, "duf:m:p:")) != EOF)
241 switch(ch) {
242 case 'd': /* debug */
243 Debug++;
244 break;
245 case 'u': /* insecure */
246 Insecure++;
247 break;
248 case 'f': /* configuration file */
249 ConfFile = optarg;
250 break;
251 case 'm': /* mark interval */
252 MarkInterval = atoi(optarg) * 60;
253 break;
254 case 'p': /* path */
255 LogName = optarg;
256 break;
257 case '?':
258 default:
259 usage();
260 }
261 if ((argc -= optind) != 0)
262 usage();
263
264 if (!Debug)
265 (void)daemon(0, 0);
266 else
267 setlinebuf(stdout);
268
269 consfile.f_type = F_CONSOLE;
270 (void)strcpy(consfile.f_un.f_fname, ctty);
271 (void)gethostname(LocalHostName, sizeof(LocalHostName));
272 if ((p = strchr(LocalHostName, '.')) != NULL) {
273 *p++ = '\0';
274 LocalDomain = p;
275 } else
276 LocalDomain = "";
277 (void)signal(SIGTERM, die);
278 (void)signal(SIGINT, Debug ? die : SIG_IGN);
279 (void)signal(SIGQUIT, Debug ? die : SIG_IGN);
280 (void)signal(SIGCHLD, reapchild);
281 (void)signal(SIGALRM, domark);
282 (void)alarm(TIMERINTVL);
283 (void)unlink(LogName);
284
285 #ifndef SUN_LEN
286 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
287 #endif
288 memset(&sunx, 0, sizeof(sunx));
289 sunx.sun_family = AF_UNIX;
290 (void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
291 funix = socket(AF_UNIX, SOCK_DGRAM, 0);
292 if (funix < 0 ||
293 bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
294 chmod(LogName, 0666) < 0) {
295 (void) snprintf(line, sizeof line, "cannot create %s", LogName);
296 logerror(line);
297 dprintf("cannot create %s (%d)\n", LogName, errno);
298 die(0);
299 }
300 finet = socket(AF_INET, SOCK_DGRAM, 0);
301 inetm = 0;
302 if (finet >= 0) {
303 struct servent *sp;
304
305 sp = getservbyname("syslog", "udp");
306 if (sp == NULL) {
307 errno = 0;
308 logerror("syslog/udp: unknown service");
309 die(0);
310 }
311 memset(&sin, 0, sizeof(sin));
312 sin.sin_family = AF_INET;
313 sin.sin_port = LogPort = sp->s_port;
314 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
315 logerror("bind");
316 if (!Debug)
317 die(0);
318 } else {
319 inetm = FDMASK(finet);
320 InetInuse = 1;
321 }
322 }
323 if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
324 klogm = FDMASK(fklog);
325 else {
326 dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
327 klogm = 0;
328 }
329
330 /* tuck my process id away */
331 fp = fopen(PidFile, "w");
332 if (fp != NULL) {
333 fprintf(fp, "%d\n", getpid());
334 (void) fclose(fp);
335 }
336
337 dprintf("off & running....\n");
338
339 init(0);
340 (void)signal(SIGHUP, init);
341
342 for (;;) {
343 int nfds, readfds = FDMASK(funix) | inetm | klogm;
344
345 dprintf("readfds = %#x\n", readfds);
346 nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
347 (fd_set *)NULL, (struct timeval *)NULL);
348 if (nfds == 0)
349 continue;
350 if (nfds < 0) {
351 if (errno != EINTR)
352 logerror("select");
353 continue;
354 }
355 dprintf("got a message (%d, %#x)\n", nfds, readfds);
356 if (readfds & klogm) {
357 i = read(fklog, line, sizeof(line) - 1);
358 if (i > 0) {
359 line[i] = '\0';
360 printsys(line);
361 } else if (i < 0 && errno != EINTR) {
362 logerror("klog");
363 fklog = -1;
364 klogm = 0;
365 }
366 }
367 if (readfds & FDMASK(funix)) {
368 len = sizeof(fromunix);
369 i = recvfrom(funix, line, MAXLINE, 0,
370 (struct sockaddr *)&fromunix, &len);
371 if (i > 0) {
372 line[i] = '\0';
373 printline(LocalHostName, line);
374 } else if (i < 0 && errno != EINTR)
375 logerror("recvfrom unix");
376 }
377 if (readfds & inetm) {
378 len = sizeof(frominet);
379 i = recvfrom(finet, line, MAXLINE, 0,
380 (struct sockaddr *)&frominet, &len);
381 if (Insecure) {
382 if (i > 0) {
383 line[i] = '\0';
384 printline(cvthname(&frominet), line);
385 } else if (i < 0 && errno != EINTR)
386 logerror("recvfrom inet");
387 }
388 }
389 }
390 }
391
392 void
393 usage()
394 {
395
396 (void)fprintf(stderr,
397 "usage: syslogd [-f conffile] [-m markinterval] [-p logpath]\n");
398 exit(1);
399 }
400
401 /*
402 * Take a raw input line, decode the message, and print the message
403 * on the appropriate log files.
404 */
405 void
406 printline(hname, msg)
407 char *hname;
408 char *msg;
409 {
410 int c, pri;
411 char *p, *q, line[MAXLINE + 1];
412
413 /* test for special codes */
414 pri = DEFUPRI;
415 p = msg;
416 if (*p == '<') {
417 pri = 0;
418 while (isdigit(*++p))
419 pri = 10 * pri + (*p - '0');
420 if (*p == '>')
421 ++p;
422 }
423 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
424 pri = DEFUPRI;
425
426 /* don't allow users to log kernel messages */
427 if (LOG_FAC(pri) == LOG_KERN)
428 pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
429
430 q = line;
431
432 while ((c = *p++) != '\0' &&
433 q < &line[sizeof(line) - 2]) {
434 c &= 0177;
435 if (iscntrl(c))
436 if (c == '\n')
437 *q++ = ' ';
438 else if (c == '\t')
439 *q++ = '\t';
440 else {
441 *q++ = '^';
442 *q++ = c ^ 0100;
443 }
444 else
445 *q++ = c;
446 }
447 *q = '\0';
448
449 logmsg(pri, line, hname, 0);
450 }
451
452 /*
453 * Take a raw input line from /dev/klog, split and format similar to syslog().
454 */
455 void
456 printsys(msg)
457 char *msg;
458 {
459 int c, pri, flags;
460 char *lp, *p, *q, line[MAXLINE + 1];
461
462 (void)strcpy(line, "mach_kernel: ");
463 lp = line + strlen(line);
464 for (p = msg; *p != '\0'; ) {
465 flags = SYNC_FILE | ADDDATE; /* fsync file after write */
466 pri = DEFSPRI;
467 if (*p == '<') {
468 pri = 0;
469 while (isdigit(*++p))
470 pri = 10 * pri + (*p - '0');
471 if (*p == '>')
472 ++p;
473 } else {
474 /* kernel printf's come out on console */
475 flags |= IGN_CONS;
476 }
477 if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
478 pri = DEFSPRI;
479 q = lp;
480 while (*p != '\0' && (c = *p++) != '\n' &&
481 q < &line[MAXLINE])
482 *q++ = c;
483 *q = '\0';
484 logmsg(pri, line, LocalHostName, flags);
485 }
486 }
487
488 time_t now;
489
490 /*
491 * Log a message to the appropriate log files, users, etc. based on
492 * the priority.
493 */
494 void
495 logmsg(pri, msg, from, flags)
496 int pri;
497 char *msg, *from;
498 int flags;
499 {
500 struct filed *f;
501 int fac, msglen, omask, prilev;
502 char *timestamp;
503
504 dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
505 pri, flags, from, msg);
506
507 omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
508
509 /*
510 * Check to see if msg looks non-standard.
511 */
512 msglen = strlen(msg);
513 if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
514 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
515 flags |= ADDDATE;
516
517 (void)time(&now);
518 if (flags & ADDDATE)
519 timestamp = ctime(&now) + 4;
520 else {
521 timestamp = msg;
522 msg += 16;
523 msglen -= 16;
524 }
525
526 /* extract facility and priority level */
527 if (flags & MARK)
528 fac = LOG_NFACILITIES;
529 else
530 fac = LOG_FAC(pri);
531 prilev = LOG_PRI(pri);
532
533 /* log the message to the particular outputs */
534 if (!Initialized) {
535 f = &consfile;
536 f->f_file = open(ctty, O_WRONLY, 0);
537
538 if (f->f_file >= 0) {
539 fprintlog(f, flags, msg);
540 (void)close(f->f_file);
541 }
542 (void)sigsetmask(omask);
543 return;
544 }
545 for (f = Files; f; f = f->f_next) {
546 /* skip messages that are incorrect priority */
547 if (f->f_pmask[fac] < prilev ||
548 f->f_pmask[fac] == INTERNAL_NOPRI)
549 continue;
550
551 if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
552 continue;
553
554 /* don't output marks to recently written files */
555 if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
556 continue;
557
558 /*
559 * suppress duplicate lines to this file
560 */
561 if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
562 !strcmp(msg, f->f_prevline) &&
563 !strcmp(from, f->f_prevhost)) {
564 (void)strncpy(f->f_lasttime, timestamp, 15);
565 f->f_prevcount++;
566 dprintf("msg repeated %d times, %ld sec of %d\n",
567 f->f_prevcount, now - f->f_time,
568 repeatinterval[f->f_repeatcount]);
569 /*
570 * If domark would have logged this by now,
571 * flush it now (so we don't hold isolated messages),
572 * but back off so we'll flush less often
573 * in the future.
574 */
575 if (now > REPEATTIME(f)) {
576 fprintlog(f, flags, (char *)NULL);
577 BACKOFF(f);
578 }
579 } else {
580 /* new line, save it */
581 if (f->f_prevcount)
582 fprintlog(f, 0, (char *)NULL);
583 f->f_repeatcount = 0;
584 (void)strncpy(f->f_lasttime, timestamp, 15);
585 (void)strncpy(f->f_prevhost, from,
586 sizeof(f->f_prevhost));
587 if (msglen < MAXSVLINE) {
588 f->f_prevlen = msglen;
589 f->f_prevpri = pri;
590 (void)strcpy(f->f_prevline, msg);
591 fprintlog(f, flags, (char *)NULL);
592 } else {
593 f->f_prevline[0] = 0;
594 f->f_prevlen = 0;
595 fprintlog(f, flags, msg);
596 }
597 }
598 }
599 (void)sigsetmask(omask);
600 }
601
602 void
603 fprintlog(f, flags, msg)
604 struct filed *f;
605 int flags;
606 char *msg;
607 {
608 struct iovec iov[6];
609 struct iovec *v;
610 int l;
611 char line[MAXLINE + 1], repbuf[80], greetings[200];
612
613 v = iov;
614 if (f->f_type == F_WALL) {
615 v->iov_base = greetings;
616 v->iov_len = snprintf(greetings, sizeof greetings,
617 "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
618 f->f_prevhost, ctime(&now));
619 v++;
620 v->iov_base = "";
621 v->iov_len = 0;
622 v++;
623 } else {
624 v->iov_base = f->f_lasttime;
625 v->iov_len = 15;
626 v++;
627 v->iov_base = " ";
628 v->iov_len = 1;
629 v++;
630 }
631 v->iov_base = f->f_prevhost;
632 v->iov_len = strlen(v->iov_base);
633 v++;
634 v->iov_base = " ";
635 v->iov_len = 1;
636 v++;
637
638 if (msg) {
639 v->iov_base = msg;
640 v->iov_len = strlen(msg);
641 } else if (f->f_prevcount > 1) {
642 v->iov_base = repbuf;
643 v->iov_len = snprintf(repbuf, sizeof repbuf, "last message repeated %d times",
644 f->f_prevcount);
645 } else {
646 v->iov_base = f->f_prevline;
647 v->iov_len = f->f_prevlen;
648 }
649 v++;
650
651 dprintf("Logging to %s", TypeNames[f->f_type]);
652 f->f_time = now;
653
654 switch (f->f_type) {
655 case F_UNUSED:
656 dprintf("\n");
657 break;
658
659 case F_FORW:
660 dprintf(" %s\n", f->f_un.f_forw.f_hname);
661 l = snprintf(line, sizeof line, "<%d>%.15s %s", f->f_prevpri,
662 iov[0].iov_base, iov[4].iov_base);
663 if (l > MAXLINE)
664 l = MAXLINE;
665 if (sendto(finet, line, l, 0,
666 (struct sockaddr *)&f->f_un.f_forw.f_addr,
667 sizeof(f->f_un.f_forw.f_addr)) != l) {
668 int e = errno;
669 (void)close(f->f_file);
670 f->f_type = F_UNUSED;
671 errno = e;
672 logerror("sendto");
673 }
674 break;
675
676 case F_CONSOLE:
677 if (flags & IGN_CONS) {
678 dprintf(" (ignored)\n");
679 break;
680 }
681 /* FALLTHROUGH */
682
683 case F_TTY:
684 case F_FILE:
685 dprintf(" %s\n", f->f_un.f_fname);
686 if (f->f_type != F_FILE) {
687 v->iov_base = "\r\n";
688 v->iov_len = 2;
689 } else {
690 v->iov_base = "\n";
691 v->iov_len = 1;
692 }
693 again:
694 if (writev(f->f_file, iov, 6) < 0) {
695 int e = errno;
696 (void)close(f->f_file);
697 /*
698 * Check for errors on TTY's due to loss of tty
699 */
700 if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
701 f->f_file = open(f->f_un.f_fname,
702 O_WRONLY|O_APPEND, 0);
703 if (f->f_file < 0) {
704 f->f_type = F_UNUSED;
705 logerror(f->f_un.f_fname);
706 } else
707 goto again;
708 } else {
709 f->f_type = F_UNUSED;
710 errno = e;
711 logerror(f->f_un.f_fname);
712 }
713 } else if (flags & SYNC_FILE)
714 (void)fsync(f->f_file);
715 break;
716
717 case F_USERS:
718 case F_WALL:
719 dprintf("\n");
720 v->iov_base = "\r\n";
721 v->iov_len = 2;
722 wallmsg(f, iov);
723 break;
724 }
725 f->f_prevcount = 0;
726 }
727
728 /*
729 * WALLMSG -- Write a message to the world at large
730 *
731 * Write the specified message to either the entire
732 * world, or a list of approved users.
733 */
734 void
735 wallmsg(f, iov)
736 struct filed *f;
737 struct iovec *iov;
738 {
739 static int reenter; /* avoid calling ourselves */
740 FILE *uf;
741 struct utmp ut;
742 int i;
743 char *p;
744 char line[sizeof(ut.ut_line) + 1];
745
746 if (reenter++)
747 return;
748 if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
749 logerror(_PATH_UTMP);
750 reenter = 0;
751 return;
752 }
753 /* NOSTRICT */
754 while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
755 if (ut.ut_name[0] == '\0')
756 continue;
757 strncpy(line, ut.ut_line, sizeof(ut.ut_line));
758 line[sizeof(ut.ut_line)] = '\0';
759 if (f->f_type == F_WALL) {
760 if ((p = ttymsg(iov, 6, line, 60*5)) != NULL) {
761 errno = 0; /* already in msg */
762 logerror(p);
763 }
764 continue;
765 }
766 /* should we send the message to this user? */
767 for (i = 0; i < MAXUNAMES; i++) {
768 if (!f->f_un.f_uname[i][0])
769 break;
770 if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
771 UT_NAMESIZE)) {
772 if ((p = ttymsg(iov, 6, line, 60*5)) != NULL) {
773 errno = 0; /* already in msg */
774 logerror(p);
775 }
776 break;
777 }
778 }
779 }
780 (void)fclose(uf);
781 reenter = 0;
782 }
783
784 void
785 reapchild(signo)
786 int signo;
787 {
788 union wait status;
789
790 while (wait3((int *)&status, WNOHANG, (struct rusage *)NULL) > 0)
791 ;
792 }
793
794 /*
795 * Return a printable representation of a host address.
796 */
797 char *
798 cvthname(f)
799 struct sockaddr_in *f;
800 {
801 struct hostent *hp;
802 char *p;
803
804 dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
805
806 if (f->sin_family != AF_INET) {
807 dprintf("Malformed from address\n");
808 return ("???");
809 }
810 hp = gethostbyaddr((char *)&f->sin_addr,
811 sizeof(struct in_addr), f->sin_family);
812 if (hp == 0) {
813 dprintf("Host name for your address (%s) unknown\n",
814 inet_ntoa(f->sin_addr));
815 return (inet_ntoa(f->sin_addr));
816 }
817 if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
818 *p = '\0';
819 return (hp->h_name);
820 }
821
822 void
823 domark(signo)
824 int signo;
825 {
826 struct filed *f;
827
828 now = time((time_t *)NULL);
829 MarkSeq += TIMERINTVL;
830 if (MarkSeq >= MarkInterval) {
831 logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
832 MarkSeq = 0;
833 }
834
835 for (f = Files; f; f = f->f_next) {
836 if (f->f_prevcount && now >= REPEATTIME(f)) {
837 dprintf("flush %s: repeated %d times, %d sec.\n",
838 TypeNames[f->f_type], f->f_prevcount,
839 repeatinterval[f->f_repeatcount]);
840 fprintlog(f, 0, (char *)NULL);
841 BACKOFF(f);
842 }
843 }
844 (void)alarm(TIMERINTVL);
845 }
846
847 /*
848 * Print syslogd errors some place.
849 */
850 void
851 logerror(type)
852 char *type;
853 {
854 char buf[100];
855
856 if (errno)
857 (void)snprintf(buf,
858 sizeof(buf), "syslogd: %s: %s", type, strerror(errno));
859 else
860 (void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
861 errno = 0;
862 dprintf("%s\n", buf);
863 logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
864 }
865
866 void
867 die(signo)
868 int signo;
869 {
870 struct filed *f;
871 char buf[100];
872
873 for (f = Files; f != NULL; f = f->f_next) {
874 /* flush any pending output */
875 if (f->f_prevcount)
876 fprintlog(f, 0, (char *)NULL);
877 }
878 if (signo) {
879 dprintf("syslogd: exiting on signal %d\n", signo);
880 (void)snprintf(buf, sizeof buf, "exiting on signal %d", signo);
881 errno = 0;
882 logerror(buf);
883 }
884 (void)unlink(LogName);
885 exit(0);
886 }
887
888 /*
889 * INIT -- Initialize syslogd from configuration table
890 */
891 void
892 init(signo)
893 int signo;
894 {
895 int i;
896 FILE *cf;
897 struct filed *f, *next, **nextp;
898 char *p;
899 char cline[LINE_MAX];
900
901 dprintf("init\n");
902
903 /*
904 * Close all open log files.
905 */
906 Initialized = 0;
907 for (f = Files; f != NULL; f = next) {
908 /* flush any pending output */
909 if (f->f_prevcount)
910 fprintlog(f, 0, (char *)NULL);
911
912 switch (f->f_type) {
913 case F_FILE:
914 case F_TTY:
915 case F_CONSOLE:
916 case F_FORW:
917 (void)close(f->f_file);
918 break;
919 }
920 next = f->f_next;
921 free((char *)f);
922 }
923 Files = NULL;
924 nextp = &Files;
925
926 /* open the configuration file */
927 if ((cf = fopen(ConfFile, "r")) == NULL) {
928 dprintf("cannot open %s\n", ConfFile);
929 *nextp = (struct filed *)calloc(1, sizeof(*f));
930 cfline("*.ERR\t/dev/console", *nextp);
931 (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
932 cfline("*.PANIC\t*", (*nextp)->f_next);
933 Initialized = 1;
934 return;
935 }
936
937 /*
938 * Foreach line in the conf table, open that file.
939 */
940 f = NULL;
941 while (fgets(cline, sizeof(cline), cf) != NULL) {
942 /*
943 * check for end-of-section, comments, strip off trailing
944 * spaces and newline character.
945 */
946 for (p = cline; isspace(*p); ++p)
947 continue;
948 if (*p == NULL || *p == '#')
949 continue;
950 for (p = strchr(cline, '\0'); isspace(*--p);)
951 continue;
952 *++p = '\0';
953 f = (struct filed *)calloc(1, sizeof(*f));
954 *nextp = f;
955 nextp = &f->f_next;
956 cfline(cline, f);
957 }
958
959 /* close the configuration file */
960 (void)fclose(cf);
961
962 Initialized = 1;
963
964 if (Debug) {
965 for (f = Files; f; f = f->f_next) {
966 for (i = 0; i <= LOG_NFACILITIES; i++)
967 if (f->f_pmask[i] == INTERNAL_NOPRI)
968 printf("X ");
969 else
970 printf("%d ", f->f_pmask[i]);
971 printf("%s: ", TypeNames[f->f_type]);
972 switch (f->f_type) {
973 case F_FILE:
974 case F_TTY:
975 case F_CONSOLE:
976 printf("%s", f->f_un.f_fname);
977 break;
978
979 case F_FORW:
980 printf("%s", f->f_un.f_forw.f_hname);
981 break;
982
983 case F_USERS:
984 for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
985 printf("%s, ", f->f_un.f_uname[i]);
986 break;
987 }
988 printf("\n");
989 }
990 }
991
992 logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
993 dprintf("syslogd: restarted\n");
994 }
995
996 /*
997 * Crack a configuration file line
998 */
999 void
1000 cfline(line, f)
1001 char *line;
1002 struct filed *f;
1003 {
1004 struct hostent *hp;
1005 int i, pri;
1006 char *bp, *p, *q;
1007 char buf[MAXLINE], ebuf[100];
1008
1009 dprintf("cfline(%s)\n", line);
1010
1011 errno = 0; /* keep strerror() stuff out of logerror messages */
1012
1013 /* clear out file entry */
1014 memset(f, 0, sizeof(*f));
1015 for (i = 0; i <= LOG_NFACILITIES; i++)
1016 f->f_pmask[i] = INTERNAL_NOPRI;
1017
1018 /* scan through the list of selectors */
1019 for (p = line; *p && *p != '\t';) {
1020
1021 /* find the end of this facility name list */
1022 for (q = p; *q && *q != '\t' && *q++ != '.'; )
1023 continue;
1024
1025 /* collect priority name */
1026 for (bp = buf; *q && !strchr("\t,;", *q); )
1027 *bp++ = *q++;
1028 *bp = '\0';
1029
1030 /* skip cruft */
1031 while (strchr(", ;", *q))
1032 q++;
1033
1034 /* decode priority name */
1035 if (*buf == '*')
1036 pri = LOG_PRIMASK + 1;
1037 else {
1038 pri = decode(buf, prioritynames);
1039 if (pri < 0) {
1040 (void)snprintf(ebuf, sizeof ebuf,
1041 "unknown priority name \"%s\"", buf);
1042 logerror(ebuf);
1043 return;
1044 }
1045 }
1046
1047 /* scan facilities */
1048 while (*p && !strchr("\t.;", *p)) {
1049 for (bp = buf; *p && !strchr("\t,;.", *p); )
1050 *bp++ = *p++;
1051 *bp = '\0';
1052 if (*buf == '*')
1053 for (i = 0; i < LOG_NFACILITIES; i++)
1054 f->f_pmask[i] = pri;
1055 else {
1056 i = decode(buf, facilitynames);
1057 if (i < 0) {
1058 (void)snprintf(ebuf, sizeof ebuf,
1059 "unknown facility name \"%s\"",
1060 buf);
1061 logerror(ebuf);
1062 return;
1063 }
1064 f->f_pmask[i >> 3] = pri;
1065 }
1066 while (*p == ',' || *p == ' ')
1067 p++;
1068 }
1069
1070 p = q;
1071 }
1072
1073 /* skip to action part */
1074 while (*p == '\t')
1075 p++;
1076
1077 switch (*p)
1078 {
1079 case '@':
1080 if (!InetInuse)
1081 break;
1082 (void)strcpy(f->f_un.f_forw.f_hname, ++p);
1083 hp = gethostbyname(p);
1084 if (hp == NULL) {
1085 extern int h_errno;
1086
1087 logerror(hstrerror(h_errno));
1088 break;
1089 }
1090 memset(&f->f_un.f_forw.f_addr, 0,
1091 sizeof(f->f_un.f_forw.f_addr));
1092 f->f_un.f_forw.f_addr.sin_family = AF_INET;
1093 f->f_un.f_forw.f_addr.sin_port = LogPort;
1094 memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
1095 f->f_type = F_FORW;
1096 break;
1097
1098 case '/':
1099 (void)strcpy(f->f_un.f_fname, p);
1100 if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1101 f->f_file = F_UNUSED;
1102 logerror(p);
1103 break;
1104 }
1105 if (isatty(f->f_file))
1106 f->f_type = F_TTY;
1107 else
1108 f->f_type = F_FILE;
1109 if (strcmp(p, ctty) == 0)
1110 f->f_type = F_CONSOLE;
1111 break;
1112
1113 case '*':
1114 f->f_type = F_WALL;
1115 break;
1116
1117 default:
1118 for (i = 0; i < MAXUNAMES && *p; i++) {
1119 for (q = p; *q && *q != ','; )
1120 q++;
1121 (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1122 if ((q - p) > UT_NAMESIZE)
1123 f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1124 else
1125 f->f_un.f_uname[i][q - p] = '\0';
1126 while (*q == ',' || *q == ' ')
1127 q++;
1128 p = q;
1129 }
1130 f->f_type = F_USERS;
1131 break;
1132 }
1133 }
1134
1135
1136 /*
1137 * Decode a symbolic name to a numeric value
1138 */
1139 int
1140 decode(name, codetab)
1141 const char *name;
1142 CODE *codetab;
1143 {
1144 CODE *c;
1145 char *p, buf[40];
1146
1147 if (isdigit(*name))
1148 return (atoi(name));
1149
1150 for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1151 if (isupper(*name))
1152 *p = tolower(*name);
1153 else
1154 *p = *name;
1155 }
1156 *p = '\0';
1157 for (c = codetab; c->c_name; c++)
1158 if (!strcmp(buf, c->c_name))
1159 return (c->c_val);
1160
1161 return (-1);
1162 }