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