1/* 2 * Copyright (c) 2013 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_client.h> 33#include <asl_private.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <dispatch/dispatch.h> 37 38#define BUF_SIZE 512 39 40static dispatch_queue_t redirect_serial_q; 41static dispatch_group_t read_source_group; 42 43typedef struct 44{ 45 int level; 46 asl_client_t *asl; 47 asl_msg_t *msg; 48 49 /* Buffered reading */ 50 char *buf; 51 char *w; 52 53 dispatch_source_t read_source; 54} asl_redirect_t; 55 56static asl_redirect_t *redirect_descriptors = NULL; 57static int n_redirect_descriptors = 0; 58 59/* 60 * Read from the FD until there is no more to read and redirect to ASL. 61 * Preconditions: 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 65 * 66 * Return values: 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 69 * is returned. 70 */ 71static inline int 72_read_redirect(int descriptor, int flush) 73{ 74 int total_read = 0; 75 int nbytes; 76 asl_redirect_t *aslr = &redirect_descriptors[descriptor]; 77 78 while ((nbytes = read(descriptor, aslr->w, BUF_SIZE - (aslr->w - aslr->buf) - 1)) > 0) 79 { 80 char *s, *p; 81 82 /* Increment our returned number read */ 83 total_read += nbytes; 84 85 /* Increment our write location */ 86 aslr->w += nbytes; 87 aslr->w[0] = '\0'; 88 89 /* One line at a time */ 90 for (p = aslr->buf; p < aslr->w; p = s + 1) 91 { 92 /* Find null or \n */ 93 for (s = p; *s && *s != '\n'; s++); 94 95 if (*s == '\n') *s='\0'; 96 97 if (s < aslr->w || aslr->buf == p) 98 { 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); 101 } 102 else 103 { 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); 107 break; 108 } 109 } 110 111 if (p == aslr->w) 112 { 113 /* Start writing at the beginning in the case where we cleared the buffer */ 114 aslr->w = aslr->buf; 115 } 116 } 117 118 /* Flush if requested or we're at EOF */ 119 if (flush || nbytes == 0) 120 { 121 if (aslr->w > aslr->buf) 122 { 123 *aslr->w = '\0'; 124 asl_log((aslclient)aslr->asl, (aslmsg)aslr->msg, aslr->level, "%s", aslr->buf); 125 } 126 } 127 128 if (nbytes == 0) return EOF; 129 return total_read; 130} 131 132static void 133read_from_source(void *_source) 134{ 135 dispatch_source_t source = (dispatch_source_t)_source; 136 int descriptor = dispatch_source_get_handle(source); 137 if (_read_redirect(descriptor, 0) == EOF) dispatch_source_cancel(source); 138} 139 140static void 141cancel_source(void *_source) 142{ 143 dispatch_source_t source = (dispatch_source_t)_source; 144 int descriptor = dispatch_source_get_handle(source); 145 asl_redirect_t *aslr = &redirect_descriptors[descriptor]; 146 147 /* Flush the buffer */ 148 _read_redirect(descriptor, 1); 149 150 close(descriptor); 151 152 asl_client_release(aslr->asl); 153 asl_msg_release(aslr->msg); 154 free(aslr->buf); 155 156 memset(aslr, 0, sizeof(*aslr)); 157 dispatch_release(source); 158 dispatch_group_leave(read_source_group); 159} 160 161__attribute__ ((visibility ("hidden"))) 162void 163_asl_redirect_fork_child(void) 164{ 165 if (redirect_descriptors) { 166 free(redirect_descriptors); 167 n_redirect_descriptors = 0; 168 redirect_descriptors = NULL; 169 } 170} 171 172static void 173redirect_atexit(void) 174{ 175 int i; 176 177 /* stdout is linebuffered, so flush the buffer */ 178 if (redirect_descriptors[STDOUT_FILENO].buf) fflush(stdout); 179 180 /* Cancel all of our dispatch sources, so they flush to ASL */ 181 for (i = 0; i < n_redirect_descriptors; i++) { 182 if (redirect_descriptors[i].read_source) 183 dispatch_source_cancel(redirect_descriptors[i].read_source); 184 } 185 186 /* Wait at least three seconds for our sources to flush to ASL */ 187 dispatch_group_wait(read_source_group, dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC)); 188} 189 190static void 191asl_descriptor_init(void *ctx __unused) 192{ 193 assert((redirect_descriptors = calloc(16, sizeof(*redirect_descriptors))) != NULL); 194 n_redirect_descriptors = 16; 195 196 redirect_serial_q = dispatch_queue_create("com.apple.asl-redirect", NULL); 197 assert(redirect_serial_q != NULL); 198 199 read_source_group = dispatch_group_create(); 200 assert(read_source_group != NULL); 201 202 atexit(redirect_atexit); 203} 204 205static int 206asl_log_from_descriptor(aslclient ac, aslmsg am, int level, int descriptor) 207{ 208 int err __block = 0; 209 static dispatch_once_t once_control; 210 dispatch_once_f(&once_control, NULL, asl_descriptor_init); 211 asl_client_t *asl = (asl_client_t *)ac; 212 asl_msg_t *msg = (asl_msg_t *)am; 213 214 if (descriptor < 0) return EBADF; 215 216 if (msg != NULL) 217 { 218 msg = asl_msg_copy(msg); 219 if (msg == NULL) return ENOMEM; 220 } 221 222 dispatch_sync(redirect_serial_q, ^{ 223 dispatch_source_t read_source; 224 225 /* Reallocate if we need more space */ 226 if (descriptor >= n_redirect_descriptors) 227 { 228 size_t new_n = 1 << (fls(descriptor) + 1); 229 asl_redirect_t *new_array = realloc(redirect_descriptors, new_n * sizeof(*redirect_descriptors)); 230 if (!new_array) 231 { 232 err = errno; 233 return; 234 } 235 236 redirect_descriptors = new_array; 237 memset(redirect_descriptors + n_redirect_descriptors, 0, (new_n - n_redirect_descriptors) * sizeof(*redirect_descriptors)); 238 n_redirect_descriptors = new_n; 239 } 240 241 /* If we're already listening on it, return error. */ 242 if (redirect_descriptors[descriptor].buf != NULL) 243 { 244 err = EBADF; 245 return; 246 } 247 248 /* Initialize our buffer */ 249 redirect_descriptors[descriptor].buf = (char *)malloc(BUF_SIZE); 250 if (redirect_descriptors[descriptor].buf == NULL) 251 { 252 err = errno; 253 return; 254 } 255 256 redirect_descriptors[descriptor].w = redirect_descriptors[descriptor].buf; 257 258 /* Store our ASL settings */ 259 redirect_descriptors[descriptor].level = level; 260 redirect_descriptors[descriptor].asl = asl_client_retain(asl); 261 redirect_descriptors[descriptor].msg = msg; 262 263 /* Don't block on reads from this descriptor */ 264 (void)fcntl(descriptor, F_SETFL, O_NONBLOCK); 265 266 /* Start listening */ 267 read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, descriptor, 0, redirect_serial_q); 268 redirect_descriptors[descriptor].read_source = read_source; 269 dispatch_set_context(read_source, read_source); 270 dispatch_source_set_event_handler_f(read_source, read_from_source); 271 dispatch_source_set_cancel_handler_f(read_source, cancel_source); 272 dispatch_group_enter(read_source_group); 273 dispatch_resume(read_source); 274 }); 275 276 if (err) asl_msg_release(msg); 277 278 return err; 279} 280 281int 282asl_log_descriptor(aslclient ac, aslmsg am, int level, int descriptor, uint32_t fd_type) 283{ 284 int pipepair[2]; 285 int retval; 286 int oerrno = errno; 287 288 if (fd_type == ASL_LOG_DESCRIPTOR_READ) return asl_log_from_descriptor(ac, am, level, descriptor); 289 290 assert(fd_type == ASL_LOG_DESCRIPTOR_WRITE); 291 292 /* Create pipe */ 293 if (pipe(pipepair) == -1) 294 { 295 retval = errno; 296 errno = oerrno; 297 return retval; 298 } 299 300 /* Close the read descriptor but not the write descriptor on exec */ 301 if (fcntl(pipepair[0], F_SETFD, FD_CLOEXEC) == -1) 302 { 303 retval = errno; 304 errno = oerrno; 305 return retval; 306 } 307 308 /* Replace the existing descriptor */ 309 if (dup2(pipepair[1], descriptor) == -1) 310 { 311 close(pipepair[0]); 312 close(pipepair[1]); 313 retval = errno; 314 errno = oerrno; 315 return retval; 316 } 317 318 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */ 319 if (descriptor == STDOUT_FILENO) setlinebuf(stdout); 320 321 /* Close the duplicate descriptors since they've been reassigned */ 322 close(pipepair[1]); 323 324 /* Hand off the read end of our pipe to asl_log_descriptor */ 325 return asl_log_from_descriptor(ac, am, level, pipepair[0]); 326} 327