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