]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/notifications.c
configd-1061.0.2.tar.gz
[apple/configd.git] / scutil.tproj / notifications.c
1 /*
2 * Copyright (c) 2000-2005, 2008-2015, 2017, 2018 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 char *
46 elapsed()
47 {
48 size_t n;
49 static char str[128];
50 struct tm tm_diff;
51 struct tm tm_now;
52 struct timeval tv_diff;
53 struct timeval tv_now;
54 static struct timeval tv_then = { 0, 0 };
55
56 (void)gettimeofday(&tv_now, NULL);
57
58 (void)localtime_r(&tv_now.tv_sec, &tm_now);
59
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",
63 tm_now.tm_hour,
64 tm_now.tm_min,
65 tm_now.tm_sec,
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)",
69 tv_diff.tv_sec,
70 tv_diff.tv_usec / 1000);
71 }
72
73 tv_then = tv_now;
74 return str;
75 }
76
77
78 static CFComparisonResult
79 sort_keys(const void *p1, const void *p2, void *context)
80 {
81 #pragma unused(context)
82 CFStringRef key1 = (CFStringRef)p1;
83 CFStringRef key2 = (CFStringRef)p2;
84 return CFStringCompare(key1, key2, 0);
85 }
86
87
88 __private_extern__
89 void
90 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
91 {
92 #pragma unused(info)
93 int i;
94 CFIndex n;
95
96 SCPrint(TRUE, stdout, CFSTR("notification callback (store address = %p).\n"), store);
97
98 n = CFArrayGetCount(changedKeys);
99 if (n > 0) {
100 for (i = 0; i < n; i++) {
101 SCPrint(TRUE,
102 stdout,
103 CFSTR(" %s changedKey [%d] = %@\n"),
104 elapsed(),
105 i,
106 CFArrayGetValueAtIndex(changedKeys, i));
107 }
108 } else {
109 SCPrint(TRUE, stdout, CFSTR(" no changed key's.\n"));
110 }
111
112 return;
113 }
114
115
116 __private_extern__
117 void
118 do_notify_list(int argc, char **argv)
119 {
120 #pragma unused(argv)
121 int i;
122 CFArrayRef list;
123 CFIndex listCnt;
124 Boolean isRegex = FALSE;
125 CFMutableArrayRef sortedList;
126
127 if (store == NULL) {
128 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
129 return;
130 }
131
132 if (argc == 1)
133 isRegex = TRUE;
134
135 list = isRegex ? watchedPatterns : watchedKeys;
136 if (!list) {
137 SCPrint(TRUE,
138 stdout,
139 CFSTR(" no notifier %s.\n"),
140 isRegex ? "patterns" : "keys");
141 return;
142 }
143
144 listCnt = CFArrayGetCount(list);
145 sortedList = CFArrayCreateMutableCopy(NULL, listCnt, list);
146 CFArraySortValues(sortedList,
147 CFRangeMake(0, listCnt),
148 sort_keys,
149 NULL);
150
151 if (listCnt > 0) {
152 for (i = 0; i < listCnt; i++) {
153 SCPrint(TRUE,
154 stdout,
155 CFSTR(" notifier %s [%d] = %@\n"),
156 isRegex ? "pattern" : "key",
157 i,
158 CFArrayGetValueAtIndex(sortedList, i));
159 }
160 } else {
161 SCPrint(TRUE,
162 stdout,
163 CFSTR(" no notifier %s.\n"),
164 isRegex ? "patterns" : "keys");
165 }
166 CFRelease(sortedList);
167
168 return;
169 }
170
171
172 __private_extern__
173 void
174 do_notify_add(int argc, char **argv)
175 {
176 CFStringRef key;
177 CFMutableArrayRef keys;
178 Boolean isRegex = FALSE;
179
180 if (store == NULL) {
181 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
182 return;
183 }
184
185 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
186
187 if (argc == 2)
188 isRegex = TRUE;
189
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;
195 }
196
197 CFArrayAppendValue(keys, key);
198 CFRelease(key);
199
200 if (!SCDynamicStoreSetNotificationKeys(store, watchedKeys, watchedPatterns)) {
201 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
202 }
203
204 return;
205 }
206
207
208 __private_extern__
209 void
210 do_notify_remove(int argc, char **argv)
211 {
212 CFStringRef key;
213 CFMutableArrayRef keys;
214 CFIndex i;
215 Boolean isRegex = FALSE;
216
217 if (store == NULL) {
218 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(kSCStatusNoStoreSession));
219 return;
220 }
221
222 key = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
223
224 if (argc == 2)
225 isRegex = TRUE;
226
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)) {
239 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
240 }
241
242 return;
243 }
244
245
246 __private_extern__
247 void
248 do_notify_changes(int argc, char **argv)
249 {
250 #pragma unused(argc)
251 #pragma unused(argv)
252 CFArrayRef list;
253 CFIndex listCnt;
254 int i;
255
256 list = SCDynamicStoreCopyNotifiedKeys(store);
257 if (!list) {
258 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
259 return;
260 }
261
262 listCnt = CFArrayGetCount(list);
263 if (listCnt > 0) {
264 for (i = 0; i < listCnt; i++) {
265 SCPrint(TRUE,
266 stdout,
267 CFSTR(" %s changedKey [%d] = %@\n"),
268 elapsed(),
269 i,
270 CFArrayGetValueAtIndex(list, i));
271 }
272 } else {
273 SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n"));
274 }
275 CFRelease(list);
276
277 return;
278 }
279
280
281 static void *
282 _watcher(void *arg)
283 {
284 #pragma unused(arg)
285 notifyRl = CFRunLoopGetCurrent();
286 if (notifyRl == NULL) {
287 SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n"));
288 return NULL;
289 }
290
291 if (doDispatch) {
292 if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) {
293 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
294 notifyRl = NULL;
295 return NULL;
296 }
297 notifyRls = (CFRunLoopSourceRef)kCFNull;
298 } else {
299 notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
300 if (notifyRls == NULL) {
301 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
302 notifyRl = NULL;
303 return NULL;
304 }
305 CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode);
306 }
307
308 pthread_setname_np("n.watch");
309 CFRunLoopRun();
310 notifyRl = NULL;
311 return NULL;
312 }
313
314 __private_extern__
315 void
316 do_notify_watch(int argc, char **argv)
317 {
318 #pragma unused(argc)
319 #pragma unused(argv)
320 pthread_attr_t tattr;
321 pthread_t tid;
322
323 if (notifyRl != NULL) {
324 SCPrint(TRUE, stdout, CFSTR("already active\n"));
325 return;
326 }
327
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);
334
335 return;
336 }
337
338
339 __private_extern__
340 void
341 do_notify_wait(int argc, char **argv)
342 {
343 #pragma unused(argc)
344 #pragma unused(argv)
345 if (!SCDynamicStoreNotifyWait(store)) {
346 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
347 return;
348 }
349
350 return;
351 }
352
353
354 __private_extern__
355 void
356 do_notify_file(int argc, char **argv)
357 {
358 int32_t reqID = 0;
359 int fd;
360 union {
361 char data[4];
362 int32_t gotID;
363 } buf;
364 char *bufPtr;
365 int needed;
366
367 if (argc == 1) {
368 if ((sscanf(argv[0], "%d", &reqID) != 1)) {
369 SCPrint(TRUE, stdout, CFSTR("invalid identifier.\n"));
370 return;
371 }
372 }
373
374 if (!SCDynamicStoreNotifyFileDescriptor(store, reqID, &fd)) {
375 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
376 return;
377 }
378
379 memset(buf.data, 0, sizeof(buf.data));
380 bufPtr = &buf.data[0];
381 needed = sizeof(buf.gotID);
382 while (needed > 0) {
383 ssize_t got;
384
385 got = read(fd, bufPtr, needed);
386 if (got == -1) {
387 /* if error detected */
388 SCPrint(TRUE, stdout, CFSTR("read() failed: %s.\n"), strerror(errno));
389 break;
390 }
391
392 if (got == 0) {
393 /* if end of file detected */
394 SCPrint(TRUE, stdout, CFSTR("read(): detected end of file.\n"));
395 break;
396 }
397
398 SCPrint(TRUE, stdout, CFSTR("Received %ld bytes.\n"), got);
399 bufPtr += got;
400 needed -= got;
401 }
402
403 if (needed != sizeof(buf.gotID)) {
404 SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID);
405 }
406
407 /* report the keys that changed */
408 do_notify_changes(0, NULL);
409
410 /* this utility only allows processes one notification per "n.file" request */
411 (void) SCDynamicStoreNotifyCancel(store);
412
413 (void) close(fd); /* close my side of the file descriptor */
414
415 return;
416 }
417
418
419 __private_extern__
420 void
421 do_notify_cancel(int argc, char **argv)
422 {
423 #pragma unused(argc)
424 #pragma unused(argv)
425 if (notifyRls != NULL) {
426 if (doDispatch) {
427 if (!SCDynamicStoreSetDispatchQueue(store, NULL)) {
428 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
429 return;
430 }
431 } else {
432 CFRunLoopSourceInvalidate(notifyRls);
433 CFRelease(notifyRls);
434 }
435 notifyRls = NULL;
436 } else {
437 if (!SCDynamicStoreNotifyCancel(store)) {
438 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError()));
439 return;
440 }
441 }
442
443 if (notifyRl != NULL) {
444 CFRunLoopStop(notifyRl);
445 }
446
447 return;
448 }