]> git.saurik.com Git - apple/libc.git/blame - gen/asl_fd.c
Libc-825.25.tar.gz
[apple/libc.git] / gen / asl_fd.c
CommitLineData
ad3c9f2a
A
1/*
2 * Copyright (c) 2011 Apple 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 <assert.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <sys/event.h>
31#include <asl.h>
32#include <asl_private.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <dispatch/dispatch.h>
36
37/* asl.c */
38__private_extern__ void asl_client_release(asl_client_t *asl);
39__private_extern__ asl_client_t *asl_client_retain(asl_client_t *asl);
40
41#define BUF_SIZE 512
42
43static dispatch_queue_t redirect_serial_q;
44static dispatch_group_t read_source_group;
45
46typedef struct {
47 int level;
48 asl_client_t *asl;
49 asl_msg_t *msg;
50
51 /* Buffered reading */
52 char *buf;
53 char *w;
54
55 dispatch_source_t read_source;
56} asl_redirect_t;
57
58static asl_redirect_t *redirect_descriptors = NULL;
59static int n_redirect_descriptors = 0;
60
61/* Read from the FD until there is no more to read and redirect to ASL.
62 * Preconditions:
63 * 1: called from the appropriate serial queue for operating on
64 * redirect_descriptors
65 * 2: descriptor corresponds to a valid entry in redirect_descriptors
66 *
67 * Return values:
68 * If the pipe is closed, EOF is returned regardless of how many bytes
69 * were processed. If the pipe is still open, the number of read bytes
70 * is returned.
71 */
72static inline int _read_redirect(int descriptor, int flush) {
73 int total_read = 0;
74 int nbytes;
75 asl_redirect_t *aslr = &redirect_descriptors[descriptor];
76
77 while ((nbytes = read(descriptor, aslr->w, BUF_SIZE - (aslr->w - aslr->buf) - 1)) > 0) {
78 char *s, *p;
79
80 /* Increment our returned number read */
81 total_read += nbytes;
82
83 nbytes += (aslr->w - aslr->buf);
84 aslr->buf[nbytes] = '\0';
85
86 /* One line at a time */
87 for (p=aslr->buf; *p && (p - aslr->buf) < nbytes; p = s + 1) {
88 // Find null or \n
89 for (s=p; *s && *s != '\n'; s++);
90 if (*s == '\n') {
91 *s='\0';
92 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", p);
93 } else if (aslr->buf != p) {
94 memmove(aslr->buf, p, BUF_SIZE - (p - aslr->buf));
95 aslr->w = aslr->buf + (s - p);
96 break;
97 } else if (nbytes == BUF_SIZE - 1) {
98 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", p);
99 aslr->w = aslr->buf;
100 break;
101 }
102 }
103 }
104
105 /* Flush if requested or we're at EOF */
106 if (flush || nbytes == 0) {
107 if (aslr->w > aslr->buf) {
108 *aslr->w = '\0';
109 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", aslr->buf);
110 }
111 }
112
113 if (nbytes == 0)
114 return EOF;
115 return total_read;
116}
117
118static void read_from_source(void *_source) {
119 dispatch_source_t source = (dispatch_source_t)_source;
120 int descriptor = dispatch_source_get_handle(source);
121 if (_read_redirect(descriptor, 0) == EOF) {
122 dispatch_source_cancel(source);
123 }
124}
125
126static void cancel_source(void *_source) {
127 dispatch_source_t source = (dispatch_source_t)_source;
128 int descriptor = dispatch_source_get_handle(source);
129 asl_redirect_t *aslr = &redirect_descriptors[descriptor];
130
131 /* Flush the buffer */
132 _read_redirect(descriptor, 1);
133
134 close(descriptor);
135
136 asl_client_release(aslr->asl);
137 asl_msg_release(aslr->msg);
138 free(aslr->buf);
139
140 memset(aslr, 0, sizeof(*aslr));
141 dispatch_release(source);
142 dispatch_group_leave(read_source_group);
143}
144
145
146static void redirect_atexit(void) {
147 int i;
148
149 /* stdout is linebuffered, so flush the buffer */
150 if (redirect_descriptors[STDOUT_FILENO].buf)
151 fflush(stdout);
152
153 /* Cancel all of our dispatch sources, so they flush to ASL */
154 for (i=0; i < n_redirect_descriptors; i++)
155 if (redirect_descriptors[i].read_source)
156 dispatch_source_cancel(redirect_descriptors[i].read_source);
157
158 /* Wait at least three seconds for our sources to flush to ASL */
159 dispatch_group_wait(read_source_group, dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC));
160}
161
162static void asl_descriptor_init(void *ctx __unused)
163{
164 assert((redirect_descriptors = calloc(16, sizeof(*redirect_descriptors))) != NULL);
165 n_redirect_descriptors = 16;
166
167 redirect_serial_q = dispatch_queue_create("com.apple.asl-redirect", NULL);
168 assert(redirect_serial_q != NULL);
169
170 read_source_group = dispatch_group_create();
171 assert(read_source_group != NULL);
172
173 atexit(redirect_atexit);
174}
175
176static int asl_log_from_descriptor(aslclient ac, aslmsg am, int level, int descriptor) {
177 int err __block = 0;
178 static dispatch_once_t once_control;
179 dispatch_once_f(&once_control, NULL, asl_descriptor_init);
180 asl_client_t *asl = (asl_client_t *)ac;
181 asl_msg_t *msg = (asl_msg_t *)am;
182
183 if (descriptor < 0)
184 return EBADF;
185
186 if (msg != NULL) {
187 msg = asl_msg_copy(msg);
188 if (msg == NULL)
189 return ENOMEM;
190 }
191
192 dispatch_sync(redirect_serial_q, ^{
193 dispatch_source_t read_source;
194
195 /* Reallocate if we need more space */
196 if (descriptor >= n_redirect_descriptors) {
197 size_t new_n = 1 << (ffs(descriptor) + 1);
198 asl_redirect_t *new_array = realloc(redirect_descriptors, new_n * sizeof(*redirect_descriptors));
199 if (!new_array) {
200 err = errno;
201 return;
202 }
203 redirect_descriptors = new_array;
204 memset(redirect_descriptors + n_redirect_descriptors, 0, new_n - n_redirect_descriptors);
205 n_redirect_descriptors = new_n;
206 }
207
208 /* If we're already listening on it, return error. */
209 if (redirect_descriptors[descriptor].buf != NULL) {
210 err = EBADF;
211 return;
212 }
213
214 /* Initialize our buffer */
215 redirect_descriptors[descriptor].buf = (char *)malloc(BUF_SIZE);
216 if (redirect_descriptors[descriptor].buf == NULL) {
217 err = errno;
218 return;
219 }
220 redirect_descriptors[descriptor].w = redirect_descriptors[descriptor].buf;
221
222 /* Store our ASL settings */
223 redirect_descriptors[descriptor].level = level;
224 redirect_descriptors[descriptor].asl = asl_client_retain(asl);
225 redirect_descriptors[descriptor].msg = msg;
226
227 /* Don't block on reads from this descriptor */
228 (void)fcntl(descriptor, F_SETFL, O_NONBLOCK);
229
230 /* Start listening */
231 read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, descriptor, 0, redirect_serial_q);
232 redirect_descriptors[descriptor].read_source = read_source;
233 dispatch_set_context(read_source, read_source);
234 dispatch_source_set_event_handler_f(read_source, read_from_source);
235 dispatch_source_set_cancel_handler_f(read_source, cancel_source);
236 dispatch_group_enter(read_source_group);
237 dispatch_resume(read_source);
238 });
239
240 if (err) {
241 asl_msg_release(msg);
242 }
243
244 return err;
245}
246
247int asl_log_descriptor(aslclient ac, aslmsg am, int level, int descriptor, uint32_t fd_type) {
248 int pipepair[2];
249 int retval;
250 int oerrno = errno;
251
252 if (fd_type == ASL_LOG_DESCRIPTOR_READ)
253 return asl_log_from_descriptor(ac, am, level, descriptor);
254
255 assert(fd_type == ASL_LOG_DESCRIPTOR_WRITE);
256
257 /* Create pipe */
258 if (pipe(pipepair) == -1) {
259 retval = errno;
260 errno = oerrno;
261 return retval;
262 }
263
264 /* Close the read descriptor but not the write descriptor on exec */
265 if (fcntl(pipepair[0], F_SETFD, FD_CLOEXEC) == -1) {
266 retval = errno;
267 errno = oerrno;
268 return retval;
269 }
270
271 /* Replace the existing descriptor */
272 if (dup2(pipepair[1], descriptor) == -1) {
273 close(pipepair[0]);
274 close(pipepair[1]);
275 retval = errno;
276 errno = oerrno;
277 return retval;
278 }
279
280 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
281 if (descriptor == STDOUT_FILENO)
282 setlinebuf(stdout);
283
284 /* Close the duplicate descriptors since they've been reassigned */
285 close(pipepair[1]);
286
287 /* Hand off the read end of our pipe to asl_log_descriptor */
288 return asl_log_from_descriptor(ac, am, level, pipepair[0]);
289}