]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl_fd.c
syslog-385.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_fd.c
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
40 static dispatch_queue_t redirect_serial_q;
41 static dispatch_group_t read_source_group;
42
43 typedef 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
56 static asl_redirect_t *redirect_descriptors = NULL;
57 static 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 */
71 static inline int
72 _read_redirect(int descriptor, int flush)
73 {
74 int total_read = 0;
75 ssize_t 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
132 static void
133 read_from_source(void *_source)
134 {
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);
138 }
139
140 static void
141 cancel_source(void *_source)
142 {
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];
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")))
162 void
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
172 static void
173 redirect_atexit(void)
174 {
175 int i;
176
177 /* stdout is linebuffered, so flush the buffer */
178 if (n_redirect_descriptors > STDOUT_FILENO && redirect_descriptors[STDOUT_FILENO].buf) {
179 fflush(stdout);
180 }
181
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);
186 }
187
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));
190 }
191
192 static void
193 asl_descriptor_init(void *ctx __unused)
194 {
195 assert((redirect_descriptors = calloc(16, sizeof(*redirect_descriptors))) != NULL);
196 n_redirect_descriptors = 16;
197
198 redirect_serial_q = dispatch_queue_create("com.apple.asl-redirect", NULL);
199 assert(redirect_serial_q != NULL);
200
201 read_source_group = dispatch_group_create();
202 assert(read_source_group != NULL);
203
204 atexit(redirect_atexit);
205 }
206
207 static int
208 asl_log_from_descriptor(aslclient ac, aslmsg am, int level, int descriptor)
209 {
210 int err __block = 0;
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;
215
216 if (descriptor < 0) return EBADF;
217
218 if (msg != NULL)
219 {
220 msg = asl_msg_copy(msg);
221 if (msg == NULL) return ENOMEM;
222 }
223
224 dispatch_sync(redirect_serial_q, ^{
225 dispatch_source_t read_source;
226
227 /* Reallocate if we need more space */
228 if (descriptor >= n_redirect_descriptors)
229 {
230 size_t new_n = 1 << (fls(descriptor) + 1);
231 asl_redirect_t *new_array = realloc(redirect_descriptors, new_n * sizeof(*redirect_descriptors));
232 if (!new_array)
233 {
234 err = errno;
235 return;
236 }
237
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;
241 }
242
243 /* If we're already listening on it, return error. */
244 if (redirect_descriptors[descriptor].buf != NULL)
245 {
246 err = EBADF;
247 return;
248 }
249
250 /* Initialize our buffer */
251 redirect_descriptors[descriptor].buf = (char *)malloc(BUF_SIZE);
252 if (redirect_descriptors[descriptor].buf == NULL)
253 {
254 err = errno;
255 return;
256 }
257
258 redirect_descriptors[descriptor].w = redirect_descriptors[descriptor].buf;
259
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;
264
265 /* Don't block on reads from this descriptor */
266 (void)fcntl(descriptor, F_SETFL, O_NONBLOCK);
267
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);
276 });
277
278 if (err) asl_msg_release(msg);
279
280 return err;
281 }
282
283 int
284 asl_log_descriptor(aslclient ac, aslmsg am, int level, int descriptor, uint32_t fd_type)
285 {
286 int pipepair[2];
287 int retval;
288 int oerrno = errno;
289
290 if (fd_type == ASL_LOG_DESCRIPTOR_READ) return asl_log_from_descriptor(ac, am, level, descriptor);
291
292 assert(fd_type == ASL_LOG_DESCRIPTOR_WRITE);
293
294 /* Create pipe */
295 if (pipe(pipepair) == -1)
296 {
297 retval = errno;
298 errno = oerrno;
299 return retval;
300 }
301
302 /* Close the read descriptor but not the write descriptor on exec */
303 if (fcntl(pipepair[0], F_SETFD, FD_CLOEXEC) == -1)
304 {
305 retval = errno;
306 errno = oerrno;
307 return retval;
308 }
309
310 /* Replace the existing descriptor */
311 if (dup2(pipepair[1], descriptor) == -1)
312 {
313 close(pipepair[0]);
314 close(pipepair[1]);
315 retval = errno;
316 errno = oerrno;
317 return retval;
318 }
319
320 /* If we capture STDOUT_FILENO, make sure we linebuffer stdout */
321 if (descriptor == STDOUT_FILENO) setlinebuf(stdout);
322
323 /* Close the duplicate descriptors since they've been reassigned */
324 close(pipepair[1]);
325
326 /* Hand off the read end of our pipe to asl_log_descriptor */
327 return asl_log_from_descriptor(ac, am, level, pipepair[0]);
328 }