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