]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
9de8ab86 | 2 | * Copyright (c) 2000, 2001, 2003-2005, 2009, 2011, 2012, 2015 Apple Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
9de8ab86 | 5 | * |
009ee3c6 A |
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. | |
9de8ab86 | 12 | * |
009ee3c6 A |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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. | |
9de8ab86 | 20 | * |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
24 | /* |
25 | * Modification History | |
26 | * | |
27 | * June 1, 2001 Allan Nathanson <ajn@apple.com> | |
28 | * - public API conversion | |
29 | * | |
30 | * June 2, 2000 Allan Nathanson <ajn@apple.com> | |
31 | * - initial revision | |
32 | */ | |
33 | ||
5958d7c0 | 34 | |
dbf6a266 A |
35 | #include <unistd.h> |
36 | ||
5958d7c0 | 37 | #include "configd.h" |
dbf6a266 A |
38 | #include "configd_server.h" |
39 | #include "session.h" | |
5958d7c0 A |
40 | |
41 | ||
009ee3c6 | 42 | __private_extern__ CFMutableDictionaryRef sessionData = NULL; |
0fae82ee | 43 | |
009ee3c6 | 44 | __private_extern__ CFMutableDictionaryRef storeData = NULL; |
5958d7c0 | 45 | |
009ee3c6 | 46 | __private_extern__ CFMutableDictionaryRef patternData = NULL; |
5958d7c0 | 47 | |
009ee3c6 | 48 | __private_extern__ CFMutableSetRef changedKeys = NULL; |
5958d7c0 | 49 | |
009ee3c6 | 50 | __private_extern__ CFMutableSetRef deferredRemovals = NULL; |
5958d7c0 | 51 | |
009ee3c6 | 52 | __private_extern__ CFMutableSetRef removedSessionKeys = NULL; |
5958d7c0 | 53 | |
009ee3c6 | 54 | __private_extern__ CFMutableSetRef needsNotification = NULL; |
5958d7c0 | 55 | |
5958d7c0 | 56 | |
009ee3c6 | 57 | __private_extern__ |
5958d7c0 A |
58 | void |
59 | _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey) | |
60 | { | |
61 | CFDictionaryRef dict; | |
62 | CFMutableDictionaryRef newDict; | |
63 | CFArrayRef watchers; | |
64 | CFMutableArrayRef newWatchers; | |
65 | CFArrayRef watcherRefs; | |
66 | CFMutableArrayRef newWatcherRefs; | |
67 | CFIndex i; | |
68 | int refCnt; | |
69 | CFNumberRef refNum; | |
70 | ||
71 | /* | |
0fae82ee | 72 | * Get the dictionary associated with this key out of the store |
5958d7c0 | 73 | */ |
0fae82ee | 74 | dict = CFDictionaryGetValue(storeData, watchedKey); |
5958d7c0 A |
75 | if (dict) { |
76 | newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); | |
77 | } else { | |
78 | newDict = CFDictionaryCreateMutable(NULL, | |
79 | 0, | |
80 | &kCFTypeDictionaryKeyCallBacks, | |
81 | &kCFTypeDictionaryValueCallBacks); | |
82 | } | |
83 | ||
84 | /* | |
85 | * Get the set of watchers out of the keys dictionary | |
86 | */ | |
87 | watchers = CFDictionaryGetValue(newDict, kSCDWatchers); | |
88 | watcherRefs = CFDictionaryGetValue(newDict, kSCDWatcherRefs); | |
89 | if (watchers) { | |
90 | newWatchers = CFArrayCreateMutableCopy(NULL, 0, watchers); | |
91 | newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs); | |
92 | } else { | |
93 | newWatchers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
94 | newWatcherRefs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Add my session to the set of watchers | |
99 | */ | |
100 | i = CFArrayGetFirstIndexOfValue(newWatchers, | |
101 | CFRangeMake(0, CFArrayGetCount(newWatchers)), | |
102 | sessionNum); | |
dbf6a266 | 103 | if (i == kCFNotFound) { |
5958d7c0 A |
104 | /* if this is the first instance of this session watching this key */ |
105 | CFArrayAppendValue(newWatchers, sessionNum); | |
106 | refCnt = 1; | |
107 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
108 | CFArrayAppendValue(newWatcherRefs, refNum); | |
109 | CFRelease(refNum); | |
110 | } else { | |
111 | /* if this is another instance of this session watching this key */ | |
112 | refNum = CFArrayGetValueAtIndex(newWatcherRefs, i); | |
113 | CFNumberGetValue(refNum, kCFNumberIntType, &refCnt); | |
114 | refCnt++; | |
115 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
116 | CFArraySetValueAtIndex(newWatcherRefs, i, refNum); | |
117 | CFRelease(refNum); | |
118 | } | |
119 | ||
120 | /* | |
121 | * Update the keys dictionary | |
122 | */ | |
123 | CFDictionarySetValue(newDict, kSCDWatchers, newWatchers); | |
124 | CFRelease(newWatchers); | |
125 | CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs); | |
126 | CFRelease(newWatcherRefs); | |
127 | ||
128 | /* | |
0fae82ee | 129 | * Update the store for this key |
5958d7c0 | 130 | */ |
0fae82ee | 131 | CFDictionarySetValue(storeData, watchedKey, newDict); |
5958d7c0 A |
132 | CFRelease(newDict); |
133 | ||
dbf6a266 | 134 | #ifdef DEBUG |
9de8ab86 | 135 | SC_log(LOG_DEBUG, " _addWatcher: %@, %@", sessionNum, watchedKey); |
dbf6a266 | 136 | #endif /* DEBUG */ |
5958d7c0 A |
137 | |
138 | return; | |
139 | } | |
140 | ||
141 | ||
009ee3c6 | 142 | __private_extern__ |
5958d7c0 A |
143 | void |
144 | _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey) | |
145 | { | |
146 | CFDictionaryRef dict; | |
147 | CFMutableDictionaryRef newDict; | |
148 | CFArrayRef watchers; | |
149 | CFMutableArrayRef newWatchers; | |
150 | CFArrayRef watcherRefs; | |
151 | CFMutableArrayRef newWatcherRefs; | |
152 | CFIndex i; | |
153 | int refCnt; | |
154 | CFNumberRef refNum; | |
155 | ||
156 | /* | |
0fae82ee | 157 | * Get the dictionary associated with this key out of the store |
5958d7c0 | 158 | */ |
0fae82ee | 159 | dict = CFDictionaryGetValue(storeData, watchedKey); |
5958d7c0 A |
160 | if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) { |
161 | /* key doesn't exist (isn't this really fatal?) */ | |
dbf6a266 | 162 | #ifdef DEBUG |
9de8ab86 | 163 | SC_log(LOG_DEBUG, " _removeWatcher: %@, %@, key not watched", sessionNum, watchedKey); |
dbf6a266 | 164 | #endif /* DEBUG */ |
5958d7c0 A |
165 | return; |
166 | } | |
167 | newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); | |
168 | ||
169 | /* | |
170 | * Get the set of watchers out of the keys dictionary and | |
171 | * remove this session from the list. | |
172 | */ | |
173 | watchers = CFDictionaryGetValue(newDict, kSCDWatchers); | |
174 | newWatchers = CFArrayCreateMutableCopy(NULL, 0, watchers); | |
175 | ||
176 | watcherRefs = CFDictionaryGetValue(newDict, kSCDWatcherRefs); | |
177 | newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs); | |
178 | ||
179 | /* locate the session reference */ | |
180 | i = CFArrayGetFirstIndexOfValue(newWatchers, | |
181 | CFRangeMake(0, CFArrayGetCount(newWatchers)), | |
182 | sessionNum); | |
dbf6a266 A |
183 | if (i == kCFNotFound) { |
184 | #ifdef DEBUG | |
9de8ab86 | 185 | SC_log(LOG_DEBUG, " _removeWatcher: %@, %@, session not watching", sessionNum, watchedKey); |
dbf6a266 | 186 | #endif /* DEBUG */ |
5958d7c0 A |
187 | CFRelease(newDict); |
188 | CFRelease(newWatchers); | |
189 | CFRelease(newWatcherRefs); | |
190 | return; | |
191 | } | |
192 | ||
193 | /* remove one session reference */ | |
194 | refNum = CFArrayGetValueAtIndex(newWatcherRefs, i); | |
195 | CFNumberGetValue(refNum, kCFNumberIntType, &refCnt); | |
196 | if (--refCnt > 0) { | |
197 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
198 | CFArraySetValueAtIndex(newWatcherRefs, i, refNum); | |
199 | CFRelease(refNum); | |
200 | } else { | |
201 | /* if this was the last reference */ | |
202 | CFArrayRemoveValueAtIndex(newWatchers, i); | |
203 | CFArrayRemoveValueAtIndex(newWatcherRefs, i); | |
204 | } | |
205 | ||
206 | if (CFArrayGetCount(newWatchers) > 0) { | |
207 | /* if this key is still being "watched" */ | |
208 | CFDictionarySetValue(newDict, kSCDWatchers, newWatchers); | |
209 | CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs); | |
210 | } else { | |
211 | /* no watchers left, remove the empty set */ | |
212 | CFDictionaryRemoveValue(newDict, kSCDWatchers); | |
213 | CFDictionaryRemoveValue(newDict, kSCDWatcherRefs); | |
214 | } | |
215 | CFRelease(newWatchers); | |
216 | CFRelease(newWatcherRefs); | |
217 | ||
218 | if (CFDictionaryGetCount(newDict) > 0) { | |
219 | /* if this key is still active */ | |
0fae82ee | 220 | CFDictionarySetValue(storeData, watchedKey, newDict); |
5958d7c0 A |
221 | } else { |
222 | /* no information left, remove the empty dictionary */ | |
0fae82ee | 223 | CFDictionaryRemoveValue(storeData, watchedKey); |
5958d7c0 A |
224 | } |
225 | CFRelease(newDict); | |
226 | ||
dbf6a266 | 227 | #ifdef DEBUG |
9de8ab86 | 228 | SC_log(LOG_DEBUG, " _removeWatcher: %@, %@", sessionNum, watchedKey); |
dbf6a266 A |
229 | #endif /* DEBUG */ |
230 | ||
231 | return; | |
232 | } | |
233 | ||
234 | ||
5e9ce69e A |
235 | #define N_QUICK 64 |
236 | ||
237 | ||
dbf6a266 A |
238 | __private_extern__ |
239 | void | |
a40a14f8 | 240 | pushNotifications(FILE *_configd_trace) |
dbf6a266 | 241 | { |
dbf6a266 A |
242 | CFIndex notifyCnt; |
243 | int server; | |
5e9ce69e A |
244 | const void * sessionsToNotify_q[N_QUICK]; |
245 | const void ** sessionsToNotify = sessionsToNotify_q; | |
dbf6a266 | 246 | SCDynamicStorePrivateRef storePrivate; |
5e9ce69e | 247 | serverSessionRef theSession; |
dbf6a266 A |
248 | |
249 | if (needsNotification == NULL) | |
250 | return; /* if no sessions need to be kicked */ | |
251 | ||
252 | notifyCnt = CFSetGetCount(needsNotification); | |
5e9ce69e A |
253 | if (notifyCnt > (CFIndex)(sizeof(sessionsToNotify_q) / sizeof(CFNumberRef))) |
254 | sessionsToNotify = CFAllocatorAllocate(NULL, notifyCnt * sizeof(CFNumberRef), 0); | |
dbf6a266 A |
255 | CFSetGetValues(needsNotification, sessionsToNotify); |
256 | while (--notifyCnt >= 0) { | |
257 | (void) CFNumberGetValue(sessionsToNotify[notifyCnt], | |
258 | kCFNumberIntType, | |
259 | &server); | |
260 | theSession = getSession(server); | |
261 | storePrivate = (SCDynamicStorePrivateRef)theSession->store; | |
262 | ||
263 | /* | |
264 | * deliver notifications to client sessions | |
265 | */ | |
266 | if ((storePrivate->notifyStatus == Using_NotifierInformViaMachPort) && | |
267 | (storePrivate->notifyPort != MACH_PORT_NULL)) { | |
dbf6a266 A |
268 | /* |
269 | * Post notification as mach message | |
270 | */ | |
9de8ab86 A |
271 | SC_trace(_configd_trace, "%s : %5d : port = %d, msgid = %d\n", |
272 | "-->port", | |
273 | storePrivate->server, | |
274 | storePrivate->notifyPort, | |
275 | storePrivate->notifyPortIdentifier); | |
a40a14f8 | 276 | |
edebe297 | 277 | _SC_sendMachMessage(storePrivate->notifyPort, storePrivate->notifyPortIdentifier); |
dbf6a266 A |
278 | } |
279 | ||
280 | if ((storePrivate->notifyStatus == Using_NotifierInformViaFD) && | |
281 | (storePrivate->notifyFile >= 0)) { | |
282 | ssize_t written; | |
283 | ||
9de8ab86 A |
284 | SC_trace(_configd_trace, "%s : %5d : fd = %d, msgid = %d\n", |
285 | "-->fd ", | |
286 | storePrivate->server, | |
287 | storePrivate->notifyFile, | |
288 | storePrivate->notifyFileIdentifier); | |
dbf6a266 A |
289 | |
290 | written = write(storePrivate->notifyFile, | |
291 | &storePrivate->notifyFileIdentifier, | |
292 | sizeof(storePrivate->notifyFileIdentifier)); | |
293 | if (written == -1) { | |
294 | if (errno == EWOULDBLOCK) { | |
295 | #ifdef DEBUG | |
9de8ab86 | 296 | SC_log(LOG_DEBUG, "sorry, only one outstanding notification per session"); |
dbf6a266 A |
297 | #endif /* DEBUG */ |
298 | } else { | |
299 | #ifdef DEBUG | |
9de8ab86 | 300 | SC_log(LOG_DEBUG, "could not send notification, write() failed: %s", |
dbf6a266 A |
301 | strerror(errno)); |
302 | #endif /* DEBUG */ | |
303 | storePrivate->notifyFile = -1; | |
304 | } | |
305 | } else if (written != sizeof(storePrivate->notifyFileIdentifier)) { | |
306 | #ifdef DEBUG | |
9de8ab86 | 307 | SC_log(LOG_DEBUG, "could not send notification, incomplete write()"); |
dbf6a266 A |
308 | #endif /* DEBUG */ |
309 | storePrivate->notifyFile = -1; | |
310 | } | |
311 | } | |
312 | ||
313 | if ((storePrivate->notifyStatus == Using_NotifierInformViaSignal) && | |
314 | (storePrivate->notifySignal > 0)) { | |
315 | kern_return_t status; | |
316 | pid_t pid; | |
317 | /* | |
318 | * Post notification as signal | |
319 | */ | |
320 | status = pid_for_task(storePrivate->notifySignalTask, &pid); | |
321 | if (status == KERN_SUCCESS) { | |
9de8ab86 A |
322 | SC_trace(_configd_trace, "%s : %5d : pid = %d, signal = sig%s (%d)\n", |
323 | "-->sig ", | |
324 | storePrivate->server, | |
325 | pid, | |
326 | sys_signame[storePrivate->notifySignal], | |
327 | storePrivate->notifySignal); | |
a40a14f8 | 328 | |
dbf6a266 | 329 | if (kill(pid, storePrivate->notifySignal) != 0) { |
a40a14f8 | 330 | if (errno != ESRCH) { |
9de8ab86 A |
331 | SC_log(LOG_NOTICE, "could not send sig%s to PID %d: %s", |
332 | sys_signame[storePrivate->notifySignal], | |
333 | pid, | |
334 | strerror(errno)); | |
a40a14f8 | 335 | } |
dbf6a266 A |
336 | } |
337 | } else { | |
338 | mach_port_type_t pt; | |
339 | ||
a40a14f8 A |
340 | __MACH_PORT_DEBUG(TRUE, "*** pushNotifications pid_for_task failed: releasing task", storePrivate->notifySignalTask); |
341 | if (mach_port_type(mach_task_self(), storePrivate->notifySignalTask, &pt) == KERN_SUCCESS) { | |
342 | if ((pt & MACH_PORT_TYPE_DEAD_NAME) != 0) { | |
9de8ab86 | 343 | SC_log(LOG_NOTICE, "pid_for_task() failed: %s", mach_error_string(status)); |
a40a14f8 | 344 | } |
dbf6a266 | 345 | } else { |
9de8ab86 | 346 | SC_log(LOG_NOTICE, "mach_port_type() failed: %s", mach_error_string(status)); |
dbf6a266 | 347 | } |
dbf6a266 | 348 | |
dbf6a266 | 349 | /* don't bother with any more attempts */ |
a40a14f8 | 350 | (void) mach_port_deallocate(mach_task_self(), storePrivate->notifySignalTask); |
dbf6a266 A |
351 | storePrivate->notifySignal = 0; |
352 | storePrivate->notifySignalTask = TASK_NULL; | |
353 | } | |
354 | } | |
355 | } | |
5e9ce69e | 356 | if (sessionsToNotify != sessionsToNotify_q) CFAllocatorDeallocate(NULL, sessionsToNotify); |
dbf6a266 A |
357 | |
358 | /* | |
359 | * this list of notifications have been posted, wait for some more. | |
360 | */ | |
361 | CFRelease(needsNotification); | |
362 | needsNotification = NULL; | |
5958d7c0 A |
363 | |
364 | return; | |
365 | } |