2 * Copyright (c) 2004-2010 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 static char myname
[MAXHOSTNAMELEN
+ 1] = {0};
67 static OSSpinLock count_lock
= 0;
70 static vproc_transaction_t vproc_trans
= {0};
73 #define QUOTA_TABLE_SIZE 8192
74 #define QUOTA_TABLE_SLOTS 8
76 #define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***"
77 #define QUOTA_EXCEEDED_LEVEL "3"
79 static time_t quota_table_time
= 0;
80 static pid_t quota_table_pid
[QUOTA_TABLE_SIZE
];
81 static int32_t quota_table_quota
[QUOTA_TABLE_SIZE
];
83 static const char *kern_notify_key
[] =
85 "com.apple.system.log.kernel.emergency",
86 "com.apple.system.log.kernel.alert",
87 "com.apple.system.log.kernel.critical",
88 "com.apple.system.log.kernel.error",
89 "com.apple.system.log.kernel.warning",
90 "com.apple.system.log.kernel.notice",
91 "com.apple.system.log.kernel.info",
92 "com.apple.system.log.kernel.debug"
99 TAILQ_ENTRY(asloutput
) entries
;
106 TAILQ_ENTRY(aslmatch
) entries
;
109 TAILQ_HEAD(ae
, aslevent
) Eventq
;
110 TAILQ_HEAD(ao
, asloutput
) Outq
;
111 TAILQ_HEAD(am
, aslmatch
) Matchq
;
114 _insertString(char *s
, char **l
, uint32_t x
)
118 if (s
== NULL
) return l
;
121 l
= (char **)malloc(2 * sizeof(char *));
122 if (l
== NULL
) return NULL
;
135 for (i
= 0; l
[i
] != NULL
; i
++);
136 len
= i
+ 1; /* count the NULL on the end of the list too! */
138 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
139 if (l
== NULL
) return NULL
;
141 if ((x
>= (len
- 1)) || (x
== IndexNull
))
143 l
[len
- 1] = strdup(s
);
144 if (l
[len
- 1] == NULL
)
154 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
156 if (l
[x
] == NULL
) return NULL
;
162 explode(const char *s
, const char *delim
)
169 if (s
== NULL
) return NULL
;
177 for (i
= 0; p
[i
] != '\0'; i
++)
181 /* not inside a quoted string: check for delimiters and quotes */
182 if (strchr(delim
, p
[i
]) != NULL
) break;
183 else if (p
[i
] == '\'') quote
= p
[i
];
184 else if (p
[i
] == '"') quote
= p
[i
];
188 /* inside a quoted string - look for matching quote */
189 if (p
[i
] == quote
) quote
= '\0';
195 if (t
== NULL
) return NULL
;
197 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
199 l
= _insertString(t
, l
, IndexNull
);
202 if (p
[i
] == '\0') return l
;
203 if (p
[i
+ 1] == '\0') l
= _insertString("", l
, IndexNull
);
215 if (l
== NULL
) return;
216 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
221 * Quotas are maintained using a very fast fixed-size table.
222 * We hash into the pid table (quota_table_pid) using the last 10
223 * bits of the pid, so the table has 1024 "buckets". The table is
224 * actually just an array with 8 entry slots (for collisions) per bucket.
225 * If there are more than 8 pids that hash to the same bucket, we
226 * re-use the one with the lowest message usage (highest remaining
227 * quota). This can lead to "generosity: if there are nine or more
228 * pids with the same last 10 bits all logging like crazy, we may
229 * end up allowing some of them to log more than their quota.
230 * That would be a remarkably rare occurrence.
234 quota_check(pid_t pid
, time_t now
, aslmsg msg
)
239 if (msg
== NULL
) return VERIFY_STATUS_INVALID_MESSAGE
;
240 if (global
.mps_limit
== 0) return VERIFY_STATUS_OK
;
242 OSSpinLockLock(&global
.lock
);
244 if (quota_table_time
!= now
)
246 memset(quota_table_pid
, 0, sizeof(quota_table_pid
));
247 quota_table_time
= now
;
250 /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */
251 x
= (pid
& 0x000003ff) << 3;
253 max
= quota_table_quota
[x
];
255 for (i
= 0; i
< QUOTA_TABLE_SLOTS
; i
++)
257 if (quota_table_pid
[x
] == 0)
259 quota_table_pid
[x
] = pid
;
260 quota_table_quota
[x
] = global
.mps_limit
;
262 OSSpinLockUnlock(&global
.lock
);
263 return VERIFY_STATUS_OK
;
266 if (quota_table_pid
[x
] == pid
)
268 quota_table_quota
[x
] = quota_table_quota
[x
] - 1;
270 if (quota_table_quota
[x
] == 0)
272 quota_table_quota
[x
] = -1;
275 asprintf(&str
, QUOTA_EXCEEDED_MESSAGE
, (int)pid
, global
.mps_limit
);
278 asl_set(msg
, ASL_KEY_MSG
, str
);
280 asl_set(msg
, ASL_KEY_LEVEL
, QUOTA_EXCEEDED_LEVEL
);
283 OSSpinLockUnlock(&global
.lock
);
284 return VERIFY_STATUS_OK
;
287 if (quota_table_quota
[x
] < 0)
289 OSSpinLockUnlock(&global
.lock
);
290 return VERIFY_STATUS_EXCEEDED_QUOTA
;
293 OSSpinLockUnlock(&global
.lock
);
294 return VERIFY_STATUS_OK
;
297 if (quota_table_quota
[x
] > max
)
300 max
= quota_table_quota
[x
];
306 /* can't find the pid and no slots were available - reuse slot with highest remaining quota */
307 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
);
308 quota_table_pid
[maxx
] = pid
;
309 quota_table_quota
[maxx
] = global
.mps_limit
;
311 OSSpinLockUnlock(&global
.lock
);
312 return VERIFY_STATUS_OK
;
316 asl_check_option(aslmsg msg
, const char *opt
)
321 if (msg
== NULL
) return 0;
322 if (opt
== NULL
) return 0;
325 if (len
== 0) return 0;
327 p
= asl_get(msg
, ASL_KEY_OPTION
);
328 if (p
== NULL
) return 0;
332 while ((*p
== ' ') || (*p
== '\t') || (*p
== ',')) p
++;
333 if (*p
== '\0') return 0;
335 if (strncasecmp(p
, opt
, len
) == 0)
338 if ((*p
== ' ') || (*p
== '\t') || (*p
== ',') || (*p
== '\0')) return 1;
341 while ((*p
!= ' ') && (*p
!= '\t') && (*p
!= ',') && (*p
!= '\0')) p
++;
358 aslevent_log(aslmsg msg
, char *outid
)
363 for (i
= Outq
.tqh_first
; i
!= NULL
; i
= i
->entries
.tqe_next
)
365 if ((outid
!= NULL
) && (strcmp(i
->outid
, outid
) == 0))
367 status
= i
->sendmsg(msg
, outid
);
368 if (status
< 0) break;
376 aslevent_addmatch(asl_msg_t
*query
, char *outid
)
378 struct aslmatch
*tmp
;
380 if (query
== NULL
) return -1;
381 if (outid
== NULL
) return -1;
383 tmp
= calloc(1, sizeof(struct aslmatch
));
384 if (tmp
== NULL
) return -1;
388 TAILQ_INSERT_TAIL(&Matchq
, tmp
, entries
);
394 asl_message_match_and_log(aslmsg msg
)
399 if (msg
== NULL
) return;
401 for (i
= Matchq
.tqh_first
; i
!= NULL
; i
= i
->entries
.tqe_next
)
403 if (asl_msg_cmp(i
->query
, (asl_msg_t
*)msg
) != 0)
405 status
= aslevent_log(msg
, i
->outid
);
406 if (status
< 0) break;
412 aslevent_removefd(int fd
)
414 struct aslevent
*e
, *next
;
416 e
= Eventq
.tqh_first
;
420 next
= e
->entries
.tqe_next
;
433 static const char *_source_name(int n
)
437 case SOURCE_INTERNAL
: return "internal";
438 case SOURCE_ASL_SOCKET
: return "ASL socket";
439 case SOURCE_BSD_SOCKET
: return "BSD socket";
440 case SOURCE_UDP_SOCKET
: return "UDP socket";
441 case SOURCE_KERN
: return "kernel";
442 case SOURCE_ASL_MESSAGE
: return "ASL message";
443 case SOURCE_LAUNCHD
: return "launchd";
444 case SOURCE_SESSION
: return "session";
445 default: return "unknown";
454 struct aslevent
*e
, *next
;
456 struct timeval zero
= {0};
459 e
= Eventq
.tqh_first
;
463 next
= e
->entries
.tqe_next
;
467 FD_SET(e
->fd
, &test
);
470 if (select(max
, &test
, NULL
, NULL
, &zero
) < 0)
472 asldebug("aslevent_check: fd %d source %d (%s) errno %d\n", e
->fd
, e
->source
, _source_name(e
->source
), e
->fd
);
485 if (gethostname(myname
, MAXHOSTNAMELEN
) < 0)
487 memset(myname
, 0, sizeof(myname
));
491 dot
= strchr(myname
, '.');
492 if (dot
!= NULL
) *dot
= '\0';
494 return (const char *)myname
;
498 asl_client_count_increment()
500 OSSpinLockLock(&count_lock
);
502 #ifndef CONFIG_IPHONE
503 if (global
.client_count
== 0) vproc_trans
= vproc_transaction_begin(NULL
);
505 global
.client_count
++;
507 asldebug("global.client_count++ (%d)\n", global
.client_count
);
510 OSSpinLockUnlock(&count_lock
);
514 asl_client_count_decrement()
516 OSSpinLockLock(&count_lock
);
518 if (global
.client_count
> 0) global
.client_count
--;
519 #ifndef CONFIG_IPHONE
520 if (global
.client_count
== 0) vproc_transaction_end(NULL
, vproc_trans
);
523 asldebug("global.client_count-- (%d)\n", global
.client_count
);
526 OSSpinLockUnlock(&count_lock
);
530 aslevent_addfd(int source
, int fd
, uint32_t flags
, aslreadfn readfn
, aslwritefn writefn
, aslexceptfn exceptfn
)
533 int found
= 0, status
;
534 #ifdef LOCAL_PEERCRED
540 struct sockaddr_storage ss
;
541 char *sender
, str
[256];
547 memset(&ss
, 0, sizeof(struct sockaddr_storage
));
548 memset(str
, 0, sizeof(str
));
550 len
= sizeof(struct sockaddr_storage
);
552 if (flags
& ADDFD_FLAGS_LOCAL
)
554 snprintf(str
, sizeof(str
), "localhost");
557 #ifdef LOCAL_PEERCRED
560 status
= getsockopt(fd
, LOCAL_PEERCRED
, 1, &cr
, &len
);
570 status
= getpeername(fd
, (struct sockaddr
*)&ss
, &len
);
575 /* UNIX Domain socket */
576 snprintf(str
, sizeof(str
), "localhost");
581 if (inet_ntop(ss
.ss_family
, (struct sockaddr
*)&ss
, str
, 256) == NULL
) sender
= str
;
586 asldebug("source %d fd %d flags 0x%08x UID %d GID %d Sender %s\n", source
, fd
, flags
, u
, g
, (sender
== NULL
) ? "NULL" : sender
);
588 for (e
= Eventq
.tqh_first
; e
!= NULL
; e
= e
->entries
.tqe_next
)
593 e
->writefn
= writefn
;
594 e
->exceptfn
= exceptfn
;
595 if (e
->sender
!= NULL
) free(e
->sender
);
599 e
->sender
= strdup(sender
);
600 if (e
->sender
== NULL
) return -1;
611 e
= calloc(1, sizeof(struct aslevent
));
612 if (e
== NULL
) return -1;
617 e
->writefn
= writefn
;
618 e
->exceptfn
= exceptfn
;
622 e
->sender
= strdup(sender
);
623 if (e
->sender
== NULL
) return -1;
629 TAILQ_INSERT_TAIL(&Eventq
, e
, entries
);
635 * Checks message content and sets attributes as required
637 * SOURCE_INTERNAL log messages sent by syslogd itself
638 * SOURCE_ASL_SOCKET legacy asl(3) TCP socket
639 * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket
640 * SOURCE_UDP_SOCKET from the network
641 * SOURCE_KERN from the kernel
642 * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3)
643 * SOURCE_LAUNCHD forwarded from launchd
647 aslmsg_verify(uint32_t source
, struct aslevent
*e
, aslmsg msg
, int32_t *kern_post_level
)
649 const char *val
, *fac
, *ruval
, *rgval
;
653 uint32_t status
, level
, fnum
;
656 if (msg
== NULL
) return VERIFY_STATUS_INVALID_MESSAGE
;
658 if (kern_post_level
!= NULL
) *kern_post_level
= -1;
664 val
= asl_get(msg
, ASL_KEY_TIME
);
665 if (val
!= NULL
) tick
= asl_parse_time(val
);
667 /* Set time to now if it is unset or from the future (not allowed!) */
668 if ((tick
== 0) || (tick
> now
)) tick
= now
;
670 /* Canonical form: seconds since the epoch */
671 snprintf(buf
, sizeof(buf
) - 1, "%lu", tick
);
672 asl_set(msg
, ASL_KEY_TIME
, buf
);
675 if (e
== NULL
) asl_set(msg
, ASL_KEY_HOST
, whatsmyhostname());
676 else if (e
->sender
!= NULL
)
678 if (!strcmp(e
->sender
, "localhost")) asl_set(msg
, ASL_KEY_HOST
, whatsmyhostname());
679 else asl_set(msg
, ASL_KEY_HOST
, e
->sender
);
685 val
= asl_get(msg
, ASL_KEY_PID
);
686 if (val
== NULL
) asl_set(msg
, ASL_KEY_PID
, "0");
687 else pid
= (pid_t
)atoi(val
);
689 /* if PID is 1 (launchd), use the refpid if there is one */
692 val
= asl_get(msg
, ASL_KEY_REF_PID
);
693 if (val
!= NULL
) pid
= (pid_t
)atoi(val
);
697 * if quotas are enabled and pid > 1 (not kernel or launchd)
698 * and no processes are watching, then check quota
700 if ((global
.mps_limit
> 0) && (pid
> 1) && (global
.watchers_active
== 0))
702 status
= quota_check(pid
, now
, msg
);
703 if (status
!= VERIFY_STATUS_OK
) return status
;
708 val
= asl_get(msg
, ASL_KEY_UID
);
713 case SOURCE_INTERNAL
:
715 /* we know the UID is 0 */
717 asl_set(msg
, ASL_KEY_UID
, "0");
720 case SOURCE_ASL_SOCKET
:
721 case SOURCE_ASL_MESSAGE
:
724 /* we trust the UID in the message */
725 if (val
!= NULL
) uid
= atoi(val
);
728 case SOURCE_BSD_SOCKET
:
729 case SOURCE_UDP_SOCKET
:
733 if (e
== NULL
) asl_set(msg
, ASL_KEY_UID
, "-2");
734 else if (e
->uid
== 99) asl_set(msg
, ASL_KEY_UID
, "-2");
738 snprintf(buf
, sizeof(buf
), "%d", e
->uid
);
739 asl_set(msg
, ASL_KEY_UID
, buf
);
742 else if ((e
!= NULL
) && (e
->uid
!= 99))
745 snprintf(buf
, sizeof(buf
), "%d", e
->uid
);
746 asl_set(msg
, ASL_KEY_UID
, buf
);
751 asl_set(msg
, ASL_KEY_UID
, "-2");
756 val
= asl_get(msg
, ASL_KEY_GID
);
761 case SOURCE_INTERNAL
:
763 /* we know the GID is 0 */
764 asl_set(msg
, ASL_KEY_GID
, "0");
767 case SOURCE_ASL_SOCKET
:
768 case SOURCE_ASL_MESSAGE
:
771 /* we trust the GID in the message */
774 case SOURCE_BSD_SOCKET
:
775 case SOURCE_UDP_SOCKET
:
779 if (e
== NULL
) asl_set(msg
, ASL_KEY_GID
, "-2");
780 else if (e
->gid
== 99) asl_set(msg
, ASL_KEY_GID
, "-2");
783 snprintf(buf
, sizeof(buf
), "%d", e
->gid
);
784 asl_set(msg
, ASL_KEY_GID
, buf
);
787 else if ((e
!= NULL
) && (e
->gid
!= 99))
789 snprintf(buf
, sizeof(buf
), "%d", e
->gid
);
790 asl_set(msg
, ASL_KEY_GID
, buf
);
795 asl_set(msg
, ASL_KEY_GID
, "-2");
800 val
= asl_get(msg
, ASL_KEY_SENDER
);
807 asl_set(msg
, ASL_KEY_SENDER
, "kernel");
810 case SOURCE_INTERNAL
:
812 asl_set(msg
, ASL_KEY_SENDER
, "syslogd");
817 asl_set(msg
, ASL_KEY_SENDER
, "Unknown");
821 else if ((source
!= SOURCE_KERN
) && (uid
!= 0) && (!strcmp(val
, "kernel")))
823 /* allow UID 0 to send messages with "Sender kernel", but nobody else */
824 asl_set(msg
, ASL_KEY_SENDER
, "Unknown");
828 val
= asl_get(msg
, ASL_KEY_LEVEL
);
829 level
= ASL_LEVEL_DEBUG
;
830 if ((val
!= NULL
) && (val
[1] == '\0') && (val
[0] >= '0') && (val
[0] <= '7')) level
= val
[0] - '0';
831 snprintf(buf
, sizeof(buf
), "%d", level
);
832 asl_set(msg
, ASL_KEY_LEVEL
, buf
);
835 fac
= asl_get(msg
, ASL_KEY_FACILITY
);
838 if (source
== SOURCE_KERN
) fac
= "kern";
840 asl_set(msg
, ASL_KEY_FACILITY
, fac
);
842 else if (fac
[0] == '#')
845 if ((fac
[1] >= '0') && (fac
[1] <= '9'))
847 fnum
= atoi(fac
+ 1) << 3;
848 if ((fnum
== 0) && (strcmp(fac
+ 1, "0"))) fnum
= LOG_USER
;
851 fac
= asl_syslog_faciliy_num_to_name(fnum
);
852 asl_set(msg
, ASL_KEY_FACILITY
, fac
);
854 else if (!strncmp(fac
, SYSTEM_RESERVED
, SYSTEM_RESERVED_LEN
))
856 /* only UID 0 may use "com.apple.system" */
857 if (uid
!= 0) asl_set(msg
, ASL_KEY_FACILITY
, FACILITY_USER
);
861 * kernel messages are only readable by root and admin group.
862 * all other messages are admin-only readable unless they already
863 * have specific read access controls set.
865 if (source
== SOURCE_KERN
)
867 asl_set(msg
, ASL_KEY_READ_UID
, "0");
868 asl_set(msg
, ASL_KEY_READ_GID
, "80");
872 ruval
= asl_get(msg
, ASL_KEY_READ_UID
);
873 rgval
= asl_get(msg
, ASL_KEY_READ_GID
);
875 if ((ruval
== NULL
) && (rgval
== NULL
))
877 asl_set(msg
, ASL_KEY_READ_GID
, "80");
881 /* Set DB Expire Time for com.apple.system.utmpx and lastlog */
882 if ((!strcmp(fac
, "com.apple.system.utmpx")) || (!strcmp(fac
, "com.apple.system.lastlog")))
884 snprintf(buf
, sizeof(buf
), "%lu", tick
+ global
.utmp_ttl
);
885 asl_set(msg
, ASL_KEY_EXPIRE_TIME
, buf
);
888 /* Set DB Expire Time for Filestsrem errors */
889 if (!strcmp(fac
, FSLOG_VAL_FACILITY
))
891 snprintf(buf
, sizeof(buf
), "%lu", tick
+ global
.fs_ttl
);
892 asl_set(msg
, ASL_KEY_EXPIRE_TIME
, buf
);
896 * special case handling of kernel disaster messages
898 if ((source
== SOURCE_KERN
) && (level
<= KERN_DISASTER_LEVEL
))
900 if (kern_post_level
!= NULL
) *kern_post_level
= level
;
901 disaster_message(msg
);
904 return VERIFY_STATUS_OK
;
908 aslevent_addoutput(aslsendmsgfn fn
, const char *outid
)
910 struct asloutput
*tmp
;
912 tmp
= calloc(1, sizeof(struct asloutput
));
913 if (tmp
== NULL
) return -1;
918 TAILQ_INSERT_TAIL(&Outq
, tmp
, entries
);
924 aslevent_fdsets(fd_set
*rd
, fd_set
*wr
, fd_set
*ex
)
929 // asldebug("--> aslevent_fdsets\n");
934 for (e
= Eventq
.tqh_first
; e
!= NULL
; e
= e
->entries
.tqe_next
)
936 if (e
->fd
< 0) continue;
938 // asldebug("adding fd %d\n", e->fd);
942 status
= MAX(e
->fd
, status
);
948 status
= MAX(e
->fd
, status
);
954 status
= MAX(e
->fd
, status
);
958 // asldebug("<--aslevent_fdsets\n");
965 struct aslevent
*e
, *next
;
967 e
= Eventq
.tqh_first
;
971 next
= e
->entries
.tqe_next
;
974 TAILQ_REMOVE(&Eventq
, e
, entries
);
975 if (e
->sender
!= NULL
) free(e
->sender
);
984 list_append_msg(asl_search_result_t
*list
, aslmsg msg
)
986 if (list
== NULL
) return;
987 if (msg
== NULL
) return;
990 * NB: curr is the list size
991 * grow list if necessary
993 if (list
->count
== list
->curr
)
997 list
->msg
= (asl_msg_t
**)calloc(LIST_SIZE_DELTA
, sizeof(asl_msg_t
*));
1001 list
->msg
= (asl_msg_t
**)reallocf(list
->msg
, (list
->curr
+ LIST_SIZE_DELTA
) * sizeof(asl_msg_t
*));
1004 if (list
->msg
== NULL
)
1011 list
->curr
+= LIST_SIZE_DELTA
;
1014 list
->msg
[list
->count
] = (asl_msg_t
*)msg
;
1019 work_enqueue(aslmsg m
)
1021 pthread_mutex_lock(global
.work_queue_lock
);
1022 list_append_msg(global
.work_queue
, m
);
1023 pthread_mutex_unlock(global
.work_queue_lock
);
1024 pthread_cond_signal(&global
.work_queue_cond
);
1028 asl_enqueue_message(uint32_t source
, struct aslevent
*e
, aslmsg msg
)
1033 if (msg
== NULL
) return;
1036 status
= aslmsg_verify(source
, e
, msg
, &kplevel
);
1037 if (status
== VERIFY_STATUS_OK
)
1039 if ((source
== SOURCE_KERN
) && (kplevel
>= 0)) notify_post(kern_notify_key
[kplevel
]);
1049 work_dequeue(uint32_t *count
)
1053 pthread_mutex_lock(global
.work_queue_lock
);
1054 if (global
.work_queue
->count
== 0)
1056 pthread_cond_wait(&global
.work_queue_cond
, global
.work_queue_lock
);
1062 if (global
.work_queue
->count
== 0)
1064 pthread_mutex_unlock(global
.work_queue_lock
);
1068 work
= (aslmsg
*)(global
.work_queue
->msg
);
1069 *count
= global
.work_queue
->count
;
1071 global
.work_queue
->count
= 0;
1072 global
.work_queue
->curr
= 0;
1073 global
.work_queue
->msg
= NULL
;
1075 pthread_mutex_unlock(global
.work_queue_lock
);
1080 aslevent_handleevent(fd_set
*rd
, fd_set
*wr
, fd_set
*ex
)
1087 // asldebug("--> aslevent_handleevent\n");
1091 for (e
= Eventq
.tqh_first
; e
!= NULL
; e
= e
->entries
.tqe_next
)
1099 if (FD_ISSET(e
->fd
, rd
) && (e
->readfn
!= NULL
))
1101 // asldebug("handling read event on %d\n", e->fd);
1102 msg
= e
->readfn(e
->fd
);
1103 if (msg
== NULL
) continue;
1105 asl_enqueue_message(e
->source
, e
, msg
);
1108 if (FD_ISSET(e
->fd
, ex
) && e
->exceptfn
)
1110 asldebug("handling except event on %d\n", e
->fd
);
1111 out
= e
->exceptfn(e
->fd
);
1112 if (out
== NULL
) asldebug("error writing message\n\n");
1116 if (cleanup
!= 0) aslevent_cleanup();
1118 // asldebug("<-- aslevent_handleevent\n");
1122 asl_log_string(const char *str
)
1126 if (str
== NULL
) return 1;
1128 msg
= (aslmsg
)asl_msg_from_string(str
);
1129 if (msg
== NULL
) return 1;
1131 asl_enqueue_message(SOURCE_INTERNAL
, NULL
, msg
);
1137 asldebug(const char *str
, ...)
1143 OSSpinLockLock(&global
.lock
);
1144 if (global
.debug
== 0)
1146 OSSpinLockUnlock(&global
.lock
);
1151 if (global
.debug_file
!= NULL
) dfp
= fopen(global
.debug_file
, "a");
1154 OSSpinLockUnlock(&global
.lock
);
1159 status
= vfprintf(dfp
, str
, v
);
1162 if (global
.debug_file
!= NULL
) fclose(dfp
);
1163 OSSpinLockUnlock(&global
.lock
);
1174 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [%s -- MARK --] [%s 0] [%s 0] [Facility syslog]",
1176 ASL_KEY_LEVEL
, ASL_LEVEL_INFO
,
1177 ASL_KEY_PID
, getpid(),
1178 ASL_KEY_MSG
, ASL_KEY_UID
, ASL_KEY_GID
);
1180 asl_log_string(str
);
1181 if (str
!= NULL
) free(str
);
1185 asl_syslog_input_convert(const char *in
, int len
, char *rhost
, uint32_t source
)
1187 int pf
, pri
, index
, n
;
1188 char *p
, *colon
, *brace
, *space
, *tmp
, *tval
, *hval
, *sval
, *pval
, *mval
;
1195 if (in
== NULL
) return NULL
;
1196 if (len
<= 0) return NULL
;
1209 /* skip leading whitespace */
1210 while ((index
< len
) && ((*p
== ' ') || (*p
== '\t')))
1216 if (index
>= len
) return NULL
;
1218 /* parse "<NN>" priority (level and facility) */
1224 n
= sscanf(p
, "%d", &pf
);
1228 if (pf
> 0x7) fval
= asl_syslog_faciliy_num_to_name(pf
& LOG_FACMASK
);
1231 while ((index
< len
) && (*p
!= '>'))
1244 snprintf(prival
, sizeof(prival
), "%d", pri
);
1246 /* check if a timestamp is included */
1247 if (((len
- index
) > 15) && (p
[9] == ':') && (p
[12] == ':') && (p
[15] == ' '))
1250 if (tmp
== NULL
) return NULL
;
1255 tick
= asl_parse_time(tmp
);
1256 if (tick
== (time_t)-1)
1263 gmtime_r(&tick
, &time
);
1264 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
);
1271 /* stop here for kernel messages */
1272 if (source
== SOURCE_KERN
)
1274 msg
= asl_new(ASL_TYPE_MSG
);
1275 if (msg
== NULL
) return NULL
;
1277 asl_set(msg
, ASL_KEY_MSG
, p
);
1278 asl_set(msg
, ASL_KEY_LEVEL
, prival
);
1279 asl_set(msg
, ASL_KEY_PID
, "0");
1280 asl_set(msg
, ASL_KEY_HOST
, whatsmyhostname());
1285 /* if message is from a network socket, hostname follows */
1286 if (source
== SOURCE_UDP_SOCKET
)
1288 space
= strchr(p
, ' ');
1292 hval
= malloc(n
+ 1);
1293 if (hval
== NULL
) return NULL
;
1303 colon
= strchr(p
, ':');
1304 brace
= strchr(p
, '[');
1306 /* check for "sender:" or sender[pid]:" */
1309 if ((brace
!= NULL
) && (brace
< colon
))
1312 sval
= malloc(n
+ 1);
1313 if (sval
== NULL
) return NULL
;
1318 n
= colon
- (brace
+ 1) - 1;
1319 pval
= malloc(n
+ 1);
1320 if (pval
== NULL
) return NULL
;
1322 memcpy(pval
, (brace
+ 1), n
);
1328 sval
= malloc(n
+ 1);
1329 if (sval
== NULL
) return NULL
;
1349 mval
= malloc(n
+ 1);
1350 if (mval
== NULL
) return NULL
;
1356 if (fval
== NULL
) fval
= asl_syslog_faciliy_num_to_name(LOG_USER
);
1358 msg
= asl_new(ASL_TYPE_MSG
);
1359 if (msg
== NULL
) return NULL
;
1363 asl_set(msg
, ASL_KEY_TIME
, tval
);
1367 if (fval
!= NULL
) asl_set(msg
, "Facility", fval
);
1368 else asl_set(msg
, "Facility", "user");
1372 asl_set(msg
, ASL_KEY_SENDER
, sval
);
1378 asl_set(msg
, ASL_KEY_PID
, pval
);
1383 asl_set(msg
, ASL_KEY_PID
, "-1");
1388 asl_set(msg
, ASL_KEY_MSG
, mval
);
1392 asl_set(msg
, ASL_KEY_LEVEL
, prival
);
1393 asl_set(msg
, ASL_KEY_UID
, "-2");
1394 asl_set(msg
, ASL_KEY_GID
, "-2");
1396 if (rhost
== NULL
) asl_set(msg
, ASL_KEY_HOST
, whatsmyhostname());
1397 else if (hval
!= NULL
) asl_set(msg
, ASL_KEY_HOST
, hval
);
1398 else asl_set(msg
, ASL_KEY_HOST
, rhost
);
1400 if (hval
!= NULL
) free(hval
);
1406 asl_input_parse(const char *in
, int len
, char *rhost
, uint32_t source
)
1409 int status
, x
, legacy
;
1411 asldebug("asl_input_parse: %s\n", (in
== NULL
) ? "NULL" : in
);
1413 if (in
== NULL
) return NULL
;
1418 /* calculate length if not provided */
1419 if (len
== 0) len
= strlen(in
);
1422 * Determine if the input is "old" syslog format or new ASL format.
1423 * Old format lines should start with "<", but they can just be straight text.
1424 * ASL input starts with a length (10 bytes) followed by a space and a '['.
1426 if ((in
[0] != '<') && (len
> 11))
1428 status
= sscanf(in
, "%d ", &x
);
1429 if ((status
== 1) && (in
[10] == ' ') && (in
[11] == '[')) legacy
= 0;
1432 if (legacy
== 1) return asl_syslog_input_convert(in
, len
, rhost
, source
);
1434 msg
= (aslmsg
)asl_msg_from_string(in
+ 11);
1435 if (msg
== NULL
) return NULL
;
1437 if (rhost
!= NULL
) asl_set(msg
, ASL_KEY_HOST
, rhost
);
1443 get_line_from_file(FILE *f
)
1448 out
= fgetln(f
, &len
);
1449 if (out
== NULL
) return NULL
;
1450 if (len
== 0) return NULL
;
1452 s
= malloc(len
+ 1);
1453 if (s
== NULL
) return NULL
;
1455 memcpy(s
, out
, len
);
1462 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
)
1469 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",
1470 when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg);
1473 m
= asl_new(ASL_TYPE_MSG
);
1474 if (m
== NULL
) return;
1477 if (priority
< ASL_LEVEL_EMERG
) priority
= ASL_LEVEL_EMERG
;
1478 if (priority
> ASL_LEVEL_DEBUG
) priority
= ASL_LEVEL_DEBUG
;
1479 snprintf(str
, sizeof(str
), "%d", priority
);
1481 asl_set(m
, ASL_KEY_LEVEL
, str
);
1486 snprintf(str
, sizeof(str
), "%lu", when
->tv_sec
);
1487 asl_set(m
, ASL_KEY_TIME
, str
);
1489 snprintf(str
, sizeof(str
), "%lu", 1000 * (unsigned long int)when
->tv_usec
);
1490 asl_set(m
, ASL_KEY_TIME_NSEC
, str
);
1495 snprintf(str
, sizeof(str
), "%lu", now
);
1496 asl_set(m
, ASL_KEY_TIME
, str
);
1500 asl_set(m
, ASL_KEY_HOST
, whatsmyhostname());
1503 asl_set(m
, ASL_KEY_FACILITY
, FACILITY_CONSOLE
);
1506 snprintf(str
, sizeof(str
), "%u", (unsigned int)sender_uid
);
1507 asl_set(m
, ASL_KEY_UID
, str
);
1510 snprintf(str
, sizeof(str
), "%u", (unsigned int)sender_gid
);
1511 asl_set(m
, ASL_KEY_GID
, str
);
1516 snprintf(str
, sizeof(str
), "%u", (unsigned int)from_pid
);
1517 asl_set(m
, ASL_KEY_PID
, str
);
1521 if ((about_pid
> 0) && (about_pid
!= from_pid
))
1523 snprintf(str
, sizeof(str
), "%u", (unsigned int)about_pid
);
1524 asl_set(m
, ASL_KEY_REF_PID
, str
);
1528 if (from_name
!= NULL
)
1530 asl_set(m
, ASL_KEY_SENDER
, from_name
);
1534 if (sender_uid
!= 0)
1536 snprintf(str
, sizeof(str
), "%d", (int)sender_uid
);
1537 asl_set(m
, ASL_KEY_READ_UID
, str
);
1540 /* Reference Process */
1541 if (about_name
!= NULL
)
1543 if ((from_name
!= NULL
) && (strcmp(from_name
, about_name
) != 0))
1545 asl_set(m
, ASL_KEY_REF_PROC
, about_name
);
1550 if (session_name
!= NULL
)
1552 asl_set(m
, ASL_KEY_SESSION
, session_name
);
1558 asl_set(m
, ASL_KEY_MSG
, msg
);
1561 /* verify and push to receivers */
1562 asl_enqueue_message(SOURCE_LAUNCHD
, NULL
, m
);
1570 _vprocmgr_log_drain(NULL
, NULL
, launchd_callback
);