]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1983, 1988, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
ac2f15b3 | 35 | static const char copyright[] = |
b7080c8e A |
36 | "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\ |
37 | The Regents of the University of California. All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
ac2f15b3 | 41 | #if 0 |
b7080c8e | 42 | static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94"; |
ac2f15b3 | 43 | #endif |
b7080c8e A |
44 | #endif /* not lint */ |
45 | ||
46 | /* | |
47 | * syslogd -- log system messages | |
48 | * | |
49 | * This program implements a system log. It takes a series of lines. | |
50 | * Each line may have a priority, signified as "<n>" as | |
51 | * the first characters of the line. If this is | |
52 | * not present, a default priority is used. | |
53 | * | |
54 | * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will | |
55 | * cause it to reread its configuration file. | |
56 | * | |
57 | * Defined Constants: | |
58 | * | |
59 | * MAXLINE -- the maximimum line length that can be handled. | |
60 | * DEFUPRI -- the default priority for user messages | |
61 | * DEFSPRI -- the default priority for kernel messages | |
62 | * | |
63 | * Author: Eric Allman | |
64 | * extensive changes by Ralph Campbell | |
65 | * more extensive changes by Eric Allman (again) | |
ac2f15b3 A |
66 | * Extension to log by program name as well as facility and priority |
67 | * by Peter da Silva. | |
68 | * -u and -v by Harlan Stenn. | |
69 | * Priority comparison code by Harlan Stenn. | |
b7080c8e A |
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 */ | |
ac2f15b3 | 77 | #define TTYMSGTIME 1 /* timed out passed to ttymsg */ |
b7080c8e A |
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> | |
ac2f15b3 | 84 | #include <sys/queue.h> |
b7080c8e A |
85 | #include <sys/uio.h> |
86 | #include <sys/un.h> | |
87 | #include <sys/time.h> | |
88 | #include <sys/resource.h> | |
ac2f15b3 A |
89 | #include <sys/syslimits.h> |
90 | #include <grp.h> | |
b7080c8e A |
91 | |
92 | #include <netinet/in.h> | |
93 | #include <netdb.h> | |
94 | #include <arpa/inet.h> | |
95 | ||
96 | #include <ctype.h> | |
ac2f15b3 | 97 | #include <err.h> |
b7080c8e A |
98 | #include <errno.h> |
99 | #include <fcntl.h> | |
ac2f15b3 A |
100 | #include <limits.h> |
101 | #include <paths.h> | |
b7080c8e A |
102 | #include <signal.h> |
103 | #include <stdio.h> | |
104 | #include <stdlib.h> | |
105 | #include <string.h> | |
ac2f15b3 | 106 | #include <sysexits.h> |
b7080c8e A |
107 | #include <unistd.h> |
108 | #include <utmp.h> | |
ac2f15b3 A |
109 | #include <string.h> |
110 | ||
b7080c8e | 111 | #include "pathnames.h" |
ac2f15b3 | 112 | #include "ttymsg.h" |
b7080c8e A |
113 | |
114 | #define SYSLOG_NAMES | |
115 | #include <sys/syslog.h> | |
116 | ||
ac2f15b3 A |
117 | #ifdef NI_WITHSCOPEID |
118 | static const int withscopeid = NI_WITHSCOPEID; | |
119 | #else | |
120 | static const int withscopeid; | |
121 | #endif | |
b7080c8e | 122 | |
ac2f15b3 A |
123 | const char *ConfFile = _PATH_LOGCONF; |
124 | const char *PidFile = _PATH_LOGPID; | |
125 | const char ctty[] = _PATH_CONSOLE; | |
b7080c8e A |
126 | |
127 | #define dprintf if (Debug) printf | |
128 | ||
129 | #define MAXUNAMES 20 /* maximum number of user names */ | |
130 | ||
ac2f15b3 A |
131 | #define MAXFUNIX 20 |
132 | ||
133 | int nfunix = 1; | |
134 | const char *funixn[MAXFUNIX] = { _PATH_LOG }; | |
135 | int funix[MAXFUNIX]; | |
136 | ||
b7080c8e A |
137 | /* |
138 | * Flags to logmsg(). | |
139 | */ | |
140 | ||
141 | #define IGN_CONS 0x001 /* don't print on console */ | |
142 | #define SYNC_FILE 0x002 /* do fsync on file after printing */ | |
143 | #define ADDDATE 0x004 /* add a date to the message */ | |
144 | #define MARK 0x008 /* this message is a mark */ | |
ac2f15b3 | 145 | #define ISKERNEL 0x010 /* kernel generated message */ |
b7080c8e A |
146 | |
147 | /* | |
148 | * This structure represents the files that will have log | |
149 | * copies printed. | |
150 | */ | |
151 | ||
152 | struct filed { | |
153 | struct filed *f_next; /* next in linked list */ | |
154 | short f_type; /* entry type, see below */ | |
155 | short f_file; /* file descriptor */ | |
156 | time_t f_time; /* time this was last written */ | |
ac2f15b3 | 157 | char *f_host; /* host from which to recd. */ |
b7080c8e | 158 | u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */ |
ac2f15b3 A |
159 | u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */ |
160 | #define PRI_LT 0x1 | |
161 | #define PRI_EQ 0x2 | |
162 | #define PRI_GT 0x4 | |
163 | char *f_program; /* program this applies to */ | |
b7080c8e A |
164 | union { |
165 | char f_uname[MAXUNAMES][UT_NAMESIZE+1]; | |
166 | struct { | |
ac2f15b3 A |
167 | char f_hname[MAXHOSTNAMELEN]; |
168 | struct addrinfo *f_addr; | |
169 | ||
b7080c8e A |
170 | } f_forw; /* forwarding address */ |
171 | char f_fname[MAXPATHLEN]; | |
ac2f15b3 A |
172 | struct { |
173 | char f_pname[MAXPATHLEN]; | |
174 | pid_t f_pid; | |
175 | } f_pipe; | |
b7080c8e A |
176 | } f_un; |
177 | char f_prevline[MAXSVLINE]; /* last message logged */ | |
178 | char f_lasttime[16]; /* time of last occurrence */ | |
ac2f15b3 | 179 | char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */ |
b7080c8e A |
180 | int f_prevpri; /* pri of f_prevline */ |
181 | int f_prevlen; /* length of f_prevline */ | |
182 | int f_prevcount; /* repetition cnt of prevline */ | |
ac2f15b3 A |
183 | u_int f_repeatcount; /* number of "repeated" msgs */ |
184 | }; | |
185 | ||
186 | /* | |
187 | * Queue of about-to-be dead processes we should watch out for. | |
188 | */ | |
189 | ||
190 | TAILQ_HEAD(stailhead, deadq_entry) deadq_head; | |
191 | struct stailhead *deadq_headp; | |
192 | ||
193 | struct deadq_entry { | |
194 | pid_t dq_pid; | |
195 | int dq_timeout; | |
196 | TAILQ_ENTRY(deadq_entry) dq_entries; | |
197 | }; | |
198 | ||
199 | /* | |
200 | * The timeout to apply to processes waiting on the dead queue. Unit | |
201 | * of measure is `mark intervals', i.e. 20 minutes by default. | |
202 | * Processes on the dead queue will be terminated after that time. | |
203 | */ | |
204 | ||
205 | #define DQ_TIMO_INIT 2 | |
206 | ||
207 | typedef struct deadq_entry *dq_t; | |
208 | ||
209 | ||
210 | /* | |
211 | * Struct to hold records of network addresses that are allowed to log | |
212 | * to us. | |
213 | */ | |
214 | struct allowedpeer { | |
215 | int isnumeric; | |
216 | u_short port; | |
217 | union { | |
218 | struct { | |
219 | struct sockaddr_storage addr; | |
220 | struct sockaddr_storage mask; | |
221 | } numeric; | |
222 | char *name; | |
223 | } u; | |
224 | #define a_addr u.numeric.addr | |
225 | #define a_mask u.numeric.mask | |
226 | #define a_name u.name | |
b7080c8e A |
227 | }; |
228 | ||
ac2f15b3 | 229 | |
b7080c8e A |
230 | /* |
231 | * Intervals at which we flush out "message repeated" messages, | |
232 | * in seconds after previous message is logged. After each flush, | |
233 | * we move to the next interval until we reach the largest. | |
234 | */ | |
235 | int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */ | |
236 | #define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1) | |
237 | #define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) | |
238 | #define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ | |
239 | (f)->f_repeatcount = MAXREPEAT; \ | |
240 | } | |
241 | ||
242 | /* values for f_type */ | |
243 | #define F_UNUSED 0 /* unused entry */ | |
244 | #define F_FILE 1 /* regular file */ | |
245 | #define F_TTY 2 /* terminal */ | |
246 | #define F_CONSOLE 3 /* console terminal */ | |
247 | #define F_FORW 4 /* remote machine */ | |
248 | #define F_USERS 5 /* list of users */ | |
249 | #define F_WALL 6 /* everyone logged on */ | |
ac2f15b3 A |
250 | #define F_PIPE 7 /* pipe to program */ |
251 | #define F_CHECKTTY 8 /* think it's a tty, so check */ | |
b7080c8e | 252 | |
ac2f15b3 | 253 | const char *TypeNames[9] = { |
b7080c8e | 254 | "UNUSED", "FILE", "TTY", "CONSOLE", |
ac2f15b3 A |
255 | "FORW", "USERS", "WALL", "PIPE", |
256 | "CHECKTTY" | |
b7080c8e A |
257 | }; |
258 | ||
ac2f15b3 A |
259 | static struct filed *Files = NULL; /* Log files that we write to */ |
260 | static struct filed consfile; /* Console */ | |
261 | ||
262 | static int Debug = 0; /* debug flag */ | |
263 | static int resolve = 1; /* resolve hostname */ | |
264 | static char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */ | |
265 | static char *LocalDomain = NULL; /* our local domain name */ | |
266 | static int *finet = NULL; /* Internet datagram socket */ | |
267 | static int fklog = -1; /* /dev/klog */ | |
268 | static int Initialized = 0; /* set when we have initialized ourselves */ | |
269 | static int MarkInterval = 20 * 60; /* interval between marks in seconds */ | |
270 | static int MarkSeq = 0; /* mark sequence number */ | |
271 | #ifdef __APPLE__ | |
272 | static int RcvSockBufSize = 49152; /* Our default receive socket buffer size 3301629*/ | |
273 | #endif | |
274 | static int SecureMode = 0; /* when true, receive only unix domain socks */ | |
275 | #ifdef INET6 | |
276 | static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ | |
277 | #else | |
278 | static int family = PF_INET; /* protocol family (IPv4 only) */ | |
279 | #endif | |
280 | static int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ | |
281 | static int use_bootfile = 0; /* log entire bootfile for every kern msg */ | |
282 | static int no_compress = 0; /* don't compress messages (1=pipes, 2=all) */ | |
283 | ||
284 | static char bootfile[MAXLINE+1]; /* booted kernel file */ | |
285 | ||
286 | struct allowedpeer *AllowedPeers = NULL; /* List of allowed peers */ | |
287 | static int NumAllowed = 0; /* Number of entries in AllowedPeers */ | |
288 | ||
289 | static int UniquePriority = 0; /* Only log specified priority? */ | |
290 | static int LogFacPri = 0; /* Put facility and priority in log message: */ | |
291 | /* 0=no, 1=numeric, 2=names */ | |
292 | static int KeepKernFac = 0; /* Keep remotely logged kernel facility */ | |
293 | static int create_files = 0; | |
294 | ||
295 | volatile sig_atomic_t MarkSet = 0, WantDie = 0; | |
296 | ||
297 | static int allowaddr(char *); | |
298 | static void cfline(const char *, struct filed *, | |
299 | const char *, const char *); | |
300 | static const char *cvthname(struct sockaddr *); | |
301 | static void deadq_enter(pid_t, const char *); | |
302 | static int deadq_remove(pid_t); | |
303 | static int decode(const char *, CODE *); | |
304 | static void die(int); | |
305 | static void dodie(int); | |
306 | static void domark(int); | |
307 | static void fprintlog(struct filed *, int, const char *); | |
308 | static int *socksetup(int, const char *); | |
309 | static void init(int); | |
310 | static void logerror(const char *); | |
311 | static void logmsg(int, const char *, const char *, int); | |
312 | static void log_deadchild(pid_t, int, const char *); | |
313 | static void markit(void); | |
314 | static int skip_message(const char *, const char *); | |
315 | static void printline(const char *, char *); | |
316 | static void printsys(char *); | |
317 | static int p_open(const char *, pid_t *); | |
318 | static void readklog(void); | |
319 | static void reapchild(int); | |
320 | static void usage(void); | |
321 | static int validate(struct sockaddr *, const char *); | |
322 | static void unmapped(struct sockaddr *); | |
323 | static void wallmsg(struct filed *, struct iovec *); | |
324 | static int waitdaemon(int, int, int); | |
325 | static void timedout(int); | |
b7080c8e A |
326 | |
327 | int | |
ac2f15b3 | 328 | main(int argc, char *argv[]) |
b7080c8e | 329 | { |
ac2f15b3 | 330 | int ch, i, fdsrmax = 0, l; |
b7080c8e | 331 | struct sockaddr_un sunx, fromunix; |
ac2f15b3 A |
332 | struct sockaddr_storage frominet; |
333 | fd_set *fdsr = NULL; | |
b7080c8e | 334 | FILE *fp; |
ac2f15b3 A |
335 | char line[MAXLINE + 1]; |
336 | const char *bindhostname, *hname; | |
337 | struct timeval tv, *tvp; | |
338 | struct sigaction sact; | |
339 | sigset_t mask; | |
340 | pid_t ppid = 1; | |
341 | socklen_t len; | |
342 | ||
343 | bindhostname = NULL; | |
344 | while ((ch = getopt(argc, argv, "46ACa:b:cdf:kl:m:nop:P:suv")) != -1) | |
345 | switch (ch) { | |
346 | case '4': | |
347 | family = PF_INET; | |
348 | break; | |
349 | #ifdef INET6 | |
350 | case '6': | |
351 | family = PF_INET6; | |
352 | break; | |
353 | #endif | |
354 | case 'A': | |
355 | send_to_all++; | |
356 | break; | |
357 | case 'C': | |
358 | create_files++; | |
359 | break; | |
360 | case 'a': /* allow specific network addresses only */ | |
361 | if (allowaddr(optarg) == -1) | |
362 | usage(); | |
363 | break; | |
364 | case 'b': | |
365 | bindhostname = optarg; | |
366 | break; | |
367 | case 'c': | |
368 | no_compress++; | |
369 | break; | |
b7080c8e A |
370 | case 'd': /* debug */ |
371 | Debug++; | |
372 | break; | |
b7080c8e A |
373 | case 'f': /* configuration file */ |
374 | ConfFile = optarg; | |
375 | break; | |
ac2f15b3 A |
376 | case 'k': /* keep remote kern fac */ |
377 | KeepKernFac = 1; | |
378 | break; | |
379 | case 'l': | |
380 | if (nfunix < MAXFUNIX) | |
381 | funixn[nfunix++] = optarg; | |
382 | else | |
383 | warnx("out of descriptors, ignoring %s", | |
384 | optarg); | |
385 | break; | |
b7080c8e A |
386 | case 'm': /* mark interval */ |
387 | MarkInterval = atoi(optarg) * 60; | |
388 | break; | |
ac2f15b3 A |
389 | case 'n': |
390 | resolve = 0; | |
391 | break; | |
392 | case 'o': | |
393 | use_bootfile = 1; | |
394 | break; | |
b7080c8e | 395 | case 'p': /* path */ |
ac2f15b3 | 396 | funixn[0] = optarg; |
b7080c8e | 397 | break; |
ac2f15b3 A |
398 | case 'P': /* path for alt. PID */ |
399 | PidFile = optarg; | |
400 | break; | |
401 | case 's': /* no network mode */ | |
402 | SecureMode++; | |
7ba0088d | 403 | break; |
ac2f15b3 A |
404 | case 'u': /* only log specified priority */ |
405 | UniquePriority++; | |
406 | break; | |
407 | case 'v': /* log facility and priority */ | |
408 | LogFacPri++; | |
7ba0088d | 409 | break; |
b7080c8e A |
410 | case '?': |
411 | default: | |
412 | usage(); | |
413 | } | |
414 | if ((argc -= optind) != 0) | |
415 | usage(); | |
416 | ||
ac2f15b3 A |
417 | if (!Debug) { |
418 | ppid = waitdaemon(0, 0, 30); | |
419 | if (ppid < 0) | |
420 | err(1, "could not become daemon"); | |
421 | } else { | |
b7080c8e | 422 | setlinebuf(stdout); |
ac2f15b3 A |
423 | } |
424 | ||
425 | if (NumAllowed) | |
426 | endservent(); | |
b7080c8e A |
427 | |
428 | consfile.f_type = F_CONSOLE; | |
ac2f15b3 A |
429 | (void)strlcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1, |
430 | sizeof(consfile.f_un.f_fname)); | |
431 | #ifdef __APPLE__ | |
432 | /* We lack getbootfile() 3187949 and 3187947 */ | |
433 | (void)strlcpy(bootfile, "/mach_kernel", sizeof("/mach_kernel")); | |
434 | #else | |
435 | (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile)); | |
436 | #endif | |
437 | (void)signal(SIGTERM, dodie); | |
438 | (void)signal(SIGINT, Debug ? dodie : SIG_IGN); | |
439 | (void)signal(SIGQUIT, Debug ? dodie : SIG_IGN); | |
440 | /* | |
441 | * We don't want the SIGCHLD and SIGHUP handlers to interfere | |
442 | * with each other; they are likely candidates for being called | |
443 | * simultaneously (SIGHUP closes pipe descriptor, process dies, | |
444 | * SIGCHLD happens). | |
445 | */ | |
446 | sigemptyset(&mask); | |
447 | sigaddset(&mask, SIGHUP); | |
448 | sact.sa_handler = reapchild; | |
449 | sact.sa_mask = mask; | |
450 | sact.sa_flags = SA_RESTART; | |
451 | (void)sigaction(SIGCHLD, &sact, NULL); | |
b7080c8e | 452 | (void)signal(SIGALRM, domark); |
ac2f15b3 | 453 | (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */ |
b7080c8e | 454 | (void)alarm(TIMERINTVL); |
ac2f15b3 A |
455 | |
456 | TAILQ_INIT(&deadq_head); | |
b7080c8e A |
457 | |
458 | #ifndef SUN_LEN | |
459 | #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) | |
460 | #endif | |
ac2f15b3 A |
461 | for (i = 0; i < nfunix; i++) { |
462 | (void)unlink(funixn[i]); | |
463 | memset(&sunx, 0, sizeof(sunx)); | |
464 | sunx.sun_family = AF_UNIX; | |
465 | (void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path)); | |
466 | funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0); | |
467 | if (funix[i] < 0 || | |
468 | bind(funix[i], (struct sockaddr *)&sunx, | |
469 | SUN_LEN(&sunx)) < 0 || | |
470 | chmod(funixn[i], 0666) < 0) { | |
471 | (void)snprintf(line, sizeof line, | |
472 | "cannot create %s", funixn[i]); | |
473 | logerror(line); | |
474 | dprintf("cannot create %s (%d)\n", funixn[i], errno); | |
475 | if (i == 0) | |
b7080c8e | 476 | die(0); |
ac2f15b3 A |
477 | } |
478 | #ifdef __APPLE__ | |
479 | if (setsockopt(funix[i], SOL_SOCKET, SO_RCVBUF, &RcvSockBufSize, sizeof(int)) < 0) | |
480 | logerror("setsockopt funix"); | |
481 | #endif | |
482 | } | |
483 | if (SecureMode <= 1) | |
484 | finet = socksetup(family, bindhostname); | |
485 | ||
486 | if (finet) { | |
487 | if (SecureMode) { | |
488 | for (i = 0; i < *finet; i++) { | |
489 | if (shutdown(finet[i+1], SHUT_RD) < 0) { | |
490 | logerror("shutdown"); | |
491 | if (!Debug) | |
492 | die(0); | |
493 | } | |
494 | } | |
b7080c8e | 495 | } else { |
ac2f15b3 | 496 | dprintf("listening on inet and/or inet6 socket\n"); |
b7080c8e | 497 | } |
ac2f15b3 | 498 | dprintf("sending on inet and/or inet6 socket\n"); |
b7080c8e | 499 | } |
ac2f15b3 | 500 | |
b7080c8e | 501 | if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0) |
ac2f15b3 A |
502 | if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0) |
503 | fklog = -1; | |
504 | if (fklog < 0) | |
b7080c8e | 505 | dprintf("can't open %s (%d)\n", _PATH_KLOG, errno); |
b7080c8e A |
506 | |
507 | /* tuck my process id away */ | |
508 | fp = fopen(PidFile, "w"); | |
509 | if (fp != NULL) { | |
510 | fprintf(fp, "%d\n", getpid()); | |
ac2f15b3 | 511 | (void)fclose(fp); |
b7080c8e A |
512 | } |
513 | ||
514 | dprintf("off & running....\n"); | |
515 | ||
516 | init(0); | |
ac2f15b3 A |
517 | /* prevent SIGHUP and SIGCHLD handlers from running in parallel */ |
518 | sigemptyset(&mask); | |
519 | sigaddset(&mask, SIGCHLD); | |
520 | sact.sa_handler = init; | |
521 | sact.sa_mask = mask; | |
522 | sact.sa_flags = SA_RESTART; | |
523 | (void)sigaction(SIGHUP, &sact, NULL); | |
524 | ||
525 | tvp = &tv; | |
526 | tv.tv_sec = tv.tv_usec = 0; | |
527 | ||
528 | if (fklog != -1 && fklog > fdsrmax) | |
529 | fdsrmax = fklog; | |
530 | if (finet && !SecureMode) { | |
531 | for (i = 0; i < *finet; i++) { | |
532 | if (finet[i+1] != -1 && finet[i+1] > fdsrmax) | |
533 | fdsrmax = finet[i+1]; | |
534 | } | |
535 | } | |
536 | for (i = 0; i < nfunix; i++) { | |
537 | if (funix[i] != -1 && funix[i] > fdsrmax) | |
538 | fdsrmax = funix[i]; | |
539 | } | |
540 | ||
541 | fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS), | |
542 | sizeof(fd_mask)); | |
543 | if (fdsr == NULL) | |
544 | errx(1, "calloc fd_set"); | |
b7080c8e A |
545 | |
546 | for (;;) { | |
ac2f15b3 A |
547 | if (MarkSet) |
548 | markit(); | |
549 | if (WantDie) | |
550 | die(WantDie); | |
551 | ||
552 | bzero(fdsr, howmany(fdsrmax+1, NFDBITS) * | |
553 | sizeof(fd_mask)); | |
554 | ||
555 | if (fklog != -1) | |
556 | FD_SET(fklog, fdsr); | |
557 | if (finet && !SecureMode) { | |
558 | for (i = 0; i < *finet; i++) { | |
559 | if (finet[i+1] != -1) | |
560 | FD_SET(finet[i+1], fdsr); | |
561 | } | |
562 | } | |
563 | for (i = 0; i < nfunix; i++) { | |
564 | if (funix[i] != -1) | |
565 | FD_SET(funix[i], fdsr); | |
566 | } | |
b7080c8e | 567 | |
ac2f15b3 A |
568 | i = select(fdsrmax+1, fdsr, NULL, NULL, tvp); |
569 | switch (i) { | |
570 | case 0: | |
571 | if (tvp) { | |
572 | tvp = NULL; | |
573 | if (ppid != 1) | |
574 | kill(ppid, SIGALRM); | |
575 | } | |
b7080c8e | 576 | continue; |
ac2f15b3 | 577 | case -1: |
b7080c8e A |
578 | if (errno != EINTR) |
579 | logerror("select"); | |
580 | continue; | |
581 | } | |
ac2f15b3 A |
582 | if (fklog != -1 && FD_ISSET(fklog, fdsr)) |
583 | readklog(); | |
584 | if (finet && !SecureMode) { | |
585 | for (i = 0; i < *finet; i++) { | |
586 | if (FD_ISSET(finet[i+1], fdsr)) { | |
587 | len = sizeof(frominet); | |
588 | l = recvfrom(finet[i+1], line, MAXLINE, | |
589 | 0, (struct sockaddr *)&frominet, | |
590 | &len); | |
591 | if (l > 0) { | |
592 | line[l] = '\0'; | |
593 | hname = cvthname((struct sockaddr *)&frominet); | |
594 | unmapped((struct sockaddr *)&frominet); | |
595 | if (validate((struct sockaddr *)&frominet, hname)) | |
596 | printline(hname, line); | |
597 | } else if (l < 0 && errno != EINTR) | |
598 | logerror("recvfrom inet"); | |
599 | } | |
b7080c8e A |
600 | } |
601 | } | |
ac2f15b3 A |
602 | for (i = 0; i < nfunix; i++) { |
603 | if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) { | |
604 | len = sizeof(fromunix); | |
605 | l = recvfrom(funix[i], line, MAXLINE, 0, | |
606 | (struct sockaddr *)&fromunix, &len); | |
607 | if (l > 0) { | |
608 | line[l] = '\0'; | |
609 | printline(LocalHostName, line); | |
610 | } else if (l < 0 && errno != EINTR) | |
611 | logerror("recvfrom unix"); | |
b7080c8e | 612 | } |
ac2f15b3 | 613 | } |
b7080c8e | 614 | } |
ac2f15b3 A |
615 | if (fdsr) |
616 | free(fdsr); | |
617 | } | |
618 | ||
619 | static void | |
620 | unmapped(struct sockaddr *sa) | |
621 | { | |
622 | struct sockaddr_in6 *sin6; | |
623 | struct sockaddr_in sin4; | |
624 | ||
625 | if (sa->sa_family != AF_INET6) | |
626 | return; | |
627 | if (sa->sa_len != sizeof(struct sockaddr_in6) || | |
628 | sizeof(sin4) > sa->sa_len) | |
629 | return; | |
630 | sin6 = (struct sockaddr_in6 *)sa; | |
631 | if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | |
632 | return; | |
633 | ||
634 | memset(&sin4, 0, sizeof(sin4)); | |
635 | sin4.sin_family = AF_INET; | |
636 | sin4.sin_len = sizeof(struct sockaddr_in); | |
637 | memcpy(&sin4.sin_addr, &sin6->sin6_addr.s6_addr[12], | |
638 | sizeof(sin4.sin_addr)); | |
639 | sin4.sin_port = sin6->sin6_port; | |
640 | ||
641 | memcpy(sa, &sin4, sin4.sin_len); | |
b7080c8e A |
642 | } |
643 | ||
ac2f15b3 A |
644 | static void |
645 | usage(void) | |
b7080c8e A |
646 | { |
647 | ||
ac2f15b3 A |
648 | fprintf(stderr, "%s\n%s\n%s\n%s\n", |
649 | "usage: syslogd [-46Acdknosuv] [-a allowed_peer]", | |
650 | " [-b bind address] [-f config_file]", | |
651 | " [-l log_socket] [-m mark_interval]", | |
652 | " [-P pid_file] [-p log_socket]"); | |
b7080c8e A |
653 | exit(1); |
654 | } | |
655 | ||
656 | /* | |
657 | * Take a raw input line, decode the message, and print the message | |
658 | * on the appropriate log files. | |
659 | */ | |
ac2f15b3 A |
660 | static void |
661 | printline(const char *hname, char *msg) | |
b7080c8e A |
662 | { |
663 | int c, pri; | |
664 | char *p, *q, line[MAXLINE + 1]; | |
665 | ||
666 | /* test for special codes */ | |
667 | pri = DEFUPRI; | |
668 | p = msg; | |
669 | if (*p == '<') { | |
670 | pri = 0; | |
671 | while (isdigit(*++p)) | |
672 | pri = 10 * pri + (*p - '0'); | |
673 | if (*p == '>') | |
674 | ++p; | |
675 | } | |
676 | if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) | |
677 | pri = DEFUPRI; | |
678 | ||
679 | /* don't allow users to log kernel messages */ | |
ac2f15b3 | 680 | if (LOG_FAC(pri) == LOG_KERN && !KeepKernFac) |
b7080c8e A |
681 | pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); |
682 | ||
683 | q = line; | |
684 | ||
ac2f15b3 A |
685 | while ((c = (unsigned char)*p++) != '\0' && |
686 | q < &line[sizeof(line) - 4]) { | |
687 | #ifdef __APPLE__ | |
688 | /* Gross installer hack to be removed 3314128 */ | |
689 | if (LOG_FACMASK&pri != LOG_INSTALL) { | |
690 | if ((c & 0x80) && c < 0xA0) { | |
691 | c &= 0x7F; | |
692 | *q++ = 'M'; | |
693 | *q++ = '-'; | |
694 | } | |
695 | } | |
696 | #endif | |
697 | if (isascii(c) && iscntrl(c)) { | |
698 | if (c == '\n') { | |
b7080c8e | 699 | *q++ = ' '; |
ac2f15b3 | 700 | } else if (c == '\t') { |
b7080c8e | 701 | *q++ = '\t'; |
ac2f15b3 | 702 | } else { |
b7080c8e A |
703 | *q++ = '^'; |
704 | *q++ = c ^ 0100; | |
705 | } | |
ac2f15b3 | 706 | } else { |
b7080c8e | 707 | *q++ = c; |
ac2f15b3 | 708 | } |
b7080c8e A |
709 | } |
710 | *q = '\0'; | |
711 | ||
712 | logmsg(pri, line, hname, 0); | |
713 | } | |
714 | ||
715 | /* | |
ac2f15b3 | 716 | * Read /dev/klog while data are available, split into lines. |
b7080c8e | 717 | */ |
ac2f15b3 A |
718 | static void |
719 | readklog(void) | |
b7080c8e | 720 | { |
ac2f15b3 A |
721 | char *p, *q, line[MAXLINE + 1]; |
722 | int len, i; | |
b7080c8e | 723 | |
ac2f15b3 A |
724 | len = 0; |
725 | for (;;) { | |
726 | i = read(fklog, line + len, MAXLINE - 1 - len); | |
727 | if (i > 0) { | |
728 | line[i + len] = '\0'; | |
b7080c8e | 729 | } else { |
ac2f15b3 A |
730 | if (i < 0 && errno != EINTR && errno != EAGAIN) { |
731 | logerror("klog"); | |
732 | fklog = -1; | |
733 | } | |
734 | break; | |
b7080c8e | 735 | } |
ac2f15b3 A |
736 | |
737 | for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) { | |
738 | *q = '\0'; | |
739 | printsys(p); | |
740 | } | |
741 | len = strlen(p); | |
742 | if (len >= MAXLINE - 1) { | |
743 | printsys(p); | |
744 | len = 0; | |
745 | } | |
746 | if (len > 0) | |
747 | memmove(line, p, len + 1); | |
748 | } | |
749 | if (len > 0) | |
750 | printsys(line); | |
751 | } | |
752 | ||
753 | /* | |
754 | * Take a raw input line from /dev/klog, format similar to syslog(). | |
755 | */ | |
756 | static void | |
757 | printsys(char *p) | |
758 | { | |
759 | int pri, flags; | |
760 | ||
761 | flags = ISKERNEL | SYNC_FILE | ADDDATE; /* fsync after write */ | |
762 | pri = DEFSPRI; | |
763 | if (*p == '<') { | |
764 | pri = 0; | |
765 | while (isdigit(*++p)) | |
766 | pri = 10 * pri + (*p - '0'); | |
767 | if (*p == '>') | |
768 | ++p; | |
769 | #ifndef __APPLE__ | |
770 | if ((pri & LOG_FACMASK) == LOG_CONSOLE) | |
771 | flags |= IGN_CONS; | |
772 | #endif | |
773 | } else { | |
774 | /* kernel printf's come out on console */ | |
775 | flags |= IGN_CONS; | |
b7080c8e | 776 | } |
ac2f15b3 A |
777 | if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) |
778 | pri = DEFSPRI; | |
779 | logmsg(pri, p, LocalHostName, flags); | |
b7080c8e A |
780 | } |
781 | ||
ac2f15b3 A |
782 | static time_t now; |
783 | ||
784 | /* | |
785 | * Match a program or host name against a specification. | |
786 | * Return a non-0 value if the message must be ignored | |
787 | * based on the specification. | |
788 | */ | |
789 | static int | |
790 | skip_message(const char *name, const char *spec) { | |
791 | const char *s; | |
792 | char prev, next; | |
793 | int exclude = 0; | |
794 | /* Behaviour on explicit match */ | |
795 | ||
796 | if (spec == NULL) | |
797 | return 0; | |
798 | switch (*spec) { | |
799 | case '-': | |
800 | exclude = 1; | |
801 | /*FALLTHROUGH*/ | |
802 | case '+': | |
803 | spec++; | |
804 | break; | |
805 | default: | |
806 | break; | |
807 | } | |
808 | s = strstr (spec, name); | |
809 | ||
810 | if (s != NULL) { | |
811 | prev = (s == spec ? ',' : *(s - 1)); | |
812 | next = *(s + strlen (name)); | |
813 | ||
814 | if (prev == ',' && (next == '\0' || next == ',')) | |
815 | /* Explicit match: skip iff the spec is an | |
816 | exclusive one. */ | |
817 | return exclude; | |
818 | } | |
819 | ||
820 | /* No explicit match for this name: skip the message iff | |
821 | the spec is an inclusive one. */ | |
822 | return !exclude; | |
823 | } | |
b7080c8e A |
824 | |
825 | /* | |
826 | * Log a message to the appropriate log files, users, etc. based on | |
827 | * the priority. | |
828 | */ | |
ac2f15b3 A |
829 | static void |
830 | logmsg(int pri, const char *msg, const char *from, int flags) | |
b7080c8e A |
831 | { |
832 | struct filed *f; | |
ac2f15b3 A |
833 | int i, fac, msglen, omask, prilev; |
834 | const char *timestamp; | |
835 | char prog[NAME_MAX+1]; | |
836 | char buf[MAXLINE+1]; | |
b7080c8e A |
837 | |
838 | dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n", | |
839 | pri, flags, from, msg); | |
840 | ||
841 | omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM)); | |
842 | ||
843 | /* | |
844 | * Check to see if msg looks non-standard. | |
845 | */ | |
846 | msglen = strlen(msg); | |
847 | if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' || | |
848 | msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') | |
849 | flags |= ADDDATE; | |
850 | ||
851 | (void)time(&now); | |
ac2f15b3 | 852 | if (flags & ADDDATE) { |
b7080c8e | 853 | timestamp = ctime(&now) + 4; |
ac2f15b3 | 854 | } else { |
b7080c8e A |
855 | timestamp = msg; |
856 | msg += 16; | |
857 | msglen -= 16; | |
858 | } | |
859 | ||
ac2f15b3 A |
860 | /* skip leading blanks */ |
861 | while (isspace(*msg)) { | |
862 | msg++; | |
863 | msglen--; | |
864 | } | |
865 | ||
b7080c8e A |
866 | /* extract facility and priority level */ |
867 | if (flags & MARK) | |
868 | fac = LOG_NFACILITIES; | |
869 | else | |
870 | fac = LOG_FAC(pri); | |
871 | prilev = LOG_PRI(pri); | |
872 | ||
ac2f15b3 A |
873 | /* extract program name */ |
874 | for (i = 0; i < NAME_MAX; i++) { | |
875 | if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[') | |
876 | break; | |
877 | prog[i] = msg[i]; | |
878 | } | |
879 | prog[i] = 0; | |
880 | ||
881 | /* add kernel prefix for kernel messages */ | |
882 | if (flags & ISKERNEL) { | |
883 | snprintf(buf, sizeof(buf), "%s: %s", | |
884 | use_bootfile ? bootfile : "kernel", msg); | |
885 | msg = buf; | |
886 | msglen = strlen(buf); | |
887 | } | |
888 | ||
b7080c8e A |
889 | /* log the message to the particular outputs */ |
890 | if (!Initialized) { | |
891 | f = &consfile; | |
892 | f->f_file = open(ctty, O_WRONLY, 0); | |
893 | ||
894 | if (f->f_file >= 0) { | |
895 | fprintlog(f, flags, msg); | |
896 | (void)close(f->f_file); | |
897 | } | |
898 | (void)sigsetmask(omask); | |
899 | return; | |
900 | } | |
901 | for (f = Files; f; f = f->f_next) { | |
902 | /* skip messages that are incorrect priority */ | |
ac2f15b3 A |
903 | if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev)) |
904 | ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev)) | |
905 | ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev)) | |
906 | ) | |
907 | || f->f_pmask[fac] == INTERNAL_NOPRI) | |
908 | continue; | |
909 | ||
910 | /* skip messages with the incorrect hostname */ | |
911 | if (skip_message(from, f->f_host)) | |
912 | continue; | |
913 | ||
914 | /* skip messages with the incorrect program name */ | |
915 | if (skip_message(prog, f->f_program)) | |
b7080c8e A |
916 | continue; |
917 | ||
ac2f15b3 | 918 | /* skip message to console if it has already been printed */ |
b7080c8e A |
919 | if (f->f_type == F_CONSOLE && (flags & IGN_CONS)) |
920 | continue; | |
921 | ||
922 | /* don't output marks to recently written files */ | |
923 | if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) | |
924 | continue; | |
925 | ||
926 | /* | |
927 | * suppress duplicate lines to this file | |
928 | */ | |
ac2f15b3 A |
929 | if (no_compress - (f->f_type != F_PIPE) < 1 && |
930 | (flags & MARK) == 0 && msglen == f->f_prevlen && | |
b7080c8e | 931 | !strcmp(msg, f->f_prevline) && |
ac2f15b3 A |
932 | !strcasecmp(from, f->f_prevhost)) { |
933 | (void)strlcpy(f->f_lasttime, timestamp, 16); | |
b7080c8e A |
934 | f->f_prevcount++; |
935 | dprintf("msg repeated %d times, %ld sec of %d\n", | |
ac2f15b3 | 936 | f->f_prevcount, (long)(now - f->f_time), |
b7080c8e A |
937 | repeatinterval[f->f_repeatcount]); |
938 | /* | |
939 | * If domark would have logged this by now, | |
940 | * flush it now (so we don't hold isolated messages), | |
941 | * but back off so we'll flush less often | |
942 | * in the future. | |
943 | */ | |
944 | if (now > REPEATTIME(f)) { | |
945 | fprintlog(f, flags, (char *)NULL); | |
946 | BACKOFF(f); | |
947 | } | |
948 | } else { | |
949 | /* new line, save it */ | |
950 | if (f->f_prevcount) | |
951 | fprintlog(f, 0, (char *)NULL); | |
952 | f->f_repeatcount = 0; | |
ac2f15b3 A |
953 | f->f_prevpri = pri; |
954 | (void)strlcpy(f->f_lasttime, timestamp, 16); | |
955 | (void)strlcpy(f->f_prevhost, from, | |
956 | sizeof(f->f_prevhost)); | |
b7080c8e A |
957 | if (msglen < MAXSVLINE) { |
958 | f->f_prevlen = msglen; | |
ac2f15b3 | 959 | (void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline)); |
b7080c8e A |
960 | fprintlog(f, flags, (char *)NULL); |
961 | } else { | |
962 | f->f_prevline[0] = 0; | |
963 | f->f_prevlen = 0; | |
964 | fprintlog(f, flags, msg); | |
965 | } | |
966 | } | |
967 | } | |
968 | (void)sigsetmask(omask); | |
969 | } | |
970 | ||
ac2f15b3 A |
971 | static void |
972 | fprintlog(struct filed *f, int flags, const char *msg) | |
b7080c8e | 973 | { |
ac2f15b3 | 974 | struct iovec iov[7]; |
b7080c8e | 975 | struct iovec *v; |
ac2f15b3 A |
976 | struct addrinfo *r; |
977 | int i, l, lsent = 0; | |
978 | char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL; | |
979 | const char *msgret; | |
b7080c8e A |
980 | |
981 | v = iov; | |
982 | if (f->f_type == F_WALL) { | |
983 | v->iov_base = greetings; | |
ac2f15b3 | 984 | v->iov_len = snprintf(greetings, sizeof greetings, |
b7080c8e A |
985 | "\r\n\7Message from syslogd@%s at %.24s ...\r\n", |
986 | f->f_prevhost, ctime(&now)); | |
ac2f15b3 A |
987 | if (v->iov_len > 0) |
988 | v++; | |
b7080c8e A |
989 | v->iov_base = ""; |
990 | v->iov_len = 0; | |
991 | v++; | |
992 | } else { | |
993 | v->iov_base = f->f_lasttime; | |
994 | v->iov_len = 15; | |
995 | v++; | |
996 | v->iov_base = " "; | |
997 | v->iov_len = 1; | |
998 | v++; | |
999 | } | |
ac2f15b3 A |
1000 | |
1001 | if (LogFacPri) { | |
1002 | static char fp_buf[30]; /* Hollow laugh */ | |
1003 | int fac = f->f_prevpri & LOG_FACMASK; | |
1004 | int pri = LOG_PRI(f->f_prevpri); | |
1005 | const char *f_s = NULL; | |
1006 | char f_n[5]; /* Hollow laugh */ | |
1007 | const char *p_s = NULL; | |
1008 | char p_n[5]; /* Hollow laugh */ | |
1009 | ||
1010 | if (LogFacPri > 1) { | |
1011 | CODE *c; | |
1012 | ||
1013 | for (c = facilitynames; c->c_name; c++) { | |
1014 | if (c->c_val == fac) { | |
1015 | f_s = c->c_name; | |
1016 | break; | |
1017 | } | |
1018 | } | |
1019 | for (c = prioritynames; c->c_name; c++) { | |
1020 | if (c->c_val == pri) { | |
1021 | p_s = c->c_name; | |
1022 | break; | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | if (!f_s) { | |
1027 | snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac)); | |
1028 | f_s = f_n; | |
1029 | } | |
1030 | if (!p_s) { | |
1031 | snprintf(p_n, sizeof p_n, "%d", pri); | |
1032 | p_s = p_n; | |
1033 | } | |
1034 | snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s); | |
1035 | v->iov_base = fp_buf; | |
1036 | v->iov_len = strlen(fp_buf); | |
1037 | } else { | |
1038 | v->iov_base=""; | |
1039 | v->iov_len = 0; | |
1040 | } | |
1041 | v++; | |
1042 | ||
b7080c8e A |
1043 | v->iov_base = f->f_prevhost; |
1044 | v->iov_len = strlen(v->iov_base); | |
1045 | v++; | |
1046 | v->iov_base = " "; | |
1047 | v->iov_len = 1; | |
1048 | v++; | |
1049 | ||
1050 | if (msg) { | |
ac2f15b3 A |
1051 | wmsg = strdup(msg); /* XXX iov_base needs a `const' sibling. */ |
1052 | if (wmsg == NULL) { | |
1053 | logerror("strdup"); | |
1054 | exit(1); | |
1055 | } | |
1056 | v->iov_base = wmsg; | |
b7080c8e A |
1057 | v->iov_len = strlen(msg); |
1058 | } else if (f->f_prevcount > 1) { | |
1059 | v->iov_base = repbuf; | |
ac2f15b3 A |
1060 | v->iov_len = snprintf(repbuf, sizeof repbuf, |
1061 | "last message repeated %d times", f->f_prevcount); | |
b7080c8e A |
1062 | } else { |
1063 | v->iov_base = f->f_prevline; | |
1064 | v->iov_len = f->f_prevlen; | |
1065 | } | |
1066 | v++; | |
1067 | ||
ac2f15b3 A |
1068 | if (f->f_file == -1) { |
1069 | int oflags = O_WRONLY|O_APPEND; | |
1070 | struct group *gr; | |
1071 | struct stat sb; | |
1072 | int mode = 0640; | |
1073 | int exists = 0; | |
1074 | ||
1075 | if( stat(f->f_un.f_fname, &sb) == 0 ) { | |
1076 | mode = 0; | |
1077 | exists++; | |
1078 | } | |
1079 | if (create_files && !exists) | |
1080 | oflags |= O_CREAT; | |
1081 | if ((f->f_file = open(f->f_un.f_fname, oflags, mode)) < 0) { | |
1082 | f->f_type = F_UNUSED; | |
1083 | /* We can no longer log this error, since calling | |
1084 | * logerror() could bring us back here again. | |
1085 | * Instead, call dprintf(), which will aid in | |
1086 | * debugging, but not cause the looping. | |
1087 | */ | |
1088 | dprintf("Error openning %s", f->f_un.f_fname); | |
1089 | if (msg) free(wmsg); | |
1090 | return; | |
1091 | } | |
1092 | /* Only chown the file if we created it. If it already | |
1093 | * existed, leave whatever was there. | |
1094 | */ | |
1095 | if( !exists ) { | |
1096 | gr = getgrnam("admin"); | |
1097 | if( gr ) | |
1098 | fchown(f->f_file, 0, gr->gr_gid); | |
1099 | if (f->f_type == F_CHECKTTY) { | |
1100 | if (isatty(f->f_file)) { | |
1101 | if (strcmp(f->f_un.f_fname, ctty) == 0) | |
1102 | f->f_type = F_CONSOLE; | |
1103 | else | |
1104 | f->f_type = F_TTY; | |
1105 | } | |
1106 | } | |
1107 | } | |
1108 | } | |
1109 | ||
b7080c8e A |
1110 | dprintf("Logging to %s", TypeNames[f->f_type]); |
1111 | f->f_time = now; | |
1112 | ||
1113 | switch (f->f_type) { | |
1114 | case F_UNUSED: | |
1115 | dprintf("\n"); | |
1116 | break; | |
1117 | ||
1118 | case F_FORW: | |
1119 | dprintf(" %s\n", f->f_un.f_forw.f_hname); | |
ac2f15b3 A |
1120 | /* check for local vs remote messages */ |
1121 | if (strcasecmp(f->f_prevhost, LocalHostName)) | |
1122 | l = snprintf(line, sizeof line - 1, | |
1123 | "<%d>%.15s Forwarded from %s: %s", | |
1124 | f->f_prevpri, iov[0].iov_base, f->f_prevhost, | |
1125 | iov[5].iov_base); | |
1126 | else | |
1127 | l = snprintf(line, sizeof line - 1, "<%d>%.15s %s", | |
1128 | f->f_prevpri, iov[0].iov_base, iov[5].iov_base); | |
1129 | if (l < 0) | |
1130 | l = 0; | |
1131 | else if (l > MAXLINE) | |
b7080c8e | 1132 | l = MAXLINE; |
ac2f15b3 A |
1133 | |
1134 | if (finet) { | |
1135 | for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) { | |
1136 | for (i = 0; i < *finet; i++) { | |
1137 | #if 0 | |
1138 | /* | |
1139 | * should we check AF first, or just | |
1140 | * trial and error? FWD | |
1141 | */ | |
1142 | if (r->ai_family == | |
1143 | address_family_of(finet[i+1])) | |
1144 | #endif | |
1145 | lsent = sendto(finet[i+1], line, l, 0, | |
1146 | r->ai_addr, r->ai_addrlen); | |
1147 | if (lsent == l) | |
1148 | break; | |
1149 | } | |
1150 | if (lsent == l && !send_to_all) | |
1151 | break; | |
1152 | } | |
1153 | dprintf("lsent/l: %d/%d\n", lsent, l); | |
1154 | if (lsent != l) { | |
1155 | int e = errno; | |
1156 | logerror("sendto"); | |
1157 | errno = e; | |
1158 | switch (errno) { | |
1159 | case EHOSTUNREACH: | |
1160 | case EHOSTDOWN: | |
1161 | break; | |
1162 | /* case EBADF: */ | |
1163 | /* case EACCES: */ | |
1164 | /* case ENOTSOCK: */ | |
1165 | /* case EFAULT: */ | |
1166 | /* case EMSGSIZE: */ | |
1167 | /* case EAGAIN: */ | |
1168 | /* case ENOBUFS: */ | |
1169 | /* case ECONNREFUSED: */ | |
1170 | default: | |
1171 | dprintf("removing entry\n", e); | |
1172 | (void)close(f->f_file); | |
1173 | f->f_type = F_UNUSED; | |
1174 | break; | |
1175 | } | |
1176 | } | |
1177 | } | |
1178 | break; | |
1179 | ||
1180 | case F_FILE: | |
1181 | dprintf(" %s\n", f->f_un.f_fname); | |
1182 | v->iov_base = "\n"; | |
1183 | v->iov_len = 1; | |
1184 | if (writev(f->f_file, iov, 7) < 0) { | |
b7080c8e A |
1185 | int e = errno; |
1186 | (void)close(f->f_file); | |
1187 | f->f_type = F_UNUSED; | |
1188 | errno = e; | |
ac2f15b3 A |
1189 | logerror(f->f_un.f_fname); |
1190 | } else if (flags & SYNC_FILE) | |
1191 | (void)fsync(f->f_file); | |
1192 | break; | |
1193 | ||
1194 | case F_PIPE: | |
1195 | dprintf(" %s\n", f->f_un.f_pipe.f_pname); | |
1196 | v->iov_base = "\n"; | |
1197 | v->iov_len = 1; | |
1198 | if (f->f_un.f_pipe.f_pid == 0) { | |
1199 | if ((f->f_file = p_open(f->f_un.f_pipe.f_pname, | |
1200 | &f->f_un.f_pipe.f_pid)) < 0) { | |
1201 | f->f_type = F_UNUSED; | |
1202 | logerror(f->f_un.f_pipe.f_pname); | |
1203 | break; | |
1204 | } | |
1205 | } | |
1206 | if (writev(f->f_file, iov, 7) < 0) { | |
1207 | int e = errno; | |
1208 | (void)close(f->f_file); | |
1209 | if (f->f_un.f_pipe.f_pid > 0) | |
1210 | deadq_enter(f->f_un.f_pipe.f_pid, | |
1211 | f->f_un.f_pipe.f_pname); | |
1212 | f->f_un.f_pipe.f_pid = 0; | |
1213 | errno = e; | |
1214 | logerror(f->f_un.f_pipe.f_pname); | |
b7080c8e A |
1215 | } |
1216 | break; | |
1217 | ||
1218 | case F_CONSOLE: | |
1219 | if (flags & IGN_CONS) { | |
1220 | dprintf(" (ignored)\n"); | |
1221 | break; | |
1222 | } | |
1223 | /* FALLTHROUGH */ | |
1224 | ||
1225 | case F_TTY: | |
ac2f15b3 A |
1226 | dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname); |
1227 | v->iov_base = "\r\n"; | |
1228 | v->iov_len = 2; | |
1229 | ||
1230 | errno = 0; /* ttymsg() only sometimes returns an errno */ | |
1231 | if ((msgret = ttymsg(iov, 7, f->f_un.f_fname, 10))) { | |
1232 | f->f_type = F_UNUSED; | |
1233 | logerror(msgret); | |
b7080c8e | 1234 | } |
b7080c8e A |
1235 | break; |
1236 | ||
1237 | case F_USERS: | |
1238 | case F_WALL: | |
1239 | dprintf("\n"); | |
1240 | v->iov_base = "\r\n"; | |
1241 | v->iov_len = 2; | |
1242 | wallmsg(f, iov); | |
1243 | break; | |
1244 | } | |
1245 | f->f_prevcount = 0; | |
ac2f15b3 A |
1246 | if (msg) |
1247 | free(wmsg); | |
b7080c8e A |
1248 | } |
1249 | ||
1250 | /* | |
1251 | * WALLMSG -- Write a message to the world at large | |
1252 | * | |
1253 | * Write the specified message to either the entire | |
1254 | * world, or a list of approved users. | |
1255 | */ | |
ac2f15b3 A |
1256 | static void |
1257 | wallmsg(struct filed *f, struct iovec *iov) | |
b7080c8e A |
1258 | { |
1259 | static int reenter; /* avoid calling ourselves */ | |
1260 | FILE *uf; | |
1261 | struct utmp ut; | |
1262 | int i; | |
ac2f15b3 | 1263 | const char *p; |
b7080c8e A |
1264 | char line[sizeof(ut.ut_line) + 1]; |
1265 | ||
1266 | if (reenter++) | |
1267 | return; | |
1268 | if ((uf = fopen(_PATH_UTMP, "r")) == NULL) { | |
1269 | logerror(_PATH_UTMP); | |
1270 | reenter = 0; | |
1271 | return; | |
1272 | } | |
1273 | /* NOSTRICT */ | |
1274 | while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) { | |
1275 | if (ut.ut_name[0] == '\0') | |
1276 | continue; | |
ac2f15b3 | 1277 | (void)strlcpy(line, ut.ut_line, sizeof(line)); |
b7080c8e | 1278 | if (f->f_type == F_WALL) { |
ac2f15b3 | 1279 | if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) != NULL) { |
b7080c8e A |
1280 | errno = 0; /* already in msg */ |
1281 | logerror(p); | |
1282 | } | |
1283 | continue; | |
1284 | } | |
1285 | /* should we send the message to this user? */ | |
1286 | for (i = 0; i < MAXUNAMES; i++) { | |
1287 | if (!f->f_un.f_uname[i][0]) | |
1288 | break; | |
1289 | if (!strncmp(f->f_un.f_uname[i], ut.ut_name, | |
1290 | UT_NAMESIZE)) { | |
ac2f15b3 A |
1291 | if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) |
1292 | != NULL) { | |
b7080c8e A |
1293 | errno = 0; /* already in msg */ |
1294 | logerror(p); | |
1295 | } | |
1296 | break; | |
1297 | } | |
1298 | } | |
1299 | } | |
1300 | (void)fclose(uf); | |
1301 | reenter = 0; | |
1302 | } | |
1303 | ||
ac2f15b3 A |
1304 | static void |
1305 | reapchild(int signo ) | |
b7080c8e | 1306 | { |
ac2f15b3 A |
1307 | int status; |
1308 | pid_t pid; | |
1309 | struct filed *f; | |
b7080c8e | 1310 | |
ac2f15b3 A |
1311 | while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) { |
1312 | if (!Initialized) | |
1313 | /* Don't tell while we are initting. */ | |
1314 | continue; | |
1315 | ||
1316 | /* First, look if it's a process from the dead queue. */ | |
1317 | if (deadq_remove(pid)) | |
1318 | goto oncemore; | |
1319 | ||
1320 | /* Now, look in list of active processes. */ | |
1321 | for (f = Files; f; f = f->f_next) | |
1322 | if (f->f_type == F_PIPE && | |
1323 | f->f_un.f_pipe.f_pid == pid) { | |
1324 | (void)close(f->f_file); | |
1325 | f->f_un.f_pipe.f_pid = 0; | |
1326 | log_deadchild(pid, status, | |
1327 | f->f_un.f_pipe.f_pname); | |
1328 | break; | |
1329 | } | |
1330 | oncemore: | |
1331 | continue; | |
1332 | } | |
b7080c8e A |
1333 | } |
1334 | ||
1335 | /* | |
1336 | * Return a printable representation of a host address. | |
1337 | */ | |
ac2f15b3 A |
1338 | static const char * |
1339 | cvthname(struct sockaddr *f) | |
b7080c8e | 1340 | { |
ac2f15b3 A |
1341 | int error; |
1342 | sigset_t omask, nmask; | |
b7080c8e | 1343 | char *p; |
ac2f15b3 | 1344 | static char hname[NI_MAXHOST], ip[NI_MAXHOST]; |
b7080c8e | 1345 | |
ac2f15b3 A |
1346 | error = getnameinfo((struct sockaddr *)f, |
1347 | ((struct sockaddr *)f)->sa_len, | |
1348 | ip, sizeof ip, NULL, 0, | |
1349 | NI_NUMERICHOST | withscopeid); | |
1350 | dprintf("cvthname(%s)\n", ip); | |
b7080c8e | 1351 | |
ac2f15b3 A |
1352 | if (error) { |
1353 | dprintf("Malformed from address %s\n", gai_strerror(error)); | |
b7080c8e A |
1354 | return ("???"); |
1355 | } | |
ac2f15b3 A |
1356 | if (!resolve) |
1357 | return (ip); | |
1358 | ||
1359 | sigemptyset(&nmask); | |
1360 | sigaddset(&nmask, SIGHUP); | |
1361 | sigprocmask(SIG_BLOCK, &nmask, &omask); | |
1362 | error = getnameinfo((struct sockaddr *)f, | |
1363 | ((struct sockaddr *)f)->sa_len, | |
1364 | hname, sizeof hname, NULL, 0, | |
1365 | NI_NAMEREQD | withscopeid); | |
1366 | sigprocmask(SIG_SETMASK, &omask, NULL); | |
1367 | if (error) { | |
1368 | dprintf("Host name for your address (%s) unknown\n", ip); | |
1369 | return (ip); | |
1370 | } | |
1371 | /* XXX Not quite correct, but close enough for government work. */ | |
1372 | if ((p = strchr(hname, '.')) && strcasecmp(p + 1, LocalDomain) == 0) | |
b7080c8e | 1373 | *p = '\0'; |
ac2f15b3 | 1374 | return (hname); |
b7080c8e A |
1375 | } |
1376 | ||
ac2f15b3 A |
1377 | static void |
1378 | dodie(int signo) | |
b7080c8e | 1379 | { |
b7080c8e | 1380 | |
ac2f15b3 A |
1381 | WantDie = signo; |
1382 | } | |
b7080c8e | 1383 | |
ac2f15b3 A |
1384 | static void |
1385 | domark(int signo ) | |
1386 | { | |
1387 | ||
1388 | MarkSet = 1; | |
b7080c8e A |
1389 | } |
1390 | ||
1391 | /* | |
1392 | * Print syslogd errors some place. | |
1393 | */ | |
ac2f15b3 A |
1394 | static void |
1395 | logerror(const char *type) | |
b7080c8e | 1396 | { |
ac2f15b3 | 1397 | char buf[512]; |
b7080c8e A |
1398 | |
1399 | if (errno) | |
1400 | (void)snprintf(buf, | |
ac2f15b3 | 1401 | sizeof buf, "syslogd: %s: %s", type, strerror(errno)); |
b7080c8e | 1402 | else |
ac2f15b3 | 1403 | (void)snprintf(buf, sizeof buf, "syslogd: %s", type); |
b7080c8e A |
1404 | errno = 0; |
1405 | dprintf("%s\n", buf); | |
1406 | logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE); | |
1407 | } | |
1408 | ||
ac2f15b3 A |
1409 | static void |
1410 | die(int signo) | |
b7080c8e A |
1411 | { |
1412 | struct filed *f; | |
ac2f15b3 | 1413 | int was_initialized; |
b7080c8e | 1414 | char buf[100]; |
ac2f15b3 | 1415 | int i; |
b7080c8e | 1416 | |
ac2f15b3 A |
1417 | was_initialized = Initialized; |
1418 | Initialized = 0; /* Don't log SIGCHLDs. */ | |
b7080c8e A |
1419 | for (f = Files; f != NULL; f = f->f_next) { |
1420 | /* flush any pending output */ | |
1421 | if (f->f_prevcount) | |
1422 | fprintlog(f, 0, (char *)NULL); | |
ac2f15b3 A |
1423 | if (f->f_type == F_PIPE) |
1424 | (void)close(f->f_file); | |
b7080c8e | 1425 | } |
ac2f15b3 | 1426 | Initialized = was_initialized; |
b7080c8e A |
1427 | if (signo) { |
1428 | dprintf("syslogd: exiting on signal %d\n", signo); | |
ac2f15b3 | 1429 | (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo); |
b7080c8e A |
1430 | errno = 0; |
1431 | logerror(buf); | |
1432 | } | |
ac2f15b3 A |
1433 | for (i = 0; i < nfunix; i++) |
1434 | if (funixn[i] && funix[i] != -1) | |
1435 | (void)unlink(funixn[i]); | |
1436 | exit(1); | |
b7080c8e A |
1437 | } |
1438 | ||
1439 | /* | |
1440 | * INIT -- Initialize syslogd from configuration table | |
1441 | */ | |
ac2f15b3 A |
1442 | static void |
1443 | init(int signo) | |
b7080c8e A |
1444 | { |
1445 | int i; | |
1446 | FILE *cf; | |
1447 | struct filed *f, *next, **nextp; | |
1448 | char *p; | |
1449 | char cline[LINE_MAX]; | |
ac2f15b3 A |
1450 | char prog[NAME_MAX+1]; |
1451 | char host[MAXHOSTNAMELEN]; | |
1452 | char oldLocalHostName[MAXHOSTNAMELEN]; | |
1453 | char hostMsg[2*MAXHOSTNAMELEN+40]; | |
1454 | char bootfileMsg[LINE_MAX]; | |
b7080c8e A |
1455 | |
1456 | dprintf("init\n"); | |
1457 | ||
ac2f15b3 A |
1458 | /* |
1459 | * Load hostname (may have changed). | |
1460 | */ | |
1461 | if (signo != 0) | |
1462 | (void)strlcpy(oldLocalHostName, LocalHostName, | |
1463 | sizeof(oldLocalHostName)); | |
1464 | if (gethostname(LocalHostName, sizeof(LocalHostName))) | |
1465 | err(EX_OSERR, "gethostname() failed"); | |
1466 | if ((p = strchr(LocalHostName, '.')) != NULL) { | |
1467 | *p++ = '\0'; | |
1468 | LocalDomain = p; | |
1469 | } else { | |
1470 | LocalDomain = ""; | |
1471 | } | |
1472 | ||
b7080c8e A |
1473 | /* |
1474 | * Close all open log files. | |
1475 | */ | |
1476 | Initialized = 0; | |
1477 | for (f = Files; f != NULL; f = next) { | |
1478 | /* flush any pending output */ | |
1479 | if (f->f_prevcount) | |
1480 | fprintlog(f, 0, (char *)NULL); | |
1481 | ||
1482 | switch (f->f_type) { | |
1483 | case F_FILE: | |
b7080c8e | 1484 | case F_FORW: |
ac2f15b3 A |
1485 | case F_CONSOLE: |
1486 | case F_TTY: | |
1487 | (void)close(f->f_file); | |
1488 | break; | |
1489 | case F_PIPE: | |
b7080c8e | 1490 | (void)close(f->f_file); |
ac2f15b3 A |
1491 | if (f->f_un.f_pipe.f_pid > 0) |
1492 | deadq_enter(f->f_un.f_pipe.f_pid, | |
1493 | f->f_un.f_pipe.f_pname); | |
1494 | f->f_un.f_pipe.f_pid = 0; | |
b7080c8e A |
1495 | break; |
1496 | } | |
1497 | next = f->f_next; | |
ac2f15b3 A |
1498 | if (f->f_program) free(f->f_program); |
1499 | if (f->f_host) free(f->f_host); | |
b7080c8e A |
1500 | free((char *)f); |
1501 | } | |
1502 | Files = NULL; | |
1503 | nextp = &Files; | |
1504 | ||
1505 | /* open the configuration file */ | |
1506 | if ((cf = fopen(ConfFile, "r")) == NULL) { | |
1507 | dprintf("cannot open %s\n", ConfFile); | |
1508 | *nextp = (struct filed *)calloc(1, sizeof(*f)); | |
ac2f15b3 A |
1509 | if (*nextp == NULL) { |
1510 | logerror("calloc"); | |
1511 | exit(1); | |
1512 | } | |
1513 | cfline("*.ERR\t/dev/console", *nextp, "*", "*"); | |
b7080c8e | 1514 | (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f)); |
ac2f15b3 A |
1515 | if ((*nextp)->f_next == NULL) { |
1516 | logerror("calloc"); | |
1517 | exit(1); | |
1518 | } | |
1519 | cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*"); | |
b7080c8e A |
1520 | Initialized = 1; |
1521 | return; | |
1522 | } | |
1523 | ||
1524 | /* | |
1525 | * Foreach line in the conf table, open that file. | |
1526 | */ | |
1527 | f = NULL; | |
ac2f15b3 A |
1528 | (void)strlcpy(host, "*", sizeof(host)); |
1529 | (void)strlcpy(prog, "*", sizeof(prog)); | |
b7080c8e A |
1530 | while (fgets(cline, sizeof(cline), cf) != NULL) { |
1531 | /* | |
1532 | * check for end-of-section, comments, strip off trailing | |
ac2f15b3 A |
1533 | * spaces and newline character. #!prog is treated specially: |
1534 | * following lines apply only to that program. | |
b7080c8e A |
1535 | */ |
1536 | for (p = cline; isspace(*p); ++p) | |
1537 | continue; | |
ac2f15b3 A |
1538 | if (*p == 0) |
1539 | continue; | |
1540 | if (*p == '#') { | |
1541 | p++; | |
1542 | if (*p != '!' && *p != '+' && *p != '-') | |
1543 | continue; | |
1544 | } | |
1545 | if (*p == '+' || *p == '-') { | |
1546 | host[0] = *p++; | |
1547 | while (isspace(*p)) | |
1548 | p++; | |
1549 | if ((!*p) || (*p == '*')) { | |
1550 | (void)strlcpy(host, "*", sizeof(host)); | |
1551 | continue; | |
1552 | } | |
1553 | if (*p == '@') | |
1554 | p = LocalHostName; | |
1555 | for (i = 1; i < MAXHOSTNAMELEN - 1; i++) { | |
1556 | if (!isalnum(*p) && *p != '.' && *p != '-' | |
1557 | && *p != ',') | |
1558 | break; | |
1559 | host[i] = *p++; | |
1560 | } | |
1561 | host[i] = '\0'; | |
1562 | continue; | |
1563 | } | |
1564 | if (*p == '!') { | |
1565 | p++; | |
1566 | while (isspace(*p)) p++; | |
1567 | if ((!*p) || (*p == '*')) { | |
1568 | (void)strlcpy(prog, "*", sizeof(prog)); | |
1569 | continue; | |
1570 | } | |
1571 | for (i = 0; i < NAME_MAX; i++) { | |
1572 | if (!isprint(p[i])) | |
1573 | break; | |
1574 | prog[i] = p[i]; | |
1575 | } | |
1576 | prog[i] = 0; | |
b7080c8e | 1577 | continue; |
ac2f15b3 | 1578 | } |
b7080c8e A |
1579 | for (p = strchr(cline, '\0'); isspace(*--p);) |
1580 | continue; | |
1581 | *++p = '\0'; | |
1582 | f = (struct filed *)calloc(1, sizeof(*f)); | |
ac2f15b3 A |
1583 | if (f == NULL) { |
1584 | logerror("calloc"); | |
1585 | exit(1); | |
1586 | } | |
b7080c8e A |
1587 | *nextp = f; |
1588 | nextp = &f->f_next; | |
ac2f15b3 | 1589 | cfline(cline, f, prog, host); |
b7080c8e A |
1590 | } |
1591 | ||
1592 | /* close the configuration file */ | |
1593 | (void)fclose(cf); | |
1594 | ||
1595 | Initialized = 1; | |
1596 | ||
1597 | if (Debug) { | |
1598 | for (f = Files; f; f = f->f_next) { | |
1599 | for (i = 0; i <= LOG_NFACILITIES; i++) | |
1600 | if (f->f_pmask[i] == INTERNAL_NOPRI) | |
1601 | printf("X "); | |
1602 | else | |
1603 | printf("%d ", f->f_pmask[i]); | |
1604 | printf("%s: ", TypeNames[f->f_type]); | |
1605 | switch (f->f_type) { | |
1606 | case F_FILE: | |
b7080c8e A |
1607 | printf("%s", f->f_un.f_fname); |
1608 | break; | |
1609 | ||
ac2f15b3 A |
1610 | case F_CONSOLE: |
1611 | case F_TTY: | |
1612 | printf("%s%s", _PATH_DEV, f->f_un.f_fname); | |
1613 | break; | |
1614 | ||
b7080c8e A |
1615 | case F_FORW: |
1616 | printf("%s", f->f_un.f_forw.f_hname); | |
1617 | break; | |
1618 | ||
ac2f15b3 A |
1619 | case F_PIPE: |
1620 | printf("%s", f->f_un.f_pipe.f_pname); | |
1621 | break; | |
1622 | ||
b7080c8e A |
1623 | case F_USERS: |
1624 | for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++) | |
1625 | printf("%s, ", f->f_un.f_uname[i]); | |
1626 | break; | |
1627 | } | |
ac2f15b3 A |
1628 | if (f->f_program) |
1629 | printf(" (%s)", f->f_program); | |
b7080c8e A |
1630 | printf("\n"); |
1631 | } | |
1632 | } | |
1633 | ||
1634 | logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE); | |
1635 | dprintf("syslogd: restarted\n"); | |
ac2f15b3 A |
1636 | /* |
1637 | * Log a change in hostname, but only on a restart. | |
1638 | */ | |
1639 | if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) { | |
1640 | (void)snprintf(hostMsg, sizeof(hostMsg), | |
1641 | "syslogd: hostname changed, \"%s\" to \"%s\"", | |
1642 | oldLocalHostName, LocalHostName); | |
1643 | logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE); | |
1644 | dprintf("%s\n", hostMsg); | |
1645 | } | |
1646 | /* | |
1647 | * Log the kernel boot file if we aren't going to use it as | |
1648 | * the prefix, and if this is *not* a restart. | |
1649 | */ | |
1650 | if (signo == 0 && !use_bootfile) { | |
1651 | (void)snprintf(bootfileMsg, sizeof(bootfileMsg), | |
1652 | "syslogd: kernel boot file is %s", bootfile); | |
1653 | logmsg(LOG_KERN|LOG_INFO, bootfileMsg, LocalHostName, ADDDATE); | |
1654 | dprintf("%s\n", bootfileMsg); | |
1655 | } | |
b7080c8e A |
1656 | } |
1657 | ||
1658 | /* | |
1659 | * Crack a configuration file line | |
1660 | */ | |
ac2f15b3 A |
1661 | static void |
1662 | cfline(const char *line, struct filed *f, const char *prog, const char *host) | |
b7080c8e | 1663 | { |
ac2f15b3 A |
1664 | struct addrinfo hints, *res; |
1665 | int error, i, pri; | |
1666 | const char *p, *q; | |
1667 | char *bp, *port; | |
b7080c8e A |
1668 | char buf[MAXLINE], ebuf[100]; |
1669 | ||
ac2f15b3 | 1670 | dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host); |
b7080c8e A |
1671 | |
1672 | errno = 0; /* keep strerror() stuff out of logerror messages */ | |
1673 | ||
1674 | /* clear out file entry */ | |
1675 | memset(f, 0, sizeof(*f)); | |
1676 | for (i = 0; i <= LOG_NFACILITIES; i++) | |
1677 | f->f_pmask[i] = INTERNAL_NOPRI; | |
1678 | ||
ac2f15b3 A |
1679 | /* save hostname if any */ |
1680 | if (host && *host == '*') | |
1681 | host = NULL; | |
1682 | if (host) { | |
1683 | int hl, dl; | |
1684 | ||
1685 | f->f_host = strdup(host); | |
1686 | if (f->f_host == NULL) { | |
1687 | logerror("strdup"); | |
1688 | exit(1); | |
1689 | } | |
1690 | hl = strlen(f->f_host); | |
1691 | if (f->f_host[hl-1] == '.') | |
1692 | f->f_host[--hl] = '\0'; | |
1693 | dl = strlen(LocalDomain) + 1; | |
1694 | if (hl > dl && f->f_host[hl-dl] == '.' && | |
1695 | strcasecmp(f->f_host + hl - dl + 1, LocalDomain) == 0) | |
1696 | f->f_host[hl-dl] = '\0'; | |
1697 | } | |
1698 | ||
1699 | /* save program name if any */ | |
1700 | if (prog && *prog == '*') | |
1701 | prog = NULL; | |
1702 | if (prog) { | |
1703 | f->f_program = strdup(prog); | |
1704 | if (f->f_program == NULL) { | |
1705 | logerror("strdup"); | |
1706 | exit(1); | |
1707 | } | |
1708 | } | |
1709 | ||
b7080c8e | 1710 | /* scan through the list of selectors */ |
ac2f15b3 A |
1711 | for (p = line; *p && *p != '\t' && *p != ' ';) { |
1712 | int pri_done; | |
1713 | int pri_cmp; | |
1714 | int pri_invert; | |
b7080c8e A |
1715 | |
1716 | /* find the end of this facility name list */ | |
ac2f15b3 | 1717 | for (q = p; *q && *q != '\t' && *q != ' ' && *q++ != '.'; ) |
b7080c8e A |
1718 | continue; |
1719 | ||
ac2f15b3 A |
1720 | /* get the priority comparison */ |
1721 | pri_cmp = 0; | |
1722 | pri_done = 0; | |
1723 | pri_invert = 0; | |
1724 | if (*q == '!') { | |
1725 | pri_invert = 1; | |
1726 | q++; | |
1727 | } | |
1728 | while (!pri_done) { | |
1729 | switch (*q) { | |
1730 | case '<': | |
1731 | pri_cmp |= PRI_LT; | |
1732 | q++; | |
1733 | break; | |
1734 | case '=': | |
1735 | pri_cmp |= PRI_EQ; | |
1736 | q++; | |
1737 | break; | |
1738 | case '>': | |
1739 | pri_cmp |= PRI_GT; | |
1740 | q++; | |
1741 | break; | |
1742 | default: | |
1743 | pri_done++; | |
1744 | break; | |
1745 | } | |
1746 | } | |
1747 | ||
b7080c8e | 1748 | /* collect priority name */ |
ac2f15b3 | 1749 | for (bp = buf; *q && !strchr("\t,; ", *q); ) |
b7080c8e A |
1750 | *bp++ = *q++; |
1751 | *bp = '\0'; | |
1752 | ||
1753 | /* skip cruft */ | |
ac2f15b3 | 1754 | while (strchr(",;", *q)) |
b7080c8e A |
1755 | q++; |
1756 | ||
1757 | /* decode priority name */ | |
ac2f15b3 | 1758 | if (*buf == '*') { |
b7080c8e | 1759 | pri = LOG_PRIMASK + 1; |
ac2f15b3 A |
1760 | pri_cmp = PRI_LT | PRI_EQ | PRI_GT; |
1761 | } else { | |
b7080c8e A |
1762 | pri = decode(buf, prioritynames); |
1763 | if (pri < 0) { | |
ac2f15b3 | 1764 | (void)snprintf(ebuf, sizeof ebuf, |
b7080c8e A |
1765 | "unknown priority name \"%s\"", buf); |
1766 | logerror(ebuf); | |
1767 | return; | |
1768 | } | |
1769 | } | |
ac2f15b3 A |
1770 | if (!pri_cmp) |
1771 | pri_cmp = (UniquePriority) | |
1772 | ? (PRI_EQ) | |
1773 | : (PRI_EQ | PRI_GT) | |
1774 | ; | |
1775 | if (pri_invert) | |
1776 | pri_cmp ^= PRI_LT | PRI_EQ | PRI_GT; | |
b7080c8e A |
1777 | |
1778 | /* scan facilities */ | |
ac2f15b3 A |
1779 | while (*p && !strchr("\t.; ", *p)) { |
1780 | for (bp = buf; *p && !strchr("\t,;. ", *p); ) | |
b7080c8e A |
1781 | *bp++ = *p++; |
1782 | *bp = '\0'; | |
ac2f15b3 A |
1783 | |
1784 | if (*buf == '*') { | |
1785 | for (i = 0; i < LOG_NFACILITIES; i++) { | |
b7080c8e | 1786 | f->f_pmask[i] = pri; |
ac2f15b3 A |
1787 | f->f_pcmp[i] = pri_cmp; |
1788 | } | |
1789 | } else { | |
b7080c8e A |
1790 | i = decode(buf, facilitynames); |
1791 | if (i < 0) { | |
ac2f15b3 | 1792 | (void)snprintf(ebuf, sizeof ebuf, |
b7080c8e A |
1793 | "unknown facility name \"%s\"", |
1794 | buf); | |
1795 | logerror(ebuf); | |
1796 | return; | |
1797 | } | |
1798 | f->f_pmask[i >> 3] = pri; | |
ac2f15b3 | 1799 | f->f_pcmp[i >> 3] = pri_cmp; |
b7080c8e A |
1800 | } |
1801 | while (*p == ',' || *p == ' ') | |
1802 | p++; | |
1803 | } | |
1804 | ||
1805 | p = q; | |
1806 | } | |
1807 | ||
1808 | /* skip to action part */ | |
ac2f15b3 | 1809 | while (*p == '\t' || *p == ' ') |
b7080c8e A |
1810 | p++; |
1811 | ||
ac2f15b3 | 1812 | switch (*p) { |
b7080c8e | 1813 | case '@': |
ac2f15b3 A |
1814 | port = p; |
1815 | p = strsep(&port, ":"); | |
1816 | (void)strlcpy(f->f_un.f_forw.f_hname, ++p, | |
1817 | sizeof(f->f_un.f_forw.f_hname)); | |
1818 | memset(&hints, 0, sizeof(hints)); | |
1819 | hints.ai_family = family; | |
1820 | hints.ai_socktype = SOCK_DGRAM; | |
1821 | error = getaddrinfo(f->f_un.f_forw.f_hname, port ? port : "syslog", &hints, | |
1822 | &res); | |
1823 | if (error) { | |
1824 | logerror(gai_strerror(error)); | |
b7080c8e A |
1825 | break; |
1826 | } | |
ac2f15b3 | 1827 | f->f_un.f_forw.f_addr = res; |
b7080c8e A |
1828 | f->f_type = F_FORW; |
1829 | break; | |
1830 | ||
1831 | case '/': | |
ac2f15b3 A |
1832 | /* Delay opening files until we're ready to log to them */ |
1833 | f->f_file = -1; | |
1834 | if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV)) == 0) | |
1835 | f->f_type = F_CHECKTTY; | |
b7080c8e A |
1836 | else |
1837 | f->f_type = F_FILE; | |
ac2f15b3 A |
1838 | (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname)); |
1839 | break; | |
1840 | ||
1841 | case '|': | |
1842 | f->f_un.f_pipe.f_pid = 0; | |
1843 | (void)strlcpy(f->f_un.f_fname, p + 1, sizeof(f->f_un.f_fname)); | |
1844 | f->f_type = F_PIPE; | |
b7080c8e A |
1845 | break; |
1846 | ||
1847 | case '*': | |
1848 | f->f_type = F_WALL; | |
1849 | break; | |
1850 | ||
1851 | default: | |
1852 | for (i = 0; i < MAXUNAMES && *p; i++) { | |
1853 | for (q = p; *q && *q != ','; ) | |
1854 | q++; | |
1855 | (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE); | |
1856 | if ((q - p) > UT_NAMESIZE) | |
1857 | f->f_un.f_uname[i][UT_NAMESIZE] = '\0'; | |
1858 | else | |
1859 | f->f_un.f_uname[i][q - p] = '\0'; | |
1860 | while (*q == ',' || *q == ' ') | |
1861 | q++; | |
1862 | p = q; | |
1863 | } | |
1864 | f->f_type = F_USERS; | |
1865 | break; | |
1866 | } | |
1867 | } | |
1868 | ||
1869 | ||
1870 | /* | |
1871 | * Decode a symbolic name to a numeric value | |
1872 | */ | |
ac2f15b3 A |
1873 | static int |
1874 | decode(const char *name, CODE *codetab) | |
b7080c8e A |
1875 | { |
1876 | CODE *c; | |
1877 | char *p, buf[40]; | |
1878 | ||
1879 | if (isdigit(*name)) | |
1880 | return (atoi(name)); | |
1881 | ||
1882 | for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) { | |
1883 | if (isupper(*name)) | |
1884 | *p = tolower(*name); | |
1885 | else | |
1886 | *p = *name; | |
1887 | } | |
1888 | *p = '\0'; | |
1889 | for (c = codetab; c->c_name; c++) | |
1890 | if (!strcmp(buf, c->c_name)) | |
1891 | return (c->c_val); | |
1892 | ||
1893 | return (-1); | |
1894 | } | |
ac2f15b3 A |
1895 | |
1896 | static void | |
1897 | markit(void) | |
1898 | { | |
1899 | struct filed *f; | |
1900 | dq_t q, next; | |
1901 | ||
1902 | now = time((time_t *)NULL); | |
1903 | MarkSeq += TIMERINTVL; | |
1904 | if (MarkInterval && (MarkSeq >= MarkInterval)) { | |
1905 | logmsg(LOG_INFO, "-- MARK --", | |
1906 | LocalHostName, ADDDATE|MARK); | |
1907 | MarkSeq = 0; | |
1908 | } | |
1909 | ||
1910 | for (f = Files; f; f = f->f_next) { | |
1911 | if (f->f_prevcount && now >= REPEATTIME(f)) { | |
1912 | dprintf("flush %s: repeated %d times, %d sec.\n", | |
1913 | TypeNames[f->f_type], f->f_prevcount, | |
1914 | repeatinterval[f->f_repeatcount]); | |
1915 | fprintlog(f, 0, (char *)NULL); | |
1916 | BACKOFF(f); | |
1917 | } | |
1918 | } | |
1919 | ||
1920 | /* Walk the dead queue, and see if we should signal somebody. */ | |
1921 | for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = next) { | |
1922 | next = TAILQ_NEXT(q, dq_entries); | |
1923 | ||
1924 | switch (q->dq_timeout) { | |
1925 | case 0: | |
1926 | /* Already signalled once, try harder now. */ | |
1927 | if (kill(q->dq_pid, SIGKILL) != 0) | |
1928 | (void)deadq_remove(q->dq_pid); | |
1929 | break; | |
1930 | ||
1931 | case 1: | |
1932 | /* | |
1933 | * Timed out on dead queue, send terminate | |
1934 | * signal. Note that we leave the removal | |
1935 | * from the dead queue to reapchild(), which | |
1936 | * will also log the event (unless the process | |
1937 | * didn't even really exist, in case we simply | |
1938 | * drop it from the dead queue). | |
1939 | */ | |
1940 | if (kill(q->dq_pid, SIGTERM) != 0) | |
1941 | (void)deadq_remove(q->dq_pid); | |
1942 | /* FALLTHROUGH */ | |
1943 | ||
1944 | default: | |
1945 | q->dq_timeout--; | |
1946 | } | |
1947 | } | |
1948 | MarkSet = 0; | |
1949 | (void)alarm(TIMERINTVL); | |
1950 | } | |
1951 | ||
1952 | /* | |
1953 | * fork off and become a daemon, but wait for the child to come online | |
1954 | * before returing to the parent, or we get disk thrashing at boot etc. | |
1955 | * Set a timer so we don't hang forever if it wedges. | |
1956 | */ | |
1957 | static int | |
1958 | waitdaemon(int nochdir, int noclose, int maxwait) | |
1959 | { | |
1960 | int fd; | |
1961 | int status; | |
1962 | pid_t pid, childpid; | |
1963 | ||
1964 | switch (childpid = fork()) { | |
1965 | case -1: | |
1966 | return (-1); | |
1967 | case 0: | |
1968 | break; | |
1969 | default: | |
1970 | signal(SIGALRM, timedout); | |
1971 | alarm(maxwait); | |
1972 | while ((pid = wait3(&status, 0, NULL)) != -1) { | |
1973 | if (WIFEXITED(status)) | |
1974 | errx(1, "child pid %d exited with return code %d", | |
1975 | pid, WEXITSTATUS(status)); | |
1976 | if (WIFSIGNALED(status)) | |
1977 | errx(1, "child pid %d exited on signal %d%s", | |
1978 | pid, WTERMSIG(status), | |
1979 | WCOREDUMP(status) ? " (core dumped)" : | |
1980 | ""); | |
1981 | if (pid == childpid) /* it's gone... */ | |
1982 | break; | |
1983 | } | |
1984 | exit(0); | |
1985 | } | |
1986 | ||
1987 | if (setsid() == -1) | |
1988 | return (-1); | |
1989 | ||
1990 | if (!nochdir) | |
1991 | (void)chdir("/"); | |
1992 | ||
1993 | if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { | |
1994 | (void)dup2(fd, STDIN_FILENO); | |
1995 | (void)dup2(fd, STDOUT_FILENO); | |
1996 | (void)dup2(fd, STDERR_FILENO); | |
1997 | if (fd > 2) | |
1998 | (void)close (fd); | |
1999 | } | |
2000 | return (getppid()); | |
2001 | } | |
2002 | ||
2003 | /* | |
2004 | * We get a SIGALRM from the child when it's running and finished doing it's | |
2005 | * fsync()'s or O_SYNC writes for all the boot messages. | |
2006 | * | |
2007 | * We also get a signal from the kernel if the timer expires, so check to | |
2008 | * see what happened. | |
2009 | */ | |
2010 | static void | |
2011 | timedout(int sig ) | |
2012 | { | |
2013 | int left; | |
2014 | left = alarm(0); | |
2015 | signal(SIGALRM, SIG_DFL); | |
2016 | if (left == 0) | |
2017 | errx(1, "timed out waiting for child"); | |
2018 | else | |
2019 | _exit(0); | |
2020 | } | |
2021 | ||
2022 | /* | |
2023 | * Add `s' to the list of allowable peer addresses to accept messages | |
2024 | * from. | |
2025 | * | |
2026 | * `s' is a string in the form: | |
2027 | * | |
2028 | * [*]domainname[:{servicename|portnumber|*}] | |
2029 | * | |
2030 | * or | |
2031 | * | |
2032 | * netaddr/maskbits[:{servicename|portnumber|*}] | |
2033 | * | |
2034 | * Returns -1 on error, 0 if the argument was valid. | |
2035 | */ | |
2036 | static int | |
2037 | allowaddr(char *s) | |
2038 | { | |
2039 | char *cp1, *cp2; | |
2040 | struct allowedpeer ap; | |
2041 | struct servent *se; | |
2042 | int masklen = -1, i; | |
2043 | struct addrinfo hints, *res; | |
2044 | struct in_addr *addrp, *maskp; | |
2045 | u_int32_t *addr6p, *mask6p; | |
2046 | char ip[NI_MAXHOST]; | |
2047 | ||
2048 | #ifdef INET6 | |
2049 | if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL) | |
2050 | #endif | |
2051 | cp1 = s; | |
2052 | if ((cp1 = strrchr(cp1, ':'))) { | |
2053 | /* service/port provided */ | |
2054 | *cp1++ = '\0'; | |
2055 | if (strlen(cp1) == 1 && *cp1 == '*') | |
2056 | /* any port allowed */ | |
2057 | ap.port = 0; | |
2058 | else if ((se = getservbyname(cp1, "udp"))) { | |
2059 | ap.port = ntohs(se->s_port); | |
2060 | } else { | |
2061 | ap.port = strtol(cp1, &cp2, 0); | |
2062 | if (*cp2 != '\0') | |
2063 | return (-1); /* port not numeric */ | |
2064 | } | |
2065 | } else { | |
2066 | if ((se = getservbyname("syslog", "udp"))) | |
2067 | ap.port = ntohs(se->s_port); | |
2068 | else | |
2069 | /* sanity, should not happen */ | |
2070 | ap.port = 514; | |
2071 | } | |
2072 | ||
2073 | if ((cp1 = strchr(s, '/')) != NULL && | |
2074 | strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) { | |
2075 | *cp1 = '\0'; | |
2076 | if ((masklen = atoi(cp1 + 1)) < 0) | |
2077 | return (-1); | |
2078 | } | |
2079 | #ifdef INET6 | |
2080 | if (*s == '[') { | |
2081 | cp2 = s + strlen(s) - 1; | |
2082 | if (*cp2 == ']') { | |
2083 | ++s; | |
2084 | *cp2 = '\0'; | |
2085 | } else { | |
2086 | cp2 = NULL; | |
2087 | } | |
2088 | } else { | |
2089 | cp2 = NULL; | |
2090 | } | |
2091 | #endif | |
2092 | memset(&hints, 0, sizeof(hints)); | |
2093 | hints.ai_family = PF_UNSPEC; | |
2094 | hints.ai_socktype = SOCK_DGRAM; | |
2095 | hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; | |
2096 | if (getaddrinfo(s, NULL, &hints, &res) == 0) { | |
2097 | ap.isnumeric = 1; | |
2098 | memcpy(&ap.a_addr, res->ai_addr, res->ai_addrlen); | |
2099 | memset(&ap.a_mask, 0, sizeof(ap.a_mask)); | |
2100 | ap.a_mask.ss_family = res->ai_family; | |
2101 | if (res->ai_family == AF_INET) { | |
2102 | ap.a_mask.ss_len = sizeof(struct sockaddr_in); | |
2103 | maskp = &((struct sockaddr_in *)&ap.a_mask)->sin_addr; | |
2104 | addrp = &((struct sockaddr_in *)&ap.a_addr)->sin_addr; | |
2105 | if (masklen < 0) { | |
2106 | /* use default netmask */ | |
2107 | if (IN_CLASSA(ntohl(addrp->s_addr))) | |
2108 | maskp->s_addr = htonl(IN_CLASSA_NET); | |
2109 | else if (IN_CLASSB(ntohl(addrp->s_addr))) | |
2110 | maskp->s_addr = htonl(IN_CLASSB_NET); | |
2111 | else | |
2112 | maskp->s_addr = htonl(IN_CLASSC_NET); | |
2113 | } else if (masklen <= 32) { | |
2114 | /* convert masklen to netmask */ | |
2115 | if (masklen == 0) | |
2116 | maskp->s_addr = 0; | |
2117 | else | |
2118 | maskp->s_addr = htonl(~((1 << (32 - masklen)) - 1)); | |
2119 | } else { | |
2120 | freeaddrinfo(res); | |
2121 | return (-1); | |
2122 | } | |
2123 | /* Lose any host bits in the network number. */ | |
2124 | addrp->s_addr &= maskp->s_addr; | |
2125 | } | |
2126 | #ifdef INET6 | |
2127 | else if (res->ai_family == AF_INET6 && masklen <= 128) { | |
2128 | ap.a_mask.ss_len = sizeof(struct sockaddr_in6); | |
2129 | if (masklen < 0) | |
2130 | masklen = 128; | |
2131 | mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr; | |
2132 | /* convert masklen to netmask */ | |
2133 | while (masklen > 0) { | |
2134 | if (masklen < 32) { | |
2135 | *mask6p = htonl(~(0xffffffff >> masklen)); | |
2136 | break; | |
2137 | } | |
2138 | *mask6p++ = 0xffffffff; | |
2139 | masklen -= 32; | |
2140 | } | |
2141 | /* Lose any host bits in the network number. */ | |
2142 | mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr; | |
2143 | addr6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_addr)->sin6_addr; | |
2144 | for (i = 0; i < 4; i++) | |
2145 | addr6p[i] &= mask6p[i]; | |
2146 | } | |
2147 | #endif | |
2148 | else { | |
2149 | freeaddrinfo(res); | |
2150 | return (-1); | |
2151 | } | |
2152 | freeaddrinfo(res); | |
2153 | } else { | |
2154 | /* arg `s' is domain name */ | |
2155 | ap.isnumeric = 0; | |
2156 | ap.a_name = s; | |
2157 | if (cp1) | |
2158 | *cp1 = '/'; | |
2159 | #ifdef INET6 | |
2160 | if (cp2) { | |
2161 | *cp2 = ']'; | |
2162 | --s; | |
2163 | } | |
2164 | #endif | |
2165 | } | |
2166 | ||
2167 | if (Debug) { | |
2168 | printf("allowaddr: rule %d: ", NumAllowed); | |
2169 | if (ap.isnumeric) { | |
2170 | printf("numeric, "); | |
2171 | getnameinfo((struct sockaddr *)&ap.a_addr, | |
2172 | ((struct sockaddr *)&ap.a_addr)->sa_len, | |
2173 | ip, sizeof ip, NULL, 0, | |
2174 | NI_NUMERICHOST | withscopeid); | |
2175 | printf("addr = %s, ", ip); | |
2176 | getnameinfo((struct sockaddr *)&ap.a_mask, | |
2177 | ((struct sockaddr *)&ap.a_mask)->sa_len, | |
2178 | ip, sizeof ip, NULL, 0, | |
2179 | NI_NUMERICHOST | withscopeid); | |
2180 | printf("mask = %s; ", ip); | |
2181 | } else { | |
2182 | printf("domainname = %s; ", ap.a_name); | |
2183 | } | |
2184 | printf("port = %d\n", ap.port); | |
2185 | } | |
2186 | ||
2187 | if ((AllowedPeers = realloc(AllowedPeers, | |
2188 | ++NumAllowed * sizeof(struct allowedpeer))) | |
2189 | == NULL) { | |
2190 | logerror("realloc"); | |
2191 | exit(1); | |
2192 | } | |
2193 | memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer)); | |
2194 | return (0); | |
2195 | } | |
2196 | ||
2197 | /* | |
2198 | * Validate that the remote peer has permission to log to us. | |
2199 | */ | |
2200 | static int | |
2201 | validate(struct sockaddr *sa, const char *hname) | |
2202 | { | |
2203 | int i, j, reject; | |
2204 | size_t l1, l2; | |
2205 | char *cp, name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV]; | |
2206 | struct allowedpeer *ap; | |
2207 | struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL; | |
2208 | struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL; | |
2209 | struct addrinfo hints, *res; | |
2210 | u_short sport; | |
2211 | ||
2212 | if (NumAllowed == 0) | |
2213 | /* traditional behaviour, allow everything */ | |
2214 | return (1); | |
2215 | ||
2216 | (void)strlcpy(name, hname, sizeof(name)); | |
2217 | memset(&hints, 0, sizeof(hints)); | |
2218 | hints.ai_family = PF_UNSPEC; | |
2219 | hints.ai_socktype = SOCK_DGRAM; | |
2220 | hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; | |
2221 | if (getaddrinfo(name, NULL, &hints, &res) == 0) | |
2222 | freeaddrinfo(res); | |
2223 | else if (strchr(name, '.') == NULL) { | |
2224 | strlcat(name, ".", sizeof name); | |
2225 | strlcat(name, LocalDomain, sizeof name); | |
2226 | } | |
2227 | if (getnameinfo(sa, sa->sa_len, ip, sizeof ip, port, sizeof port, | |
2228 | NI_NUMERICHOST | withscopeid | NI_NUMERICSERV) != 0) | |
2229 | return (0); /* for safety, should not occur */ | |
2230 | dprintf("validate: dgram from IP %s, port %s, name %s;\n", | |
2231 | ip, port, name); | |
2232 | sport = atoi(port); | |
2233 | ||
2234 | /* now, walk down the list */ | |
2235 | for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) { | |
2236 | if (ap->port != 0 && ap->port != sport) { | |
2237 | dprintf("rejected in rule %d due to port mismatch.\n", i); | |
2238 | continue; | |
2239 | } | |
2240 | ||
2241 | if (ap->isnumeric) { | |
2242 | if (ap->a_addr.ss_family != sa->sa_family) { | |
2243 | dprintf("rejected in rule %d due to address family mismatch.\n", i); | |
2244 | continue; | |
2245 | } | |
2246 | if (ap->a_addr.ss_family == AF_INET) { | |
2247 | sin4 = (struct sockaddr_in *)sa; | |
2248 | a4p = (struct sockaddr_in *)&ap->a_addr; | |
2249 | m4p = (struct sockaddr_in *)&ap->a_mask; | |
2250 | if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr) | |
2251 | != a4p->sin_addr.s_addr) { | |
2252 | dprintf("rejected in rule %d due to IP mismatch.\n", i); | |
2253 | continue; | |
2254 | } | |
2255 | } | |
2256 | #ifdef INET6 | |
2257 | else if (ap->a_addr.ss_family == AF_INET6) { | |
2258 | sin6 = (struct sockaddr_in6 *)sa; | |
2259 | a6p = (struct sockaddr_in6 *)&ap->a_addr; | |
2260 | m6p = (struct sockaddr_in6 *)&ap->a_mask; | |
2261 | #ifdef NI_WITHSCOPEID | |
2262 | if (a6p->sin6_scope_id != 0 && | |
2263 | sin6->sin6_scope_id != a6p->sin6_scope_id) { | |
2264 | dprintf("rejected in rule %d due to scope mismatch.\n", i); | |
2265 | continue; | |
2266 | } | |
2267 | #endif | |
2268 | reject = 0; | |
2269 | for (j = 0; j < 16; j += 4) { | |
2270 | if ((*(u_int32_t *)&sin6->sin6_addr.s6_addr[j] & *(u_int32_t *)&m6p->sin6_addr.s6_addr[j]) | |
2271 | != *(u_int32_t *)&a6p->sin6_addr.s6_addr[j]) { | |
2272 | ++reject; | |
2273 | break; | |
2274 | } | |
2275 | } | |
2276 | if (reject) { | |
2277 | dprintf("rejected in rule %d due to IP mismatch.\n", i); | |
2278 | continue; | |
2279 | } | |
2280 | } | |
2281 | #endif | |
2282 | else | |
2283 | continue; | |
2284 | } else { | |
2285 | cp = ap->a_name; | |
2286 | l1 = strlen(name); | |
2287 | if (*cp == '*') { | |
2288 | /* allow wildmatch */ | |
2289 | cp++; | |
2290 | l2 = strlen(cp); | |
2291 | if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) { | |
2292 | dprintf("rejected in rule %d due to name mismatch.\n", i); | |
2293 | continue; | |
2294 | } | |
2295 | } else { | |
2296 | /* exact match */ | |
2297 | l2 = strlen(cp); | |
2298 | if (l2 != l1 || memcmp(cp, name, l1) != 0) { | |
2299 | dprintf("rejected in rule %d due to name mismatch.\n", i); | |
2300 | continue; | |
2301 | } | |
2302 | } | |
2303 | } | |
2304 | dprintf("accepted in rule %d.\n", i); | |
2305 | return (1); /* hooray! */ | |
2306 | } | |
2307 | return (0); | |
2308 | } | |
2309 | ||
2310 | /* | |
2311 | * Fairly similar to popen(3), but returns an open descriptor, as | |
2312 | * opposed to a FILE *. | |
2313 | */ | |
2314 | static int | |
2315 | p_open(const char *prog, pid_t *pid) | |
2316 | { | |
2317 | int pfd[2], nulldesc, i; | |
2318 | sigset_t omask, mask; | |
2319 | char *argv[4]; /* sh -c cmd NULL */ | |
2320 | char errmsg[200]; | |
2321 | ||
2322 | if (pipe(pfd) == -1) | |
2323 | return (-1); | |
2324 | if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1) | |
2325 | /* we are royally screwed anyway */ | |
2326 | return (-1); | |
2327 | ||
2328 | sigemptyset(&mask); | |
2329 | sigaddset(&mask, SIGALRM); | |
2330 | sigaddset(&mask, SIGHUP); | |
2331 | sigprocmask(SIG_BLOCK, &mask, &omask); | |
2332 | switch ((*pid = fork())) { | |
2333 | case -1: | |
2334 | sigprocmask(SIG_SETMASK, &omask, 0); | |
2335 | close(nulldesc); | |
2336 | return (-1); | |
2337 | ||
2338 | case 0: | |
2339 | /* XXX should check for NULL return */ | |
2340 | argv[0] = strdup("sh"); | |
2341 | argv[1] = strdup("-c"); | |
2342 | argv[2] = strdup(prog); | |
2343 | argv[3] = NULL; | |
2344 | if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) { | |
2345 | logerror("strdup"); | |
2346 | exit(1); | |
2347 | } | |
2348 | ||
2349 | alarm(0); | |
2350 | (void)setsid(); /* Avoid catching SIGHUPs. */ | |
2351 | ||
2352 | /* | |
2353 | * Throw away pending signals, and reset signal | |
2354 | * behaviour to standard values. | |
2355 | */ | |
2356 | signal(SIGALRM, SIG_IGN); | |
2357 | signal(SIGHUP, SIG_IGN); | |
2358 | sigprocmask(SIG_SETMASK, &omask, 0); | |
2359 | signal(SIGPIPE, SIG_DFL); | |
2360 | signal(SIGQUIT, SIG_DFL); | |
2361 | signal(SIGALRM, SIG_DFL); | |
2362 | signal(SIGHUP, SIG_DFL); | |
2363 | ||
2364 | dup2(pfd[0], STDIN_FILENO); | |
2365 | dup2(nulldesc, STDOUT_FILENO); | |
2366 | dup2(nulldesc, STDERR_FILENO); | |
2367 | for (i = getdtablesize(); i > 2; i--) | |
2368 | (void)close(i); | |
2369 | ||
2370 | (void)execvp(_PATH_BSHELL, argv); | |
2371 | _exit(255); | |
2372 | } | |
2373 | ||
2374 | sigprocmask(SIG_SETMASK, &omask, 0); | |
2375 | close(nulldesc); | |
2376 | close(pfd[0]); | |
2377 | /* | |
2378 | * Avoid blocking on a hung pipe. With O_NONBLOCK, we are | |
2379 | * supposed to get an EWOULDBLOCK on writev(2), which is | |
2380 | * caught by the logic above anyway, which will in turn close | |
2381 | * the pipe, and fork a new logging subprocess if necessary. | |
2382 | * The stale subprocess will be killed some time later unless | |
2383 | * it terminated itself due to closing its input pipe (so we | |
2384 | * get rid of really dead puppies). | |
2385 | */ | |
2386 | if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) { | |
2387 | /* This is bad. */ | |
2388 | (void)snprintf(errmsg, sizeof errmsg, | |
2389 | "Warning: cannot change pipe to PID %d to " | |
2390 | "non-blocking behaviour.", | |
2391 | (int)*pid); | |
2392 | logerror(errmsg); | |
2393 | } | |
2394 | return (pfd[1]); | |
2395 | } | |
2396 | ||
2397 | static void | |
2398 | deadq_enter(pid_t pid, const char *name) | |
2399 | { | |
2400 | dq_t p; | |
2401 | int status; | |
2402 | ||
2403 | /* | |
2404 | * Be paranoid, if we can't signal the process, don't enter it | |
2405 | * into the dead queue (perhaps it's already dead). If possible, | |
2406 | * we try to fetch and log the child's status. | |
2407 | */ | |
2408 | if (kill(pid, 0) != 0) { | |
2409 | if (waitpid(pid, &status, WNOHANG) > 0) | |
2410 | log_deadchild(pid, status, name); | |
2411 | return; | |
2412 | } | |
2413 | ||
2414 | p = malloc(sizeof(struct deadq_entry)); | |
2415 | if (p == NULL) { | |
2416 | logerror("malloc"); | |
2417 | exit(1); | |
2418 | } | |
2419 | ||
2420 | p->dq_pid = pid; | |
2421 | p->dq_timeout = DQ_TIMO_INIT; | |
2422 | TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries); | |
2423 | } | |
2424 | ||
2425 | static int | |
2426 | deadq_remove(pid_t pid) | |
2427 | { | |
2428 | dq_t q; | |
2429 | ||
2430 | TAILQ_FOREACH(q, &deadq_head, dq_entries) { | |
2431 | if (q->dq_pid == pid) { | |
2432 | TAILQ_REMOVE(&deadq_head, q, dq_entries); | |
2433 | free(q); | |
2434 | return (1); | |
2435 | } | |
2436 | } | |
2437 | ||
2438 | return (0); | |
2439 | } | |
2440 | ||
2441 | static void | |
2442 | log_deadchild(pid_t pid, int status, const char *name) | |
2443 | { | |
2444 | int code; | |
2445 | char buf[256]; | |
2446 | const char *reason; | |
2447 | ||
2448 | errno = 0; /* Keep strerror() stuff out of logerror messages. */ | |
2449 | if (WIFSIGNALED(status)) { | |
2450 | reason = "due to signal"; | |
2451 | code = WTERMSIG(status); | |
2452 | } else { | |
2453 | reason = "with status"; | |
2454 | code = WEXITSTATUS(status); | |
2455 | if (code == 0) | |
2456 | return; | |
2457 | } | |
2458 | (void)snprintf(buf, sizeof buf, | |
2459 | "Logging subprocess %d (%s) exited %s %d.", | |
2460 | pid, name, reason, code); | |
2461 | logerror(buf); | |
2462 | } | |
2463 | ||
2464 | static int * | |
2465 | socksetup(int af, const char *bindhostname) | |
2466 | { | |
2467 | struct addrinfo hints, *res, *r; | |
2468 | int error, maxs, *s, *socks; | |
2469 | ||
2470 | memset(&hints, 0, sizeof(hints)); | |
2471 | hints.ai_flags = AI_PASSIVE; | |
2472 | hints.ai_family = af; | |
2473 | hints.ai_socktype = SOCK_DGRAM; | |
2474 | error = getaddrinfo(bindhostname, "syslog", &hints, &res); | |
2475 | if (error) { | |
2476 | logerror(gai_strerror(error)); | |
2477 | errno = 0; | |
2478 | die(0); | |
2479 | } | |
2480 | ||
2481 | /* Count max number of sockets we may open */ | |
2482 | for (maxs = 0, r = res; r; r = r->ai_next, maxs++); | |
2483 | socks = malloc((maxs+1) * sizeof(int)); | |
2484 | if (socks == NULL) { | |
2485 | logerror("couldn't allocate memory for sockets"); | |
2486 | die(0); | |
2487 | } | |
2488 | ||
2489 | *socks = 0; /* num of sockets counter at start of array */ | |
2490 | s = socks + 1; | |
2491 | for (r = res; r; r = r->ai_next) { | |
2492 | *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); | |
2493 | if (*s < 0) { | |
2494 | logerror("socket"); | |
2495 | continue; | |
2496 | } | |
2497 | if (r->ai_family == AF_INET6) { | |
2498 | int on = 1; | |
2499 | if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, | |
2500 | (char *)&on, sizeof (on)) < 0) { | |
2501 | logerror("setsockopt"); | |
2502 | close(*s); | |
2503 | continue; | |
2504 | } | |
2505 | } | |
2506 | if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { | |
2507 | close(*s); | |
2508 | logerror("bind"); | |
2509 | continue; | |
2510 | } | |
2511 | ||
2512 | (*socks)++; | |
2513 | s++; | |
2514 | } | |
2515 | ||
2516 | if (*socks == 0) { | |
2517 | free(socks); | |
2518 | if (Debug) | |
2519 | return (NULL); | |
2520 | else | |
2521 | die(0); | |
2522 | } | |
2523 | if (res) | |
2524 | freeaddrinfo(res); | |
2525 | ||
2526 | return (socks); | |
2527 | } |