#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "scutil.h"
#include "notifications.h"
static int osig;
static struct sigaction *oact = NULL;
static CFComparisonResult
sort_keys(const void *p1, const void *p2, void *context) {
CFStringRef key1 = (CFStringRef)p1;
CFStringRef key2 = (CFStringRef)p2;
return CFStringCompare(key1, key2, 0);
}
__private_extern__
void
storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
int i;
CFIndex n;
SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
n = CFArrayGetCount(changedKeys);
if (n > 0) {
for (i = 0; i < n; i++) {
SCPrint(TRUE,
stdout,
CFSTR(" changed key [%d] = %@\n"),
i,
CFArrayGetValueAtIndex(changedKeys, i));
}
} else {
SCPrint(TRUE, stdout, CFSTR(" no changed key's.\n"));
}
return;
}
__private_extern__
void
do_notify_list(int argc, char **argv)
{
int i;
CFArrayRef list;
CFIndex listCnt;
Boolean isRegex = FALSE;
CFMutableArrayRef sortedList;
if (store == NULL) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
return;
}
if (argc == 1)
isRegex = TRUE;
list = isRegex ? watchedPatterns : watchedKeys;
if (!list) {
SCPrint(TRUE,
stdout,
CFSTR(" no notifier %s.\n"),
isRegex ? "patterns" : "keys");
return;
}
listCnt = CFArrayGetCount(list);
sortedList = CFArrayCreateMutableCopy(NULL, listCnt, list);
CFArraySortValues(sortedList,
CFRangeMake(0, listCnt),
sort_keys,
NULL);
if (listCnt > 0) {
for (i = 0; i < listCnt; i++) {
SCPrint(TRUE,
stdout,
CFSTR(" notifier %s [%d] = %@\n"),
isRegex ? "pattern" : "key",
i,
CFArrayGetValueAtIndex(sortedList, i));
}
} else {
SCPrint(TRUE,
stdout,
CFSTR(" no notifier %s.\n"),
isRegex ? "patterns" : "keys");
}
CFRelease(sortedList);
return;
}
__private_extern__
void
do_notify_add(int argc, char **argv)
{
CFStringRef key;
CFMutableArrayRef keys;
Boolean isRegex = FALSE;
if (store == NULL) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
return;
}
key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
if (argc == 2)
isRegex = TRUE;
keys = isRegex ? watchedPatterns : watchedKeys;
if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists));
CFRelease(key);
return;
}
CFArrayAppendValue(keys, key);
CFRelease(key);
if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
}
return;
}
__private_extern__
void
do_notify_remove(int argc, char **argv)
{
CFStringRef key;
CFMutableArrayRef keys;
CFIndex i;
Boolean isRegex = FALSE;
if (store == NULL) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
return;
}
key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
if (argc == 2)
isRegex = TRUE;
keys = isRegex ? watchedPatterns : watchedKeys;
i = CFArrayGetFirstIndexOfValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key);
CFRelease(key);
if (i == kCFNotFound) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoKey));
return;
}
CFArrayRemoveValueAtIndex(keys, i);
if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
}
return;
}
__private_extern__
void
do_notify_changes(int argc, char **argv)
{
CFArrayRef list;
CFIndex listCnt;
int i;
list = SCDynamicStoreCopyNotifiedKeys(store);
if (!list) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
listCnt = CFArrayGetCount(list);
if (listCnt > 0) {
for (i = 0; i < listCnt; i++) {
SCPrint(TRUE,
stdout,
CFSTR(" changedKey [%d] = %@\n"),
i,
CFArrayGetValueAtIndex(list, i));
}
} else {
SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n"));
}
CFRelease(list);
return;
}
static void *
_watcher(void *arg)
{
notifyRl = CFRunLoopGetCurrent();
if (!notifyRl) {
SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n"));
return NULL;
}
notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (!notifyRls) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return NULL;
}
CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
CFRunLoopRun();
return NULL;
}
__private_extern__
void
do_notify_watch(int argc, char **argv)
{
pthread_attr_t tattr;
pthread_t tid;
if (notifyRl) {
return;
}
pthread_attr_init(&tattr);
pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, _watcher, NULL);
pthread_attr_destroy(&tattr);
return;
}
__private_extern__
void
do_notify_wait(int argc, char **argv)
{
if (!SCDynamicStoreNotifyWait(store)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
return;
}
static boolean_t
notificationWatcher(SCDynamicStoreRef store, void *arg)
{
SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
SCPrint(TRUE, stdout, CFSTR(" arg = %s.\n"), (char *)arg);
return TRUE;
}
static boolean_t
notificationWatcherVerbose(SCDynamicStoreRef store, void *arg)
{
SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
SCPrint(TRUE, stdout, CFSTR(" arg = %s.\n"), (char *)arg);
do_notify_changes(0, NULL);
return TRUE;
}
__private_extern__
void
do_notify_callback(int argc, char **argv)
{
SCDynamicStoreCallBack_v1 func = notificationWatcher;
if ((argc == 1) && (strcmp(argv[0], "verbose") == 0)) {
func = notificationWatcherVerbose;
}
if (!SCDynamicStoreNotifyCallback(store, CFRunLoopGetCurrent(), func, "Changed detected by callback handler!")) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
return;
}
__private_extern__
void
do_notify_file(int argc, char **argv)
{
int32_t reqID = 0;
int fd;
union {
char data[4];
int32_t gotID;
} buf;
char *bufPtr;
int needed;
if (argc == 1) {
if ((sscanf(argv[0], "%d", &reqID) != 1)) {
SCPrint(TRUE, stdout, CFSTR("invalid identifier.\n"));
return;
}
}
if (!SCDynamicStoreNotifyFileDescriptor(store, reqID, &fd)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
bzero(buf.data, sizeof(buf.data));
bufPtr = &buf.data[0];
needed = sizeof(buf.gotID);
while (needed > 0) {
int got;
got = read(fd, bufPtr, needed);
if (got == -1) {
SCPrint(TRUE, stdout, CFSTR("read() failed: %s.\n"), strerror(errno));
break;
}
if (got == 0) {
SCPrint(TRUE, stdout, CFSTR("read(): detected end of file.\n"));
break;
}
SCPrint(TRUE, stdout, CFSTR("Received %d bytes.\n"), got);
bufPtr += got;
needed -= got;
}
if (needed != sizeof(buf.gotID)) {
SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID);
}
(void) SCDynamicStoreNotifyCancel(store);
(void) close(fd);
return;
}
static void
signalCatcher(int signum)
{
static int n = 0;
SCPrint(TRUE, stdout, CFSTR("Received sig%s (#%d).\n"), sys_signame[signum], n++);
return;
}
__private_extern__
void
do_notify_signal(int argc, char **argv)
{
int sig;
pid_t pid;
struct sigaction nact;
int ret;
if (isdigit(*argv[0])) {
if ((sscanf(argv[0], "%d", &sig) != 1) || (sig <= 0) || (sig >= NSIG)) {
SCPrint(TRUE, stdout, CFSTR("signal must be in the range of 1 .. %d.\n"), NSIG-1);
return;
}
} else {
for (sig = 1; sig < NSIG; sig++) {
if (strcasecmp(argv[0], sys_signame[sig]) == 0)
break;
}
if (sig >= NSIG) {
CFMutableStringRef str;
SCPrint(TRUE, stdout, CFSTR("Signal must be one of the following:\n"));
str = CFStringCreateMutable(NULL, 0);
for (sig = 1; sig < NSIG; sig++) {
CFStringAppendFormat(str, NULL, CFSTR(" %-6s"), sys_signame[sig]);
if ((sig % 10) == 0) {
CFStringAppendFormat(str, NULL, CFSTR("\n"));
}
}
if ((sig % 10) != 0) {
CFStringAppendFormat(str, NULL, CFSTR("\n"));
}
SCPrint(TRUE, stdout, CFSTR("%@"), str);
CFRelease(str);
return;
}
}
if ((argc != 2) || (sscanf(argv[1], "%d", &pid) != 1)) {
pid = getpid();
}
if (oact != NULL) {
ret = sigaction(osig, oact, NULL);
} else {
oact = malloc(sizeof(struct sigaction));
}
nact.sa_handler = signalCatcher;
sigemptyset(&nact.sa_mask);
nact.sa_flags = SA_RESTART;
ret = sigaction(sig, &nact, oact);
osig = sig;
SCPrint(TRUE, stdout, CFSTR("signal handler started.\n"));
if (!SCDynamicStoreNotifySignal(store, pid, sig)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
return;
}
__private_extern__
void
do_notify_cancel(int argc, char **argv)
{
int ret;
if (notifyRls) {
CFRunLoopSourceInvalidate(notifyRls);
CFRelease(notifyRls);
notifyRls = NULL;
}
if (notifyRl) {
CFRunLoopStop(notifyRl);
notifyRl = NULL;
}
if (!SCDynamicStoreNotifyCancel(store)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
if (oact != NULL) {
ret = sigaction(osig, oact, NULL);
free(oact);
oact = NULL;
}
return;
}