1/* 2 * Copyright (c) 2000-2005, 2011 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 24/* 25 * Modification History 26 * 27 * June 1, 2001 Allan Nathanson <ajn@apple.com> 28 * - public API conversion 29 * 30 * November 9, 2000 Allan Nathanson <ajn@apple.com> 31 * - initial revision 32 */ 33 34#include <sys/types.h> 35 36#include "scutil.h" 37#include "cache.h" 38 39 40#pragma mark - 41#pragma mark SCDynamicStore "cache" 42 43 44static Boolean use_cache = FALSE; 45 46static CFMutableDictionaryRef cached_keys = NULL; 47static CFMutableDictionaryRef cached_set = NULL; 48static CFMutableArrayRef cached_removals = NULL; 49static CFMutableArrayRef cached_notifys = NULL; 50 51 52static void 53cache_open(void) 54{ 55 if (use_cache) { 56 // if we are already using the cache 57 cache_close(); 58 } 59 60 cached_keys = CFDictionaryCreateMutable(NULL, 61 0, 62 &kCFTypeDictionaryKeyCallBacks, 63 &kCFTypeDictionaryValueCallBacks); 64 cached_set = CFDictionaryCreateMutable(NULL, 65 0, 66 &kCFTypeDictionaryKeyCallBacks, 67 &kCFTypeDictionaryValueCallBacks); 68 cached_removals = CFArrayCreateMutable(NULL, 69 0, 70 &kCFTypeArrayCallBacks); 71 cached_notifys = CFArrayCreateMutable(NULL, 72 0, 73 &kCFTypeArrayCallBacks); 74 75 use_cache = TRUE; 76 return; 77} 78 79 80static CFPropertyListRef 81cache_SCDynamicStoreCopyValue(SCDynamicStoreRef store, CFStringRef key) 82{ 83 CFPropertyListRef value; 84 85 value = CFDictionaryGetValue(cached_set, key); 86 if (value) { 87 // if we have "set" a new value 88 return (CFRetain(value)); 89 } 90 91 if (CFArrayContainsValue(cached_removals, 92 CFRangeMake(0, CFArrayGetCount(cached_removals)), 93 key)) { 94 // if we have "removed" the key 95 _SCErrorSet(kSCStatusNoKey); 96 return NULL; 97 } 98 99 value = CFDictionaryGetValue(cached_keys, key); 100 if (value) { 101 // if we have a cached value 102 return (CFRetain(value)); 103 } 104 105 value = SCDynamicStoreCopyValue(store, key); 106 if (value) { 107 CFDictionarySetValue(cached_keys, key, value); 108 } 109 110 return value; 111} 112 113 114static void 115cache_SCDynamicStoreSetValue(SCDynamicStoreRef store, CFStringRef key, CFPropertyListRef value) 116{ 117 CFIndex i; 118 119 i = CFArrayGetFirstIndexOfValue(cached_removals, 120 CFRangeMake(0, CFArrayGetCount(cached_removals)), 121 key); 122 if (i != kCFNotFound) { 123 // if previously "removed" 124 CFArrayRemoveValueAtIndex(cached_removals, i); 125 } 126 127 CFDictionarySetValue(cached_set, key, value); 128 129 return; 130} 131 132static void 133cache_SCDynamicStoreRemoveValue(SCDynamicStoreRef store, CFStringRef key) 134{ 135 CFDictionaryRemoveValue(cached_set, key); 136 137 if (!CFArrayContainsValue(cached_removals, 138 CFRangeMake(0, CFArrayGetCount(cached_removals)), 139 key)) { 140 CFArrayAppendValue(cached_removals, key); 141 } 142 143 return; 144} 145 146 147static void 148cache_SCDynamicStoreNotifyValue(SCDynamicStoreRef store, CFStringRef key) 149{ 150 if (!CFArrayContainsValue(cached_notifys, 151 CFRangeMake(0, CFArrayGetCount(cached_notifys)), 152 key)) { 153 CFArrayAppendValue(cached_notifys, key); 154 } 155 156 return; 157} 158 159 160static void 161cache_write(SCDynamicStoreRef store) 162{ 163 if ((CFDictionaryGetCount(cached_set) > 0) || 164 (CFArrayGetCount(cached_removals) > 0) || 165 (CFArrayGetCount(cached_notifys) > 0)) { 166 if (!SCDynamicStoreSetMultiple(store, 167 cached_set, 168 cached_removals, 169 cached_notifys)) { 170 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 171 } 172 } 173 174 return; 175} 176 177 178__private_extern__ 179void 180cache_close(void) 181{ 182 if (!use_cache) { 183 return; 184 } 185 186 CFRelease(cached_keys); 187 CFRelease(cached_set); 188 CFRelease(cached_removals); 189 CFRelease(cached_notifys); 190 191 use_cache = FALSE; 192 return; 193} 194 195 196#pragma mark - 197#pragma mark SCDynamicStore operations 198 199 200__private_extern__ 201void 202do_block(int argc, char **argv) 203{ 204 Boolean enable = FALSE; 205 206 if (argc >= 1) { 207 if ((strcasecmp(argv[0], "begin") == 0) || 208 (strcasecmp(argv[0], "start") == 0) || 209 (strcasecmp(argv[0], "on" ) == 0) || 210 (strcasecmp(argv[0], "1" ) == 0)) { 211 enable = TRUE; 212 } else if ((strcasecmp(argv[0], "end" ) == 0) || 213 (strcasecmp(argv[0], "stop" ) == 0) || 214 (strcasecmp(argv[0], "off" ) == 0) || 215 (strcasecmp(argv[0], "0" ) == 0)) { 216 enable = FALSE; 217 } else { 218 SCPrint(TRUE, stdout, CFSTR("invalid value\n")); 219 return; 220 } 221 } else { 222 enable = !use_cache; // toggle 223 } 224 225 if (enable) { 226 // begin block of SCDynamicStore operations 227 if (use_cache) { 228 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusLocked)); 229 return; 230 } 231 232 SCPrint(TRUE, stdout, CFSTR("Begin block of SCDynamicStore operations\n")); 233 234 cache_open(); 235 } else { 236 CFIndex n; 237 238 // end block of SCDynamicStore operations 239 if (!use_cache) { 240 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNeedLock)); 241 return; 242 } 243 244 n = CFDictionaryGetCount(cached_keys) + 245 CFArrayGetCount(cached_removals) + 246 CFArrayGetCount(cached_notifys); 247 SCPrint(TRUE, stdout, 248 CFSTR("End block of SCDynamicStore operations%s\n"), 249 (n > 0) ? ", posting changes" : ""); 250 if (n > 0) { 251 cache_write(store); 252 } 253 cache_close(); 254 } 255 256 return; 257} 258 259 260static CFComparisonResult 261sort_keys(const void *p1, const void *p2, void *context) { 262 CFStringRef key1 = (CFStringRef)p1; 263 CFStringRef key2 = (CFStringRef)p2; 264 return CFStringCompare(key1, key2, 0); 265} 266 267 268#define N_QUICK 64 269 270 271__private_extern__ 272void 273do_list(int argc, char **argv) 274{ 275 int i; 276 CFStringRef pattern; 277 CFArrayRef list; 278 CFIndex listCnt; 279 CFMutableArrayRef sortedList; 280 281 pattern = CFStringCreateWithCString(NULL, 282 (argc >= 1) ? argv[0] : ".*", 283 kCFStringEncodingUTF8); 284 285 list = SCDynamicStoreCopyKeyList(store, pattern); 286 CFRelease(pattern); 287 if (list == NULL) { 288 if (SCError() != kSCStatusOK) { 289 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 290 return; 291 } else { 292 if (!use_cache) { 293 SCPrint(TRUE, stdout, CFSTR(" no keys.\n")); 294 return; 295 } else { 296 CFIndex n; 297 298 n = CFDictionaryGetCount(cached_set); 299 if (n > 0) { 300 const void * cachedKeys_q[N_QUICK]; 301 const void ** cachedKeys = cachedKeys_q; 302 303 if (n > (CFIndex)(sizeof(cachedKeys_q) / sizeof(CFStringRef))) { 304 cachedKeys = CFAllocatorAllocate(NULL, n * sizeof(CFStringRef), 0); 305 } 306 CFDictionaryGetKeysAndValues(cached_set, cachedKeys, NULL); 307 list = CFArrayCreate(NULL, cachedKeys, n, &kCFTypeArrayCallBacks); 308 if (cachedKeys != cachedKeys_q) { 309 CFAllocatorDeallocate(NULL, cachedKeys); 310 } 311 } else { 312 SCPrint(TRUE, stdout, CFSTR(" no keys.\n")); 313 return; 314 } 315 } 316 } 317 } else if (use_cache && 318 ((CFDictionaryGetCount(cached_set) > 0) || (CFArrayGetCount(cached_removals) > 0))) { 319 SCPrint(TRUE, stdout, 320 CFSTR(" Note: SCDynamicStore transactions in progress, key list (below) may be out of date.\n\n")); 321 } 322 323 listCnt = CFArrayGetCount(list); 324 sortedList = CFArrayCreateMutableCopy(NULL, listCnt, list); 325 CFRelease(list); 326 CFArraySortValues(sortedList, 327 CFRangeMake(0, listCnt), 328 sort_keys, 329 NULL); 330 331 if (listCnt > 0) { 332 for (i = 0; i < listCnt; i++) { 333 SCPrint(TRUE, 334 stdout, 335 CFSTR(" subKey [%d] = %@\n"), 336 i, 337 CFArrayGetValueAtIndex(sortedList, i)); 338 } 339 } else { 340 SCPrint(TRUE, stdout, CFSTR(" no keys.\n")); 341 } 342 CFRelease(sortedList); 343 344 return; 345} 346 347 348__private_extern__ 349void 350do_add(int argc, char **argv) 351{ 352 CFStringRef key; 353 354 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 355 356 if (argc < 2) { 357 if (!use_cache) { 358 if (!SCDynamicStoreAddValue(store, key, value)) { 359 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 360 } 361 } else { 362 CFTypeRef val; 363 364 val = cache_SCDynamicStoreCopyValue(store, key); 365 if (val != NULL) { 366 CFRelease(val); 367 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists)); 368 } else { 369 cache_SCDynamicStoreSetValue(store, key, value); 370 } 371 } 372 } else { 373 if (!use_cache) { 374 if (!SCDynamicStoreAddTemporaryValue(store, key, value)) { 375 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 376 } 377 } else { 378 CFTypeRef val; 379 380 val = cache_SCDynamicStoreCopyValue(store, key); 381 if (val != NULL) { 382 CFRelease(val); 383 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists)); 384 } else { 385 if (!SCDynamicStoreAddTemporaryValue(store, key, value)) { 386 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 387 } else { 388 // and save the temp value in the cache too! 389 cache_SCDynamicStoreSetValue(store, key, value); 390 } 391 } 392 } 393 } 394 395 CFRelease(key); 396 return; 397} 398 399 400__private_extern__ 401void 402do_get(int argc, char **argv) 403{ 404 CFStringRef key; 405 CFPropertyListRef newValue; 406 407 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 408 if (!use_cache) { 409 newValue = SCDynamicStoreCopyValue(store, key); 410 } else { 411 newValue = cache_SCDynamicStoreCopyValue(store, key); 412 } 413 CFRelease(key); 414 if (newValue == NULL) { 415 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 416 return; 417 } 418 419 if (value != NULL) { 420 CFRelease(value); /* we have new information, release the old */ 421 } 422 value = newValue; 423 424 return; 425} 426 427 428__private_extern__ 429void 430do_set(int argc, char **argv) 431{ 432 CFStringRef key; 433 434 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 435 if (!use_cache) { 436 if (!SCDynamicStoreSetValue(store, key, value)) { 437 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 438 } 439 } else { 440 cache_SCDynamicStoreSetValue(store, key, value); 441 } 442 CFRelease(key); 443 return; 444} 445 446 447__private_extern__ 448void 449do_show(int argc, char **argv) 450{ 451 CFStringRef key; 452 CFPropertyListRef newValue; 453 454 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 455 456 if (argc == 1) { 457 if (!use_cache) { 458 newValue = SCDynamicStoreCopyValue(store, key); 459 } else { 460 newValue = cache_SCDynamicStoreCopyValue(store, key); 461 } 462 } else { 463 CFArrayRef patterns; 464 465 patterns = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 466 if (!use_cache) { 467 newValue = SCDynamicStoreCopyMultiple(store, NULL, patterns); 468 } else { 469 CFArrayRef keys; 470 CFMutableDictionaryRef newDict; 471 472 newDict = CFDictionaryCreateMutable(NULL, 473 0, 474 &kCFTypeDictionaryKeyCallBacks, 475 &kCFTypeDictionaryValueCallBacks); 476 keys = SCDynamicStoreCopyKeyList(store, key); 477 if (keys != NULL) { 478 CFIndex i; 479 CFIndex n; 480 481 n = CFArrayGetCount(keys); 482 for (i = 0; i < n; i++) { 483 CFStringRef storeKey; 484 CFTypeRef storeVal; 485 486 storeKey = CFArrayGetValueAtIndex(keys, i); 487 storeVal = cache_SCDynamicStoreCopyValue(store, storeKey); 488 if (storeVal != NULL) { 489 CFDictionarySetValue(newDict, storeKey, storeVal); 490 CFRelease(storeVal); 491 } 492 } 493 CFRelease(keys); 494 } 495 496 if ((CFDictionaryGetCount(cached_set) > 0) || (CFArrayGetCount(cached_removals) > 0)) { 497 SCPrint(TRUE, stdout, CFSTR(" Note: SCDynamicStore locked, keys included (below) may be out of date.\n\n")); 498 } 499 500 newValue = newDict; 501 } 502 CFRelease(patterns); 503 } 504 505 CFRelease(key); 506 if (newValue == NULL) { 507 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 508 return; 509 } 510 511 SCPrint(TRUE, stdout, CFSTR("%@\n"), newValue); 512 CFRelease(newValue); 513 return; 514} 515 516 517__private_extern__ 518void 519do_remove(int argc, char **argv) 520{ 521 CFStringRef key; 522 523 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 524 if (!use_cache) { 525 if (!SCDynamicStoreRemoveValue(store, key)) { 526 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 527 } 528 } else { 529 cache_SCDynamicStoreRemoveValue(store, key); 530 } 531 CFRelease(key); 532 return; 533} 534 535 536__private_extern__ 537void 538do_notify(int argc, char **argv) 539{ 540 CFStringRef key; 541 542 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); 543 if (!use_cache) { 544 if (!SCDynamicStoreNotifyValue(store, key)) { 545 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 546 } 547 } else { 548 cache_SCDynamicStoreNotifyValue(store, key); 549 } 550 CFRelease(key); 551 return; 552} 553