]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/bsd_out.c
syslog-13.1.tar.gz
[apple/syslog.git] / syslogd.tproj / bsd_out.c
1 /*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/uio.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <notify.h>
38 #include "daemon.h"
39
40 #define MY_ID "bsd_out"
41
42 #define _PATH_WALL "/usr/bin/wall"
43 #define ASL_KEY_FACILITY "Facility"
44 #define FACILITY_KERNEL "kern"
45 #define _PATH_CONSOLE "/dev/console"
46 #define IndexNull ((uint32_t)-1)
47
48 #define DST_TYPE_NONE 0
49 #define DST_TYPE_FILE 1
50 #define DST_TYPE_CONS 2
51 #define DST_TYPE_SOCK 3
52 #define DST_TYPE_WALL 4
53 #define DST_TYPE_NOTE 5
54
55 static asl_msg_t *query = NULL;
56 static int reset = 0;
57
58 struct config_rule
59 {
60 uint32_t count;
61 char *dst;
62 int fd;
63 int type;
64 struct sockaddr *addr;
65 char **facility;
66 int *pri;
67 TAILQ_ENTRY(config_rule) entries;
68 };
69
70 static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
71
72 int bsd_out_close();
73 static int _parse_config_file(const char *);
74
75 static void
76 _do_reset(void)
77 {
78 bsd_out_close();
79 _parse_config_file(_PATH_SYSLOG_CONF);
80 }
81
82 static char **
83 _insertString(char *s, char **l, uint32_t x)
84 {
85 int i, len;
86
87 if (s == NULL) return l;
88 if (l == NULL)
89 {
90 l = (char **)malloc(2 * sizeof(char *));
91 if (l == NULL) return NULL;
92
93 l[0] = strdup(s);
94 if (l[0] == NULL)
95 {
96 free(l);
97 return NULL;
98 }
99
100 l[1] = NULL;
101 return l;
102 }
103
104 for (i = 0; l[i] != NULL; i++);
105 len = i + 1; /* count the NULL on the end of the list too! */
106
107 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
108 if (l == NULL) return NULL;
109
110 if ((x >= (len - 1)) || (x == IndexNull))
111 {
112 l[len - 1] = strdup(s);
113 if (l[len - 1] == NULL)
114 {
115 free(l);
116 return NULL;
117 }
118
119 l[len] = NULL;
120 return l;
121 }
122
123 for (i = len; i > x; i--) l[i] = l[i - 1];
124 l[x] = strdup(s);
125 if (l[x] == NULL) return NULL;
126
127 return l;
128 }
129
130 static char **
131 _explode(char *s, char *delim)
132 {
133 char **l = NULL;
134 char *p, *t;
135 int i, n;
136
137 if (s == NULL) return NULL;
138
139 p = s;
140 while (p[0] != '\0')
141 {
142 for (i = 0; ((p[i] != '\0') && (strchr(delim, p[i]) == NULL)); i++);
143 n = i;
144 t = malloc(n + 1);
145 if (t == NULL) return NULL;
146
147 for (i = 0; i < n; i++) t[i] = p[i];
148 t[n] = '\0';
149 l = _insertString(t, l, IndexNull);
150 free(t);
151 t = NULL;
152 if (p[i] == '\0') return l;
153 if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
154 p = p + i + 1;
155 }
156
157 return l;
158 }
159
160 static void
161 _freeList(char **l)
162 {
163 int i;
164
165 if (l == NULL) return;
166 for (i = 0; l[i] != NULL; i++) free(l[i]);
167 free(l);
168 }
169
170 static int
171 _level_for_name(const char *name)
172 {
173 if (name == NULL) return -1;
174
175 if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG;
176 if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG;
177 if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT;
178 if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT;
179 if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR;
180 if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR;
181 if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING;
182 if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING;
183 if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE;
184 if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO;
185 if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG;
186 if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG;
187
188 /* special case */
189 if (!strcasecmp(name, "none")) return -2;
190
191 return -1;
192 }
193
194 static int
195 _syslog_dst_open(struct config_rule *r)
196 {
197 int i;
198 char *node, *serv;
199 struct addrinfo hints, *gai, *ai;
200
201 if (r == NULL) return -1;
202
203 r->fd = -1;
204
205 if (r->dst[0] == '/')
206 {
207 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644);
208 if (r->fd < 0)
209 {
210 asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
211 return -1;
212 }
213
214 r->type = DST_TYPE_FILE;
215 if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS;
216
217 return 0;
218 }
219
220 if (r->dst[0] == '!')
221 {
222 r->type = DST_TYPE_NOTE;
223 return 0;
224 }
225
226 if (r->dst[0] == '@')
227 {
228 node = strdup(r->dst + 1);
229 if (node == NULL) return -1;
230
231 serv = NULL;
232 serv = strrchr(node, ':');
233 if (serv != NULL) *serv++ = '\0';
234 else serv = "syslog";
235
236 memset(&hints, 0, sizeof(hints));
237 hints.ai_family = PF_UNSPEC;
238 hints.ai_socktype = SOCK_DGRAM;
239 i = getaddrinfo(node, serv, &hints, &gai);
240 free(node);
241 if (i != 0)
242 {
243 asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i));
244 return -1;
245 }
246
247 for (ai = gai; ai != NULL; ai = ai->ai_next)
248 {
249 r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
250 if (r->fd < 0) continue;
251
252 r->addr = (struct sockaddr *)calloc(1, ai->ai_addrlen);
253 if (r->addr == NULL) return -1;
254
255 memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
256
257 break;
258 }
259
260 freeaddrinfo(gai);
261
262 if (r->fd < 0)
263 {
264 asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
265 return -1;
266 }
267
268 if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0)
269 {
270 close(r->fd);
271 r->fd = -1;
272 asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
273 return -1;
274 }
275
276 r->type = DST_TYPE_SOCK;
277 return 0;
278
279 }
280
281 if (strcmp(r->dst, "*") == 0)
282 {
283 r->type = DST_TYPE_WALL;
284 return 0;
285 }
286
287 /* Can't deal with dst! */
288 asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst);
289 return -1;
290 }
291
292 static int
293 _parse_line(char *s)
294 {
295 char **semi, **comma;
296 int i, j, n, lasts, lastc, pri;
297 struct config_rule *out;
298
299 if (s == NULL) return -1;
300 while ((*s == ' ') || (*s == '\t')) s++;
301 if (*s == '#') return -1;
302
303 semi = _explode(s, "; \t");
304
305 if (semi == NULL) return -1;
306 out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
307 if (out == NULL) return -1;
308
309 n = 0;
310 lasts = -1;
311 for (i = 0; semi[i] != NULL; i++)
312 {
313 if (semi[i][0] == '\0') continue;
314 n++;
315 lasts = i;
316 }
317
318 out->dst = strdup(semi[lasts]);
319 if (out->dst == NULL) return -1;
320
321 _syslog_dst_open(out);
322
323 for (i = 0; i < lasts; i++)
324 {
325 if (semi[i][0] == '\0') continue;
326 comma = _explode(semi[i], ",.");
327 lastc = -1;
328 for (j = 0; comma[j] != NULL; j++)
329 {
330 if (comma[j][0] == '\0') continue;
331 lastc = j;
332 }
333
334 for (j = 0; j < lastc; j++)
335 {
336 if (comma[j][0] == '\0') continue;
337 pri = _level_for_name(comma[lastc]);
338 if (pri == -1) continue;
339
340 if (out->count == 0)
341 {
342 out->facility = (char **)calloc(1, sizeof(char *));
343 out->pri = (int *)calloc(1, sizeof(int));
344 }
345 else
346 {
347 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
348 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
349 }
350
351 if (out->facility == NULL) return -1;
352 if (out->pri == NULL) return -1;
353
354 out->facility[out->count] = strdup(comma[j]);
355 if (out->facility[out->count] == NULL) return -1;
356
357 out->pri[out->count] = pri;
358 out->count++;
359 }
360
361 _freeList(comma);
362 }
363
364 _freeList(semi);
365
366 TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
367
368 return 0;
369 }
370
371 static char *
372 bsd_log_string(const char *msg)
373 {
374 uint32_t i, len, outlen;
375 char *out, *q;
376 uint8_t c;
377
378 if (msg == NULL) return NULL;
379
380 len = strlen(msg);
381
382 outlen = len + 1;
383 for (i = 0; i < len; i++)
384 {
385 c = msg[i];
386 if (isascii(c) && iscntrl(c) && (c != '\t')) outlen++;
387 }
388
389 out = malloc(outlen);
390 if (out == NULL) return NULL;
391
392 q = out;
393
394 for (i = 0; i < len; i++)
395 {
396 c = msg[i];
397
398 if (isascii(c) && iscntrl(c))
399 {
400 if (c == '\n')
401 {
402 *q++ = '\\';
403 *q++ = 'n';
404 }
405 else if (c == '\t')
406 {
407 *q++ = c;
408 }
409 else
410 {
411 *q++ = '^';
412 *q++ = c ^ 0100;
413 }
414 }
415 else
416 {
417 *q++ = c;
418 }
419 }
420
421 *q = '\0';
422
423 return out;
424 }
425
426 static int
427 _syslog_send(asl_msg_t *msg, struct config_rule *r, char **out, char **fwd)
428 {
429 char *so, *sf, *vt, *p, *outmsg;
430 const char *vtime, *vhost, *vident, *vpid, *vmsg, *vlevel, *vfacility;
431 size_t outlen, n;
432 int pf, fc, status;
433 FILE *pw;
434 time_t tick;
435
436 if (out == NULL) return -1;
437 if (fwd == NULL) return -1;
438
439 if (r->type == DST_TYPE_NOTE)
440 {
441 notify_post(r->dst+1);
442 return 0;
443 }
444
445 vt = NULL;
446 outmsg = NULL;
447
448 /* Build output string if it hasn't been built by a previous rule-match */
449 if (*out == NULL)
450 {
451 vtime = asl_get(msg, ASL_KEY_TIME);
452 if (vtime != NULL)
453 {
454 tick = asl_parse_time(vtime);
455 if (tick != (time_t)-1)
456 {
457 p = ctime(&tick);
458 vt = malloc(16);
459 if (vt == NULL) return -1;
460
461 memcpy(vt, p+4, 15);
462 vt[15] = '\0';
463 }
464 }
465 else if (strlen(vtime) < 24)
466 {
467 vt = strdup(vtime);
468 if (vt == NULL) return -1;
469 }
470 else
471 {
472 vt = malloc(16);
473 if (vt == NULL) return -1;
474
475 memcpy(vt, vtime+4, 15);
476 vt[15] = '\0';
477 }
478
479 if (vt == NULL)
480 {
481 tick = time(NULL);
482 p = ctime(&tick);
483 vt = malloc(16);
484 if (vt == NULL) return -1;
485
486 memcpy(vt, p+4, 15);
487 vt[15] = '\0';
488 }
489
490 vhost = asl_get(msg, ASL_KEY_HOST);
491 if (vhost == NULL) vhost = "localhost";
492
493 vident = asl_get(msg, ASL_KEY_SENDER);
494 if ((vident != NULL) && (!strcmp(vident, "Unknown"))) vident = NULL;
495
496 vpid = asl_get(msg, ASL_KEY_PID);
497 if ((vpid != NULL) && (!strcmp(vpid, "-1"))) vpid = NULL;
498
499 if ((vpid != NULL) && (vident == NULL)) vident = "Unknown";
500
501 vmsg = asl_get(msg, ASL_KEY_MSG);
502 if (vmsg != NULL) outmsg = bsd_log_string(vmsg);
503
504 n = 0;
505 if (vt != NULL) n += (strlen(vt) + 1);
506 if (vhost != NULL) n += (strlen(vhost) + 1);
507 if (vident != NULL) n += strlen(vident);
508 n += 2;
509 if (vpid != NULL) n += (strlen(vpid) + 2);
510
511 if (outmsg != NULL) n += strlen(outmsg);
512
513 if (n == 0) return -1;
514 n += 2;
515
516 so = calloc(1, n);
517 if (so == NULL) return -1;
518
519 if (vt != NULL)
520 {
521 strcat(so, vt);
522 strcat(so, " ");
523 }
524
525 if (vhost != NULL)
526 {
527 strcat(so, vhost);
528 strcat(so, " ");
529 }
530
531 if (vident != NULL)
532 {
533 strcat(so, vident);
534 if (vpid != NULL)
535 {
536 strcat(so, "[");
537 strcat(so, vpid);
538 strcat(so, "]");
539 }
540 }
541
542 strcat(so, ": ");
543
544 if (outmsg != NULL)
545 {
546 strcat(so, outmsg);
547 free(outmsg);
548 strcat(so, "\n");
549 }
550
551 free(vt);
552 *out = so;
553 }
554
555 if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
556 {
557 pf = 7;
558 vlevel = asl_get(msg, ASL_KEY_LEVEL);
559 if (vlevel != NULL) pf = atoi(vlevel);
560
561 fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY));
562 if (fc > 0) pf |= fc;
563
564 sf = NULL;
565 asprintf(&sf, "<%d>%s", pf, *out);
566 if (sf == NULL) return -1;
567 *fwd = sf;
568 }
569
570 outlen = 0;
571 if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
572 else outlen = strlen(*out);
573
574 if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
575 {
576 /*
577 * Special case for kernel messages.
578 * Don't write kernel messages to /dev/console.
579 * The kernel printf routine already sends them to /dev/console
580 * so writing them here would cause duplicates.
581 */
582 vfacility = asl_get(msg, ASL_KEY_FACILITY);
583 if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) return 0;
584
585 status = write(r->fd, *out, outlen);
586 if ((status < 0) || (status < outlen))
587 {
588 asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
589
590 /* Try re-opening the file (once) and write again */
591 close(r->fd);
592 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT, 0644);
593 if (r->fd < 0)
594 {
595 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
596 return -1;
597 }
598
599 status = write(r->fd, *out, outlen);
600 if ((status < 0) || (status < outlen))
601 {
602 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
603 }
604 }
605 }
606 else if (r->type == DST_TYPE_SOCK)
607 {
608 status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len);
609 if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno));
610 }
611 else if (r->type == DST_TYPE_WALL)
612 {
613 pw = popen(_PATH_WALL, "w");
614 if (pw < 0)
615 {
616 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
617 return -1;
618 }
619
620 fprintf(pw, *out);
621 pclose(pw);
622 }
623
624 return 0;
625 }
626
627 static int
628 _syslog_rule_match(asl_msg_t *msg, struct config_rule *r)
629 {
630 uint32_t i, test, f, pri;
631 const char *val;
632
633 if (msg == NULL) return 0;
634 if (r == NULL) return 0;
635 if (r->count == 0) return 0;
636
637 test = 0;
638
639 for (i = 0; i < r->count; i++)
640 {
641 if (r->pri[i] == -1) continue;
642
643 if ((test == 1) && (r->pri[i] >= 0)) continue;
644 if ((test == 0) && (r->pri[i] == -2)) continue;
645
646 f = 0;
647 if (strcmp(r->facility[i], "*") == 0) f = 1;
648 else
649 {
650 val = asl_get(msg, ASL_KEY_FACILITY);
651 if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0)) f = 1;
652 }
653
654 if (f == 0) continue;
655
656 /* Turn off matching facility with priority "none" */
657 if (r->pri[i] == -2)
658 {
659 test = 0;
660 continue;
661 }
662
663 val = asl_get(msg, ASL_KEY_LEVEL);
664 if (val == NULL) continue;
665
666 pri = atoi(val);
667 if (pri < 0) continue;
668
669 if (pri <= r->pri[i]) test = 1;
670 }
671
672 return test;
673 }
674
675 int
676 bsd_out_sendmsg(asl_msg_t *msg, const char *outid)
677 {
678 struct config_rule *r;
679 char *out, *fwd;
680
681 if (reset != 0)
682 {
683 _do_reset();
684 reset = 0;
685 }
686
687 if (msg == NULL) return -1;
688
689 out = NULL;
690 fwd = NULL;
691
692 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
693 {
694 if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd);
695 }
696
697 if (out != NULL) free(out);
698 if (fwd != NULL) free(fwd);
699
700 return 0;
701 }
702
703 static int
704 _parse_config_file(const char *confname)
705 {
706 FILE *cf;
707 char *line;
708
709 cf = fopen(confname, "r");
710 if (cf == NULL) return 1;
711
712 while (NULL != (line = get_line_from_file(cf)))
713 {
714 _parse_line(line);
715 free(line);
716 }
717
718 fclose(cf);
719
720 return 0;
721 }
722
723 int
724 bsd_out_init(void)
725 {
726 asldebug("%s: init\n", MY_ID);
727
728 TAILQ_INIT(&bsd_out_rule);
729
730 query = asl_new(ASL_TYPE_QUERY);
731 aslevent_addmatch(query, MY_ID);
732 aslevent_addoutput(bsd_out_sendmsg, MY_ID);
733
734 _parse_config_file(_PATH_SYSLOG_CONF);
735 return 0;
736 }
737
738 int
739 bsd_out_reset(void)
740 {
741 reset = 1;
742 return 0;
743 }
744
745 int
746 bsd_out_close(void)
747 {
748 struct config_rule *r, *n;
749 int i;
750
751 n = NULL;
752 for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
753 {
754 n = r->entries.tqe_next;
755
756 if (r->dst != NULL) free(r->dst);
757 if (r->fd > 0) close(r->fd);
758 if (r->addr != NULL) free(r->addr);
759 if (r->facility != NULL)
760 {
761 for (i = 0; i < r->count; i++)
762 {
763 if (r->facility[i] != NULL) free(r->facility[i]);
764 }
765 free(r->facility);
766 }
767 if (r->pri != NULL) free(r->pri);
768
769 TAILQ_REMOVE(&bsd_out_rule, r, entries);
770 free(r);
771 }
772
773 return 0;
774 }