2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 #include <CommonCrypto/CommonDigest.h>
26 #include <sys/param.h>
27 #include <sys/queue.h>
28 #include <sys/types.h>
30 #include <SystemConfiguration/SCPrivate.h>
31 #include <SystemConfiguration/scprefs_observer.h>
37 iterate_dir(const char *d_name
, const char *f_name
,
38 CC_SHA1_CTX
*ctxP
, Boolean
*found
)
43 dir
= opendir(d_name
);
46 /* if directory path does not exist */
50 while ((dp
= readdir(dir
)) != NULL
) {
51 char full_path
[MAXPATHLEN
];
54 if ((strcmp(dp
->d_name
, ".") == 0) ||
55 (strcmp(dp
->d_name
, "..") == 0)) {
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) {
67 * if this is the requested file, include
68 * the path and last modification time in
71 CC_SHA1_Update(ctxP
, full_path
, (CC_LONG
)strlen(full_path
));
73 (void *)&s
.st_mtimespec
.tv_sec
,
74 sizeof(s
.st_mtimespec
.tv_sec
));
83 static CF_RETURNS_RETAINED CFDataRef
84 build_digest(const char *top_dir
, const char *file
)
86 unsigned char bytes
[CC_SHA1_DIGEST_LENGTH
];
88 CFDataRef digest
= NULL
;
89 Boolean found
= FALSE
;
92 iterate_dir(top_dir
, file
, &ctx
, &found
);
93 CC_SHA1_Final(bytes
, &ctx
);
95 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
101 #pragma mark perfs_observer Private
103 struct _scprefs_observer_t
{
104 _scprefs_observer_type type
;
105 dispatch_block_t block
;
107 SLIST_ENTRY(_scprefs_observer_t
) next
;
108 dispatch_queue_t queue
;
112 #define MOBILE_PREFERENCES_PATH "/var/mobile/Library/Preferences"
114 prefs_observer_get_prefs_path(scprefs_observer_t observer
)
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
132 * Iterate through all of the directories and subdirectories.
133 * If the file within those directories has changed,
134 * then generate the notification.
137 has_changed(scprefs_observer_t observer
) {
139 CFDataRef digest
= NULL
;
140 const char * starting_path
;
142 starting_path
= prefs_observer_get_prefs_path(observer
);
144 digest
= build_digest(starting_path
, observer
->file
);
146 /* compare old vs. new digest */
147 changed
= _SC_CFEqual(digest
, observer
->digest
)?FALSE
:TRUE
;
149 /* save the digest */
150 if (observer
->digest
!= NULL
) {
151 CFRelease(observer
->digest
);
154 observer
->digest
= digest
;
156 SCLog(_sc_verbose
, LOG_NOTICE
, CFSTR("The following file: %s, %s \n"),
157 observer
->file
, (changed
)?"has changed":"has not changed");
161 static dispatch_queue_t
162 prefs_observer_queue
;
164 /* This holds the list of the observers */
165 static SLIST_HEAD(mylist
, _scprefs_observer_t
) head
;
168 prefs_observer_release(scprefs_observer_t observer
)
170 SLIST_REMOVE(&head
, observer
, _scprefs_observer_t
, next
);
172 /* Now free the observer */
173 if (observer
->digest
!= NULL
) {
174 CFRelease(observer
->digest
);
181 prefs_observer_handle_notifications()
183 scprefs_observer_t observer
;
185 SCLog(_sc_verbose
, LOG_NOTICE
, CFSTR("PrefsObserver Notification received \n"));
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
);
196 #define PREFS_OBSERVER_KEY "com.apple.ManagedConfiguration.profileListChanged"
198 _prefs_observer_init()
202 prefs_observer_queue
= dispatch_queue_create("com.apple.SystemConfiguration.SCPreferencesObserver", NULL
);
206 notify_register_dispatch(PREFS_OBSERVER_KEY
,
208 prefs_observer_queue
,
209 ^(int token
) { prefs_observer_handle_notifications(); });
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
)
218 scprefs_observer_t observer
;
221 path_buflen
= strlen(plist_name
) + 1;
223 observer
= (scprefs_observer_t
)malloc(sizeof(struct _scprefs_observer_t
) + path_buflen
);
224 bzero((void *)observer
, sizeof(struct _scprefs_observer_t
));
226 /* Create the observer */
227 observer
->type
= type
;
228 strlcpy(observer
->file
, plist_name
, path_buflen
);
230 observer
->queue
= queue
;
231 observer
->block
= Block_copy(block
);
237 #pragma mark perfs_observer Public SPI
239 _scprefs_observer_watch(_scprefs_observer_type type
, const char *plist_name
,
240 dispatch_queue_t queue
, dispatch_block_t block
)
242 scprefs_observer_t elem
;
243 static dispatch_once_t initialized
;
245 dispatch_once(&initialized
, ^{
246 _prefs_observer_init();
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"),
253 dispatch_sync(prefs_observer_queue
, ^{
254 /* Enqueue the request */
255 SLIST_INSERT_HEAD(&head
, elem
, next
);
260 /* This will cancel/deregister the given watcher. This will be synchronized on the
261 * internally created queue. */
263 _scprefs_observer_cancel(scprefs_observer_t observer
)
265 dispatch_sync(prefs_observer_queue
, ^{
266 prefs_observer_release((scprefs_observer_t
)observer
);
279 dispatch_queue_t q
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.mainQ", NULL
);
281 dispatch_queue_t q1
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ1", NULL
);
283 dispatch_block_t b1
= ^{
284 printf("Block 1 executed \n");
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");
292 dispatch_queue_t q3
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL
);
294 dispatch_block_t b3
= ^{
295 printf("Block 3 executed \n");
298 __block scprefs_observer_t observer1
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "com.apple.SystemConfiguration", q1
, b1
);
300 __block scprefs_observer_t observer2
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "foo", q2
, b2
);
302 __block scprefs_observer_t observer3
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "bar", q3
, b3
);
304 __block scprefs_observer_t observer
= NULL
;
311 _SC_prefs_observer_cancel(observer1
);
315 if (observer
!= NULL
) _SC_prefs_observer_cancel(observer
);
316 observer
= _SC_prefs_observer_watch(SC_prefs_observer_type_mcx
, "test", q2
, b2
);
319 observer1
= observer
;
325 _SC_prefs_observer_cancel(observer2
);
328 if (observer
!= NULL
) _SC_prefs_observer_cancel(observer
);
331 observer
= _SC_prefs_observer_watch(SC_prefs_observer_type_mcx
, "test", q2
, b2
);