uicache.mm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /* UIKit Tools - command-line utilities for UIKit
  2. * Copyright (C) 2018-2019 Sam Bingner
  3. * Copyright (C) 2008-2012 Jay Freeman (saurik)
  4. * Portions Copyright (C) 2019 Sam Bingner
  5. */
  6. /* This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. /* Modified BSD License {{{ */
  20. /*
  21. * Redistribution and use in source and binary
  22. * forms, with or without modification, are permitted
  23. * provided that the following conditions are met:
  24. *
  25. * 1. Redistributions of source code must retain the
  26. * above copyright notice, this list of conditions
  27. * and the following disclaimer.
  28. * 2. Redistributions in binary form must reproduce the
  29. * above copyright notice, this list of conditions
  30. * and the following disclaimer in the documentation
  31. * and/or other materials provided with the
  32. * distribution.
  33. * 3. The name of the author may not be used to endorse
  34. * or promote products derived from this software
  35. * without specific prior written permission.
  36. *
  37. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
  38. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  39. * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  40. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  41. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
  42. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  43. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  44. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  45. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  46. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  47. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  48. * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  49. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  50. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  51. */
  52. /* }}} */
  53. #import <Foundation/Foundation.h>
  54. #import "NSTask.h"
  55. #import <dlfcn.h>
  56. #import <notify.h>
  57. #import <sys/types.h>
  58. #import <sys/stat.h>
  59. #import <unistd.h>
  60. #import <getopt.h>
  61. #import <launch.h>
  62. #import <objc/runtime.h>
  63. #import <crt_externs.h>
  64. #import <xpc/xpc.h>
  65. #include <spawn.h>
  66. #include <sys/wait.h>
  67. #include "csstore.hpp"
  68. @interface PBSSystemService : NSObject
  69. +(id)sharedInstance;
  70. -(void)relaunchBackboardd;
  71. @end
  72. @interface PBSSystemServiceConnection : NSObject
  73. +(id)sharedConnection;
  74. -(id)systemServiceProxy;
  75. @end
  76. @interface NSMutableArray (Cydia)
  77. - (void) addInfoDictionary:(NSDictionary *)info;
  78. @end
  79. @implementation NSMutableArray (Cydia)
  80. - (void) addInfoDictionary:(NSDictionary *)info {
  81. [self addObject:info];
  82. }
  83. - (NSArray *) allInfoDictionaries {
  84. return self;
  85. }
  86. @end
  87. @interface NSMutableDictionary (Cydia)
  88. - (void) addInfoDictionary:(NSDictionary *)info;
  89. @end
  90. @implementation NSMutableDictionary (Cydia)
  91. - (void) addInfoDictionary:(NSDictionary *)info {
  92. NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
  93. [self setObject:info forKey:bundle];
  94. }
  95. - (NSArray *) allInfoDictionaries {
  96. return [self allValues];
  97. }
  98. @end
  99. @interface LSApplicationProxy : NSObject
  100. - (NSString*) applicationIdentifier;
  101. - (NSURL*) bundleURL;
  102. - (NSDate*) registeredDate;
  103. @end
  104. @interface LSApplicationWorkspace : NSObject
  105. + (id) defaultWorkspace;
  106. - (BOOL) registerApplication:(id)application;
  107. - (BOOL) unregisterApplication:(id)application;
  108. - (BOOL) invalidateIconCache:(id)bundle;
  109. - (BOOL) registerApplicationDictionary:(id)application;
  110. - (BOOL) installApplication:(id)application withOptions:(id)options;
  111. - (BOOL) _LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)system internal:(BOOL)internal user:(BOOL)user;
  112. - (NSArray<LSApplicationProxy*>*) allApplications;
  113. @end
  114. @interface MCMAppDataContainer
  115. +(id)containerWithIdentifier:(NSString*)identifier createIfNecessary:(bool)create existed:(bool*)existed error:(NSError*)error;
  116. -(NSURL*)url;
  117. @end
  118. @interface FBSSystemService
  119. +(id)sharedService;
  120. -(void)sendActions:(NSSet*)actions withResult:(id)result;
  121. @end
  122. typedef enum {
  123. None = 0,
  124. RestartRenderServer = (1 << 0), // also relaunch backboardd
  125. SnapshotTransition = (1 << 1),
  126. FadeToBlackTransition = (1 << 2),
  127. } SBSRelaunchActionStyle;
  128. @interface SBSRelaunchAction
  129. +(id)actionWithReason:(id)reason options:(int64_t)options targetURL:(NSURL*)url;
  130. @end
  131. static int verbose=0;
  132. static int standard_uicache(void);
  133. static Class $MCMPluginKitPluginDataContainer;
  134. static Class $MCMAppDataContainer;
  135. static Class $LSApplicationWorkspace;
  136. LSApplicationWorkspace *workspace=nil;
  137. extern char **environ;
  138. int my_system(const char *cmd) {
  139. pid_t pid;
  140. char *argv[] = {"sh", "-c", (char*)cmd, NULL};
  141. int status;
  142. fprintf(stderr, "Run command: %s\n", cmd);
  143. status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
  144. if (status == 0) {
  145. printf("Child pid: %i\n", pid);
  146. if (waitpid(pid, &status, 0) != -1) {
  147. printf("Child exited with status %i\n", status);
  148. } else {
  149. perror("waitpid");
  150. }
  151. } else {
  152. printf("posix_spawn: %s\n", strerror(status));
  153. }
  154. return status;
  155. }
  156. NSString *getAppPath(NSString *path)
  157. {
  158. path = [path stringByResolvingSymlinksInPath];
  159. if (![path hasPrefix:@"/Applications/"]) {
  160. fprintf(stderr, "Error: Path must be within /Applications/\n");
  161. return nil;
  162. }
  163. return [NSString pathWithComponents:[[path pathComponents] subarrayWithRange:NSMakeRange(0, 3)]];
  164. }
  165. bool appIsRegistered(NSString *path)
  166. {
  167. @autoreleasepool {
  168. path = getAppPath(path);
  169. if (!path) return false;
  170. for (LSApplicationProxy *app in [workspace allApplications]) {
  171. if ([path isEqualToString:[[app bundleURL] path]]) return true;
  172. }
  173. return false;
  174. }
  175. }
  176. bool unregisterPath(NSString *path)
  177. {
  178. @autoreleasepool {
  179. if (verbose) fprintf(stderr, "Unregistering %s\n", path.lastPathComponent.UTF8String);
  180. path = getAppPath(path);
  181. if (!path) return false;
  182. if (appIsRegistered(path) && ![workspace unregisterApplication:[NSURL fileURLWithPath:path]]) {
  183. fprintf(stderr, "Error: unregisterApplication failed for %s\n", path.lastPathComponent.UTF8String);
  184. return false;
  185. }
  186. }
  187. return true;
  188. }
  189. // 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.
  190. bool registerPath(NSString *path)
  191. {
  192. if (!path) {
  193. if (verbose) fprintf(stderr, "registerPath called with no path\n");
  194. return false;
  195. }
  196. NSString *realPath = getAppPath(path);
  197. if (!realPath) {
  198. if (verbose) fprintf(stderr, "unable to determine path for %s\n", path.UTF8String);
  199. return false;
  200. }
  201. NSDictionary *infoDictionary = [NSDictionary dictionaryWithContentsOfFile:
  202. [realPath stringByAppendingPathComponent:@"Info.plist"]];
  203. NSString *bundleID = [infoDictionary objectForKey:@"CFBundleIdentifier"];
  204. if (bundleID) {
  205. NSFileManager *fm = [NSFileManager defaultManager];
  206. if ([infoDictionary objectForKey:@"CFBundleExecutable"]) {
  207. NSString *executable = [realPath stringByAppendingPathComponent:[infoDictionary objectForKey:@"CFBundleExecutable"]];
  208. if (![fm fileExistsAtPath:executable]) {
  209. fprintf(stderr, "Error: CFBundleExecutable defined but missing for %s - this is a fatal error. Aborting.\n", realPath.lastPathComponent.UTF8String);
  210. return false;
  211. }
  212. }
  213. NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  214. @"System", @"ApplicationType",
  215. @YES, @"BundleNameIsLocalized",
  216. bundleID, @"CFBundleIdentifier",
  217. @NO, @"CompatibilityState",
  218. @NO, @"IsDeletable",
  219. realPath, @"Path",
  220. [NSMutableDictionary dictionary], @"_LSBundlePlugins",
  221. nil];
  222. id appContainer = [$MCMAppDataContainer containerWithIdentifier:bundleID
  223. createIfNecessary:YES existed:NULL error:nil];
  224. NSString *appContainerPath = [[appContainer url] path];
  225. if (appContainerPath) {
  226. dict[@"Container"] = appContainerPath;
  227. }
  228. NSString *pluginsPath = [realPath stringByAppendingPathComponent:@"PlugIns"];
  229. for (NSString *plugin in [fm contentsOfDirectoryAtPath:pluginsPath error:nil]) {
  230. NSString *pluginPath = [pluginsPath stringByAppendingPathComponent:plugin];
  231. NSString *pluginInfoPlistPath = [pluginPath stringByAppendingPathComponent:@"Info.plist"];
  232. NSString *pluginBundleIdentifier = [[NSDictionary dictionaryWithContentsOfFile:pluginInfoPlistPath] objectForKey:@"CFBundleIdentifier"];
  233. if (pluginBundleIdentifier) {
  234. id pluginContainer = [$MCMPluginKitPluginDataContainer containerWithIdentifier:pluginBundleIdentifier
  235. createIfNecessary:YES existed:NULL error:nil];
  236. NSURL *pluginContainerURL = [pluginContainer url];
  237. NSString *pluginContainerPath = [pluginContainerURL path];
  238. dict[@"_LSBundlePlugins"][pluginBundleIdentifier] = @{
  239. @"ApplicationType": @"PluginKitPlugin",
  240. @"BundleNameIsLocalized": @YES,
  241. @"CFBundleIdentifier": pluginBundleIdentifier,
  242. @"CompatibilityState": @NO,
  243. @"Container": pluginContainerPath,
  244. @"Path": pluginPath,
  245. @"PluginOwnerBundleID": bundleID
  246. };
  247. }
  248. }
  249. if (![[$LSApplicationWorkspace defaultWorkspace] registerApplicationDictionary:dict]) {
  250. fprintf(stderr, "Error: registerApplicationDictionary failed for %s\n", path.lastPathComponent.UTF8String);
  251. return false;
  252. }
  253. } else {
  254. return unregisterPath(realPath);
  255. }
  256. return true;
  257. }
  258. void usage(void)
  259. {
  260. fprintf(stderr, "Usage: %s [-hrv] [[-p | -u] /Applications/App.app]]\n", getprogname());
  261. exit(EXIT_FAILURE);
  262. }
  263. pid_t launch_get_job_pid(const char * job)
  264. {
  265. launch_data_t resp;
  266. launch_data_t msg;
  267. msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
  268. if (msg == NULL) {
  269. return -1;
  270. }
  271. launch_data_dict_insert(msg, launch_data_new_string(job), LAUNCH_KEY_GETJOB);
  272. resp = launch_msg(msg);
  273. launch_data_free(msg);
  274. if (resp == NULL) {
  275. return -1;
  276. }
  277. if (launch_data_get_type(resp) != LAUNCH_DATA_DICTIONARY) return -1;
  278. launch_data_t pid_data = launch_data_dict_lookup(resp, "PID");
  279. if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) return -1;
  280. pid_t pid = (pid_t)launch_data_get_integer(pid_data);
  281. launch_data_free(resp);
  282. return pid;
  283. }
  284. int standard_uicache(void)
  285. {
  286. @autoreleasepool {
  287. if (kCFCoreFoundationVersionNumber > 1000 && // this API is on iOS 7 but invaliding the icon cache is harder there
  288. [workspace respondsToSelector:@selector(_LSPrivateRebuildApplicationDatabasesForSystemApps:internal:user:)]) {
  289. if (![workspace _LSPrivateRebuildApplicationDatabasesForSystemApps:YES internal:YES user:NO])
  290. fprintf(stderr, "failed to rebuild application databases");
  291. return 0;
  292. }
  293. bool respring(false);
  294. NSString *home(NSHomeDirectory());
  295. NSString *path([NSString stringWithFormat:@"%@/Library/Caches/com.apple.mobile.installation.plist", home]);
  296. my_system("killall -SIGSTOP SpringBoard");
  297. sleep(1);
  298. @try {
  299. DeleteCSStores([home UTF8String]);
  300. my_system("killall lsd");
  301. if ([workspace respondsToSelector:@selector(invalidateIconCache:)])
  302. while (![workspace invalidateIconCache:nil])
  303. sleep(1);
  304. if (NSMutableDictionary *cache = [NSMutableDictionary dictionaryWithContentsOfFile:path]) {
  305. NSFileManager *manager = [NSFileManager defaultManager];
  306. NSError *error = nil;
  307. NSMutableDictionary *bundles([NSMutableDictionary dictionaryWithCapacity:16]);
  308. id after = [cache objectForKey:@"System"];
  309. if (after == nil) { error:
  310. fprintf(stderr, "%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
  311. goto cached;
  312. }
  313. id before([[after copy] autorelease]);
  314. [after removeAllObjects];
  315. NSArray *cached([cache objectForKey:@"InfoPlistCachedKeys"]);
  316. NSMutableSet *removed([NSMutableSet set]);
  317. for (NSDictionary *info in [before allInfoDictionaries])
  318. if (NSString *path = [info objectForKey:@"Path"])
  319. [removed addObject:path];
  320. if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
  321. for (NSString *app in apps)
  322. if ([app hasSuffix:@".app"]) {
  323. NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
  324. NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
  325. if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:plist]) {
  326. if (NSString *identifier = [info objectForKey:@"CFBundleIdentifier"]) {
  327. [bundles setObject:path forKey:identifier];
  328. [removed removeObject:path];
  329. if (cached != nil) {
  330. NSMutableDictionary *merged([before objectForKey:identifier]);
  331. if (merged == nil)
  332. merged = [NSMutableDictionary dictionary];
  333. else
  334. merged = [[merged mutableCopy] autorelease];
  335. for (NSString *key in cached)
  336. if (NSObject *value = [info objectForKey:key])
  337. [merged setObject:value forKey:key];
  338. else
  339. [merged removeObjectForKey:key];
  340. info = merged;
  341. }
  342. [info setObject:path forKey:@"Path"];
  343. [info setObject:@"System" forKey:@"ApplicationType"];
  344. [after addInfoDictionary:info];
  345. } else
  346. fprintf(stderr, "%s missing CFBundleIdentifier", [app UTF8String]);
  347. }
  348. }
  349. } else goto error;
  350. [cache writeToFile:path atomically:YES];
  351. if (workspace != nil) {
  352. if ([workspace respondsToSelector:@selector(invalidateIconCache:)]) {
  353. for (NSString *identifier in bundles)
  354. [workspace invalidateIconCache:identifier];
  355. } else {
  356. for (NSString *identifier in bundles) {
  357. NSString *path([bundles objectForKey:identifier]);
  358. [workspace unregisterApplication:[NSURL fileURLWithPath:path]];
  359. }
  360. }
  361. for (NSString *identifier in bundles) {
  362. NSString *path([bundles objectForKey:identifier]);
  363. if (kCFCoreFoundationVersionNumber >= 800)
  364. [workspace registerApplicationDictionary:[after objectForKey:identifier]];
  365. else
  366. [workspace registerApplication:[NSURL fileURLWithPath:path]];
  367. }
  368. for (NSString *path in removed)
  369. [workspace unregisterApplication:[NSURL fileURLWithPath:path]];
  370. }
  371. } else fprintf(stderr, "cannot open cache file. incorrect user?\n");
  372. cached:
  373. if (respring || kCFCoreFoundationVersionNumber >= 550.32) {
  374. unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-icons", home] UTF8String]);
  375. unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-icons.plist", home] UTF8String]);
  376. unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-smallicons", home] UTF8String]);
  377. unlink([[NSString stringWithFormat:@"%@/Library/Caches/com.apple.springboard-imagecache-smallicons.plist", home] UTF8String]);
  378. my_system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/SpringBoardIconCache", home] UTF8String]);
  379. my_system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/SpringBoardIconCache-small", home] UTF8String]);
  380. my_system([[NSString stringWithFormat:@"rm -rf %@/Library/Caches/com.apple.IconsCache", home] UTF8String]);
  381. }
  382. my_system("killall installd");
  383. } @finally {
  384. my_system("killall -SIGCONT SpringBoard");
  385. }
  386. notify_post("com.apple.mobile.application_installed");
  387. return 0;
  388. }
  389. }
  390. pid_t pidOfCydia(void) {
  391. launch_data_t request = launch_data_new_string(LAUNCH_KEY_GETJOBS);
  392. launch_data_t response = launch_msg(request);
  393. launch_data_free(request);
  394. __block pid_t pid=-1;
  395. if (response == NULL || launch_data_get_type(response) != LAUNCH_DATA_DICTIONARY) return -1;
  396. xpc_dictionary_apply((xpc_object_t)response, ^bool(const char *key, xpc_object_t value) {
  397. if (xpc_get_type(value) == XPC_TYPE_DICTIONARY) {
  398. const char *program = xpc_dictionary_get_string(value, "Program");
  399. if (program && strcmp(program, "/Applications/Cydia.app/Cydia") == 0) {
  400. pid = (pid_t)xpc_dictionary_get_int64(value, "PID");
  401. if (verbose) fprintf(stderr, "Found Cydia running with PID: %d\n", pid);
  402. return false;
  403. }
  404. }
  405. return true;
  406. });
  407. if (pid>0) {
  408. return pid;
  409. }
  410. return -1;
  411. }
  412. int optimized_uicache(void) {
  413. __block int rv=0;
  414. NSMutableDictionary *registered = [NSMutableDictionary new];
  415. static void (^readHandler)(NSFileHandle*) = ^(NSFileHandle *fh) {
  416. NSData *output = [fh readDataToEndOfFile];
  417. if (output.length==0) return;
  418. const char *found_path = (const char *)[output bytes];
  419. NSArray *found = [@(found_path) pathComponents];
  420. if (found.count >= 3) {
  421. NSString *appPath = [@"/Applications" stringByAppendingPathComponent:found[2]];
  422. @synchronized (registered) {
  423. if (registered[appPath]) return;
  424. if (verbose) fprintf(stderr, "Updating %s\n", appPath.lastPathComponent.UTF8String);
  425. registered[appPath] = @YES;
  426. }
  427. pid_t cydia_pid;
  428. if ([found[2] isEqualToString:@"Cydia.app"] &&
  429. (cydia_pid = pidOfCydia()) > 0) {
  430. // We are in cydia and trying to refresh it - this will kill it. Let's schedule it for later.
  431. if (verbose) fprintf(stderr, "Waiting to refresh Cydia...\n");
  432. pid_t pid = fork();
  433. if (pid == 0) {
  434. setpgrp();
  435. signal(SIGHUP, SIG_IGN);
  436. signal(SIGPIPE, SIG_IGN);
  437. fclose(stdin);
  438. freopen("/dev/null", "a", stderr);
  439. freopen("/dev/null", "a", stdout);
  440. pid = fork();
  441. if (pid == 0) {
  442. while (kill(cydia_pid, 0)==0) {
  443. sleep(1);
  444. }
  445. const char *uicache = (*_NSGetArgv())[0];
  446. execl(uicache, uicache, "-vvvvvvv", NULL);
  447. fprintf(stderr, "Unable to exec\n");
  448. fflush(stderr);
  449. exit(-1);
  450. }
  451. exit(0);
  452. } else if (pid > 0) {
  453. int stat;
  454. waitpid(pid, &stat, 0);
  455. return;
  456. } else {
  457. fprintf(stderr, "Unable to fork\n");
  458. }
  459. }
  460. if (!registerPath(appPath)) rv++;
  461. }
  462. };
  463. NSFileManager *fm = [NSFileManager defaultManager];
  464. NSMutableDictionary *apps = [NSMutableDictionary new];
  465. NSMutableArray *cleanup = [NSMutableArray new];
  466. NSMutableArray *finds = [NSMutableArray new];
  467. if (verbose>1) fprintf(stderr, "Enumerating apps\n");
  468. for (LSApplicationProxy *app in [workspace allApplications]) {
  469. NSString *path = [[app bundleURL] path];
  470. if (![path hasPrefix:@"/Applications/"]) continue;
  471. if (verbose>1) fprintf(stderr, "Checking %s\n", path.lastPathComponent.UTF8String);
  472. NSDate *lastRegistered = [app registeredDate];
  473. if ([fm fileExistsAtPath:path]) {
  474. // Check for updated components
  475. NSTask *find = [NSTask new];
  476. [find setLaunchPath:@"/usr/bin/find"];
  477. [find setStandardOutput:[NSPipe pipe]];
  478. NSString *stampPath = [NSString stringWithFormat:@"/var/tmp/uicache.stamp.%@", app.applicationIdentifier];
  479. [fm createFileAtPath:stampPath contents:nil attributes:@{NSFileModificationDate: lastRegistered}];
  480. [cleanup addObject:stampPath];
  481. [find setArguments:@[ path, @"-newer", stampPath, @"-print0", @"-quit"]];
  482. [finds addObject:find];
  483. [find launch];
  484. apps[path.lastPathComponent] = app;
  485. } else {
  486. if (verbose) fprintf(stderr, "De-registering removed app: %s\n", path.lastPathComponent.UTF8String);
  487. @synchronized (registered) {
  488. if (registered[path]) continue;
  489. registered[path] = @YES;
  490. }
  491. if (!unregisterPath(path)) rv++;
  492. }
  493. }
  494. for (NSString* existing in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/Applications" error:nil]) {
  495. NSString *path = [@"/Applications" stringByAppendingPathComponent:existing];
  496. if (apps[existing] || registered[path] || ![existing hasSuffix:@".app"]) continue;
  497. if (verbose) fprintf(stderr, "Registering new app: %s\n", existing.UTF8String);
  498. @synchronized (registered) {
  499. registered[path] = @YES;
  500. }
  501. if (!registerPath(path)) rv++;
  502. }
  503. for (NSTask *find in finds) {
  504. if (verbose>2) fprintf(stderr, "waiting for find %s\n", [find.arguments componentsJoinedByString:@" "].UTF8String);
  505. readHandler([find.standardOutput fileHandleForReading]);
  506. [find waitUntilExit];
  507. }
  508. for (NSString *path in cleanup) {
  509. [fm removeItemAtPath:path error:nil];
  510. }
  511. return rv;
  512. }
  513. int main(int argc, const char *argv[])
  514. {
  515. if (getuid() == 0) {
  516. // Be mobile
  517. if (setuid(501)) {
  518. fprintf(stderr, "Error: unable to become mobile");
  519. return -1;
  520. }
  521. }
  522. dlopen("/System/Library/PrivateFrameworks/MobileContainerManager.framework/MobileContainerManager", RTLD_LAZY);
  523. dlopen("/System/Library/PrivateFrameworks/FrontBoardServices.framework/FrontBoardServices", RTLD_LAZY);
  524. dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY);
  525. Class $SBSRelaunchAction = objc_getClass("SBSRelaunchAction");
  526. Class $FBSSystemService = objc_getClass("FBSSystemService");
  527. $MCMPluginKitPluginDataContainer = objc_getClass("MCMPluginKitPluginDataContainer");
  528. $MCMAppDataContainer = objc_getClass("MCMAppDataContainer");
  529. $LSApplicationWorkspace = objc_getClass("LSApplicationWorkspace");
  530. workspace = [$LSApplicationWorkspace defaultWorkspace];
  531. static int rv=0;
  532. @autoreleasepool {
  533. bool respring=false, do_all=false;
  534. void (*jb_oneshot_entitle_now)(pid_t a, uint64_t b);
  535. void *libjb = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY);
  536. if (libjb) {
  537. dlerror();
  538. jb_oneshot_entitle_now = (void (*)(pid_t, uint64_t))dlsym(libjb, "jb_oneshot_entitle_now");
  539. if (!dlerror()) jb_oneshot_entitle_now(getpid(), 2);
  540. }
  541. NSMutableDictionary *paths = [NSMutableDictionary new];
  542. NSMutableDictionary *unregister_paths = [NSMutableDictionary new];
  543. static struct option long_options[] =
  544. {
  545. {"all", no_argument, 0, 'a'},
  546. {"help", no_argument, 0, 'h'},
  547. {"path", required_argument, 0, 'p'},
  548. {"unregister", required_argument, 0, 'u'},
  549. {"respring", no_argument, 0, 'r'},
  550. {"verbose", no_argument, 0, 'v'},
  551. {0, 0, 0, 0}
  552. };
  553. int option_index = 0;
  554. char ch;
  555. bool have_path = false;
  556. while ((ch = getopt_long(argc, (char *const *)argv, "ap:ru:vh?", long_options, &option_index)) != -1) {
  557. switch (ch)
  558. {
  559. case 'a':
  560. do_all = true;
  561. break;
  562. case 'h':
  563. usage();
  564. break;
  565. case 'p':
  566. paths[@(optarg)] = @YES;
  567. have_path = true;
  568. break;
  569. case 'r':
  570. respring = true;
  571. break;
  572. case 'u':
  573. unregister_paths[@(optarg)] = @YES;
  574. have_path = true;
  575. break;
  576. case 'v':
  577. verbose++;
  578. break;
  579. default:
  580. break;
  581. }
  582. }
  583. if (do_all || !$MCMPluginKitPluginDataContainer || !$MCMAppDataContainer || !$LSApplicationWorkspace) {
  584. rv = standard_uicache();
  585. } else if (have_path) {
  586. for (NSString *path in [paths allKeys]) {
  587. if (verbose) fprintf(stderr, "Refreshing %s\n", path.UTF8String);
  588. if (!registerPath(path)) rv++;
  589. }
  590. for (NSString *path in [unregister_paths allKeys]) {
  591. if (!unregisterPath(path)) rv++;
  592. }
  593. } else {
  594. rv += optimized_uicache();
  595. }
  596. if ( respring )
  597. {
  598. [[[PBSSystemServiceConnection sharedConnection] systemServiceProxy] relaunchBackboardd];
  599. pid_t sb_pid = launch_get_job_pid("com.apple.SpringBoard");
  600. if ($SBSRelaunchAction && $FBSSystemService) {
  601. id action = [$SBSRelaunchAction actionWithReason:@"respring" options:RestartRenderServer targetURL:nil];
  602. id sharedService = [$FBSSystemService sharedService];
  603. [sharedService sendActions:[NSSet setWithObject:action] withResult:nil];
  604. for (int i=0; i<100; i++) {
  605. if (kill(sb_pid, 0)) {
  606. break;
  607. }
  608. usleep(1000);
  609. }
  610. } else {
  611. my_system("launchctl stop com.apple.PineBoard");
  612. my_system("launchctl stop com.apple.backboardd");
  613. }
  614. }
  615. } // @autoreleasepool
  616. return rv;
  617. }