]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/daemon.c
83912fb9c358f267ba68c386d65360e81f3cb1f8
[apple/syslog.git] / syslogd.tproj / daemon.c
1 /*
2 * Copyright (c) 2004-2012 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 <TargetConditionals.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/ucred.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #define SYSLOG_NAMES
36 #include <syslog.h>
37 #include <sys/fslog.h>
38 #include <vproc.h>
39 #include <pthread.h>
40 #include <vproc_priv.h>
41 #include <mach/mach.h>
42 #include <libkern/OSAtomic.h>
43 #include <libproc.h>
44 #include <uuid/uuid.h>
45 #include <asl_private.h>
46 #include "daemon.h"
47
48 #define LIST_SIZE_DELTA 256
49 #define STATS_TABLE_SIZE 256
50
51 #define forever for(;;)
52
53 #define ASL_MSG_TYPE_MASK 0x0000000f
54 #define ASL_TYPE_ERROR 2
55
56 #define ASL_KEY_FACILITY "Facility"
57
58 #define FACILITY_USER "user"
59 #define FACILITY_CONSOLE "com.apple.console"
60 #define SYSTEM_RESERVED "com.apple.system"
61 #define SYSTEM_RESERVED_LEN 16
62
63 #define VERIFY_STATUS_OK 0
64 #define VERIFY_STATUS_INVALID_MESSAGE 1
65 #define VERIFY_STATUS_EXCEEDED_QUOTA 2
66
67 extern void disaster_message(asl_msg_t *m);
68 extern int asl_action_reset(void);
69 extern int asl_action_control_set_param(const char *s);
70
71 static char myname[MAXHOSTNAMELEN + 1] = {0};
72 static int name_change_token = -1;
73
74 static OSSpinLock count_lock = 0;
75
76 #if !TARGET_OS_EMBEDDED
77 static vproc_transaction_t vproc_trans = {0};
78 #endif
79
80 #define DEFAULT_MEMORY_MAX SYSLOGD_WORK_QUEUE_MEMORY
81
82 #define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit - remaining messages this second discarded ***"
83
84 #define DEFAULT_DB_FILE_MAX 25600000
85 #define DEFAULT_DB_MEMORY_MAX 256
86 #define DEFAULT_DB_MEMORY_STR_MAX 1024000
87 #define DEFAULT_MPS_LIMIT 500
88 #define DEFAULT_REMOTE_DELAY 5000
89 #define DEFAULT_BSD_MAX_DUP_SEC 30
90 #define DEFAULT_MARK_SEC 0
91 #define DEFAULT_UTMP_TTL_SEC 31622400
92 #define DEFAULT_STATS_INTERVAL 600
93
94 #define ASL_STATS_LEVEL 5
95
96 static time_t quota_time = 0;
97 static int32_t kern_quota;
98 static int32_t kern_level;
99
100 static const char *kern_notify_key[] =
101 {
102 "com.apple.system.log.kernel.emergency",
103 "com.apple.system.log.kernel.alert",
104 "com.apple.system.log.kernel.critical",
105 "com.apple.system.log.kernel.error",
106 "com.apple.system.log.kernel.warning",
107 "com.apple.system.log.kernel.notice",
108 "com.apple.system.log.kernel.info",
109 "com.apple.system.log.kernel.debug"
110 };
111
112 static int kern_notify_token[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
113
114 static stats_table_t *
115 stats_table_new()
116 {
117 stats_table_t *t = (stats_table_t *)malloc(sizeof(stats_table_t));
118 if (t == NULL) return NULL;
119
120 t->bucket_count = STATS_TABLE_SIZE;
121 t->bucket = (sender_stats_t **)calloc(t->bucket_count, sizeof(sender_stats_t *));
122 if (t->bucket == NULL)
123 {
124 free(t);
125 return NULL;
126 }
127
128 t->mcount = 0;
129 t->shim_count = 0;
130
131 return t;
132 }
133
134 static asl_msg_t *
135 stats_table_final(stats_table_t *t)
136 {
137 uint32_t i;
138 asl_msg_t *msg;
139 char val[64];
140
141 if (t == NULL) return NULL;
142
143 msg = asl_msg_new(ASL_TYPE_MSG);
144 if (msg == NULL) return NULL;
145
146 asl_msg_set_key_val(msg, ASL_KEY_MSG, "ASL Sender Statistics");
147 asl_msg_set_key_val(msg, ASL_KEY_SENDER, "syslogd");
148 asl_msg_set_key_val(msg, ASL_KEY_FACILITY, "com.apple.asl.statistics");
149 snprintf(val, sizeof(val), "%d", global.pid);
150 asl_msg_set_key_val(msg, ASL_KEY_PID, val);
151 snprintf(val, sizeof(val), "%d", ASL_STATS_LEVEL);
152 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, val);
153 snprintf(val, sizeof(val), "%u", t->mcount);
154 asl_msg_set_key_val(msg, "MsgCount", val);
155 snprintf(val, sizeof(val), "%u", t->shim_count);
156 asl_msg_set_key_val(msg, "ShimCount", val);
157
158 for (i = 0; i < t->bucket_count; i++)
159 {
160 sender_stats_t *s;
161 s = t->bucket[i];
162 while (s != NULL)
163 {
164 char val[64], *key = NULL;
165 sender_stats_t *n = s->next;
166
167 snprintf(val, sizeof(val), "%llu %llu", s->count, s->size);
168 asprintf(&key, "*%s", s->sender);
169 if (key != NULL) asl_msg_set_key_val(msg, key, val);
170 free(key);
171 free(s->sender);
172 free(s);
173 s = n;
174 }
175 }
176
177 free(t->bucket);
178 free(t);
179
180 return msg;
181 }
182
183 static void
184 stats_table_update(stats_table_t *t, const char *sender, uint64_t msg_size)
185 {
186 uint32_t i;
187 sender_stats_t *s;
188 uint8_t *p;
189
190 if (t == NULL) return;
191 if (sender == NULL) return;
192
193 /* hash */
194 i = 0;
195 for (p = (uint8_t *)sender; *p != '\0'; p++) i = (i << 1) ^ (i ^ *p);
196 i %= STATS_TABLE_SIZE;
197
198 for (s = t->bucket[i]; s != NULL; s = s->next)
199 {
200 if ((s->sender != NULL) && (strcmp(sender, s->sender) == 0))
201 {
202 s->count++;
203 s->size += msg_size;
204 return;
205 }
206 }
207
208 s = (sender_stats_t *)malloc(sizeof(sender_stats_t));
209 s->sender = strdup(sender);
210 if (s->sender == NULL)
211 {
212 free(s);
213 return;
214 }
215
216 s->count = 1;
217 s->size = msg_size;
218 s->next = t->bucket[i];
219 t->bucket[i] = s;
220 }
221
222 static uint32_t
223 kern_quota_check(time_t now, asl_msg_t *msg, uint32_t level)
224 {
225 char *str, lstr[2];
226
227 if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
228 if (global.mps_limit == 0) return VERIFY_STATUS_OK;
229
230 if (quota_time != now)
231 {
232 kern_quota = global.mps_limit;
233 kern_level = 7;
234 quota_time = now;
235 }
236
237 if (level < kern_level) kern_level = level;
238 if (kern_quota > 0) kern_quota--;
239
240 if (kern_quota > 0) return VERIFY_STATUS_OK;
241 if (kern_quota < 0) return VERIFY_STATUS_EXCEEDED_QUOTA;
242
243 kern_quota = -1;
244
245 str = NULL;
246 asprintf(&str, QUOTA_KERN_EXCEEDED_MESSAGE, global.mps_limit);
247 if (str != NULL)
248 {
249 asl_msg_set_key_val(msg, ASL_KEY_MSG, str);
250 free(str);
251 lstr[0] = kern_level + '0';
252 lstr[1] = 0;
253 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, lstr);
254 }
255
256 return VERIFY_STATUS_OK;
257 }
258
259 static void
260 stats_msg(const char *sender, time_t now, asl_msg_t *msg)
261 {
262 asl_msg_t *x;
263 uint64_t msize = 0;
264
265 /* flush stats after N seconds */
266 if ((global.stats_interval != 0) && ((now - global.stats_last) >= global.stats_interval) && (global.stats != NULL))
267 {
268 asl_msg_t *msg = stats_table_final(global.stats);
269 process_message(msg, SOURCE_INTERNAL);
270 global.stats = NULL;
271 global.stats_last = now;
272 }
273
274 if (global.stats == NULL) global.stats = stats_table_new();
275
276 for (x = msg; x != NULL; x = x->next) msize += x->mem_size;
277
278 const char *shim_vers = asl_msg_get_val_for_key(msg, "ASLSHIM");
279 global.stats->mcount++;
280 if (shim_vers != NULL) global.stats->shim_count++;
281
282 /* update count and total size - total and for this sender */
283 stats_table_update(global.stats, "*", msize);
284 stats_table_update(global.stats, sender, msize);
285 }
286
287 static const char *
288 whatsmyhostname()
289 {
290 static dispatch_once_t once;
291 int check, status;
292
293 if (global.hostname != NULL) return global.hostname;
294
295 dispatch_once(&once, ^{
296 snprintf(myname, sizeof(myname), "%s", "localhost");
297 notify_register_check(kNotifySCHostNameChange, &name_change_token);
298 });
299
300 check = 1;
301 status = 0;
302
303 if (name_change_token >= 0) status = notify_check(name_change_token, &check);
304
305 if ((status == 0) && (check == 0)) return (const char *)myname;
306
307 if (gethostname(myname, MAXHOSTNAMELEN) < 0)
308 {
309 snprintf(myname, sizeof(myname), "%s", "localhost");
310 }
311 else
312 {
313 char *dot;
314 dot = strchr(myname, '.');
315 if (dot != NULL) *dot = '\0';
316 }
317
318 return (const char *)myname;
319 }
320
321 void
322 asl_client_count_increment()
323 {
324 OSSpinLockLock(&count_lock);
325
326 #if !TARGET_OS_EMBEDDED
327 if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL);
328 #endif
329 global.client_count++;
330
331 OSSpinLockUnlock(&count_lock);
332 }
333
334 void
335 asl_client_count_decrement()
336 {
337 OSSpinLockLock(&count_lock);
338
339 if (global.client_count > 0) global.client_count--;
340 #if !TARGET_OS_EMBEDDED
341 if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans);
342 #endif
343
344 OSSpinLockUnlock(&count_lock);
345 }
346
347 /*
348 * Checks message content and sets attributes as required
349 *
350 * SOURCE_INTERNAL log messages sent by syslogd itself
351 * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
352 * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
353 * SOURCE_UDP_SOCKET from the network
354 * SOURCE_KERN from the kernel
355 * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
356 * SOURCE_LAUNCHD forwarded from launchd
357 */
358
359 static uint32_t
360 aslmsg_verify(asl_msg_t *msg, uint32_t source, int32_t *kern_post_level, uid_t *uid_out)
361 {
362 const char *val, *fac, *ruval, *rgval, *sval = NULL;
363 char buf[64];
364 time_t tick, now;
365 uid_t uid;
366 gid_t gid;
367 uint32_t status, level, fnum;
368 pid_t pid;
369 uuid_string_t ustr;
370 struct proc_uniqidentifierinfo pinfo;
371
372 if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE;
373
374 /* Time */
375 now = time(NULL);
376
377 if (kern_post_level != NULL) *kern_post_level = -1;
378 if (uid_out != NULL) *uid_out = -2;
379
380 /* PID */
381 pid = 0;
382
383 val = asl_msg_get_val_for_key(msg, ASL_KEY_PID);
384 if (val == NULL) asl_msg_set_key_val(msg, ASL_KEY_PID, "0");
385 else pid = (pid_t)atoi(val);
386
387 /* if PID is 1 (launchd), use the RefPID and RefProc provided */
388 if (pid == 1)
389 {
390 val = asl_msg_get_val_for_key(msg, ASL_KEY_REF_PID);
391 if (val != NULL) pid = (pid_t)atoi(val);
392
393 sval = asl_msg_get_val_for_key(msg, ASL_KEY_REF_PROC);
394 }
395
396 /* Level */
397 val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
398 level = ASL_LEVEL_DEBUG;
399 if (source == SOURCE_KERN) level = ASL_LEVEL_NOTICE;
400
401 if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0';
402 snprintf(buf, sizeof(buf), "%d", level);
403 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, buf);
404
405
406 /* check kernel quota if enabled and no processes are watching */
407 if ((pid == 0) && (global.mps_limit > 0) && (global.watchers_active == 0))
408 {
409 status = kern_quota_check(now, msg, level);
410 if (status != VERIFY_STATUS_OK) return status;
411 }
412
413 if (pid != 0)
414 {
415 /* set Sender_Mach_UUID */
416 uuid_clear(pinfo.p_uuid);
417 if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &pinfo, sizeof(pinfo)) == sizeof(pinfo))
418 {
419 uuid_unparse(pinfo.p_uuid, ustr);
420 asl_msg_set_key_val(msg, ASL_KEY_SENDER_MACH_UUID, ustr);
421 }
422 }
423
424 tick = 0;
425 val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
426 if (val != NULL) tick = asl_core_parse_time(val, NULL);
427
428 /* Set time to now if it is unset or from the future (not allowed!) */
429 if ((tick == 0) || (tick > now)) tick = now;
430
431 /* Canonical form: seconds since the epoch */
432 snprintf(buf, sizeof(buf) - 1, "%llu", (unsigned long long) tick);
433 asl_msg_set_key_val(msg, ASL_KEY_TIME, buf);
434
435 /* Host */
436 val = asl_msg_get_val_for_key(msg, ASL_KEY_HOST);
437 if (val == NULL) asl_msg_set_key_val(msg, ASL_KEY_HOST, whatsmyhostname());
438
439 /* UID & GID */
440 uid = -2;
441 val = asl_msg_get_val_for_key(msg, ASL_KEY_UID);
442 if (val == NULL)
443 {
444 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
445 }
446 else
447 {
448 uid = atoi(val);
449 if ((uid == 0) && strcmp(val, "0"))
450 {
451 uid = -2;
452 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
453 }
454 }
455
456 gid = -2;
457 val = asl_msg_get_val_for_key(msg, ASL_KEY_GID);
458 if (val == NULL)
459 {
460 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
461 }
462 else
463 {
464 gid = atoi(val);
465 if ((gid == 0) && strcmp(val, "0"))
466 {
467 gid = -2;
468 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
469 }
470 }
471
472 switch (source)
473 {
474 case SOURCE_KERN:
475 case SOURCE_INTERNAL:
476 {
477 uid = 0;
478 asl_msg_set_key_val(msg, ASL_KEY_UID, "0");
479
480 gid = 0;
481 asl_msg_set_key_val(msg, ASL_KEY_GID, "0");
482
483 break;
484 }
485 case SOURCE_ASL_MESSAGE:
486 case SOURCE_LAUNCHD:
487 {
488 break;
489 }
490 default:
491 {
492 /* do not trust the UID 0 or GID 0 or 80 in SOURCE_BSD_SOCKET or SOURCE_UNKNOWN */
493 if (uid == 0)
494 {
495 uid = -2;
496 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
497 }
498
499 if ((gid == 0) || (gid == 80))
500 {
501 gid = -2;
502 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
503 }
504 }
505 }
506
507 if (uid_out != NULL) *uid_out = uid;
508
509 /* Sender */
510 if (sval == NULL) sval = asl_msg_get_val_for_key(msg, ASL_KEY_SENDER);
511 if (sval == NULL)
512 {
513 switch (source)
514 {
515 case SOURCE_KERN:
516 {
517 sval = "kernel";
518 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
519 break;
520 }
521 case SOURCE_INTERNAL:
522 {
523 sval = "syslogd";
524 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
525 break;
526 }
527 default:
528 {
529 asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
530 }
531 }
532 }
533 else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(sval, "kernel")))
534 {
535 /* allow UID 0 to send messages with "Sender kernel", but nobody else */
536 asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
537 sval = NULL;
538 }
539
540 /* Facility */
541 fac = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
542 if (fac == NULL)
543 {
544 if (source == SOURCE_KERN) fac = "kern";
545 else fac = "user";
546 asl_msg_set_key_val(msg, ASL_KEY_FACILITY, fac);
547 }
548 else if (fac[0] == '#')
549 {
550 fnum = LOG_USER;
551 if ((fac[1] >= '0') && (fac[1] <= '9'))
552 {
553 fnum = atoi(fac + 1) << 3;
554 if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER;
555 }
556
557 fac = asl_syslog_faciliy_num_to_name(fnum);
558 asl_msg_set_key_val(msg, ASL_KEY_FACILITY, fac);
559 }
560 else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
561 {
562 /* only UID 0 may use "com.apple.system" */
563 if (uid != 0) asl_msg_set_key_val(msg, ASL_KEY_FACILITY, FACILITY_USER);
564 }
565
566 /*
567 * kernel messages are only readable by root and admin group.
568 * all other messages are admin-only readable unless they already
569 * have specific read access controls set.
570 */
571 if (source == SOURCE_KERN)
572 {
573 asl_msg_set_key_val(msg, ASL_KEY_READ_UID, "0");
574 asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
575 }
576 else
577 {
578 ruval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_UID);
579 rgval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_GID);
580
581 if ((ruval == NULL) && (rgval == NULL))
582 {
583 asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
584 }
585 }
586
587 /* Set DB Expire Time for com.apple.system.utmpx and lastlog */
588 if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog")))
589 {
590 snprintf(buf, sizeof(buf), "%llu", (unsigned long long) tick + global.utmp_ttl);
591 asl_msg_set_key_val(msg, ASL_KEY_EXPIRE_TIME, buf);
592 }
593
594 /* Set DB Expire Time for Filesystem errors */
595 if (!strcmp(fac, FSLOG_VAL_FACILITY))
596 {
597 snprintf(buf, sizeof(buf), "%llu", (unsigned long long) tick + FS_TTL_SEC);
598 asl_msg_set_key_val(msg, ASL_KEY_EXPIRE_TIME, buf);
599 }
600
601 /*
602 * special case handling of kernel disaster messages
603 */
604 if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL))
605 {
606 if (kern_post_level != NULL) *kern_post_level = level;
607 disaster_message(msg);
608 }
609
610 /*
611 * gather sender stats
612 */
613 if (source != SOURCE_INTERNAL) stats_msg(sval, now, msg);
614
615 return VERIFY_STATUS_OK;
616 }
617
618 void
619 list_append_msg(asl_msg_list_t *list, asl_msg_t *msg)
620 {
621 if (list == NULL) return;
622 if (msg == NULL) return;
623
624 /*
625 * NB: curr is the list size
626 * grow list if necessary
627 */
628 if (list->count == list->curr)
629 {
630 if (list->curr == 0)
631 {
632 list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
633 }
634 else
635 {
636 list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
637 }
638
639 if (list->msg == NULL)
640 {
641 list->curr = 0;
642 list->count = 0;
643 return;
644 }
645
646 list->curr += LIST_SIZE_DELTA;
647 }
648
649 list->msg[list->count] = (asl_msg_t *)msg;
650 list->count++;
651 }
652
653 void
654 init_globals(void)
655 {
656 asl_out_rule_t *r;
657
658 OSSpinLockLock(&global.lock);
659
660 global.pid = getpid();
661 global.debug = 0;
662 free(global.debug_file);
663 global.debug_file = NULL;
664 global.launchd_enabled = 1;
665
666 #if TARGET_OS_EMBEDDED
667 global.dbtype = DB_TYPE_MEMORY;
668 #else
669 global.dbtype = DB_TYPE_FILE;
670 #endif
671 global.db_file_max = DEFAULT_DB_FILE_MAX;
672 global.db_memory_max = DEFAULT_DB_MEMORY_MAX;
673 global.db_memory_str_max = DEFAULT_DB_MEMORY_STR_MAX;
674 global.mps_limit = DEFAULT_MPS_LIMIT;
675 global.remote_delay_time = DEFAULT_REMOTE_DELAY;
676 global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
677 global.mark_time = DEFAULT_MARK_SEC;
678 global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
679 global.memory_max = DEFAULT_MEMORY_MAX;
680 global.stats_interval = DEFAULT_STATS_INTERVAL;
681
682 global.asl_out_module = asl_out_module_init();
683 OSSpinLockUnlock(&global.lock);
684
685 if (global.asl_out_module != NULL)
686 {
687 for (r = global.asl_out_module->ruleset; r != NULL; r = r->next)
688 {
689 if ((r->action == ACTION_SET_PARAM) && (r->query == NULL) && (!strncmp(r->options, "debug", 5))) control_set_param(r->options, true);
690 }
691 }
692 }
693
694 /*
695 * Used to set config parameters.
696 * Line format "= name value"
697 */
698 int
699 control_set_param(const char *s, bool eval)
700 {
701 char **l;
702 uint32_t intval, count, v32a, v32b, v32c;
703
704 if (s == NULL) return -1;
705 if (s[0] == '\0') return 0;
706
707 /* skip '=' and whitespace */
708 if (*s == '=') s++;
709 while ((*s == ' ') || (*s == '\t')) s++;
710
711 l = explode(s, " \t");
712 if (l == NULL) return -1;
713
714 for (count = 0; l[count] != NULL; count++);
715
716 /* name is required */
717 if (count == 0)
718 {
719 free_string_list(l);
720 return -1;
721 }
722
723 /* Check variables that allow 0 or 1 / boolean */
724 if (!strcasecmp(l[0], "debug"))
725 {
726 /* = debug [0|1] [file] */
727 if (count == 1)
728 {
729 intval = (eval) ? 1 : 0;
730 config_debug(intval, NULL);
731 }
732 else if (!strcmp(l[1], "0"))
733 {
734 config_debug(0, l[2]);
735 }
736 else if (!strcmp(l[1], "1"))
737 {
738 config_debug(1, l[2]);
739 }
740 else
741 {
742 intval = (eval) ? 1 : 0;
743 config_debug(intval, l[1]);
744 }
745
746 free_string_list(l);
747 return 0;
748 }
749
750 /* value is required */
751 if (count == 1)
752 {
753 free_string_list(l);
754 return -1;
755 }
756
757 if (!strcasecmp(l[0], "hostname"))
758 {
759 /* = hostname name */
760 OSSpinLockLock(&global.lock);
761 if (eval)
762 {
763 global.hostname = strdup(l[1]);
764 }
765 else
766 {
767 free(global.hostname);
768 global.hostname = NULL;
769 }
770 OSSpinLockUnlock(&global.lock);
771 }
772 else if (!strcasecmp(l[0], "mark_time"))
773 {
774 /* = mark_time seconds */
775 OSSpinLockLock(&global.lock);
776 if (eval) global.mark_time = atoll(l[1]);
777 else global.mark_time = 0;
778 OSSpinLockUnlock(&global.lock);
779 }
780 else if (!strcasecmp(l[0], "dup_delay"))
781 {
782 /* = bsd_max_dup_time seconds */
783 OSSpinLockLock(&global.lock);
784 if (eval) global.bsd_max_dup_time = atoll(l[1]);
785 else global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
786 OSSpinLockUnlock(&global.lock);
787 }
788 else if (!strcasecmp(l[0], "remote_delay"))
789 {
790 /* = remote_delay microseconds */
791 OSSpinLockLock(&global.lock);
792 if (eval) global.remote_delay_time = atol(l[1]);
793 else global.remote_delay_time = DEFAULT_REMOTE_DELAY;
794 OSSpinLockUnlock(&global.lock);
795 }
796 else if (!strcasecmp(l[0], "utmp_ttl"))
797 {
798 /* = utmp_ttl seconds */
799 OSSpinLockLock(&global.lock);
800 if (eval) global.utmp_ttl = (time_t)atoll(l[1]);
801 else global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
802 OSSpinLockUnlock(&global.lock);
803 }
804 else if (!strcasecmp(l[0], "mps_limit"))
805 {
806 /* = mps_limit number */
807 OSSpinLockLock(&global.lock);
808 if (eval) global.mps_limit = (uint32_t)atol(l[1]);
809 else global.mps_limit = DEFAULT_MPS_LIMIT;
810 OSSpinLockUnlock(&global.lock);
811 }
812 else if (!strcasecmp(l[0], "memory_max"))
813 {
814 /* = memory_max number */
815 OSSpinLockLock(&global.lock);
816 if (eval) global.memory_max = (int64_t)atoll(l[1]);
817 else global.memory_max = DEFAULT_MEMORY_MAX;
818 OSSpinLockUnlock(&global.lock);
819 }
820 else if (!strcasecmp(l[0], "stats_interval"))
821 {
822 /* = stats_interval number */
823 OSSpinLockLock(&global.lock);
824 if (eval) global.stats_interval = (time_t)atoll(l[1]);
825 else global.stats_interval = DEFAULT_STATS_INTERVAL;
826 OSSpinLockUnlock(&global.lock);
827 }
828 else if (!strcasecmp(l[0], "max_file_size"))
829 {
830 /* = max_file_size bytes */
831 pthread_mutex_lock(global.db_lock);
832
833 if (global.dbtype & DB_TYPE_FILE)
834 {
835 asl_store_close(global.file_db);
836 global.file_db = NULL;
837 if (eval) global.db_file_max = atoi(l[1]);
838 else global.db_file_max = DEFAULT_DB_FILE_MAX;
839 }
840
841 pthread_mutex_unlock(global.db_lock);
842 }
843 else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
844 {
845 /* NB this is private / unpublished */
846 /* = db type [max]... */
847 if (eval)
848 {
849 v32a = 0;
850 v32b = 0;
851 v32c = 0;
852
853 if ((l[1][0] >= '0') && (l[1][0] <= '9'))
854 {
855 intval = atoi(l[1]);
856 if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
857 if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
858 if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
859 }
860 else if (!strcasecmp(l[1], "file"))
861 {
862 intval = DB_TYPE_FILE;
863 if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
864 }
865 else if (!strncasecmp(l[1], "mem", 3))
866 {
867 intval = DB_TYPE_MEMORY;
868 if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
869 }
870 else
871 {
872 free_string_list(l);
873 return -1;
874 }
875
876 if (v32a == 0) v32a = global.db_file_max;
877 if (v32b == 0) v32b = global.db_memory_max;
878 if (v32c == 0) v32c = global.db_memory_str_max;
879
880 config_data_store(intval, v32a, v32b, v32c);
881 }
882 else
883 {
884 #if TARGET_OS_EMBEDDED
885 intval = DB_TYPE_MEMORY;
886 #else
887 intval = DB_TYPE_FILE;
888 #endif
889 config_data_store(intval, DEFAULT_DB_FILE_MAX, DEFAULT_DB_MEMORY_MAX, DEFAULT_DB_MEMORY_STR_MAX);
890 }
891 }
892
893 free_string_list(l);
894 return 0;
895 }
896
897 static int
898 control_message(asl_msg_t *msg)
899 {
900 const char *str = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
901
902 if (str == NULL) return 0;
903
904 if (!strncmp(str, "= reset", 7))
905 {
906 init_globals();
907 return asl_action_reset();
908 }
909 else if (!strncmp(str, "= crash", 7))
910 {
911 abort();
912 }
913 else if (!strncmp(str, "@ ", 2))
914 {
915 return asl_action_control_set_param(str);
916 }
917 else if (!strncmp(str, "= ", 2))
918 {
919 return control_set_param(str, true);
920 }
921
922 return 0;
923 }
924
925 void
926 process_message(asl_msg_t *msg, uint32_t source)
927 {
928 int64_t msize = 0;
929 static bool wq_draining = false;
930 bool is_control = false;
931 asl_msg_t *x;
932
933 if (msg == NULL) return;
934
935 is_control = asl_check_option(msg, ASL_OPT_CONTROL) != 0;
936
937 if ((!is_control) && wq_draining)
938 {
939 if (global.memory_size >= (global.memory_max / 2))
940 {
941 asldebug("Work queue draining: dropped message.\n");
942 asl_msg_release(msg);
943 return;
944 }
945 else
946 {
947 asldebug("Work queue re-enabled at 1/2 max. size %lld max %lld\n", global.memory_size, global.memory_max);
948 wq_draining = false;
949 }
950 }
951
952 __block vproc_transaction_t vt = vproc_transaction_begin(NULL);
953
954 for (x = msg; x != NULL; x = x->next) msize += x->mem_size;
955
956 if ((global.memory_size + msize) >= global.memory_max)
957 {
958 char str[256];
959
960 wq_draining = true;
961 asl_msg_release(msg);
962
963 asldebug("Work queue disabled. msize %lld size %lld max %lld\n", msize, global.memory_size + msize, global.memory_max);
964 snprintf(str, sizeof(str), "[Sender syslogd] [Level 2] [PID %u] [Message Internal memory size limit %lld exceeded - dropping messages] [UID 0] [UID 0] [Facility syslog]", global.pid, global.memory_max);
965 msg = asl_msg_from_string(str);
966 }
967
968 OSAtomicAdd64(msize, &global.memory_size);
969 OSAtomicIncrement32(&global.work_queue_count);
970
971 dispatch_async(global.work_queue, ^{
972 int32_t kplevel;
973 uint32_t status;
974 uid_t uid;
975
976 kplevel = -1;
977 uid = -2;
978
979 status = aslmsg_verify(msg, source, &kplevel, &uid);
980 if (status == VERIFY_STATUS_OK)
981 {
982 if ((source == SOURCE_KERN) && (kplevel >= 0))
983 {
984 if (kplevel > 7) kplevel = 7;
985 if (kern_notify_token[kplevel] < 0)
986 {
987 status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel]));
988 if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status);
989 }
990
991 notify_post(kern_notify_key[kplevel]);
992 }
993
994 if ((uid == 0) && is_control) control_message(msg);
995
996 /*
997 * Send message to output module chain (asl is first).
998 * The last module in the chain will decrement global.memory_size.
999 */
1000 asl_out_message(msg, msize);
1001 }
1002 else
1003 {
1004 OSAtomicAdd64(-1ll * msize, &global.memory_size);
1005 }
1006
1007 asl_msg_release(msg);
1008
1009 OSAtomicDecrement32(&global.work_queue_count);
1010 vproc_transaction_end(NULL, vt);
1011 });
1012 }
1013
1014 int
1015 internal_log_message(const char *str)
1016 {
1017 asl_msg_t *msg;
1018
1019 if (str == NULL) return 1;
1020
1021 msg = asl_msg_from_string(str);
1022 if (msg == NULL) return 1;
1023
1024 process_message(msg, SOURCE_INTERNAL);
1025
1026 return 0;
1027 }
1028
1029 int
1030 asldebug(const char *str, ...)
1031 {
1032 va_list v;
1033 FILE *dfp = NULL;
1034
1035 if (global.debug == 0) return 0;
1036
1037 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
1038 else dfp = fopen(global.debug_file, "a");
1039 if (dfp == NULL) return 0;
1040
1041 va_start(v, str);
1042 fprintf(dfp, "W %d %llu", global.work_queue_count, global.memory_size);
1043 if (global.memory_db != NULL) fprintf(dfp, " M %u %u %lu", global.memory_db->record_count, global.memory_db->string_count, global.memory_db->curr_string_mem);
1044 fprintf(dfp, " ; ");
1045 vfprintf(dfp, str, v);
1046 va_end(v);
1047
1048 fclose(dfp);
1049
1050 return 0;
1051 }
1052
1053 void
1054 asl_mark(void)
1055 {
1056 char *str = NULL;
1057
1058 asprintf(&str, "[Sender syslogd] [Level 6] [PID %u] [Message -- MARK --] [UID 0] [UID 0] [Facility syslog]", global.pid);
1059 internal_log_message(str);
1060 free(str);
1061 }
1062
1063 asl_msg_t *
1064 asl_syslog_input_convert(const char *in, int len, char *rhost, uint32_t source)
1065 {
1066 int pf, pri, index, n;
1067 char *p, *colon, *brace, *space, *tmp, *tval, *hval, *sval, *pval, *mval;
1068 char prival[8];
1069 const char *fval;
1070 asl_msg_t *msg;
1071 struct tm time;
1072 time_t tick;
1073
1074 if (in == NULL) return NULL;
1075 if (len <= 0) return NULL;
1076
1077 pri = LOG_DEBUG;
1078 if (source == SOURCE_KERN) pri = LOG_NOTICE;
1079
1080 tval = NULL;
1081 hval = NULL;
1082 sval = NULL;
1083 pval = NULL;
1084 mval = NULL;
1085 fval = NULL;
1086
1087 index = 0;
1088 p = (char *)in;
1089
1090 /* skip leading whitespace */
1091 while ((index < len) && ((*p == ' ') || (*p == '\t')))
1092 {
1093 p++;
1094 index++;
1095 }
1096
1097 if (index >= len) return NULL;
1098
1099 /* parse "<NN>" priority (level and facility) */
1100 if (*p == '<')
1101 {
1102 p++;
1103 index++;
1104
1105 n = sscanf(p, "%d", &pf);
1106 if (n == 1)
1107 {
1108 pri = pf & 0x7;
1109 if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK);
1110 }
1111
1112 while ((index < len) && (*p != '>'))
1113 {
1114 p++;
1115 index++;
1116 }
1117
1118 if (index < len)
1119 {
1120 p++;
1121 index++;
1122 }
1123 }
1124
1125 snprintf(prival, sizeof(prival), "%d", pri);
1126
1127 /* check if a timestamp is included */
1128 if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' '))
1129 {
1130 tmp = malloc(16);
1131 if (tmp == NULL) return NULL;
1132
1133 memcpy(tmp, p, 15);
1134 tmp[15] = '\0';
1135
1136 tick = asl_core_parse_time(tmp, NULL);
1137 if (tick == (time_t)-1)
1138 {
1139 tval = tmp;
1140 }
1141 else
1142 {
1143 free(tmp);
1144 gmtime_r(&tick, &time);
1145 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);
1146 }
1147
1148 p += 16;
1149 index += 16;
1150 }
1151
1152 /* stop here for kernel messages */
1153 if (source == SOURCE_KERN)
1154 {
1155 msg = asl_msg_new(ASL_TYPE_MSG);
1156 if (msg == NULL) return NULL;
1157
1158 asl_msg_set_key_val(msg, ASL_KEY_MSG, p);
1159 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
1160 asl_msg_set_key_val(msg, ASL_KEY_PID, "0");
1161
1162 return msg;
1163 }
1164
1165 /* if message is from a network socket, hostname follows */
1166 if (source == SOURCE_UDP_SOCKET)
1167 {
1168 space = strchr(p, ' ');
1169 if (space != NULL)
1170 {
1171 n = space - p;
1172 hval = malloc(n + 1);
1173 if (hval == NULL) return NULL;
1174
1175 memcpy(hval, p, n);
1176 hval[n] = '\0';
1177
1178 p = space + 1;
1179 index += (n + 1);
1180 }
1181 }
1182
1183 colon = strchr(p, ':');
1184 brace = strchr(p, '[');
1185
1186 /* check for "sender:" or sender[pid]:" */
1187 if (colon != NULL)
1188 {
1189 if ((brace != NULL) && (brace < colon))
1190 {
1191 n = brace - p;
1192 sval = malloc(n + 1);
1193 if (sval == NULL) return NULL;
1194
1195 memcpy(sval, p, n);
1196 sval[n] = '\0';
1197
1198 n = colon - (brace + 1) - 1;
1199 pval = malloc(n + 1);
1200 if (pval == NULL) return NULL;
1201
1202 memcpy(pval, (brace + 1), n);
1203 pval[n] = '\0';
1204 }
1205 else
1206 {
1207 n = colon - p;
1208 sval = malloc(n + 1);
1209 if (sval == NULL) return NULL;
1210
1211 memcpy(sval, p, n);
1212 sval[n] = '\0';
1213 }
1214
1215 n = colon - p;
1216 p = colon + 1;
1217 index += (n + 1);
1218 }
1219
1220 if (*p == ' ')
1221 {
1222 p++;
1223 index++;
1224 }
1225
1226 n = len - index;
1227 if (n > 0)
1228 {
1229 mval = malloc(n + 1);
1230 if (mval == NULL) return NULL;
1231
1232 memcpy(mval, p, n);
1233 mval[n] = '\0';
1234 }
1235
1236 if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER);
1237
1238 msg = asl_msg_new(ASL_TYPE_MSG);
1239 if (msg == NULL) return NULL;
1240
1241 if (tval != NULL)
1242 {
1243 asl_msg_set_key_val(msg, ASL_KEY_TIME, tval);
1244 free(tval);
1245 }
1246
1247 if (fval != NULL) asl_msg_set_key_val(msg, "Facility", fval);
1248 else asl_msg_set_key_val(msg, "Facility", "user");
1249
1250 if (sval != NULL)
1251 {
1252 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
1253 free(sval);
1254 }
1255
1256 if (pval != NULL)
1257 {
1258 asl_msg_set_key_val(msg, ASL_KEY_PID, pval);
1259 free(pval);
1260 }
1261 else
1262 {
1263 asl_msg_set_key_val(msg, ASL_KEY_PID, "-1");
1264 }
1265
1266 if (mval != NULL)
1267 {
1268 asl_msg_set_key_val(msg, ASL_KEY_MSG, mval);
1269 free(mval);
1270 }
1271
1272 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
1273 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
1274 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
1275
1276 if (hval != NULL)
1277 {
1278 asl_msg_set_key_val(msg, ASL_KEY_HOST, hval);
1279 free(hval);
1280 }
1281 else if (rhost != NULL)
1282 {
1283 asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
1284 }
1285
1286 return msg;
1287 }
1288
1289 asl_msg_t *
1290 asl_input_parse(const char *in, int len, char *rhost, uint32_t source)
1291 {
1292 asl_msg_t *msg;
1293 int status, x, legacy, off;
1294
1295 asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
1296
1297 if (in == NULL) return NULL;
1298
1299 legacy = 1;
1300 msg = NULL;
1301
1302 /* calculate length if not provided */
1303 if (len == 0) len = strlen(in);
1304
1305 /*
1306 * Determine if the input is "old" syslog format or new ASL format.
1307 * Old format lines should start with "<", but they can just be straight text.
1308 * ASL input may start with a length (10 bytes) followed by a space and a '['.
1309 * The length is optional, so ASL messages may also just start with '['.
1310 */
1311 if ((in[0] != '<') && (len > 11))
1312 {
1313 status = sscanf(in, "%d ", &x);
1314 if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0;
1315 }
1316
1317 if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, source);
1318
1319 off = 11;
1320 if (in[0] == '[') off = 0;
1321
1322 msg = asl_msg_from_string(in + off);
1323 if (msg == NULL) return NULL;
1324
1325 if (rhost != NULL) asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
1326
1327 return msg;
1328 }
1329
1330 #if !TARGET_IPHONE_SIMULATOR
1331 void
1332 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)
1333 {
1334 asl_msg_t *m;
1335 char str[256];
1336 time_t now;
1337
1338 if (global.launchd_enabled == 0) return;
1339
1340 /*
1341 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",
1342 when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1343 */
1344
1345 m = asl_msg_new(ASL_TYPE_MSG);
1346 if (m == NULL) return;
1347
1348 /* Level */
1349 if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG;
1350 if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG;
1351 snprintf(str, sizeof(str), "%d", priority);
1352
1353 asl_msg_set_key_val(m, ASL_KEY_LEVEL, str);
1354
1355 /* Time */
1356 if (when != NULL)
1357 {
1358 snprintf(str, sizeof(str), "%llu", (unsigned long long) when->tv_sec);
1359 asl_msg_set_key_val(m, ASL_KEY_TIME, str);
1360
1361 snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec);
1362 asl_msg_set_key_val(m, ASL_KEY_TIME_NSEC, str);
1363 }
1364 else
1365 {
1366 now = time(NULL);
1367 snprintf(str, sizeof(str), "%llu", (unsigned long long) now);
1368 asl_msg_set_key_val(m, ASL_KEY_TIME, str);
1369 }
1370
1371 /* Facility */
1372 asl_msg_set_key_val(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
1373
1374 /* UID */
1375 snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
1376 asl_msg_set_key_val(m, ASL_KEY_UID, str);
1377
1378 /* GID */
1379 snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
1380 asl_msg_set_key_val(m, ASL_KEY_GID, str);
1381
1382 /* PID */
1383 if (from_pid != 0)
1384 {
1385 snprintf(str, sizeof(str), "%u", (unsigned int)from_pid);
1386 asl_msg_set_key_val(m, ASL_KEY_PID, str);
1387 }
1388
1389 /* Reference PID */
1390 if ((about_pid > 0) && (about_pid != from_pid))
1391 {
1392 snprintf(str, sizeof(str), "%u", (unsigned int)about_pid);
1393 asl_msg_set_key_val(m, ASL_KEY_REF_PID, str);
1394 }
1395
1396 /* Sender */
1397 if (from_name != NULL)
1398 {
1399 asl_msg_set_key_val(m, ASL_KEY_SENDER, from_name);
1400 }
1401
1402 /* ReadUID */
1403 if (sender_uid != 0)
1404 {
1405 snprintf(str, sizeof(str), "%d", (int)sender_uid);
1406 asl_msg_set_key_val(m, ASL_KEY_READ_UID, str);
1407 }
1408
1409 /* Reference Process */
1410 if (about_name != NULL)
1411 {
1412 if ((from_name != NULL) && (strcmp(from_name, about_name) != 0))
1413 {
1414 asl_msg_set_key_val(m, ASL_KEY_REF_PROC, about_name);
1415 }
1416 }
1417
1418 /* Session */
1419 if (session_name != NULL)
1420 {
1421 asl_msg_set_key_val(m, ASL_KEY_SESSION, session_name);
1422 }
1423
1424 /* Message */
1425 if (msg != NULL)
1426 {
1427 asl_msg_set_key_val(m, ASL_KEY_MSG, msg);
1428 }
1429
1430 process_message(m, SOURCE_LAUNCHD);
1431 }
1432
1433 #endif