1 #include <dispatch/dispatch.h>
2 #include <os/assumes.h>
6 #include "launch_internal.h"
7 #include "vproc_internal.h"
10 #define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7)
11 #define LAUNCHD_DEBUG_LOG "launchd-debug.%s.log"
12 #define LAUNCHD_PERF_LOG "launchd-perf.%s.log"
13 #define LAUNCHD_SHUTDOWN_LOG "launchd-shutdown.%s.log"
14 #define LAUNCHD_LOWLEVEL_LOG "launchd-lowlevel.%s.log"
16 os_redirect_assumes(_launchd_os_redirect
);
18 char *launchd_username
= "unknown";
19 char *launchd_label
= "com.apple.launchd.unknown";
20 mach_port_t launchd_drain_reply_port
;
21 bool launchd_var_available
= false;
22 int64_t launchd_system_start
;
24 static FILE *_launchd_shutdown_log
;
25 static FILE *_launchd_debug_log
;
26 static FILE *_launchd_perf_log
;
27 static STAILQ_HEAD(, logmsg_s
) _launchd_logq
= STAILQ_HEAD_INITIALIZER(_launchd_logq
);
28 static size_t _launchd_logq_sz
;
29 static size_t _launchd_logq_cnt
;
30 static int _launchd_log_up2
= LOG_UPTO(LOG_NOTICE
);
32 static int64_t _launchd_shutdown_start
;
34 struct _launchd_open_log_ctx_s
{
40 _launchd_open_log_once(void *ctx
)
42 struct _launchd_open_log_ctx_s
*ctx2
= ctx
;
43 const char *path
= ctx2
->path
;
44 FILE **filep
= ctx2
->filep
;
47 snprintf(saved
, sizeof(saved
), "%s.1", path
);
48 (void)rename(path
, saved
);
50 FILE *file
= fopen(path
, "w");
52 (void)_fd(fileno(file
));
54 } else if (launchd_console
) {
55 fprintf(launchd_console
, "Could not open %s: %d: %s\n", path
, errno
, strerror(errno
));
60 _launchd_shutdown_start_once(void *ctx
__attribute__((unused
)))
62 _launchd_shutdown_start
= runtime_get_wall_time();
66 runtime_setlogmask(int maskpri
)
68 _launchd_log_up2
= maskpri
;
69 return _launchd_log_up2
;
73 _logmsg_add(struct launchd_syslog_attr
*attr
, int err_num
, const char *msg
)
75 size_t lm_sz
= sizeof(struct logmsg_s
) + strlen(msg
) + strlen(attr
->from_name
) + strlen(attr
->about_name
) + strlen(attr
->session_name
) + 4;
79 /* Force the unpacking for the log_drain cause unalignment faults. */
80 lm_sz
= ROUND_TO_64BIT_WORD_SIZE(lm_sz
);
82 if (unlikely((lm
= calloc(1, lm_sz
)) == NULL
)) {
88 lm
->when
= runtime_get_wall_time();
89 lm
->from_pid
= attr
->from_pid
;
90 lm
->about_pid
= attr
->about_pid
;
91 lm
->err_num
= err_num
;
92 lm
->pri
= attr
->priority
;
95 data_off
+= sprintf(data_off
, "%s", msg
) + 1;
96 lm
->from_name
= data_off
;
97 data_off
+= sprintf(data_off
, "%s", attr
->from_name
) + 1;
98 lm
->about_name
= data_off
;
99 data_off
+= sprintf(data_off
, "%s", attr
->about_name
) + 1;
100 lm
->session_name
= data_off
;
101 data_off
+= sprintf(data_off
, "%s", attr
->session_name
) + 1;
103 STAILQ_INSERT_TAIL(&_launchd_logq
, lm
, sqe
);
104 _launchd_logq_sz
+= lm_sz
;
111 _logmsg_remove(struct logmsg_s
*lm
)
113 STAILQ_REMOVE(&_launchd_logq
, lm
, logmsg_s
, sqe
);
114 _launchd_logq_sz
-= lm
->obj_sz
;
121 _launchd_os_redirect(const char *message
)
123 launchd_syslog(LOG_ERR
, "%s", message
);
128 launchd_syslog(int pri
, const char *message
, ...)
130 struct launchd_syslog_attr attr
= {
131 .from_name
= launchd_label
,
132 .about_name
= launchd_label
,
133 .session_name
= pid1_magic
? "System" : "Background",
135 .from_uid
= launchd_uid
,
136 .from_pid
= getpid(),
137 .about_pid
= getpid(),
142 va_start(ap
, message
);
143 launchd_vsyslog(&attr
, message
, ap
);
148 launchd_vsyslog(struct launchd_syslog_attr
*attr
, const char *fmt
, va_list args
)
150 int saved_errno
= errno
;
153 static dispatch_once_t perf_once
= 0;
154 static dispatch_once_t shutdown_once
= 0;
155 static dispatch_once_t shutdown_start_once
= 0;
156 static dispatch_once_t debug_once
= 0;
158 bool echo2console
= (attr
->priority
& LOG_CONSOLE
);
159 attr
->priority
&= ~LOG_CONSOLE
;
160 if (attr
->priority
== LOG_APPLEONLY
&& launchd_apple_internal
) {
161 attr
->priority
= LOG_NOTICE
;
164 FILE *log2here
= NULL
;
166 /* launchctl is responsible for mounting /var as read-write. So we need to
167 * wait until /var/log is available before trying to log anything to our log
168 * anything to our auxilliary log files. This is kind of a hack because
169 * we'll lose the first few relevant messages at boot for debug and
170 * performance logging, but the loss isn't too bad.
172 if (launchd_var_available
) {
173 /* This file is for logging low-level errors where we can't necessarily be
174 * assured that we can write to the console or use syslog.
176 char *store
= launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_LOGS
, NULL
);
179 if (attr
->priority
== LOG_PERF
) {
180 if (launchd_log_perf
) {
181 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_PERF_LOG
, store
, launchd_username
);
183 struct _launchd_open_log_ctx_s ctx2
= {
185 .filep
= &_launchd_perf_log
,
187 dispatch_once_f(&perf_once
, &ctx2
, _launchd_open_log_once
);
188 log2here
= _launchd_perf_log
;
191 // Don't log performance messages to the normal syslog store.
192 attr
->priority
= LOG_DEBUG
+ 1;
194 if (launchd_shutting_down
&& launchd_log_shutdown
) {
195 dispatch_once_f(&shutdown_start_once
, NULL
, _launchd_shutdown_start_once
);
197 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_SHUTDOWN_LOG
, store
, launchd_username
);
199 struct _launchd_open_log_ctx_s ctx2
= {
201 .filep
= &_launchd_shutdown_log
,
203 dispatch_once_f(&shutdown_once
, &ctx2
, _launchd_open_log_once
);
204 log2here
= _launchd_shutdown_log
;
205 } else if (launchd_log_debug
) {
206 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_DEBUG_LOG
, store
, launchd_username
);
208 struct _launchd_open_log_ctx_s ctx2
= {
210 .filep
= &_launchd_debug_log
,
212 dispatch_once_f(&debug_once
, &ctx2
, _launchd_open_log_once
);
213 log2here
= _launchd_debug_log
;
221 vsnprintf(message
, sizeof(message
), fmt
, args
);
222 if (echo2console
&& launchd_console
) {
223 fprintf(launchd_console
, "%-32s %-8u %-64s %-8u %s\n", attr
->from_name
, attr
->from_pid
, attr
->about_name
, attr
->about_pid
, message
);
228 if (log2here
== _launchd_shutdown_log
) {
229 delta
= runtime_get_wall_time() - _launchd_shutdown_start
;
231 delta
= runtime_get_wall_time() - launchd_system_start
;
234 fprintf(log2here
, "%-8lld %-32s %-8u %-24s %-8u %s\n", delta
, attr
->from_name
, attr
->from_pid
, attr
->about_name
, attr
->about_pid
, message
);
237 if ((LOG_MASK(attr
->priority
) & _launchd_log_up2
)) {
238 _logmsg_add(attr
, saved_errno
, message
);
243 _launchd_log_pack(vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
248 *outvalCnt
= _launchd_logq_sz
;
250 mig_allocate(outval
, *outvalCnt
);
252 if (unlikely(*outval
== 0)) {
256 offset
= (void *)*outval
;
257 while ((lm
= STAILQ_FIRST(&_launchd_logq
))) {
258 lm
->from_name_offset
= lm
->from_name
- (char *)lm
;
259 lm
->about_name_offset
= lm
->about_name
- (char *)lm
;
260 lm
->msg_offset
= lm
->msg
- (char *)lm
;
261 lm
->session_name_offset
= lm
->session_name
- (char *)lm
;
263 memcpy(offset
, lm
, lm
->obj_sz
);
265 offset
+= lm
->obj_sz
;
274 _launchd_log_uncork_pending_drain(void)
276 mach_msg_type_number_t outvalCnt
;
277 mach_port_t tmp_port
;
280 if (!launchd_drain_reply_port
) {
284 if (_launchd_logq_cnt
== 0) {
288 if (_launchd_log_pack(&outval
, &outvalCnt
) != 0) {
292 tmp_port
= launchd_drain_reply_port
;
293 launchd_drain_reply_port
= MACH_PORT_NULL
;
295 if (unlikely(errno
= job_mig_log_drain_reply(tmp_port
, 0, outval
, outvalCnt
))) {
296 if (errno
!= MACH_SEND_INVALID_DEST
) {
297 (void)os_assumes_zero(errno
);
299 (void)os_assumes_zero(launchd_mport_deallocate(tmp_port
));
302 mig_deallocate(outval
, outvalCnt
);
306 launchd_log_push(void)
308 vm_offset_t outval
= 0;
309 mach_msg_type_number_t outvalCnt
= 0;
312 if (_launchd_perf_log
) {
313 (void)fflush(_launchd_perf_log
);
315 if (_launchd_shutdown_log
) {
316 (void)fflush(_launchd_shutdown_log
);
318 if (_launchd_debug_log
) {
319 (void)fflush(_launchd_debug_log
);
322 if (_launchd_logq_cnt
&& _launchd_log_pack(&outval
, &outvalCnt
) == 0) {
323 (void)_vprocmgr_log_forward(inherited_bootstrap_port
, (void *)outval
, outvalCnt
);
324 mig_deallocate(outval
, outvalCnt
);
327 _launchd_log_uncork_pending_drain();
332 launchd_log_forward(uid_t forward_uid
, gid_t forward_gid
, vm_offset_t inval
, mach_msg_type_number_t invalCnt
)
334 struct logmsg_s
*lm
, *lm_walk
;
335 mach_msg_type_number_t data_left
= invalCnt
;
341 for (lm_walk
= (struct logmsg_s
*)inval
; (data_left
> 0) && (lm_walk
->obj_sz
<= data_left
); lm_walk
= ((void *)lm_walk
+ lm_walk
->obj_sz
)) {
342 /* malloc(3) returns non-NULL when you ask for zero bytes. If our object
343 * is zero bytes, something is wrong.
345 if (lm_walk
->obj_sz
== 0) {
346 launchd_syslog(LOG_WARNING
, "Encountered a log message of size 0 with %u bytes left in forwarded data. Ignoring remaining messages.", data_left
);
350 if (lm_walk
->obj_sz
< sizeof(struct logmsg_s
)) {
351 launchd_syslog(LOG_WARNING
, "Received bytes %llu are less than expected bytes %lu.", lm_walk
->obj_sz
, sizeof(struct logmsg_s
));
355 if (!(lm
= malloc(lm_walk
->obj_sz
))) {
356 launchd_syslog(LOG_WARNING
, "Failed to allocate %llu bytes for log message with %u bytes left in forwarded data. Ignoring remaining messages.", lm_walk
->obj_sz
, data_left
);
360 memcpy(lm
, lm_walk
, lm_walk
->obj_sz
);
361 lm
->sender_uid
= forward_uid
;
362 lm
->sender_gid
= forward_gid
;
364 lm
->from_name
+= (size_t)lm
;
365 lm
->about_name
+= (size_t)lm
;
366 lm
->msg
+= (size_t)lm
;
367 lm
->session_name
+= (size_t)lm
;
369 STAILQ_INSERT_TAIL(&_launchd_logq
, lm
, sqe
);
370 _launchd_logq_sz
+= lm
->obj_sz
;
373 data_left
-= lm
->obj_sz
;
376 mig_deallocate(inval
, invalCnt
);
382 launchd_log_drain(mach_port_t srp
, vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
384 (void)os_assumes_zero(launchd_drain_reply_port
);
386 if ((_launchd_logq_cnt
== 0) || launchd_shutting_down
) {
387 launchd_drain_reply_port
= srp
;
388 (void)os_assumes_zero(launchd_mport_notify_req(launchd_drain_reply_port
, MACH_NOTIFY_DEAD_NAME
));
393 return _launchd_log_pack(outval
, outvalCnt
);
397 launchd_closelog(void)
401 if (_launchd_shutdown_log
) {
402 (void)fflush(_launchd_shutdown_log
);
403 (void)runtime_fsync(fileno(_launchd_shutdown_log
));