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