1/* 2 * Copyright (c) 2000-2005, 2008-2014 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 45static int osig; 46static struct sigaction *oact = NULL; 47 48 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 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 89 90__private_extern__ 91void 92storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 93{ 94 int i; 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) { 101 for (i = 0; i < n; i++) { 102 SCPrint(TRUE, 103 stdout, 104 CFSTR(" %s changedKey [%d] = %@\n"), 105 elapsed(), 106 i, 107 CFArrayGetValueAtIndex(changedKeys, i)); 108 } 109 } else { 110 SCPrint(TRUE, stdout, CFSTR(" no changed key's.\n")); 111 } 112 113 return; 114} 115 116 117__private_extern__ 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; 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__ 173void 174do_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__ 209void 210do_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__ 247void 248do_notify_changes(int argc, char **argv) 249{ 250 CFArrayRef list; 251 CFIndex listCnt; 252 int i; 253 254 list = SCDynamicStoreCopyNotifiedKeys(store); 255 if (!list) { 256 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 257 return; 258 } 259 260 listCnt = CFArrayGetCount(list); 261 if (listCnt > 0) { 262 for (i = 0; i < listCnt; i++) { 263 SCPrint(TRUE, 264 stdout, 265 CFSTR(" %s changedKey [%d] = %@\n"), 266 elapsed(), 267 i, 268 CFArrayGetValueAtIndex(list, i)); 269 } 270 } else { 271 SCPrint(TRUE, stdout, CFSTR(" no changedKey's.\n")); 272 } 273 CFRelease(list); 274 275 return; 276} 277 278 279static void * 280_watcher(void *arg) 281{ 282 notifyRl = CFRunLoopGetCurrent(); 283 if (notifyRl == NULL) { 284 SCPrint(TRUE, stdout, CFSTR(" CFRunLoopGetCurrent() failed\n")); 285 return NULL; 286 } 287 288 if (doDispatch) { 289 if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) { 290 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 291 notifyRl = NULL; 292 return NULL; 293 } 294 notifyRls = (CFRunLoopSourceRef)kCFNull; 295 } else { 296 notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 297 if (notifyRls == NULL) { 298 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 299 notifyRl = NULL; 300 return NULL; 301 } 302 CFRunLoopAddSource(notifyRl, notifyRls, kCFRunLoopDefaultMode); 303 } 304 305 pthread_setname_np("n.watch"); 306 CFRunLoopRun(); 307 notifyRl = NULL; 308 return NULL; 309} 310 311__private_extern__ 312void 313do_notify_watch(int argc, char **argv) 314{ 315 pthread_attr_t tattr; 316 pthread_t tid; 317 318 if (notifyRl != NULL) { 319 SCPrint(TRUE, stdout, CFSTR("already active\n")); 320 return; 321 } 322 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 330 return; 331} 332 333 334__private_extern__ 335void 336do_notify_wait(int argc, char **argv) 337{ 338 if (!SCDynamicStoreNotifyWait(store)) { 339 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 340 return; 341 } 342 343 return; 344} 345 346 347__private_extern__ 348void 349do_notify_file(int argc, char **argv) 350{ 351 int32_t reqID = 0; 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)) { 362 SCPrint(TRUE, stdout, CFSTR("invalid identifier.\n")); 363 return; 364 } 365 } 366 367 if (!SCDynamicStoreNotifyFileDescriptor(store, reqID, &fd)) { 368 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 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 ssize_t got; 377 378 got = read(fd, bufPtr, needed); 379 if (got == -1) { 380 /* if error detected */ 381 SCPrint(TRUE, stdout, CFSTR("read() failed: %s.\n"), strerror(errno)); 382 break; 383 } 384 385 if (got == 0) { 386 /* if end of file detected */ 387 SCPrint(TRUE, stdout, CFSTR("read(): detected end of file.\n")); 388 break; 389 } 390 391 SCPrint(TRUE, stdout, CFSTR("Received %ld bytes.\n"), got); 392 bufPtr += got; 393 needed -= got; 394 } 395 396 if (needed != sizeof(buf.gotID)) { 397 SCPrint(TRUE, stdout, CFSTR(" Received notification, identifier = %d.\n"), buf.gotID); 398 } 399 400 /* report the keys that changed */ 401 do_notify_changes(0, NULL); 402 403 /* this utility only allows processes one notification per "n.file" request */ 404 (void) SCDynamicStoreNotifyCancel(store); 405 406 (void) close(fd); /* close my side of the file descriptor */ 407 408 return; 409} 410 411 412static void 413signalCatcher(int signum) 414{ 415 static int n = 0; 416 417 SCPrint(TRUE, stdout, CFSTR("Received sig%s (#%d).\n"), sys_signame[signum], n++); 418 return; 419} 420 421 422__private_extern__ 423void 424do_notify_signal(int argc, char **argv) 425{ 426 int sig; 427 pid_t pid; 428 struct sigaction nact; 429 430 if (isdigit(*argv[0])) { 431 if ((sscanf(argv[0], "%d", &sig) != 1) || (sig <= 0) || (sig >= NSIG)) { 432 SCPrint(TRUE, stdout, CFSTR("signal must be in the range of 1 .. %d.\n"), NSIG-1); 433 return; 434 } 435 } else { 436 for (sig = 1; sig < NSIG; sig++) { 437 if (strcasecmp(argv[0], sys_signame[sig]) == 0) 438 break; 439 } 440 if (sig >= NSIG) { 441 CFMutableStringRef str; 442 443 SCPrint(TRUE, stdout, CFSTR("Signal must be one of the following:\n")); 444 445 str = CFStringCreateMutable(NULL, 0); 446 for (sig = 1; sig < NSIG; sig++) { 447 CFStringAppendFormat(str, NULL, CFSTR(" %-6s"), sys_signame[sig]); 448 if ((sig % 10) == 0) { 449 CFStringAppendFormat(str, NULL, CFSTR("\n")); 450 } 451 } 452 if ((sig % 10) != 0) { 453 CFStringAppendFormat(str, NULL, CFSTR("\n")); 454 } 455 SCPrint(TRUE, stdout, CFSTR("%@"), str); 456 CFRelease(str); 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) { 467 (void) sigaction(osig, oact, NULL); /* restore original signal handler */ 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; 475 (void) sigaction(sig, &nact, oact); 476 osig = sig; 477 SCPrint(TRUE, stdout, CFSTR("signal handler started.\n")); 478 479 if (!SCDynamicStoreNotifySignal(store, pid, sig)) { 480 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 481 return; 482 } 483 484 return; 485} 486 487 488__private_extern__ 489void 490do_notify_cancel(int argc, char **argv) 491{ 492 if (notifyRls != NULL) { 493 if (doDispatch) { 494 if (!SCDynamicStoreSetDispatchQueue(store, NULL)) { 495 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 496 return; 497 } 498 } else { 499 CFRunLoopSourceInvalidate(notifyRls); 500 CFRelease(notifyRls); 501 } 502 notifyRls = NULL; 503 } else { 504 if (!SCDynamicStoreNotifyCancel(store)) { 505 SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); 506 return; 507 } 508 } 509 510 if (notifyRl != NULL) { 511 CFRunLoopStop(notifyRl); 512 } 513 514 if (oact != NULL) { 515 (void) sigaction(osig, oact, NULL); /* restore original signal handler */ 516 free(oact); 517 oact = NULL; 518 } 519 520 return; 521} 522