2 * Copyright (c) 2012, 2013, 2015-2019 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>
27 #include <sys/param.h>
28 #include <sys/queue.h>
29 #include <sys/types.h>
33 #define SC_LOG_HANDLE __log_SCPreferences
34 #endif //SC_LOG_HANDLE
35 os_log_t
SC_LOG_HANDLE(void);
37 #include <SystemConfiguration/SCPrivate.h>
38 #include <SystemConfiguration/scprefs_observer.h>
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
53 iterate_dir(const char *d_name
, const char *f_name
,
54 CC_SHA256_CTX
*ctxP
, Boolean
*found
)
59 dir
= opendir(d_name
);
62 /* if directory path does not exist */
66 while ((dp
= readdir(dir
)) != NULL
) {
67 char full_path
[MAXPATHLEN
];
70 if ((strcmp(dp
->d_name
, ".") == 0) ||
71 (strcmp(dp
->d_name
, "..") == 0)) {
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) {
83 * if this is the requested file, include
84 * the path and last modification time in
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
));
98 #else // !TARGET_OS_IPHONE
100 observe_plist(const char *d_name
, const char *f_name
,
101 CC_SHA256_CTX
*ctxP
, Boolean
*found
)
103 char full_path
[MAXPATHLEN
];
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
));
117 #endif // !TARGET_OS_IPHONE
119 static CF_RETURNS_RETAINED CFDataRef
120 build_digest(const char *top_dir
, const char *file
)
122 unsigned char bytes
[CC_SHA256_DIGEST_LENGTH
];
124 CFDataRef digest
= NULL
;
125 Boolean found
= FALSE
;
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
);
135 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
141 #pragma mark perfs_observer Private
143 struct _scprefs_observer_t
{
144 _scprefs_observer_type type
;
145 dispatch_block_t block
;
147 SLIST_ENTRY(_scprefs_observer_t
) next
;
148 dispatch_queue_t queue
;
153 prefs_observer_get_prefs_path(scprefs_observer_t observer
)
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
169 * Iterate through all of the directories and subdirectories.
170 * If the file within those directories has changed,
171 * then generate the notification.
174 has_changed(scprefs_observer_t observer
) {
176 CFDataRef digest
= NULL
;
177 const char * starting_path
;
179 starting_path
= prefs_observer_get_prefs_path(observer
);
181 digest
= build_digest(starting_path
, observer
->file
);
183 /* compare old vs. new digest */
184 changed
= _SC_CFEqual(digest
, observer
->digest
)?FALSE
:TRUE
;
186 /* save the digest */
187 if (observer
->digest
!= NULL
) {
188 CFRelease(observer
->digest
);
191 observer
->digest
= digest
;
193 SC_log(changed
? LOG_INFO
: LOG_DEBUG
,
194 "preferences file: \"%s\" %s",
196 changed
? "changed" : "did not change");
200 static dispatch_queue_t
201 prefs_observer_queue
;
203 /* This holds the list of the observers */
204 static SLIST_HEAD(mylist
, _scprefs_observer_t
) head
;
207 prefs_observer_release(scprefs_observer_t observer
)
209 SLIST_REMOVE(&head
, observer
, _scprefs_observer_t
, next
);
211 /* Now free the observer */
212 if (observer
->digest
!= NULL
) {
213 CFRelease(observer
->digest
);
220 prefs_observer_handle_notifications()
222 scprefs_observer_t observer
;
224 SC_log(LOG_DEBUG
, "PrefsObserver notification received");
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
);
235 _prefs_observer_init()
240 prefs_observer_queue
= dispatch_queue_create("com.apple.SystemConfiguration.SCPreferencesObserver", NULL
);
244 status
= notify_register_dispatch(PREFS_OBSERVER_KEY
,
246 prefs_observer_queue
,
248 #pragma unused(token)
249 prefs_observer_handle_notifications();
251 if (status
!= NOTIFY_STATUS_OK
) {
252 SC_log(LOG_INFO
, "notify_register_dispatch() failed: %d", status
);
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
)
262 scprefs_observer_t observer
;
265 path_buflen
= strlen(plist_name
) + 1;
267 observer
= (scprefs_observer_t
)malloc(sizeof(struct _scprefs_observer_t
) + path_buflen
);
268 memset((void *)observer
, 0, sizeof(struct _scprefs_observer_t
));
270 /* Create the observer */
271 observer
->type
= type
;
272 strlcpy(observer
->file
, plist_name
, path_buflen
);
274 observer
->queue
= queue
;
275 observer
->block
= Block_copy(block
);
281 #pragma mark perfs_observer Public SPI
283 _scprefs_observer_watch(_scprefs_observer_type type
, const char *plist_name
,
284 dispatch_queue_t queue
, dispatch_block_t block
)
286 scprefs_observer_t elem
;
287 static dispatch_once_t initialized
;
289 dispatch_once(&initialized
, ^{
290 _prefs_observer_init();
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
);
296 dispatch_sync(prefs_observer_queue
, ^{
297 /* Enqueue the request */
298 SLIST_INSERT_HEAD(&head
, elem
, next
);
303 /* This will cancel/deregister the given watcher. This will be synchronized on the
304 * internally created queue. */
306 _scprefs_observer_cancel(scprefs_observer_t observer
)
308 dispatch_sync(prefs_observer_queue
, ^{
309 prefs_observer_release((scprefs_observer_t
)observer
);
322 dispatch_queue_t q
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.mainQ", NULL
);
324 dispatch_queue_t q1
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ1", NULL
);
326 dispatch_block_t b1
= ^{
327 printf("Block 1 executed\n");
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");
335 dispatch_queue_t q3
= dispatch_queue_create("com.apple.SystemConfiguration.PrefsObserver.testQ2", NULL
);
337 dispatch_block_t b3
= ^{
338 printf("Block 3 executed\n");
341 __block scprefs_observer_t observer1
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "com.apple.SystemConfiguration", q1
, b1
);
343 __block scprefs_observer_t observer2
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "foo", q2
, b2
);
345 __block scprefs_observer_t observer3
= _scprefs_observer_watch(scprefs_observer_type_mcx
, "bar", q3
, b3
);
347 __block scprefs_observer_t observer
= NULL
;
354 _SC_prefs_observer_cancel(observer1
);
358 if (observer
!= NULL
) _SC_prefs_observer_cancel(observer
);
359 observer
= _SC_prefs_observer_watch(SC_prefs_observer_type_mcx
, "test", q2
, b2
);
362 observer1
= observer
;
368 _SC_prefs_observer_cancel(observer2
);
371 if (observer
!= NULL
) _SC_prefs_observer_cancel(observer
);
374 observer
= _SC_prefs_observer_watch(SC_prefs_observer_type_mcx
, "test", q2
, b2
);