]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/scprefs_observer.c
configd-1061.0.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / scprefs_observer.c
1 /*
2 * Copyright (c) 2012, 2013, 2015-2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <CommonCrypto/CommonDigest.h>
24 #include <dirent.h>
25 #include <notify.h>
26 #include <os/log.h>
27 #include <sys/param.h>
28 #include <sys/queue.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #ifndef SC_LOG_HANDLE
33 #define SC_LOG_HANDLE __log_SCPreferences
34 #endif //SC_LOG_HANDLE
35 os_log_t SC_LOG_HANDLE(void);
36
37 #include <SystemConfiguration/SCPrivate.h>
38 #include <SystemConfiguration/scprefs_observer.h>
39
40 #define MANAGED_PREFERENCES_PATH "/Library/Managed Preferences"
41 #if TARGET_OS_IPHONE
42 #define MOBILE_PREFERENCES_PATH "/var/mobile/Library/Preferences"
43 #endif // TARGET_OS_IPHONE
44
45 #if !TARGET_OS_IPHONE
46 #define PREFS_OBSERVER_KEY "com.apple.MCX._managementStatusChangedForDomains"
47 #else // !TARGET_OS_IPHONE
48 #define PREFS_OBSERVER_KEY "com.apple.ManagedConfiguration.profileListChanged"
49 #endif // !TARGET_OS_IPHONE
50
51 #pragma mark -
52 #pragma mark Utils
53
54 static void
55 iterate_dir(const char *d_name, const char *f_name,
56 CC_SHA256_CTX *ctxP, Boolean *found)
57 {
58 DIR *dir;
59 struct dirent * dp;
60
61 dir = opendir(d_name);
62
63 if (dir == NULL) {
64 /* if directory path does not exist */
65 return;
66 }
67
68 while ((dp = readdir(dir)) != NULL) {
69 char full_path[MAXPATHLEN];
70 struct stat s;
71
72 if ((strcmp(dp->d_name, ".") == 0) ||
73 (strcmp(dp->d_name, "..") == 0)) {
74 continue;
75 }
76
77 /* check path */
78 snprintf(full_path, sizeof(full_path), "%s/%s", d_name, dp->d_name);
79 if (stat(full_path, &s) == 0) {
80 if (S_ISDIR(s.st_mode)) {
81 // if sub-directory, iterate
82 iterate_dir(full_path, f_name, ctxP, found);
83 } else if (strcmp(f_name, dp->d_name) == 0) {
84 /*
85 * if this is the requested file, include
86 * the path and last modification time in
87 * the digest
88 */
89 CC_SHA256_Update(ctxP, full_path, (CC_LONG)strlen(full_path));
90 CC_SHA256_Update(ctxP,
91 (void *)&s.st_mtimespec.tv_sec,
92 sizeof(s.st_mtimespec.tv_sec));
93 *found = TRUE;
94 }
95 }
96 }
97 closedir(dir);
98 return;
99 }
100
101 static CF_RETURNS_RETAINED CFDataRef
102 build_digest(const char *top_dir, const char *file)
103 {
104 unsigned char bytes[CC_SHA256_DIGEST_LENGTH];
105 CC_SHA256_CTX ctx;
106 CFDataRef digest = NULL;
107 Boolean found = FALSE;
108
109 CC_SHA256_Init(&ctx);
110 iterate_dir(top_dir, file, &ctx, &found);
111 CC_SHA256_Final(bytes, &ctx);
112 if (found) {
113 digest = CFDataCreate(NULL, bytes, sizeof(bytes));
114 }
115 return (digest);
116 }
117
118 #pragma mark -
119 #pragma mark perfs_observer Private
120
121 struct _scprefs_observer_t {
122 _scprefs_observer_type type;
123 dispatch_block_t block;
124 CFDataRef digest;
125 SLIST_ENTRY(_scprefs_observer_t) next;
126 dispatch_queue_t queue;
127 char file[0];
128 };
129
130 static const char *
131 prefs_observer_get_prefs_path(scprefs_observer_t observer)
132 {
133 switch (observer->type) {
134 #if !TARGET_OS_IPHONE
135 case scprefs_observer_type_mcx:
136 return MANAGED_PREFERENCES_PATH;
137 #else // !TARGET_OS_IPHONE
138 case scprefs_observer_type_global:
139 return MANAGED_PREFERENCES_PATH;
140 case scprefs_observer_type_profile:
141 return MOBILE_PREFERENCES_PATH;
142 #endif // !TARGET_OS_IPHONE
143 default:
144 return (NULL);
145 }
146 }
147
148 /*
149 * Iterate through all of the directories and subdirectories.
150 * If the file within those directories has changed,
151 * then generate the notification.
152 */
153 static Boolean
154 has_changed(scprefs_observer_t observer) {
155 Boolean changed;
156 CFDataRef digest = NULL;
157 const char * starting_path;
158
159 starting_path = prefs_observer_get_prefs_path(observer);
160
161 digest = build_digest(starting_path, observer->file);
162
163 /* compare old vs. new digest */
164 changed = _SC_CFEqual(digest, observer->digest)?FALSE:TRUE;
165
166 /* save the digest */
167 if (observer->digest != NULL) {
168 CFRelease(observer->digest);
169 }
170
171 observer->digest = digest;
172
173 SC_log(changed ? LOG_INFO : LOG_DEBUG,
174 "preferences file: \"%s\" %s",
175 observer->file,
176 changed ? "changed" : "did not change");
177 return changed;
178 }
179
180 static dispatch_queue_t
181 prefs_observer_queue;
182
183 /* This holds the list of the observers */
184 static SLIST_HEAD(mylist, _scprefs_observer_t) head;
185
186 static void
187 prefs_observer_release(scprefs_observer_t observer)
188 {
189 SLIST_REMOVE(&head, observer, _scprefs_observer_t, next);
190
191 /* Now free the observer */
192 if (observer->digest != NULL) {
193 CFRelease(observer->digest);
194 }
195
196 free(observer);
197 }
198
199 static void
200 prefs_observer_handle_notifications()
201 {
202 scprefs_observer_t observer;
203
204 SC_log(LOG_DEBUG, "PrefsObserver notification received");
205
206 SLIST_FOREACH(observer, &head, next) {
207 /* if the preferences plist changed, call the block */
208 if (has_changed(observer)) {
209 dispatch_async(observer->queue, observer->block);
210 }
211 }
212 }
213
214 static void
215 _prefs_observer_init()
216 {
217 uint32_t status;
218 static int token;
219
220 prefs_observer_queue = dispatch_queue_create("com.apple.SystemConfiguration.SCPreferencesObserver", NULL);
221
222 SLIST_INIT(&head);
223
224 status = notify_register_dispatch(PREFS_OBSERVER_KEY,
225 &token,
226 prefs_observer_queue,
227 ^(int token) {
228 #pragma unused(token)
229 prefs_observer_handle_notifications();
230 });
231 if (status != NOTIFY_STATUS_OK) {
232 SC_log(LOG_INFO, "notify_register_dispatch() failed: %d", status);
233 }
234 }
235
236 static scprefs_observer_t
237 prefs_observer_priv_create(_scprefs_observer_type type,
238 const char *plist_name,
239 dispatch_queue_t queue,
240 dispatch_block_t block)
241 {
242 scprefs_observer_t observer;
243 size_t path_buflen;
244
245 path_buflen = strlen(plist_name) + 1;
246
247 observer = (scprefs_observer_t)malloc(sizeof(struct _scprefs_observer_t) + path_buflen);
248 memset((void *)observer, 0, sizeof(struct _scprefs_observer_t));
249
250 /* Create the observer */
251 observer->type = type;
252 strlcpy(observer->file, plist_name, path_buflen);
253
254 observer->queue = queue;
255 observer->block = Block_copy(block);
256
257 return (observer);
258 }
259
260 #pragma mark -
261 #pragma mark perfs_observer Public SPI
262 scprefs_observer_t
263 _scprefs_observer_watch(_scprefs_observer_type type, const char *plist_name,
264 dispatch_queue_t queue, dispatch_block_t block)
265 {
266 scprefs_observer_t elem;
267 static dispatch_once_t initialized;
268
269 dispatch_once(&initialized, ^{
270 _prefs_observer_init();
271 });
272
273 elem = prefs_observer_priv_create(type, plist_name, queue, block);
274 SC_log(LOG_INFO, "Created a new element to watch for %s", elem->file);
275
276 dispatch_sync(prefs_observer_queue, ^{
277 /* Enqueue the request */
278 SLIST_INSERT_HEAD(&head, elem, next);
279 });
280 return (elem);
281 }
282
283 /* This will cancel/deregister the given watcher. This will be synchronized on the
284 * internally created queue. */
285 void
286 _scprefs_observer_cancel(scprefs_observer_t observer)
287 {
288 dispatch_sync(prefs_observer_queue, ^{
289 prefs_observer_release((scprefs_observer_t)observer);
290 });
291 }
292
293 #pragma mark -
294
295 #ifdef TEST_MAIN
296 int main()
297 {
298 int random = 1;
299
300 _sc_verbose = 1;
301
302 dispatch_queue_t q = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.mainQ", NULL);
303
304 dispatch_queue_t q1 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ1", NULL);
305
306 dispatch_block_t b1 = ^{
307 printf("Block 1 executed\n");
308 };
309
310 dispatch_queue_t q2 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL);
311 dispatch_block_t b2 = ^{
312 printf("Block 2 executed\n");
313 };
314
315 dispatch_queue_t q3 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL);
316
317 dispatch_block_t b3 = ^{
318 printf("Block 3 executed\n");
319 };
320
321 __block scprefs_observer_t observer1 = _scprefs_observer_watch(scprefs_observer_type_mcx, "com.apple.SystemConfiguration", q1, b1);
322
323 __block scprefs_observer_t observer2 = _scprefs_observer_watch(scprefs_observer_type_mcx, "foo", q2, b2);
324
325 __block scprefs_observer_t observer3 = _scprefs_observer_watch(scprefs_observer_type_mcx, "bar", q3, b3);
326
327 __block scprefs_observer_t observer = NULL;
328
329 while (1) {
330 switch (random % 3)
331 {
332 case 0:
333 dispatch_async(q, ^{
334 _SC_prefs_observer_cancel(observer1);
335 observer1 = NULL;
336 });
337 dispatch_async(q, ^{
338 if (observer != NULL) _SC_prefs_observer_cancel(observer);
339 observer = _SC_prefs_observer_watch(SC_prefs_observer_type_mcx, "test", q2, b2);
340 });
341 dispatch_sync(q, ^{
342 observer1 = observer;
343 });
344 sleep(random);
345 break;
346 case 1:
347 dispatch_async(q, ^{
348 _SC_prefs_observer_cancel(observer2);
349 });
350 dispatch_async(q, ^{
351 if (observer != NULL) _SC_prefs_observer_cancel(observer);
352 });
353 dispatch_sync(q, ^{
354 observer = _SC_prefs_observer_watch(SC_prefs_observer_type_mcx, "test", q2, b2);
355 });
356 sleep(random);
357 break;
358 case 2:
359 sleep (random);
360 default:
361 break;
362 }
363 random++;
364 }
365 dispatch_main();
366 }
367 #endif