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