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