]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/notify.c
492b6e7a0d763626d57c5d1cc17f466f91ea395e
[apple/configd.git] / scutil.tproj / notify.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26 /*
27 * Modification History
28 *
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
31 *
32 * November 9, 2000 Allan Nathanson <ajn@apple.com>
33 * - initial revision
34 */
35
36 #include <pthread.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <unistd.h>
41
42 #include "scutil.h"
43 #include "notify.h"
44
45
46 static int osig;
47 static struct sigaction *oact = NULL;
48
49
50 static CFComparisonResult
51 sort_keys(const void *p1, const void *p2, void *context) {
52 CFStringRef key1 = (CFStringRef)p1;
53 CFStringRef key2 = (CFStringRef)p2;
54 return CFStringCompare(key1, key2, 0);
55 }
56
57
58 void
59 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
60 {
61 int i;
62 CFIndex n;
63
64 SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
65
66 n = CFArrayGetCount(changedKeys);
67 if (n > 0) {
68 for (i = 0; i < n; i++) {
69 SCPrint(TRUE,
70 stdout,
71 CFSTR(" changed key [%d] = %@\n"),
72 i,
73 CFArrayGetValueAtIndex(changedKeys, i));
74 }
75 } else {
76 SCPrint(TRUE, stdout, CFSTR(" no changed key's.\n"));
77 }
78
79 return;
80 }
81
82
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) {
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 void
138 do_notify_add(int argc, char **argv)
139 {
140 CFStringRef key;
141 CFMutableArrayRef keys;
142 Boolean isRegex = FALSE;
143
144 if (!store) {
145 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
146 return;
147 }
148
149 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
150
151 if (argc == 2)
152 isRegex = TRUE;
153
154 keys = isRegex ? watchedPatterns : watchedKeys;
155 if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key)) {
156 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusKeyExists));
157 CFRelease(key);
158 return;
159 }
160
161 CFArrayAppendValue(keys, key);
162 CFRelease(key);
163
164 if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
165 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
166 }
167
168 return;
169 }
170
171
172 void
173 do_notify_remove(int argc, char **argv)
174 {
175 CFStringRef key;
176 CFMutableArrayRef keys;
177 CFIndex i;
178 Boolean isRegex = FALSE;
179
180 if (!store) {
181 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
182 return;
183 }
184
185 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingMacRoman);
186
187 if (argc == 2)
188 isRegex = TRUE;
189
190 keys = isRegex ? watchedPatterns : watchedKeys;
191 i = CFArrayGetFirstIndexOfValue(keys, CFRangeMake(0, CFArrayGetCount(keys)), key);
192 CFRelease(key);
193
194 if (i == kCFNotFound) {
195 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoKey));
196 return;
197 }
198
199 CFArrayRemoveValueAtIndex(keys, i);
200
201 if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
202 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
203 }
204
205 return;
206 }
207
208
209 void
210 do_notify_changes(int argc, char **argv)
211 {
212 CFArrayRef list;
213 CFIndex listCnt;
214 int i;
215
216 list = SCDynamicStoreCopyNotifiedKeys(store);
217 if (!list) {
218 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
219 return;
220 }
221
222 listCnt = CFArrayGetCount(list);
223 if (listCnt > 0) {
224 for (i = 0; i < listCnt; i++) {
225 SCPrint(TRUE,
226 stdout,
227 CFSTR(" changedKey [%d] = %@\n"),
228 i,
229 CFArrayGetValueAtIndex(list, i));
230 }
231 } else {
232 SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n"));
233 }
234 CFRelease(list);
235
236 return;
237 }
238
239
240 static void *
241 _watcher(void *arg)
242 {
243 notifyRl = CFRunLoopGetCurrent();
244 if (!notifyRl) {
245 SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n"));
246 return NULL;
247 }
248
249 notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
250 if (!notifyRls) {
251 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
252 return NULL;
253 }
254 CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
255
256 CFRunLoopRun();
257 return NULL;
258 }
259
260 void
261 do_notify_watch(int argc, char **argv)
262 {
263 pthread_attr_t tattr;
264 pthread_t tid;
265
266 if (notifyRl) {
267 return;
268 }
269
270 pthread_attr_init(&tattr);
271 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
272 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
273 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
274 pthread_create(&tid, &tattr, _watcher, NULL);
275 pthread_attr_destroy(&tattr);
276
277 return;
278 }
279
280
281 void
282 do_notify_wait(int argc, char **argv)
283 {
284 if (!SCDynamicStoreNotifyWait(store)) {
285 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
286 return;
287 }
288
289 return;
290 }
291
292
293 static boolean_t
294 notificationWatcher(SCDynamicStoreRef store, void *arg)
295 {
296 SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
297 SCPrint(TRUE, stdout, CFSTR(" arg = %s.\n"), (char *)arg);
298 return TRUE;
299 }
300
301
302 static boolean_t
303 notificationWatcherVerbose(SCDynamicStoreRef store, void *arg)
304 {
305 SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
306 SCPrint(TRUE, stdout, CFSTR(" arg = %s.\n"), (char *)arg);
307 do_notify_changes(0, NULL); /* report the keys which changed */
308 return TRUE;
309 }
310
311
312 void
313 do_notify_callback(int argc, char **argv)
314 {
315 SCDynamicStoreCallBack_v1 func = notificationWatcher;
316
317 if ((argc == 1) && (strcmp(argv[0], "verbose") == 0)) {
318 func = notificationWatcherVerbose;
319 }
320
321 if (!SCDynamicStoreNotifyCallback(store, CFRunLoopGetCurrent(), func, "Changed detected by callback handler!")) {
322 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
323 return;
324 }
325
326 return;
327 }
328
329
330 void
331 do_notify_file(int argc, char **argv)
332 {
333 int32_t reqID = 0;
334 int fd;
335 union {
336 char data[4];
337 int32_t gotID;
338 } buf;
339 char *bufPtr;
340 int needed;
341
342 if (argc == 1) {
343 if ((sscanf(argv[0], "%d", &reqID) != 1)) {
344 SCPrint(TRUE, stdout, CFSTR("invalid identifier.\n"));
345 return;
346 }
347 }
348
349 if (!SCDynamicStoreNotifyFileDescriptor(store, reqID, &fd)) {
350 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
351 return;
352 }
353
354 bzero(buf.data, sizeof(buf.data));
355 bufPtr = &buf.data[0];
356 needed = sizeof(buf.gotID);
357 while (needed > 0) {
358 int got;
359
360 got = read(fd, bufPtr, needed);
361 if (got == -1) {
362 /* if error detected */
363 SCPrint(TRUE, stdout, CFSTR("read() failed: %s.\n"), strerror(errno));
364 break;
365 }
366
367 if (got == 0) {
368 /* if end of file detected */
369 SCPrint(TRUE, stdout, CFSTR("read(): detected end of file.\n"));
370 break;
371 }
372
373 SCPrint(TRUE, stdout, CFSTR("Received %d bytes.\n"), got);
374 bufPtr += got;
375 needed -= got;
376 }
377
378 if (needed != sizeof(buf.gotID)) {
379 SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID);
380 }
381
382 /* this utility only allows processes one notification per "n.file" request */
383 (void) SCDynamicStoreNotifyCancel(store);
384
385 (void) close(fd); /* close my side of the file descriptor */
386
387 return;
388 }
389
390
391 static void
392 signalCatcher(int signum)
393 {
394 static int n = 0;
395
396 SCPrint(TRUE, stdout, CFSTR("Received sig%s (#%d).\n"), sys_signame[signum], n++);
397 return;
398 }
399
400
401 void
402 do_notify_signal(int argc, char **argv)
403 {
404 int sig;
405 pid_t pid;
406 struct sigaction nact;
407 int ret;
408
409 if (isdigit(*argv[0])) {
410 if ((sscanf(argv[0], "%d", &sig) != 1) || (sig <= 0) || (sig >= NSIG)) {
411 SCPrint(TRUE, stdout, CFSTR("signal must be in the range of 1 .. %d.\n"), NSIG-1);
412 return;
413 }
414 } else {
415 for (sig = 1; sig < NSIG; sig++) {
416 if (strcasecmp(argv[0], sys_signame[sig]) == 0)
417 break;
418 }
419 if (sig >= NSIG) {
420 CFMutableStringRef str;
421
422 SCPrint(TRUE, stdout, CFSTR("Signal must be one of the following:\n"));
423
424 str = CFStringCreateMutable(NULL, 0);
425 for (sig = 1; sig < NSIG; sig++) {
426 CFStringAppendFormat(str, NULL, CFSTR(" %-6s"), sys_signame[sig]);
427 if ((sig % 10) == 0) {
428 CFStringAppendFormat(str, NULL, CFSTR("\n"));
429 }
430 }
431 if ((sig % 10) != 0) {
432 CFStringAppendFormat(str, NULL, CFSTR("\n"));
433 }
434 SCPrint(TRUE, stdout, CFSTR("%@"), str);
435 CFRelease(str);
436 return;
437 }
438
439 }
440
441 if ((argc != 2) || (sscanf(argv[1], "%d", &pid) != 1)) {
442 pid = getpid();
443 }
444
445 if (oact != NULL) {
446 ret = sigaction(osig, oact, NULL); /* restore original signal handler */
447 } else {
448 oact = malloc(sizeof(struct sigaction));
449 }
450
451 nact.sa_handler = signalCatcher;
452 sigemptyset(&nact.sa_mask);
453 nact.sa_flags = SA_RESTART;
454 ret = sigaction(sig, &nact, oact);
455 osig = sig;
456 SCPrint(TRUE, stdout, CFSTR("signal handler started.\n"));
457
458 if (!SCDynamicStoreNotifySignal(store, pid, sig)) {
459 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
460 return;
461 }
462
463 return;
464 }
465
466
467 void
468 do_notify_cancel(int argc, char **argv)
469 {
470 int ret;
471
472 if (notifyRls) {
473 CFRunLoopRemoveSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
474 CFRelease(notifyRls);
475 notifyRls = NULL;
476 }
477
478 if (notifyRl) {
479 CFRunLoopStop(notifyRl);
480 notifyRl = NULL;
481 }
482
483 if (!SCDynamicStoreNotifyCancel(store)) {
484 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
485 return;
486 }
487
488 if (oact != NULL) {
489 ret = sigaction(osig, oact, NULL); /* restore original signal handler */
490 free(oact);
491 oact = NULL;
492 }
493
494 return;
495 }