1/*
2 * Copyright (c) 2000-2004, 2006, 2008, 2010, 2011, 2014 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#include "configd.h"
35#include "session.h"
36#include "pattern.h"
37
38
39static __inline__ void
40my_CFArrayApplyFunction(CFArrayRef		theArray,
41			CFArrayApplierFunction	applier,
42			void			*context)
43{
44	CFAllocatorRef	myAllocator;
45	CFArrayRef	myArray;
46
47	myAllocator = CFGetAllocator(theArray);
48	myArray     = CFArrayCreateCopy(myAllocator, theArray);
49	CFArrayApplyFunction(myArray, CFRangeMake(0, CFArrayGetCount(myArray)), applier, context);
50	CFRelease(myArray);
51	return;
52}
53
54
55static int
56hasKey(CFMutableArrayRef keys, CFStringRef key)
57{
58	if (keys != NULL) {
59		CFIndex	n;
60
61		n = CFArrayGetCount(keys);
62		if (CFArrayContainsValue(keys, CFRangeMake(0, n), key)) {
63			/* sorry, pattern already exists in notifier list */
64			return kSCStatusKeyExists;
65		}
66	}
67
68	return kSCStatusOK;
69}
70
71
72static void
73addKey(CFMutableArrayRef *keysP, CFStringRef key)
74{
75	if (*keysP == NULL) {
76		*keysP = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
77	}
78
79	CFArrayAppendValue(*keysP, key);
80	return;
81}
82
83
84__private_extern__
85int
86__SCDynamicStoreAddWatchedKey(SCDynamicStoreRef store, CFStringRef key, Boolean isRegex, Boolean internal)
87{
88	int				sc_status	= kSCStatusOK;
89	CFNumberRef			sessionNum	= NULL;
90	SCDynamicStorePrivateRef	storePrivate	= (SCDynamicStorePrivateRef)store;
91
92	if (_configd_trace) {
93		SCTrace(TRUE, _configd_trace,
94			CFSTR("%s : %5d : %s : %@\n"),
95			internal ? "*watch+" : "watch+ ",
96			storePrivate->server,
97			isRegex  ? "pattern" : "key",
98			key);
99	}
100
101	sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &storePrivate->server);
102
103	if (isRegex) {
104		sc_status = hasKey(storePrivate->patterns, key);
105		if (sc_status != kSCStatusOK) {
106			goto done;
107		}
108
109		/*
110		 * add this session as a pattern watcher
111		 */
112		if (!patternAddSession(key, sessionNum)) {
113			sc_status = kSCStatusInvalidArgument;
114			goto done;
115		}
116
117		/* add pattern to this sessions notifier list */
118		addKey(&storePrivate->patterns, key);
119	} else {
120		sc_status = hasKey(storePrivate->keys, key);
121		if (sc_status != kSCStatusOK) {
122			goto done;
123		}
124
125		/*
126		 * We are watching a specific key. As such, update the
127		 * store to mark our interest in any changes.
128		 */
129		_addWatcher(sessionNum, key);
130
131		/* add key to this sessions notifier list */
132		addKey(&storePrivate->keys, key);
133	}
134
135    done :
136
137	if (sessionNum != NULL)	CFRelease(sessionNum);
138	return sc_status;
139}
140
141
142__private_extern__
143kern_return_t
144_notifyadd(mach_port_t 			server,
145	   xmlData_t			keyRef,		/* raw XML bytes */
146	   mach_msg_type_number_t	keyLen,
147	   int				isRegex,
148	   int				*sc_status
149)
150{
151	CFStringRef		key		= NULL;	/* key  (un-serialized) */
152	serverSessionRef	mySession;
153
154	/* un-serialize the key */
155	if (!_SCUnserializeString(&key, NULL, (void *)keyRef, keyLen)) {
156		*sc_status = kSCStatusFailed;
157		goto done;
158	}
159
160	if (!isA_CFString(key)) {
161		*sc_status = kSCStatusInvalidArgument;
162		goto done;
163	}
164
165	mySession = getSession(server);
166	if (mySession == NULL) {
167		*sc_status = kSCStatusNoStoreSession;	/* you must have an open session to play */
168		goto done;
169	}
170
171	*sc_status = __SCDynamicStoreAddWatchedKey(mySession->store, key, isRegex != 0, FALSE);
172
173    done :
174
175	if (key)	CFRelease(key);
176	return KERN_SUCCESS;
177}
178
179
180/*
181 * "context" argument for removeOldKey() and addNewKey()
182 */
183typedef struct {
184	SCDynamicStoreRef       store;
185	CFArrayRef		oldKeys;	/* for addNewKey */
186	CFArrayRef		newKeys;	/* for removeOldKey */
187	Boolean			isRegex;
188	int			sc_status;
189} updateKeysContext, *updateKeysContextRef;
190
191
192static void
193removeOldKey(const void *value, void *context)
194{
195	CFStringRef			oldKey		= (CFStringRef)value;
196	updateKeysContextRef		myContextRef	= (updateKeysContextRef)context;
197
198	if (myContextRef->sc_status != kSCStatusOK) {
199		return;
200	}
201
202	if ((myContextRef->newKeys == NULL) ||
203	    !CFArrayContainsValue(myContextRef->newKeys,
204				  CFRangeMake(0, CFArrayGetCount(myContextRef->newKeys)),
205				  oldKey)) {
206		/* the old notification key is not being retained, remove it */
207		myContextRef->sc_status = __SCDynamicStoreRemoveWatchedKey(myContextRef->store,
208									   oldKey,
209									   myContextRef->isRegex,
210									   TRUE);
211	}
212
213	return;
214}
215
216
217static void
218addNewKey(const void *value, void *context)
219{
220	CFStringRef			newKey		= (CFStringRef)value;
221	updateKeysContextRef		myContextRef	= (updateKeysContextRef)context;
222
223	if (myContextRef->sc_status != kSCStatusOK) {
224		return;
225	}
226
227	if ((myContextRef->oldKeys == NULL) ||
228	    !CFArrayContainsValue(myContextRef->oldKeys,
229				  CFRangeMake(0, CFArrayGetCount(myContextRef->oldKeys)),
230				  newKey)) {
231		/* if this is a new notification key */
232		myContextRef->sc_status = __SCDynamicStoreAddWatchedKey(myContextRef->store,
233									newKey,
234									myContextRef->isRegex,
235									TRUE);
236	}
237
238	return;
239}
240
241
242__private_extern__
243int
244__SCDynamicStoreSetNotificationKeys(SCDynamicStoreRef store, CFArrayRef keys, CFArrayRef patterns)
245{
246	updateKeysContext		myContext;
247	SCDynamicStorePrivateRef	storePrivate = (SCDynamicStorePrivateRef)store;
248
249	if (_configd_trace) {
250		SCTrace(TRUE, _configd_trace,
251			CFSTR("watch   : %5d : %ld keys, %ld patterns\n"),
252			storePrivate->server,
253			keys     ? CFArrayGetCount(keys)     : 0,
254			patterns ? CFArrayGetCount(patterns) : 0);
255	}
256
257	myContext.store     = store;
258	myContext.sc_status = kSCStatusOK;
259
260	/* remove any previously registered keys, register any new keys */
261	myContext.oldKeys = NULL;
262	myContext.newKeys = keys;
263	myContext.isRegex = FALSE;
264	if (storePrivate->keys != NULL) {
265		myContext.oldKeys = CFArrayCreateCopy(NULL, storePrivate->keys);
266		my_CFArrayApplyFunction(storePrivate->keys, removeOldKey, &myContext);
267	}
268	if (keys != NULL) {
269		CFArrayApplyFunction(keys,
270				     CFRangeMake(0, CFArrayGetCount(keys)),
271				     addNewKey,
272				     &myContext);
273	}
274	if (myContext.oldKeys != NULL) CFRelease(myContext.oldKeys);
275
276	/* remove any previously registered patterns, register any new patterns */
277	myContext.oldKeys = NULL;
278	myContext.newKeys = patterns;
279	myContext.isRegex = TRUE;
280	if (storePrivate->patterns != NULL) {
281		myContext.oldKeys = CFArrayCreateCopy(NULL, storePrivate->patterns);
282		my_CFArrayApplyFunction(storePrivate->patterns, removeOldKey, &myContext);
283	}
284	if (patterns != NULL) {
285		CFArrayApplyFunction(patterns,
286				     CFRangeMake(0, CFArrayGetCount(patterns)),
287				     addNewKey,
288				     &myContext);
289	}
290	if (myContext.oldKeys != NULL) CFRelease(myContext.oldKeys);
291
292	return myContext.sc_status;
293}
294
295
296__private_extern__
297kern_return_t
298_notifyset(mach_port_t 			server,
299	   xmlData_t			keysRef,		/* raw XML bytes */
300	   mach_msg_type_number_t	keysLen,
301	   xmlData_t			patternsRef,		/* raw XML bytes */
302	   mach_msg_type_number_t	patternsLen,
303	   int				*sc_status
304)
305{
306	CFArrayRef		keys		= NULL;	/* key (un-serialized) */
307	serverSessionRef	mySession;
308	CFArrayRef		patterns	= NULL;	/* patterns (un-serialized) */
309
310	*sc_status = kSCStatusOK;
311
312	if ((keysRef != NULL) && (keysLen > 0)) {
313		/* un-serialize the keys */
314		if (!_SCUnserialize((CFPropertyListRef *)&keys, NULL, (void *)keysRef, keysLen)) {
315			*sc_status = kSCStatusFailed;
316		}
317	}
318
319	if ((patternsRef != NULL) && (patternsLen > 0)) {
320		/* un-serialize the patterns */
321		if (!_SCUnserialize((CFPropertyListRef *)&patterns, NULL, (void *)patternsRef, patternsLen)) {
322			*sc_status = kSCStatusFailed;
323		}
324	}
325
326	if (*sc_status != kSCStatusOK) {
327		goto done;
328	}
329
330	if ((keys != NULL) && !isA_CFArray(keys)) {
331		*sc_status = kSCStatusInvalidArgument;
332		goto done;
333	}
334
335	if ((patterns != NULL) && !isA_CFArray(patterns)) {
336		*sc_status = kSCStatusInvalidArgument;
337		goto done;
338	}
339
340	mySession = getSession(server);
341	if (mySession == NULL) {
342		/* you must have an open session to play */
343		*sc_status = kSCStatusNoStoreSession;
344		goto done;
345	}
346
347	*sc_status = __SCDynamicStoreSetNotificationKeys(mySession->store, keys, patterns);
348
349    done :
350
351	if (keys != NULL)	CFRelease(keys);
352	if (patterns != NULL)	CFRelease(patterns);
353
354	return KERN_SUCCESS;
355}
356