]> git.saurik.com Git - apple/libc.git/blob - gen/asl_fd.c
Libc-825.26.tar.gz
[apple/libc.git] / gen / asl_fd.c
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
43 static dispatch_queue_t redirect_serial_q;
44 static dispatch_group_t read_source_group;
45
46 typedef 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
58 static asl_redirect_t *redirect_descriptors = NULL;
59 static 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 */
72 static 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 /* Increment our write location */
84 aslr->w += nbytes;
85 aslr->w[0] = '\0';
86
87 /* One line at a time */
88 for (p = aslr->buf; p < aslr->w; p = s + 1) {
89 /* Find null or \n */
90 for (s=p; *s && *s != '\n'; s++);
91
92 if (*s == '\n') {
93 *s='\0';
94 }
95
96 if (s < aslr->w || aslr->buf == p) {
97 /* Either the first of multiple messages or one message which is larger than our buffer */
98 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", p);
99 } else {
100 /* We reached the end of the buffer, move this chunk to the start. */
101 memmove(aslr->buf, p, BUF_SIZE - (p - aslr->buf));
102 aslr->w = aslr->buf + (s - p);
103 break;
104 }
105 }
106
107 if (p == aslr->w) {
108 /* Start writing at the beginning in the case where we cleared the buffer */
109 aslr->w = aslr->buf;
110 }
111 }
112
113 /* Flush if requested or we're at EOF */
114 if (flush || nbytes == 0) {
115 if (aslr->w > aslr->buf) {
116 *aslr->w = '\0';
117 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", aslr->buf);
118 }
119 }
120
121 if (nbytes == 0)
122 return EOF;
123 return total_read;
124 }
125
126 static void read_from_source(void *_source) {
127 dispatch_source_t source = (dispatch_source_t)_source;
128 int descriptor = dispatch_source_get_handle(source);
129 if (_read_redirect(descriptor, 0) == EOF) {
130 dispatch_source_cancel(source);
131 }
132 }
133
134 static void cancel_source(void *_source) {
135 dispatch_source_t source = (dispatch_source_t)_source;
136 int descriptor = dispatch_source_get_handle(source);
137 asl_redirect_t *aslr = &redirect_descriptors[descriptor];
138
139 /* Flush the buffer */
140 _read_redirect(descriptor, 1);
141
142 close(descriptor);
143
144 asl_client_release(aslr->asl);
145 asl_msg_release(aslr->msg);
146 free(aslr->buf);
147
148 memset(aslr, 0, sizeof(*aslr));
149 dispatch_release(source);
150 dispatch_group_leave(read_source_group);
151 }
152
153
154 static void redirect_atexit(void) {
155 int i;
156
157 /* stdout is linebuffered, so flush the buffer */
158 if (redirect_descriptors[STDOUT_FILENO].buf)
159 fflush(stdout);
160
161 /* Cancel all of our dispatch sources, so they flush to ASL */
162 for (i=0; i < n_redirect_descriptors; i++)
163 if (redirect_descriptors[i].read_source)
164 dispatch_source_cancel(redirect_descriptors[i].read_source);
165
166 /* Wait at least three seconds for our sources to flush to ASL */
167 dispatch_group_wait(read_source_group, dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC));
168 }
169
170 static void asl_descriptor_init(void *ctx __unused)
171 {
172 assert((redirect_descriptors = calloc(16, sizeof(*redirect_descriptors))) != NULL);
173 n_redirect_descriptors = 16;
174
175 redirect_serial_q = dispatch_queue_create("com.apple.asl-redirect", NULL);
176 assert(redirect_serial_q != NULL);
177
178 read_source_group = dispatch_group_create();
179 assert(read_source_group != NULL);
180
181 atexit(redirect_atexit);
182 }
183
184 static int asl_log_from_descriptor(aslclient ac, aslmsg am, int level, int descriptor) {
185 int err __block = 0;
186 static dispatch_once_t once_control;
187 dispatch_once_f(&once_control, NULL, asl_descriptor_init);
188 asl_client_t *asl = (asl_client_t *)ac;
189 asl_msg_t *msg = (asl_msg_t *)am;
190
191 if (descriptor < 0)
192 return EBADF;
193
194 if (msg != NULL) {
195 msg = asl_msg_copy(msg);
196 if (msg == NULL)
197 return ENOMEM;
198 }
199
200 dispatch_sync(redirect_serial_q, ^{
201 dispatch_source_t read_source;
202
203 /* Reallocate if we need more space */
204 if (descriptor >= n_redirect_descriptors) {
205 size_t new_n = 1 << (fls(descriptor) + 1);
206 asl_redirect_t *new_array = realloc(redirect_descriptors, new_n * sizeof(*redirect_descriptors));
207 if (!new_array) {
208 err = errno;
209 return;
210 }
211 redirect_descriptors = new_array;
212 memset(redirect_descriptors + n_redirect_descriptors, 0, (new_n - n_redirect_descriptors) * sizeof(*redirect_descriptors));
213 n_redirect_descriptors = new_n;
214 }
215
216 /* If we're already listening on it, return error. */
217 if (redirect_descriptors[descriptor].buf != NULL) {
218 err = EBADF;
219 return;
220 }
221
222 /* Initialize our buffer */
223 redirect_descriptors[descriptor].buf = (char *)malloc(BUF_SIZE);
224 if (redirect_descriptors[descriptor].buf == NULL) {
225 err = errno;
226 return;
227 }
228 redirect_descriptors[descriptor].w = redirect_descriptors[descriptor].buf;
229
230 /* Store our ASL settings */
231 redirect_descriptors[descriptor].level = level;
232 redirect_descriptors[descriptor].asl = asl_client_retain(asl);
233 redirect_descriptors[descriptor].msg = msg;
234
235 /* Don't block on reads from this descriptor */
236 (void)fcntl(descriptor, F_SETFL, O_NONBLOCK);
237
238 /* Start listening */
239 read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, descriptor, 0, redirect_serial_q);
240 redirect_descriptors[descriptor].read_source = read_source;
241 dispatch_set_context(read_source, read_source);
242 dispatch_source_set_event_handler_f(read_source, read_from_source);
243 dispatch_source_set_cancel_handler_f(read_source, cancel_source);
244 dispatch_group_enter(read_source_group);
245 dispatch_resume(read_source);
246 });
247
248 if (err) {
249 asl_msg_release(msg);
250 }
251
252 return err;
253 }
254
255 int asl_log_descriptor(aslclient ac, aslmsg am, int level, int descriptor, uint32_t fd_type) {
256 int pipepair[2];
257 int retval;
258 int oerrno = errno;
259
260 if (fd_type == ASL_LOG_DESCRIPTOR_READ)
261 return asl_log_from_descriptor(ac, am, level, descriptor);
262
263 assert(fd_type == ASL_LOG_DESCRIPTOR_WRITE);
264
265 /* Create pipe */
266 if (pipe(pipepair) == -1) {
267 retval = errno;
268 errno = oerrno;
269 return retval;
270 }
271
272 /* Close the read descriptor but not the write descriptor on exec */
273 if (fcntl(pipepair[0], F_SETFD, FD_CLOEXEC) == -1) {
274 retval = errno;
275 errno = oerrno;
276 return retval;
277 }
278
279 /* Replace the existing descriptor */
280 if (dup2(pipepair[1], descriptor) == -1) {
281 close(pipepair[0]);
282 close(pipepair[1]);
283 retval = errno;
284 errno = oerrno;
285 return retval;
286 }
287
288 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
289 if (descriptor == STDOUT_FILENO)
290 setlinebuf(stdout);
291
292 /* Close the duplicate descriptors since they've been reassigned */
293 close(pipepair[1]);
294
295 /* Hand off the read end of our pipe to asl_log_descriptor */
296 return asl_log_from_descriptor(ac, am, level, pipepair[0]);
297 }