2 * Copyright (c) 2000-2005, 2008-2015, 2017, 2018 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
35 #include <sys/types.h>
36 #include <sys/socket.h>
42 #include "notifications.h"
52 struct timeval tv_diff
;
53 struct timeval tv_now
;
54 static struct timeval tv_then
= { 0, 0 };
56 (void)gettimeofday(&tv_now
, NULL
);
58 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
60 timersub(&tv_now
, &tv_then
, &tv_diff
);
61 (void)localtime_r(&tv_diff
.tv_sec
, &tm_diff
);
62 n
= snprintf(str
, sizeof(str
), "%2d:%02d:%02d.%03d",
66 tv_now
.tv_usec
/ 1000);
67 if (((tv_then
.tv_sec
!= 0) || (tv_then
.tv_usec
!= 0)) && (n
< sizeof(str
))) {
68 snprintf(&str
[n
], sizeof(str
) - n
, " (+%ld.%03d)",
70 tv_diff
.tv_usec
/ 1000);
78 static CFComparisonResult
79 sort_keys(const void *p1
, const void *p2
, void *context
)
81 #pragma unused(context)
82 CFStringRef key1
= (CFStringRef
)p1
;
83 CFStringRef key2
= (CFStringRef
)p2
;
84 return CFStringCompare(key1
, key2
, 0);
90 storeCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
96 SCPrint(TRUE
, stdout
, CFSTR("notification callback (store address = %p).\n"), store
);
98 n
= CFArrayGetCount(changedKeys
);
100 for (i
= 0; i
< n
; i
++) {
103 CFSTR(" %s changedKey [%d] = %@\n"),
106 CFArrayGetValueAtIndex(changedKeys
, i
));
109 SCPrint(TRUE
, stdout
, CFSTR(" no changed key's.\n"));
118 do_notify_list(int argc
, char **argv
)
124 Boolean isRegex
= FALSE
;
125 CFMutableArrayRef sortedList
;
128 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession
));
135 list
= isRegex
? watchedPatterns
: watchedKeys
;
139 CFSTR(" no notifier %s.\n"),
140 isRegex
? "patterns" : "keys");
144 listCnt
= CFArrayGetCount(list
);
145 sortedList
= CFArrayCreateMutableCopy(NULL
, listCnt
, list
);
146 CFArraySortValues(sortedList
,
147 CFRangeMake(0, listCnt
),
152 for (i
= 0; i
< listCnt
; i
++) {
155 CFSTR(" notifier %s [%d] = %@\n"),
156 isRegex
? "pattern" : "key",
158 CFArrayGetValueAtIndex(sortedList
, i
));
163 CFSTR(" no notifier %s.\n"),
164 isRegex
? "patterns" : "keys");
166 CFRelease(sortedList
);
174 do_notify_add(int argc
, char **argv
)
177 CFMutableArrayRef keys
;
178 Boolean isRegex
= FALSE
;
181 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession
));
185 key
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
190 keys
= isRegex
? watchedPatterns
: watchedKeys
;
191 if (CFArrayContainsValue(keys
, CFRangeMake(0, CFArrayGetCount(keys
)), key
)) {
192 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists
));
197 CFArrayAppendValue(keys
, key
);
200 if (!SCDynamicStoreSetNotificationKeys(store
, watchedKeys
, watchedPatterns
)) {
201 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
210 do_notify_remove(int argc
, char **argv
)
213 CFMutableArrayRef keys
;
215 Boolean isRegex
= FALSE
;
218 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession
));
222 key
= CFStringCreateWithCString(NULL
, argv
[0], kCFStringEncodingUTF8
);
227 keys
= isRegex
? watchedPatterns
: watchedKeys
;
228 i
= CFArrayGetFirstIndexOfValue(keys
, CFRangeMake(0, CFArrayGetCount(keys
)), key
);
231 if (i
== kCFNotFound
) {
232 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(kSCStatusNoKey
));
236 CFArrayRemoveValueAtIndex(keys
, i
);
238 if (!SCDynamicStoreSetNotificationKeys(store
, watchedKeys
, watchedPatterns
)) {
239 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
248 do_notify_changes(int argc
, char **argv
)
256 list
= SCDynamicStoreCopyNotifiedKeys(store
);
258 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
262 listCnt
= CFArrayGetCount(list
);
264 for (i
= 0; i
< listCnt
; i
++) {
267 CFSTR(" %s changedKey [%d] = %@\n"),
270 CFArrayGetValueAtIndex(list
, i
));
273 SCPrint(TRUE
, stdout
, CFSTR(" no changedKey's.\n"));
285 notifyRl
= CFRunLoopGetCurrent();
286 if (notifyRl
== NULL
) {
287 SCPrint(TRUE
, stdout
, CFSTR(" CFRunLoopGetCurrent() failed\n"));
292 if (!SCDynamicStoreSetDispatchQueue(store
, dispatch_get_main_queue())) {
293 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
297 notifyRls
= (CFRunLoopSourceRef
)kCFNull
;
299 notifyRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
300 if (notifyRls
== NULL
) {
301 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
305 CFRunLoopAddSource(notifyRl
, notifyRls
, kCFRunLoopDefaultMode
);
308 pthread_setname_np("n.watch");
316 do_notify_watch(int argc
, char **argv
)
320 pthread_attr_t tattr
;
323 if (notifyRl
!= NULL
) {
324 SCPrint(TRUE
, stdout
, CFSTR("already active\n"));
328 pthread_attr_init(&tattr
);
329 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
330 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
331 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
332 pthread_create(&tid
, &tattr
, _watcher
, NULL
);
333 pthread_attr_destroy(&tattr
);
341 do_notify_wait(int argc
, char **argv
)
345 if (!SCDynamicStoreNotifyWait(store
)) {
346 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
356 do_notify_file(int argc
, char **argv
)
368 if ((sscanf(argv
[0], "%d", &reqID
) != 1)) {
369 SCPrint(TRUE
, stdout
, CFSTR("invalid identifier.\n"));
374 if (!SCDynamicStoreNotifyFileDescriptor(store
, reqID
, &fd
)) {
375 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
379 memset(buf
.data
, 0, sizeof(buf
.data
));
380 bufPtr
= &buf
.data
[0];
381 needed
= sizeof(buf
.gotID
);
385 got
= read(fd
, bufPtr
, needed
);
387 /* if error detected */
388 SCPrint(TRUE
, stdout
, CFSTR("read() failed: %s.\n"), strerror(errno
));
393 /* if end of file detected */
394 SCPrint(TRUE
, stdout
, CFSTR("read(): detected end of file.\n"));
398 SCPrint(TRUE
, stdout
, CFSTR("Received %ld bytes.\n"), got
);
403 if (needed
!= sizeof(buf
.gotID
)) {
404 SCPrint(TRUE
, stdout
, CFSTR(" Received notification, identifier = %d.\n"), buf
.gotID
);
407 /* report the keys that changed */
408 do_notify_changes(0, NULL
);
410 /* this utility only allows processes one notification per "n.file" request */
411 (void) SCDynamicStoreNotifyCancel(store
);
413 (void) close(fd
); /* close my side of the file descriptor */
421 do_notify_cancel(int argc
, char **argv
)
425 if (notifyRls
!= NULL
) {
427 if (!SCDynamicStoreSetDispatchQueue(store
, NULL
)) {
428 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
432 CFRunLoopSourceInvalidate(notifyRls
);
433 CFRelease(notifyRls
);
437 if (!SCDynamicStoreNotifyCancel(store
)) {
438 SCPrint(TRUE
, stdout
, CFSTR(" %s\n"), SCErrorString(SCError()));
443 if (notifyRl
!= NULL
) {
444 CFRunLoopStop(notifyRl
);