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