]> git.saurik.com Git - apple/libc.git/blob - gen/_simple.c
9b8aeb174799a55942f1ef8c33641d7efefd7423
[apple/libc.git] / gen / _simple.c
1 /*
2 * Copyright (c) 2005, 2006 Apple Computer, 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 #include <string.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <setjmp.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <mach/mach_init.h>
30 #include <mach/vm_map.h>
31 #include <asl.h>
32 #include <sys/time.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <pthread.h>
36 #include <errno.h>
37
38 #include "_simple.h"
39
40 #define VM_PAGE_SIZE 4096
41 #define SBUF_SIZE(s) (((SBUF *)(s))->b.end - ((SBUF *)(s))->b.buf + 1)
42 /* we use a small buffer to minimize stack usage constraints */
43 #define MYBUFSIZE 32
44
45 typedef struct _BUF {
46 char *buf;
47 char *ptr;
48 char *end;
49 int fd;
50 void (*full)(struct _BUF *);
51 } BUF;
52
53 typedef struct _SBUF {
54 BUF b;
55 jmp_buf j;
56 } SBUF;
57
58 static int asl_socket;
59 static pthread_once_t asl_socket_once = PTHREAD_ONCE_INIT;
60
61 /* private extern exports from asl.c */
62 const char *_asl_escape(unsigned char);
63 int _asl_server_socket(int *, struct sockaddr_un *);
64
65 /* flush the buffer */
66 static void
67 _flush(BUF *b)
68 {
69 char *buf = b->buf;
70 int n = b->ptr - buf;
71 int w;
72
73 while(n > 0) {
74 w = write(b->fd, buf, n);
75 if(w < 0) {
76 if(errno == EINTR || errno == EAGAIN)
77 continue;
78 break;
79 }
80 n -= w;
81 buf += n;
82 }
83 }
84
85 /* flush the buffer and reset the pointer */
86 static void
87 _flush_reset(BUF *b)
88 {
89 _flush(b);
90 b->ptr = b->buf;
91 }
92
93 /* enlarge the buffer */
94 static void
95 _enlarge(BUF *b)
96 {
97 vm_address_t new;
98 vm_size_t sold, snew;
99 intptr_t diff;
100
101 new = (vm_address_t)(b->end + 1);
102 if(vm_allocate(mach_task_self(), &new, VM_PAGE_SIZE, 0) == 0) {
103 /* page is adjacent */
104 b->end += VM_PAGE_SIZE;
105 return;
106 }
107 sold = SBUF_SIZE(b);
108 snew = (sold + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1);
109 if(vm_allocate(mach_task_self(), &new, snew, 1) != 0)
110 longjmp(((SBUF *)b)->j, 1); /* out of memory */
111 diff = new - (vm_address_t)b->buf;
112 memcpy((void *)new, b->buf, sold);
113 if((intptr_t)(b->buf) & (VM_PAGE_SIZE - 1)) {
114 sold &= ~(VM_PAGE_SIZE - 1);
115 b->buf = (char *)((intptr_t)(b->buf + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1));
116 b->end = (char *)(new + snew - 1);
117 } else
118 b->end += diff + VM_PAGE_SIZE;
119 if(sold > 0) {
120 vm_deallocate(mach_task_self(), (vm_address_t)b->buf, sold);
121 }
122 b->buf = (char *)new;
123 b->ptr += diff;
124 }
125
126 static inline void put_s(BUF *, _esc_func, const char *);
127 /* output a single character */
128 static inline void
129 put_c(BUF *b, _esc_func esc, unsigned char c)
130 {
131 const char *cp;
132
133 if(esc && (cp = esc(c)) != NULL)
134 put_s(b, NULL, cp);
135 else {
136 if(b->ptr >= b->end)
137 b->full(b);
138 *b->ptr++ = c;
139 }
140 }
141
142 /* output a null-terminated string */
143 static inline void
144 put_s(BUF *b, _esc_func esc, const char *str)
145 {
146 while(*str)
147 put_c(b, esc, *str++);
148 }
149
150 /* output a string of the specified size */
151 static inline void
152 put_n(BUF *b, _esc_func esc, const char *str, int n)
153 {
154 while(n-- > 0)
155 put_c(b, esc, *str++);
156 }
157
158 /*
159 * Output the signed decimal string representing the number in "in". "width" is
160 * the minimum field width, and "zero" is a boolean value, true for zero padding
161 * (otherwise blank padding).
162 */
163 static void
164 dec(BUF *b, _esc_func esc, long long in, int width, int zero)
165 {
166 char buf[32];
167 char *cp = buf + sizeof(buf);
168 int pad;
169 int neg = 0;
170 unsigned long long n = (unsigned long long)in;
171
172 if(in < 0) {
173 neg++;
174 width--;
175 n = ~n + 1;
176 }
177 *--cp = 0;
178 if(n) {
179 while(n) {
180 *--cp = (n % 10) + '0';
181 n /= 10;
182 }
183 } else
184 *--cp = '0';
185 if(neg && zero) {
186 put_c(b, esc, '-');
187 neg = 0;
188 }
189 pad = width - strlen(cp);
190 zero = zero ? '0' : ' ';
191 while(pad-- > 0)
192 put_c(b, esc, zero);
193 if(neg)
194 put_c(b, esc, '-');
195 put_s(b, esc, cp);
196 }
197
198 /*
199 * Output the hex string representing the number in "n". "width" is the
200 * minimum field width, and "zero" is a boolean value, true for zero padding
201 * (otherwise blank padding). "upper" is a boolean value, true for upper
202 * case hex characters, lower case otherwise. "p" is a boolean value, true
203 * if 0x should be prepended (for %p), otherwise nothing.
204 */
205 static char _h[] = "0123456789abcdef";
206 static char _H[] = "0123456789ABCDEF";
207 static char _0x[] = "0x";
208
209 static void
210 hex(BUF *b, _esc_func esc, unsigned long long n, int width, int zero, int upper, int p)
211 {
212 char buf[32];
213 char *cp = buf + sizeof(buf);
214 char *h = upper ? _H : _h;
215
216 *--cp = 0;
217 if(n) {
218 while(n) {
219 *--cp = h[n & 0xf];
220 n >>= 4;
221 }
222 } else
223 *--cp = '0';
224 if(p) {
225 width -= 2;
226 if(zero) {
227 put_s(b, esc, _0x);
228 p = 0;
229 }
230 }
231 width -= strlen(cp);
232 zero = zero ? '0' : ' ';
233 while(width-- > 0)
234 put_c(b, esc, zero);
235 if(p)
236 put_s(b, esc, _0x);
237 put_s(b, esc, cp);
238 }
239
240 /*
241 * Output the unsigned decimal string representing the number in "n". "width"
242 * is the minimum field width, and "zero" is a boolean value, true for zero
243 * padding (otherwise blank padding).
244 */
245 static void
246 udec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
247 {
248 char buf[32];
249 char *cp = buf + sizeof(buf);
250 int pad;
251
252 *--cp = 0;
253 if(n) {
254 while(n) {
255 *--cp = (n % 10) + '0';
256 n /= 10;
257 }
258 } else
259 *--cp = '0';
260 pad = width - strlen(cp);
261 zero = zero ? '0' : ' ';
262 while(pad-- > 0)
263 put_c(b, esc, zero);
264 put_s(b, esc, cp);
265 }
266
267 /*
268 * Output the unsigned decimal string representing the number in "n", rounded
269 * to the nearest MB, KB or b. "width" is the minimum field width, and "zero"
270 * is a boolean value, true for zero padding (otherwise blank padding).
271 */
272 static void
273 ydec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
274 {
275 if(n >= 10 * (1 << 20)) {
276 n += (1 << 19);
277 udec(b, esc, n >> 20, width, zero);
278 put_s(b, esc, "MB");
279 } else if (n >= 10 * (1 << 10)) {
280 n += (1 << 9);
281 udec(b, esc, n >> 10, width, zero);
282 put_s(b, esc, "KB");
283 } else {
284 udec(b, esc, n, width, zero);
285 put_s(b, esc, "b");
286 }
287 }
288
289 /*
290 * The actual engine for all the _simple_*printf routines.
291 */
292 static void
293 __simple_bprintf(BUF *b, _esc_func esc, const char *fmt, va_list ap)
294 {
295 while(*fmt) {
296 int lflag, zero, width;
297 char *cp;
298 if(!(cp = strchr(fmt, '%'))) {
299 put_s(b, esc, fmt);
300 break;
301 }
302 put_n(b, esc, fmt, cp - fmt);
303 fmt = cp + 1;
304 if(*fmt == '%') {
305 put_c(b, esc, '%');
306 fmt++;
307 continue;
308 }
309 lflag = zero = width = 0;
310 for(;;) {
311 switch(*fmt) {
312 case '0':
313 zero++;
314 fmt++;
315 /* drop through */
316 case '1': case '2': case '3': case '4': case '5':
317 case '6': case '7': case '8': case '9':
318 while(*fmt >= '0' && *fmt <= '9')
319 width = 10 * width + (*fmt++ - '0');
320 continue;
321 case 'c':
322 zero = zero ? '0' : ' ';
323 width--;
324 while(width-- > 0)
325 put_c(b, esc, zero);
326 put_c(b, esc, va_arg(ap, int));
327 break;
328 case 'd': case 'i':
329 switch(lflag) {
330 case 0:
331 dec(b, esc, va_arg(ap, int), width, zero);
332 break;
333 case 1:
334 dec(b, esc, va_arg(ap, long), width, zero);
335 break;
336 default:
337 dec(b, esc, va_arg(ap, long long), width, zero);
338 break;
339 }
340 break;
341 case 'l':
342 lflag++;
343 fmt++;
344 continue;
345 case 'p':
346 hex(b, esc, (unsigned long)va_arg(ap, void *), width, zero, 0, 1);
347 break;
348 case 's':
349 cp = va_arg(ap, char *);
350 width -= strlen(cp);
351 zero = zero ? '0' : ' ';
352 while(width-- > 0)
353 put_c(b, esc, zero);
354 put_s(b, esc, cp);
355 break;
356 case 'u':
357 switch(lflag) {
358 case 0:
359 udec(b, esc, va_arg(ap, unsigned int), width, zero);
360 break;
361 case 1:
362 udec(b, esc, va_arg(ap, unsigned long), width, zero);
363 break;
364 default:
365 udec(b, esc, va_arg(ap, unsigned long long), width, zero);
366 break;
367 }
368 break;
369 case 'X': case 'x':
370 switch(lflag) {
371 case 0:
372 hex(b, esc, va_arg(ap, unsigned int), width, zero,
373 *fmt == 'X', 0);
374 break;
375 case 1:
376 hex(b, esc, va_arg(ap, unsigned long), width, zero,
377 *fmt == 'X', 0);
378 break;
379 default:
380 hex(b, esc, va_arg(ap, unsigned long long), width, zero,
381 *fmt == 'X', 0);
382 break;
383 }
384 break;
385 case 'y':
386 switch(lflag) {
387 case 0:
388 ydec(b, esc, va_arg(ap, unsigned int), width, zero);
389 break;
390 case 1:
391 ydec(b, esc, va_arg(ap, unsigned long), width, zero);
392 break;
393 default:
394 ydec(b, esc, va_arg(ap, unsigned long long), width, zero);
395 break;
396 }
397 break;
398 default:
399 put_c(b, esc, *fmt);
400 break;
401 }
402 break;
403 }
404 fmt++;
405 }
406 }
407
408 /*
409 * A simplified vfprintf variant. The format string is interpreted with
410 * arguments from the va_list, and the results are written to the given
411 * file descriptor.
412 */
413 void
414 _simple_vdprintf(int fd, const char *fmt, va_list ap)
415 {
416 BUF b;
417 char buf[MYBUFSIZE];
418
419 b.buf = buf;
420 b.fd = fd;
421 b.ptr = b.buf;
422 b.end = b.buf + MYBUFSIZE;
423 b.full = _flush_reset;
424 __simple_bprintf(&b, NULL, fmt, ap);
425 _flush(&b);
426 }
427
428 /*
429 * A simplified fprintf variant. The format string is interpreted with
430 * arguments from the variable argument list, and the results are written
431 * to the given file descriptor.
432 */
433 void
434 _simple_dprintf(int fd, const char *fmt, ...)
435 {
436 va_list ap;
437
438 va_start(ap, fmt);
439 _simple_vdprintf(fd, fmt, ap);
440 va_end(ap);
441 }
442
443 /*
444 * A simplified string allocate routine. Pass the opaque pointer to structure
445 * to _simple_*sprintf() routines. Use _simple_string() to retrieve the
446 * current string (the string is guaranteed to be null terminated only on
447 * the call to _simple_string()). Use _simple_sfree() to free the structure
448 * and string memory.
449 */
450 _SIMPLE_STRING
451 _simple_salloc(void)
452 {
453 SBUF *b;
454
455 if(vm_allocate(mach_task_self(), (vm_address_t *)&b, VM_PAGE_SIZE, 1))
456 return NULL;
457 b->b.ptr = b->b.buf = (char *)b + sizeof(SBUF);
458 b->b.end = (char *)b + VM_PAGE_SIZE - 1;
459 b->b.full = _enlarge;
460 return (_SIMPLE_STRING)b;
461 }
462
463 /*
464 * The format string is interpreted with arguments from the va_list, and the
465 * results are appended to the string maintained by the opaque structure, as
466 * returned by a previous call to _simple_salloc(). Non-zero is returned on
467 * out-of-memory error.
468 */
469 int
470 _simple_vsprintf(_SIMPLE_STRING b, const char *fmt, va_list ap)
471 {
472 return _simple_vesprintf(b, NULL, fmt, ap);
473 }
474
475 /*
476 * The format string is interpreted with arguments from the variable argument
477 * list, and the results are appended to the string maintained by the opaque
478 * structure, as returned by a previous call to _simple_salloc(). Non-zero is
479 * returned on out-of-memory error.
480 */
481 int
482 _simple_sprintf(_SIMPLE_STRING b, const char *fmt, ...)
483 {
484 va_list ap;
485 int ret;
486
487 va_start(ap, fmt);
488 ret = _simple_vesprintf(b, NULL, fmt, ap);
489 va_end(ap);
490 return ret;
491 }
492
493 /*
494 * Like _simple_vsprintf(), except __esc is a function to call on each
495 * character; the function returns NULL if the character should be passed
496 * as is, otherwise, the returned character string is used instead.
497 */
498 int
499 _simple_vesprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, va_list ap)
500 {
501 if(setjmp(((SBUF *)b)->j))
502 return -1;
503 __simple_bprintf((BUF *)b, esc, fmt, ap);
504 return 0;
505 }
506
507 /*
508 * Like _simple_sprintf(), except __esc is a function to call on each
509 * character; the function returns NULL if the character should be passed
510 * as is, otherwise, the returned character string is used instead.
511 */
512 int _simple_esprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, ...)
513 {
514 va_list ap;
515 int ret;
516
517 va_start(ap, fmt);
518 ret = _simple_vesprintf(b, esc, fmt, ap);
519 va_end(ap);
520 return ret;
521 }
522
523 /*
524 * Return the null terminated string from the opaque structure, as returned
525 * by a previous call to _simple_salloc().
526 */
527 char *
528 _simple_string(_SIMPLE_STRING b)
529 {
530 *((BUF *)b)->ptr = 0;
531 return ((BUF *)b)->buf;
532 }
533
534 /*
535 * Reposition the pointer to the first null in the buffer. After a call to
536 * _simple_string, the buffer can be modified, and shrunk.
537 */
538 void
539 _simple_sresize(_SIMPLE_STRING b)
540 {
541 ((BUF *)b)->ptr = ((BUF *)b)->buf + strlen(((BUF *)b)->buf);
542 }
543
544 /*
545 * Append the null-terminated string to the string associated with the opaque
546 * structure. Non-zero is returned on out-of-memory error.
547 */
548 int
549 _simple_sappend(_SIMPLE_STRING b, const char *str)
550 {
551 return _simple_esappend(b, NULL, str);
552 }
553
554 /*
555 * Like _simple_sappend(), except __esc is a function to call on each
556 * character; the function returns NULL if the character should be passed
557 * as is, otherwise, the returned character string is used instead.
558 */
559 int _simple_esappend(_SIMPLE_STRING b, _esc_func esc, const char *str)
560 {
561 if(setjmp(((SBUF *)b)->j))
562 return -1;
563 put_s((BUF *)b, esc, str);
564 return 0;
565 }
566
567 /*
568 * Write the string associated with the opaque structure to the file descriptor.
569 */
570 void
571 _simple_put(_SIMPLE_STRING b, int fd)
572 {
573 ((BUF *)b)->fd = fd;
574 _flush((BUF *)b);
575 }
576
577 /*
578 * Write the string associated with the opaque structure and a trailing newline,
579 * to the file descriptor.
580 */
581 void
582 _simple_putline(_SIMPLE_STRING b, int fd)
583 {
584 ((BUF *)b)->fd = fd;
585 *((BUF *)b)->ptr++ = '\n';
586 _flush((BUF *)b);
587 ((BUF *)b)->ptr--;
588 }
589
590 /*
591 * Free the opaque structure, and the associated string.
592 */
593 void
594 _simple_sfree(_SIMPLE_STRING b)
595 {
596 vm_size_t s;
597
598 if(((intptr_t)(((SBUF *)b)->b.buf) & (VM_PAGE_SIZE - 1)) == 0) {
599 vm_deallocate(mach_task_self(), (vm_address_t)((SBUF *)b)->b.buf, SBUF_SIZE(b));
600 s = VM_PAGE_SIZE;
601 } else
602 s = ((SBUF *)b)->b.end - (char *)b + 1;
603 vm_deallocate(mach_task_self(), (vm_address_t)b, s);
604 }
605
606 /*
607 * Simplified ASL log interface; does not use malloc. Unfortunately, this
608 * requires knowledge of the format used by ASL.
609 */
610 static void
611 socket_init(void)
612 {
613 struct sockaddr_un server;
614 _asl_server_socket(&asl_socket, &server);
615 }
616
617 void
618 _simple_asl_log(int level, const char *facility, const char *message)
619 {
620 _SIMPLE_STRING b;
621
622 if(pthread_once(&asl_socket_once, socket_init) != 0)
623 return;
624 if((b = _simple_salloc()) == NULL)
625 return;
626 do {
627 char *cp, *bp;
628 unsigned u;
629 struct timeval tv;
630
631 if(_simple_sprintf(b, "%10u [Time ", 0))
632 break;
633 gettimeofday(&tv, NULL);
634 if(_simple_esprintf(b, _asl_escape, "%lu", tv.tv_sec))
635 break;
636 if(_simple_sappend(b, "] [Host] [Sender "))
637 break;
638 if(_simple_esappend(b, _asl_escape, getprogname()))
639 break;
640 if(_simple_sappend(b, "] [PID "))
641 break;
642 if(_simple_esprintf(b, _asl_escape, "%u", getpid()))
643 break;
644 if(_simple_sappend(b, "] [UID "))
645 break;
646 if(_simple_esprintf(b, _asl_escape, "%d", getuid()))
647 break;
648 if(_simple_sappend(b, "] [GID "))
649 break;
650 if(_simple_esprintf(b, _asl_escape, "%d", getgid()))
651 break;
652 if(_simple_sappend(b, "] [Level "))
653 break;
654 if(_simple_esprintf(b, _asl_escape, "%d", level))
655 break;
656 if(_simple_sappend(b, "] [Message "))
657 break;
658 if(_simple_esappend(b, _asl_escape, message))
659 break;
660 /* remove trailing (escaped) newlines */
661 cp = _simple_string(b);
662 cp += strlen(cp);
663 for(;;) {
664 cp -= 2;
665 if(strcmp(cp, "\\n") != 0)
666 break;
667 *cp = 0;
668 }
669 _simple_sresize(b);
670 if(_simple_sappend(b, "] [Facility "))
671 break;
672 if(_simple_esappend(b, _asl_escape, facility))
673 break;
674 if(_simple_sappend(b, "]\n"))
675 break;
676 cp = _simple_string(b);
677 u = strlen(cp) - 10; // includes newline and null
678 bp = cp + 10;
679 if(u == 0)
680 *--bp = '0';
681 else
682 while(bp > cp && u) {
683 *--bp = u % 10 + '0';
684 u /= 10;
685 }
686 write(asl_socket, cp, strlen(cp) + 1);
687 } while(0);
688 _simple_sfree(b);
689 }