]> git.saurik.com Git - apple/configd.git/blame - scutil.tproj/notifications.c
configd-453.19.tar.gz
[apple/configd.git] / scutil.tproj / notifications.c
CommitLineData
5958d7c0 1/*
17d3ee29 2 * Copyright (c) 2000-2004, 2008-2011 Apple Inc. All rights reserved.
5958d7c0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
009ee3c6 5 *
009ee3c6
A
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
5958d7c0
A
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
009ee3c6
A
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 *
5958d7c0
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
0fae82ee
A
24/*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
009ee3c6 34#include <pthread.h>
5958d7c0
A
35#include <sys/types.h>
36#include <sys/socket.h>
17d3ee29 37#include <sys/time.h>
5958d7c0
A
38#include <sys/un.h>
39#include <unistd.h>
40
41#include "scutil.h"
dbf6a266 42#include "notifications.h"
0fae82ee
A
43
44
45static int osig;
46static struct sigaction *oact = NULL;
47
48
17d3ee29
A
49static char *
50elapsed()
51{
52 int n;
53 static char str[128];
54 struct tm tm_diff;
55 struct tm tm_now;
56 struct timeval tv_diff;
57 struct timeval tv_now;
58 static struct timeval tv_then = { 0, 0 };
59
60 (void)gettimeofday(&tv_now, NULL);
61
62 (void)localtime_r(&tv_now.tv_sec, &tm_now);
63
64 timersub(&tv_now, &tv_then, &tv_diff);
65 (void)localtime_r(&tv_diff.tv_sec, &tm_diff);
66 n = snprintf(str, sizeof(str), "%2d:%02d:%02d.%03d",
67 tm_now.tm_hour,
68 tm_now.tm_min,
69 tm_now.tm_sec,
70 tv_now.tv_usec / 1000);
71 if (((tv_then.tv_sec != 0) || (tv_then.tv_usec != 0)) && (n < sizeof(str))) {
72 snprintf(&str[n], sizeof(str) - n, " (+%ld.%03d)",
73 tv_diff.tv_sec,
74 tv_diff.tv_usec / 1000);
75 }
76
77 tv_then = tv_now;
78 return str;
79}
80
81
0fae82ee
A
82static CFComparisonResult
83sort_keys(const void *p1, const void *p2, void *context) {
84 CFStringRef key1 = (CFStringRef)p1;
85 CFStringRef key2 = (CFStringRef)p2;
86 return CFStringCompare(key1, key2, 0);
87}
88
5958d7c0 89
dbf6a266 90__private_extern__
5958d7c0 91void
0fae82ee 92storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
5958d7c0 93{
5958d7c0 94 int i;
0fae82ee
A
95 CFIndex n;
96
97 SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
98
99 n = CFArrayGetCount(changedKeys);
100 if (n > 0) {
009ee3c6 101 for (i = 0; i < n; i++) {
0fae82ee
A
102 SCPrint(TRUE,
103 stdout,
17d3ee29
A
104 CFSTR(" %s changedKey [%d] = %@\n"),
105 elapsed(),
0fae82ee
A
106 i,
107 CFArrayGetValueAtIndex(changedKeys, i));
108 }
109 } else {
a5f60add 110 SCPrint(TRUE, stdout, CFSTR(" no changed key's.\n"));
0fae82ee
A
111 }
112
113 return;
114}
115
116
dbf6a266 117__private_extern__
0fae82ee
A
118void
119do_notify_list(int argc, char **argv)
120{
121 int i;
122 CFArrayRef list;
123 CFIndex listCnt;
124 Boolean isRegex = FALSE;
125 CFMutableArrayRef sortedList;
5958d7c0 126
dbf6a266 127 if (store == NULL) {
009ee3c6
A
128 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
129 return;
130 }
131
5958d7c0 132 if (argc == 1)
0fae82ee 133 isRegex = TRUE;
5958d7c0 134
009ee3c6 135 list = isRegex ? watchedPatterns : watchedKeys;
0fae82ee 136 if (!list) {
009ee3c6
A
137 SCPrint(TRUE,
138 stdout,
139 CFSTR(" no notifier %s.\n"),
140 isRegex ? "patterns" : "keys");
5958d7c0
A
141 return;
142 }
143
144 listCnt = CFArrayGetCount(list);
0fae82ee 145 sortedList = CFArrayCreateMutableCopy(NULL, listCnt, list);
0fae82ee
A
146 CFArraySortValues(sortedList,
147 CFRangeMake(0, listCnt),
148 sort_keys,
149 NULL);
150
5958d7c0 151 if (listCnt > 0) {
009ee3c6 152 for (i = 0; i < listCnt; i++) {
0fae82ee
A
153 SCPrint(TRUE,
154 stdout,
a5f60add
A
155 CFSTR(" notifier %s [%d] = %@\n"),
156 isRegex ? "pattern" : "key",
0fae82ee
A
157 i,
158 CFArrayGetValueAtIndex(sortedList, i));
5958d7c0
A
159 }
160 } else {
a5f60add
A
161 SCPrint(TRUE,
162 stdout,
163 CFSTR(" no notifier %s.\n"),
164 isRegex ? "patterns" : "keys");
5958d7c0 165 }
0fae82ee 166 CFRelease(sortedList);
5958d7c0
A
167
168 return;
169}
170
171
dbf6a266 172__private_extern__
5958d7c0
A
173void
174do_notify_add(int argc, char **argv)
175{
009ee3c6
A
176 CFStringRef key;
177 CFMutableArrayRef keys;
178 Boolean isRegex = FALSE;
179
dbf6a266 180 if (store == NULL) {
009ee3c6
A
181 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
182 return;
183 }
5958d7c0 184
dbf6a266 185 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
5958d7c0
A
186
187 if (argc == 2)
0fae82ee 188 isRegex = TRUE;
5958d7c0 189
009ee3c6
A
190 keys = isRegex ? watchedPatterns : watchedKeys;
191 if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key)) {
192 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists));
193 CFRelease(key);
194 return;
5958d7c0 195 }
009ee3c6
A
196
197 CFArrayAppendValue(keys, key);
0fae82ee 198 CFRelease(key);
009ee3c6
A
199
200 if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
201 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
202 }
203
5958d7c0
A
204 return;
205}
206
207
dbf6a266 208__private_extern__
5958d7c0
A
209void
210do_notify_remove(int argc, char **argv)
211{
009ee3c6
A
212 CFStringRef key;
213 CFMutableArrayRef keys;
214 CFIndex i;
215 Boolean isRegex = FALSE;
216
dbf6a266 217 if (store == NULL) {
009ee3c6
A
218 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
219 return;
220 }
5958d7c0 221
dbf6a266 222 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
5958d7c0
A
223
224 if (argc == 2)
0fae82ee 225 isRegex = TRUE;
5958d7c0 226
009ee3c6
A
227 keys = isRegex ? watchedPatterns : watchedKeys;
228 i = CFArrayGetFirstIndexOfValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key);
229 CFRelease(key);
230
231 if (i == kCFNotFound) {
232 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoKey));
233 return;
234 }
235
236 CFArrayRemoveValueAtIndex(keys, i);
237
238 if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
0fae82ee 239 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
5958d7c0 240 }
009ee3c6 241
5958d7c0
A
242 return;
243}
244
245
dbf6a266 246__private_extern__
5958d7c0
A
247void
248do_notify_changes(int argc, char **argv)
249{
250 CFArrayRef list;
251 CFIndex listCnt;
5958d7c0
A
252 int i;
253
0fae82ee
A
254 list = SCDynamicStoreCopyNotifiedKeys(store);
255 if (!list) {
256 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
5958d7c0
A
257 return;
258 }
259
260 listCnt = CFArrayGetCount(list);
261 if (listCnt > 0) {
009ee3c6 262 for (i = 0; i < listCnt; i++) {
0fae82ee
A
263 SCPrint(TRUE,
264 stdout,
17d3ee29
A
265 CFSTR(" %s changedKey [%d] = %@\n"),
266 elapsed(),
0fae82ee
A
267 i,
268 CFArrayGetValueAtIndex(list, i));
5958d7c0
A
269 }
270 } else {
0fae82ee 271 SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n"));
5958d7c0
A
272 }
273 CFRelease(list);
274
275 return;
276}
277
278
009ee3c6
A
279static void *
280_watcher(void *arg)
5958d7c0 281{
009ee3c6 282 notifyRl = CFRunLoopGetCurrent();
6bb65964 283 if (notifyRl == NULL) {
009ee3c6
A
284 SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n"));
285 return NULL;
286 }
287
d0784775
A
288 if (doDispatch) {
289 if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_current_queue())) {
290 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
6bb65964 291 notifyRl = NULL;
d0784775
A
292 return NULL;
293 }
294 notifyRls = (CFRunLoopSourceRef)kCFNull;
6bb65964 295 } else {
d0784775 296 notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
6bb65964 297 if (notifyRls == NULL) {
d0784775 298 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
6bb65964 299 notifyRl = NULL;
d0784775
A
300 return NULL;
301 }
302 CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
009ee3c6 303 }
009ee3c6 304
6bb65964 305 pthread_setname_np("n.watch");
009ee3c6 306 CFRunLoopRun();
6bb65964 307 notifyRl = NULL;
009ee3c6
A
308 return NULL;
309}
310
dbf6a266 311__private_extern__
009ee3c6
A
312void
313do_notify_watch(int argc, char **argv)
314{
315 pthread_attr_t tattr;
316 pthread_t tid;
317
6bb65964
A
318 if (notifyRl != NULL) {
319 SCPrint(TRUE, stdout, CFSTR("already active\n"));
0fae82ee
A
320 return;
321 }
5958d7c0 322
009ee3c6
A
323 pthread_attr_init(&tattr);
324 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
325 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
326// pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
327 pthread_create(&tid, &tattr, _watcher, NULL);
328 pthread_attr_destroy(&tattr);
329
0fae82ee
A
330 return;
331}
332
333
dbf6a266 334__private_extern__
0fae82ee
A
335void
336do_notify_wait(int argc, char **argv)
337{
338 if (!SCDynamicStoreNotifyWait(store)) {
339 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
5958d7c0
A
340 return;
341 }
342
5958d7c0
A
343 return;
344}
345
346
dbf6a266 347__private_extern__
5958d7c0
A
348void
349do_notify_file(int argc, char **argv)
350{
351 int32_t reqID = 0;
5958d7c0
A
352 int fd;
353 union {
354 char data[4];
355 int32_t gotID;
356 } buf;
357 char *bufPtr;
358 int needed;
359
360 if (argc == 1) {
361 if ((sscanf(argv[0], "%d", &reqID) != 1)) {
0fae82ee 362 SCPrint(TRUE, stdout, CFSTR("invalid identifier.\n"));
5958d7c0
A
363 return;
364 }
365 }
366
0fae82ee
A
367 if (!SCDynamicStoreNotifyFileDescriptor(store, reqID, &fd)) {
368 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
5958d7c0
A
369 return;
370 }
371
372 bzero(buf.data, sizeof(buf.data));
373 bufPtr = &buf.data[0];
374 needed = sizeof(buf.gotID);
375 while (needed > 0) {
376 int got;
377
378 got = read(fd, bufPtr, needed);
379 if (got == -1) {
380 /* if error detected */
0fae82ee 381 SCPrint(TRUE, stdout, CFSTR("read() failed: %s.\n"), strerror(errno));
5958d7c0
A
382 break;
383 }
384
385 if (got == 0) {
386 /* if end of file detected */
0fae82ee 387 SCPrint(TRUE, stdout, CFSTR("read(): detected end of file.\n"));
5958d7c0
A
388 break;
389 }
390
0fae82ee 391 SCPrint(TRUE, stdout, CFSTR("Received %d bytes.\n"), got);
5958d7c0
A
392 bufPtr += got;
393 needed -= got;
394 }
395
396 if (needed != sizeof(buf.gotID)) {
0fae82ee 397 SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID);
5958d7c0
A
398 }
399
a40a14f8
A
400 /* report the keys that changed */
401 do_notify_changes(0, NULL);
402
5958d7c0 403 /* this utility only allows processes one notification per "n.file" request */
0fae82ee 404 (void) SCDynamicStoreNotifyCancel(store);
5958d7c0
A
405
406 (void) close(fd); /* close my side of the file descriptor */
407
408 return;
409}
410
411
5958d7c0
A
412static void
413signalCatcher(int signum)
414{
415 static int n = 0;
416
a5f60add 417 SCPrint(TRUE, stdout, CFSTR("Received sig%s (#%d).\n"), sys_signame[signum], n++);
5958d7c0
A
418 return;
419}
420
421
dbf6a266 422__private_extern__
5958d7c0
A
423void
424do_notify_signal(int argc, char **argv)
425{
426 int sig;
427 pid_t pid;
428 struct sigaction nact;
5958d7c0
A
429
430 if (isdigit(*argv[0])) {
431 if ((sscanf(argv[0], "%d", &sig) != 1) || (sig <= 0) || (sig >= NSIG)) {
0fae82ee 432 SCPrint(TRUE, stdout, CFSTR("signal must be in the range of 1 .. %d.\n"), NSIG-1);
5958d7c0
A
433 return;
434 }
435 } else {
009ee3c6 436 for (sig = 1; sig < NSIG; sig++) {
a5f60add 437 if (strcasecmp(argv[0], sys_signame[sig]) == 0)
5958d7c0
A
438 break;
439 }
440 if (sig >= NSIG) {
0fae82ee
A
441 CFMutableStringRef str;
442
443 SCPrint(TRUE, stdout, CFSTR("Signal must be one of the following:\n"));
444
445 str = CFStringCreateMutable(NULL, 0);
009ee3c6 446 for (sig = 1; sig < NSIG; sig++) {
a5f60add 447 CFStringAppendFormat(str, NULL, CFSTR(" %-6s"), sys_signame[sig]);
0fae82ee
A
448 if ((sig % 10) == 0) {
449 CFStringAppendFormat(str, NULL, CFSTR("\n"));
450 }
451 }
452 if ((sig % 10) != 0) {
453 CFStringAppendFormat(str, NULL, CFSTR("\n"));
5958d7c0 454 }
0fae82ee
A
455 SCPrint(TRUE, stdout, CFSTR("%@"), str);
456 CFRelease(str);
5958d7c0
A
457 return;
458 }
459
460 }
461
462 if ((argc != 2) || (sscanf(argv[1], "%d", &pid) != 1)) {
463 pid = getpid();
464 }
465
466 if (oact != NULL) {
a40a14f8 467 (void) sigaction(osig, oact, NULL); /* restore original signal handler */
5958d7c0
A
468 } else {
469 oact = malloc(sizeof(struct sigaction));
470 }
471
472 nact.sa_handler = signalCatcher;
473 sigemptyset(&nact.sa_mask);
474 nact.sa_flags = SA_RESTART;
a40a14f8 475 (void) sigaction(sig, &nact, oact);
5958d7c0 476 osig = sig;
0fae82ee 477 SCPrint(TRUE, stdout, CFSTR("signal handler started.\n"));
5958d7c0 478
0fae82ee
A
479 if (!SCDynamicStoreNotifySignal(store, pid, sig)) {
480 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
5958d7c0
A
481 return;
482 }
483
484 return;
485}
486
487
dbf6a266 488__private_extern__
5958d7c0
A
489void
490do_notify_cancel(int argc, char **argv)
491{
6bb65964 492 if (notifyRls != NULL) {
d0784775
A
493 if (doDispatch) {
494 if (!SCDynamicStoreSetDispatchQueue(store, NULL)) {
495 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
496 return;
497 }
6bb65964 498 } else {
d0784775
A
499 CFRunLoopSourceInvalidate(notifyRls);
500 CFRelease(notifyRls);
501 }
0fae82ee 502 notifyRls = NULL;
6bb65964
A
503 } else {
504 if (!SCDynamicStoreNotifyCancel(store)) {
505 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
506 return;
507 }
0fae82ee
A
508 }
509
6bb65964 510 if (notifyRl != NULL) {
009ee3c6 511 CFRunLoopStop(notifyRl);
5958d7c0
A
512 }
513
514 if (oact != NULL) {
a40a14f8 515 (void) sigaction(osig, oact, NULL); /* restore original signal handler */
5958d7c0
A
516 free(oact);
517 oact = NULL;
518 }
519
520 return;
521}