From daa9914d66e0e5d39a244bacc6b3377a06527f06 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sat, 13 Sep 2025 13:25:27 +0800 Subject: [PATCH 1/5] Add support for compiling for bridgeOS --- bootstrap.c | 6 +++--- dumpjpcategory.c | 4 ++-- limit.c | 4 ++-- load.c | 2 +- print.c | 16 ++++++++-------- procinfo.c | 4 ++-- userswitch.c | 2 +- version.c | 4 ++-- xpc_helper.c | 8 ++++---- xpc_private.h | 28 ++++++++++++++++++++++++---- 10 files changed, 49 insertions(+), 29 deletions(-) diff --git a/bootstrap.c b/bootstrap.c index 3d3339d..d62d499 100644 --- a/bootstrap.c +++ b/bootstrap.c @@ -61,7 +61,7 @@ bootstrap_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **appl if (argc > 2) { paths = launchctl_parse_load_unload(0, argc - 2, argv + 2); xpc_dictionary_set_value(dict, "paths", paths); - if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) { + if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, bridgeOS 7.0, *)) { if (xpc_dictionary_get_uint64(dict, "type") == 1 && xpc_user_sessions_enabled() != 0) { xpc_array_apply(paths, ^bool(size_t index, xpc_object_t val) { xpc_object_t plist = launchctl_xpc_from_plist(xpc_string_get_string_ptr(val)); @@ -128,7 +128,7 @@ bootout_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) if (argc > 2 && name == NULL) { paths = launchctl_parse_load_unload(0, argc - 2, argv + 2); xpc_dictionary_set_value(dict, "paths", paths); - if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) { + if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, bridgeOS 7.0, *)) { if (xpc_dictionary_get_uint64(dict, "type") == 1 && xpc_user_sessions_enabled() != 0) { xpc_array_apply(paths, ^bool(size_t index, xpc_object_t val) { xpc_object_t plist = launchctl_xpc_from_plist(xpc_string_get_string_ptr(val)); @@ -146,7 +146,7 @@ bootout_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) } } - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { xpc_dictionary_set_bool(dict, "no-einprogress", true); } diff --git a/dumpjpcategory.c b/dumpjpcategory.c index a3dc448..2a347d0 100644 --- a/dumpjpcategory.c +++ b/dumpjpcategory.c @@ -47,7 +47,7 @@ dumpjpcategory_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char * launchctl_setup_xpc_dict_for_service_name("system", dict, NULL); xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -59,7 +59,7 @@ dumpjpcategory_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char * fprintf(stderr, "Dump jetsamproperties category is not supported on this platform.\n"); } - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { if (retval == 0) launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); diff --git a/limit.c b/limit.c index d1aab7c..ef01e5f 100644 --- a/limit.c +++ b/limit.c @@ -86,7 +86,7 @@ limit_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) printlimits: xpc_dictionary_set_bool(dict, "print", true); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "file", STDOUT_FILENO); @@ -97,7 +97,7 @@ limit_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) fprintf(stderr, "Could not print resource limits: %d: %s\n", err, xpc_strerror(err)); } - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } diff --git a/load.c b/load.c index 8a420ed..9d4950b 100644 --- a/load.c +++ b/load.c @@ -98,7 +98,7 @@ load_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) xpc_dictionary_set_bool(dict, "enable", wflag); } else { xpc_dictionary_set_bool(dict, "disable", wflag); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { xpc_dictionary_set_bool(dict, "no-einprogress", true); } } diff --git a/print.c b/print.c index dd31e61..65fc3a2 100644 --- a/print.c +++ b/print.c @@ -52,7 +52,7 @@ print_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) if ((ret = launchctl_setup_xpc_dict_for_service_name(argv[1], dict, &name)) != 0) return ret; - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -64,7 +64,7 @@ print_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) ret = launchctl_send_xpc_to_launchd(XPC_ROUTINE_PRINT, dict, &reply); if (ret == 0) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } @@ -92,7 +92,7 @@ print_cache_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **ap xpc_dictionary_set_uint64(dict, "type", 1); xpc_dictionary_set_uint64(dict, "handle", 0); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -102,7 +102,7 @@ print_cache_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **ap ret = launchctl_send_xpc_to_launchd(XPC_ROUTINE_PRINT, dict, &reply); if (ret == 0) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } @@ -130,7 +130,7 @@ print_disabled_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char * xpc_dictionary_set_uint64(dict, "type", 1); xpc_dictionary_set_uint64(dict, "handle", 0); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -140,7 +140,7 @@ print_disabled_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char * ret = launchctl_send_xpc_to_launchd(XPC_ROUTINE_PRINT, dict, &reply); if (ret == 0) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } @@ -168,7 +168,7 @@ dumpstate_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **appl xpc_dictionary_set_uint64(dict, "type", 1); xpc_dictionary_set_uint64(dict, "handle", 0); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -177,7 +177,7 @@ dumpstate_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **appl ret = launchctl_send_xpc_to_launchd(XPC_ROUTINE_DUMPSTATE, dict, &reply); if (ret == 0) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } diff --git a/procinfo.c b/procinfo.c index 9fc526d..c41a33e 100644 --- a/procinfo.c +++ b/procinfo.c @@ -613,14 +613,14 @@ procinfo_launchd_info : { dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_int64(dict, "pid", pid); - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); } retval = launchctl_send_xpc_to_launchd(XPC_ROUTINE_PRINT_SERVICE, dict, &reply); if (retval == 0) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); vm_deallocate(mach_task_self(), addr, sz); } diff --git a/userswitch.c b/userswitch.c index 97e0c0d..de2295e 100644 --- a/userswitch.c +++ b/userswitch.c @@ -41,7 +41,7 @@ userswitch_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **app int ret = ENOTSUP; - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { long olduid = 0, newuid = 0; olduid = strtol(argv[1], NULL, 0); diff --git a/version.c b/version.c index 0adf61f..315ff3c 100644 --- a/version.c +++ b/version.c @@ -43,7 +43,7 @@ version_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) vm_address_t addr = 0; vm_size_t sz = 0x100000; - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { addr = launchctl_create_shmem(dict, sz); } else { xpc_dictionary_set_fd(dict, "fd", STDOUT_FILENO); @@ -62,7 +62,7 @@ version_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) fprintf(stderr, "Could not print variant: %d: %s\n", ret, xpc_strerror(ret)); } - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { launchctl_print_shmem(reply, addr, sz, stdout); if (addr != 0) diff --git a/xpc_helper.c b/xpc_helper.c index 8eadcbd..f053f84 100644 --- a/xpc_helper.c +++ b/xpc_helper.c @@ -61,7 +61,7 @@ launchctl_send_xpc_to_launchd(uint64_t routine, xpc_object_t msg, xpc_object_t * xpc_dictionary_set_uint64(msg, "routine", routine); int ret = 0; - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { ret = _xpc_pipe_interface_routine(bootstrap_pipe, 0, msg, reply, 0); } else { ret = xpc_pipe_routine(bootstrap_pipe, msg, reply); @@ -122,7 +122,7 @@ launchctl_xpc_object_print(xpc_object_t in, const char *name, int level) void launchctl_setup_xpc_dict(xpc_object_t dict) { - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)) { + if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { xpc_dictionary_set_uint64(dict, "type", 7); } else { xpc_dictionary_set_uint64(dict, "type", 1); @@ -170,7 +170,7 @@ launchctl_setup_xpc_dict_for_service_name(char *servicetarget, xpc_object_t dict if (name != NULL) { *name = split[1]; } - if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) { + if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, bridgeOS 7.0, *)) { if (xpc_user_sessions_enabled() && launchctl_test_xpc_send(1, handle, split[1]) == false) { uint64_t fguid = xpc_user_sessions_get_foreground_uid(0); @@ -189,7 +189,7 @@ launchctl_setup_xpc_dict_for_service_name(char *servicetarget, xpc_object_t dict } else if (strcmp(split[0], "user") == 0) { xpc_dictionary_set_uint64(dict, "type", 2); if (split[1] != NULL && strcmp(split[1], "foreground") == 0) { - if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) { + if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, bridgeOS 7.0, *)) { if (xpc_user_sessions_enabled() == 0) { fprintf(stderr, "user/foreground/ specifier is not supported on this platform\n"); diff --git a/xpc_private.h b/xpc_private.h index c67bbb9..d8156d3 100644 --- a/xpc_private.h +++ b/xpc_private.h @@ -32,6 +32,26 @@ #ifndef _LAUNCHCTL_XPC_PRIVATE_H_ #define _LAUNCHCTL_XPC_PRIVATE_H_ +// clang-format off +#if defined(__has_feature) && defined(__has_attribute) +#if __has_attribute(availability) + +#ifndef __API_AVAILABLE_PLATFORM_bridgeos +#define __API_AVAILABLE_PLATFORM_bridgeos(x) bridgeos,introduced=x +#endif + +#ifndef __API_DEPRECATED_PLATFORM_bridgeos +#define __API_DEPRECATED_PLATFORM_bridgeos(x,y) bridgeos,introduced=x,deprecated=y +#endif + +#ifndef __API_UNAVAILABLE_PLATFORM_bridgeos +#define __API_UNAVAILABLE_PLATFORM_bridgeos bridgeos,unavailable +#endif + +#endif +#endif +// clang-format on + enum { XPC_ROUTINE_KICKSTART_SERVICE = 702, XPC_ROUTINE_ATTACH_SERVICE = 703, @@ -65,12 +85,12 @@ XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL1 XPC_NONNULL2 XPC_NONNULL3 int xpc_pipe_r XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL1 XPC_NONNULL3 XPC_NONNULL4 int _xpc_pipe_interface_routine(xpc_pipe_t pipe, uint64_t routine, xpc_object_t message, xpc_object_t XPC_GIVES_REFERENCE *reply, uint64_t flags) - __API_AVAILABLE(ios(15.0)); + __API_AVAILABLE(ios(15.0), tvos(15.0), watchos(8.0), bridgeos(6.0)); -int launch_active_user_switch(long, long) __API_AVAILABLE(ios(15.0)); +int launch_active_user_switch(long, long) __API_AVAILABLE(ios(15.0), tvos(15.0), watchos(8.0), bridgeos(6.0)); -int64_t xpc_user_sessions_enabled(void) __API_AVAILABLE(ios(16.0)); -uint64_t xpc_user_sessions_get_foreground_uid(uint64_t) __API_AVAILABLE(ios(16.0)); +int64_t xpc_user_sessions_enabled(void) __API_AVAILABLE(ios(16.0), tvos(16.0), watchos(9.0), bridgeos(7.0)); +uint64_t xpc_user_sessions_get_foreground_uid(uint64_t) __API_AVAILABLE(ios(16.0), tvos(16.0), watchos(9.0), bridgeos(7.0)); XPC_EXPORT XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL1 xpc_object_t xpc_create_from_plist(const void *data, size_t length); From 75032555d6868e0068b7d190eb87d6672a8f8c7c Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sat, 13 Sep 2025 13:53:41 +0800 Subject: [PATCH 2/5] Fix issues due to launch_active_user_switch() not existing on bridgeOS 10 / tvOS 26 --- userswitch.c | 7 ++++++- xpc_private.h | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/userswitch.c b/userswitch.c index de2295e..f9f9dbd 100644 --- a/userswitch.c +++ b/userswitch.c @@ -41,13 +41,18 @@ userswitch_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **app int ret = ENOTSUP; - if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { + if (__builtin_available(macOS 16.0, iOS 19.0, tvOS 19.0, watchOS 12.0, bridgeOS 10.0, *)) { + /* The code is like this because __builtin_available cannot be combined with other operators */ + } else if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, bridgeOS 6.0, *)) { long olduid = 0, newuid = 0; olduid = strtol(argv[1], NULL, 0); newuid = strtol(argv[1], NULL, 0); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ret = launch_active_user_switch(olduid, newuid); +#pragma clang diagnostic pop } if (ret != 0) { diff --git a/xpc_private.h b/xpc_private.h index d8156d3..0add169 100644 --- a/xpc_private.h +++ b/xpc_private.h @@ -87,10 +87,12 @@ XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL1 XPC_NONNULL3 XPC_NONNULL4 int _xpc_pipe_ uint64_t routine, xpc_object_t message, xpc_object_t XPC_GIVES_REFERENCE *reply, uint64_t flags) __API_AVAILABLE(ios(15.0), tvos(15.0), watchos(8.0), bridgeos(6.0)); -int launch_active_user_switch(long, long) __API_AVAILABLE(ios(15.0), tvos(15.0), watchos(8.0), bridgeos(6.0)); +int __attribute__((weak_import, weak)) launch_active_user_switch(long, long) + API_DEPRECATED("No longer exist", ios(15.0, 19.0), tvos(15.0, 19.0), watchos(8.0, 12.0), bridgeos(6.0, 10.0)); int64_t xpc_user_sessions_enabled(void) __API_AVAILABLE(ios(16.0), tvos(16.0), watchos(9.0), bridgeos(7.0)); -uint64_t xpc_user_sessions_get_foreground_uid(uint64_t) __API_AVAILABLE(ios(16.0), tvos(16.0), watchos(9.0), bridgeos(7.0)); +uint64_t xpc_user_sessions_get_foreground_uid(uint64_t) + __API_AVAILABLE(ios(16.0), tvos(16.0), watchos(9.0), bridgeos(7.0)); XPC_EXPORT XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL1 xpc_object_t xpc_create_from_plist(const void *data, size_t length); From 3a0adaab394efd648a1668319d6ffbc5010b1477 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sat, 13 Sep 2025 14:30:50 +0800 Subject: [PATCH 3/5] Add enter-rem and enter-rem-dev --- Makefile | 2 +- launchctl.c | 2 ++ launchctl.h | 4 ++++ rem.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ xpc_private.h | 2 ++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 rem.c diff --git a/Makefile b/Makefile index fe4afa7..03b6652 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ DESTDIR ?= SRC := attach.c blame.c bootstrap.c enable.c env.c error.c examine.c kickstart.c SRC += kill.c launchctl.c limit.c list.c load.c manager.c plist.c print.c reboot.c SRC += remove.c runstats.c start_stop.c userswitch.c version.c xpc_helper.c -SRC += dumpjpcategory.c procinfo.c resolveport.c +SRC += dumpjpcategory.c procinfo.c resolveport.c rem.c ifeq ($(DEBUG),1) CFLAGS += -O0 -g -fsanitize=address,undefined -fno-omit-frame-pointer diff --git a/launchctl.c b/launchctl.c index 22dc42f..7fa7b64 100644 --- a/launchctl.c +++ b/launchctl.c @@ -81,6 +81,8 @@ static const struct { { "dumpstate", "Dumps launchd state to stdout.", NULL, dumpstate_cmd }, { "dumpjpcategory", "Dumps the jetsam properties category for all services.", NULL, dumpjpcategory_cmd }, { "reboot", "Initiates a system reboot of the specified type.", "[system|halt|obliterate|userspace] [-s]", reboot_cmd }, + { "enter-rem", "Enter into Restricted Execution Mode (REM).", NULL, enter_rem_cmd }, + { "enter-rem-dev", "Enter into Restricted Execution Mode (REM) in development mode.", NULL, enter_rem_dev_cmd }, { "userswitch", "Initiates a user switch.", " ", userswitch_cmd }, { "load", "Bootstraps a service or directory of services.", "", load_cmd }, { "unload", "Unloads a service or directory of services.", "", load_cmd }, diff --git a/launchctl.h b/launchctl.h index b2ef185..52c991c 100644 --- a/launchctl.h +++ b/launchctl.h @@ -123,6 +123,10 @@ cmd_main hostinfo_cmd; // resolveport.c cmd_main resolveport_cmd; +// rem.c +cmd_main enter_rem_cmd; +cmd_main enter_rem_dev_cmd; + void launchctl_xpc_object_print(xpc_object_t, const char *name, int level); int launchctl_send_xpc_to_launchd(uint64_t routine, xpc_object_t msg, xpc_object_t *reply); void launchctl_setup_xpc_dict(xpc_object_t dict); diff --git a/rem.c b/rem.c new file mode 100644 index 0000000..5faaf73 --- /dev/null +++ b/rem.c @@ -0,0 +1,63 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Procursus Team + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include "launchctl.h" +#include "xpc_private.h" + +int +enter_rem_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) +{ + int ret = ENOTSUP; + + if (__builtin_available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, bridgeOS 9.0, *)) + ret = launch_userspace_reboot_with_purpose(4); + + if (ret != 0) + fprintf(stderr, "Failed to enter REM: %d: %s\n", ret, xpc_strerror(ret)); + + return ret; +} + +int +enter_rem_dev_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) +{ + int ret = ENOTSUP; + + if (__builtin_available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, bridgeOS 9.0, *)) + ret = launch_userspace_reboot_with_purpose(5); + + if (ret != 0) + fprintf(stderr, "Failed to enter REM (development): %d: %s\n", ret, xpc_strerror(ret)); + + return ret; +} diff --git a/xpc_private.h b/xpc_private.h index 0add169..819417d 100644 --- a/xpc_private.h +++ b/xpc_private.h @@ -109,6 +109,8 @@ void xpc_dictionary_apply_f(xpc_object_t xdict, void *ctx, xpc_dictionary_applie typedef void (*xpc_array_applier_f)(size_t index, xpc_object_t value, void *context); void xpc_array_apply_f(xpc_object_t xarray, void *context, xpc_array_applier_f applier); +int launch_userspace_reboot_with_purpose(int) __API_AVAILABLE(ios(18.0), tvos(18.0), watchos(11.0), bridgeos(9.0)); + enum { ENODOMAIN = 112, ENOSERVICE = 113, From 34e76c8a2b17cbedadfa9b9096becd779b28f59f Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sat, 13 Sep 2025 15:40:02 +0800 Subject: [PATCH 4/5] procinfo: remove unused variable proc_argv --- procinfo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/procinfo.c b/procinfo.c index c41a33e..0018b15 100644 --- a/procinfo.c +++ b/procinfo.c @@ -443,7 +443,6 @@ procinfo_proc_info : { assert(p < &procargs[argmax]); // reached argv[0] - char *proc_argv = (char *)p; printf("argument vector = {\n"); for (int32_t i = 0; i < proc_argc && p < &procargs[argmax]; i++) { printf("\t[%d] = %s\n", i, p); From 43afaf8257ada76a135efac825451ed7778ee1c0 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 14 Sep 2025 12:29:38 +0800 Subject: [PATCH 5/5] Add dump_xsc --- launchctl.c | 1 + launchctl.h | 1 + print.c | 32 ++++++++++++++++++++++++++++++++ xpc_private.h | 3 ++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/launchctl.c b/launchctl.c index 7fa7b64..832f551 100644 --- a/launchctl.c +++ b/launchctl.c @@ -79,6 +79,7 @@ static const struct { { "examine", "Runs the specified analysis tool against launchd in a non-reentrant manner.", "[ [arg0, arg1, ... , @PID, ...]]", examine_cmd }, { "config", "Modifies persistent configuration parameters for launchd domains.", NULL, config_cmd }, { "dumpstate", "Dumps launchd state to stdout.", NULL, dumpstate_cmd }, + { "dump-xsc", "Dumps launchd XPC string caches to stdout.", "", dump_xsc_cmd }, { "dumpjpcategory", "Dumps the jetsam properties category for all services.", NULL, dumpjpcategory_cmd }, { "reboot", "Initiates a system reboot of the specified type.", "[system|halt|obliterate|userspace] [-s]", reboot_cmd }, { "enter-rem", "Enter into Restricted Execution Mode (REM).", NULL, enter_rem_cmd }, diff --git a/launchctl.h b/launchctl.h index 52c991c..539c5c2 100644 --- a/launchctl.h +++ b/launchctl.h @@ -60,6 +60,7 @@ cmd_main print_cmd; cmd_main print_cache_cmd; cmd_main print_disabled_cmd; cmd_main dumpstate_cmd; +cmd_main dump_xsc_cmd; // env.c cmd_main setenv_cmd; diff --git a/print.c b/print.c index 65fc3a2..d62bc58 100644 --- a/print.c +++ b/print.c @@ -193,3 +193,35 @@ dumpstate_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **appl return ret; } + +int +dump_xsc_cmd(xpc_object_t *msg, int argc, char **argv, char **envp, char **apple) +{ + int ret = ENOTSUP; + xpc_object_t reply; + vm_address_t addr; + vm_size_t sz = 0xa00000; + + if (__builtin_available(macOS 16.0, iOS 19.0, tvOS 19.0, watchOS 12.0, bridgeOS 10.0, *)) { + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + *msg = dict; + + if ((ret = launchctl_setup_xpc_dict_for_service_name("system", dict, NULL)) != 0) + return ret; + + if (argc > 1) + xpc_dictionary_set_string(dict, "name", argv[1]); + + addr = launchctl_create_shmem(dict, sz); + ret = launchctl_send_xpc_to_launchd(XPC_ROUTINE_DUMP_XSC, dict, &reply); + + if (ret) + fprintf(stderr, "State-dump failed with error %d\n", ret); + else + launchctl_print_shmem(reply, addr, sz, stdout); + + vm_deallocate(mach_task_self(), addr, sz); + } + + return ret; +} diff --git a/xpc_private.h b/xpc_private.h index 819417d..c86bb99 100644 --- a/xpc_private.h +++ b/xpc_private.h @@ -75,7 +75,8 @@ enum { XPC_ROUTINE_EXAMINE = 826, XPC_ROUTINE_PRINT = 828, XPC_ROUTINE_DUMPSTATE = 834, - XPC_ROUTINE_DUMPJPCATEGORY = 837 + XPC_ROUTINE_DUMPJPCATEGORY = 837, + XPC_ROUTINE_DUMP_XSC = 849, }; XPC_DECL(xpc_pipe);