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