#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <unistd.h>
#include "scutil.h"
#include "notifications.h"
static char *
elapsed()
{
size_t n;
static char str[128];
struct tm tm_diff;
struct tm tm_now;
struct timeval tv_diff;
struct timeval tv_now;
static struct timeval tv_then = { 0, 0 };
(void)gettimeofday(&tv_now, NULL);
(void)localtime_r(&tv_now.tv_sec, &tm_now);
timersub(&tv_now, &tv_then, &tv_diff);
(void)localtime_r(&tv_diff.tv_sec, &tm_diff);
n = snprintf(str, sizeof(str), "%2d:%02d:%02d.%03d",
tm_now.tm_hour,
tm_now.tm_min,
tm_now.tm_sec,
tv_now.tv_usec / 1000);
if (((tv_then.tv_sec != 0) || (tv_then.tv_usec != 0)) && (n < sizeof(str))) {
snprintf(&str[n], sizeof(str) - n, " (+%ld.%03d)",
tv_diff.tv_sec,
tv_diff.tv_usec / 1000);
}
tv_then = tv_now;
return str;
}
static CFComparisonResult
sort_keys(const void *p1, const void *p2, void *context)
{
#pragma unused(context)
CFStringRef key1 = (CFStringRef)p1;
CFStringRef key2 = (CFStringRef)p2;
return CFStringCompare(key1, key2, 0);
}
__private_extern__
void
storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
#pragma unused(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(" %s changedKey [%d] = %@\n"),
elapsed(),
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)
{
#pragma unused(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)
{
#pragma unused(argc)
#pragma unused(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(" %s changedKey [%d] = %@\n"),
elapsed(),
i,
CFArrayGetValueAtIndex(list, i));
}
} else {
SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n"));
}
CFRelease(list);
return;
}
static void *
_watcher(void *arg)
{
#pragma unused(arg)
notifyRl = CFRunLoopGetCurrent();
if (notifyRl == NULL) {
SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n"));
return NULL;
}
if (doDispatch) {
if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
notifyRl = NULL;
return NULL;
}
notifyRls = (CFRunLoopSourceRef)kCFNull;
} else {
notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (notifyRls == NULL) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
notifyRl = NULL;
return NULL;
}
CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
}
pthread_setname_np("n.watch");
CFRunLoopRun();
notifyRl = NULL;
return NULL;
}
__private_extern__
void
do_notify_watch(int argc, char **argv)
{
#pragma unused(argc)
#pragma unused(argv)
pthread_attr_t tattr;
pthread_t tid;
if (notifyRl != NULL) {
SCPrint(TRUE, stdout, CFSTR("already active\n"));
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)
{
#pragma unused(argc)
#pragma unused(argv)
if (!SCDynamicStoreNotifyWait(store)) {
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;
}
memset(buf.data, 0, sizeof(buf.data));
bufPtr = &buf.data[0];
needed = sizeof(buf.gotID);
while (needed > 0) {
ssize_t 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 %ld bytes.\n"), got);
bufPtr += got;
needed -= got;
}
if (needed != sizeof(buf.gotID)) {
SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID);
}
do_notify_changes(0, NULL);
(void) SCDynamicStoreNotifyCancel(store);
(void) close(fd);
return;
}
__private_extern__
void
do_notify_cancel(int argc, char **argv)
{
#pragma unused(argc)
#pragma unused(argv)
if (notifyRls != NULL) {
if (doDispatch) {
if (!SCDynamicStoreSetDispatchQueue(store, NULL)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
} else {
CFRunLoopSourceInvalidate(notifyRls);
CFRelease(notifyRls);
}
notifyRls = NULL;
} else {
if (!SCDynamicStoreNotifyCancel(store)) {
SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
return;
}
}
if (notifyRl != NULL) {
CFRunLoopStop(notifyRl);
}
return;
}