2 * Copyright (c) 2000, 2001, 2003-2005, 2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * June 2, 2000 Allan Nathanson <ajn@apple.com>
38 #include "configd_server.h"
42 __private_extern__ CFMutableDictionaryRef sessionData
= NULL
;
44 __private_extern__ CFMutableDictionaryRef storeData
= NULL
;
45 __private_extern__ CFMutableDictionaryRef storeData_s
= NULL
;
47 __private_extern__ CFMutableDictionaryRef patternData
= NULL
;
48 __private_extern__ CFMutableDictionaryRef patternData_s
= NULL
;
50 __private_extern__ CFMutableSetRef changedKeys
= NULL
;
51 __private_extern__ CFMutableSetRef changedKeys_s
= NULL
;
53 __private_extern__ CFMutableSetRef deferredRemovals
= NULL
;
54 __private_extern__ CFMutableSetRef deferredRemovals_s
= NULL
;
56 __private_extern__ CFMutableSetRef removedSessionKeys
= NULL
;
57 __private_extern__ CFMutableSetRef removedSessionKeys_s
= NULL
;
59 __private_extern__ CFMutableSetRef needsNotification
= NULL
;
61 __private_extern__
int storeLocked
= 0; /* > 0 if dynamic store locked */
66 _swapLockedStoreData()
71 storeData
= storeData_s
;
75 patternData
= patternData_s
;
79 changedKeys
= changedKeys_s
;
82 temp
= deferredRemovals
;
83 deferredRemovals
= deferredRemovals_s
;
84 deferredRemovals_s
= temp
;
86 temp
= removedSessionKeys
;
87 removedSessionKeys
= removedSessionKeys_s
;
88 removedSessionKeys_s
= temp
;
96 _addWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
99 CFMutableDictionaryRef newDict
;
101 CFMutableArrayRef newWatchers
;
102 CFArrayRef watcherRefs
;
103 CFMutableArrayRef newWatcherRefs
;
109 * Get the dictionary associated with this key out of the store
111 dict
= CFDictionaryGetValue(storeData
, watchedKey
);
113 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
115 newDict
= CFDictionaryCreateMutable(NULL
,
117 &kCFTypeDictionaryKeyCallBacks
,
118 &kCFTypeDictionaryValueCallBacks
);
122 * Get the set of watchers out of the keys dictionary
124 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
125 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
127 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
128 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
130 newWatchers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
131 newWatcherRefs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
135 * Add my session to the set of watchers
137 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
138 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
140 if (i
== kCFNotFound
) {
141 /* if this is the first instance of this session watching this key */
142 CFArrayAppendValue(newWatchers
, sessionNum
);
144 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
145 CFArrayAppendValue(newWatcherRefs
, refNum
);
148 /* if this is another instance of this session watching this key */
149 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
150 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
152 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
153 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
158 * Update the keys dictionary
160 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
161 CFRelease(newWatchers
);
162 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
163 CFRelease(newWatcherRefs
);
166 * Update the store for this key
168 CFDictionarySetValue(storeData
, watchedKey
, newDict
);
172 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" _addWatcher: %@, %@"), sessionNum
, watchedKey
);
181 _removeWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
183 CFDictionaryRef dict
;
184 CFMutableDictionaryRef newDict
;
186 CFMutableArrayRef newWatchers
;
187 CFArrayRef watcherRefs
;
188 CFMutableArrayRef newWatcherRefs
;
194 * Get the dictionary associated with this key out of the store
196 dict
= CFDictionaryGetValue(storeData
, watchedKey
);
197 if ((dict
== NULL
) || (CFDictionaryContainsKey(dict
, kSCDWatchers
) == FALSE
)) {
198 /* key doesn't exist (isn't this really fatal?) */
200 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" _removeWatcher: %@, %@, key not watched"), sessionNum
, watchedKey
);
204 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
207 * Get the set of watchers out of the keys dictionary and
208 * remove this session from the list.
210 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
211 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
213 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
214 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
216 /* locate the session reference */
217 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
218 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
220 if (i
== kCFNotFound
) {
222 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" _removeWatcher: %@, %@, session not watching"), sessionNum
, watchedKey
);
225 CFRelease(newWatchers
);
226 CFRelease(newWatcherRefs
);
230 /* remove one session reference */
231 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
232 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
234 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
235 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
238 /* if this was the last reference */
239 CFArrayRemoveValueAtIndex(newWatchers
, i
);
240 CFArrayRemoveValueAtIndex(newWatcherRefs
, i
);
243 if (CFArrayGetCount(newWatchers
) > 0) {
244 /* if this key is still being "watched" */
245 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
246 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
248 /* no watchers left, remove the empty set */
249 CFDictionaryRemoveValue(newDict
, kSCDWatchers
);
250 CFDictionaryRemoveValue(newDict
, kSCDWatcherRefs
);
252 CFRelease(newWatchers
);
253 CFRelease(newWatcherRefs
);
255 if (CFDictionaryGetCount(newDict
) > 0) {
256 /* if this key is still active */
257 CFDictionarySetValue(storeData
, watchedKey
, newDict
);
259 /* no information left, remove the empty dictionary */
260 CFDictionaryRemoveValue(storeData
, watchedKey
);
265 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" _removeWatcher: %@, %@"), sessionNum
, watchedKey
);
274 pushNotifications(FILE *_configd_trace
)
276 const void **sessionsToNotify
;
279 serverSessionRef theSession
;
280 SCDynamicStorePrivateRef storePrivate
;
282 if (needsNotification
== NULL
)
283 return; /* if no sessions need to be kicked */
285 notifyCnt
= CFSetGetCount(needsNotification
);
286 sessionsToNotify
= malloc(notifyCnt
* sizeof(CFNumberRef
));
287 CFSetGetValues(needsNotification
, sessionsToNotify
);
288 while (--notifyCnt
>= 0) {
289 (void) CFNumberGetValue(sessionsToNotify
[notifyCnt
],
292 theSession
= getSession(server
);
293 storePrivate
= (SCDynamicStorePrivateRef
)theSession
->store
;
296 * deliver notifications to client sessions
298 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaMachPort
) &&
299 (storePrivate
->notifyPort
!= MACH_PORT_NULL
)) {
301 * Post notification as mach message
303 if (_configd_trace
!= NULL
) {
304 SCTrace(TRUE
, _configd_trace
,
305 CFSTR("%s : %5d : port = %d, msgid = %d\n"),
307 storePrivate
->server
,
308 storePrivate
->notifyPort
,
309 storePrivate
->notifyPortIdentifier
);
312 _SC_sendMachMessage(storePrivate
->notifyPort
, storePrivate
->notifyPortIdentifier
);
315 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaFD
) &&
316 (storePrivate
->notifyFile
>= 0)) {
319 if (_configd_trace
!= NULL
) {
320 SCTrace(TRUE
, _configd_trace
,
321 CFSTR("%s : %5d : fd = %d, msgid = %d\n"),
323 storePrivate
->server
,
324 storePrivate
->notifyFile
,
325 storePrivate
->notifyFileIdentifier
);
328 written
= write(storePrivate
->notifyFile
,
329 &storePrivate
->notifyFileIdentifier
,
330 sizeof(storePrivate
->notifyFileIdentifier
));
332 if (errno
== EWOULDBLOCK
) {
334 SCLog(_configd_verbose
, LOG_DEBUG
,
335 CFSTR("sorry, only one outstanding notification per session."));
339 SCLog(_configd_verbose
, LOG_DEBUG
,
340 CFSTR("could not send notification, write() failed: %s"),
343 storePrivate
->notifyFile
= -1;
345 } else if (written
!= sizeof(storePrivate
->notifyFileIdentifier
)) {
347 SCLog(_configd_verbose
, LOG_DEBUG
,
348 CFSTR("could not send notification, incomplete write()"));
350 storePrivate
->notifyFile
= -1;
354 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaSignal
) &&
355 (storePrivate
->notifySignal
> 0)) {
356 kern_return_t status
;
359 * Post notification as signal
361 status
= pid_for_task(storePrivate
->notifySignalTask
, &pid
);
362 if (status
== KERN_SUCCESS
) {
363 if (_configd_trace
!= NULL
) {
364 SCTrace(TRUE
, _configd_trace
,
365 CFSTR("%s : %5d : pid = %d, signal = sig%s (%d)\n"),
367 storePrivate
->server
,
369 sys_signame
[storePrivate
->notifySignal
],
370 storePrivate
->notifySignal
);
373 if (kill(pid
, storePrivate
->notifySignal
) != 0) {
374 if (errno
!= ESRCH
) {
376 CFSTR("could not send sig%s to PID %d: %s"),
377 sys_signame
[storePrivate
->notifySignal
],
385 __MACH_PORT_DEBUG(TRUE
, "*** pushNotifications pid_for_task failed: releasing task", storePrivate
->notifySignalTask
);
386 if (mach_port_type(mach_task_self(), storePrivate
->notifySignalTask
, &pt
) == KERN_SUCCESS
) {
387 if ((pt
& MACH_PORT_TYPE_DEAD_NAME
) != 0) {
388 SCLog(TRUE
, LOG_ERR
, CFSTR("pushNotifications pid_for_task() failed: %s"), mach_error_string(status
));
391 SCLog(TRUE
, LOG_ERR
, CFSTR("pushNotifications mach_port_type() failed: %s"), mach_error_string(status
));
394 /* don't bother with any more attempts */
395 (void) mach_port_deallocate(mach_task_self(), storePrivate
->notifySignalTask
);
396 storePrivate
->notifySignal
= 0;
397 storePrivate
->notifySignalTask
= TASK_NULL
;
401 free(sessionsToNotify
);
404 * this list of notifications have been posted, wait for some more.
406 CFRelease(needsNotification
);
407 needsNotification
= NULL
;