2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <sys/ucred.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
36 #include <sys/fslog.h>
39 #include <vproc_priv.h>
40 #include <mach/mach.h>
42 #include <libkern/OSAtomic.h>
45 #define LIST_SIZE_DELTA 256
47 #define streq(A,B) (strcmp(A,B)==0)
48 #define forever for(;;)
50 #define ASL_MSG_TYPE_MASK 0x0000000f
51 #define ASL_TYPE_ERROR 2
53 #define ASL_KEY_FACILITY "Facility"
55 #define FACILITY_USER "user"
56 #define FACILITY_CONSOLE "com.apple.console"
57 #define SYSTEM_RESERVED "com.apple.system"
58 #define SYSTEM_RESERVED_LEN 16
60 #define VERIFY_STATUS_OK 0
61 #define VERIFY_STATUS_INVALID_MESSAGE 1
62 #define VERIFY_STATUS_EXCEEDED_QUOTA 2
64 extern void disaster_message(aslmsg m
);
65 extern int asl_action_reset(void);
67 static char myname
[MAXHOSTNAMELEN
+ 1] = {0};
68 static int name_change_token
= -1;
70 static OSSpinLock count_lock
= 0;
73 static vproc_transaction_t vproc_trans
= {0};
76 #define QUOTA_TABLE_SIZE 8192
77 #define QUOTA_TABLE_SLOTS 8
79 #define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***"
80 #define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit - remaining messages this second discarded ***"
81 #define QUOTA_EXCEEDED_LEVEL "3"
83 #define DEFAULT_DB_FILE_MAX 25600000
84 #define DEFAULT_DB_MEMORY_MAX 8192
85 #define DEFAULT_DB_MINI_MAX 256
86 #define DEFAULT_MPS_LIMIT 500
87 #define DEFAULT_REMOTE_DELAY 5000
88 #define DEFAULT_BSD_MAX_DUP_SEC 30
89 #define DEFAULT_MARK_SEC 0
90 #define DEFAULT_UTMP_TTL_SEC 31622400
92 static time_t quota_table_time
= 0;
93 static pid_t quota_table_pid
[QUOTA_TABLE_SIZE
];
94 static int32_t quota_table_quota
[QUOTA_TABLE_SIZE
];
95 static int32_t kern_quota
;
96 static int32_t kern_level
;
98 static const char *kern_notify_key
[] =
100 "com.apple.system.log.kernel.emergency",
101 "com.apple.system.log.kernel.alert",
102 "com.apple.system.log.kernel.critical",
103 "com.apple.system.log.kernel.error",
104 "com.apple.system.log.kernel.warning",
105 "com.apple.system.log.kernel.notice",
106 "com.apple.system.log.kernel.info",
107 "com.apple.system.log.kernel.debug"
110 static int kern_notify_token
[8] = {-1, -1, -1, -1, -1, -1, -1, -1 };
113 _insertString(char *s
, char **l
, uint32_t x
)
117 if (s
== NULL
) return l
;
120 l
= (char **)malloc(2 * sizeof(char *));
121 if (l
== NULL
) return NULL
;
134 for (i
= 0; l
[i
] != NULL
; i
++);
135 len
= i
+ 1; /* count the NULL on the end of the list too! */
137 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
138 if (l
== NULL
) return NULL
;
140 if ((x
>= (len
- 1)) || (x
== IndexNull
))
142 l
[len
- 1] = strdup(s
);
143 if (l
[len
- 1] == NULL
)
153 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
155 if (l
[x
] == NULL
) return NULL
;
161 explode(const char *s
, const char *delim
)
168 if (s
== NULL
) return NULL
;
176 for (i
= 0; p
[i
] != '\0'; i
++)
180 /* not inside a quoted string: check for delimiters and quotes */
181 if (strchr(delim
, p
[i
]) != NULL
) break;
182 else if (p
[i
] == '\'') quote
= p
[i
];
183 else if (p
[i
] == '"') quote
= p
[i
];
187 /* inside a quoted string - look for matching quote */
188 if (p
[i
] == quote
) quote
= '\0';
194 if (t
== NULL
) return NULL
;
196 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
198 l
= _insertString(t
, l
, IndexNull
);
201 if (p
[i
] == '\0') return l
;
202 if (p
[i
+ 1] == '\0') l
= _insertString("", l
, IndexNull
);
214 if (l
== NULL
) return;
215 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
220 * Quotas are maintained using a very fast fixed-size table.
221 * We hash into the pid table (quota_table_pid) using the last 10
222 * bits of the pid, so the table has 1024 "buckets". The table is
223 * actually just an array with 8 entry slots (for collisions) per bucket.
224 * If there are more than 8 pids that hash to the same bucket, we
225 * re-use the one with the lowest message usage (highest remaining
226 * quota). This can lead to "generosity: if there are nine or more
227 * pids with the same last 10 bits all logging like crazy, we may
228 * end up allowing some of them to log more than their quota.
229 * That would be a remarkably rare occurrence.
233 quota_check(pid_t pid
, time_t now
, aslmsg msg
, uint32_t level
)
238 if (msg
== NULL
) return VERIFY_STATUS_INVALID_MESSAGE
;
239 if (global
.mps_limit
== 0) return VERIFY_STATUS_OK
;
241 if (quota_table_time
!= now
)
243 memset(quota_table_pid
, 0, sizeof(quota_table_pid
));
244 kern_quota
= global
.mps_limit
;
246 quota_table_time
= now
;
249 /* kernel gets it's own quota */
252 if (level
< kern_level
) kern_level
= level
;
253 if (kern_quota
> 0) kern_quota
--;
255 if (kern_quota
> 0) return VERIFY_STATUS_OK
;
256 if (kern_quota
< 0) return VERIFY_STATUS_EXCEEDED_QUOTA
;
261 asprintf(&str
, QUOTA_KERN_EXCEEDED_MESSAGE
, global
.mps_limit
);
264 asl_set(msg
, ASL_KEY_MSG
, str
);
266 lstr
[0] = kern_level
+ '0';
268 asl_set(msg
, ASL_KEY_LEVEL
, lstr
);
271 return VERIFY_STATUS_OK
;
274 /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */
275 x
= (pid
& 0x000003ff) << 3;
277 max
= quota_table_quota
[x
];
279 for (i
= 0; i
< QUOTA_TABLE_SLOTS
; i
++)
281 if (quota_table_pid
[x
] == 0)
283 quota_table_pid
[x
] = pid
;
284 quota_table_quota
[x
] = global
.mps_limit
;
286 return VERIFY_STATUS_OK
;
289 if (quota_table_pid
[x
] == pid
)
291 quota_table_quota
[x
] = quota_table_quota
[x
] - 1;
293 if (quota_table_quota
[x
] == 0)
295 quota_table_quota
[x
] = -1;
298 asprintf(&str
, QUOTA_EXCEEDED_MESSAGE
, (int)pid
, global
.mps_limit
);
301 asl_set(msg
, ASL_KEY_MSG
, str
);
303 asl_set(msg
, ASL_KEY_LEVEL
, QUOTA_EXCEEDED_LEVEL
);
306 return VERIFY_STATUS_OK
;
309 if (quota_table_quota
[x
] < 0)
311 return VERIFY_STATUS_EXCEEDED_QUOTA
;
314 return VERIFY_STATUS_OK
;
317 if (quota_table_quota
[x
] > max
)
320 max
= quota_table_quota
[x
];
326 /* can't find the pid and no slots were available - reuse slot with highest remaining quota */
327 asldebug("Quotas: reused slot %d pid %d quota %d for new pid %d\n", maxx
, (int)quota_table_pid
[maxx
], quota_table_quota
[maxx
], (int)pid
);
328 quota_table_pid
[maxx
] = pid
;
329 quota_table_quota
[maxx
] = global
.mps_limit
;
331 return VERIFY_STATUS_OK
;
335 asl_check_option(aslmsg msg
, const char *opt
)
340 if (msg
== NULL
) return 0;
341 if (opt
== NULL
) return 0;
344 if (len
== 0) return 0;
346 p
= asl_get(msg
, ASL_KEY_OPTION
);
347 if (p
== NULL
) return 0;
351 while ((*p
== ' ') || (*p
== '\t') || (*p
== ',')) p
++;
352 if (*p
== '\0') return 0;
354 if (strncasecmp(p
, opt
, len
) == 0)
357 if ((*p
== ' ') || (*p
== '\t') || (*p
== ',') || (*p
== '\0')) return 1;
360 while ((*p
!= ' ') && (*p
!= '\t') && (*p
!= ',') && (*p
!= '\0')) p
++;
369 static dispatch_once_t once
;
373 dispatch_once(&once
, ^{
374 snprintf(myname
, sizeof(myname
), "%s", "localhost");
375 notify_register_check(kNotifySCHostNameChange
, &name_change_token
);
381 if (name_change_token
>= 0) status
= notify_check(name_change_token
, &check
);
383 if ((status
== 0) && (check
== 0)) return (const char *)myname
;
385 if (gethostname(myname
, MAXHOSTNAMELEN
) < 0)
387 snprintf(myname
, sizeof(myname
), "%s", "localhost");
391 dot
= strchr(myname
, '.');
392 if (dot
!= NULL
) *dot
= '\0';
395 return (const char *)myname
;
399 asl_client_count_increment()
401 OSSpinLockLock(&count_lock
);
403 #ifndef CONFIG_IPHONE
404 if (global
.client_count
== 0) vproc_trans
= vproc_transaction_begin(NULL
);
406 global
.client_count
++;
408 asldebug("global.client_count++ (%d)\n", global
.client_count
);
411 OSSpinLockUnlock(&count_lock
);
415 asl_client_count_decrement()
417 OSSpinLockLock(&count_lock
);
419 if (global
.client_count
> 0) global
.client_count
--;
420 #ifndef CONFIG_IPHONE
421 if (global
.client_count
== 0) vproc_transaction_end(NULL
, vproc_trans
);
424 asldebug("global.client_count-- (%d)\n", global
.client_count
);
427 OSSpinLockUnlock(&count_lock
);
431 * Checks message content and sets attributes as required
433 * SOURCE_INTERNAL log messages sent by syslogd itself
434 * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
435 * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
436 * SOURCE_UDP_SOCKET from the network
437 * SOURCE_KERN from the kernel
438 * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
439 * SOURCE_LAUNCHD forwarded from launchd
443 aslmsg_verify(aslmsg msg
, uint32_t source
, int32_t *kern_post_level
, uid_t
*uid_out
)
445 const char *val
, *fac
, *ruval
, *rgval
;
450 uint32_t status
, level
, fnum
;
453 if (msg
== NULL
) return VERIFY_STATUS_INVALID_MESSAGE
;
455 if (kern_post_level
!= NULL
) *kern_post_level
= -1;
456 if (uid_out
!= NULL
) *uid_out
= -2;
461 val
= asl_get(msg
, ASL_KEY_PID
);
462 if (val
== NULL
) asl_set(msg
, ASL_KEY_PID
, "0");
463 else pid
= (pid_t
)atoi(val
);
465 /* if PID is 1 (launchd), use the refpid if there is one */
468 val
= asl_get(msg
, ASL_KEY_REF_PID
);
469 if (val
!= NULL
) pid
= (pid_t
)atoi(val
);
476 val
= asl_get(msg
, ASL_KEY_LEVEL
);
477 level
= ASL_LEVEL_DEBUG
;
478 if ((val
!= NULL
) && (val
[1] == '\0') && (val
[0] >= '0') && (val
[0] <= '7')) level
= val
[0] - '0';
479 snprintf(buf
, sizeof(buf
), "%d", level
);
480 asl_set(msg
, ASL_KEY_LEVEL
, buf
);
483 * check quota if no processes are watching
485 if (global
.watchers_active
== 0)
487 status
= quota_check(pid
, now
, msg
, level
);
488 if (status
!= VERIFY_STATUS_OK
) return status
;
492 val
= asl_get(msg
, ASL_KEY_TIME
);
493 if (val
!= NULL
) tick
= asl_parse_time(val
);
495 /* Set time to now if it is unset or from the future (not allowed!) */
496 if ((tick
== 0) || (tick
> now
)) tick
= now
;
498 /* Canonical form: seconds since the epoch */
499 snprintf(buf
, sizeof(buf
) - 1, "%lu", tick
);
500 asl_set(msg
, ASL_KEY_TIME
, buf
);
502 val
= asl_get(msg
, ASL_KEY_HOST
);
503 if (val
== NULL
) asl_set(msg
, ASL_KEY_HOST
, whatsmyhostname());
506 val
= asl_get(msg
, ASL_KEY_UID
);
510 if ((uid
== 0) && strcmp(val
, "0")) uid
= -2;
511 if (uid_out
!= NULL
) *uid_out
= uid
;
515 val
= asl_get(msg
, ASL_KEY_GID
);
519 if ((gid
== 0) && strcmp(val
, "0")) gid
= -2;
526 case SOURCE_INTERNAL
:
528 asl_set(msg
, ASL_KEY_UID
, "0");
529 asl_set(msg
, ASL_KEY_GID
, "0");
532 case SOURCE_ASL_SOCKET
:
533 case SOURCE_ASL_MESSAGE
:
536 /* we trust the UID & GID in the message */
541 /* we do not trust the UID 0 or GID 0 or 80 in the message */
542 if (uid
== 0) asl_set(msg
, ASL_KEY_UID
, "-2");
543 if ((gid
== 0) || (gid
== 80)) asl_set(msg
, ASL_KEY_GID
, "-2");
548 val
= asl_get(msg
, ASL_KEY_SENDER
);
555 asl_set(msg
, ASL_KEY_SENDER
, "kernel");
558 case SOURCE_INTERNAL
:
560 asl_set(msg
, ASL_KEY_SENDER
, "syslogd");
565 asl_set(msg
, ASL_KEY_SENDER
, "Unknown");
569 else if ((source
!= SOURCE_KERN
) && (uid
!= 0) && (!strcmp(val
, "kernel")))
571 /* allow UID 0 to send messages with "Sender kernel", but nobody else */
572 asl_set(msg
, ASL_KEY_SENDER
, "Unknown");
576 fac
= asl_get(msg
, ASL_KEY_FACILITY
);
579 if (source
== SOURCE_KERN
) fac
= "kern";
581 asl_set(msg
, ASL_KEY_FACILITY
, fac
);
583 else if (fac
[0] == '#')
586 if ((fac
[1] >= '0') && (fac
[1] <= '9'))
588 fnum
= atoi(fac
+ 1) << 3;
589 if ((fnum
== 0) && (strcmp(fac
+ 1, "0"))) fnum
= LOG_USER
;
592 fac
= asl_syslog_faciliy_num_to_name(fnum
);
593 asl_set(msg
, ASL_KEY_FACILITY
, fac
);
595 else if (!strncmp(fac
, SYSTEM_RESERVED
, SYSTEM_RESERVED_LEN
))
597 /* only UID 0 may use "com.apple.system" */
598 if (uid
!= 0) asl_set(msg
, ASL_KEY_FACILITY
, FACILITY_USER
);
602 * kernel messages are only readable by root and admin group.
603 * all other messages are admin-only readable unless they already
604 * have specific read access controls set.
606 if (source
== SOURCE_KERN
)
608 asl_set(msg
, ASL_KEY_READ_UID
, "0");
609 asl_set(msg
, ASL_KEY_READ_GID
, "80");
613 ruval
= asl_get(msg
, ASL_KEY_READ_UID
);
614 rgval
= asl_get(msg
, ASL_KEY_READ_GID
);
616 if ((ruval
== NULL
) && (rgval
== NULL
))
618 asl_set(msg
, ASL_KEY_READ_GID
, "80");
622 /* Set DB Expire Time for com.apple.system.utmpx and lastlog */
623 if ((!strcmp(fac
, "com.apple.system.utmpx")) || (!strcmp(fac
, "com.apple.system.lastlog")))
625 snprintf(buf
, sizeof(buf
), "%lu", tick
+ global
.utmp_ttl
);
626 asl_set(msg
, ASL_KEY_EXPIRE_TIME
, buf
);
629 /* Set DB Expire Time for Filesystem errors */
630 if (!strcmp(fac
, FSLOG_VAL_FACILITY
))
632 snprintf(buf
, sizeof(buf
), "%lu", tick
+ FS_TTL_SEC
);
633 asl_set(msg
, ASL_KEY_EXPIRE_TIME
, buf
);
637 * special case handling of kernel disaster messages
639 if ((source
== SOURCE_KERN
) && (level
<= KERN_DISASTER_LEVEL
))
641 if (kern_post_level
!= NULL
) *kern_post_level
= level
;
642 disaster_message(msg
);
645 return VERIFY_STATUS_OK
;
649 list_append_msg(asl_search_result_t
*list
, aslmsg msg
)
651 if (list
== NULL
) return;
652 if (msg
== NULL
) return;
655 * NB: curr is the list size
656 * grow list if necessary
658 if (list
->count
== list
->curr
)
662 list
->msg
= (asl_msg_t
**)calloc(LIST_SIZE_DELTA
, sizeof(asl_msg_t
*));
666 list
->msg
= (asl_msg_t
**)reallocf(list
->msg
, (list
->curr
+ LIST_SIZE_DELTA
) * sizeof(asl_msg_t
*));
669 if (list
->msg
== NULL
)
676 list
->curr
+= LIST_SIZE_DELTA
;
679 list
->msg
[list
->count
] = (asl_msg_t
*)msg
;
686 OSSpinLockLock(&global
.lock
);
689 free(global
.debug_file
);
690 global
.debug_file
= NULL
;
693 global
.dbtype
= DB_TYPE_MINI
;
695 global
.dbtype
= DB_TYPE_FILE
;
697 global
.db_file_max
= DEFAULT_DB_FILE_MAX
;
698 global
.db_memory_max
= DEFAULT_DB_MEMORY_MAX
;
699 global
.db_mini_max
= DEFAULT_DB_MINI_MAX
;
700 global
.mps_limit
= DEFAULT_MPS_LIMIT
;
701 global
.remote_delay_time
= DEFAULT_REMOTE_DELAY
;
702 global
.bsd_max_dup_time
= DEFAULT_BSD_MAX_DUP_SEC
;
703 global
.mark_time
= DEFAULT_MARK_SEC
;
704 global
.utmp_ttl
= DEFAULT_UTMP_TTL_SEC
;
706 OSSpinLockUnlock(&global
.lock
);
710 * Used to set config parameters.
711 * Line format "= name value"
714 control_set_param(const char *s
)
717 uint32_t intval
, count
, v32a
, v32b
, v32c
;
719 if (s
== NULL
) return -1;
720 if (s
[0] == '\0') return 0;
722 /* skip '=' and whitespace */
724 while ((*s
== ' ') || (*s
== '\t')) s
++;
726 l
= explode(s
, " \t");
727 if (l
== NULL
) return -1;
729 for (count
= 0; l
[count
] != NULL
; count
++);
731 /* name is required */
738 /* value is required */
745 if (!strcasecmp(l
[0], "debug"))
747 /* = debug {0|1} [file] */
749 config_debug(intval
, l
[2]);
751 else if (!strcasecmp(l
[0], "mark_time"))
753 /* = mark_time seconds */
754 OSSpinLockLock(&global
.lock
);
755 global
.mark_time
= atoll(l
[1]);
756 OSSpinLockUnlock(&global
.lock
);
758 else if (!strcasecmp(l
[0], "dup_delay"))
760 /* = bsd_max_dup_time seconds */
761 OSSpinLockLock(&global
.lock
);
762 global
.bsd_max_dup_time
= atoll(l
[1]);
763 OSSpinLockUnlock(&global
.lock
);
765 else if (!strcasecmp(l
[0], "remote_delay"))
767 /* = remote_delay microseconds */
768 OSSpinLockLock(&global
.lock
);
769 global
.remote_delay_time
= atol(l
[1]);
770 OSSpinLockUnlock(&global
.lock
);
772 else if (!strcasecmp(l
[0], "utmp_ttl"))
774 /* = utmp_ttl seconds */
775 OSSpinLockLock(&global
.lock
);
776 global
.utmp_ttl
= (time_t)atoll(l
[1]);
777 OSSpinLockUnlock(&global
.lock
);
779 else if (!strcasecmp(l
[0], "mps_limit"))
781 /* = mps_limit number */
782 OSSpinLockLock(&global
.lock
);
783 global
.mps_limit
= (uint32_t)atol(l
[1]);
784 OSSpinLockUnlock(&global
.lock
);
786 else if (!strcasecmp(l
[0], "max_file_size"))
788 /* = max_file_size bytes */
789 pthread_mutex_lock(global
.db_lock
);
791 if (global
.dbtype
& DB_TYPE_FILE
)
793 asl_store_close(global
.file_db
);
794 global
.file_db
= NULL
;
795 global
.db_file_max
= atoi(l
[1]);
798 pthread_mutex_unlock(global
.db_lock
);
800 else if ((!strcasecmp(l
[0], "db")) || (!strcasecmp(l
[0], "database")) || (!strcasecmp(l
[0], "datastore")))
802 /* NB this is private / unpublished */
803 /* = db type [max]... */
809 if ((l
[1][0] >= '0') && (l
[1][0] <= '9'))
812 if ((count
>= 3) && (strcmp(l
[2], "-"))) v32a
= atoi(l
[2]);
813 if ((count
>= 4) && (strcmp(l
[3], "-"))) v32b
= atoi(l
[3]);
814 if ((count
>= 5) && (strcmp(l
[4], "-"))) v32c
= atoi(l
[4]);
816 else if (!strcasecmp(l
[1], "file"))
818 intval
= DB_TYPE_FILE
;
819 if ((count
>= 3) && (strcmp(l
[2], "-"))) v32a
= atoi(l
[2]);
821 else if (!strncasecmp(l
[1], "mem", 3))
823 intval
= DB_TYPE_MEMORY
;
824 if ((count
>= 3) && (strcmp(l
[2], "-"))) v32b
= atoi(l
[2]);
826 else if (!strncasecmp(l
[1], "min", 3))
828 intval
= DB_TYPE_MINI
;
829 if ((count
>= 3) && (strcmp(l
[2], "-"))) v32c
= atoi(l
[2]);
837 if (v32a
== 0) v32a
= global
.db_file_max
;
838 if (v32b
== 0) v32b
= global
.db_memory_max
;
839 if (v32c
== 0) v32c
= global
.db_mini_max
;
841 config_data_store(intval
, v32a
, v32b
, v32c
);
849 control_message(aslmsg msg
)
851 const char *str
= asl_get(msg
, ASL_KEY_MSG
);
853 if (str
== NULL
) return 0;
855 if (!strncmp(str
, "= reset", 7))
858 return asl_action_reset();
860 else if (!strncmp(str
, "= rotate", 8))
862 const char *p
= str
+ 8;
863 while ((*p
== ' ') || (*p
== '\t')) p
++;
864 if (*p
== '\0') p
= NULL
;
865 return asl_action_file_rotate(p
);
867 else if (!strncmp(str
, "= ", 2))
869 return control_set_param(str
);
876 process_message(aslmsg msg
, uint32_t source
)
882 if (msg
== NULL
) return;
887 status
= aslmsg_verify(msg
, source
, &kplevel
, &uid
);
888 if (status
== VERIFY_STATUS_OK
)
890 if ((source
== SOURCE_KERN
) && (kplevel
>= 0))
892 if (kplevel
> 7) kplevel
= 7;
893 if (kern_notify_token
[kplevel
] < 0)
895 status
= notify_register_plain(kern_notify_key
[kplevel
], &(kern_notify_token
[kplevel
]));
896 if (status
!= 0) asldebug("notify_register_plain(%s) failed status %u\n", status
);
899 notify_post(kern_notify_key
[kplevel
]);
902 if ((uid
== 0) && asl_check_option(msg
, ASL_OPT_CONTROL
)) control_message(msg
);
904 /* send message to output modules */
905 asl_out_message(msg
);
906 if (global
.bsd_out_enabled
) bsd_out_message(msg
);
913 internal_log_message(const char *str
)
917 if (str
== NULL
) return 1;
919 msg
= (aslmsg
)asl_msg_from_string(str
);
920 if (msg
== NULL
) return 1;
922 dispatch_async(global
.work_queue
, ^{ process_message(msg
, SOURCE_INTERNAL
); });
928 asldebug(const char *str
, ...)
933 if (global
.debug
== 0) return 0;
935 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
936 else dfp
= fopen(global
.debug_file
, "a");
937 if (dfp
== NULL
) return 0;
940 vfprintf(dfp
, str
, v
);
954 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]",
956 ASL_KEY_LEVEL
, ASL_LEVEL_INFO
,
957 ASL_KEY_PID
, getpid(),
958 ASL_KEY_MSG
, ASL_KEY_UID
, ASL_KEY_GID
);
960 internal_log_message(str
);
961 if (str
!= NULL
) free(str
);
965 asl_syslog_input_convert(const char *in
, int len
, char *rhost
, uint32_t source
)
967 int pf
, pri
, index
, n
;
968 char *p
, *colon
, *brace
, *space
, *tmp
, *tval
, *hval
, *sval
, *pval
, *mval
;
975 if (in
== NULL
) return NULL
;
976 if (len
<= 0) return NULL
;
989 /* skip leading whitespace */
990 while ((index
< len
) && ((*p
== ' ') || (*p
== '\t')))
996 if (index
>= len
) return NULL
;
998 /* parse "<NN>" priority (level and facility) */
1004 n
= sscanf(p
, "%d", &pf
);
1008 if (pf
> 0x7) fval
= asl_syslog_faciliy_num_to_name(pf
& LOG_FACMASK
);
1011 while ((index
< len
) && (*p
!= '>'))
1024 snprintf(prival
, sizeof(prival
), "%d", pri
);
1026 /* check if a timestamp is included */
1027 if (((len
- index
) > 15) && (p
[9] == ':') && (p
[12] == ':') && (p
[15] == ' '))
1030 if (tmp
== NULL
) return NULL
;
1035 tick
= asl_parse_time(tmp
);
1036 if (tick
== (time_t)-1)
1043 gmtime_r(&tick
, &time
);
1044 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
);
1051 /* stop here for kernel messages */
1052 if (source
== SOURCE_KERN
)
1054 msg
= asl_new(ASL_TYPE_MSG
);
1055 if (msg
== NULL
) return NULL
;
1057 asl_set(msg
, ASL_KEY_MSG
, p
);
1058 asl_set(msg
, ASL_KEY_LEVEL
, prival
);
1059 asl_set(msg
, ASL_KEY_PID
, "0");
1064 /* if message is from a network socket, hostname follows */
1065 if (source
== SOURCE_UDP_SOCKET
)
1067 space
= strchr(p
, ' ');
1071 hval
= malloc(n
+ 1);
1072 if (hval
== NULL
) return NULL
;
1082 colon
= strchr(p
, ':');
1083 brace
= strchr(p
, '[');
1085 /* check for "sender:" or sender[pid]:" */
1088 if ((brace
!= NULL
) && (brace
< colon
))
1091 sval
= malloc(n
+ 1);
1092 if (sval
== NULL
) return NULL
;
1097 n
= colon
- (brace
+ 1) - 1;
1098 pval
= malloc(n
+ 1);
1099 if (pval
== NULL
) return NULL
;
1101 memcpy(pval
, (brace
+ 1), n
);
1107 sval
= malloc(n
+ 1);
1108 if (sval
== NULL
) return NULL
;
1128 mval
= malloc(n
+ 1);
1129 if (mval
== NULL
) return NULL
;
1135 if (fval
== NULL
) fval
= asl_syslog_faciliy_num_to_name(LOG_USER
);
1137 msg
= asl_new(ASL_TYPE_MSG
);
1138 if (msg
== NULL
) return NULL
;
1142 asl_set(msg
, ASL_KEY_TIME
, tval
);
1146 if (fval
!= NULL
) asl_set(msg
, "Facility", fval
);
1147 else asl_set(msg
, "Facility", "user");
1151 asl_set(msg
, ASL_KEY_SENDER
, sval
);
1157 asl_set(msg
, ASL_KEY_PID
, pval
);
1162 asl_set(msg
, ASL_KEY_PID
, "-1");
1167 asl_set(msg
, ASL_KEY_MSG
, mval
);
1171 asl_set(msg
, ASL_KEY_LEVEL
, prival
);
1172 asl_set(msg
, ASL_KEY_UID
, "-2");
1173 asl_set(msg
, ASL_KEY_GID
, "-2");
1177 asl_set(msg
, ASL_KEY_HOST
, hval
);
1180 else if (rhost
!= NULL
)
1182 asl_set(msg
, ASL_KEY_HOST
, rhost
);
1189 asl_input_parse(const char *in
, int len
, char *rhost
, uint32_t source
)
1192 int status
, x
, legacy
, off
;
1194 asldebug("asl_input_parse: %s\n", (in
== NULL
) ? "NULL" : in
);
1196 if (in
== NULL
) return NULL
;
1201 /* calculate length if not provided */
1202 if (len
== 0) len
= strlen(in
);
1205 * Determine if the input is "old" syslog format or new ASL format.
1206 * Old format lines should start with "<", but they can just be straight text.
1207 * ASL input may start with a length (10 bytes) followed by a space and a '['.
1208 * The length is optional, so ASL messages may also just start with '['.
1210 if ((in
[0] != '<') && (len
> 11))
1212 status
= sscanf(in
, "%d ", &x
);
1213 if ((status
== 1) && (in
[10] == ' ') && (in
[11] == '[')) legacy
= 0;
1216 if (legacy
== 1) return asl_syslog_input_convert(in
, len
, rhost
, source
);
1219 if (in
[0] == '[') off
= 0;
1221 msg
= (aslmsg
)asl_msg_from_string(in
+ off
);
1222 if (msg
== NULL
) return NULL
;
1224 if (rhost
!= NULL
) asl_set(msg
, ASL_KEY_HOST
, rhost
);
1230 get_line_from_file(FILE *f
)
1235 out
= fgetln(f
, &len
);
1236 if (out
== NULL
) return NULL
;
1237 if (len
== 0) return NULL
;
1239 s
= malloc(len
+ 1);
1240 if (s
== NULL
) return NULL
;
1242 memcpy(s
, out
, len
);
1244 if (s
[len
- 1] != '\n') len
++;
1250 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
)
1257 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",
1258 when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1261 m
= asl_new(ASL_TYPE_MSG
);
1262 if (m
== NULL
) return;
1265 if (priority
< ASL_LEVEL_EMERG
) priority
= ASL_LEVEL_EMERG
;
1266 if (priority
> ASL_LEVEL_DEBUG
) priority
= ASL_LEVEL_DEBUG
;
1267 snprintf(str
, sizeof(str
), "%d", priority
);
1269 asl_set(m
, ASL_KEY_LEVEL
, str
);
1274 snprintf(str
, sizeof(str
), "%lu", when
->tv_sec
);
1275 asl_set(m
, ASL_KEY_TIME
, str
);
1277 snprintf(str
, sizeof(str
), "%lu", 1000 * (unsigned long int)when
->tv_usec
);
1278 asl_set(m
, ASL_KEY_TIME_NSEC
, str
);
1283 snprintf(str
, sizeof(str
), "%lu", now
);
1284 asl_set(m
, ASL_KEY_TIME
, str
);
1288 asl_set(m
, ASL_KEY_FACILITY
, FACILITY_CONSOLE
);
1291 snprintf(str
, sizeof(str
), "%u", (unsigned int)sender_uid
);
1292 asl_set(m
, ASL_KEY_UID
, str
);
1295 snprintf(str
, sizeof(str
), "%u", (unsigned int)sender_gid
);
1296 asl_set(m
, ASL_KEY_GID
, str
);
1301 snprintf(str
, sizeof(str
), "%u", (unsigned int)from_pid
);
1302 asl_set(m
, ASL_KEY_PID
, str
);
1306 if ((about_pid
> 0) && (about_pid
!= from_pid
))
1308 snprintf(str
, sizeof(str
), "%u", (unsigned int)about_pid
);
1309 asl_set(m
, ASL_KEY_REF_PID
, str
);
1313 if (from_name
!= NULL
)
1315 asl_set(m
, ASL_KEY_SENDER
, from_name
);
1319 if (sender_uid
!= 0)
1321 snprintf(str
, sizeof(str
), "%d", (int)sender_uid
);
1322 asl_set(m
, ASL_KEY_READ_UID
, str
);
1325 /* Reference Process */
1326 if (about_name
!= NULL
)
1328 if ((from_name
!= NULL
) && (strcmp(from_name
, about_name
) != 0))
1330 asl_set(m
, ASL_KEY_REF_PROC
, about_name
);
1335 if (session_name
!= NULL
)
1337 asl_set(m
, ASL_KEY_SESSION
, session_name
);
1343 asl_set(m
, ASL_KEY_MSG
, msg
);
1346 dispatch_async(global
.work_queue
, ^{ process_message(m
, SOURCE_LAUNCHD
); });