]>
Commit | Line | Data |
---|---|---|
b16a592a A |
1 | /* |
2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
02b408bf A |
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. | |
b16a592a A |
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, | |
02b408bf A |
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. | |
b16a592a A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <sys/types.h> | |
25 | #include <sys/stat.h> | |
26 | #include <sys/fcntl.h> | |
27 | #include <sys/socket.h> | |
28 | #include <sys/un.h> | |
29 | #include <sys/uio.h> | |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <string.h> | |
33 | #include <unistd.h> | |
34 | #include <errno.h> | |
35 | #include "daemon.h" | |
36 | ||
37 | #define forever for(;;) | |
38 | ||
39 | #define MY_ID "asl" | |
40 | ||
41 | static int sock = -1; | |
42 | static FILE *aslfile = NULL; | |
43 | static asl_msg_t *query = NULL; | |
44 | ||
45 | extern int asl_log_filter; | |
46 | ||
47 | #define MATCH_EOF -1 | |
48 | #define MATCH_NULL 0 | |
49 | #define MATCH_TRUE 1 | |
50 | #define MATCH_FALSE 2 | |
51 | ||
52 | extern int prune; | |
53 | ||
54 | static int filter_token = -1; | |
55 | ||
56 | struct prune_query_entry | |
57 | { | |
58 | asl_msg_t *query; | |
59 | TAILQ_ENTRY(prune_query_entry) entries; | |
60 | }; | |
61 | ||
62 | static TAILQ_HEAD(pql, prune_query_entry) pquery; | |
63 | ||
64 | static int | |
65 | _search_next(FILE *log, char **outstr) | |
66 | { | |
67 | char *str; | |
68 | aslmsg m; | |
69 | int match, i; | |
70 | struct prune_query_entry *p; | |
71 | ||
72 | *outstr = NULL; | |
73 | ||
74 | if (log == NULL) return MATCH_EOF; | |
75 | ||
76 | str = get_line_from_file(log); | |
77 | if (str == NULL) return MATCH_EOF; | |
78 | ||
79 | m = asl_msg_from_string(str); | |
80 | if (m == NULL) | |
81 | { | |
82 | free(str); | |
83 | return MATCH_NULL; | |
84 | } | |
85 | ||
86 | *outstr = str; | |
87 | ||
88 | for (i = 0, p = pquery.tqh_first; p != NULL; p = p->entries.tqe_next, i++) | |
89 | { | |
90 | match = asl_msg_cmp(p->query, m); | |
91 | if (match == 1) | |
92 | { | |
93 | asl_free(m); | |
94 | return MATCH_TRUE; | |
95 | } | |
96 | } | |
97 | ||
98 | asl_free(m); | |
99 | return MATCH_FALSE; | |
100 | } | |
101 | ||
102 | /* | |
103 | * Pruning the main output file (asl.log) | |
104 | * | |
105 | * The prune file (_PATH_ASL_PRUNE) is set up by the syslog command-line utiliy. | |
106 | * It contains a set of queries. The main output file is read, and each | |
107 | * message is matched against these queries. If any query matches, we save | |
108 | * that message. Anything that doesn't match is discarded. | |
109 | * | |
110 | */ | |
111 | int | |
112 | asl_prune(asl_msg_t *inq) | |
113 | { | |
114 | char *pname, *str; | |
115 | FILE *pfile, *qfile; | |
116 | struct prune_query_entry *p, *n; | |
117 | asl_msg_t *q; | |
118 | int status, incount, outcount; | |
119 | ||
120 | asldebug("syslogd: pruning %s\n", _PATH_ASL_OUT); | |
121 | ||
122 | if (inq != NULL) | |
123 | { | |
124 | TAILQ_INIT(&pquery); | |
125 | p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry)); | |
126 | if (p == NULL) return -1; | |
127 | ||
128 | p->query = inq; | |
129 | TAILQ_INSERT_TAIL(&pquery, p, entries); | |
130 | } | |
131 | else | |
132 | { | |
133 | qfile = fopen(_PATH_ASL_PRUNE, "r"); | |
134 | if (qfile == NULL) | |
135 | { | |
136 | asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_PRUNE, strerror(errno)); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | TAILQ_INIT(&pquery); | |
141 | ||
142 | forever | |
143 | { | |
144 | str = get_line_from_file(qfile); | |
145 | if (str == NULL) break; | |
146 | ||
147 | q = asl_msg_from_string(str); | |
148 | asldebug("syslogd: prune line %s\n", str); | |
149 | ||
150 | free(str); | |
151 | if (q == NULL) continue; | |
152 | ||
153 | if (q->type != ASL_TYPE_QUERY) | |
154 | { | |
155 | asl_free(q); | |
156 | continue; | |
157 | } | |
158 | ||
159 | p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry)); | |
160 | if (p == NULL) return -1; | |
161 | ||
162 | p->query = q; | |
163 | TAILQ_INSERT_TAIL(&pquery, p, entries); | |
164 | } | |
165 | } | |
166 | ||
167 | pname = NULL; | |
168 | asprintf(&pname, "%s.%d", _PATH_ASL_OUT, getpid()); | |
169 | if (pname == NULL) return -1; | |
170 | ||
171 | pfile = fopen(pname, "w"); | |
172 | if (pfile == NULL) | |
173 | { | |
174 | asldebug("syslogd: can't write %s: %s\n", pname, strerror(errno)); | |
175 | free(pname); | |
176 | return -1; | |
177 | } | |
178 | ||
179 | fclose(aslfile); | |
180 | aslfile = fopen(_PATH_ASL_OUT, "r"); | |
181 | if (aslfile == NULL) | |
182 | { | |
183 | asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_OUT, strerror(errno)); | |
184 | free(pname); | |
185 | aslfile = fopen(_PATH_ASL_OUT, "a"); | |
186 | return -1; | |
187 | } | |
188 | ||
189 | incount = 0; | |
190 | outcount = 0; | |
191 | ||
192 | do | |
193 | { | |
194 | str = NULL; | |
195 | incount++; | |
196 | status = _search_next(aslfile, &str); | |
197 | ||
198 | /* | |
199 | * Pruning deletes records that match the search. | |
200 | * If the match fails, we keep the record. | |
201 | */ | |
202 | if (status == MATCH_FALSE) | |
203 | { | |
204 | outcount++; | |
205 | fprintf(pfile, "%s\n", str); | |
206 | } | |
207 | ||
208 | if (str != NULL) free(str); | |
209 | } | |
210 | while (status != MATCH_EOF); | |
211 | ||
212 | fclose(pfile); | |
213 | fclose(aslfile); | |
214 | ||
215 | unlink(_PATH_ASL_OUT); | |
216 | rename(pname, _PATH_ASL_OUT); | |
217 | free(pname); | |
218 | unlink(_PATH_ASL_PRUNE); | |
219 | aslfile = fopen(_PATH_ASL_OUT, "a"); | |
220 | ||
221 | n = NULL; | |
222 | for (p = pquery.tqh_first; p != NULL; p = n) | |
223 | { | |
224 | n = p->entries.tqe_next; | |
225 | ||
226 | if (p->query != NULL) asl_free(p->query); | |
227 | ||
228 | TAILQ_REMOVE(&pquery, p, entries); | |
229 | free(p); | |
230 | } | |
231 | ||
232 | asldebug("syslogd: prune %d records in, %d records out\n", incount, outcount); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | asl_msg_t * | |
238 | asl_in_getmsg(int fd) | |
239 | { | |
240 | char *out; | |
241 | asl_msg_t *m; | |
242 | uint32_t len, n; | |
243 | char ls[16]; | |
244 | ||
245 | n = read(fd, ls, 11); | |
246 | if (n < 11) | |
247 | { | |
248 | if (n <= 0) | |
249 | { | |
250 | asldebug("%s: read error (len): %s\n", MY_ID, strerror(errno)); | |
251 | if (errno != EINTR) | |
252 | { | |
253 | close(fd); | |
254 | aslevent_removefd(fd); | |
255 | return NULL; | |
256 | } | |
257 | } | |
258 | ||
259 | return NULL; | |
260 | } | |
261 | ||
262 | len = atoi(ls); | |
263 | asldebug("%s: expecting message length %d bytes\n", MY_ID, len); | |
264 | out = malloc(len); | |
265 | if (out == NULL) return NULL; | |
266 | ||
267 | n = read(fd, out, len); | |
268 | if (n < len) | |
269 | { | |
270 | if (n <= 0) | |
271 | { | |
272 | asldebug("%s: read error (body): %s\n", MY_ID, strerror(errno)); | |
273 | if (errno != EINTR) | |
274 | { | |
275 | close(fd); | |
276 | aslevent_removefd(fd); | |
277 | free(out); | |
278 | return NULL; | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | m = asl_msg_from_string(out); | |
284 | free(out); | |
285 | return m; | |
286 | } | |
287 | ||
288 | asl_msg_t * | |
289 | asl_in_acceptmsg(int fd) | |
290 | { | |
291 | int clientfd; | |
292 | ||
293 | asldebug("%s: accepting message\n", MY_ID); | |
294 | clientfd = accept(fd, NULL, 0); | |
295 | if (clientfd < 0) | |
296 | { | |
297 | asldebug("%s: error accepting socket fd %d: %s\n", MY_ID, fd, strerror(errno)); | |
298 | return NULL; | |
299 | } | |
300 | ||
301 | if (fcntl(clientfd, F_SETFL, O_NONBLOCK) < 0) | |
302 | { | |
303 | close(clientfd); | |
304 | clientfd = -1; | |
305 | asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, clientfd, strerror(errno)); | |
306 | return NULL; | |
307 | } | |
308 | ||
309 | aslevent_addfd(clientfd, asl_in_getmsg, NULL, NULL); | |
310 | return NULL; | |
311 | } | |
312 | ||
313 | int | |
314 | aslmod_sendmsg(asl_msg_t *msg, const char *outid) | |
315 | { | |
316 | const char *vlevel; | |
317 | char *mstr; | |
318 | uint32_t n, lmask; | |
319 | int status, x, level; | |
320 | ||
321 | if (aslfile == NULL) return -1; | |
322 | ||
323 | /* set up com.apple.syslog.asl_filter */ | |
324 | if (filter_token == -1) | |
325 | { | |
326 | status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token); | |
327 | if (status != NOTIFY_STATUS_OK) | |
328 | { | |
329 | filter_token = -1; | |
330 | } | |
331 | else | |
332 | { | |
333 | status = notify_check(filter_token, &x); | |
334 | if (status == NOTIFY_STATUS_OK) status = notify_set_state(filter_token, asl_log_filter); | |
335 | if (status != NOTIFY_STATUS_OK) | |
336 | { | |
337 | notify_cancel(filter_token); | |
338 | filter_token = -1; | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | if (filter_token >= 0) | |
344 | { | |
345 | x = 1; | |
346 | status = notify_check(filter_token, &x); | |
347 | if ((status == NOTIFY_STATUS_OK) && (x == 1)) | |
348 | { | |
349 | x = asl_log_filter; | |
350 | status = notify_get_state(filter_token, &x); | |
351 | if ((status == NOTIFY_STATUS_OK) && (x != 0)) asl_log_filter = x; | |
352 | } | |
353 | } | |
354 | ||
355 | vlevel = asl_get(msg, ASL_KEY_LEVEL); | |
356 | level = 7; | |
357 | if (vlevel != NULL) level = atoi(vlevel); | |
358 | lmask = ASL_FILTER_MASK(level); | |
359 | if ((lmask & asl_log_filter) == 0) return 0; | |
360 | ||
361 | mstr = asl_msg_to_string(msg, &n); | |
362 | if (mstr != NULL) | |
363 | { | |
364 | fprintf(aslfile, "%s\n", mstr); | |
365 | fflush(aslfile); | |
366 | free(mstr); | |
367 | } | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
372 | int | |
373 | asl_in_init(void) | |
374 | { | |
375 | struct sockaddr_un sun; | |
376 | int rbufsize; | |
377 | int len; | |
378 | ||
379 | asldebug("%s: init\n", MY_ID); | |
380 | if (sock >= 0) return sock; | |
381 | ||
382 | unlink(_PATH_ASL_IN); | |
383 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | |
384 | if (sock < 0) | |
385 | { | |
386 | asldebug("%s: couldn't create socket for %s: %s\n", MY_ID, _PATH_ASL_IN, strerror(errno)); | |
387 | return -1; | |
388 | } | |
389 | ||
390 | asldebug("%s: creating %s for fd %d\n", MY_ID, _PATH_ASL_IN, sock); | |
391 | ||
392 | memset(&sun, 0, sizeof(sun)); | |
393 | sun.sun_family = AF_UNIX; | |
394 | strcpy(sun.sun_path, _PATH_ASL_IN); | |
395 | ||
396 | len = sizeof(struct sockaddr_un); | |
397 | if (bind(sock, (struct sockaddr *)&sun, len) < 0) | |
398 | { | |
399 | asldebug("%s: couldn't bind socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); | |
400 | close(sock); | |
401 | sock = -1; | |
402 | return -1; | |
403 | } | |
404 | ||
405 | rbufsize = 128 * 1024; | |
406 | len = sizeof(rbufsize); | |
407 | ||
408 | if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0) | |
409 | { | |
410 | asldebug("%s: couldn't set receive buffer size for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); | |
411 | close(sock); | |
412 | sock = -1; | |
413 | return -1; | |
414 | } | |
415 | ||
416 | if (listen(sock, SOMAXCONN) < 0) | |
417 | { | |
418 | asldebug("%s: couldn't listen on socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); | |
419 | close(sock); | |
420 | sock = -1; | |
421 | unlink(_PATH_ASL_IN); | |
422 | return -1; | |
423 | } | |
424 | ||
425 | if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) | |
426 | { | |
427 | asldebug("%s: couldn't set O_NONBLOCK for socket %d (%s): %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); | |
428 | close(sock); | |
429 | sock = -1; | |
430 | return -1; | |
431 | } | |
432 | ||
433 | chmod(_PATH_ASL_IN, 0666); | |
434 | ||
435 | /* Add logger routine for main output file */ | |
436 | aslfile = fopen(_PATH_ASL_OUT, "a"); | |
437 | if (aslfile != NULL) | |
438 | { | |
439 | query = asl_new(ASL_TYPE_QUERY); | |
440 | aslevent_addmatch(query, MY_ID); | |
441 | aslevent_addoutput(aslmod_sendmsg, MY_ID); | |
442 | } | |
443 | ||
444 | return aslevent_addfd(sock, asl_in_acceptmsg, NULL, NULL); | |
445 | } | |
446 | ||
447 | int | |
448 | asl_in_reset(void) | |
449 | { | |
450 | return 0; | |
451 | } | |
452 | ||
453 | int | |
454 | asl_in_close(void) | |
455 | { | |
456 | if (sock < 0) return 1; | |
457 | ||
458 | if (filter_token >= 0) notify_cancel(filter_token); | |
459 | filter_token = -1; | |
460 | asl_log_filter = 0; | |
461 | ||
462 | asl_free(query); | |
463 | close(sock); | |
464 | if (aslfile != NULL) fclose(aslfile); | |
465 | unlink(_PATH_ASL_IN); | |
466 | ||
467 | return 0; | |
468 | } |