2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <sys/types.h>
30 #include <sys/event.h>
32 #include <asl_client.h>
33 #include <asl_private.h>
36 #include <dispatch/dispatch.h>
40 static dispatch_queue_t redirect_serial_q
;
41 static dispatch_group_t read_source_group
;
49 /* Buffered reading */
53 dispatch_source_t read_source
;
56 static asl_redirect_t
*redirect_descriptors
= NULL
;
57 static int n_redirect_descriptors
= 0;
60 * Read from the FD until there is no more to read and redirect to ASL.
62 * 1: called from the appropriate serial queue for operating on
63 * redirect_descriptors
64 * 2: descriptor corresponds to a valid entry in redirect_descriptors
67 * If the pipe is closed, EOF is returned regardless of how many bytes
68 * were processed. If the pipe is still open, the number of read bytes
72 _read_redirect(int descriptor
, int flush
)
76 asl_redirect_t
*aslr
= &redirect_descriptors
[descriptor
];
78 while ((nbytes
= read(descriptor
, aslr
->w
, BUF_SIZE
- (aslr
->w
- aslr
->buf
) - 1)) > 0)
82 /* Increment our returned number read */
85 /* Increment our write location */
89 /* One line at a time */
90 for (p
= aslr
->buf
; p
< aslr
->w
; p
= s
+ 1)
93 for (s
= p
; *s
&& *s
!= '\n'; s
++);
95 if (*s
== '\n') *s
='\0';
97 if (s
< aslr
->w
|| aslr
->buf
== p
)
99 /* Either the first of multiple messages or one message which is larger than our buffer */
100 asl_log((aslclient
)aslr
->asl
, (aslmsg
)aslr
->msg
, aslr
->level
, "%s", p
);
104 /* We reached the end of the buffer, move this chunk to the start. */
105 memmove(aslr
->buf
, p
, BUF_SIZE
- (p
- aslr
->buf
));
106 aslr
->w
= aslr
->buf
+ (s
- p
);
113 /* Start writing at the beginning in the case where we cleared the buffer */
118 /* Flush if requested or we're at EOF */
119 if (flush
|| nbytes
== 0)
121 if (aslr
->w
> aslr
->buf
)
124 asl_log((aslclient
)aslr
->asl
, (aslmsg
)aslr
->msg
, aslr
->level
, "%s", aslr
->buf
);
128 if (nbytes
== 0) return EOF
;
133 read_from_source(void *_source
)
135 dispatch_source_t source
= (dispatch_source_t
)_source
;
136 int descriptor
= (int)dispatch_source_get_handle(source
);
137 if (_read_redirect(descriptor
, 0) == EOF
) dispatch_source_cancel(source
);
141 cancel_source(void *_source
)
143 dispatch_source_t source
= (dispatch_source_t
)_source
;
144 int descriptor
= (int)dispatch_source_get_handle(source
);
145 asl_redirect_t
*aslr
= &redirect_descriptors
[descriptor
];
147 /* Flush the buffer */
148 _read_redirect(descriptor
, 1);
152 asl_client_release(aslr
->asl
);
153 asl_msg_release(aslr
->msg
);
156 memset(aslr
, 0, sizeof(*aslr
));
157 dispatch_release(source
);
158 dispatch_group_leave(read_source_group
);
161 __attribute__ ((visibility ("hidden")))
163 _asl_redirect_fork_child(void)
165 if (redirect_descriptors
) {
166 free(redirect_descriptors
);
167 n_redirect_descriptors
= 0;
168 redirect_descriptors
= NULL
;
173 redirect_atexit(void)
177 /* stdout is linebuffered, so flush the buffer */
178 if (n_redirect_descriptors
> STDOUT_FILENO
&& redirect_descriptors
[STDOUT_FILENO
].buf
) {
182 /* Cancel all of our dispatch sources, so they flush to ASL */
183 for (i
= 0; i
< n_redirect_descriptors
; i
++) {
184 if (redirect_descriptors
[i
].read_source
)
185 dispatch_source_cancel(redirect_descriptors
[i
].read_source
);
188 /* Wait at least three seconds for our sources to flush to ASL */
189 dispatch_group_wait(read_source_group
, dispatch_time(DISPATCH_TIME_NOW
, 3LL * NSEC_PER_SEC
));
193 asl_descriptor_init(void *ctx __unused
)
195 assert((redirect_descriptors
= calloc(16, sizeof(*redirect_descriptors
))) != NULL
);
196 n_redirect_descriptors
= 16;
198 redirect_serial_q
= dispatch_queue_create("com.apple.asl-redirect", NULL
);
199 assert(redirect_serial_q
!= NULL
);
201 read_source_group
= dispatch_group_create();
202 assert(read_source_group
!= NULL
);
204 atexit(redirect_atexit
);
208 asl_log_from_descriptor(aslclient ac
, aslmsg am
, int level
, int descriptor
)
211 static dispatch_once_t once_control
;
212 dispatch_once_f(&once_control
, NULL
, asl_descriptor_init
);
213 asl_client_t
*asl
= (asl_client_t
*)ac
;
214 asl_msg_t
*msg
= (asl_msg_t
*)am
;
216 if (descriptor
< 0) return EBADF
;
220 msg
= asl_msg_copy(msg
);
221 if (msg
== NULL
) return ENOMEM
;
224 dispatch_sync(redirect_serial_q
, ^{
225 dispatch_source_t read_source
;
227 /* Reallocate if we need more space */
228 if (descriptor
>= n_redirect_descriptors
)
230 size_t new_n
= 1 << (fls(descriptor
) + 1);
231 asl_redirect_t
*new_array
= realloc(redirect_descriptors
, new_n
* sizeof(*redirect_descriptors
));
238 redirect_descriptors
= new_array
;
239 memset(redirect_descriptors
+ n_redirect_descriptors
, 0, (new_n
- n_redirect_descriptors
) * sizeof(*redirect_descriptors
));
240 n_redirect_descriptors
= (int)new_n
;
243 /* If we're already listening on it, return error. */
244 if (redirect_descriptors
[descriptor
].buf
!= NULL
)
250 /* Initialize our buffer */
251 redirect_descriptors
[descriptor
].buf
= (char *)malloc(BUF_SIZE
);
252 if (redirect_descriptors
[descriptor
].buf
== NULL
)
258 redirect_descriptors
[descriptor
].w
= redirect_descriptors
[descriptor
].buf
;
260 /* Store our ASL settings */
261 redirect_descriptors
[descriptor
].level
= level
;
262 redirect_descriptors
[descriptor
].asl
= asl_client_retain(asl
);
263 redirect_descriptors
[descriptor
].msg
= msg
;
265 /* Don't block on reads from this descriptor */
266 (void)fcntl(descriptor
, F_SETFL
, O_NONBLOCK
);
268 /* Start listening */
269 read_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
, descriptor
, 0, redirect_serial_q
);
270 redirect_descriptors
[descriptor
].read_source
= read_source
;
271 dispatch_set_context(read_source
, read_source
);
272 dispatch_source_set_event_handler_f(read_source
, read_from_source
);
273 dispatch_source_set_cancel_handler_f(read_source
, cancel_source
);
274 dispatch_group_enter(read_source_group
);
275 dispatch_resume(read_source
);
278 if (err
) asl_msg_release(msg
);
284 asl_log_descriptor(aslclient ac
, aslmsg am
, int level
, int descriptor
, uint32_t fd_type
)
290 if (fd_type
== ASL_LOG_DESCRIPTOR_READ
) return asl_log_from_descriptor(ac
, am
, level
, descriptor
);
292 assert(fd_type
== ASL_LOG_DESCRIPTOR_WRITE
);
295 if (pipe(pipepair
) == -1)
302 /* Close the read descriptor but not the write descriptor on exec */
303 if (fcntl(pipepair
[0], F_SETFD
, FD_CLOEXEC
) == -1)
310 /* Replace the existing descriptor */
311 if (dup2(pipepair
[1], descriptor
) == -1)
320 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
321 if (descriptor
== STDOUT_FILENO
) setlinebuf(stdout
);
323 /* Close the duplicate descriptors since they've been reassigned */
326 /* Hand off the read end of our pipe to asl_log_descriptor */
327 return asl_log_from_descriptor(ac
, am
, level
, pipepair
[0]);