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 /* 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 126static 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 134static 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 154static 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 170static 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 184static 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 255int 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} 298