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 36static void 37iterate_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 83static CF_RETURNS_RETAINED CFDataRef 84build_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 103struct _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" 113static const char * 114prefs_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 */ 136static Boolean 137has_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 161static dispatch_queue_t 162prefs_observer_queue; 163 164/* This holds the list of the observers */ 165static SLIST_HEAD(mylist, _scprefs_observer_t) head; 166 167static void 168prefs_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 180static void 181prefs_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" 197static 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 212static scprefs_observer_t 213prefs_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 238scprefs_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. */ 262void 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 273int 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 345