]> git.saurik.com Git - uikittools.git/blob - sbreload.c
Invalidate the Launch Services icon cache.
[uikittools.git] / sbreload.c
1 /* UIKit Tools - command-line utilities for UIKit
2 * Copyright (C) 2008-2012 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* }}} */
39
40 #include <launch.h>
41 #include <notify.h>
42
43 #include <stdio.h>
44 #include <unistd.h>
45
46 #include <CoreFoundation/CoreFoundation.h>
47
48 launch_data_t
49 CF2launch_data(CFTypeRef cfr);
50
51 void
52 myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
53 {
54 launch_data_t ik, iw, where = context;
55
56 ik = CF2launch_data(key);
57 iw = CF2launch_data(value);
58
59 launch_data_dict_insert(where, iw, launch_data_get_string(ik));
60 launch_data_free(ik);
61 }
62
63 launch_data_t
64 CF2launch_data(CFTypeRef cfr)
65 {
66 launch_data_t r;
67 CFTypeID cft = CFGetTypeID(cfr);
68
69 if (cft == CFStringGetTypeID()) {
70 char buf[4096];
71 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
72 r = launch_data_alloc(LAUNCH_DATA_STRING);
73 launch_data_set_string(r, buf);
74 } else if (cft == CFBooleanGetTypeID()) {
75 r = launch_data_alloc(LAUNCH_DATA_BOOL);
76 launch_data_set_bool(r, CFBooleanGetValue(cfr));
77 } else if (cft == CFArrayGetTypeID()) {
78 CFIndex i, ac = CFArrayGetCount(cfr);
79 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
80 for (i = 0; i < ac; i++) {
81 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
82 if (v) {
83 launch_data_t iv = CF2launch_data(v);
84 launch_data_array_set_index(r, iv, i);
85 }
86 }
87 } else if (cft == CFDictionaryGetTypeID()) {
88 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
89 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
90 } else if (cft == CFDataGetTypeID()) {
91 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
92 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
93 } else if (cft == CFNumberGetTypeID()) {
94 long long n;
95 double d;
96 CFNumberType cfnt = CFNumberGetType(cfr);
97 switch (cfnt) {
98 case kCFNumberSInt8Type:
99 case kCFNumberSInt16Type:
100 case kCFNumberSInt32Type:
101 case kCFNumberSInt64Type:
102 case kCFNumberCharType:
103 case kCFNumberShortType:
104 case kCFNumberIntType:
105 case kCFNumberLongType:
106 case kCFNumberLongLongType:
107 CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
108 r = launch_data_alloc(LAUNCH_DATA_INTEGER);
109 launch_data_set_integer(r, n);
110 break;
111 case kCFNumberFloat32Type:
112 case kCFNumberFloat64Type:
113 case kCFNumberFloatType:
114 case kCFNumberDoubleType:
115 CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
116 r = launch_data_alloc(LAUNCH_DATA_REAL);
117 launch_data_set_real(r, d);
118 break;
119 default:
120 r = NULL;
121 break;
122 }
123 } else {
124 r = NULL;
125 }
126 return r;
127 }
128
129 CFPropertyListRef
130 CreateMyPropertyListFromFile(const char *posixfile)
131 {
132 CFPropertyListRef propertyList;
133 CFStringRef errorString;
134 CFDataRef resourceData;
135 SInt32 errorCode;
136 CFURLRef fileURL;
137
138 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
139 if (!fileURL) {
140 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
141 }
142 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
143 fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
144 }
145 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
146 if (!propertyList) {
147 fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
148 }
149
150 return propertyList;
151 }
152
153 #define _assert(test, format, args...) do { \
154 if (test) break; \
155 fprintf(stderr, format "\n", ##args); \
156 return 1; \
157 } while (false)
158
159 void stop() {
160 sleep(1);
161 }
162
163 #define SpringBoard_plist "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
164
165 int main(int argc, const char *argv[]) {
166 _assert(argc == 1, "usage: sbreload");
167
168 CFDictionaryRef plist = CreateMyPropertyListFromFile(SpringBoard_plist);
169 _assert(plist != NULL, "CreateMyPropertyListFromFile() == NULL");
170
171 launch_data_t job = CF2launch_data(plist);
172 _assert(job != NULL, "CF2launch_data() == NULL");
173
174 launch_data_t data, request, response;
175
176 data = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
177 _assert(data != NULL, "launch_data_dict_lookup(LABEL) == NULL");
178 const char *label = launch_data_get_string(data);
179
180 request = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
181 launch_data_dict_insert(request, launch_data_new_string(label), LAUNCH_KEY_GETJOB);
182
183 response = launch_msg(request);
184 _assert(response != NULL, "launch_msg(GetJob) == NULL");
185 launch_data_free(request);
186
187 pid_t pid;
188
189 if (launch_data_get_type(response) == LAUNCH_DATA_ERRNO) {
190 int error = launch_data_get_errno(response);
191 _assert(error == ESRCH, "GetJob(%s): %s", label, strerror(error));
192 pid = -1;
193 } else if (launch_data_get_type(response) == LAUNCH_DATA_DICTIONARY) {
194 data = launch_data_dict_lookup(response, LAUNCH_JOBKEY_PID);
195 _assert(data != NULL, "launch_data_dict_lookup(PID) == NULL");
196 pid = launch_data_get_integer(data);
197 } else _assert(false, "launch_data_get_type() not in (DICTIONARY, ERRNO)");
198
199 launch_data_free(response);
200
201 // 600 is being used to approximate 4.x/5.x boundary
202 if (kCFCoreFoundationVersionNumber < 600) {
203 fprintf(stderr, "notify_post(com.apple.mobile.springboard_teardown)\n");
204 notify_post("com.apple.mobile.springboard_teardown");
205 } else {
206 // XXX: this code is preferable to launchctl unoad but it requires libvproc? :(
207 //vproc_err_t *error = _vproc_send_signal_by_label(label, VPROC_MAGIC_UNLOAD_SIGNAL);
208 //_assert(error == NULL, "_vproc_send_signal_by_label(UNLOAD) != NULL");
209
210 fprintf(stderr, "launchctl unload SpringBoard.plist\n");
211 system("launchctl unload " SpringBoard_plist);
212 }
213
214 if (pid != -1) {
215 fprintf(stderr, "waiting for kill(%u) != 0...\n", pid);
216 while (kill(pid, 0) == 0)
217 stop();
218
219 int error = errno;
220 _assert(error == ESRCH, "kill(%u): %s", pid, strerror(error));
221 }
222
223 request = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
224 launch_data_dict_insert(request, job, LAUNCH_KEY_SUBMITJOB);
225
226 for (;;) {
227 response = launch_msg(request);
228 _assert(response != NULL, "launch_msg(SubmitJob) == NULL");
229
230 _assert(launch_data_get_type(response) == LAUNCH_DATA_ERRNO, "launch_data_get_type() != ERRNO");
231 int error = launch_data_get_errno(response);
232 launch_data_free(response);
233
234 const char *string = strerror(error);
235
236 if (error == EEXIST) {
237 fprintf(stderr, "SubmitJob(%s): %s, retrying...\n", label, string);
238 stop();
239 } else {
240 _assert(error == 0, "SubmitJob(%s): %s", label, string);
241 break;
242 }
243 }
244
245 launch_data_free(request);
246
247 return 0;
248 }