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