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