2 * Copyright (c) 2000, 2001, 2003-2005 Apple Computer, 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
);
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
304 if (_configd_verbose
) {
305 SCLog(TRUE
, LOG_DEBUG
, CFSTR("sending mach message notification."));
306 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" port = %d"), storePrivate
->notifyPort
);
307 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" msgid = %d"), storePrivate
->notifyPortIdentifier
);
310 _SC_sendMachMessage(storePrivate
->notifyPort
, storePrivate
->notifyPortIdentifier
);
313 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaFD
) &&
314 (storePrivate
->notifyFile
>= 0)) {
318 if (_configd_verbose
) {
319 SCLog(TRUE
, LOG_DEBUG
, CFSTR("sending (UNIX domain) socket notification"));
320 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" fd = %d"), storePrivate
->notifyFile
);
321 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" msgid = %d"), storePrivate
->notifyFileIdentifier
);
325 written
= write(storePrivate
->notifyFile
,
326 &storePrivate
->notifyFileIdentifier
,
327 sizeof(storePrivate
->notifyFileIdentifier
));
329 if (errno
== EWOULDBLOCK
) {
331 SCLog(_configd_verbose
, LOG_DEBUG
,
332 CFSTR("sorry, only one outstanding notification per session."));
336 SCLog(_configd_verbose
, LOG_DEBUG
,
337 CFSTR("could not send notification, write() failed: %s"),
340 storePrivate
->notifyFile
= -1;
342 } else if (written
!= sizeof(storePrivate
->notifyFileIdentifier
)) {
344 SCLog(_configd_verbose
, LOG_DEBUG
,
345 CFSTR("could not send notification, incomplete write()"));
347 storePrivate
->notifyFile
= -1;
351 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaSignal
) &&
352 (storePrivate
->notifySignal
> 0)) {
353 kern_return_t status
;
356 * Post notification as signal
358 status
= pid_for_task(storePrivate
->notifySignalTask
, &pid
);
359 if (status
== KERN_SUCCESS
) {
361 if (_configd_verbose
) {
362 SCLog(TRUE
, LOG_DEBUG
, CFSTR("sending signal notification"));
363 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" pid = %d"), pid
);
364 SCLog(TRUE
, LOG_DEBUG
, CFSTR(" signal = %d"), storePrivate
->notifySignal
);
367 if (kill(pid
, storePrivate
->notifySignal
) != 0) {
369 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("could not send signal: %s"), strerror(errno
));
371 status
= KERN_FAILURE
;
376 if ((mach_port_type(mach_task_self(), storePrivate
->notifySignalTask
, &pt
) == KERN_SUCCESS
) &&
377 (pt
& MACH_PORT_TYPE_DEAD_NAME
)) {
378 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("could not send signal, process died"));
380 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("could not send signal: %s"), mach_error_string(status
));
384 if (status
!= KERN_SUCCESS
) {
385 /* don't bother with any more attempts */
386 (void) mach_port_destroy(mach_task_self(), storePrivate
->notifySignalTask
);
387 storePrivate
->notifySignal
= 0;
388 storePrivate
->notifySignalTask
= TASK_NULL
;
392 free(sessionsToNotify
);
395 * this list of notifications have been posted, wait for some more.
397 CFRelease(needsNotification
);
398 needsNotification
= NULL
;