]>
Commit | Line | Data |
---|---|---|
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 | } |