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