2 * Copyright (c) 2011 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_private.h>
35 #include <dispatch/dispatch.h>
38 __private_extern__
void asl_client_release(asl_client_t
*asl
);
39 __private_extern__ asl_client_t
*asl_client_retain(asl_client_t
*asl
);
43 static dispatch_queue_t redirect_serial_q
;
44 static dispatch_group_t read_source_group
;
51 /* Buffered reading */
55 dispatch_source_t read_source
;
58 static asl_redirect_t
*redirect_descriptors
= NULL
;
59 static int n_redirect_descriptors
= 0;
61 /* Read from the FD until there is no more to read and redirect to ASL.
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
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
72 static inline int _read_redirect(int descriptor
, int flush
) {
75 asl_redirect_t
*aslr
= &redirect_descriptors
[descriptor
];
77 while ((nbytes
= read(descriptor
, aslr
->w
, BUF_SIZE
- (aslr
->w
- aslr
->buf
) - 1)) > 0) {
80 /* Increment our returned number read */
83 /* Increment our write location */
87 /* One line at a time */
88 for (p
= aslr
->buf
; p
< aslr
->w
; p
= s
+ 1) {
90 for (s
=p
; *s
&& *s
!= '\n'; s
++);
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
);
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
);
108 /* Start writing at the beginning in the case where we cleared the buffer */
113 /* Flush if requested or we're at EOF */
114 if (flush
|| nbytes
== 0) {
115 if (aslr
->w
> aslr
->buf
) {
117 asl_log((aslclient
)aslr
->asl
, (aslmsg
)aslr
->msg
, aslr
->level
, "%s", aslr
->buf
);
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
);
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
];
139 /* Flush the buffer */
140 _read_redirect(descriptor
, 1);
144 asl_client_release(aslr
->asl
);
145 asl_msg_release(aslr
->msg
);
148 memset(aslr
, 0, sizeof(*aslr
));
149 dispatch_release(source
);
150 dispatch_group_leave(read_source_group
);
154 static void redirect_atexit(void) {
157 /* stdout is linebuffered, so flush the buffer */
158 if (redirect_descriptors
[STDOUT_FILENO
].buf
)
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
);
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
));
170 static void asl_descriptor_init(void *ctx __unused
)
172 assert((redirect_descriptors
= calloc(16, sizeof(*redirect_descriptors
))) != NULL
);
173 n_redirect_descriptors
= 16;
175 redirect_serial_q
= dispatch_queue_create("com.apple.asl-redirect", NULL
);
176 assert(redirect_serial_q
!= NULL
);
178 read_source_group
= dispatch_group_create();
179 assert(read_source_group
!= NULL
);
181 atexit(redirect_atexit
);
184 static int asl_log_from_descriptor(aslclient ac
, aslmsg am
, int level
, int descriptor
) {
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
;
195 msg
= asl_msg_copy(msg
);
200 dispatch_sync(redirect_serial_q
, ^{
201 dispatch_source_t read_source
;
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
));
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
;
216 /* If we're already listening on it, return error. */
217 if (redirect_descriptors
[descriptor
].buf
!= NULL
) {
222 /* Initialize our buffer */
223 redirect_descriptors
[descriptor
].buf
= (char *)malloc(BUF_SIZE
);
224 if (redirect_descriptors
[descriptor
].buf
== NULL
) {
228 redirect_descriptors
[descriptor
].w
= redirect_descriptors
[descriptor
].buf
;
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
;
235 /* Don't block on reads from this descriptor */
236 (void)fcntl(descriptor
, F_SETFL
, O_NONBLOCK
);
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
);
249 asl_msg_release(msg
);
255 int asl_log_descriptor(aslclient ac
, aslmsg am
, int level
, int descriptor
, uint32_t fd_type
) {
260 if (fd_type
== ASL_LOG_DESCRIPTOR_READ
)
261 return asl_log_from_descriptor(ac
, am
, level
, descriptor
);
263 assert(fd_type
== ASL_LOG_DESCRIPTOR_WRITE
);
266 if (pipe(pipepair
) == -1) {
272 /* Close the read descriptor but not the write descriptor on exec */
273 if (fcntl(pipepair
[0], F_SETFD
, FD_CLOEXEC
) == -1) {
279 /* Replace the existing descriptor */
280 if (dup2(pipepair
[1], descriptor
) == -1) {
288 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
289 if (descriptor
== STDOUT_FILENO
)
292 /* Close the duplicate descriptors since they've been reassigned */
295 /* Hand off the read end of our pipe to asl_log_descriptor */
296 return asl_log_from_descriptor(ac
, am
, level
, pipepair
[0]);