From 907d20416333ce7a16953cafc2b938a1758f2bee Mon Sep 17 00:00:00 2001 From: Sam Bingner Date: Wed, 23 Oct 2019 23:26:45 -1000 Subject: Update sbreload to avoid strange apple(?) bug --- data/uikittools/1_uicache.diff | 1170 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1170 insertions(+) create mode 100644 data/uikittools/1_uicache.diff (limited to 'data/uikittools/1_uicache.diff') diff --git a/data/uikittools/1_uicache.diff b/data/uikittools/1_uicache.diff new file mode 100644 index 000000000..8a01dd2e3 --- /dev/null +++ b/data/uikittools/1_uicache.diff @@ -0,0 +1,1170 @@ +diff -urN uikittools/sbreload.c uikittools+uicache/sbreload.c +--- uikittools/sbreload.c 2018-10-04 15:58:42.000000000 -1000 ++++ uikittools+uicache/sbreload.c 1969-12-31 14:00:00.000000000 -1000 +@@ -1,270 +0,0 @@ +-/* UIKit Tools - command-line utilities for UIKit +- * Copyright (C) 2008-2012 Jay Freeman (saurik) +-*/ +- +-/* 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 +-#include +- +-#include +-#include +- +-#include +- +-/* Set platform binary flag */ +-#define FLAG_PLATFORMIZE (1 << 1) +-#include +- +-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" +- +-int main(int argc, const char *argv[]) { +- platformizeme(); +- _assert(argc == 1, "usage: sbreload"); +- +- 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; +-} +diff -urN uikittools/sbreload.m uikittools+uicache/sbreload.m +--- uikittools/sbreload.m 1969-12-31 14:00:00.000000000 -1000 ++++ uikittools+uicache/sbreload.m 2019-06-07 22:37:29.000000000 -1000 +@@ -0,0 +1,336 @@ ++/* UIKit Tools - command-line utilities for UIKit ++ * Copyright (C) 2008-2012 Jay Freeman (saurik) ++*/ ++ ++/* 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 ++#include ++ ++#include ++#include ++ ++#include ++ ++/* Set platform binary flag */ ++#define FLAG_PLATFORMIZE (1 << 1) ++#include ++#include ++ ++@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; ++} +diff -urN uikittools/sbreload.xml uikittools+uicache/sbreload.xml +--- uikittools/sbreload.xml 1969-12-31 14:00:00.000000000 -1000 ++++ uikittools+uicache/sbreload.xml 2019-06-07 22:37:29.000000000 -1000 +@@ -0,0 +1,13 @@ ++ ++ ++ ++ com.apple.frontboard.launchapplications ++ ++ com.apple.frontboard.shutdown ++ ++ platform-application ++ ++ com.apple.private.skip-library-validation ++ ++ ++ +diff -urN uikittools/uicache.mm uikittools+uicache/uicache.mm +--- uikittools/uicache.mm 2018-10-04 15:58:19.000000000 -1000 ++++ uikittools+uicache/uicache.mm 2019-06-07 22:37:39.000000000 -1000 +@@ -1,7 +1,17 @@ + /* UIKit Tools - command-line utilities for UIKit + * Copyright (C) 2008-2012 Jay Freeman (saurik) ++ * Portions Copyright (C) 2019 Sam Bingner + */ + ++/* uicache.mm ++ * ++ * Licensed under GPL v2.0 license as available at: ++ * https://www.gnu.org/licenses/gpl2.txt ++ * ++ * In adition, usage must meet the following ++ * Modified BSD terms: ++ */ ++ + /* Modified BSD License {{{ */ + /* + * Redistribution and use in source and binary +@@ -38,13 +48,16 @@ + /* }}} */ + + #import +- +-#include +-#include +-#include +-#include +- +-#include ++#import ++#import ++#import ++#import ++#import ++#import ++#import ++#import ++#import ++#import + + #include "csstore.hpp" + +@@ -81,6 +94,12 @@ + + @end + ++@interface LSApplicationProxy : NSObject ++- (NSString*) applicationIdentifier; ++- (NSURL*) resourcesDirectoryURL; ++- (NSDate*) registeredDate; ++@end ++ + @interface LSApplicationWorkspace : NSObject + + (id) defaultWorkspace; + - (BOOL) registerApplication:(id)application; +@@ -89,16 +108,188 @@ + - (BOOL) registerApplicationDictionary:(id)application; + - (BOOL) installApplication:(id)application withOptions:(id)options; + - (BOOL) _LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)system internal:(BOOL)internal user:(BOOL)user; ++- (NSArray*) allApplications; ++@end ++ ++@interface MCMAppDataContainer +++(id)containerWithIdentifier:(NSString*)identifier createIfNecessary:(bool)create existed:(bool*)existed error:(NSError*)error; ++-(NSURL*)url; ++@end ++ ++@interface FBSSystemService +++(id)sharedService; ++-(void)sendActions:(NSSet*)actions withResult:(id)result; + @end + +-int main(int argc, const char *argv[]) { +- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ++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 ++ ++static int verbose=0; ++static int standard_uicache(void); ++static Class $MCMPluginKitPluginDataContainer; ++static Class $MCMAppDataContainer; ++static Class $LSApplicationWorkspace; ++LSApplicationWorkspace *workspace=nil; ++ ++NSString *getAppPath(NSString *path) ++{ ++ path = [path stringByResolvingSymlinksInPath]; ++ if (![path hasPrefix:@"/Applications/"]) { ++ fprintf(stderr, "Error: Path must be within /Applications/\n"); ++ return nil; ++ } ++ return [NSString pathWithComponents:[[path pathComponents] subarrayWithRange:NSMakeRange(0, 3)]]; ++} + +- Class $LSApplicationWorkspace(objc_getClass("LSApplicationWorkspace")); +- LSApplicationWorkspace *workspace($LSApplicationWorkspace == nil ? nil : [$LSApplicationWorkspace defaultWorkspace]); ++bool appIsRegistered(NSString *path) ++{ ++ @autoreleasepool { ++ path = getAppPath(path); ++ if (!path) return false; ++ for (LSApplicationProxy *app in [workspace allApplications]) { ++ if ([path isEqualToString:[[app resourcesDirectoryURL] path]]) return true; ++ } ++ return false; ++ } ++} + +- if (kCFCoreFoundationVersionNumber > 1000) // this API is on iOS 7 but invaliding the icon cache is harder there +- if ([workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) { ++bool unregisterPath(NSString *path) ++{ ++ @autoreleasepool { ++ if (verbose) fprintf(stderr, "Unregistering %s\n", path.lastPathComponent.UTF8String); ++ path = getAppPath(path); ++ if (!path) return false; ++ if (appIsRegistered(path) && ![workspace unregisterApplication:[NSURL fileURLWithPath:path]]) { ++ fprintf(stderr, "Error: unregisterApplication failed for %s\n", path.lastPathComponent.UTF8String); ++ return false; ++ } ++ } ++ return true; ++} ++ ++// Credit to coolstar for finding how to do this and not sharing with the community thereby forcing me to figure out how to do the same thing. ++bool registerPath(NSString *path) ++{ ++ if (!path) { ++ if (verbose) fprintf(stderr, "registerPath called with no path\n"); ++ return false; ++ } ++ NSString *realPath = getAppPath(path); ++ if (!realPath) { ++ if (verbose) fprintf(stderr, "unable to determine path for %s\n", path.UTF8String); ++ return false; ++ } ++ NSDictionary *infoDictionary = [NSDictionary dictionaryWithContentsOfFile: ++ [realPath stringByAppendingPathComponent:@"Info.plist"]]; ++ NSString *bundleID = [infoDictionary objectForKey:@"CFBundleIdentifier"]; ++ ++ if (bundleID) { ++ NSFileManager *fm = [NSFileManager defaultManager]; ++ ++ if ([infoDictionary objectForKey:@"CFBundleExecutable"]) { ++ NSString *executable = [realPath stringByAppendingPathComponent:[infoDictionary objectForKey:@"CFBundleExecutable"]]; ++ if (![fm fileExistsAtPath:executable]) { ++ fprintf(stderr, "Error: CFBundleExecutable defined but missing for %s - this is a fatal error. Aborting.\n", realPath.lastPathComponent.UTF8String); ++ return false; ++ } ++ } ++ ++ NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: ++ @"System", @"ApplicationType", ++ @YES, @"BundleNameIsLocalized", ++ bundleID, @"CFBundleIdentifier", ++ @NO, @"CompatibilityState", ++ @NO, @"IsDeletable", ++ realPath, @"Path", ++ [NSMutableDictionary dictionary], @"_LSBundlePlugins", ++ nil]; ++ ++ id appContainer = [$MCMAppDataContainer containerWithIdentifier:bundleID ++ createIfNecessary:YES existed:NULL error:nil]; ++ ++ NSString *appContainerPath = [[appContainer url] path]; ++ if (appContainerPath) { ++ dict[@"Container"] = appContainerPath; ++ } ++ NSString *pluginsPath = [realPath stringByAppendingPathComponent:@"PlugIns"]; ++ for (NSString *plugin in [fm contentsOfDirectoryAtPath:pluginsPath error:nil]) { ++ NSString *pluginPath = [pluginsPath stringByAppendingPathComponent:plugin]; ++ NSString *pluginInfoPlistPath = [pluginPath stringByAppendingPathComponent:@"Info.plist"]; ++ NSString *pluginBundleIdentifier = [[NSDictionary dictionaryWithContentsOfFile:pluginInfoPlistPath] objectForKey:@"CFBundleIdentifier"]; ++ if (pluginBundleIdentifier) { ++ id pluginContainer = [$MCMPluginKitPluginDataContainer containerWithIdentifier:pluginBundleIdentifier ++ createIfNecessary:YES existed:NULL error:nil]; ++ NSURL *pluginContainerURL = [pluginContainer url]; ++ NSString *pluginContainerPath = [pluginContainerURL path]; ++ dict[@"_LSBundlePlugins"][pluginBundleIdentifier] = @{ ++ @"ApplicationType": @"PluginKitPlugin", ++ @"BundleNameIsLocalized": @YES, ++ @"CFBundleIdentifier": pluginBundleIdentifier, ++ @"CompatibilityState": @NO, ++ @"Container": pluginContainerPath, ++ @"Path": pluginPath, ++ @"PluginOwnerBundleID": bundleID ++ }; ++ } ++ } ++ if (![[$LSApplicationWorkspace defaultWorkspace] registerApplicationDictionary:dict]) { ++ fprintf(stderr, "Error: registerApplicationDictionary failed for %s\n", path.lastPathComponent.UTF8String); ++ return false; ++ } ++ } else { ++ return unregisterPath(realPath); ++ } ++ return true; ++} ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: %s [-hrv] [[-p | -u] /Applications/App.app]]\n", getprogname()); ++ exit(EXIT_FAILURE); ++} ++ ++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 standard_uicache(void) ++{ ++ @autoreleasepool { ++ if (kCFCoreFoundationVersionNumber > 1000 && // this API is on iOS 7 but invaliding the icon cache is harder there ++ [workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) { + if (![workspace _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:NO]) + fprintf(stderr, "failed to rebuild application databases"); + return 0; +@@ -226,12 +417,247 @@ + system("killall -SIGCONT SpringBoard"); + } + +- if (respring) +- system("launchctl stop com.apple.SpringBoard"); +- else +- notify_post("com.apple.mobile.application_installed"); +- +- [pool release]; ++ notify_post("com.apple.mobile.application_installed"); + + return 0; ++ } ++} ++ ++pid_t pidOfCydia(void) { ++ launch_data_t request = launch_data_new_string(LAUNCH_KEY_GETJOBS); ++ launch_data_t response = launch_msg(request); ++ launch_data_free(request); ++ __block pid_t pid=-1; ++ ++ if (response == NULL || launch_data_get_type(response) != LAUNCH_DATA_DICTIONARY) return -1; ++ ++ xpc_dictionary_apply((xpc_object_t)response, ^bool(const char *key, xpc_object_t value) { ++ if (xpc_get_type(value) == XPC_TYPE_DICTIONARY) { ++ const char *program = xpc_dictionary_get_string(value, "Program"); ++ if (program && strcmp(program, "/Applications/Cydia.app/Cydia") == 0) { ++ pid = (pid_t)xpc_dictionary_get_int64(value, "PID"); ++ if (verbose) fprintf(stderr, "Found Cydia running with PID: %d\n", pid); ++ return false; ++ } ++ } ++ return true; ++ }); ++ ++ if (pid>0) { ++ return pid; ++ } ++ return -1; ++} ++ ++int optimized_uicache(void) { ++ __block int rv=0; ++ NSMutableDictionary *registered = [NSMutableDictionary new]; ++ static void (^readHandler)(NSFileHandle*) = ^(NSFileHandle *fh) { ++ NSData *output = [fh readDataToEndOfFile]; ++ if (output.length==0) return; ++ const char *found_path = (const char *)[output bytes]; ++ NSArray *found = [@(found_path) pathComponents]; ++ if (found.count >= 3) { ++ NSString *appPath = [@"/Applications" stringByAppendingPathComponent:found[2]]; ++ @synchronized (registered) { ++ if (registered[appPath]) return; ++ if (verbose) fprintf(stderr, "Updating %s\n", appPath.lastPathComponent.UTF8String); ++ registered[appPath] = @YES; ++ } ++ pid_t cydia_pid; ++ if ([found[2] isEqualToString:@"Cydia.app"] && ++ (cydia_pid = pidOfCydia()) > 0) { ++ // We are in cydia and trying to refresh it - this will kill it. Let's schedule it for later. ++ if (verbose) fprintf(stderr, "Waiting to refresh Cydia...\n"); ++ pid_t pid = fork(); ++ if (pid == 0) { ++ setpgrp(); ++ signal(SIGHUP, SIG_IGN); ++ signal(SIGPIPE, SIG_IGN); ++ fclose(stdin); ++ freopen("/dev/null", "a", stderr); ++ freopen("/dev/null", "a", stdout); ++ pid = fork(); ++ if (pid == 0) { ++ while (kill(cydia_pid, 0)==0) { ++ sleep(1); ++ } ++ const char *uicache = (*_NSGetArgv())[0]; ++ execl(uicache, uicache, "-vvvvvvv", NULL); ++ fprintf(stderr, "Unable to exec\n"); ++ fflush(stderr); ++ exit(-1); ++ } ++ exit(0); ++ } else if (pid > 0) { ++ int stat; ++ waitpid(pid, &stat, 0); ++ return; ++ } else { ++ fprintf(stderr, "Unable to fork\n"); ++ } ++ } ++ if (!registerPath(appPath)) rv++; ++ } ++ }; ++ NSFileManager *fm = [NSFileManager defaultManager]; ++ NSMutableDictionary *apps = [NSMutableDictionary new]; ++ NSMutableArray *cleanup = [NSMutableArray new]; ++ NSMutableArray *finds = [NSMutableArray new]; ++ if (verbose>1) fprintf(stderr, "Enumerating apps\n"); ++ for (LSApplicationProxy *app in [workspace allApplications]) { ++ NSString *path = [[app resourcesDirectoryURL] path]; ++ if (![path hasPrefix:@"/Applications/"]) continue; ++ if (verbose>1) fprintf(stderr, "Checking %s\n", path.lastPathComponent.UTF8String); ++ ++ NSDate *lastRegistered = [app registeredDate]; ++ ++ if ([fm fileExistsAtPath:path]) { ++ // Check for updated components ++ NSTask *find = [NSTask new]; ++ [find setLaunchPath:@"/usr/bin/find"]; ++ [find setStandardOutput:[NSPipe pipe]]; ++ NSString *stampPath = [NSString stringWithFormat:@"/var/tmp/uicache.stamp.%@", app.applicationIdentifier]; ++ [fm createFileAtPath:stampPath contents:nil attributes:@{NSFileModificationDate: lastRegistered}]; ++ [cleanup addObject:stampPath]; ++ [find setArguments:@[ path, @"-newer", stampPath, @"-print0", @"-quit"]]; ++ [finds addObject:find]; ++ [find launch]; ++ apps[path.lastPathComponent] = app; ++ } else { ++ if (verbose) fprintf(stderr, "De-registering removed app: %s\n", path.lastPathComponent.UTF8String); ++ @synchronized (registered) { ++ if (registered[path]) continue; ++ registered[path] = @YES; ++ } ++ if (!unregisterPath(path)) rv++; ++ } ++ } ++ ++ for (NSString* existing in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/Applications" error:nil]) { ++ NSString *path = [@"/Applications" stringByAppendingPathComponent:existing]; ++ if (apps[existing] || registered[path] || ![existing hasSuffix:@".app"]) continue; ++ if (verbose) fprintf(stderr, "Registering new app: %s\n", existing.UTF8String); ++ @synchronized (registered) { ++ registered[path] = @YES; ++ } ++ if (!registerPath(path)) rv++; ++ } ++ for (NSTask *find in finds) { ++ if (verbose>2) fprintf(stderr, "waiting for find %s\n", [find.arguments componentsJoinedByString:@" "].UTF8String); ++ readHandler([find.standardOutput fileHandleForReading]); ++ [find waitUntilExit]; ++ } ++ for (NSString *path in cleanup) { ++ [fm removeItemAtPath:path error:nil]; ++ } ++ return rv; ++} ++ ++int main(int argc, const char *argv[]) ++{ ++ if (getuid() == 0) { ++ // Be mobile ++ if (setuid(501)) { ++ fprintf(stderr, "Error: unable to become mobile"); ++ return -1; ++ } ++ } ++ dlopen("/System/Library/PrivateFrameworks/MobileContainerManager.framework/MobileContainerManager", RTLD_LAZY); ++ 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"); ++ $MCMPluginKitPluginDataContainer = objc_getClass("MCMPluginKitPluginDataContainer"); ++ $MCMAppDataContainer = objc_getClass("MCMAppDataContainer"); ++ $LSApplicationWorkspace = objc_getClass("LSApplicationWorkspace"); ++ workspace = [$LSApplicationWorkspace defaultWorkspace]; ++ ++ static int rv=0; ++ @autoreleasepool { ++ bool respring=false, do_all=false; ++ void (*jb_oneshot_entitle_now)(pid_t a, uint64_t b); ++ void *libjb = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); ++ ++ if (libjb) { ++ dlerror(); ++ jb_oneshot_entitle_now = (void (*)(pid_t, uint64_t))dlsym(libjb, "jb_oneshot_entitle_now"); ++ if (!dlerror()) jb_oneshot_entitle_now(getpid(), 2); ++ } ++ ++ NSMutableDictionary *paths = [NSMutableDictionary new]; ++ NSMutableDictionary *unregister_paths = [NSMutableDictionary new]; ++ static struct option long_options[] = ++ { ++ {"all", no_argument, 0, 'a'}, ++ {"help", no_argument, 0, 'h'}, ++ {"path", required_argument, 0, 'p'}, ++ {"unregister", required_argument, 0, 'u'}, ++ {"respring", no_argument, 0, 'r'}, ++ {"verbose", no_argument, 0, 'v'}, ++ {0, 0, 0, 0} ++ }; ++ int option_index = 0; ++ char ch; ++ bool have_path = false; ++ while ((ch = getopt_long(argc, (char *const *)argv, "ap:ru:vh?", long_options, &option_index)) != -1) { ++ switch (ch) ++ { ++ case 'a': ++ do_all = true; ++ break; ++ case 'h': ++ usage(); ++ break; ++ case 'p': ++ paths[@(optarg)] = @YES; ++ have_path = true; ++ break; ++ case 'r': ++ respring = true; ++ break; ++ case 'u': ++ unregister_paths[@(optarg)] = @YES; ++ have_path = true; ++ break; ++ case 'v': ++ verbose++; ++ break; ++ default: ++ break; ++ } ++ } ++ if (do_all || !$MCMPluginKitPluginDataContainer || !$MCMAppDataContainer || !$LSApplicationWorkspace) { ++ rv = standard_uicache(); ++ } else if (have_path) { ++ for (NSString *path in [paths allKeys]) { ++ if (verbose) fprintf(stderr, "Refreshing %s\n", path.UTF8String); ++ if (!registerPath(path)) rv++; ++ } ++ for (NSString *path in [unregister_paths allKeys]) { ++ if (!unregisterPath(path)) rv++; ++ } ++ } else { ++ rv += optimized_uicache(); ++ } ++ if ( respring ) ++ { ++ pid_t sb_pid = launch_get_job_pid("com.apple.SpringBoard"); ++ if ($SBSRelaunchAction && $FBSSystemService) { ++ 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)) { ++ break; ++ } ++ usleep(1000); ++ } ++ } else { ++ system("launchctl stop com.apple.SpringBoard"); ++ system("launchctl stop com.apple.backboardd"); ++ } ++ } ++ } // @autoreleasepool ++ return rv; + } +diff -urN uikittools/uicache.xml uikittools+uicache/uicache.xml +--- uikittools/uicache.xml 2018-10-04 15:58:37.000000000 -1000 ++++ uikittools+uicache/uicache.xml 2019-06-07 22:37:29.000000000 -1000 +@@ -4,23 +4,22 @@ + com.apple.private.mobileinstall.allowedSPI + + InstallForLaunchServices ++ UninstallForLaunchServices + +- + com.apple.lsapplicationworkspace.rebuildappdatabases + +- + com.apple.private.MobileContainerManager.allowed + +- + com.apple.private.kernel.override-cpumon + +- + com.apple.vpn.installer_events + +- ++ com.apple.frontboard.launchapplications ++ ++ com.apple.frontboard.shutdown ++ + platform-application + +- + com.apple.private.skip-library-validation + + -- cgit v1.2.3