2 * Copyright (c) 2000, 2001, 2003-2005, 2009, 2011, 2012, 2015-2017 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
;
46 __private_extern__ CFMutableDictionaryRef patternData
= NULL
;
48 __private_extern__ CFMutableSetRef changedKeys
= NULL
;
50 __private_extern__ CFMutableSetRef deferredRemovals
= NULL
;
52 __private_extern__ CFMutableSetRef removedSessionKeys
= NULL
;
54 __private_extern__ CFMutableSetRef needsNotification
= NULL
;
59 _addWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
62 CFMutableDictionaryRef newDict
;
64 CFMutableArrayRef newWatchers
;
65 CFArrayRef watcherRefs
;
66 CFMutableArrayRef newWatcherRefs
;
72 * Get the dictionary associated with this key out of the store
74 dict
= CFDictionaryGetValue(storeData
, watchedKey
);
76 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
78 newDict
= CFDictionaryCreateMutable(NULL
,
80 &kCFTypeDictionaryKeyCallBacks
,
81 &kCFTypeDictionaryValueCallBacks
);
85 * Get the set of watchers out of the keys dictionary
87 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
88 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
90 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
91 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
93 newWatchers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
94 newWatcherRefs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
98 * Add my session to the set of watchers
100 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
101 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
103 if (i
== kCFNotFound
) {
104 /* if this is the first instance of this session watching this key */
105 CFArrayAppendValue(newWatchers
, sessionNum
);
107 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
108 CFArrayAppendValue(newWatcherRefs
, refNum
);
111 /* if this is another instance of this session watching this key */
112 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
113 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
115 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
116 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
121 * Update the keys dictionary
123 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
124 CFRelease(newWatchers
);
125 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
126 CFRelease(newWatcherRefs
);
129 * Update the store for this key
131 CFDictionarySetValue(storeData
, watchedKey
, newDict
);
135 SC_log(LOG_DEBUG
, " _addWatcher: %@, %@", sessionNum
, watchedKey
);
144 _removeWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
146 CFDictionaryRef dict
;
147 CFMutableDictionaryRef newDict
;
149 CFMutableArrayRef newWatchers
;
150 CFArrayRef watcherRefs
;
151 CFMutableArrayRef newWatcherRefs
;
157 * Get the dictionary associated with this key out of the store
159 dict
= CFDictionaryGetValue(storeData
, watchedKey
);
160 if ((dict
== NULL
) || !CFDictionaryContainsKey(dict
, kSCDWatchers
)) {
161 /* key doesn't exist (isn't this really fatal?) */
163 SC_log(LOG_DEBUG
, " _removeWatcher: %@, %@, key not watched", sessionNum
, watchedKey
);
167 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
170 * Get the set of watchers out of the keys dictionary and
171 * remove this session from the list.
173 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
174 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
176 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
177 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
179 /* locate the session reference */
180 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
181 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
183 if (i
== kCFNotFound
) {
185 SC_log(LOG_DEBUG
, " _removeWatcher: %@, %@, session not watching", sessionNum
, watchedKey
);
188 CFRelease(newWatchers
);
189 CFRelease(newWatcherRefs
);
193 /* remove one session reference */
194 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
195 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
197 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
198 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
201 /* if this was the last reference */
202 CFArrayRemoveValueAtIndex(newWatchers
, i
);
203 CFArrayRemoveValueAtIndex(newWatcherRefs
, i
);
206 if (CFArrayGetCount(newWatchers
) > 0) {
207 /* if this key is still being "watched" */
208 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
209 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
211 /* no watchers left, remove the empty set */
212 CFDictionaryRemoveValue(newDict
, kSCDWatchers
);
213 CFDictionaryRemoveValue(newDict
, kSCDWatcherRefs
);
215 CFRelease(newWatchers
);
216 CFRelease(newWatcherRefs
);
218 if (CFDictionaryGetCount(newDict
) > 0) {
219 /* if this key is still active */
220 CFDictionarySetValue(storeData
, watchedKey
, newDict
);
222 /* no information left, remove the empty dictionary */
223 CFDictionaryRemoveValue(storeData
, watchedKey
);
228 SC_log(LOG_DEBUG
, " _removeWatcher: %@, %@", sessionNum
, watchedKey
);
244 const void * sessionsToNotify_q
[N_QUICK
];
245 const void ** sessionsToNotify
= sessionsToNotify_q
;
246 SCDynamicStorePrivateRef storePrivate
;
247 serverSessionRef theSession
;
249 if (needsNotification
== NULL
)
250 return; /* if no sessions need to be kicked */
252 notifyCnt
= CFSetGetCount(needsNotification
);
253 if (notifyCnt
> (CFIndex
)(sizeof(sessionsToNotify_q
) / sizeof(CFNumberRef
)))
254 sessionsToNotify
= CFAllocatorAllocate(NULL
, notifyCnt
* sizeof(CFNumberRef
), 0);
255 CFSetGetValues(needsNotification
, sessionsToNotify
);
256 while (--notifyCnt
>= 0) {
257 (void) CFNumberGetValue(sessionsToNotify
[notifyCnt
],
260 theSession
= getSession(server
);
261 assert(theSession
!= NULL
);
263 storePrivate
= (SCDynamicStorePrivateRef
)theSession
->store
;
266 * deliver [CFRunLoop/dispatch] notifications to client sessions
268 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaMachPort
) &&
269 (storePrivate
->notifyPort
!= MACH_PORT_NULL
)) {
271 * Associate notification activity with the client
273 os_activity_scope(theSession
->activity
);
276 * Post notification as mach message
278 SC_trace("-->port : %5d : port = %d",
279 storePrivate
->server
,
280 storePrivate
->notifyPort
);
282 /* use a random (and non-zero) identifier */
283 while (storePrivate
->notifyPortIdentifier
== 0) {
284 storePrivate
->notifyPortIdentifier
= (mach_msg_id_t
)random();
287 _SC_sendMachMessage(storePrivate
->notifyPort
, storePrivate
->notifyPortIdentifier
);
290 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaFD
) &&
291 (storePrivate
->notifyFile
>= 0)) {
295 * Associate notification activity with the client
297 os_activity_scope(theSession
->activity
);
299 SC_trace("-->fd : %5d : fd = %d, msgid = %d",
300 storePrivate
->server
,
301 storePrivate
->notifyFile
,
302 storePrivate
->notifyFileIdentifier
);
304 written
= write(storePrivate
->notifyFile
,
305 &storePrivate
->notifyFileIdentifier
,
306 sizeof(storePrivate
->notifyFileIdentifier
));
308 if (errno
== EWOULDBLOCK
) {
310 SC_log(LOG_DEBUG
, "sorry, only one outstanding notification per session");
314 SC_log(LOG_DEBUG
, "could not send notification, write() failed: %s",
317 storePrivate
->notifyFile
= -1;
319 } else if (written
!= sizeof(storePrivate
->notifyFileIdentifier
)) {
321 SC_log(LOG_DEBUG
, "could not send notification, incomplete write()");
323 storePrivate
->notifyFile
= -1;
327 if ((storePrivate
->notifyStatus
== Using_NotifierInformViaSignal
) &&
328 (storePrivate
->notifySignal
> 0)) {
329 kern_return_t status
;
333 * Associate notification activity with the client
335 os_activity_scope(theSession
->activity
);
338 * Post notification as signal
340 status
= pid_for_task(storePrivate
->notifySignalTask
, &pid
);
341 if (status
== KERN_SUCCESS
) {
342 SC_trace("-->sig : %5d : pid = %d, signal = sig%s (%d)",
343 storePrivate
->server
,
345 sys_signame
[storePrivate
->notifySignal
],
346 storePrivate
->notifySignal
);
348 if (kill(pid
, storePrivate
->notifySignal
) != 0) {
349 if (errno
!= ESRCH
) {
350 SC_log(LOG_NOTICE
, "could not send sig%s to PID %d: %s",
351 sys_signame
[storePrivate
->notifySignal
],
359 __MACH_PORT_DEBUG(TRUE
, "*** pushNotifications pid_for_task failed: releasing task", storePrivate
->notifySignalTask
);
360 if (mach_port_type(mach_task_self(), storePrivate
->notifySignalTask
, &pt
) == KERN_SUCCESS
) {
361 if ((pt
& MACH_PORT_TYPE_DEAD_NAME
) != 0) {
362 SC_log(LOG_NOTICE
, "pid_for_task() failed: %s", mach_error_string(status
));
365 SC_log(LOG_NOTICE
, "mach_port_type() failed: %s", mach_error_string(status
));
368 /* don't bother with any more attempts */
369 (void) mach_port_deallocate(mach_task_self(), storePrivate
->notifySignalTask
);
370 storePrivate
->notifySignal
= 0;
371 storePrivate
->notifySignalTask
= TASK_NULL
;
375 if (sessionsToNotify
!= sessionsToNotify_q
) CFAllocatorDeallocate(NULL
, sessionsToNotify
);
378 * this list of notifications have been posted, wait for some more.
380 CFRelease(needsNotification
);
381 needsNotification
= NULL
;