/* UIKit Tools - command-line utilities for UIKit * Copyright (C) 2018-2019 Sam Bingner * Copyright (C) 2008-2012 Jay Freeman (saurik) * Portions Copyright (C) 2019 Sam Bingner */ /* 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 . */ /* 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. */ /* }}} */ #import #import #import #import #import #import #import #import #import #import #import #import #include "csstore.hpp" #if TARGET_OS_IPHONE #define FRONTBOARD "SpringBoard" #define FRONTBOARD_ID "com.apple.SpringBoard" #elif TARGET_OS_TV #define FRONTBOARD "PineBoard" #define FRONTBOARD_ID "com.apple.PineBoard" @interface PBSSystemService : NSObject +(id)sharedInstance; -(void)relaunchBackboardd; @end @interface PBSSystemServiceConnection : NSObject +(id)sharedConnection; -(id)systemServiceProxy; @end #else #error "Unsupported target OS" #endif @interface NSMutableArray (Cydia) - (void) addInfoDictionary:(NSDictionary *)info; @end @implementation NSMutableArray (Cydia) - (void) addInfoDictionary:(NSDictionary *)info { [self addObject:info]; } - (NSArray *) allInfoDictionaries { return self; } @end @interface NSMutableDictionary (Cydia) - (void) addInfoDictionary:(NSDictionary *)info; @end @implementation NSMutableDictionary (Cydia) - (void) addInfoDictionary:(NSDictionary *)info { NSString *bundle = [info objectForKey:@"CFBundleIdentifier"]; [self setObject:info forKey:bundle]; } - (NSArray *) allInfoDictionaries { return [self allValues]; } @end @interface _LSApplicationState : NSObject -(bool)isValid; @end @interface LSApplicationProxy : NSObject +(LSApplicationProxy*)applicationProxyForIdentifier:(NSString*)appid; -(NSString*)applicationIdentifier; -(_LSApplicationState*)appState; -(NSURL*)bundleURL; -(NSURL*)resourcesDirectoryURL; -(NSURL*)containerURL; -(NSDate*)registeredDate; @end @interface LSApplicationWorkspace : NSObject + (LSApplicationWorkspace*) defaultWorkspace; - (BOOL) registerApplication:(id)application; - (BOOL) unregisterApplication:(id)application; - (BOOL) invalidateIconCache:(id)bundle; - (BOOL) registerApplicationDictionary:(id)application; - (BOOL) installApplication:(id)application withOptions:(id)options; - (BOOL) _LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)system internal:(BOOL)internal user:(BOOL)user; - (NSArray*) allApplications; - (NSArray*) installedApplications; @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 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 @implementation LSApplicationProxy (uicache) -(NSURL*)appURL { return [self respondsToSelector:@selector(bundleURL)]?[self bundleURL]:[self resourcesDirectoryURL]; } @end static int verbose=0; static int standard_uicache(void); static Class $MCMPluginKitPluginDataContainer; static Class $MCMAppDataContainer; static Class $LSApplicationWorkspace; LSApplicationWorkspace *workspace; static bool force; NSArray *getAllApplications() { LSApplicationWorkspace *w = workspace ?: [LSApplicationWorkspace defaultWorkspace]; if ([w respondsToSelector:@selector(allApplications)]) return [w allApplications]; else if ([w respondsToSelector:@selector(installedApplications)]) { NSMutableArray *apps = [NSMutableArray array]; for (NSString *applicationIdentifier in [w installedApplications]) { [apps addObject:[LSApplicationProxy applicationProxyForIdentifier:applicationIdentifier]]; } return apps; } fprintf(stderr, "Error: This iOS version doesn't support listing all applications\n"); return [NSArray array]; } int list_all_apps(void) { NSArray *allApps = getAllApplications(); for (LSApplicationProxy *app in allApps) { printf("%s : %s\n", app.applicationIdentifier.UTF8String, app.appURL.path.UTF8String); } return 0; } int list_app(NSString *appid) { if (appid == NULL) { return 1; } LSApplicationProxy *app = [LSApplicationProxy applicationProxyForIdentifier:appid]; _LSApplicationState *appState = nil; BOOL hasAppState = [app respondsToSelector:@selector(appState)]; if (hasAppState) { appState = [app appState]; if (![appState isValid]) { fprintf(stderr, "Invalid appID provided: %s\n", appid.UTF8String); return 1; } } printf("Information for %s\n", appid.UTF8String); if (hasAppState) printf("State: %s\n", [[appState description] UTF8String]); printf("Path: %s\n", app.appURL.path.UTF8String); printf("Container Path: %s\n", app.containerURL.path.UTF8String); return 0; } NSString *pathInApplications(NSString *path) { NSArray *pathComponents = [path pathComponents]; if ([path hasPrefix:@"/Applications/"]) { return [NSString pathWithComponents:[pathComponents subarrayWithRange:NSMakeRange(0, 3)]]; } else if ([path hasPrefix:@"/var/stash"] && [pathComponents[4] isEqualToString:@"Applications"]) { // Matches /var/stash/*/Applications/ return [@"/Applications/" stringByAppendingPathComponent:pathComponents[5]]; } return nil; } NSString *getAppPath(NSString *path) { if (force) return path; return pathInApplications(path);; } bool appIsRegistered(NSString *path) { if (force) return true; @autoreleasepool { path = getAppPath(path); for (LSApplicationProxy *app in getAllApplications()) { NSString *appPath = getAppPath(app.appURL.path); if ([path isEqualToString:appPath]) return true; } return false; } } bool unregisterPath(NSString *path) { @autoreleasepool { NSURL *url = nil; if (![path hasPrefix:@"/"]) { // Maybe it's an app_id LSApplicationProxy *app = [LSApplicationProxy applicationProxyForIdentifier:path]; if (app) { url = [app appURL]; if (verbose) fprintf(stderr, "Resolved bundle ID %s to path %s\n", path.UTF8String, url.path.UTF8String); path = [url path]; } } else { path = getAppPath(path); if (!path) { fprintf(stderr, "Error: Path \"%s\" must be within /Applications/\n", path.UTF8String); return false; } url = [NSURL fileURLWithPath:path]; } if (!url) return false; if (verbose) fprintf(stderr, "Unregistering %s\n", path.lastPathComponent.UTF8String); if (appIsRegistered(path) && ![workspace unregisterApplication:url]) { 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) { fprintf(stderr, "Error: Path \"%s\" must be within /Applications/\n", path.UTF8String); return false; } if (verbose) fprintf(stderr, "Refreshing %s\n", realPath.lastPathComponent.UTF8String); 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]) { if ([bundleID isEqual:@"com.apple.TrustMe"]) { return true; } 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?[$MCMAppDataContainer containerWithIdentifier:bundleID createIfNecessary:YES existed:NULL error:nil]: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 ($MCMPluginKitPluginDataContainer && 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]] | -l [application.id]\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; } bool respring(false); NSString *home(NSHomeDirectory()); NSString *path([NSString stringWithFormat:@"%@/Library/Caches/com.apple.mobile.installation.plist", home]); system("killall -SIGSTOP " FRONTBOARD); sleep(1); @try { DeleteCSStores([home UTF8String]); system("killall lsd"); if ([workspace respondsToSelector:@selector(invalidateIconCache:)]) while (![workspace invalidateIconCache:nil]) sleep(1); if (NSMutableDictionary *cache = [NSMutableDictionary dictionaryWithContentsOfFile:path]) { NSFileManager *manager = [NSFileManager defaultManager]; NSError *error = nil; NSMutableDictionary *bundles([NSMutableDictionary dictionaryWithCapacity:16]); id after = [cache objectForKey:@"System"]; if (after == nil) { error: fprintf(stderr, "%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]); goto cached; } id before([[after copy] autorelease]); [after removeAllObjects]; NSArray *cached([cache objectForKey:@"InfoPlistCachedKeys"]); NSMutableSet *removed([NSMutableSet set]); for (NSDictionary *info in [before allInfoDictionaries]) if (NSString *path = [info objectForKey:@"Path"]) [removed addObject:path]; if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) { for (NSString *app in apps) if ([app hasSuffix:@".app"]) { NSString *path = [@"/Applications" stringByAppendingPathComponent:app]; NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"]; if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:plist]) { if (NSString *identifier = [info objectForKey:@"CFBundleIdentifier"]) { [bundles setObject:path forKey:identifier]; [removed removeObject:path]; if (cached != nil) { NSMutableDictionary *merged([before objectForKey:identifier]); if (merged == nil) merged = [NSMutableDictionary dictionary]; else merged = [[merged mutableCopy] autorelease]; for (NSString *key in cached) if (NSObject *value = [info objectForKey:key]) [merged setObject:value forKey:key]; else [merged removeObjectForKey:key]; info = merged; } [info setObject:path forKey:@"Path"]; [info setObject:@"System" forKey:@"ApplicationType"]; [after addInfoDictionary:info]; } else fprintf(stderr, "%s missing CFBundleIdentifier", [app UTF8String]); } } } else goto error; [cache writeToFile:path atomically:YES]; if (workspace != nil) { if ([workspace respondsToSelector:@selector(invalidateIconCache:)]) { for (NSString *identifier in bundles) [workspace invalidateIconCache:identifier]; } else { for (NSString *identifier in bundles) { NSString *path([bundles objectForKey:identifier]); [workspace unregisterApplication:[NSURL fileURLWithPath:path]]; } } for (NSString *identifier in bundles) { NSString *path([bundles objectForKey:identifier]); if (kCFCoreFoundationVersionNumber >= 800) [workspace registerApplicationDictionary:[after objectForKey:identifier]]; else [workspace registerApplication:[NSURL fileURLWithPath:path]]; } for (NSString *path in removed) [workspace unregisterApplication:[NSURL fileURLWithPath:path]]; } } else fprintf(stderr, "cannot open cache file. incorrect user?\n"); cached: if (respring || kCFCoreFoundationVersionNumber >= 550.32) { unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-icons", home] UTF8String]); unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-icons.plist", home] UTF8String]); unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-smallicons", home] UTF8String]); unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-smallicons.plist", home] UTF8String]); system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/SpringBoardIconCache", home] UTF8String]); system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/SpringBoardIconCache-small", home] UTF8String]); system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/com.apple.IconsCache", home] UTF8String]); } system("killall installd"); } @finally { system("killall -SIGCONT " FRONTBOARD); } 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; } if (verbose>2) fprintf(stderr, "%s is not Cydia...\n", program); } return true; }); if (pid>0) { return pid; } return -1; } bool cydiaCache(pid_t cydia_pid) { // 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 true; } fprintf(stderr, "Unable to fork\n"); return false; } 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 && cydiaCache(cydia_pid)) { // Is cydia and we queued a uicache in background so skip return; } 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 getAllApplications()) { NSString *path = pathInApplications([[app appURL] path]); if (!path) continue; if (verbose>1) fprintf(stderr, "Checking %s\n", path.lastPathComponent.UTF8String); if ([fm fileExistsAtPath:path]) { NSDate *lastRegistered = [app registeredDate]; pid_t cydia_pid; // 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, list_apps=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'}, {"list", optional_argument, 0, 'l'}, {"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; NSString *app_id = NULL; char ch; bool have_path = false; while ((ch = getopt_long(argc, (char *const *)argv, "afl::p:ru:vh?", long_options, &option_index)) != -1) { switch (ch) { case 'a': do_all = true; break; case 'f': force = true; break; case 'h': usage(); break; case 'l': if (optarg) { app_id = @(optarg); } else if (argv[optind] != NULL && argv[optind][0] != '-') { app_id = @(argv[optind++]); } list_apps = true; break; case 'p': [paths setObject:@YES forKey:@(optarg)]; have_path = true; break; case 'r': respring = true; break; case 'u': [unregister_paths setObject:@YES forKey:@(optarg)]; fprintf(stderr, "%s\n", optarg); have_path = true; break; case 'v': verbose++; break; default: break; } } if (list_apps) { if (verbose>2) fprintf(stderr, "Listing apps\n"); if (do_all || have_path || respring) { usage(); return 1; } if (app_id != NULL) { return list_app(app_id); } return list_all_apps(); } if (do_all || !$MCMPluginKitPluginDataContainer || !$MCMAppDataContainer || !$LSApplicationWorkspace || kCFCoreFoundationVersionNumber < 1200.00) { if (verbose>2) fprintf(stderr, "Using standard uicache\n"); rv = standard_uicache(); } else if (have_path) { if (verbose>2) fprintf(stderr, "Using per-path uicache\n"); for (NSString *path in [paths allKeys]) { if (!registerPath(path)) rv++; } for (NSString *path in [unregister_paths allKeys]) { if (!unregisterPath(path)) rv++; } } else { if (verbose>2) fprintf(stderr, "Using optimized uicache\n"); rv += optimized_uicache(); } if ( respring ) { #if TARGET_OS_IPHONE 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 " FRONTBOARD_ID); system("launchctl stop com.apple.backboardd"); } #elif TARGET_OS_TV [[[PBSSystemServiceConnection sharedConnection] systemServiceProxy] relaunchBackboardd]; #else #error "Unsupported target OS" #endif } } // @autoreleasepool return rv; }