1 #include <dispatch/dispatch.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 char *launchd_username
= "unknown";
17 char *launchd_label
= "com.apple.launchd.unknown";
18 mach_port_t launchd_drain_reply_port
;
19 bool launchd_var_available
= false;
20 int64_t launchd_system_start
;
22 static FILE *_launchd_shutdown_log
;
23 static FILE *_launchd_debug_log
;
24 static FILE *_launchd_perf_log
;
25 static STAILQ_HEAD(, logmsg_s
) _launchd_logq
= STAILQ_HEAD_INITIALIZER(_launchd_logq
);
26 static size_t _launchd_logq_sz
;
27 static size_t _launchd_logq_cnt
;
28 static int _launchd_log_up2
= LOG_UPTO(LOG_NOTICE
);
30 static int64_t _launchd_shutdown_start
;
32 struct _launchd_open_log_ctx_s
{
38 _launchd_open_log_once(void *ctx
)
40 struct _launchd_open_log_ctx_s
*ctx2
= ctx
;
41 const char *path
= ctx2
->path
;
42 FILE **filep
= ctx2
->filep
;
45 snprintf(saved
, sizeof(saved
), "%s.1", path
);
46 (void)rename(path
, saved
);
48 FILE *file
= fopen(path
, "w");
50 (void)_fd(fileno(file
));
52 } else if (launchd_console
) {
53 fprintf(launchd_console
, "Could not open %s: %d: %s\n", path
, errno
, strerror(errno
));
58 _launchd_shutdown_start_once(void *ctx
__attribute__((unused
)))
60 _launchd_shutdown_start
= runtime_get_wall_time();
64 runtime_setlogmask(int maskpri
)
66 _launchd_log_up2
= maskpri
;
67 return _launchd_log_up2
;
71 _logmsg_add(struct launchd_syslog_attr
*attr
, int err_num
, const char *msg
)
73 size_t lm_sz
= sizeof(struct logmsg_s
) + strlen(msg
) + strlen(attr
->from_name
) + strlen(attr
->about_name
) + strlen(attr
->session_name
) + 4;
77 /* Force the unpacking for the log_drain cause unalignment faults. */
78 lm_sz
= ROUND_TO_64BIT_WORD_SIZE(lm_sz
);
80 if (unlikely((lm
= calloc(1, lm_sz
)) == NULL
)) {
86 lm
->when
= runtime_get_wall_time();
87 lm
->from_pid
= attr
->from_pid
;
88 lm
->about_pid
= attr
->about_pid
;
89 lm
->err_num
= err_num
;
90 lm
->pri
= attr
->priority
;
93 data_off
+= sprintf(data_off
, "%s", msg
) + 1;
94 lm
->from_name
= data_off
;
95 data_off
+= sprintf(data_off
, "%s", attr
->from_name
) + 1;
96 lm
->about_name
= data_off
;
97 data_off
+= sprintf(data_off
, "%s", attr
->about_name
) + 1;
98 lm
->session_name
= data_off
;
99 data_off
+= sprintf(data_off
, "%s", attr
->session_name
) + 1;
101 STAILQ_INSERT_TAIL(&_launchd_logq
, lm
, sqe
);
102 _launchd_logq_sz
+= lm_sz
;
109 _logmsg_remove(struct logmsg_s
*lm
)
111 STAILQ_REMOVE(&_launchd_logq
, lm
, logmsg_s
, sqe
);
112 _launchd_logq_sz
-= lm
->obj_sz
;
119 _launchd_osx_redirect(const char *message
)
121 launchd_syslog(LOG_ERR
, "%s", message
);
126 launchd_syslog(int pri
, const char *message
, ...)
128 struct launchd_syslog_attr attr
= {
129 .from_name
= launchd_label
,
130 .about_name
= launchd_label
,
131 .session_name
= pid1_magic
? "System" : "Background",
133 .from_uid
= launchd_uid
,
134 .from_pid
= getpid(),
135 .about_pid
= getpid(),
140 va_start(ap
, message
);
141 launchd_vsyslog(&attr
, message
, ap
);
146 launchd_vsyslog(struct launchd_syslog_attr
*attr
, const char *fmt
, va_list args
)
148 int saved_errno
= errno
;
151 static dispatch_once_t perf_once
= 0;
152 static dispatch_once_t shutdown_once
= 0;
153 static dispatch_once_t shutdown_start_once
= 0;
154 static dispatch_once_t debug_once
= 0;
156 bool echo2console
= (attr
->priority
& LOG_CONSOLE
);
157 attr
->priority
&= ~LOG_CONSOLE
;
158 if (attr
->priority
== LOG_APPLEONLY
&& launchd_apple_internal
) {
159 attr
->priority
= LOG_NOTICE
;
162 FILE *log2here
= NULL
;
164 /* launchctl is responsible for mounting /var as read-write. So we need to
165 * wait until /var/log is available before trying to log anything to our log
166 * anything to our auxilliary log files. This is kind of a hack because
167 * we'll lose the first few relevant messages at boot for debug and
168 * performance logging, but the loss isn't too bad.
170 if (launchd_var_available
) {
171 /* This file is for logging low-level errors where we can't necessarily be
172 * assured that we can write to the console or use syslog.
174 char *store
= launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_LOGS
, NULL
);
177 if (attr
->priority
== LOG_PERF
) {
178 if (launchd_log_perf
) {
179 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_PERF_LOG
, store
, launchd_username
);
181 struct _launchd_open_log_ctx_s ctx2
= {
183 .filep
= &_launchd_perf_log
,
185 dispatch_once_f(&perf_once
, &ctx2
, _launchd_open_log_once
);
186 log2here
= _launchd_perf_log
;
189 // Don't log performance messages to the normal syslog store.
190 attr
->priority
= LOG_DEBUG
+ 1;
192 if (launchd_shutting_down
&& launchd_log_shutdown
) {
193 dispatch_once_f(&shutdown_start_once
, NULL
, _launchd_shutdown_start_once
);
195 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_SHUTDOWN_LOG
, store
, launchd_username
);
197 struct _launchd_open_log_ctx_s ctx2
= {
199 .filep
= &_launchd_shutdown_log
,
201 dispatch_once_f(&shutdown_once
, &ctx2
, _launchd_open_log_once
);
202 log2here
= _launchd_shutdown_log
;
203 } else if (launchd_log_debug
) {
204 (void)snprintf(path
, sizeof(path
), "%s" LAUNCHD_DEBUG_LOG
, store
, launchd_username
);
206 struct _launchd_open_log_ctx_s ctx2
= {
208 .filep
= &_launchd_debug_log
,
210 dispatch_once_f(&debug_once
, &ctx2
, _launchd_open_log_once
);
211 log2here
= _launchd_debug_log
;
219 vsnprintf(message
, sizeof(message
), fmt
, args
);
220 if (echo2console
&& launchd_console
) {
221 fprintf(launchd_console
, "%-32s %-8u %-64s %-8u %s\n", attr
->from_name
, attr
->from_pid
, attr
->about_name
, attr
->about_pid
, message
);
226 if (log2here
== _launchd_shutdown_log
) {
227 delta
= runtime_get_wall_time() - _launchd_shutdown_start
;
229 delta
= runtime_get_wall_time() - launchd_system_start
;
232 fprintf(log2here
, "%-8lld %-32s %-8u %-24s %-8u %s\n", delta
, attr
->from_name
, attr
->from_pid
, attr
->about_name
, attr
->about_pid
, message
);
235 if ((LOG_MASK(attr
->priority
) & _launchd_log_up2
)) {
236 _logmsg_add(attr
, saved_errno
, message
);
241 _launchd_log_pack(vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
246 *outvalCnt
= _launchd_logq_sz
;
248 mig_allocate(outval
, *outvalCnt
);
250 if (unlikely(*outval
== 0)) {
254 offset
= (void *)*outval
;
255 while ((lm
= STAILQ_FIRST(&_launchd_logq
))) {
256 lm
->from_name_offset
= lm
->from_name
- (char *)lm
;
257 lm
->about_name_offset
= lm
->about_name
- (char *)lm
;
258 lm
->msg_offset
= lm
->msg
- (char *)lm
;
259 lm
->session_name_offset
= lm
->session_name
- (char *)lm
;
261 memcpy(offset
, lm
, lm
->obj_sz
);
263 offset
+= lm
->obj_sz
;
272 _launchd_log_uncork_pending_drain(void)
274 mach_msg_type_number_t outvalCnt
;
275 mach_port_t tmp_port
;
278 if (!launchd_drain_reply_port
) {
282 if (_launchd_logq_cnt
== 0) {
286 if (_launchd_log_pack(&outval
, &outvalCnt
) != 0) {
290 tmp_port
= launchd_drain_reply_port
;
291 launchd_drain_reply_port
= MACH_PORT_NULL
;
293 if (unlikely(errno
= job_mig_log_drain_reply(tmp_port
, 0, outval
, outvalCnt
))) {
294 if (errno
!= MACH_SEND_INVALID_DEST
) {
295 (void)osx_assumes_zero(errno
);
297 (void)osx_assumes_zero(launchd_mport_deallocate(tmp_port
));
300 mig_deallocate(outval
, outvalCnt
);
304 launchd_log_push(void)
306 vm_offset_t outval
= 0;
307 mach_msg_type_number_t outvalCnt
= 0;
310 if (_launchd_perf_log
) {
311 (void)fflush(_launchd_perf_log
);
313 if (_launchd_shutdown_log
) {
314 (void)fflush(_launchd_shutdown_log
);
316 if (_launchd_debug_log
) {
317 (void)fflush(_launchd_debug_log
);
320 if (_launchd_logq_cnt
&& _launchd_log_pack(&outval
, &outvalCnt
) == 0) {
321 (void)_vprocmgr_log_forward(inherited_bootstrap_port
, (void *)outval
, outvalCnt
);
322 mig_deallocate(outval
, outvalCnt
);
325 _launchd_log_uncork_pending_drain();
330 launchd_log_forward(uid_t forward_uid
, gid_t forward_gid
, vm_offset_t inval
, mach_msg_type_number_t invalCnt
)
332 struct logmsg_s
*lm
, *lm_walk
;
333 mach_msg_type_number_t data_left
= invalCnt
;
339 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
)) {
340 /* malloc(3) returns non-NULL when you ask for zero bytes. If our object
341 * is zero bytes, something is wrong.
343 if (lm_walk
->obj_sz
== 0) {
344 launchd_syslog(LOG_WARNING
, "Encountered a log message of size 0 with %u bytes left in forwarded data. Ignoring remaining messages.", data_left
);
348 if (!(lm
= malloc(lm_walk
->obj_sz
))) {
349 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
);
353 memcpy(lm
, lm_walk
, lm_walk
->obj_sz
);
354 lm
->sender_uid
= forward_uid
;
355 lm
->sender_gid
= forward_gid
;
357 lm
->from_name
+= (size_t)lm
;
358 lm
->about_name
+= (size_t)lm
;
359 lm
->msg
+= (size_t)lm
;
360 lm
->session_name
+= (size_t)lm
;
362 STAILQ_INSERT_TAIL(&_launchd_logq
, lm
, sqe
);
363 _launchd_logq_sz
+= lm
->obj_sz
;
366 data_left
-= lm
->obj_sz
;
369 mig_deallocate(inval
, invalCnt
);
375 launchd_log_drain(mach_port_t srp
, vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
377 (void)osx_assumes_zero(launchd_drain_reply_port
);
379 if ((_launchd_logq_cnt
== 0) || launchd_shutting_down
) {
380 launchd_drain_reply_port
= srp
;
381 (void)osx_assumes_zero(launchd_mport_notify_req(launchd_drain_reply_port
, MACH_NOTIFY_DEAD_NAME
));
386 return _launchd_log_pack(outval
, outvalCnt
);
390 launchd_closelog(void)
394 if (_launchd_shutdown_log
) {
395 (void)fflush(_launchd_shutdown_log
);
396 (void)runtime_fsync(fileno(_launchd_shutdown_log
));