1/*
2 * Copyright (c) 2000-2004, 2006, 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 * March 24, 2000		Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34
35#include "configd.h"
36#include "configd_server.h"
37#include "session.h"
38#include "pattern.h"
39
40
41#define N_QUICK	32
42
43
44static void
45_notifyWatchers()
46{
47	CFIndex			keyCnt;
48	const void *		keys_q[N_QUICK];
49	const void **		keys	= keys_q;
50
51	keyCnt = CFSetGetCount(changedKeys);
52	if (keyCnt == 0)
53		return;		/* if nothing to do */
54
55	if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef)))
56		keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
57
58	CFSetGetValues(changedKeys, keys);
59
60	while (--keyCnt >= 0) {
61		CFArrayRef		changes;
62		CFDictionaryRef		dict;
63		CFDictionaryRef		info;
64		CFMutableDictionaryRef	newInfo;
65		CFMutableArrayRef	newChanges;
66		CFArrayRef		sessionsWatchingKey;
67		CFIndex			watcherCnt;
68		const void *		watchers_q[N_QUICK];
69		const void **		watchers	= watchers_q;
70
71		dict = CFDictionaryGetValue(storeData, (CFStringRef)keys[keyCnt]);
72		if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) {
73			/* key doesn't exist or nobody cares if it changed */
74			continue;
75		}
76
77		/*
78		 * Add this key to the list of changes for each of the
79		 * sessions which is "watching".
80		 */
81		sessionsWatchingKey = CFDictionaryGetValue(dict, kSCDWatchers);
82		watcherCnt = CFArrayGetCount(sessionsWatchingKey);
83		if (watcherCnt == 0) {
84			/* if no watchers */
85			continue;
86		}
87
88		if (watcherCnt > (CFIndex)(sizeof(watchers_q) / sizeof(CFNumberRef)))
89			watchers = CFAllocatorAllocate(NULL, watcherCnt * sizeof(CFNumberRef), 0);
90
91		CFArrayGetValues(sessionsWatchingKey, CFRangeMake(0, watcherCnt), watchers);
92
93		while (--watcherCnt >= 0) {
94			CFStringRef	sessionKey;
95
96			sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), watchers[watcherCnt]);
97			info = CFDictionaryGetValue(sessionData, sessionKey);
98			if (info) {
99				newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
100			} else {
101				newInfo = CFDictionaryCreateMutable(NULL,
102								    0,
103								    &kCFTypeDictionaryKeyCallBacks,
104								    &kCFTypeDictionaryValueCallBacks);
105			}
106
107			changes = CFDictionaryGetValue(newInfo, kSCDChangedKeys);
108			if (changes) {
109				newChanges = CFArrayCreateMutableCopy(NULL, 0, changes);
110			} else {
111				newChanges = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
112			}
113
114			if (CFArrayContainsValue(newChanges,
115						 CFRangeMake(0, CFArrayGetCount(newChanges)),
116						 (CFStringRef)keys[keyCnt]) == FALSE) {
117				CFArrayAppendValue(newChanges, (CFStringRef)keys[keyCnt]);
118			}
119			CFDictionarySetValue(newInfo, kSCDChangedKeys, newChanges);
120			CFRelease(newChanges);
121			CFDictionarySetValue(sessionData, sessionKey, newInfo);
122			CFRelease(newInfo);
123			CFRelease(sessionKey);
124
125			/*
126			 * flag this session as needing a kick
127			 */
128			if (needsNotification == NULL)
129				needsNotification = CFSetCreateMutable(NULL,
130								       0,
131								       &kCFTypeSetCallBacks);
132			CFSetAddValue(needsNotification, watchers[watcherCnt]);
133		}
134
135		if (watchers != watchers_q) CFAllocatorDeallocate(NULL, watchers);
136	}
137
138	if (keys != keys_q) CFAllocatorDeallocate(NULL, keys);
139
140	/*
141	 * The list of changed keys have been updated for any sessions
142	 * monitoring changes to the "store". The next step, handled by
143	 * the "configd" server, is to push out any needed notifications.
144	 */
145	CFSetRemoveAllValues(changedKeys);
146
147}
148
149
150static void
151_processDeferredRemovals()
152{
153	CFIndex			keyCnt;
154	const void *		keys_q[N_QUICK];
155	const void **		keys	= keys_q;
156
157	keyCnt = CFSetGetCount(deferredRemovals);
158	if (keyCnt == 0)
159		return;		/* if nothing to do */
160
161	if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef)))
162		keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
163
164	CFSetGetValues(deferredRemovals, keys);
165
166	while (--keyCnt >= 0) {
167		patternRemoveKey((CFStringRef)keys[keyCnt]);
168	}
169
170	if (keys != keys_q) CFAllocatorDeallocate(NULL, keys);
171
172	/*
173	 * All regex keys associated with removed store dictionary keys have
174	 * been removed. Start the list fresh again.
175	 */
176	CFSetRemoveAllValues(deferredRemovals);
177
178	return;
179}
180
181
182static void
183_cleanupRemovedSessionKeys(const void *value, void *context)
184{
185	CFStringRef		removedKey = (CFStringRef)value;
186	CFRange			dRange;
187	CFStringRef		sessionKey;
188	CFStringRef		key;
189	CFDictionaryRef		sessionDict;
190	CFArrayRef		sessionKeys;
191	CFIndex			i;
192	CFMutableDictionaryRef	newSessionDict;
193
194	dRange     = CFStringFind(removedKey, CFSTR(":"), 0);
195	sessionKey = CFStringCreateWithSubstring(NULL,
196						 removedKey,
197						 CFRangeMake(0, dRange.location));
198	key        = CFStringCreateWithSubstring(NULL,
199						 removedKey,
200						 CFRangeMake(dRange.location+dRange.length,
201							     CFStringGetLength(removedKey)-dRange.location-dRange.length));
202
203	/*
204	 * remove the key from the session key list
205	 */
206	sessionDict = CFDictionaryGetValue(sessionData, sessionKey);
207	if (!sessionDict) {
208		/* if no session */
209		goto done;
210	}
211
212	sessionKeys = CFDictionaryGetValue(sessionDict, kSCDSessionKeys);
213	if (!sessionKeys) {
214		/* if no session keys */
215		goto done;
216	}
217
218	i = CFArrayGetFirstIndexOfValue(sessionKeys,
219					CFRangeMake(0, CFArrayGetCount(sessionKeys)),
220					key);
221	if (i == kCFNotFound) {
222		/* if this session key has already been removed */
223		goto done;
224	}
225
226	newSessionDict = CFDictionaryCreateMutableCopy(NULL, 0, sessionDict);
227	if (CFArrayGetCount(sessionKeys) == 1) {
228		/* remove the last (session) key */
229		CFDictionaryRemoveValue(newSessionDict, kSCDSessionKeys);
230	} else {
231		CFMutableArrayRef	newSessionKeys;
232
233		/* remove the (session) key */
234		newSessionKeys = CFArrayCreateMutableCopy(NULL, 0, sessionKeys);
235		CFArrayRemoveValueAtIndex(newSessionKeys, i);
236		CFDictionarySetValue(newSessionDict, kSCDSessionKeys, newSessionKeys);
237		CFRelease(newSessionKeys);
238	}
239	CFDictionarySetValue(sessionData, sessionKey, newSessionDict);
240	CFRelease(newSessionDict);
241
242    done:
243
244	CFRelease(sessionKey);
245	CFRelease(key);
246
247	return;
248}
249
250
251__private_extern__
252int
253__SCDynamicStorePush(void)
254{
255	/*
256	 * push notifications to any session watching those keys which
257	 * were recently changed.
258	 */
259	_notifyWatchers();
260
261	/*
262	 * process any deferred key deletions.
263	 */
264	_processDeferredRemovals();
265
266	/*
267	 * clean up any removed session keys
268	 */
269	CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL);
270	CFSetRemoveAllValues(removedSessionKeys);
271
272	return kSCStatusOK;
273}
274