2 * Copyright (c) 2011-2012 Apple Inc. All rights reserved.
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
19 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
24 * Except as contained in this notice, the name(s) of the above
25 * copyright holders shall not be used in advertising or otherwise to
26 * promote the sale, use or other dealings in this Software without
27 * prior written authorization.
30 #ifdef HAVE_DIX_CONFIG_H
31 #include <dix-config.h>
33 #define DEBUG_CONSOLE_REDIRECT 1
34 #define HAVE_LIBDISPATCH 1
42 #include <sys/types.h>
43 #include <sys/event.h>
48 #include "console_redirect.h"
52 #ifdef HAVE_LIBDISPATCH
53 #include <dispatch/dispatch.h>
55 static dispatch_queue_t redirect_serial_q
;
56 static dispatch_group_t read_source_group
;
60 static pthread_t redirect_pthread
;
61 static pthread_mutex_t redirect_fds_lock
= PTHREAD_MUTEX_INITIALIZER
;
65 /* Notifications to our reader thread */
66 #define ASL_REDIRECT_TERMINATE ((void *)(uintptr_t)1)
74 /* Buffered reading */
78 #ifdef HAVE_LIBDISPATCH
79 dispatch_source_t read_source
;
83 static asl_redirect
*redirect_fds
= NULL
;
84 static int n_redirect_fds
= 0;
86 /* Read from the FD until there is no more to read and redirect to ASL.
88 * 1: pthread_mutex_lock lock is held (pthreads) or called
89 * from the appropriate serial queue for operating on
91 * 2: fd corresponds to a valid entry in redirect_fds
94 * If the pipe is closed, EOF is returned regardless of how many bytes
95 * were processed. If the pipe is still open, the number of read bytes
99 _read_redirect(int fd
, int flush
)
103 asl_redirect
*aslr
= &redirect_fds
[fd
];
107 BUF_SIZE
- (aslr
->w
- aslr
->buf
) - 1)) > 0) {
110 /* Increment our returned number read */
111 total_read
+= nbytes
;
113 /* Increment our write location */
117 /* One line at a time */
118 for (p
= aslr
->buf
; p
< aslr
->w
; p
= s
+ 1) {
120 for (s
= p
; *s
&& *s
!= '\n'; s
++) ;
125 if (s
< aslr
->w
|| aslr
->buf
== p
) {
126 /* Either the first of multiple messages or one message which is larger than our buffer */
127 asl_log(aslr
->asl
, aslr
->msg
, aslr
->level
, "%s", p
);
130 /* We reached the end of the buffer, move this chunk to the start. */
131 memmove(aslr
->buf
, p
, BUF_SIZE
- (p
- aslr
->buf
));
132 aslr
->w
= aslr
->buf
+ (s
- p
);
138 /* Start writing at the beginning in the case where we flushed */
143 /* Flush if requested or we're at EOF */
144 if (flush
|| nbytes
== 0) {
145 if (aslr
->w
> aslr
->buf
) {
147 asl_log(aslr
->asl
, aslr
->msg
, aslr
->level
, "%s", aslr
->buf
);
156 #ifdef HAVE_LIBDISPATCH
158 read_from_source(void *_source
)
160 dispatch_source_t source
= (dispatch_source_t
)_source
;
161 int fd
= dispatch_source_get_handle(source
);
162 if (_read_redirect(fd
, 0) == EOF
) {
163 dispatch_source_cancel(source
);
168 cancel_source(void *_source
)
170 dispatch_source_t source
= (dispatch_source_t
)_source
;
171 int fd
= dispatch_source_get_handle(source
);
172 asl_redirect
*aslr
= &redirect_fds
[fd
];
174 /* Flush the buffer */
175 _read_redirect(fd
, 1);
179 memset(aslr
, 0, sizeof(*aslr
));
180 dispatch_release(source
);
181 dispatch_group_leave(read_source_group
);
184 #else /* !HAVE_LIBDISPATCH */
186 redirect_thread(void *ctx __unused
)
192 n
= kevent(kq
, NULL
, 0, &ev
, 1, NULL
);
196 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "kevent failure: %s",
201 /* This should not happen */
207 pthread_mutex_lock(&redirect_fds_lock
);
211 asl_redirect
*aslr
= &redirect_fds
[fd
];
213 if (fd
< 0 || fd
>= n_redirect_fds
|| aslr
->buf
== NULL
) {
214 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
215 "Unexpected file descriptor: %d",
220 if (ev
.flags
& EV_EOF
) {
222 if (EOF
!= _read_redirect(fd
, 1)) {
224 NULL
, NULL
, ASL_LEVEL_ERR
,
225 "kevent reported EOF on %d, but read doesn't concur.",
230 close_fd
= (EOF
== _read_redirect(fd
, 0));
234 EV_SET(&ev
, fd
, EVFILT_READ
, EV_DELETE
, 0, 0, 0);
235 kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
238 memset(aslr
, 0, sizeof(*aslr
));
242 pthread_mutex_unlock(&redirect_fds_lock
);
245 if (ev
.udata
== ASL_REDIRECT_TERMINATE
)
259 redirect_atexit(void)
261 /* stdout is linebuffered, so flush the buffer */
262 if (redirect_fds
[STDOUT_FILENO
].buf
)
265 #ifdef HAVE_LIBDISPATCH
269 /* Cancel all of our dispatch sources, so they flush to ASL */
270 for (i
= 0; i
< n_redirect_fds
; i
++)
271 if (redirect_fds
[i
].read_source
)
272 dispatch_source_cancel(redirect_fds
[i
].read_source
);
274 /* Wait at least three seconds for our sources to flush to ASL */
275 dispatch_group_wait(read_source_group
,
276 dispatch_time(DISPATCH_TIME_NOW
, 3LL *
283 /* Tell our reader thread it is time to pack up and go home */
284 EV_SET(&ev
, 0, EVFILT_TIMER
, EV_ADD
| EV_ONESHOT
, 0, 0,
285 ASL_REDIRECT_TERMINATE
);
286 kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
288 pthread_join(redirect_pthread
, NULL
);
293 #ifdef HAVE_LIBDISPATCH
295 xq_asl_init(void *ctx __unused
)
301 assert((redirect_fds
= calloc(16, sizeof(*redirect_fds
))) != NULL
);
304 #ifdef HAVE_LIBDISPATCH
305 redirect_serial_q
= dispatch_queue_create("com.apple.asl-redirect", NULL
);
306 assert(redirect_serial_q
!= NULL
);
308 read_source_group
= dispatch_group_create();
309 assert(read_source_group
!= NULL
);
311 assert((kq
= kqueue()) != -1);
312 assert(pthread_create(&redirect_pthread
, NULL
, redirect_thread
,
316 atexit(redirect_atexit
);
319 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
320 #define fls(v) xq_fls(v)
322 static inline int fls(int value
) {
325 v
= *((unsigned int *)&value
);
327 for(b
=0 ; v
; v
>>= 1 , b
++);
334 xq_asl_log_fd(aslclient asl
, aslmsg msg
, int level
, int fd
)
336 #ifdef HAVE_LIBDISPATCH
338 static dispatch_once_t once_control
;
339 dispatch_once_f(&once_control
, NULL
, xq_asl_init
);
342 static pthread_once_t once_control
= PTHREAD_ONCE_INIT
;
343 assert(pthread_once(&once_control
, xq_asl_init
) == 0);
349 #ifdef HAVE_LIBDISPATCH
350 #define BLOCK_DONE return
351 dispatch_sync(redirect_serial_q
, ^
353 #define BLOCK_DONE goto done
354 assert(pthread_mutex_lock(&redirect_fds_lock
) == 0);
357 /* Reallocate if we need more space */
358 if (fd
>= n_redirect_fds
) {
359 size_t new_n
= 1 << (fls(fd
) + 1);
360 asl_redirect
*new_array
=
361 realloc(redirect_fds
, new_n
*
362 sizeof(*redirect_fds
));
367 redirect_fds
= new_array
;
368 memset(redirect_fds
+ n_redirect_fds
, 0, (new_n
-
369 n_redirect_fds
) * sizeof(*redirect_fds
));
370 n_redirect_fds
= new_n
;
373 /* If we're already listening on it, return error. */
374 if (redirect_fds
[fd
].buf
!= NULL
) {
379 /* Initialize our buffer */
380 redirect_fds
[fd
].buf
= (char *)malloc(BUF_SIZE
);
381 if (redirect_fds
[fd
].buf
== NULL
) {
385 redirect_fds
[fd
].w
= redirect_fds
[fd
].buf
;
387 /* Store our ASL settings */
388 redirect_fds
[fd
].level
= level
;
389 redirect_fds
[fd
].asl
= asl
;
390 redirect_fds
[fd
].msg
= msg
;
392 /* Don't block on reads from this fd */
396 /* Start listening */
397 #ifdef HAVE_LIBDISPATCH
399 dispatch_source_t read_source
=
400 dispatch_source_create(
401 DISPATCH_SOURCE_TYPE_READ
, fd
, 0,
403 redirect_fds
[fd
].read_source
= read_source
;
404 dispatch_set_context(read_source
, read_source
);
405 dispatch_source_set_event_handler_f(read_source
,
407 dispatch_source_set_cancel_handler_f(read_source
,
409 dispatch_group_enter(read_source_group
);
410 dispatch_resume(read_source
);
415 EV_SET(&ev
, fd
, EVFILT_READ
, EV_ADD
, 0, 0, 0);
416 kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
420 #ifdef HAVE_LIBDISPATCH
424 assert(pthread_mutex_unlock(&redirect_fds_lock
) == 0);
432 xq_asl_capture_fd(aslclient asl
, aslmsg msg
, int level
, int fd
)
437 if (pipe(pipepair
) == -1)
440 /* Close the read fd but not the write fd on exec */
441 if (fcntl(pipepair
[0], F_SETFD
, FD_CLOEXEC
) == -1)
444 /* Replace the existing fd */
445 if (dup2(pipepair
[1], fd
) == -1) {
451 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
452 if (fd
== STDOUT_FILENO
)
455 /* Close the duplicate fds since they've been reassigned */
458 /* Hand off the read end of our pipe to xq_asl_log_fd */
459 return xq_asl_log_fd(asl
, msg
, level
, pipepair
[0]);
462 #ifdef DEBUG_CONSOLE_REDIRECT
464 main(int argc __unused
, char * *argv __unused
)
466 xq_asl_capture_fd(NULL
, NULL
, ASL_LEVEL_NOTICE
, STDOUT_FILENO
);
467 xq_asl_capture_fd(NULL
, NULL
, ASL_LEVEL_ERR
, STDERR_FILENO
);
469 fprintf(stderr
, "TEST ERR1\n");
470 fprintf(stdout
, "TEST OUT1\n");
471 fprintf(stderr
, "TEST ERR2\n");
472 fprintf(stdout
, "TEST OUT2\n");
473 system("/bin/echo SYST OUT");
474 system("/bin/echo SYST ERR >&2");
475 fprintf(stdout
, "TEST OUT3\n");
476 fprintf(stdout
, "TEST OUT4\n");
477 fprintf(stderr
, "TEST ERR3\n");
478 fprintf(stderr
, "TEST ERR4\n");