1/* 2 * Copyright (c) 2000, 2001, 2003-2005, 2009, 2011, 2012 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 * June 2, 2000 Allan Nathanson <ajn@apple.com> 31 * - initial revision 32 */ 33 34 35#include <unistd.h> 36 37#include "configd.h" 38#include "configd_server.h" 39#include "session.h" 40 41 42__private_extern__ CFMutableDictionaryRef sessionData = NULL; 43 44__private_extern__ CFMutableDictionaryRef storeData = NULL; 45 46__private_extern__ CFMutableDictionaryRef patternData = NULL; 47 48__private_extern__ CFMutableSetRef changedKeys = NULL; 49 50__private_extern__ CFMutableSetRef deferredRemovals = NULL; 51 52__private_extern__ CFMutableSetRef removedSessionKeys = NULL; 53 54__private_extern__ CFMutableSetRef needsNotification = NULL; 55 56 57__private_extern__ 58void 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 /* 72 * Get the dictionary associated with this key out of the store 73 */ 74 dict = CFDictionaryGetValue(storeData, watchedKey); 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); 103 if (i == kCFNotFound) { 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 /* 129 * Update the store for this key 130 */ 131 CFDictionarySetValue(storeData, watchedKey, newDict); 132 CFRelease(newDict); 133 134#ifdef DEBUG 135 SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" _addWatcher: %@, %@"), sessionNum, watchedKey); 136#endif /* DEBUG */ 137 138 return; 139} 140 141 142__private_extern__ 143void 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 /* 157 * Get the dictionary associated with this key out of the store 158 */ 159 dict = CFDictionaryGetValue(storeData, watchedKey); 160 if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) { 161 /* key doesn't exist (isn't this really fatal?) */ 162#ifdef DEBUG 163 SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" _removeWatcher: %@, %@, key not watched"), sessionNum, watchedKey); 164#endif /* DEBUG */ 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); 183 if (i == kCFNotFound) { 184#ifdef DEBUG 185 SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" _removeWatcher: %@, %@, session not watching"), sessionNum, watchedKey); 186#endif /* DEBUG */ 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 */ 220 CFDictionarySetValue(storeData, watchedKey, newDict); 221 } else { 222 /* no information left, remove the empty dictionary */ 223 CFDictionaryRemoveValue(storeData, watchedKey); 224 } 225 CFRelease(newDict); 226 227#ifdef DEBUG 228 SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" _removeWatcher: %@, %@"), sessionNum, watchedKey); 229#endif /* DEBUG */ 230 231 return; 232} 233 234 235#define N_QUICK 64 236 237 238__private_extern__ 239void 240pushNotifications(FILE *_configd_trace) 241{ 242 CFIndex notifyCnt; 243 int server; 244 const void * sessionsToNotify_q[N_QUICK]; 245 const void ** sessionsToNotify = sessionsToNotify_q; 246 SCDynamicStorePrivateRef storePrivate; 247 serverSessionRef theSession; 248 249 if (needsNotification == NULL) 250 return; /* if no sessions need to be kicked */ 251 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], 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)) { 268 /* 269 * Post notification as mach message 270 */ 271 if (_configd_trace != NULL) { 272 SCTrace(TRUE, _configd_trace, 273 CFSTR("%s : %5d : port = %d, msgid = %d\n"), 274 "-->port", 275 storePrivate->server, 276 storePrivate->notifyPort, 277 storePrivate->notifyPortIdentifier); 278 } 279 280 _SC_sendMachMessage(storePrivate->notifyPort, storePrivate->notifyPortIdentifier); 281 } 282 283 if ((storePrivate->notifyStatus == Using_NotifierInformViaFD) && 284 (storePrivate->notifyFile >= 0)) { 285 ssize_t written; 286 287 if (_configd_trace != NULL) { 288 SCTrace(TRUE, _configd_trace, 289 CFSTR("%s : %5d : fd = %d, msgid = %d\n"), 290 "-->fd ", 291 storePrivate->server, 292 storePrivate->notifyFile, 293 storePrivate->notifyFileIdentifier); 294 } 295 296 written = write(storePrivate->notifyFile, 297 &storePrivate->notifyFileIdentifier, 298 sizeof(storePrivate->notifyFileIdentifier)); 299 if (written == -1) { 300 if (errno == EWOULDBLOCK) { 301#ifdef DEBUG 302 SCLog(_configd_verbose, LOG_DEBUG, 303 CFSTR("sorry, only one outstanding notification per session.")); 304#endif /* DEBUG */ 305 } else { 306#ifdef DEBUG 307 SCLog(_configd_verbose, LOG_DEBUG, 308 CFSTR("could not send notification, write() failed: %s"), 309 strerror(errno)); 310#endif /* DEBUG */ 311 storePrivate->notifyFile = -1; 312 } 313 } else if (written != sizeof(storePrivate->notifyFileIdentifier)) { 314#ifdef DEBUG 315 SCLog(_configd_verbose, LOG_DEBUG, 316 CFSTR("could not send notification, incomplete write()")); 317#endif /* DEBUG */ 318 storePrivate->notifyFile = -1; 319 } 320 } 321 322 if ((storePrivate->notifyStatus == Using_NotifierInformViaSignal) && 323 (storePrivate->notifySignal > 0)) { 324 kern_return_t status; 325 pid_t pid; 326 /* 327 * Post notification as signal 328 */ 329 status = pid_for_task(storePrivate->notifySignalTask, &pid); 330 if (status == KERN_SUCCESS) { 331 if (_configd_trace != NULL) { 332 SCTrace(TRUE, _configd_trace, 333 CFSTR("%s : %5d : pid = %d, signal = sig%s (%d)\n"), 334 "-->sig ", 335 storePrivate->server, 336 pid, 337 sys_signame[storePrivate->notifySignal], 338 storePrivate->notifySignal); 339 } 340 341 if (kill(pid, storePrivate->notifySignal) != 0) { 342 if (errno != ESRCH) { 343 SCLog(TRUE, LOG_ERR, 344 CFSTR("could not send sig%s to PID %d: %s"), 345 sys_signame[storePrivate->notifySignal], 346 pid, 347 strerror(errno)); 348 } 349 } 350 } else { 351 mach_port_type_t pt; 352 353 __MACH_PORT_DEBUG(TRUE, "*** pushNotifications pid_for_task failed: releasing task", storePrivate->notifySignalTask); 354 if (mach_port_type(mach_task_self(), storePrivate->notifySignalTask, &pt) == KERN_SUCCESS) { 355 if ((pt & MACH_PORT_TYPE_DEAD_NAME) != 0) { 356 SCLog(TRUE, LOG_ERR, CFSTR("pushNotifications pid_for_task() failed: %s"), mach_error_string(status)); 357 } 358 } else { 359 SCLog(TRUE, LOG_ERR, CFSTR("pushNotifications mach_port_type() failed: %s"), mach_error_string(status)); 360 } 361 362 /* don't bother with any more attempts */ 363 (void) mach_port_deallocate(mach_task_self(), storePrivate->notifySignalTask); 364 storePrivate->notifySignal = 0; 365 storePrivate->notifySignalTask = TASK_NULL; 366 } 367 } 368 } 369 if (sessionsToNotify != sessionsToNotify_q) CFAllocatorDeallocate(NULL, sessionsToNotify); 370 371 /* 372 * this list of notifications have been posted, wait for some more. 373 */ 374 CFRelease(needsNotification); 375 needsNotification = NULL; 376 377 return; 378} 379