]>
git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/bsd_out.c
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
27 #include <sys/socket.h>
41 #define MY_ID "bsd_out"
43 #define _PATH_WALL "/usr/bin/wall"
44 #define ASL_KEY_FACILITY "Facility"
45 #define FACILITY_KERNEL "kern"
46 #define _PATH_CONSOLE "/dev/console"
47 #define IndexNull ((uint32_t)-1)
49 #define DST_TYPE_NONE 0
50 #define DST_TYPE_FILE 1
51 #define DST_TYPE_CONS 2
52 #define DST_TYPE_SOCK 3
53 #define DST_TYPE_WALL 4
54 #define DST_TYPE_NOTE 5
56 static asl_msg_t
*query
= NULL
;
65 struct sockaddr
*addr
;
68 TAILQ_ENTRY(config_rule
) entries
;
71 static TAILQ_HEAD(cr
, config_rule
) bsd_out_rule
;
74 static int _parse_config_file(const char *);
80 _parse_config_file(_PATH_SYSLOG_CONF
);
84 _insertString(char *s
, char **l
, uint32_t x
)
88 if (s
== NULL
) return l
;
91 l
= (char **)malloc(2 * sizeof(char *));
92 if (l
== NULL
) return NULL
;
105 for (i
= 0; l
[i
] != NULL
; i
++);
106 len
= i
+ 1; /* count the NULL on the end of the list too! */
108 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
109 if (l
== NULL
) return NULL
;
111 if ((x
>= (len
- 1)) || (x
== IndexNull
))
113 l
[len
- 1] = strdup(s
);
114 if (l
[len
- 1] == NULL
)
124 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
126 if (l
[x
] == NULL
) return NULL
;
132 _explode(char *s
, char *delim
)
138 if (s
== NULL
) return NULL
;
143 for (i
= 0; ((p
[i
] != '\0') && (strchr(delim
, p
[i
]) == NULL
)); i
++);
146 if (t
== NULL
) return NULL
;
148 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
150 l
= _insertString(t
, l
, IndexNull
);
153 if (p
[i
] == '\0') return l
;
154 if (p
[i
+ 1] == '\0') l
= _insertString("", l
, IndexNull
);
166 if (l
== NULL
) return;
167 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
172 _level_for_name(const char *name
)
174 if (name
== NULL
) return -1;
176 if (!strcasecmp(name
, "emerg")) return ASL_LEVEL_EMERG
;
177 if (!strcasecmp(name
, "panic")) return ASL_LEVEL_EMERG
;
178 if (!strcasecmp(name
, "alert")) return ASL_LEVEL_ALERT
;
179 if (!strcasecmp(name
, "crit")) return ASL_LEVEL_CRIT
;
180 if (!strcasecmp(name
, "err")) return ASL_LEVEL_ERR
;
181 if (!strcasecmp(name
, "error")) return ASL_LEVEL_ERR
;
182 if (!strcasecmp(name
, "warn")) return ASL_LEVEL_WARNING
;
183 if (!strcasecmp(name
, "warning")) return ASL_LEVEL_WARNING
;
184 if (!strcasecmp(name
, "notice")) return ASL_LEVEL_NOTICE
;
185 if (!strcasecmp(name
, "info")) return ASL_LEVEL_INFO
;
186 if (!strcasecmp(name
, "debug")) return ASL_LEVEL_DEBUG
;
187 if (!strcmp(name
, "*")) return ASL_LEVEL_DEBUG
;
190 if (!strcasecmp(name
, "none")) return -2;
196 _syslog_dst_open(struct config_rule
*r
)
200 struct addrinfo hints
, *gai
, *ai
;
202 if (r
== NULL
) return -1;
206 if (r
->dst
[0] == '/')
208 r
->fd
= open(r
->dst
, O_WRONLY
| O_APPEND
| O_CREAT
, 0644);
211 asldebug("%s: open failed for file: %s (%s)\n", MY_ID
, r
->dst
, strerror(errno
));
215 r
->type
= DST_TYPE_FILE
;
216 if (!strcmp(r
->dst
, _PATH_CONSOLE
)) r
->type
= DST_TYPE_CONS
;
221 if (r
->dst
[0] == '!')
223 r
->type
= DST_TYPE_NOTE
;
227 if (r
->dst
[0] == '@')
229 node
= strdup(r
->dst
+ 1);
230 if (node
== NULL
) return -1;
233 serv
= strrchr(node
, ':');
234 if (serv
!= NULL
) *serv
++ = '\0';
235 else serv
= "syslog";
237 memset(&hints
, 0, sizeof(hints
));
238 hints
.ai_family
= PF_UNSPEC
;
239 hints
.ai_socktype
= SOCK_DGRAM
;
240 i
= getaddrinfo(node
, serv
, &hints
, &gai
);
244 asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID
, node
, serv
, gai_strerror(i
));
248 for (ai
= gai
; ai
!= NULL
; ai
= ai
->ai_next
)
250 r
->fd
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
251 if (r
->fd
< 0) continue;
253 r
->addr
= (struct sockaddr
*)calloc(1, ai
->ai_addrlen
);
254 if (r
->addr
== NULL
) return -1;
256 memcpy(r
->addr
, ai
->ai_addr
, ai
->ai_addrlen
);
265 asldebug("%s: connection failed for %s\n", MY_ID
, (r
->dst
) + 1);
269 if (fcntl(r
->fd
, F_SETFL
, O_NONBLOCK
) < 0)
273 asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID
, r
->fd
, strerror(errno
));
277 r
->type
= DST_TYPE_SOCK
;
282 if (strcmp(r
->dst
, "*") == 0)
284 r
->type
= DST_TYPE_WALL
;
288 /* Can't deal with dst! */
289 asldebug("%s: unsupported / unknown output name: %s\n", MY_ID
, r
->dst
);
296 char **semi
, **comma
;
297 int i
, j
, n
, lasts
, lastc
, pri
;
298 struct config_rule
*out
;
300 if (s
== NULL
) return -1;
301 while ((*s
== ' ') || (*s
== '\t')) s
++;
302 if (*s
== '#') return -1;
304 semi
= _explode(s
, "; \t");
306 if (semi
== NULL
) return -1;
307 out
= (struct config_rule
*)calloc(1, sizeof(struct config_rule
));
308 if (out
== NULL
) return -1;
312 for (i
= 0; semi
[i
] != NULL
; i
++)
314 if (semi
[i
][0] == '\0') continue;
319 out
->dst
= strdup(semi
[lasts
]);
320 if (out
->dst
== NULL
) return -1;
322 _syslog_dst_open(out
);
324 for (i
= 0; i
< lasts
; i
++)
326 if (semi
[i
][0] == '\0') continue;
327 comma
= _explode(semi
[i
], ",.");
329 for (j
= 0; comma
[j
] != NULL
; j
++)
331 if (comma
[j
][0] == '\0') continue;
335 for (j
= 0; j
< lastc
; j
++)
337 if (comma
[j
][0] == '\0') continue;
338 pri
= _level_for_name(comma
[lastc
]);
339 if (pri
== -1) continue;
343 out
->facility
= (char **)calloc(1, sizeof(char *));
344 out
->pri
= (int *)calloc(1, sizeof(int));
348 out
->facility
= (char **)reallocf(out
->facility
, (out
->count
+ 1) * sizeof(char *));
349 out
->pri
= (int *)reallocf(out
->pri
, (out
->count
+ 1) * sizeof(int));
352 if (out
->facility
== NULL
) return -1;
353 if (out
->pri
== NULL
) return -1;
355 out
->facility
[out
->count
] = strdup(comma
[j
]);
356 if (out
->facility
[out
->count
] == NULL
) return -1;
358 out
->pri
[out
->count
] = pri
;
367 TAILQ_INSERT_TAIL(&bsd_out_rule
, out
, entries
);
373 bsd_log_string(const char *msg
)
375 uint32_t i
, len
, outlen
;
379 if (msg
== NULL
) return NULL
;
384 for (i
= 0; i
< len
; i
++)
387 if (isascii(c
) && iscntrl(c
) && (c
!= '\t')) outlen
++;
390 out
= malloc(outlen
);
391 if (out
== NULL
) return NULL
;
395 for (i
= 0; i
< len
; i
++)
399 if (isascii(c
) && iscntrl(c
))
428 _syslog_send(asl_msg_t
*msg
, struct config_rule
*r
, char **out
, char **fwd
)
430 char *so
, *sf
, *vt
, *p
, *outmsg
;
431 const char *vtime
, *vhost
, *vident
, *vpid
, *vmsg
, *vlevel
, *vfacility
;
437 if (out
== NULL
) return -1;
438 if (fwd
== NULL
) return -1;
440 if (r
->type
== DST_TYPE_NOTE
)
442 notify_post(r
->dst
+1);
449 /* Build output string if it hasn't been built by a previous rule-match */
452 vtime
= asl_get(msg
, ASL_KEY_TIME
);
455 tick
= asl_parse_time(vtime
);
456 if (tick
!= (time_t)-1)
460 if (vt
== NULL
) return -1;
466 else if (strlen(vtime
) < 24)
469 if (vt
== NULL
) return -1;
474 if (vt
== NULL
) return -1;
476 memcpy(vt
, vtime
+4, 15);
485 if (vt
== NULL
) return -1;
491 vhost
= asl_get(msg
, ASL_KEY_HOST
);
492 if (vhost
== NULL
) vhost
= "localhost";
494 vident
= asl_get(msg
, ASL_KEY_SENDER
);
495 if ((vident
!= NULL
) && (!strcmp(vident
, "Unknown"))) vident
= NULL
;
497 vpid
= asl_get(msg
, ASL_KEY_PID
);
498 if ((vpid
!= NULL
) && (!strcmp(vpid
, "-1"))) vpid
= NULL
;
500 if ((vpid
!= NULL
) && (vident
== NULL
)) vident
= "Unknown";
502 vmsg
= asl_get(msg
, ASL_KEY_MSG
);
503 if (vmsg
!= NULL
) outmsg
= bsd_log_string(vmsg
);
506 if (vt
!= NULL
) n
+= (strlen(vt
) + 1);
507 if (vhost
!= NULL
) n
+= (strlen(vhost
) + 1);
508 if (vident
!= NULL
) n
+= strlen(vident
);
510 if (vpid
!= NULL
) n
+= (strlen(vpid
) + 2);
512 if (outmsg
!= NULL
) n
+= strlen(outmsg
);
514 if (n
== 0) return -1;
518 if (so
== NULL
) return -1;
556 if ((*fwd
== NULL
) && (r
->type
== DST_TYPE_SOCK
))
559 vlevel
= asl_get(msg
, ASL_KEY_LEVEL
);
560 if (vlevel
!= NULL
) pf
= atoi(vlevel
);
562 fc
= asl_syslog_faciliy_name_to_num(asl_get(msg
, ASL_KEY_FACILITY
));
563 if (fc
> 0) pf
|= fc
;
566 asprintf(&sf
, "<%d>%s", pf
, *out
);
567 if (sf
== NULL
) return -1;
572 if (r
->type
== DST_TYPE_SOCK
) outlen
= strlen(*fwd
);
573 else outlen
= strlen(*out
);
575 if ((r
->type
== DST_TYPE_FILE
) || (r
->type
== DST_TYPE_CONS
))
578 * Special case for kernel messages.
579 * Don't write kernel messages to /dev/console.
580 * The kernel printf routine already sends them to /dev/console
581 * so writing them here would cause duplicates.
583 vfacility
= asl_get(msg
, ASL_KEY_FACILITY
);
584 if ((vfacility
!= NULL
) && (!strcmp(vfacility
, FACILITY_KERNEL
)) && (r
->type
== DST_TYPE_CONS
)) return 0;
586 status
= write(r
->fd
, *out
, outlen
);
587 if ((status
< 0) || (status
< outlen
))
589 asldebug("%s: error writing message (%s): %s\n", MY_ID
, r
->dst
, strerror(errno
));
591 /* Try re-opening the file (once) and write again */
593 r
->fd
= open(r
->dst
, O_WRONLY
| O_APPEND
| O_CREAT
, 0644);
596 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID
, r
->dst
, strerror(errno
));
600 status
= write(r
->fd
, *out
, outlen
);
601 if ((status
< 0) || (status
< outlen
))
603 asldebug("%s: error re-writing message (%s): %s\n", MY_ID
, r
->dst
, strerror(errno
));
607 else if (r
->type
== DST_TYPE_SOCK
)
609 status
= sendto(r
->fd
, *fwd
, outlen
, 0, r
->addr
, r
->addr
->sa_len
);
610 if (status
< 0) asldebug("%s: error sending message (%s): %s\n", MY_ID
, r
->dst
, strerror(errno
));
612 else if (r
->type
== DST_TYPE_WALL
)
614 pw
= popen(_PATH_WALL
, "w");
617 asldebug("%s: error sending wall message: %s\n", MY_ID
, strerror(errno
));
629 _syslog_rule_match(asl_msg_t
*msg
, struct config_rule
*r
)
631 uint32_t i
, test
, f
, pri
;
634 if (msg
== NULL
) return 0;
635 if (r
== NULL
) return 0;
636 if (r
->count
== 0) return 0;
640 for (i
= 0; i
< r
->count
; i
++)
642 if (r
->pri
[i
] == -1) continue;
644 if ((test
== 1) && (r
->pri
[i
] >= 0)) continue;
645 if ((test
== 0) && (r
->pri
[i
] == -2)) continue;
648 if (strcmp(r
->facility
[i
], "*") == 0) f
= 1;
651 val
= asl_get(msg
, ASL_KEY_FACILITY
);
652 if ((val
!= NULL
) && (strcasecmp(r
->facility
[i
], val
) == 0)) f
= 1;
655 if (f
== 0) continue;
657 /* Turn off matching facility with priority "none" */
664 val
= asl_get(msg
, ASL_KEY_LEVEL
);
665 if (val
== NULL
) continue;
668 if (pri
< 0) continue;
670 if (pri
<= r
->pri
[i
]) test
= 1;
677 bsd_out_sendmsg(asl_msg_t
*msg
, const char *outid
)
679 struct config_rule
*r
;
688 if (msg
== NULL
) return -1;
693 for (r
= bsd_out_rule
.tqh_first
; r
!= NULL
; r
= r
->entries
.tqe_next
)
695 if (_syslog_rule_match(msg
, r
) == 1) _syslog_send(msg
, r
, &out
, &fwd
);
698 if (out
!= NULL
) free(out
);
699 if (fwd
!= NULL
) free(fwd
);
705 _parse_config_file(const char *confname
)
710 cf
= fopen(confname
, "r");
711 if (cf
== NULL
) return 1;
713 while (NULL
!= (line
= get_line_from_file(cf
)))
727 asldebug("%s: init\n", MY_ID
);
729 TAILQ_INIT(&bsd_out_rule
);
731 query
= asl_new(ASL_TYPE_QUERY
);
732 aslevent_addmatch(query
, MY_ID
);
733 aslevent_addoutput(bsd_out_sendmsg
, MY_ID
);
735 _parse_config_file(_PATH_SYSLOG_CONF
);
749 struct config_rule
*r
, *n
;
753 for (r
= bsd_out_rule
.tqh_first
; r
!= NULL
; r
= n
)
755 n
= r
->entries
.tqe_next
;
757 if (r
->dst
!= NULL
) free(r
->dst
);
758 if (r
->fd
> 0) close(r
->fd
);
759 if (r
->addr
!= NULL
) free(r
->addr
);
760 if (r
->facility
!= NULL
)
762 for (i
= 0; i
< r
->count
; i
++)
764 if (r
->facility
[i
] != NULL
) free(r
->facility
[i
]);
768 if (r
->pri
!= NULL
) free(r
->pri
);
770 TAILQ_REMOVE(&bsd_out_rule
, r
, entries
);