]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/daemon.c
8a3022283bcbabbc8921895bfb66934505d3a6a8
[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 if (uid_out != NULL) *uid_out = uid;
457
458 gid = -2;
459 val = asl_msg_get_val_for_key(msg, ASL_KEY_GID);
460 if (val == NULL)
461 {
462 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
463 }
464 else
465 {
466 gid = atoi(val);
467 if ((gid == 0) && strcmp(val, "0"))
468 {
469 gid = -2;
470 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
471 }
472 }
473
474 switch (source)
475 {
476 case SOURCE_KERN:
477 case SOURCE_INTERNAL:
478 {
479 uid = 0;
480 asl_msg_set_key_val(msg, ASL_KEY_UID, "0");
481
482 gid = 0;
483 asl_msg_set_key_val(msg, ASL_KEY_GID, "0");
484
485 break;
486 }
487 case SOURCE_UDP_SOCKET:
488 case SOURCE_ASL_MESSAGE:
489 case SOURCE_LAUNCHD:
490 {
491 break;
492 }
493 default:
494 {
495 /* do not trust the UID 0 or GID 0 or 80 in SOURCE_BSD_SOCKET or SOURCE_UNKNOWN */
496 if (uid == 0)
497 {
498 uid = -2;
499 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
500 }
501
502 if ((gid == 0) || (gid == 80))
503 {
504 gid = -2;
505 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
506 }
507 }
508 }
509
510 /* Sender */
511 if (sval == NULL) sval = asl_msg_get_val_for_key(msg, ASL_KEY_SENDER);
512 if (sval == NULL)
513 {
514 switch (source)
515 {
516 case SOURCE_KERN:
517 {
518 sval = "kernel";
519 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
520 break;
521 }
522 case SOURCE_INTERNAL:
523 {
524 sval = "syslogd";
525 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
526 break;
527 }
528 default:
529 {
530 asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
531 }
532 }
533 }
534 else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(sval, "kernel")))
535 {
536 /* allow UID 0 to send messages with "Sender kernel", but nobody else */
537 asl_msg_set_key_val(msg, ASL_KEY_SENDER, "Unknown");
538 sval = NULL;
539 }
540
541 /* Facility */
542 fac = asl_msg_get_val_for_key(msg, ASL_KEY_FACILITY);
543 if (fac == NULL)
544 {
545 if (source == SOURCE_KERN) fac = "kern";
546 else fac = "user";
547 asl_msg_set_key_val(msg, ASL_KEY_FACILITY, fac);
548 }
549 else if (fac[0] == '#')
550 {
551 fnum = LOG_USER;
552 if ((fac[1] >= '0') && (fac[1] <= '9'))
553 {
554 fnum = atoi(fac + 1) << 3;
555 if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER;
556 }
557
558 fac = asl_syslog_faciliy_num_to_name(fnum);
559 asl_msg_set_key_val(msg, ASL_KEY_FACILITY, fac);
560 }
561 else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN))
562 {
563 /* only UID 0 may use "com.apple.system" */
564 if (uid != 0) asl_msg_set_key_val(msg, ASL_KEY_FACILITY, FACILITY_USER);
565 }
566
567 /*
568 * kernel messages are only readable by root and admin group.
569 * all other messages are admin-only readable unless they already
570 * have specific read access controls set.
571 */
572 if (source == SOURCE_KERN)
573 {
574 asl_msg_set_key_val(msg, ASL_KEY_READ_UID, "0");
575 asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
576 }
577 else
578 {
579 ruval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_UID);
580 rgval = asl_msg_get_val_for_key(msg, ASL_KEY_READ_GID);
581
582 if ((ruval == NULL) && (rgval == NULL))
583 {
584 asl_msg_set_key_val(msg, ASL_KEY_READ_GID, "80");
585 }
586 }
587
588 /* Set DB Expire Time for com.apple.system.utmpx and lastlog */
589 if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog")))
590 {
591 snprintf(buf, sizeof(buf), "%llu", (unsigned long long) tick + global.utmp_ttl);
592 asl_msg_set_key_val(msg, ASL_KEY_EXPIRE_TIME, buf);
593 }
594
595 /* Set DB Expire Time for Filesystem errors */
596 if (!strcmp(fac, FSLOG_VAL_FACILITY))
597 {
598 snprintf(buf, sizeof(buf), "%llu", (unsigned long long) tick + FS_TTL_SEC);
599 asl_msg_set_key_val(msg, ASL_KEY_EXPIRE_TIME, buf);
600 }
601
602 /*
603 * special case handling of kernel disaster messages
604 */
605 if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL))
606 {
607 if (kern_post_level != NULL) *kern_post_level = level;
608 disaster_message(msg);
609 }
610
611 /*
612 * gather sender stats
613 */
614 if (source != SOURCE_INTERNAL) stats_msg(sval, now, msg);
615
616 return VERIFY_STATUS_OK;
617 }
618
619 void
620 list_append_msg(asl_msg_list_t *list, asl_msg_t *msg)
621 {
622 if (list == NULL) return;
623 if (msg == NULL) return;
624
625 /*
626 * NB: curr is the list size
627 * grow list if necessary
628 */
629 if (list->count == list->curr)
630 {
631 if (list->curr == 0)
632 {
633 list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *));
634 }
635 else
636 {
637 list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *));
638 }
639
640 if (list->msg == NULL)
641 {
642 list->curr = 0;
643 list->count = 0;
644 return;
645 }
646
647 list->curr += LIST_SIZE_DELTA;
648 }
649
650 list->msg[list->count] = (asl_msg_t *)msg;
651 list->count++;
652 }
653
654 void
655 init_globals(void)
656 {
657 asl_out_rule_t *r;
658
659 OSSpinLockLock(&global.lock);
660
661 global.pid = getpid();
662 global.debug = 0;
663 free(global.debug_file);
664 global.debug_file = NULL;
665 global.launchd_enabled = 1;
666
667 #if TARGET_OS_EMBEDDED
668 global.dbtype = DB_TYPE_MEMORY;
669 #else
670 global.dbtype = DB_TYPE_FILE;
671 #endif
672 global.db_file_max = DEFAULT_DB_FILE_MAX;
673 global.db_memory_max = DEFAULT_DB_MEMORY_MAX;
674 global.db_memory_str_max = DEFAULT_DB_MEMORY_STR_MAX;
675 global.mps_limit = DEFAULT_MPS_LIMIT;
676 global.remote_delay_time = DEFAULT_REMOTE_DELAY;
677 global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
678 global.mark_time = DEFAULT_MARK_SEC;
679 global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
680 global.memory_max = DEFAULT_MEMORY_MAX;
681 global.stats_interval = DEFAULT_STATS_INTERVAL;
682
683 global.asl_out_module = asl_out_module_init();
684 OSSpinLockUnlock(&global.lock);
685
686 if (global.asl_out_module != NULL)
687 {
688 for (r = global.asl_out_module->ruleset; r != NULL; r = r->next)
689 {
690 if ((r->action == ACTION_SET_PARAM) && (r->query == NULL) && (!strncmp(r->options, "debug", 5))) control_set_param(r->options, true);
691 }
692 }
693 }
694
695 /*
696 * Used to set config parameters.
697 * Line format "= name value"
698 */
699 int
700 control_set_param(const char *s, bool eval)
701 {
702 char **l;
703 uint32_t intval, count, v32a, v32b, v32c;
704
705 if (s == NULL) return -1;
706 if (s[0] == '\0') return 0;
707
708 /* skip '=' and whitespace */
709 if (*s == '=') s++;
710 while ((*s == ' ') || (*s == '\t')) s++;
711
712 l = explode(s, " \t");
713 if (l == NULL) return -1;
714
715 for (count = 0; l[count] != NULL; count++);
716
717 /* name is required */
718 if (count == 0)
719 {
720 free_string_list(l);
721 return -1;
722 }
723
724 /* Check variables that allow 0 or 1 / boolean */
725 if (!strcasecmp(l[0], "debug"))
726 {
727 /* = debug [0|1] [file] */
728 if (count == 1)
729 {
730 intval = (eval) ? 1 : 0;
731 config_debug(intval, NULL);
732 }
733 else if (!strcmp(l[1], "0"))
734 {
735 config_debug(0, l[2]);
736 }
737 else if (!strcmp(l[1], "1"))
738 {
739 config_debug(1, l[2]);
740 }
741 else
742 {
743 intval = (eval) ? 1 : 0;
744 config_debug(intval, l[1]);
745 }
746
747 free_string_list(l);
748 return 0;
749 }
750
751 /* value is required */
752 if (count == 1)
753 {
754 free_string_list(l);
755 return -1;
756 }
757
758 if (!strcasecmp(l[0], "hostname"))
759 {
760 /* = hostname name */
761 OSSpinLockLock(&global.lock);
762 if (eval)
763 {
764 global.hostname = strdup(l[1]);
765 }
766 else
767 {
768 free(global.hostname);
769 global.hostname = NULL;
770 }
771 OSSpinLockUnlock(&global.lock);
772 }
773 else if (!strcasecmp(l[0], "mark_time"))
774 {
775 /* = mark_time seconds */
776 OSSpinLockLock(&global.lock);
777 if (eval) global.mark_time = atoll(l[1]);
778 else global.mark_time = 0;
779 OSSpinLockUnlock(&global.lock);
780 }
781 else if (!strcasecmp(l[0], "dup_delay"))
782 {
783 /* = bsd_max_dup_time seconds */
784 OSSpinLockLock(&global.lock);
785 if (eval) global.bsd_max_dup_time = atoll(l[1]);
786 else global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
787 OSSpinLockUnlock(&global.lock);
788 }
789 else if (!strcasecmp(l[0], "remote_delay"))
790 {
791 /* = remote_delay microseconds */
792 OSSpinLockLock(&global.lock);
793 if (eval) global.remote_delay_time = atol(l[1]);
794 else global.remote_delay_time = DEFAULT_REMOTE_DELAY;
795 OSSpinLockUnlock(&global.lock);
796 }
797 else if (!strcasecmp(l[0], "utmp_ttl"))
798 {
799 /* = utmp_ttl seconds */
800 OSSpinLockLock(&global.lock);
801 if (eval) global.utmp_ttl = (time_t)atoll(l[1]);
802 else global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
803 OSSpinLockUnlock(&global.lock);
804 }
805 else if (!strcasecmp(l[0], "mps_limit"))
806 {
807 /* = mps_limit number */
808 OSSpinLockLock(&global.lock);
809 if (eval) global.mps_limit = (uint32_t)atol(l[1]);
810 else global.mps_limit = DEFAULT_MPS_LIMIT;
811 OSSpinLockUnlock(&global.lock);
812 }
813 else if (!strcasecmp(l[0], "memory_max"))
814 {
815 /* = memory_max number */
816 OSSpinLockLock(&global.lock);
817 if (eval) global.memory_max = (int64_t)atoll(l[1]);
818 else global.memory_max = DEFAULT_MEMORY_MAX;
819 OSSpinLockUnlock(&global.lock);
820 }
821 else if (!strcasecmp(l[0], "stats_interval"))
822 {
823 /* = stats_interval number */
824 OSSpinLockLock(&global.lock);
825 if (eval) global.stats_interval = (time_t)atoll(l[1]);
826 else global.stats_interval = DEFAULT_STATS_INTERVAL;
827 OSSpinLockUnlock(&global.lock);
828 }
829 else if (!strcasecmp(l[0], "max_file_size"))
830 {
831 /* = max_file_size bytes */
832 pthread_mutex_lock(global.db_lock);
833
834 if (global.dbtype & DB_TYPE_FILE)
835 {
836 asl_store_close(global.file_db);
837 global.file_db = NULL;
838 if (eval) global.db_file_max = atoi(l[1]);
839 else global.db_file_max = DEFAULT_DB_FILE_MAX;
840 }
841
842 pthread_mutex_unlock(global.db_lock);
843 }
844 else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore")))
845 {
846 /* NB this is private / unpublished */
847 /* = db type [max]... */
848 if (eval)
849 {
850 v32a = 0;
851 v32b = 0;
852 v32c = 0;
853
854 if ((l[1][0] >= '0') && (l[1][0] <= '9'))
855 {
856 intval = atoi(l[1]);
857 if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
858 if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]);
859 if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]);
860 }
861 else if (!strcasecmp(l[1], "file"))
862 {
863 intval = DB_TYPE_FILE;
864 if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]);
865 }
866 else if (!strncasecmp(l[1], "mem", 3))
867 {
868 intval = DB_TYPE_MEMORY;
869 if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]);
870 }
871 else
872 {
873 free_string_list(l);
874 return -1;
875 }
876
877 if (v32a == 0) v32a = global.db_file_max;
878 if (v32b == 0) v32b = global.db_memory_max;
879 if (v32c == 0) v32c = global.db_memory_str_max;
880
881 config_data_store(intval, v32a, v32b, v32c);
882 }
883 else
884 {
885 #if TARGET_OS_EMBEDDED
886 intval = DB_TYPE_MEMORY;
887 #else
888 intval = DB_TYPE_FILE;
889 #endif
890 config_data_store(intval, DEFAULT_DB_FILE_MAX, DEFAULT_DB_MEMORY_MAX, DEFAULT_DB_MEMORY_STR_MAX);
891 }
892 }
893
894 free_string_list(l);
895 return 0;
896 }
897
898 static int
899 control_message(asl_msg_t *msg)
900 {
901 const char *str = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
902
903 if (str == NULL) return 0;
904
905 if (!strncmp(str, "= reset", 7))
906 {
907 init_globals();
908 return asl_action_reset();
909 }
910 else if (!strncmp(str, "= crash", 7))
911 {
912 abort();
913 }
914 else if (!strncmp(str, "@ ", 2))
915 {
916 return asl_action_control_set_param(str);
917 }
918 else if (!strncmp(str, "= ", 2))
919 {
920 return control_set_param(str, true);
921 }
922
923 return 0;
924 }
925
926 void
927 process_message(asl_msg_t *msg, uint32_t source)
928 {
929 int64_t msize = 0;
930 static bool wq_draining = false;
931 bool is_control = false;
932 asl_msg_t *x;
933
934 if (msg == NULL) return;
935
936 is_control = asl_check_option(msg, ASL_OPT_CONTROL) != 0;
937
938 if ((!is_control) && wq_draining)
939 {
940 if (global.memory_size >= (global.memory_max / 2))
941 {
942 asldebug("Work queue draining: dropped message.\n");
943 asl_msg_release(msg);
944 return;
945 }
946 else
947 {
948 asldebug("Work queue re-enabled at 1/2 max. size %lld max %lld\n", global.memory_size, global.memory_max);
949 wq_draining = false;
950 }
951 }
952
953 __block vproc_transaction_t vt = vproc_transaction_begin(NULL);
954
955 for (x = msg; x != NULL; x = x->next) msize += x->mem_size;
956
957 if ((global.memory_size + msize) >= global.memory_max)
958 {
959 char str[256];
960
961 wq_draining = true;
962 asl_msg_release(msg);
963
964 asldebug("Work queue disabled. msize %lld size %lld max %lld\n", msize, global.memory_size + msize, global.memory_max);
965 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);
966 msg = asl_msg_from_string(str);
967 }
968
969 OSAtomicAdd64(msize, &global.memory_size);
970 OSAtomicIncrement32(&global.work_queue_count);
971
972 dispatch_async(global.work_queue, ^{
973 int32_t kplevel;
974 uint32_t status;
975 uid_t uid;
976
977 kplevel = -1;
978 uid = -2;
979
980 status = aslmsg_verify(msg, source, &kplevel, &uid);
981 if (status == VERIFY_STATUS_OK)
982 {
983 if ((source == SOURCE_KERN) && (kplevel >= 0))
984 {
985 if (kplevel > 7) kplevel = 7;
986 if (kern_notify_token[kplevel] < 0)
987 {
988 status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel]));
989 if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status);
990 }
991
992 notify_post(kern_notify_key[kplevel]);
993 }
994
995 if ((uid == 0) && is_control) control_message(msg);
996
997 /*
998 * Send message to output module chain (asl is first).
999 * The last module in the chain will decrement global.memory_size.
1000 */
1001 asl_out_message(msg, msize);
1002 }
1003 else
1004 {
1005 OSAtomicAdd64(-1ll * msize, &global.memory_size);
1006 }
1007
1008 asl_msg_release(msg);
1009
1010 OSAtomicDecrement32(&global.work_queue_count);
1011 vproc_transaction_end(NULL, vt);
1012 });
1013 }
1014
1015 int
1016 internal_log_message(const char *str)
1017 {
1018 asl_msg_t *msg;
1019
1020 if (str == NULL) return 1;
1021
1022 msg = asl_msg_from_string(str);
1023 if (msg == NULL) return 1;
1024
1025 process_message(msg, SOURCE_INTERNAL);
1026
1027 return 0;
1028 }
1029
1030 int
1031 asldebug(const char *str, ...)
1032 {
1033 va_list v;
1034 FILE *dfp = NULL;
1035
1036 if (global.debug == 0) return 0;
1037
1038 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
1039 else dfp = fopen(global.debug_file, "a");
1040 if (dfp == NULL) return 0;
1041
1042 va_start(v, str);
1043 fprintf(dfp, "W %d %llu", global.work_queue_count, global.memory_size);
1044 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);
1045 fprintf(dfp, " ; ");
1046 vfprintf(dfp, str, v);
1047 va_end(v);
1048
1049 fclose(dfp);
1050
1051 return 0;
1052 }
1053
1054 void
1055 asl_mark(void)
1056 {
1057 char *str = NULL;
1058
1059 asprintf(&str, "[Sender syslogd] [Level 6] [PID %u] [Message -- MARK --] [UID 0] [UID 0] [Facility syslog]", global.pid);
1060 internal_log_message(str);
1061 free(str);
1062 }
1063
1064 asl_msg_t *
1065 asl_syslog_input_convert(const char *in, int len, char *rhost, uint32_t source)
1066 {
1067 int pf, pri, index, n;
1068 char *p, *colon, *brace, *space, *tmp, *tval, *hval, *sval, *pval, *mval;
1069 char prival[8];
1070 const char *fval;
1071 asl_msg_t *msg;
1072 struct tm time;
1073 time_t tick;
1074
1075 if (in == NULL) return NULL;
1076 if (len <= 0) return NULL;
1077
1078 pri = LOG_DEBUG;
1079 if (source == SOURCE_KERN) pri = LOG_NOTICE;
1080
1081 tval = NULL;
1082 hval = NULL;
1083 sval = NULL;
1084 pval = NULL;
1085 mval = NULL;
1086 fval = NULL;
1087
1088 index = 0;
1089 p = (char *)in;
1090
1091 /* skip leading whitespace */
1092 while ((index < len) && ((*p == ' ') || (*p == '\t')))
1093 {
1094 p++;
1095 index++;
1096 }
1097
1098 if (index >= len) return NULL;
1099
1100 /* parse "<NN>" priority (level and facility) */
1101 if (*p == '<')
1102 {
1103 p++;
1104 index++;
1105
1106 n = sscanf(p, "%d", &pf);
1107 if (n == 1)
1108 {
1109 pri = pf & 0x7;
1110 if (pf > 0x7) fval = asl_syslog_faciliy_num_to_name(pf & LOG_FACMASK);
1111 }
1112
1113 while ((index < len) && (*p != '>'))
1114 {
1115 p++;
1116 index++;
1117 }
1118
1119 if (index < len)
1120 {
1121 p++;
1122 index++;
1123 }
1124 }
1125
1126 snprintf(prival, sizeof(prival), "%d", pri);
1127
1128 /* check if a timestamp is included */
1129 if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' '))
1130 {
1131 tmp = malloc(16);
1132 if (tmp == NULL) return NULL;
1133
1134 memcpy(tmp, p, 15);
1135 tmp[15] = '\0';
1136
1137 tick = asl_core_parse_time(tmp, NULL);
1138 if (tick == (time_t)-1)
1139 {
1140 tval = tmp;
1141 }
1142 else
1143 {
1144 free(tmp);
1145 gmtime_r(&tick, &time);
1146 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);
1147 }
1148
1149 p += 16;
1150 index += 16;
1151 }
1152
1153 /* stop here for kernel messages */
1154 if (source == SOURCE_KERN)
1155 {
1156 msg = asl_msg_new(ASL_TYPE_MSG);
1157 if (msg == NULL) return NULL;
1158
1159 asl_msg_set_key_val(msg, ASL_KEY_MSG, p);
1160 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
1161 asl_msg_set_key_val(msg, ASL_KEY_PID, "0");
1162
1163 return msg;
1164 }
1165
1166 /* if message is from a network socket, hostname follows */
1167 if (source == SOURCE_UDP_SOCKET)
1168 {
1169 space = strchr(p, ' ');
1170 if (space != NULL)
1171 {
1172 n = space - p;
1173 hval = malloc(n + 1);
1174 if (hval == NULL) return NULL;
1175
1176 memcpy(hval, p, n);
1177 hval[n] = '\0';
1178
1179 p = space + 1;
1180 index += (n + 1);
1181 }
1182 }
1183
1184 colon = strchr(p, ':');
1185 brace = strchr(p, '[');
1186
1187 /* check for "sender:" or sender[pid]:" */
1188 if (colon != NULL)
1189 {
1190 if ((brace != NULL) && (brace < colon))
1191 {
1192 n = brace - p;
1193 sval = malloc(n + 1);
1194 if (sval == NULL) return NULL;
1195
1196 memcpy(sval, p, n);
1197 sval[n] = '\0';
1198
1199 n = colon - (brace + 1) - 1;
1200 pval = malloc(n + 1);
1201 if (pval == NULL) return NULL;
1202
1203 memcpy(pval, (brace + 1), n);
1204 pval[n] = '\0';
1205 }
1206 else
1207 {
1208 n = colon - p;
1209 sval = malloc(n + 1);
1210 if (sval == NULL) return NULL;
1211
1212 memcpy(sval, p, n);
1213 sval[n] = '\0';
1214 }
1215
1216 n = colon - p;
1217 p = colon + 1;
1218 index += (n + 1);
1219 }
1220
1221 if (*p == ' ')
1222 {
1223 p++;
1224 index++;
1225 }
1226
1227 n = len - index;
1228 if (n > 0)
1229 {
1230 mval = malloc(n + 1);
1231 if (mval == NULL) return NULL;
1232
1233 memcpy(mval, p, n);
1234 mval[n] = '\0';
1235 }
1236
1237 if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER);
1238
1239 msg = asl_msg_new(ASL_TYPE_MSG);
1240 if (msg == NULL) return NULL;
1241
1242 if (tval != NULL)
1243 {
1244 asl_msg_set_key_val(msg, ASL_KEY_TIME, tval);
1245 free(tval);
1246 }
1247
1248 if (fval != NULL) asl_msg_set_key_val(msg, "Facility", fval);
1249 else asl_msg_set_key_val(msg, "Facility", "user");
1250
1251 if (sval != NULL)
1252 {
1253 asl_msg_set_key_val(msg, ASL_KEY_SENDER, sval);
1254 free(sval);
1255 }
1256
1257 if (pval != NULL)
1258 {
1259 asl_msg_set_key_val(msg, ASL_KEY_PID, pval);
1260 free(pval);
1261 }
1262 else
1263 {
1264 asl_msg_set_key_val(msg, ASL_KEY_PID, "-1");
1265 }
1266
1267 if (mval != NULL)
1268 {
1269 asl_msg_set_key_val(msg, ASL_KEY_MSG, mval);
1270 free(mval);
1271 }
1272
1273 asl_msg_set_key_val(msg, ASL_KEY_LEVEL, prival);
1274 asl_msg_set_key_val(msg, ASL_KEY_UID, "-2");
1275 asl_msg_set_key_val(msg, ASL_KEY_GID, "-2");
1276
1277 if (hval != NULL)
1278 {
1279 asl_msg_set_key_val(msg, ASL_KEY_HOST, hval);
1280 free(hval);
1281 }
1282 else if (rhost != NULL)
1283 {
1284 asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
1285 }
1286
1287 return msg;
1288 }
1289
1290 asl_msg_t *
1291 asl_input_parse(const char *in, int len, char *rhost, uint32_t source)
1292 {
1293 asl_msg_t *msg;
1294 int status, x, legacy, off;
1295
1296 asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in);
1297
1298 if (in == NULL) return NULL;
1299
1300 legacy = 1;
1301 msg = NULL;
1302
1303 /* calculate length if not provided */
1304 if (len == 0) len = strlen(in);
1305
1306 /*
1307 * Determine if the input is "old" syslog format or new ASL format.
1308 * Old format lines should start with "<", but they can just be straight text.
1309 * ASL input may start with a length (10 bytes) followed by a space and a '['.
1310 * The length is optional, so ASL messages may also just start with '['.
1311 */
1312 if ((in[0] != '<') && (len > 11))
1313 {
1314 status = sscanf(in, "%d ", &x);
1315 if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0;
1316 }
1317
1318 if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, source);
1319
1320 off = 11;
1321 if (in[0] == '[') off = 0;
1322
1323 msg = asl_msg_from_string(in + off);
1324 if (msg == NULL) return NULL;
1325
1326 if (rhost != NULL) asl_msg_set_key_val(msg, ASL_KEY_HOST, rhost);
1327
1328 return msg;
1329 }
1330
1331 #if !TARGET_IPHONE_SIMULATOR
1332 void
1333 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)
1334 {
1335 asl_msg_t *m;
1336 char str[256];
1337 time_t now;
1338
1339 if (global.launchd_enabled == 0) return;
1340
1341 /*
1342 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",
1343 when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1344 */
1345
1346 m = asl_msg_new(ASL_TYPE_MSG);
1347 if (m == NULL) return;
1348
1349 /* Level */
1350 if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG;
1351 if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG;
1352 snprintf(str, sizeof(str), "%d", priority);
1353
1354 asl_msg_set_key_val(m, ASL_KEY_LEVEL, str);
1355
1356 /* Time */
1357 if (when != NULL)
1358 {
1359 snprintf(str, sizeof(str), "%llu", (unsigned long long) when->tv_sec);
1360 asl_msg_set_key_val(m, ASL_KEY_TIME, str);
1361
1362 snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec);
1363 asl_msg_set_key_val(m, ASL_KEY_TIME_NSEC, str);
1364 }
1365 else
1366 {
1367 now = time(NULL);
1368 snprintf(str, sizeof(str), "%llu", (unsigned long long) now);
1369 asl_msg_set_key_val(m, ASL_KEY_TIME, str);
1370 }
1371
1372 /* Facility */
1373 asl_msg_set_key_val(m, ASL_KEY_FACILITY, FACILITY_CONSOLE);
1374
1375 /* UID */
1376 snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid);
1377 asl_msg_set_key_val(m, ASL_KEY_UID, str);
1378
1379 /* GID */
1380 snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid);
1381 asl_msg_set_key_val(m, ASL_KEY_GID, str);
1382
1383 /* PID */
1384 if (from_pid != 0)
1385 {
1386 snprintf(str, sizeof(str), "%u", (unsigned int)from_pid);
1387 asl_msg_set_key_val(m, ASL_KEY_PID, str);
1388 }
1389
1390 /* Reference PID */
1391 if ((about_pid > 0) && (about_pid != from_pid))
1392 {
1393 snprintf(str, sizeof(str), "%u", (unsigned int)about_pid);
1394 asl_msg_set_key_val(m, ASL_KEY_REF_PID, str);
1395 }
1396
1397 /* Sender */
1398 if (from_name != NULL)
1399 {
1400 asl_msg_set_key_val(m, ASL_KEY_SENDER, from_name);
1401 }
1402
1403 /* ReadUID */
1404 if (sender_uid != 0)
1405 {
1406 snprintf(str, sizeof(str), "%d", (int)sender_uid);
1407 asl_msg_set_key_val(m, ASL_KEY_READ_UID, str);
1408 }
1409
1410 /* Reference Process */
1411 if (about_name != NULL)
1412 {
1413 if ((from_name != NULL) && (strcmp(from_name, about_name) != 0))
1414 {
1415 asl_msg_set_key_val(m, ASL_KEY_REF_PROC, about_name);
1416 }
1417 }
1418
1419 /* Session */
1420 if (session_name != NULL)
1421 {
1422 asl_msg_set_key_val(m, ASL_KEY_SESSION, session_name);
1423 }
1424
1425 /* Message */
1426 if (msg != NULL)
1427 {
1428 asl_msg_set_key_val(m, ASL_KEY_MSG, msg);
1429 }
1430
1431 process_message(m, SOURCE_LAUNCHD);
1432 }
1433
1434 #endif