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