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