diff options
Diffstat (limited to 'sbreload.m')
-rw-r--r-- | sbreload.m | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/sbreload.m b/sbreload.m new file mode 100644 index 0000000..e72eb76 --- /dev/null +++ b/sbreload.m @@ -0,0 +1,351 @@ +/* UIKit Tools - command-line utilities for UIKit + * Copyright (C) 2018-2019 Sam Bingner + * Copyright (C) 2008-2012 Jay Freeman (saurik) +*/ + +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +/* Modified BSD License {{{ */ +/* + * 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. + * 3. The name of the author may not be used to endorse + * or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <launch.h> +#include <notify.h> + +#include <stdio.h> +#include <unistd.h> + +#include <Foundation/Foundation.h> + +/* Set platform binary flag */ +#define FLAG_PLATFORMIZE (1 << 1) +#include <dlfcn.h> +#include <objc/runtime.h> + +@interface FBSSystemService ++(id)sharedService; +-(void)sendActions:(NSSet*)actions withResult:(id)result; +@end + +typedef enum { + None = 0, + RestartRenderServer = (1 << 0), // also relaunch backboardd + SnapshotTransition = (1 << 1), + FadeToBlackTransition = (1 << 2), +} SBSRelaunchActionStyle; + +@interface SBSRelaunchAction ++(id)actionWithReason:(id)reason options:(int64_t)options targetURL:(NSURL*)url; +@end + +void platformizeme() { + void* handle = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); + if (!handle) return; + + // Reset errors + dlerror(); + typedef void (*fix_entitle_prt_t)(pid_t pid, uint32_t what); + fix_entitle_prt_t ptr = (fix_entitle_prt_t)dlsym(handle, "jb_oneshot_entitle_now"); + + const char *dlsym_error = dlerror(); + if (dlsym_error) { + return; + } + + ptr(getpid(), FLAG_PLATFORMIZE); +} + +launch_data_t +CF2launch_data(CFTypeRef cfr); + +void +myCFDictionaryApplyFunction(const void *key, const void *value, void *context) +{ + launch_data_t ik, iw, where = context; + + ik = CF2launch_data(key); + iw = CF2launch_data(value); + + launch_data_dict_insert(where, iw, launch_data_get_string(ik)); + launch_data_free(ik); +} + +launch_data_t +CF2launch_data(CFTypeRef cfr) +{ + launch_data_t r; + CFTypeID cft = CFGetTypeID(cfr); + + if (cft == CFStringGetTypeID()) { + char buf[4096]; + CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); + r = launch_data_alloc(LAUNCH_DATA_STRING); + launch_data_set_string(r, buf); + } else if (cft == CFBooleanGetTypeID()) { + r = launch_data_alloc(LAUNCH_DATA_BOOL); + launch_data_set_bool(r, CFBooleanGetValue(cfr)); + } else if (cft == CFArrayGetTypeID()) { + CFIndex i, ac = CFArrayGetCount(cfr); + r = launch_data_alloc(LAUNCH_DATA_ARRAY); + for (i = 0; i < ac; i++) { + CFTypeRef v = CFArrayGetValueAtIndex(cfr, i); + if (v) { + launch_data_t iv = CF2launch_data(v); + launch_data_array_set_index(r, iv, i); + } + } + } else if (cft == CFDictionaryGetTypeID()) { + r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); + } else if (cft == CFDataGetTypeID()) { + r = launch_data_alloc(LAUNCH_DATA_ARRAY); + launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); + } else if (cft == CFNumberGetTypeID()) { + long long n; + double d; + CFNumberType cfnt = CFNumberGetType(cfr); + switch (cfnt) { + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + case kCFNumberCharType: + case kCFNumberShortType: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberLongLongType: + CFNumberGetValue(cfr, kCFNumberLongLongType, &n); + r = launch_data_alloc(LAUNCH_DATA_INTEGER); + launch_data_set_integer(r, n); + break; + case kCFNumberFloat32Type: + case kCFNumberFloat64Type: + case kCFNumberFloatType: + case kCFNumberDoubleType: + CFNumberGetValue(cfr, kCFNumberDoubleType, &d); + r = launch_data_alloc(LAUNCH_DATA_REAL); + launch_data_set_real(r, d); + break; + default: + r = NULL; + break; + } + } else { + r = NULL; + } + return r; +} + +CFPropertyListRef +CreateMyPropertyListFromFile(const char *posixfile) +{ + CFPropertyListRef propertyList; + CFStringRef errorString; + CFDataRef resourceData; + SInt32 errorCode; + CFURLRef fileURL; + + fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); + if (!fileURL) { + fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile); + } + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) { + fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode); + } + propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString); + if (!propertyList) { + fprintf(stderr, "%s: propertyList is NULL\n", getprogname()); + } + + return propertyList; +} + +#define _assert(test, format, args...) do { \ + if (test) break; \ + fprintf(stderr, format "\n", ##args); \ + return 1; \ +} while (false) + +void stop() { + sleep(1); +} + +#define SpringBoard_plist "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" + +pid_t launch_get_job_pid(const char * job) +{ + launch_data_t resp; + launch_data_t msg; + + msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + if (msg == NULL) { + return -1; + } + + launch_data_dict_insert(msg, launch_data_new_string(job), LAUNCH_KEY_GETJOB); + + resp = launch_msg(msg); + launch_data_free(msg); + + if (resp == NULL) { + return -1; + } + + if (launch_data_get_type(resp) != LAUNCH_DATA_DICTIONARY) return -1; + + launch_data_t pid_data = launch_data_dict_lookup(resp, "PID"); + if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) return -1; + + pid_t pid = (pid_t)launch_data_get_integer(pid_data); + launch_data_free(resp); + return pid; +} + +int main(int argc, const char *argv[]) { + platformizeme(); + _assert(argc == 1, "usage: sbreload"); + + if (kCFCoreFoundationVersionNumber >= 1443.00) { + dlopen("/System/Library/PrivateFrameworks/FrontBoardServices.framework/FrontBoardServices", RTLD_LAZY); + dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY); + Class $SBSRelaunchAction = objc_getClass("SBSRelaunchAction"); + Class $FBSSystemService = objc_getClass("FBSSystemService"); + pid_t sb_pid = launch_get_job_pid("com.apple.SpringBoard"); + if ($SBSRelaunchAction && $FBSSystemService) { + @autoreleasepool { + id action = [$SBSRelaunchAction actionWithReason:@"respring" options:RestartRenderServer targetURL:nil]; + id sharedService = [$FBSSystemService sharedService]; + [sharedService sendActions:[NSSet setWithObject:action] withResult:nil]; + for (int i=0; i<100; i++) { + if (kill(sb_pid, 0)) { + return 0; + } + usleep(1000); + } + } + } + } + CFDictionaryRef plist = CreateMyPropertyListFromFile(SpringBoard_plist); + _assert(plist != NULL, "CreateMyPropertyListFromFile() == NULL"); + + launch_data_t job = CF2launch_data(plist); + _assert(job != NULL, "CF2launch_data() == NULL"); + + launch_data_t data, request, response; + + data = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); + _assert(data != NULL, "launch_data_dict_lookup(LABEL) == NULL"); + const char *label = launch_data_get_string(data); + + request = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_dict_insert(request, launch_data_new_string(label), LAUNCH_KEY_GETJOB); + + response = launch_msg(request); + _assert(response != NULL, "launch_msg(GetJob) == NULL"); + launch_data_free(request); + + pid_t pid; + + if (launch_data_get_type(response) == LAUNCH_DATA_ERRNO) { + int error = launch_data_get_errno(response); + _assert(error == ESRCH, "GetJob(%s): %s", label, strerror(error)); + pid = -1; + } else if (launch_data_get_type(response) == LAUNCH_DATA_DICTIONARY) { + data = launch_data_dict_lookup(response, LAUNCH_JOBKEY_PID); + _assert(data != NULL, "launch_data_dict_lookup(PID) == NULL"); + pid = launch_data_get_integer(data); + } else _assert(false, "launch_data_get_type() not in (DICTIONARY, ERRNO)"); + + launch_data_free(response); + + // 600 is being used to approximate 4.x/5.x boundary + if (kCFCoreFoundationVersionNumber < 600) { + fprintf(stderr, "notify_post(com.apple.mobile.springboard_teardown)\n"); + notify_post("com.apple.mobile.springboard_teardown"); + } else { + // XXX: this code is preferable to launchctl unoad but it requires libvproc? :( + //vproc_err_t *error = _vproc_send_signal_by_label(label, VPROC_MAGIC_UNLOAD_SIGNAL); + //_assert(error == NULL, "_vproc_send_signal_by_label(UNLOAD) != NULL"); + + fprintf(stderr, "launchctl unload SpringBoard.plist\n"); + system("launchctl unload " SpringBoard_plist); + } + + if (pid != -1) { + fprintf(stderr, "waiting for kill(%u) != 0...\n", pid); + while (kill(pid, 0) == 0) + stop(); + + int error = errno; + _assert(error == ESRCH, "kill(%u): %s", pid, strerror(error)); + } + + request = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_dict_insert(request, job, LAUNCH_KEY_SUBMITJOB); + + for (;;) { + response = launch_msg(request); + _assert(response != NULL, "launch_msg(SubmitJob) == NULL"); + + _assert(launch_data_get_type(response) == LAUNCH_DATA_ERRNO, "launch_data_get_type() != ERRNO"); + int error = launch_data_get_errno(response); + launch_data_free(response); + + const char *string = strerror(error); + + if (error == EEXIST) { + fprintf(stderr, "SubmitJob(%s): %s, retrying...\n", label, string); + stop(); + } else { + _assert(error == 0, "SubmitJob(%s): %s", label, string); + break; + } + } + + launch_data_free(request); + + return 0; +} |