date and time created 84/04/24 14:27:53 by ralph
[unix-history] / usr / src / usr.sbin / syslogd / syslogd.c
CommitLineData
e677da31
RC
1#ifndef lint
2static char SccsId[] = "@(#)syslogd.c 4.1 (Berkeley) %G%";
3#endif
4
5/*
6 * syslogd -- log system messages
7 *
8 * This program implements a system log. It takes a series of lines.
9 * Each line may have a priority, signified as "<n>" as
10 * the first three characters of the line. If this is
11 * not present, a default priority (DefPri) is used, which
12 * starts out as LOG_NOTICE. The default priority can get
13 * changed using "<*>n".
14 *
15 * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
16 * cause it to reread its configuration file.
17 *
18 * Defined Constants:
19 *
20 * DAEMON -- Userid number to setuid to after setup.
21 * MAXLINE -- the maximimum line length that can be handled.
22 * NLOGS -- the maximum number of simultaneous log files.
23 * NUSERS -- the maximum number of people that can
24 * be designated as "superusers" on your system.
25 */
26
27#define DAEMON 1 /* Daemon user-id */
28#define NLOGS 10 /* max number of log files */
29#define NSUSERS 10 /* max number of special users */
30#define MAXLINE 1024 /* maximum line length */
31
32#include <syslog.h>
33#include <errno.h>
34#include <stdio.h>
35#include <utmp.h>
36#include <ctype.h>
37#include <sys/types.h>
38#include <sys/ioctl.h>
39#include <sys/stat.h>
40#include <signal.h>
41#include <sysexits.h>
42#include <sys/socket.h>
43#include <sys/file.h>
44#include <sys/un.h>
45#include <netinet/in.h>
46#include <netdb.h>
47
48char logname[] = "/dev/log";
49char defconf[] = "/etc/syslog.conf";
50char defpid[] = "/etc/syslog.pid";
51char ctty[] = "/dev/console";
52
53typedef char bool;
54#define TRUE 1
55#define FALSE 0
56
57#define dprintf if (Debug) printf
58
59#define UNAMESZ 8 /* length of a login name */
60
61/*
62 * This structure represents the files that will have log
63 * copies printed.
64 */
65
66struct filed {
67 int f_file; /* file descriptor */
68 short f_pmask; /* priority mask */
69 short f_flags; /* see #defines below */
70 struct sockaddr_in f_addr; /* forwarding address */
71 char f_name[248]; /* filename */
72};
73
74#define F_TTY 01 /* file is a tty */
75#define F_MARK 02 /* write to the file periodically */
76#define F_FORW 04 /* forward message to another host */
77
78struct filed Files[NLOGS];
79
80/* list of superusers */
81struct susers {
82 short s_pmask; /* priority mask */
83 char s_name[UNAMESZ+1];
84};
85
86struct susers Susers[NSUSERS];
87
88int Debug; /* debug flag */
89int LogFile; /* log file descriptor */
90int DefPri = LOG_NOTICE; /* the default priority for untagged msgs */
91int Sumask = LOG_SALERT; /* lowest priority written to super-users */
92int MarkIntvl = 15; /* mark interval in minutes */
93char *ConfFile = defconf; /* configuration file */
94char rhost[32]; /* hostname of sender (forwarded messages) */
95
96extern int errno, sys_nerr;
97extern char *sys_errlist[];
98extern char *ctime();
99
100main(argc, argv)
101 int argc;
102 char **argv;
103{
104 register int i;
105 register char *p;
106 int funix, finet, defreadfds, len;
107 struct sockaddr_un sun, fromunix;
108 struct sockaddr_in sin, frominet;
109 FILE *fp;
110 char line[MAXLINE + 1];
111 extern die();
112 extern int domark();
113
114 sun.sun_family = AF_UNIX;
115 strncpy(sun.sun_path, logname, sizeof sun.sun_path);
116
117 while (--argc > 0) {
118 p = *++argv;
119 if (p[0] == '-') {
120 switch (p[1]) {
121 case 'm': /* set mark interval */
122 MarkIntvl = atoi(&p[2]);
123 if (MarkIntvl <= 0)
124 MarkIntvl = 1;
125 break;
126
127 case 'f': /* configuration file */
128 if (p[2] != '\0')
129 ConfFile = &p[2];
130 break;
131
132 case 'd': /* debug */
133 Debug++;
134 break;
135
136 case 'p': /* port */
137 if (p[2] != '\0')
138 strncpy(sun.sun_path, &p[2],
139 sizeof sun.sun_path);
140 break;
141 }
142 }
143 }
144
145 if (!Debug) {
146 if (fork())
147 exit(0);
148 for (i = 0; i < 10; i++)
149 (void) close(i);
150 (void) open("/", 0);
151 (void) dup2(0, 1);
152 (void) dup2(0, 2);
153 i = open("/dev/tty", 2);
154 if (i >= 0) {
155 ioctl(i, TIOCNOTTY, (char *)0);
156 (void) close(i);
157 }
158 }
159 signal(SIGTERM, die);
160 signal(SIGALRM, domark);
161 alarm(MarkIntvl * 60);
162
163 funix = socket(AF_UNIX, SOCK_DGRAM, 0);
164 if (funix >= 0 && bind(funix, &sun,
165 sizeof(sun.sun_family)+strlen(sun.sun_path)) < 0) {
166 close(funix);
167 funix = -1;
168 }
169 if (funix < 0) {
170 fp = fopen(ctty, "w");
171 fprintf(fp, "\r\nsyslog: cannot create %s (%d)\r\n", logname, errno);
172 dprintf("cannot create %s (%d)\n", logname, errno);
173 exit(1);
174 }
175 defreadfds = 1 << funix;
176 finet = socket(AF_INET, SOCK_DGRAM, 0);
177 if (finet >= 0) {
178 struct servent *sp;
179
180 sp = getservbyname("syslog", "udp");
181 if (sp == NULL) {
182 errno = 0;
183 logerror("syslog/udp: unknown service");
184 die();
185 }
186 sin.sin_family = AF_INET;
187 sin.sin_port = sp->s_port;
188 if (bind(finet, &sin, sizeof(sin), 0) < 0) {
189 logerror("bind");
190 die();
191 }
192 defreadfds |= 1 << finet;
193 }
194
195 /* tuck my process id away */
196 fp = fopen(defpid, "w");
197 if (fp != NULL) {
198 fprintf(fp, "%d\n", getpid());
199 fclose(fp);
200 }
201
202 dprintf("off & running....\n");
203
204 for (i = 0; i < NLOGS; i++)
205 Files[i].f_file = -1;
206 init();
207 for (;;) {
208 int domain, nfds, readfds = defreadfds;
209
210 nfds = select(20, &readfds, 0, 0, 0);
211 if (nfds == 0)
212 continue;
213 if (nfds < 0) {
214 if (errno == EINTR)
215 continue;
216 logerror("select");
217 continue;
218 }
219 if (readfds & (1 << funix)) {
220 domain = AF_UNIX;
221 len = sizeof fromunix;
222 i = recvfrom(funix, line, MAXLINE, 0, &fromunix, &len);
223 } else if (readfds & (1 << finet)) {
224 domain = AF_INET;
225 len = sizeof frominet;
226 i = recvfrom(finet, line, MAXLINE, 0, &frominet, &len);
227 }
228 if (i < 0) {
229 if (errno == EINTR)
230 continue;
231 logerror("recvfrom");
232 continue;
233 }
234 if (domain == AF_INET && !chkhost(&frominet))
235 continue;
236 line[i] = '\0';
237 printline(domain == AF_UNIX, line);
238 }
239}
240
241/*
242 * Take a raw input line, decode the message, and print the message
243 * on the appropriate log files.
244 */
245
246printline(local, msg)
247 int local;
248 char *msg;
249{
250 register char *p, *q;
251 register int c;
252 char line[MAXLINE + sizeof(rhost) + 4];
253 int pri;
254
255 /* test for special codes */
256 pri = DefPri;
257 p = msg;
258 if (p[0] == '<' && p[2] == '>') {
259 switch (p[1]) {
260 case '*': /* reset default message priority */
261 dprintf("default priority = %c\n", p[3]);
262 c = p[3] - '0';
263 if ((unsigned) c <= 9)
264 DefPri = c;
265 break;
266
267 case '$': /* reconfigure */
268 dprintf("reconfigure\n");
269 init();
270 }
271 p++;
272 pri = *p++ - '0';
273 p++;
274 if ((unsigned) pri > LOG_DEBUG)
275 pri = DefPri;
276 }
277
278 q = line;
279 if (!local) {
280 sprintf(q, "%s: ", rhost);
281 q += strlen(q);
282 }
283 while ((c = *p++ & 0177) != '\0' && c != '\n' &&
284 q < &line[sizeof(line) - 2]) {
285 if (iscntrl(c)) {
286 *q++ = '^';
287 *q++ = c ^ 0100;
288 } else
289 *q++ = c;
290 }
291 *q++ = '\n';
292 *q = '\0';
293
294 logmsg(pri, line);
295}
296
297/*
298 * Log a message to the appropriate log files, users, etc. based on
299 * the priority.
300 */
301
302logmsg(pri, msg)
303 int pri;
304 char *msg;
305{
306 char line[MAXLINE + 1];
307 register struct filed *f;
308 register int l;
309
310 /* log the message to the particular outputs */
311 for (f = Files; f < &Files[NLOGS]; f++) {
312 if (f->f_file < 0 || f->f_pmask < pri)
313 continue;
314 if (f->f_flags & F_FORW) {
315 sprintf(line, "<%d>%s", pri, msg);
316 l = strlen(line);
317 if (l > MAXLINE)
318 l = MAXLINE;
319 if (sendto(f->f_file, line, l, 0,
320 &f->f_addr, sizeof f->f_addr) != l)
321 logerror("sendto");
322 continue;
323 }
324 l = strlen(msg);
325 if (write(f->f_file, msg, l) != l) {
326 logerror(f->f_name);
327 (void) close(f->f_file);
328 f->f_file = -1;
329 }
330 if ((f->f_flags & F_TTY) && write(f->f_file, "\r", 1) != 1) {
331 logerror(f->f_name);
332 (void) close(f->f_file);
333 f->f_file = -1;
334 }
335 }
336
337 /*
338 * Output high priority messages to terminals.
339 */
340 if (pri <= Sumask)
341 wallmsg(pri, msg);
342}
343
344/*
345 * INIT -- Initialize syslog from configuration table
346 *
347 * The configuration table consists of a series of lines
348 * broken into two sections by a blank line. The first
349 * section gives a list of files to log on. The first
350 * character is a digit which is the priority mask for
351 * that file. If the second character is an asterisk, then
352 * syslog arranges for something to be printed every fifteen
353 * minutes (even if only a null line), so that crashes and
354 * other events can be localized. The rest of the line is
355 * the pathname of the log file. The second section is
356 * a list of user names; these people are all notified
357 * when subalert messages occur (if they are logged on).
358 *
359 * The configuration table will be reread by this routine
360 * if a signal 1 occurs; for that reason, it is tricky
361 * about not re-opening files and closing files it will
362 * not be using.
363 */
364
365init()
366{
367 register int i;
368 register FILE *cf;
369 register struct filed *f;
370 register char *p;
371 char cline[BUFSIZ];
372 struct servent *sp;
373 struct hostent *hp;
374 int pmask, flags;
375 long now;
376
377 dprintf("init\n");
378
379 /* ignore interrupts during this routine */
380 signal(SIGHUP, SIG_IGN);
381
382 /*
383 * Close all open log files.
384 */
385 for (f = Files; f < &Files[NLOGS]; f++) {
386 if (f->f_file < 0)
387 (void) close(f->f_file);
388 f->f_file = -1;
389 }
390
391 /* open the configuration file */
392 if ((cf = fopen(ConfFile, "r")) == NULL) {
393 dprintf("cannot open %s\n", ConfFile);
394 f = Files;
395 if ((f->f_file = open(ctty, O_WRONLY)) >= 0) {
396 strncpy(f->f_name, ctty, sizeof(f->f_name)-1);
397 f->f_pmask = LOG_CRIT;
398 f->f_flags = F_TTY|F_MARK;
399 }
400 return;
401 }
402
403 /*
404 * Foreach line in the conf table, open that file.
405 */
406 f = Files;
407 sp = getservbyname("syslogd", "udp");
408 while (fgets(cline, sizeof cline, cf) != NULL) {
409 /* check for end-of-section */
410 if (cline[0] == '\n')
411 break;
412
413 /* strip off newline character */
414 for (p = cline; *p != '\0'; p++)
415 if (*p == '\n') {
416 *p = '\0';
417 break;
418 }
419
420 dprintf("F: got line '%s'\n", cline);
421
422 /* extract priority mask and mark flag */
423 p = cline;
424 flags = 0;
425 pmask = *p++ - '0';
426 if (*p == '*') {
427 p++;
428 flags |= F_MARK;
429 }
430
431 if (f >= &Files[NLOGS])
432 continue;
433
434 /* mark entry as used and update flags */
435 if (*p == '@') {
436 hp = gethostbyname(++p);
437 if (sp != NULL && hp != NULL) {
438 bzero(&f->f_addr, sizeof f->f_addr);
439 f->f_addr.sin_family = AF_INET;
440 f->f_addr.sin_port = sp->s_port;
441 bcopy(hp->h_addr, (char *) &f->f_addr.sin_addr, hp->h_length);
442 f->f_file = socket(AF_INET, SOCK_DGRAM, 0);
443 if (f->f_file < 0) {
444 logerror("socket");
445 continue;
446 }
447 }
448 flags |= F_FORW;
449 f->f_pmask = pmask;
450 f->f_flags = flags;
451 dprintf("Host %s pmask %d flags %o\n", p, pmask, flags);
452 f++;
453 continue;
454 }
455 strncpy(f->f_name, p, sizeof(f->f_name)-1);
456 if ((f->f_file = open(p, O_WRONLY|O_APPEND)) < 0) {
457 logerror(p);
458 continue;
459 }
460 if (isatty(f->f_file))
461 flags |= F_TTY;
462 f->f_pmask = pmask;
463 f->f_flags = flags;
464 dprintf("File %s pmask %d flags %o\n", p, pmask, flags);
465 f++;
466 }
467
468 /*
469 * Read the list of users.
470 *
471 * Anyone in this list is informed directly if s/he
472 * is logged in when a high priority message comes through.
473 */
474 for (i = 0; i < NSUSERS && fgets(cline, sizeof cline, cf) != NULL; i++) {
475 /* strip off newline */
476 for (p = cline; *p != '\0'; p++)
477 if (*p == '\n') {
478 *p = '\0';
479 break;
480 }
481 dprintf("U: got line '%s'\n", cline);
482 p = cline;
483 if (isdigit(*p)) {
484 Susers[i].s_pmask = pmask = *p++ - '0';
485 if (pmask > Sumask)
486 Sumask = pmask;
487 } else
488 Susers[i].s_pmask = LOG_SALERT;
489 strncpy(Susers[i].s_name, p, UNAMESZ);
490 dprintf("Suser %s pmask %d\n", p, pmask);
491 }
492
493 /* zero the rest of the old superusers */
494 while (i < NSUSERS)
495 Susers[i++].s_name[0] = '\0';
496
497 /* close the configuration file */
498 (void) fclose(cf);
499
500 time(&now);
501 sprintf(cline, "syslog restarted %s", ctime(&now));
502 logmsg(LOG_DEBUG, cline);
503
504 /* arrange for signal 1 to reconfigure */
505 signal(SIGHUP, init);
506}
507
508/*
509 * WALLMSG -- Write a message to the world at large
510 *
511 * Write the specified message to either the entire
512 * world, or a list of approved users.
513 */
514
515wallmsg(pri, msg)
516 int pri;
517 char *msg;
518{
519 register char *p;
520 register int i;
521 int f, uf, flags, len, e;
522 struct utmp ut;
523 long now;
524 char line[MAXLINE + 100];
525 char hbuf[32];
526
527 /* open the user login file */
528 uf = open("/etc/utmp", 0);
529 if (uf < 0)
530 return;
531
532 time(&now);
533 gethostname(hbuf, sizeof hbuf);
534 sprintf(line,
535 "\r\n\007Broadcast message from syslog@%s at %.24s ...\r\n%s\r",
536 hbuf, ctime(&now), msg);
537 len = strlen(line);
538
539 /* scan the user login file */
540 while (read(uf, &ut, sizeof ut) == sizeof ut) {
541 /* is this slot used? */
542 if (ut.ut_name[0] == '\0')
543 continue;
544
545 /* should we send the message to this user? */
546 if (pri != LOG_ALERT) {
547 for (i = 0; i < NSUSERS; i++) {
548 if (pri > Susers[i].s_pmask)
549 continue;
550 if (strncmp(Susers[i].s_name, ut.ut_name,
551 sizeof ut.ut_name) == 0)
552 goto prmsg;
553 }
554 continue;
555 }
556 prmsg:
557
558 /* compute the device name */
559 p = "/dev/12345678";
560 strcpyn(&p[5], ut.ut_line, UNAMESZ);
561
562 /* open the terminal */
563 f = open(p, O_WRONLY|O_NDELAY);
564 if (f < 0)
565 continue;
566 if ((flags = fcntl(f, F_GETFL, 0)) == -1)
567 continue;
568 if (fcntl(f, F_SETFL, flags | FNDELAY) == -1)
569 goto oldway;
570 i = write(f, line, len);
571 e = errno;
572 (void) fcntl(f, F_SETFL, flags);
573 if (i == len || e != EWOULDBLOCK) {
574 (void) close(f);
575 continue;
576 }
577 oldway:
578 if (fork() == 0) {
579 (void) write(f, line, len);
580 exit(0);
581 }
582 (void) close(f);
583 }
584
585 /* close the user login file */
586 (void) close(uf);
587}
588
589/*
590 * Make sure every marked file gets written to periodically.
591 * Reset the alarm clock to call itself after MarkIntvl minutes.
592 */
593domark()
594{
595 register struct filed *f;
596 struct stat stb;
597 char buf[40];
598 long now;
599
600 dprintf("domark\n");
601
602 time(&now);
603 for (f = Files; f < &Files[NLOGS]; f++) {
604 if (f->f_file < 0 || fstat(f->f_file, &stb) < 0)
605 continue;
606 if (stb.st_mtime >= now - MarkIntvl * 60)
607 continue;
608 sprintf(buf, " --- MARK --- %s", ctime(&now));
609 logmsg(-1, buf);
610 }
611 alarm(MarkIntvl * 60);
612}
613
614/*
615 * Check to see if we should log this message.
616 */
617chkhost(f)
618 struct sockaddr_in *f;
619{
620 struct hostent *hp;
621 extern char *inet_ntoa();
622
623 dprintf("chkhost\n");
624
625 if (f->sin_family != AF_INET) {
626 dprintf("Malformed from address\n");
627 return (0);
628 }
629 hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
630 if (hp == 0) {
631 dprintf("Host name for your address (%s) unknown\n",
632 inet_ntoa(f->sin_addr));
633 return (0);
634 }
635 strncpy(rhost, hp->h_name, sizeof rhost);
636 return (1);
637}
638
639/*
640 * Print syslogd errors some place.
641 */
642logerror(type)
643 char *type;
644{
645 char buf[100];
646
647 if (errno == 0)
648 sprintf(buf, "syslogd: %s\n", type);
649 else if ((unsigned) errno > sys_nerr)
650 sprintf(buf, "syslogd: %s: error %d\n", type, errno);
651 else
652 sprintf(buf, "syslogd: %s: %s\n", type, sys_errlist[errno]);
653 errno = 0;
654 dprintf(buf);
655 logmsg(LOG_ERR, buf);
656}
657
658die()
659{
660 logmsg(LOG_DEBUG, "syslog: down\n");
661 (void) unlink(logname);
662 exit(0);
663}