Boolean
SCDynamicStoreAddWatchedKey(SCDynamicStoreRef store, CFStringRef key, Boolean isRegex)
{
	SCDynamicStorePrivateRef	storePrivate = (SCDynamicStorePrivateRef)store;
	kern_return_t			status;
	CFDataRef			utfKey;		/* serialized key */
	xmlData_t			myKeyRef;
	CFIndex				myKeyLen;
	int				sc_status;

	if (store == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoStoreSession);
		return FALSE;
	}

	if (storePrivate->server == MACH_PORT_NULL) {
		/* sorry, you must have an open session to play */
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;
	}

	/* serialize the key */
	if (!_SCSerializeString(key, &utfKey, (void **)&myKeyRef, &myKeyLen)) {
		_SCErrorSet(kSCStatusFailed);
		return FALSE;
	}

    retry :

	/* send the key to the server */
	status = notifyadd(storePrivate->server,
			   myKeyRef,
			   myKeyLen,
			   isRegex,
			   (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreAddWatchedKey notifyadd()")) {
		goto retry;
	}

	/* clean up */
	CFRelease(utfKey);

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	if (isRegex) {
		addKey(&storePrivate->patterns, key);
	} else {
		addKey(&storePrivate->keys, key);
	}
	return TRUE;
}
Boolean
SCDynamicStoreRemoveValue(SCDynamicStoreRef store, CFStringRef key)
{
	SCDynamicStorePrivateRef	storePrivate;
	kern_return_t			status;
	CFDataRef			utfKey;		/* serialized key */
	xmlData_t			myKeyRef;
	CFIndex				myKeyLen;
	int				sc_status;

	if (store == NULL) {
		store = __SCDynamicStoreNullSession();
		if (store == NULL) {
			/* sorry, you must provide a session */
			_SCErrorSet(kSCStatusNoStoreSession);
			return FALSE;
		}
	}

	storePrivate = (SCDynamicStorePrivateRef)store;
	if (storePrivate->server == MACH_PORT_NULL) {
		/* sorry, you must have an open session to play */
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;
	}

	/* serialize the key */
	if (!_SCSerializeString(key, &utfKey, (void **)&myKeyRef, &myKeyLen)) {
		_SCErrorSet(kSCStatusFailed);
		return FALSE;
	}

    retry :

	/* send the key to the server */
	status = configremove(storePrivate->server,
			      myKeyRef,
			      myKeyLen,
			      (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreRemoveValue configremove()")) {
		goto retry;
	}

	/* clean up */
	CFRelease(utfKey);

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	return TRUE;
}
Boolean
SCDynamicStoreNotifyCancel(SCDynamicStoreRef store)
{
	SCDynamicStorePrivateRef	storePrivate = (SCDynamicStorePrivateRef)store;
	kern_return_t			status;
	int				sc_status;

	if (store == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoStoreSession);
		return FALSE;
	}

	switch (storePrivate->notifyStatus) {
		case NotifierNotRegistered :
			/* if no notifications have been registered */
			return TRUE;
		case Using_NotifierInformViaRunLoop :
			CFRunLoopSourceInvalidate(storePrivate->rls);
			storePrivate->rls = NULL;
			return TRUE;
		case Using_NotifierInformViaDispatch :
			(void) SCDynamicStoreSetDispatchQueue(store, NULL);
			return TRUE;
		default :
			break;
	}

	if (storePrivate->server == MACH_PORT_NULL) {
		/* sorry, you must have an open session to play */
		sc_status = kSCStatusNoStoreServer;
		goto done;
	}

	status = notifycancel(storePrivate->server, (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreNotifyCancel notifycancel()")) {
		sc_status = kSCStatusOK;
	}

    done :

	/* set notifier inactive */
	storePrivate->notifyStatus = NotifierNotRegistered;

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	return TRUE;
}
Boolean
SCDynamicStoreNotifySignal(SCDynamicStoreRef store, pid_t pid, int sig)
{
	SCDynamicStorePrivateRef	storePrivate = (SCDynamicStorePrivateRef)store;
	kern_return_t			status;
	int				sc_status;
	task_t				task;

	if (store == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoStoreSession);
		return FALSE;
	}

	if (storePrivate->server == MACH_PORT_NULL) {
		/* sorry, you must have an open session to play */
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;
	}

	if (storePrivate->notifyStatus != NotifierNotRegistered) {
		/* sorry, you can only have one notification registered at once */
		_SCErrorSet(kSCStatusNotifierActive);
		return FALSE;
	}

	status = task_for_pid(mach_task_self(), pid, &task);
	if (status != KERN_SUCCESS) {
		SC_log(LOG_ERR, "task_for_pid() failed: %s", mach_error_string(status));
		_SCErrorSet(status);
		return FALSE;
	}

    retry :

	status = notifyviasignal(storePrivate->server, task, sig, (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreNotifySignal notifyviasignal()")) {
		goto retry;
	}

	if (status != KERN_SUCCESS) {
		_SCErrorSet(status);
		return FALSE;
	}

	/* set notifier active */
	storePrivate->notifyStatus = Using_NotifierInformViaSignal;

	return TRUE;
}
static void
rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
{
	SCDynamicStoreRef		store		= (SCDynamicStoreRef)info;
	SCDynamicStorePrivateRef	storePrivate	= (SCDynamicStorePrivateRef)store;

#ifdef	DEBUG
	SCLog(_sc_verbose, LOG_DEBUG,
	      CFSTR("schedule notifications for mode %@"),
	      (rl != NULL) ? mode : CFSTR("libdispatch"));
#endif	/* DEBUG */

	if (storePrivate->rlList == NULL) {
		CFMachPortContext	context		= { 0
							  , (void *)store
							  , CFRetain
							  , CFRelease
							  , notifyMPCopyDescription
							  };
		mach_port_t		oldNotify;
		mach_port_t		port;
		int			sc_status;
		kern_return_t		status;

#ifdef	DEBUG
		SCLog(_sc_verbose, LOG_DEBUG, CFSTR("  activate callback runloop source"));
#endif	/* DEBUG */

		/* Allocating port (for server response) */
		status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
		if (status != KERN_SUCCESS) {
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status));
			return;
		}

		status = mach_port_insert_right(mach_task_self(),
						port,
						port,
						MACH_MSG_TYPE_MAKE_SEND);
		if (status != KERN_SUCCESS) {
			/*
			 * We can't insert a send right into our own port!  This should
			 * only happen if someone stomped on OUR port (so let's leave
			 * the port alone).
			 */
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
			return;
		}

		/* Request a notification when/if the server dies */
		status = mach_port_request_notification(mach_task_self(),
							port,
							MACH_NOTIFY_NO_SENDERS,
							1,
							port,
							MACH_MSG_TYPE_MAKE_SEND_ONCE,
							&oldNotify);
		if (status != KERN_SUCCESS) {
			/*
			 * We can't request a notification for our own port!  This should
			 * only happen if someone stomped on OUR port (so let's leave
			 * the port alone).
			 */
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status));
			return;
		}

		if (oldNotify != MACH_PORT_NULL) {
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
		}

	    retry :

		__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
		status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);

		if (__SCDynamicStoreCheckRetryAndHandleError(store,
							     status,
							     &sc_status,
							     "rlsSchedule notifyviaport()")) {
			goto retry;
		}

		if (status != KERN_SUCCESS) {
			if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
				/* remove the send right that we tried (but failed) to pass to the server */
				(void) mach_port_deallocate(mach_task_self(), port);
			}

			/* remove our receive right  */
			(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
			return;
		}

		if (sc_status != kSCStatusOK) {
			/* something [else] didn't work, remove our receive right  */
			(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
			return;
		}

		__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
		storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
									   port,
									   rlsCallback,
									   &context);
		storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);

		storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	}

	if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
		if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
			/*
			 * if we are not already scheduled with this runLoop / runLoopMode
			 */
			CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
			__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
		}

		_SC_schedule(store, rl, mode, storePrivate->rlList);
	}

	return;
}
static void
rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
{
	CFIndex				n		= 0;
	SCDynamicStoreRef		store		= (SCDynamicStoreRef)info;
	SCDynamicStorePrivateRef	storePrivate	= (SCDynamicStorePrivateRef)store;

#ifdef	DEBUG
	SCLog(_sc_verbose, LOG_DEBUG,
	      CFSTR("cancel notifications for mode %@"),
	      (rl != NULL) ? mode : CFSTR("libdispatch"));
#endif	/* DEBUG */

	if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
		if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
			/*
			 * if currently scheduled on this runLoop / runLoopMode
			 */
			n = CFArrayGetCount(storePrivate->rlList);
			if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
				/*
				 * if we are no longer scheduled to receive notifications for
				 * this runLoop / runLoopMode
				 */
				CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
			}
		}
	}

	if (n == 0) {
		int		sc_status;
		kern_return_t	status;

#ifdef	DEBUG
		SCLog(_sc_verbose, LOG_DEBUG, CFSTR("  cancel callback runloop source"));
#endif	/* DEBUG */
		__MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
				  "*** rlsCancel",
				  CFMachPortGetPort(storePrivate->rlsNotifyPort));

		if (storePrivate->rlList != NULL) {
			CFRelease(storePrivate->rlList);
			storePrivate->rlList = NULL;
		}

		if (storePrivate->rlsNotifyRLS != NULL) {
			/* invalidate & remove the run loop source */
			CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
			CFRelease(storePrivate->rlsNotifyRLS);
			storePrivate->rlsNotifyRLS = NULL;
		}

		if (storePrivate->rlsNotifyPort != NULL) {
			mach_port_t	mp;

			mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
			__MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
					  "*** rlsCancel (before invalidating/releasing CFMachPort)",
					  mp);

			/* invalidate and release port */
			CFMachPortInvalidate(storePrivate->rlsNotifyPort);
			CFRelease(storePrivate->rlsNotifyPort);
			storePrivate->rlsNotifyPort = NULL;

			/* and, finally, remove our receive right  */
			(void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
		}

		if (storePrivate->server != MACH_PORT_NULL) {
			status = notifycancel(storePrivate->server, (int *)&sc_status);

			(void) __SCDynamicStoreCheckRetryAndHandleError(store,
									status,
									&sc_status,
									"rlsCancel notifycancel()");

			if (status != KERN_SUCCESS) {
				return;
			}
		}
	}

	return;
}
Boolean
SCDynamicStoreSetNotificationKeys(SCDynamicStoreRef	store,
				  CFArrayRef		keys,
				  CFArrayRef		patterns)
{
	SCDynamicStorePrivateRef	storePrivate = (SCDynamicStorePrivateRef)store;
	kern_return_t			status;
	CFDataRef			xmlKeys		= NULL;	/* keys (XML serialized) */
	xmlData_t			myKeysRef	= NULL;	/* keys (serialized) */
	CFIndex				myKeysLen	= 0;
	CFDataRef			xmlPatterns	= NULL;	/* patterns (XML serialized) */
	xmlData_t			myPatternsRef	= NULL;	/* patterns (serialized) */
	CFIndex				myPatternsLen	= 0;
	int				sc_status;
	CFMutableArrayRef		tmp;

	if (store == NULL) {
		/* sorry, you must provide a session */
		_SCErrorSet(kSCStatusNoStoreSession);
		return FALSE;
	}

	if (storePrivate->server == MACH_PORT_NULL) {
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;	/* you must have an open session to play */
	}

	/* serialize the keys */
	if (keys != NULL) {
		if (!_SCSerialize(keys, &xmlKeys, (void **)&myKeysRef, &myKeysLen)) {
			_SCErrorSet(kSCStatusFailed);
			return FALSE;
		}
	}

	/* serialize the patterns */
	if (patterns != NULL) {
		if (!_SCSerialize(patterns, &xmlPatterns, (void **)&myPatternsRef, &myPatternsLen)) {
			if (xmlKeys != NULL) CFRelease(xmlKeys);
			_SCErrorSet(kSCStatusFailed);
			return FALSE;
		}
	}

    retry :

	/* send the keys and patterns, fetch the associated result from the server */
	status = notifyset(storePrivate->server,
			   myKeysRef,
			   myKeysLen,
			   myPatternsRef,
			   myPatternsLen,
			   (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreSetNotificationKeys notifyset()")) {
		goto retry;
	}

	/* clean up */
	if (xmlKeys != NULL)		CFRelease(xmlKeys);
	if (xmlPatterns != NULL)	CFRelease(xmlPatterns);

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	/* in case we need to re-connect, save the keys/patterns */
	tmp = (keys != NULL) ? CFArrayCreateMutableCopy(NULL, 0, keys) : NULL;
	if (storePrivate->keys != NULL) CFRelease(storePrivate->keys);
	storePrivate->keys = tmp;

	tmp = (patterns != NULL) ? CFArrayCreateMutableCopy(NULL, 0, patterns) : NULL;
	if (storePrivate->patterns != NULL) CFRelease(storePrivate->patterns);
	storePrivate->patterns = tmp;

	return TRUE;
}
Boolean
SCDynamicStoreSetMultiple(SCDynamicStoreRef	store,
			  CFDictionaryRef	keysToSet,
			  CFArrayRef		keysToRemove,
			  CFArrayRef		keysToNotify)
{
	SCDynamicStorePrivateRef	storePrivate;
	kern_return_t			status;
	CFDataRef			xmlSet		= NULL;	/* key/value pairs to set (XML serialized) */
	xmlData_t			mySetRef	= NULL;	/* key/value pairs to set (serialized) */
	CFIndex				mySetLen	= 0;
	CFDataRef			xmlRemove	= NULL;	/* keys to remove (XML serialized) */
	xmlData_t			myRemoveRef	= NULL;	/* keys to remove (serialized) */
	CFIndex				myRemoveLen	= 0;
	CFDataRef			xmlNotify	= NULL;	/* keys to notify (XML serialized) */
	xmlData_t			myNotifyRef	= NULL;	/* keys to notify (serialized) */
	CFIndex				myNotifyLen	= 0;
	int				sc_status;

	if (store == NULL) {
		store = __SCDynamicStoreNullSession();
		if (store == NULL) {
			/* sorry, you must provide a session */
			_SCErrorSet(kSCStatusNoStoreSession);
			return FALSE;
		}
	}

	storePrivate = (SCDynamicStorePrivateRef)store;
	if (storePrivate->server == MACH_PORT_NULL) {
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;	/* you must have an open session to play */
	}

	/* serialize the key/value pairs to set*/
	if (keysToSet != NULL) {
		CFDictionaryRef	newInfo;
		Boolean		ok;

		newInfo = _SCSerializeMultiple(keysToSet);
		if (newInfo == NULL) {
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
		}

		ok = _SCSerialize(newInfo, &xmlSet, (void **)&mySetRef, &mySetLen);
		CFRelease(newInfo);
		if (!ok) {
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
		}
	}

	/* serialize the keys to remove */
	if (keysToRemove != NULL) {
		if (!_SCSerialize(keysToRemove, &xmlRemove, (void **)&myRemoveRef, &myRemoveLen)) {
			if (xmlSet != NULL)	CFRelease(xmlSet);
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
		}
	}

	/* serialize the keys to notify */
	if (keysToNotify != NULL) {
		if (!_SCSerialize(keysToNotify, &xmlNotify, (void **)&myNotifyRef, &myNotifyLen)) {
			if (xmlSet != NULL)	CFRelease(xmlSet);
			if (xmlRemove != NULL)	CFRelease(xmlRemove);
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
		}
	}

    retry :

	/* send the keys and patterns, fetch the associated result from the server */
	status = configset_m(storePrivate->server,
			     mySetRef,
			     mySetLen,
			     myRemoveRef,
			     myRemoveLen,
			     myNotifyRef,
			     myNotifyLen,
			     (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreSetMultiple configset_m()")) {
		goto retry;
	}

	/* clean up */
	if (xmlSet != NULL)	CFRelease(xmlSet);
	if (xmlRemove != NULL)	CFRelease(xmlRemove);
	if (xmlNotify != NULL)	CFRelease(xmlNotify);

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	return TRUE;
}
Boolean
SCDynamicStoreSetValue(SCDynamicStoreRef store, CFStringRef key, CFPropertyListRef value)
{
	SCDynamicStorePrivateRef	storePrivate;
	kern_return_t			status;
	CFDataRef			utfKey;		/* serialized key */
	xmlData_t			myKeyRef;
	CFIndex				myKeyLen;
	CFDataRef			xmlData;	/* serialized data */
	xmlData_t			myDataRef;
	CFIndex				myDataLen;
	int				sc_status;
	int				newInstance;

	if (store == NULL) {
		store = __SCDynamicStoreNullSession();
		if (store == NULL) {
			/* sorry, you must provide a session */
			_SCErrorSet(kSCStatusNoStoreSession);
			return FALSE;
		}
	}

	storePrivate = (SCDynamicStorePrivateRef)store;
	if (storePrivate->server == MACH_PORT_NULL) {
		/* sorry, you must have an open session to play */
		_SCErrorSet(kSCStatusNoStoreServer);
		return FALSE;
	}

	/* serialize the key */
	if (!_SCSerializeString(key, &utfKey, (void **)&myKeyRef, &myKeyLen)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	/* serialize the data */
	if (!_SCSerialize(value, &xmlData, (void **)&myDataRef, &myDataLen)) {
		CFRelease(utfKey);
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

    retry :

	/* send the key & data to the server, get new instance id */
	status = configset(storePrivate->server,
			   myKeyRef,
			   myKeyLen,
			   myDataRef,
			   myDataLen,
			   0,
			   &newInstance,
			   (int *)&sc_status);

	if (__SCDynamicStoreCheckRetryAndHandleError(store,
						     status,
						     &sc_status,
						     "SCDynamicStoreSetValue configset()")) {
		goto retry;
	}

	/* clean up */
	CFRelease(utfKey);
	CFRelease(xmlData);

	if (sc_status != kSCStatusOK) {
		_SCErrorSet(sc_status);
		return FALSE;
	}

	return TRUE;
}