]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/scprefs_observer.c
configd-1109.40.9.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / scprefs_observer.c
1 /*
2 * Copyright (c) 2012, 2013, 2015-2019 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 #if !TARGET_OS_IPHONE
41 #define MANAGED_PREFERENCES_PATH "/Library/Managed Preferences"
42 #define PREFS_OBSERVER_KEY "com.apple.MCX._managementStatusChangedForDomains" // posted by CF (OK for macOS, iOS)
43 #else // !TARGET_OS_IPHONE
44 #define MANAGED_PREFERENCES_MOBILE_PATH "/Library/Managed Preferences/mobile"
45 #define PREFS_OBSERVER_KEY "com.apple.ManagedConfiguration.profileListChanged" // posted by ManagedConfiguration
46 #endif // !TARGET_OS_IPHONE
47
48 #pragma mark -
49 #pragma mark Utils
50
51 #if !TARGET_OS_IPHONE
52 static void
53 iterate_dir(const char *d_name, const char *f_name,
54 CC_SHA256_CTX *ctxP, Boolean *found)
55 {
56 DIR *dir;
57 struct dirent * dp;
58
59 dir = opendir(d_name);
60
61 if (dir == NULL) {
62 /* if directory path does not exist */
63 return;
64 }
65
66 while ((dp = readdir(dir)) != NULL) {
67 char full_path[MAXPATHLEN];
68 struct stat s;
69
70 if ((strcmp(dp->d_name, ".") == 0) ||
71 (strcmp(dp->d_name, "..") == 0)) {
72 continue;
73 }
74
75 /* check path */
76 snprintf(full_path, sizeof(full_path), "%s/%s", d_name, dp->d_name);
77 if (stat(full_path, &s) == 0) {
78 if (S_ISDIR(s.st_mode)) {
79 // if sub-directory, iterate
80 iterate_dir(full_path, f_name, ctxP, found);
81 } else if (strcmp(f_name, dp->d_name) == 0) {
82 /*
83 * if this is the requested file, include
84 * the path and last modification time in
85 * the digest
86 */
87 CC_SHA256_Update(ctxP, full_path, (CC_LONG)strlen(full_path));
88 CC_SHA256_Update(ctxP,
89 (void *)&s.st_mtimespec,
90 sizeof(s.st_mtimespec));
91 *found = TRUE;
92 }
93 }
94 }
95 closedir(dir);
96 return;
97 }
98 #else // !TARGET_OS_IPHONE
99 static void
100 observe_plist(const char *d_name, const char *f_name,
101 CC_SHA256_CTX *ctxP, Boolean *found)
102 {
103 char full_path[MAXPATHLEN];
104 struct stat s;
105
106 snprintf(full_path, sizeof(full_path), "%s/%s", d_name, f_name);
107 if ((stat(full_path, &s) == 0) && S_ISREG(s.st_mode)) {
108 CC_SHA256_Update(ctxP, full_path, (CC_LONG)strlen(full_path));
109 CC_SHA256_Update(ctxP,
110 (void *)&s.st_mtimespec,
111 sizeof(s.st_mtimespec));
112 *found = TRUE;
113 }
114
115 return;
116 }
117 #endif // !TARGET_OS_IPHONE
118
119 static CF_RETURNS_RETAINED CFDataRef
120 build_digest(const char *top_dir, const char *file)
121 {
122 unsigned char bytes[CC_SHA256_DIGEST_LENGTH];
123 CC_SHA256_CTX ctx;
124 CFDataRef digest = NULL;
125 Boolean found = FALSE;
126
127 CC_SHA256_Init(&ctx);
128 #if !TARGET_OS_IPHONE
129 iterate_dir(top_dir, file, &ctx, &found);
130 #else // !TARGET_OS_IPHONE
131 observe_plist(top_dir, file, &ctx, &found);
132 #endif // !TARGET_OS_IPHONE
133 CC_SHA256_Final(bytes, &ctx);
134 if (found) {
135 digest = CFDataCreate(NULL, bytes, sizeof(bytes));
136 }
137 return (digest);
138 }
139
140 #pragma mark -
141 #pragma mark perfs_observer Private
142
143 struct _scprefs_observer_t {
144 _scprefs_observer_type type;
145 dispatch_block_t block;
146 CFDataRef digest;
147 SLIST_ENTRY(_scprefs_observer_t) next;
148 dispatch_queue_t queue;
149 char file[0];
150 };
151
152 static const char *
153 prefs_observer_get_prefs_path(scprefs_observer_t observer)
154 {
155 switch (observer->type) {
156 #if !TARGET_OS_IPHONE
157 case scprefs_observer_type_mcx:
158 return MANAGED_PREFERENCES_PATH;
159 #else // !TARGET_OS_IPHONE
160 case scprefs_observer_type_global:
161 return MANAGED_PREFERENCES_MOBILE_PATH;
162 #endif // !TARGET_OS_IPHONE
163 default:
164 return (NULL);
165 }
166 }
167
168 /*
169 * Iterate through all of the directories and subdirectories.
170 * If the file within those directories has changed,
171 * then generate the notification.
172 */
173 static Boolean
174 has_changed(scprefs_observer_t observer) {
175 Boolean changed;
176 CFDataRef digest = NULL;
177 const char * starting_path;
178
179 starting_path = prefs_observer_get_prefs_path(observer);
180
181 digest = build_digest(starting_path, observer->file);
182
183 /* compare old vs. new digest */
184 changed = _SC_CFEqual(digest, observer->digest)?FALSE:TRUE;
185
186 /* save the digest */
187 if (observer->digest != NULL) {
188 CFRelease(observer->digest);
189 }
190
191 observer->digest = digest;
192
193 SC_log(changed ? LOG_INFO : LOG_DEBUG,
194 "preferences file: \"%s\" %s",
195 observer->file,
196 changed ? "changed" : "did not change");
197 return changed;
198 }
199
200 static dispatch_queue_t
201 prefs_observer_queue;
202
203 /* This holds the list of the observers */
204 static SLIST_HEAD(mylist, _scprefs_observer_t) head;
205
206 static void
207 prefs_observer_release(scprefs_observer_t observer)
208 {
209 SLIST_REMOVE(&head, observer, _scprefs_observer_t, next);
210
211 /* Now free the observer */
212 if (observer->digest != NULL) {
213 CFRelease(observer->digest);
214 }
215
216 free(observer);
217 }
218
219 static void
220 prefs_observer_handle_notifications()
221 {
222 scprefs_observer_t observer;
223
224 SC_log(LOG_DEBUG, "PrefsObserver notification received");
225
226 SLIST_FOREACH(observer, &head, next) {
227 /* if the preferences plist changed, call the block */
228 if (has_changed(observer)) {
229 dispatch_async(observer->queue, observer->block);
230 }
231 }
232 }
233
234 static void
235 _prefs_observer_init()
236 {
237 uint32_t status;
238 static int token;
239
240 prefs_observer_queue = dispatch_queue_create("com.apple.SystemConfiguration.SCPreferencesObserver", NULL);
241
242 SLIST_INIT(&head);
243
244 status = notify_register_dispatch(PREFS_OBSERVER_KEY,
245 &token,
246 prefs_observer_queue,
247 ^(int token) {
248 #pragma unused(token)
249 prefs_observer_handle_notifications();
250 });
251 if (status != NOTIFY_STATUS_OK) {
252 SC_log(LOG_INFO, "notify_register_dispatch() failed: %d", status);
253 }
254 }
255
256 static scprefs_observer_t
257 prefs_observer_priv_create(_scprefs_observer_type type,
258 const char *plist_name,
259 dispatch_queue_t queue,
260 dispatch_block_t block)
261 {
262 scprefs_observer_t observer;
263 size_t path_buflen;
264
265 path_buflen = strlen(plist_name) + 1;
266
267 observer = (scprefs_observer_t)malloc(sizeof(struct _scprefs_observer_t) + path_buflen);
268 memset((void *)observer, 0, sizeof(struct _scprefs_observer_t));
269
270 /* Create the observer */
271 observer->type = type;
272 strlcpy(observer->file, plist_name, path_buflen);
273
274 observer->queue = queue;
275 observer->block = Block_copy(block);
276
277 return (observer);
278 }
279
280 #pragma mark -
281 #pragma mark perfs_observer Public SPI
282 scprefs_observer_t
283 _scprefs_observer_watch(_scprefs_observer_type type, const char *plist_name,
284 dispatch_queue_t queue, dispatch_block_t block)
285 {
286 scprefs_observer_t elem;
287 static dispatch_once_t initialized;
288
289 dispatch_once(&initialized, ^{
290 _prefs_observer_init();
291 });
292
293 elem = prefs_observer_priv_create(type, plist_name, queue, block);
294 SC_log(LOG_INFO, "Created a new element to watch for %s", elem->file);
295
296 dispatch_sync(prefs_observer_queue, ^{
297 /* Enqueue the request */
298 SLIST_INSERT_HEAD(&head, elem, next);
299 });
300 return (elem);
301 }
302
303 /* This will cancel/deregister the given watcher. This will be synchronized on the
304 * internally created queue. */
305 void
306 _scprefs_observer_cancel(scprefs_observer_t observer)
307 {
308 dispatch_sync(prefs_observer_queue, ^{
309 prefs_observer_release((scprefs_observer_t)observer);
310 });
311 }
312
313 #pragma mark -
314
315 #ifdef TEST_MAIN
316 int main()
317 {
318 int random = 1;
319
320 _sc_verbose = 1;
321
322 dispatch_queue_t q = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.mainQ", NULL);
323
324 dispatch_queue_t q1 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ1", NULL);
325
326 dispatch_block_t b1 = ^{
327 printf("Block 1 executed\n");
328 };
329
330 dispatch_queue_t q2 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL);
331 dispatch_block_t b2 = ^{
332 printf("Block 2 executed\n");
333 };
334
335 dispatch_queue_t q3 = dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL);
336
337 dispatch_block_t b3 = ^{
338 printf("Block 3 executed\n");
339 };
340
341 __block scprefs_observer_t observer1 = _scprefs_observer_watch(scprefs_observer_type_mcx, "com.apple.SystemConfiguration", q1, b1);
342
343 __block scprefs_observer_t observer2 = _scprefs_observer_watch(scprefs_observer_type_mcx, "foo", q2, b2);
344
345 __block scprefs_observer_t observer3 = _scprefs_observer_watch(scprefs_observer_type_mcx, "bar", q3, b3);
346
347 __block scprefs_observer_t observer = NULL;
348
349 while (1) {
350 switch (random % 3)
351 {
352 case 0:
353 dispatch_async(q, ^{
354 _SC_prefs_observer_cancel(observer1);
355 observer1 = NULL;
356 });
357 dispatch_async(q, ^{
358 if (observer != NULL) _SC_prefs_observer_cancel(observer);
359 observer = _SC_prefs_observer_watch(SC_prefs_observer_type_mcx, "test", q2, b2);
360 });
361 dispatch_sync(q, ^{
362 observer1 = observer;
363 });
364 sleep(random);
365 break;
366 case 1:
367 dispatch_async(q, ^{
368 _SC_prefs_observer_cancel(observer2);
369 });
370 dispatch_async(q, ^{
371 if (observer != NULL) _SC_prefs_observer_cancel(observer);
372 });
373 dispatch_sync(q, ^{
374 observer = _SC_prefs_observer_watch(SC_prefs_observer_type_mcx, "test", q2, b2);
375 });
376 sleep(random);
377 break;
378 case 2:
379 sleep (random);
380 default:
381 break;
382 }
383 random++;
384 }
385 dispatch_main();
386 }
387 #endif