/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include "daemon.h" #define forever for(;;) #define MY_ID "asl" static int sock = -1; static FILE *aslfile = NULL; static asl_msg_t *query = NULL; extern int asl_log_filter; #define MATCH_EOF -1 #define MATCH_NULL 0 #define MATCH_TRUE 1 #define MATCH_FALSE 2 extern int prune; static int filter_token = -1; struct prune_query_entry { asl_msg_t *query; TAILQ_ENTRY(prune_query_entry) entries; }; static TAILQ_HEAD(pql, prune_query_entry) pquery; static int _search_next(FILE *log, char **outstr) { char *str; aslmsg m; int match, i; struct prune_query_entry *p; *outstr = NULL; if (log == NULL) return MATCH_EOF; str = get_line_from_file(log); if (str == NULL) return MATCH_EOF; m = asl_msg_from_string(str); if (m == NULL) { free(str); return MATCH_NULL; } *outstr = str; for (i = 0, p = pquery.tqh_first; p != NULL; p = p->entries.tqe_next, i++) { match = asl_msg_cmp(p->query, m); if (match == 1) { asl_free(m); return MATCH_TRUE; } } asl_free(m); return MATCH_FALSE; } /* * Pruning the main output file (asl.log) * * The prune file (_PATH_ASL_PRUNE) is set up by the syslog command-line utiliy. * It contains a set of queries. The main output file is read, and each * message is matched against these queries. If any query matches, we save * that message. Anything that doesn't match is discarded. * */ int asl_prune(asl_msg_t *inq) { char *pname, *str; FILE *pfile, *qfile; struct prune_query_entry *p, *n; asl_msg_t *q; int status, incount, outcount; asldebug("syslogd: pruning %s\n", _PATH_ASL_OUT); if (inq != NULL) { TAILQ_INIT(&pquery); p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry)); if (p == NULL) return -1; p->query = inq; TAILQ_INSERT_TAIL(&pquery, p, entries); } else { qfile = fopen(_PATH_ASL_PRUNE, "r"); if (qfile == NULL) { asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_PRUNE, strerror(errno)); return 0; } TAILQ_INIT(&pquery); forever { str = get_line_from_file(qfile); if (str == NULL) break; q = asl_msg_from_string(str); asldebug("syslogd: prune line %s\n", str); free(str); if (q == NULL) continue; if (q->type != ASL_TYPE_QUERY) { asl_free(q); continue; } p = (struct prune_query_entry *)calloc(1, sizeof(struct prune_query_entry)); if (p == NULL) return -1; p->query = q; TAILQ_INSERT_TAIL(&pquery, p, entries); } } pname = NULL; asprintf(&pname, "%s.%d", _PATH_ASL_OUT, getpid()); if (pname == NULL) return -1; pfile = fopen(pname, "w"); if (pfile == NULL) { asldebug("syslogd: can't write %s: %s\n", pname, strerror(errno)); free(pname); return -1; } fclose(aslfile); aslfile = fopen(_PATH_ASL_OUT, "r"); if (aslfile == NULL) { asldebug("syslogd: can't read %s: %s\n", _PATH_ASL_OUT, strerror(errno)); free(pname); aslfile = fopen(_PATH_ASL_OUT, "a"); return -1; } incount = 0; outcount = 0; do { str = NULL; incount++; status = _search_next(aslfile, &str); /* * Pruning deletes records that match the search. * If the match fails, we keep the record. */ if (status == MATCH_FALSE) { outcount++; fprintf(pfile, "%s\n", str); } if (str != NULL) free(str); } while (status != MATCH_EOF); fclose(pfile); fclose(aslfile); unlink(_PATH_ASL_OUT); rename(pname, _PATH_ASL_OUT); free(pname); unlink(_PATH_ASL_PRUNE); aslfile = fopen(_PATH_ASL_OUT, "a"); n = NULL; for (p = pquery.tqh_first; p != NULL; p = n) { n = p->entries.tqe_next; if (p->query != NULL) asl_free(p->query); TAILQ_REMOVE(&pquery, p, entries); free(p); } asldebug("syslogd: prune %d records in, %d records out\n", incount, outcount); return 0; } asl_msg_t * asl_in_getmsg(int fd) { char *out; asl_msg_t *m; uint32_t len, n; char ls[16]; n = read(fd, ls, 11); if (n < 11) { if (n <= 0) { asldebug("%s: read error (len): %s\n", MY_ID, strerror(errno)); if (errno != EINTR) { close(fd); aslevent_removefd(fd); return NULL; } } return NULL; } len = atoi(ls); asldebug("%s: expecting message length %d bytes\n", MY_ID, len); out = malloc(len); if (out == NULL) return NULL; n = read(fd, out, len); if (n < len) { if (n <= 0) { asldebug("%s: read error (body): %s\n", MY_ID, strerror(errno)); if (errno != EINTR) { close(fd); aslevent_removefd(fd); free(out); return NULL; } } } m = asl_msg_from_string(out); free(out); return m; } asl_msg_t * asl_in_acceptmsg(int fd) { int clientfd; asldebug("%s: accepting message\n", MY_ID); clientfd = accept(fd, NULL, 0); if (clientfd < 0) { asldebug("%s: error accepting socket fd %d: %s\n", MY_ID, fd, strerror(errno)); return NULL; } if (fcntl(clientfd, F_SETFL, O_NONBLOCK) < 0) { close(clientfd); clientfd = -1; asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, clientfd, strerror(errno)); return NULL; } aslevent_addfd(clientfd, asl_in_getmsg, NULL, NULL); return NULL; } int aslmod_sendmsg(asl_msg_t *msg, const char *outid) { const char *vlevel; char *mstr; uint32_t n, lmask; int status, x, level; if (aslfile == NULL) return -1; /* set up com.apple.syslog.asl_filter */ if (filter_token == -1) { status = notify_register_check(NOTIFY_SYSTEM_ASL_FILTER, &filter_token); if (status != NOTIFY_STATUS_OK) { filter_token = -1; } else { status = notify_check(filter_token, &x); if (status == NOTIFY_STATUS_OK) status = notify_set_state(filter_token, asl_log_filter); if (status != NOTIFY_STATUS_OK) { notify_cancel(filter_token); filter_token = -1; } } } if (filter_token >= 0) { x = 1; status = notify_check(filter_token, &x); if ((status == NOTIFY_STATUS_OK) && (x == 1)) { x = asl_log_filter; status = notify_get_state(filter_token, &x); if ((status == NOTIFY_STATUS_OK) && (x != 0)) asl_log_filter = x; } } vlevel = asl_get(msg, ASL_KEY_LEVEL); level = 7; if (vlevel != NULL) level = atoi(vlevel); lmask = ASL_FILTER_MASK(level); if ((lmask & asl_log_filter) == 0) return 0; mstr = asl_msg_to_string(msg, &n); if (mstr != NULL) { fprintf(aslfile, "%s\n", mstr); fflush(aslfile); free(mstr); } return 0; } int asl_in_init(void) { struct sockaddr_un sun; int rbufsize; int len; asldebug("%s: init\n", MY_ID); if (sock >= 0) return sock; unlink(_PATH_ASL_IN); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { asldebug("%s: couldn't create socket for %s: %s\n", MY_ID, _PATH_ASL_IN, strerror(errno)); return -1; } asldebug("%s: creating %s for fd %d\n", MY_ID, _PATH_ASL_IN, sock); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strcpy(sun.sun_path, _PATH_ASL_IN); len = sizeof(struct sockaddr_un); if (bind(sock, (struct sockaddr *)&sun, len) < 0) { asldebug("%s: couldn't bind socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); close(sock); sock = -1; return -1; } rbufsize = 128 * 1024; len = sizeof(rbufsize); if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbufsize, len) < 0) { asldebug("%s: couldn't set receive buffer size for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); close(sock); sock = -1; return -1; } if (listen(sock, SOMAXCONN) < 0) { asldebug("%s: couldn't listen on socket %d for %s: %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); close(sock); sock = -1; unlink(_PATH_ASL_IN); return -1; } if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { asldebug("%s: couldn't set O_NONBLOCK for socket %d (%s): %s\n", MY_ID, sock, _PATH_ASL_IN, strerror(errno)); close(sock); sock = -1; return -1; } chmod(_PATH_ASL_IN, 0666); /* Add logger routine for main output file */ aslfile = fopen(_PATH_ASL_OUT, "a"); if (aslfile != NULL) { query = asl_new(ASL_TYPE_QUERY); aslevent_addmatch(query, MY_ID); aslevent_addoutput(aslmod_sendmsg, MY_ID); } return aslevent_addfd(sock, asl_in_acceptmsg, NULL, NULL); } int asl_in_reset(void) { return 0; } int asl_in_close(void) { if (sock < 0) return 1; if (filter_token >= 0) notify_cancel(filter_token); filter_token = -1; asl_log_filter = 0; asl_free(query); close(sock); if (aslfile != NULL) fclose(aslfile); unlink(_PATH_ASL_IN); return 0; }