]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/daemon.c
ca428fcc0e8a387273c5e21610f7349cbf09fd76
[apple/syslog.git] / syslogd.tproj / daemon.c
1 /*
2 * Copyright (c) 2004-2009 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 <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/ucred.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #define SYSLOG_NAMES
35 #include <syslog.h>
36 #include <sys/fslog.h>
37 #include <vproc.h>
38 #include <pthread.h>
39 #include <vproc_priv.h>
40 #include <mach/mach.h>
41 #include <assert.h>
42 #include <libkern/OSAtomic.h>
43 #include "daemon.h"
44
45 #define LIST_SIZE_DELTA 256
46
47 #define streq(A,B) (strcmp(A,B)==0)
48 #define forever for(;;)
49 #define IndexNull ((uint32_t)-1)
50
51 #define ASL_MSG_TYPE_MASK 0x0000000f
52 #define ASL_TYPE_ERROR 2
53
54 #define ASL_KEY_FACILITY "Facility"
55
56 #define FACILITY_USER "user"
57 #define FACILITY_CONSOLE "com.apple.console"
58 #define SYSTEM_RESERVED "com.apple.system"
59 #define SYSTEM_RESERVED_LEN 16
60
61 #define VERIFY_STATUS_OK 0
62 #define VERIFY_STATUS_INVALID_MESSAGE 1
63 #define VERIFY_STATUS_EXCEEDED_QUOTA 2
64
65 extern void disaster_message(asl_msg_t *m);
66 static char myname[MAXHOSTNAMELEN + 1] = {0};
67
68 static OSSpinLock count_lock = 0;
69
70 #ifndef CONFIG_IPHONE
71 static vproc_transaction_t vproc_trans = {0};
72 #endif
73
74 #define QUOTA_TABLE_SIZE 8192
75 #define QUOTA_TABLE_SLOTS 8
76
77 #define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***"
78 #define QUOTA_EXCEEDED_LEVEL "3"
79
80 static time_t quota_table_time = 0;
81 static pid_t quota_table_pid[QUOTA_TABLE_SIZE];
82 static int32_t quota_table_quota[QUOTA_TABLE_SIZE];
83
84 static const char *kern_notify_key[] =
85 {
86 "com.apple.system.log.kernel.emergency",
87 "com.apple.system.log.kernel.alert",
88 "com.apple.system.log.kernel.critical",
89 "com.apple.system.log.kernel.error",
90 "com.apple.system.log.kernel.warning",
91 "com.apple.system.log.kernel.notice",
92 "com.apple.system.log.kernel.info",
93 "com.apple.system.log.kernel.debug"
94 };
95
96 struct asloutput
97 {
98 aslsendmsgfn sendmsg;
99 const char *outid;
100 TAILQ_ENTRY(asloutput) entries;
101 };
102
103 struct aslmatch
104 {
105 char *outid;
106 asl_msg_t *query;
107 TAILQ_ENTRY(aslmatch) entries;
108 };
109
110 TAILQ_HEAD(ae, aslevent) Eventq;
111 TAILQ_HEAD(ao, asloutput) Outq;
112 TAILQ_HEAD(am, aslmatch) Matchq;
113
114 static char **
115 _insertString(char *s, char **l, uint32_t x)
116 {
117 int i, len;
118
119 if (s == NULL) return l;
120 if (l == NULL)
121 {
122 l = (char **)malloc(2 * sizeof(char *));
123 if (l == NULL) return NULL;
124
125 l[0] = strdup(s);
126 if (l[0] == NULL)
127 {
128 free(l);
129 return NULL;
130 }
131
132 l[1] = NULL;
133 return l;
134 }
135
136 for (i = 0; l[i] != NULL; i++);
137 len = i + 1; /* count the NULL on the end of the list too! */
138
139 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
140 if (l == NULL) return NULL;
141
142 if ((x >= (len - 1)) || (x == IndexNull))
143 {
144 l[len - 1] = strdup(s);
145 if (l[len - 1] == NULL)
146 {
147 free(l);
148 return NULL;
149 }
150
151 l[len] = NULL;
152 return l;
153 }
154
155 for (i = len; i > x; i--) l[i] = l[i - 1];
156 l[x] = strdup(s);
157 if (l[x] == NULL) return NULL;
158
159 return l;
160 }
161
162 char **
163 explode(const char *s, const char *delim)
164 {
165 char **l = NULL;
166 const char *p;
167 char *t, quote;
168 int i, n;
169
170 if (s == NULL) return NULL;
171
172 quote = '\0';
173
174 p = s;
175 while (p[0] != '\0')
176 {
177 /* scan forward */
178 for (i = 0; p[i] != '\0'; i++)
179 {
180 if (quote == '\0')
181 {
182 /* not inside a quoted string: check for delimiters and quotes */
183 if (strchr(delim, p[i]) != NULL) break;
184 else if (p[i] == '\'') quote = p[i];
185 else if (p[i] == '"') quote = p[i];
186 }
187 else
188 {
189 /* inside a quoted string - look for matching quote */
190 if (p[i] == quote) quote = '\0';
191 }
192 }
193
194 n = i;
195 t = malloc(n + 1);
196 if (t == NULL) return NULL;
197
198 for (i = 0; i < n; i++) t[i] = p[i];
199 t[n] = '\0';
200 l = _insertString(t, l, IndexNull);
201 free(t);
202 t = NULL;
203 if (p[i] == '\0') return l;
204 if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
205 p = p + i + 1;
206 }
207
208 return l;
209 }
210
211 void
212 freeList(char **l)
213 {
214 int i;
215
216 if (l == NULL) return;
217 for (i = 0; l[i] != NULL; i++) free(l[i]);
218 free(l);
219 }
220
221 /*
222 * Quotas are maintained using a very fast fixed-size table.
223 * We hash into the pid table (quota_table_pid) using the last 10
224 * bits of the pid, so the table has 1024 "buckets". The table is
225 * actually just an array with 8 entry slots (for collisions) per bucket.
226 * If there are more than 8 pids that hash to the same bucket, we
227 * re-use the one with the lowest message usage (highest remaining
228 * quota). This can lead to "generosity: if there are nine or more
229 * pids with the same last 10 bits all logging like crazy, we may
230 * end up allowing some of them to log more than their quota.
231 * That would be a remarkably rare occurrence.
232 */
233
234 static uint32_t
235 quota_check(pid_t pid, time_t now, asl_msg_t *msg)
236 {
237 int i, x, maxx, max;
238 char *str;
239
240 if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
241 if (global.mps_limit == 0) return VERIFY_STATUS_OK;
242
243 OSSpinLockLock(&global.lock);
244
245 if (quota_table_time != now)
246 {
247 memset(quota_table_pid, 0, sizeof(quota_table_pid));
248 quota_table_time = now;
249 }
250
251 /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */
252 x = (pid & 0x000003ff) << 3;
253 maxx = x;
254 max = quota_table_quota[x];
255
256 for (i = 0; i < QUOTA_TABLE_SLOTS; i++)
257 {
258 if (quota_table_pid[x] == 0)
259 {
260 quota_table_pid[x] = pid;
261 quota_table_quota[x] = global.mps_limit;
262
263 OSSpinLockUnlock(&global.lock);
264 return VERIFY_STATUS_OK;
265 }
266
267 if (quota_table_pid[x] == pid)
268 {
269 quota_table_quota[x] = quota_table_quota[x] - 1;
270
271 if (quota_table_quota[x] == 0)
272 {
273 quota_table_quota[x] = -1;
274
275 str = NULL;
276 asprintf(&str, QUOTA_EXCEEDED_MESSAGE, (int)pid, global.mps_limit);
277 if (str != NULL)
278 {
279 asl_set(msg, ASL_KEY_MSG, str);
280 free(str);
281 asl_set(msg, ASL_KEY_LEVEL, QUOTA_EXCEEDED_LEVEL);
282 }
283
284 OSSpinLockUnlock(&global.lock);
285 return VERIFY_STATUS_OK;
286 }
287
288 if (quota_table_quota[x] < 0)
289 {
290 OSSpinLockUnlock(&global.lock);
291 return VERIFY_STATUS_EXCEEDED_QUOTA;
292 }
293
294 OSSpinLockUnlock(&global.lock);
295 return VERIFY_STATUS_OK;
296 }
297
298 if (quota_table_quota[x] > max)
299 {
300 maxx = x;
301 max = quota_table_quota[x];
302 }
303
304 x += 1;
305 }
306
307 /* can't find the pid and no slots were available - reuse slot with highest remaining quota */
308 quota_table_pid[maxx] = pid;
309 quota_table_quota[maxx] = global.mps_limit;
310
311 OSSpinLockUnlock(&global.lock);
312 return VERIFY_STATUS_OK;
313 }
314
315 int
316 asl_check_option(asl_msg_t *msg, const char *opt)
317 {
318 const char *p;
319 uint32_t len;
320
321 if (msg == NULL) return 0;
322 if (opt == NULL) return 0;
323
324 len = strlen(opt);
325 if (len == 0) return 0;
326
327 p = asl_get(msg, ASL_KEY_OPTION);
328 if (p == NULL) return 0;
329
330 while (*p != '\0')
331 {
332 while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++;
333 if (*p == '\0') return 0;
334
335 if (strncasecmp(p, opt, len) == 0)
336 {
337 p += len;
338 if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1;
339 }
340
341 while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++;
342 }
343
344 return 0;
345 }
346
347 int
348 aslevent_init(void)
349 {
350 TAILQ_INIT(&Eventq);
351 TAILQ_INIT(&Outq);
352 TAILQ_INIT(&Matchq);
353
354 return 0;
355 }
356
357 int
358 aslevent_log(asl_msg_t *msg, char *outid)
359 {
360 struct asloutput *i;
361 int status = -1;
362
363 for (i = Outq.tqh_first; i != NULL; i = i->entries.tqe_next)
364 {
365 if ((outid != NULL) && (strcmp(i->outid, outid) == 0))
366 {
367 status = i->sendmsg(msg, outid);
368 }
369 }
370
371 return status;
372 }
373
374 int
375 aslevent_addmatch(asl_msg_t *query, char *outid)
376 {
377 struct aslmatch *tmp;
378
379 if (query == NULL) return -1;
380 if (outid == NULL) return -1;
381
382 tmp = calloc(1, sizeof(struct aslmatch));
383 if (tmp == NULL) return -1;
384
385 tmp->query = query;
386 tmp->outid = outid;
387 TAILQ_INSERT_TAIL(&Matchq, tmp, entries);
388
389 return 0;
390 }
391
392 void
393 asl_message_match_and_log(asl_msg_t *msg)
394 {
395 struct aslmatch *i;
396
397 if (msg == NULL) return;
398
399 for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next)
400 {
401 if (asl_msg_cmp(i->query, msg) != 0)
402 {
403 aslevent_log(msg, i->outid);
404 }
405 }
406 }
407
408 int
409 aslevent_removefd(int fd)
410 {
411 struct aslevent *e, *next;
412
413 e = Eventq.tqh_first;
414
415 while (e != NULL)
416 {
417 next = e->entries.tqe_next;
418 if (fd == e->fd)
419 {
420 e->fd = -1;
421 return 0;
422 }
423
424 e = next;
425 }
426
427 return -1;
428 }
429
430 const char *
431 whatsmyhostname()
432 {
433 char *dot;
434
435 if (gethostname(myname, MAXHOSTNAMELEN) < 0)
436 {
437 memset(myname, 0, sizeof(myname));
438 return "localhost";
439 }
440
441 dot = strchr(myname, '.');
442 if (dot != NULL) *dot = '\0';
443
444 return (const char *)myname;
445 }
446
447 void
448 asl_client_count_increment()
449 {
450 OSSpinLockLock(&count_lock);
451
452 #ifndef CONFIG_IPHONE
453 if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL);
454 #endif
455 global.client_count++;
456 #ifdef DEBUG
457 asldebug("global.client_count++ (%d)\n", global.client_count);
458 #endif
459
460 OSSpinLockUnlock(&count_lock);
461 }
462
463 void
464 asl_client_count_decrement()
465 {
466 OSSpinLockLock(&count_lock);
467
468 if (global.client_count > 0) global.client_count--;
469 #ifndef CONFIG_IPHONE
470 if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans);
471 #endif
472 #ifdef DEBUG
473 asldebug("global.client_count-- (%d)\n", global.client_count);
474 #endif
475
476 OSSpinLockUnlock(&count_lock);
477 }
478
479 int
480 aslevent_addfd(int source, int fd, uint32_t flags, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn)
481 {
482 struct aslevent *e;
483 int found = 0, status;
484 #ifdef LOCAL_PEERCRED
485 struct xucred cr;
486 #endif
487 socklen_t len;
488 uid_t u;
489 gid_t g;
490 struct sockaddr_storage ss;
491 char *sender, str[256];
492
493 u = 99;
494 g = 99;
495 sender = NULL;
496
497 memset(&ss, 0, sizeof(struct sockaddr_storage));
498 memset(str, 0, sizeof(str));
499
500 len = sizeof(struct sockaddr_storage);
501
502 if (flags & ADDFD_FLAGS_LOCAL)
503 {
504 snprintf(str, sizeof(str), "localhost");
505 sender = str;
506
507 #ifdef LOCAL_PEERCRED
508 len = sizeof(cr);
509
510 status = getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len);
511 if (status == 0)
512 {
513 u = cr.cr_uid;
514 g = cr.cr_gid;
515 }
516 #endif
517 }
518 else
519 {
520 status = getpeername(fd, (struct sockaddr *)&ss, &len);
521 if (status == 0)
522 {
523 if (len == 0)
524 {
525 /* UNIX Domain socket */
526 snprintf(str, sizeof(str), "localhost");
527 sender = str;
528 }
529 else
530 {
531 if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == NULL) sender = str;
532 }
533 }
534 }
535
536 asldebug("source %d fd %d flags 0x%08x UID %d GID %d Sender %s\n", source, fd, flags, u, g, (sender == NULL) ? "NULL" : sender );
537
538 for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
539 {
540 if (fd == e->fd)
541 {
542 e->readfn = readfn;
543 e->writefn = writefn;
544 e->exceptfn = exceptfn;
545 if (e->sender != NULL) free(e->sender);
546 e->sender = NULL;
547 if (sender != NULL)
548 {
549 e->sender = strdup(sender);
550 if (e->sender == NULL) return -1;
551 }
552
553 e->uid = u;
554 e->gid = g;
555 found = 1;
556 }
557 }
558
559 if (found) return 0;
560
561 e = calloc(1, sizeof(struct aslevent));
562 if (e == NULL) return -1;
563
564 e->source = source;
565 e->fd = fd;
566 e->readfn = readfn;
567 e->writefn = writefn;
568 e->exceptfn = exceptfn;
569 e->sender = NULL;
570 if (sender != NULL)
571 {
572 e->sender = strdup(sender);
573 if (e->sender == NULL) return -1;
574 }
575
576 e->uid = u;
577 e->gid = g;
578
579 TAILQ_INSERT_TAIL(&Eventq, e, entries);
580
581 return 0;
582 }
583
584 /*
585 * Checks message content and sets attributes as required
586 *
587 * SOURCE_INTERNAL log messages sent by syslogd itself
588 * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
589 * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
590 * SOURCE_UDP_SOCKET from the network
591 * SOURCE_KERN from the kernel
592 * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
593 * SOURCE_LAUNCHD forwarded from launchd
594 */
595
596 static uint32_t
597 aslmsg_verify(uint32_t source, struct aslevent *e, asl_msg_t *msg, int32_t *kern_post_level)
598 {
599 const char *val, *fac;
600 char buf[64];
601 time_t tick, now;
602 uid_t uid;
603 uint32_t status, level, fnum;
604 pid_t pid;
605
606 if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
607
608 if (kern_post_level != NULL) *kern_post_level = -1;
609
610 /* Time */
611 now = time(NULL);
612
613 tick = 0;
614 val = asl_get(msg, ASL_KEY_TIME);
615 if (val != NULL) tick = asl_parse_time(val);
616
617 /* Set time to now if it is unset or from the future (not allowed!) */
618 if ((tick == 0) || (tick > now)) tick = now;
619
620 /* Canonical form: seconds since the epoch */
621 snprintf(buf, sizeof(buf) - 1, "%lu", tick);
622 asl_set(msg, ASL_KEY_TIME, buf);
623
624 /* Host */
625 if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
626 else if (e->sender != NULL)
627 {
628 if (!strcmp(e->sender, "localhost")) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
629 else asl_set(msg, ASL_KEY_HOST, e->sender);
630 }
631
632 /* PID */
633 pid = 0;
634
635 val = asl_get(msg, ASL_KEY_PID);
636 if (val == NULL) asl_set(msg, ASL_KEY_PID, "0");
637 else pid = (pid_t)atoi(val);
638
639 /* if PID is 1 (launchd), use the refpid if there is one */
640 if (pid == 1)
641 {
642 val = asl_get(msg, ASL_KEY_REF_PID);
643 if (val != NULL) pid = (pid_t)atoi(val);
644 }
645
646 /* if quotas are enabled and pid > 1 (not kernel or launchd) check quota */
647 if ((global.mps_limit > 0) && (pid > 1))
648 {
649 status = quota_check(pid, now, msg);
650 if (status != VERIFY_STATUS_OK) return status;
651 }
652
653 /* UID */
654 uid = -2;
655 val = asl_get(msg, ASL_KEY_UID);
656
657 switch (source)
658 {
659 case SOURCE_KERN:
660 case SOURCE_INTERNAL:
661 {
662 /* we know the UID is 0 */
663 uid = 0;
664 asl_set(msg, ASL_KEY_UID, "0");
665 break;
666 }
667 case SOURCE_ASL_SOCKET:
668 case SOURCE_ASL_MESSAGE:
669 case SOURCE_LAUNCHD:
670 {
671 /* we trust the UID in the message */
672 if (val != NULL) uid = atoi(val);
673 break;
674 }
675 case SOURCE_BSD_SOCKET:
676 case SOURCE_UDP_SOCKET:
677 {
678 if (val == NULL)
679 {
680 if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2");
681 else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2");
682 else
683 {
684 uid = e->uid;
685 snprintf(buf, sizeof(buf), "%d", e->uid);
686 asl_set(msg, ASL_KEY_UID, buf);
687 }
688 }
689 else if ((e != NULL) && (e->uid != 99))
690 {
691 uid = e->uid;
692 snprintf(buf, sizeof(buf), "%d", e->uid);
693 asl_set(msg, ASL_KEY_UID, buf);
694 }
695 }
696 default:
697 {
698 asl_set(msg, ASL_KEY_UID, "-2");
699 }
700 }
701
702 /* GID */
703 val = asl_get(msg, ASL_KEY_GID);
704
705 switch (source)
706 {
707 case SOURCE_KERN:
708 case SOURCE_INTERNAL:
709 {
710 /* we know the GID is 0 */
711 asl_set(msg, ASL_KEY_GID, "0");
712 break;
713 }
714 case SOURCE_ASL_SOCKET:
715 case SOURCE_ASL_MESSAGE:
716 case SOURCE_LAUNCHD:
717 {
718 /* we trust the GID in the message */
719 break;
720 }
721 case SOURCE_BSD_SOCKET:
722 case SOURCE_UDP_SOCKET:
723 {
724 if (val == NULL)
725 {
726 if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2");
727 else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2");
728 else
729 {
730 snprintf(buf, sizeof(buf), "%d", e->gid);
731 asl_set(msg, ASL_KEY_GID, buf);
732 }
733 }
734 else if ((e != NULL) && (e->gid != 99))
735 {
736 snprintf(buf, sizeof(buf), "%d", e->gid);
737 asl_set(msg, ASL_KEY_GID, buf);
738 }
739 }
740 default:
741 {
742 asl_set(msg, ASL_KEY_GID, "-2");
743 }
744 }
745
746 /* Sender */
747 val = asl_get(msg, ASL_KEY_SENDER);
748 if (val == NULL)
749 {
750 switch (source)
751 {
752 case SOURCE_KERN:
753 {
754 asl_set(msg, ASL_KEY_SENDER, "kernel");
755 break;
756 }
757 case SOURCE_INTERNAL:
758 {
759 asl_set(msg, ASL_KEY_SENDER, "syslogd");
760 break;
761 }
762 default:
763 {
764 asl_set(msg, ASL_KEY_SENDER, "Unknown");
765 }
766 }
767 }
768 else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(val, "kernel")))
769 {
770 /* allow UID 0 to send messages with "Sender kernel", but nobody else */
771 asl_set(msg, ASL_KEY_SENDER, "Unknown");
772 }
773
774 /* Level */
775 val = asl_get(msg, ASL_KEY_LEVEL);
776 level = ASL_LEVEL_DEBUG;
777 if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
778 snprintf(buf, sizeof(buf), "%d", level);
779 asl_set(msg, ASL_KEY_LEVEL, buf);
780
781 /* Facility */
782 fac = asl_get(msg, ASL_KEY_FACILITY);
783 if (fac == NULL)
784 {
785 if (source == SOURCE_KERN) fac = "kern";
786 else fac = "user";
787 asl_set(msg, ASL_KEY_FACILITY, fac);
788 }
789 else if (fac[0] == '#')
790 {
791 fnum = LOG_USER;
792 if ((fac[1] >= '0') && (fac[1] <= '9'))
793 {
794 fnum = atoi(fac + 1) << 3;
795 if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER;
796 }
797
798 fac = asl_syslog_faciliy_num_to_name(fnum);
799 asl_set(msg, ASL_KEY_FACILITY, fac);
800 }
801 else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
802 {
803 /* only UID 0 may use "com.apple.system" */
804 if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER);
805 }
806
807 /*
808 * kernel messages are only readable by root and admin group.
809 */
810 if (source == SOURCE_KERN)
811 {
812 asl_set(msg, ASL_KEY_READ_UID, "0");
813 asl_set(msg, ASL_KEY_READ_GID, "80");
814 }
815
816 /*
817 * Access Control: only UID 0 may use facility com.apple.system (or anything with that prefix).
818 * N.B. kernel can use any facility name.
819 */
820
821 /* Set DB Expire Time for com.apple.system.utmpx and lastlog */
822 if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog")))
823 {
824 snprintf(buf, sizeof(buf), "%lu", tick + global.utmp_ttl);
825 asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
826 }
827
828 /* Set DB Expire Time for Filestsrem errors */
829 if (!strcmp(fac, FSLOG_VAL_FACILITY))
830 {
831 snprintf(buf, sizeof(buf), "%lu", tick + global.fs_ttl);
832 asl_set(msg, ASL_KEY_EXPIRE_TIME, buf);
833 }
834
835 /*
836 * special case handling of kernel disaster messages
837 */
838 if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL))
839 {
840 if (kern_post_level != NULL) *kern_post_level = level;
841 disaster_message(msg);
842 }
843
844 return VERIFY_STATUS_OK;
845 }
846
847 int
848 aslevent_addoutput(aslsendmsgfn fn, const char *outid)
849 {
850 struct asloutput *tmp;
851
852 tmp = calloc(1, sizeof(struct asloutput));
853 if (tmp == NULL) return -1;
854
855 tmp->sendmsg = fn;
856 tmp->outid = outid;
857
858 TAILQ_INSERT_TAIL(&Outq, tmp, entries);
859
860 return 0;
861 }
862
863 int
864 aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex)
865 {
866 struct aslevent *e;
867 int status = 0;
868
869 // asldebug("--> aslevent_fdsets\n");
870 FD_ZERO(rd);
871 FD_ZERO(wr);
872 FD_ZERO(ex);
873
874 for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
875 {
876 if (e->fd < 0) continue;
877
878 // asldebug("adding fd %d\n", e->fd);
879 if (e->readfn)
880 {
881 FD_SET(e->fd, rd);
882 status = MAX(e->fd, status);
883 }
884
885 if (e->writefn)
886 {
887 FD_SET(e->fd, wr);
888 status = MAX(e->fd, status);
889 }
890
891 if (e->exceptfn)
892 {
893 FD_SET(e->fd, ex);
894 status = MAX(e->fd, status);
895 }
896 }
897
898 // asldebug("<--aslevent_fdsets\n");
899 return status;
900 }
901
902 void
903 aslevent_cleanup()
904 {
905 struct aslevent *e, *next;
906
907 e = Eventq.tqh_first;
908
909 while (e != NULL)
910 {
911 next = e->entries.tqe_next;
912 if (e->fd < 0)
913 {
914 TAILQ_REMOVE(&Eventq, e, entries);
915 if (e->sender != NULL) free(e->sender);
916 free(e);
917 }
918
919 e = next;
920 }
921 }
922
923 void
924 list_append_msg(asl_search_result_t *list, asl_msg_t *msg)
925 {
926 if (list == NULL) return;
927 if (msg == NULL) return;
928
929 /*
930 * NB: curr is the list size
931 * grow list if necessary
932 */
933 if (list->count == list->curr)
934 {
935 if (list->curr == 0)
936 {
937 list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
938 }
939 else
940 {
941 list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
942 }
943
944 if (list->msg == NULL)
945 {
946 list->curr = 0;
947 list->count = 0;
948 return;
949 }
950
951 list->curr += LIST_SIZE_DELTA;
952 }
953
954 list->msg[list->count] = msg;
955 list->count++;
956 }
957
958 void
959 work_enqueue(asl_msg_t *m)
960 {
961 pthread_mutex_lock(global.work_queue_lock);
962 list_append_msg(global.work_queue, m);
963 pthread_mutex_unlock(global.work_queue_lock);
964 pthread_cond_signal(&global.work_queue_cond);
965 }
966
967 void
968 asl_enqueue_message(uint32_t source, struct aslevent *e, asl_msg_t *msg)
969 {
970 int32_t kplevel;
971 uint32_t status;
972
973 if (msg == NULL) return;
974
975 /* set retain count to 1 */
976 msg->type |= 0x10;
977
978 kplevel = -1;
979 status = aslmsg_verify(source, e, msg, &kplevel);
980 if (status == VERIFY_STATUS_OK)
981 {
982 if ((source == SOURCE_KERN) && (kplevel >= 0)) notify_post(kern_notify_key[kplevel]);
983 work_enqueue(msg);
984 }
985 else
986 {
987 asl_msg_release(msg);
988 }
989 }
990
991 asl_msg_t **
992 asl_work_dequeue(uint32_t *count)
993 {
994 asl_msg_t **work;
995
996 pthread_mutex_lock(global.work_queue_lock);
997 pthread_cond_wait(&global.work_queue_cond, global.work_queue_lock);
998
999 work = NULL;
1000 *count = 0;
1001
1002 if (global.work_queue->count == 0)
1003 {
1004 pthread_mutex_unlock(global.work_queue_lock);
1005 return NULL;
1006 }
1007
1008 work = global.work_queue->msg;
1009 *count = global.work_queue->count;
1010
1011 global.work_queue->count = 0;
1012 global.work_queue->curr = 0;
1013 global.work_queue->msg = NULL;
1014
1015 pthread_mutex_unlock(global.work_queue_lock);
1016 return work;
1017 }
1018
1019 void
1020 aslevent_handleevent(fd_set *rd, fd_set *wr, fd_set *ex)
1021 {
1022 struct aslevent *e;
1023 char *out = NULL;
1024 asl_msg_t *msg;
1025 int32_t cleanup;
1026
1027 // asldebug("--> aslevent_handleevent\n");
1028
1029 cleanup = 0;
1030
1031 for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next)
1032 {
1033 if (e->fd < 0)
1034 {
1035 cleanup = 1;
1036 continue;
1037 }
1038
1039 if (FD_ISSET(e->fd, rd) && (e->readfn != NULL))
1040 {
1041 // asldebug("handling read event on %d\n", e->fd);
1042 msg = e->readfn(e->fd);
1043 if (msg == NULL) continue;
1044
1045 asl_enqueue_message(e->source, e, msg);
1046 }
1047
1048 if (FD_ISSET(e->fd, ex) && e->exceptfn)
1049 {
1050 asldebug("handling except event on %d\n", e->fd);
1051 out = e->exceptfn(e->fd);
1052 if (out == NULL) asldebug("error writing message\n\n");
1053 }
1054 }
1055
1056 if (cleanup != 0) aslevent_cleanup();
1057
1058 // asldebug("<-- aslevent_handleevent\n");
1059 }
1060
1061 int
1062 asl_log_string(const char *str)
1063 {
1064 asl_msg_t *msg;
1065
1066 if (str == NULL) return 1;
1067
1068 msg = asl_msg_from_string(str);
1069 if (msg == NULL) return 1;
1070
1071 asl_enqueue_message(SOURCE_INTERNAL, NULL, msg);
1072
1073 return 0;
1074 }
1075
1076 int
1077 asldebug(const char *str, ...)
1078 {
1079 va_list v;
1080 int status;
1081 FILE *dfp;
1082
1083 OSSpinLockLock(&global.lock);
1084 if (global.debug == 0)
1085 {
1086 OSSpinLockUnlock(&global.lock);
1087 return 0;
1088 }
1089
1090 dfp = stderr;
1091 if (global.debug_file != NULL) dfp = fopen(global.debug_file, "a");
1092 if (dfp == NULL)
1093 {
1094 OSSpinLockUnlock(&global.lock);
1095 return 0;
1096 }
1097
1098 va_start(v, str);
1099 status = vfprintf(dfp, str, v);
1100 va_end(v);
1101
1102 if (global.debug_file != NULL) fclose(dfp);
1103 OSSpinLockUnlock(&global.lock);
1104
1105 return status;
1106 }
1107
1108 void
1109 asl_mark(void)
1110 {
1111 char *str;
1112
1113 str = NULL;
1114 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]",
1115 ASL_KEY_SENDER,
1116 ASL_KEY_LEVEL, ASL_LEVEL_INFO,
1117 ASL_KEY_PID, getpid(),
1118 ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID);
1119
1120 asl_log_string(str);
1121 if (str != NULL) free(str);
1122 }
1123
1124 asl_msg_t *
1125 asl_syslog_input_convert(const char *in, int len, char *rhost, int kern)
1126 {
1127 int pf, pri, index, n;
1128 char *p, *colon, *brace, *tmp, *tval, *sval, *pval, *mval;
1129 char prival[8];
1130 const char *fval;
1131 asl_msg_t *msg;
1132 struct tm time;
1133 time_t tick;
1134
1135 if (in == NULL) return NULL;
1136 if (len <= 0) return NULL;
1137
1138 pri = LOG_DEBUG;
1139 tval = NULL;
1140 sval = NULL;
1141 pval = NULL;
1142 mval = NULL;
1143 fval = NULL;
1144
1145 index = 0;
1146 p = (char *)in;
1147
1148 while ((index < len) && ((*p == ' ') || (*p == '\t')))
1149 {
1150 p++;
1151 index++;
1152 }
1153
1154 if (index >= len) return NULL;
1155
1156 if (*p == '<')
1157 {
1158 p++;
1159 index++;
1160
1161 n = sscanf(p, "%d", &pf);
1162 if (n == 1)
1163 {
1164 pri = pf & 0x7;
1165 if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK);
1166 }
1167
1168 while ((index < len) && (*p != '>'))
1169 {
1170 p++;
1171 index++;
1172 }
1173
1174 if (index < len)
1175 {
1176 p++;
1177 index++;
1178 }
1179 }
1180
1181 snprintf(prival, sizeof(prival), "%d", pri);
1182
1183 if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' '))
1184 {
1185 tmp = malloc(16);
1186 if (tmp == NULL) return NULL;
1187
1188 memcpy(tmp, p, 15);
1189 tmp[15] = '\0';
1190
1191 tick = asl_parse_time(tmp);
1192 if (tick == (time_t)-1)
1193 {
1194 tval = tmp;
1195 }
1196 else
1197 {
1198 free(tmp);
1199 gmtime_r(&tick, &time);
1200 asprintf(&tval, "%d.%02d.%02d %02d:%02d:%02d UTC", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
1201 }
1202
1203 p += 16;
1204 index += 16;
1205 }
1206
1207 if (kern != 0)
1208 {
1209 msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
1210 if (msg == NULL) return NULL;
1211
1212
1213 asl_set(msg, ASL_KEY_MSG, p);
1214
1215 asl_set(msg, ASL_KEY_LEVEL, prival);
1216
1217 asl_set(msg, ASL_KEY_PID, "0");
1218
1219 asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
1220
1221 return msg;
1222 }
1223
1224 colon = strchr(p, ':');
1225 brace = strchr(p, '[');
1226
1227 if (colon != NULL)
1228 {
1229 if ((brace != NULL) && (brace < colon))
1230 {
1231 n = brace - p;
1232 sval = malloc(n + 1);
1233 if (sval == NULL) return NULL;
1234
1235 memcpy(sval, p, n);
1236 sval[n] = '\0';
1237
1238 n = colon - (brace + 1) - 1;
1239 pval = malloc(n + 1);
1240 if (pval == NULL) return NULL;
1241
1242 memcpy(pval, (brace + 1), n);
1243 pval[n] = '\0';
1244 }
1245 else
1246 {
1247 n = colon - p;
1248 sval = malloc(n + 1);
1249 if (sval == NULL) return NULL;
1250
1251 memcpy(sval, p, n);
1252 sval[n] = '\0';
1253 }
1254
1255 n = colon - p;
1256 p = colon + 1;
1257 index += (n + 1);
1258 }
1259
1260 if (*p == ' ')
1261 {
1262 p++;
1263 index++;
1264 }
1265
1266 n = len - index;
1267 if (n > 0)
1268 {
1269 mval = malloc(n + 1);
1270 if (mval == NULL) return NULL;
1271
1272 memcpy(mval, p, n);
1273 mval[n] = '\0';
1274 }
1275
1276 if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER);
1277
1278 msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
1279 if (msg == NULL) return NULL;
1280
1281 if (tval != NULL)
1282 {
1283 asl_set(msg, ASL_KEY_TIME, tval);
1284 free(tval);
1285 }
1286
1287 if (fval != NULL) asl_set(msg, "Facility", fval);
1288 else asl_set(msg, "Facility", "user");
1289
1290 if (sval != NULL)
1291 {
1292 asl_set(msg, ASL_KEY_SENDER, sval);
1293 free(sval);
1294 }
1295
1296 if (pval != NULL)
1297 {
1298 asl_set(msg, ASL_KEY_PID, pval);
1299 free(pval);
1300 }
1301 else asl_set(msg, ASL_KEY_PID, "-1");
1302
1303 if (mval != NULL)
1304 {
1305 asl_set(msg, ASL_KEY_MSG, mval);
1306 free(mval);
1307 }
1308
1309 asl_set(msg, ASL_KEY_LEVEL, prival);
1310 asl_set(msg, ASL_KEY_UID, "-2");
1311 asl_set(msg, ASL_KEY_GID, "-2");
1312
1313 if (rhost == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname());
1314 else asl_set(msg, ASL_KEY_HOST, rhost);
1315
1316 if (msg->count == 0)
1317 {
1318 asl_msg_release(msg);
1319 return NULL;
1320 }
1321
1322 return msg;
1323 }
1324
1325 asl_msg_t *
1326 asl_input_parse(const char *in, int len, char *rhost, int kern)
1327 {
1328 asl_msg_t *m;
1329 int status, x, legacy;
1330
1331 asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
1332
1333 if (in == NULL) return NULL;
1334
1335 legacy = 1;
1336 m = NULL;
1337
1338 /* calculate length if not provided */
1339 if (len == 0) len = strlen(in);
1340
1341 /*
1342 * Determine if the input is "old" syslog format or new ASL format.
1343 * Old format lines should start with "<", but they can just be straight text.
1344 * ASL input starts with a length (10 bytes) followed by a space and a '['.
1345 */
1346 if ((in[0] != '<') && (len > 11))
1347 {
1348 status = sscanf(in, "%d ", &x);
1349 if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0;
1350 }
1351
1352 if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, kern);
1353
1354 m = asl_msg_from_string(in + 11);
1355 if (m == NULL) return NULL;
1356
1357 if (rhost != NULL) asl_set(m, ASL_KEY_HOST, rhost);
1358
1359 return m;
1360 }
1361
1362 char *
1363 get_line_from_file(FILE *f)
1364 {
1365 char *s, *out;
1366 size_t len;
1367
1368 out = fgetln(f, &len);
1369 if (out == NULL) return NULL;
1370 if (len == 0) return NULL;
1371
1372 s = malloc(len + 1);
1373 if (s == NULL) return NULL;
1374
1375 memcpy(s, out, len);
1376
1377 s[len - 1] = '\0';
1378 return s;
1379 }
1380
1381 uint32_t
1382 asl_msg_type(asl_msg_t *m)
1383 {
1384 if (m == NULL) return ASL_TYPE_ERROR;
1385 return (m->type & ASL_MSG_TYPE_MASK);
1386 }
1387
1388 void
1389 asl_msg_release(asl_msg_t *m)
1390 {
1391 int32_t newval;
1392
1393 if (m == NULL) return;
1394
1395 newval = OSAtomicAdd32(-0x10, (int32_t*)&m->type) >> 4;
1396 assert(newval >= 0);
1397
1398 if (newval > 0) return;
1399
1400 asl_free(m);
1401 }
1402
1403 asl_msg_t *
1404 asl_msg_retain(asl_msg_t *m)
1405 {
1406 int32_t newval;
1407
1408 if (m == NULL) return NULL;
1409
1410 newval = OSAtomicAdd32(0x10, (int32_t*)&m->type) >> 4;
1411 assert(newval > 0);
1412
1413 return m;
1414 }
1415
1416 void
1417 launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, const char *from_name, const char *about_name, const char *session_name, const char *msg)
1418 {
1419 asl_msg_t *m;
1420 char str[256];
1421 time_t now;
1422
1423 /*
1424 asldebug("launchd_callback Time %lu %lu PID %u RefPID %u UID %d GID %d PRI %d Sender %s Ref %s Session %s Message %s\n",
1425 when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1426 */
1427
1428 m = asl_new(ASL_TYPE_MSG);
1429 if (m == NULL) return;
1430
1431 /* Level */
1432 if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG;
1433 if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG;
1434 snprintf(str, sizeof(str), "%d", priority);
1435
1436 asl_set(m, ASL_KEY_LEVEL, str);
1437
1438 /* Time */
1439 if (when != NULL)
1440 {
1441 snprintf(str, sizeof(str), "%lu", when->tv_sec);
1442 asl_set(m, ASL_KEY_TIME, str);
1443
1444 snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec);
1445 asl_set(m, ASL_KEY_TIME_NSEC, str);
1446 }
1447 else
1448 {
1449 now = time(NULL);
1450 snprintf(str, sizeof(str), "%lu", now);
1451 asl_set(m, ASL_KEY_TIME, str);
1452 }
1453
1454 /* Host */
1455 asl_set(m, ASL_KEY_HOST, whatsmyhostname());
1456
1457 /* Facility */
1458 asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
1459
1460 /* UID */
1461 snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
1462 asl_set(m, ASL_KEY_UID, str);
1463
1464 /* GID */
1465 snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
1466 asl_set(m, ASL_KEY_GID, str);
1467
1468 /* PID */
1469 if (from_pid != 0)
1470 {
1471 snprintf(str, sizeof(str), "%u", (unsigned int)from_pid);
1472 asl_set(m, ASL_KEY_PID, str);
1473 }
1474
1475 /* Reference PID */
1476 if ((about_pid > 0) && (about_pid != from_pid))
1477 {
1478 snprintf(str, sizeof(str), "%u", (unsigned int)about_pid);
1479 asl_set(m, ASL_KEY_REF_PID, str);
1480 }
1481
1482 /* Sender */
1483 if (from_name != NULL)
1484 {
1485 asl_set(m, ASL_KEY_SENDER, from_name);
1486 }
1487
1488 /* ReadUID */
1489 if (sender_uid != 0)
1490 {
1491 snprintf(str, sizeof(str), "%d", (int)sender_uid);
1492 asl_set(m, ASL_KEY_READ_UID, str);
1493 }
1494
1495 /* Reference Process */
1496 if (about_name != NULL)
1497 {
1498 if ((from_name != NULL) && (strcmp(from_name, about_name) != 0))
1499 {
1500 asl_set(m, ASL_KEY_REF_PROC, about_name);
1501 }
1502 }
1503
1504 /* Session */
1505 if (session_name != NULL)
1506 {
1507 asl_set(m, ASL_KEY_SESSION, session_name);
1508 }
1509
1510 /* Message */
1511 if (msg != NULL)
1512 {
1513 asl_set(m, ASL_KEY_MSG, msg);
1514 }
1515
1516 /* verify and push to receivers */
1517 asl_enqueue_message(SOURCE_LAUNCHD, NULL, m);
1518 }
1519
1520 void
1521 launchd_drain()
1522 {
1523 forever
1524 {
1525 _vprocmgr_log_drain(NULL, NULL, launchd_callback);
1526 }
1527 }