/* * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #define forever for(;;) #define IndexNull ((uint32_t)-1) #define PRINT_QUIET 0x00000000 #define PRINT_KEY 0x00000001 #define PRINT_STATE 0x00000002 #define PRINT_TIME 0x00000004 #define PRINT_TYPE 0x00000008 #define PRINT_VERBOSE 0xffffffff #ifndef USEC_PER_SEC #define USEC_PER_SEC 1000000 #endif #define TYPE_NULL 0 #define TYPE_PORT 1 #define TYPE_FILE 2 #define TYPE_DISPATCH 3 #define TYPE_SIGNAL 4 #define TYPE_CHECK 5 #define TYPE_PLAIN 6 static const char *typename[] = { "unknown", "port", "file", "dispatch", "signal", "check", "plain" }; extern uint32_t notify_register_plain(const char *name, int *out_token); typedef struct { uint32_t token; uint32_t type; uint32_t signum; uint32_t count; char *name; } reg_entry_t; static reg_entry_t *reg; static uint32_t reg_count = 0; static int printopt; static int port_flag; static int file_flag; static int watch_file; static mach_port_t watch_port; dispatch_source_t timer_src; dispatch_source_t port_src; dispatch_source_t file_src; dispatch_source_t sig_src[__DARWIN_NSIG]; dispatch_queue_t watch_queue; static void usage(const char *name) { fprintf(stderr, "usage: %s [-q] [-v] [-z msec] [-M] [-R] [command ...]\n", name); fprintf(stderr, " -q quiet mode\n"); fprintf(stderr, " -v verbose - prints time, key, state value, and type\n"); fprintf(stderr, " -z msec pause msec milliseconds after posting [default 100]\n"); fprintf(stderr, " -M multiplex notifications from notifyd over a single mach port\n"); fprintf(stderr, " -R regenerate registrations if notifyd restarts\n"); fprintf(stderr, "commands:\n"); fprintf(stderr, " -port switch to mach port for subsequent registrations [default]\n"); fprintf(stderr, " -file switch to file descriptor for subsequent registrations\n"); fprintf(stderr, " -check switch to shared memory for subsequent registrations\n"); fprintf(stderr, " -signal [#] switch to signal [#] for subsequent registrations\n"); fprintf(stderr, " initial default for signal is 1 (SIGHUP)\n"); fprintf(stderr, " -dispatch switch to dispatch for subsequent registrations\n"); fprintf(stderr, " -p key post a notifcation for key\n"); fprintf(stderr, " -w key register for key and report notifications\n"); fprintf(stderr, " -# key (# is an integer value, eg \"-1\") register for key and report # notifications\n"); fprintf(stderr, " -g key get state value for key\n"); fprintf(stderr, " -s key val set state value for key\n"); } static const char * notify_status_strerror(int status) { switch (status) { case NOTIFY_STATUS_OK: return("OK"); case NOTIFY_STATUS_INVALID_NAME: return "Invalid Name"; case NOTIFY_STATUS_INVALID_TOKEN: return "Invalid Token"; case NOTIFY_STATUS_INVALID_PORT: return "Invalid Port"; case NOTIFY_STATUS_INVALID_FILE: return "Invalid File"; case NOTIFY_STATUS_INVALID_SIGNAL: return "Invalid Signal"; case NOTIFY_STATUS_INVALID_REQUEST: return "Invalid Request"; case NOTIFY_STATUS_NOT_AUTHORIZED: return "Not Authorized"; case NOTIFY_STATUS_FAILED: default: return "Failed"; } } static void reg_add(uint32_t tid, uint32_t type, uint32_t signum, uint32_t count, const char *name) { if (name == NULL) return; reg = (reg_entry_t *)reallocf(reg, (reg_count + 1) * sizeof(reg_entry_t)); if (reg == NULL) { fprintf(stderr, "Can't allocate memory!\n"); reg_count = 0; return; } reg[reg_count].token = tid; reg[reg_count].type = type; reg[reg_count].signum = signum; reg[reg_count].count = count; reg[reg_count].name = strdup(name); if (reg[reg_count].name == NULL) { fprintf(stderr, "Can't allocate memory!\n"); reg = NULL; reg_count = 0; return; } reg_count++; } static void reg_delete(uint32_t index) { uint32_t i; if (index == IndexNull) return; if (index >= reg_count) return; free(reg[index].name); for (i = index + 1; i < reg_count; i++) reg[i - 1] = reg[i]; reg_count--; if (reg_count == 0) { free(reg); reg = NULL; } else { reg = (reg_entry_t *)reallocf(reg, reg_count * sizeof(reg_entry_t)); if (reg == NULL) { fprintf(stderr, "Can't allocate memory!\n"); reg_count = 0; } } } static uint32_t reg_find_name(const char *name) { uint32_t i; for (i = 0; i < reg_count; i++) if (!strcmp(reg[i].name, name)) return i; return IndexNull; } static uint32_t reg_find_token(uint32_t tid) { uint32_t i; for (i = 0; i < reg_count; i++) if (tid == reg[i].token) return i; return IndexNull; } static void process_event(int tid) { struct timeval now; char tstr[32]; int status, index, needspace; uint64_t state; gettimeofday(&now, NULL); index = reg_find_token(tid); if (index == IndexNull) return; needspace = 0; if (printopt & PRINT_TIME) { snprintf(tstr, sizeof(tstr), "%llu", now.tv_usec + USEC_PER_SEC + 500); tstr[4] = '\0'; printf("%d.%s", (int)now.tv_sec, tstr+1); needspace = 1; } if (printopt & PRINT_KEY) { if (needspace) printf(" "); printf("%s", reg[index].name); needspace = 1; } if (printopt & PRINT_STATE) { if (needspace) printf(" "); state = 0; status = notify_get_state(tid, &state); if (status == NOTIFY_STATUS_OK) printf("%llu",(unsigned long long)state); else printf(": %s", notify_status_strerror(status)); needspace = 1; } if (printopt & PRINT_TYPE) { if (needspace) printf(" "); printf("%s", typename[reg[index].type]); needspace = 1; } if (printopt != PRINT_QUIET) printf("\n"); if ((reg[index].count != IndexNull) && (reg[index].count != 0)) reg[index].count--; if (reg[index].count == 0) { status = notify_cancel(tid); reg_delete(index); } } static void file_handler(int fd) { ssize_t i; int tid; if (fd < 0) return; i = read(fd, &tid, sizeof(tid)); if (i < 0) return; tid = ntohl(tid); process_event(tid); if (reg_count == 0) exit(0); } static void port_handler(mach_port_t port) { int tid; mach_msg_empty_rcv_t msg; kern_return_t status; if (port == MACH_PORT_NULL) return; memset(&msg, 0, sizeof(msg)); status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, 0, MACH_PORT_NULL); if (status != KERN_SUCCESS) return; tid = msg.header.msgh_id; process_event(tid); if (reg_count == 0) exit(0); } static void signal_handler(uint32_t sig) { uint32_t i, status; int check; if (printopt != PRINT_QUIET) printf("SIGNAL %u\n", sig); for (i = 0; i < reg_count; i++) { if ((reg[i].type == TYPE_SIGNAL) && (reg[i].signum == sig)) { check = 0; status = notify_check(reg[i].token, &check); if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token); } } if (reg_count == 0) exit(0); } static void dispatch_handler(const char *name) { uint32_t index = reg_find_name(name); if (index == IndexNull) return; process_event(reg[index].token); } static void timer_handler(void) { uint32_t i, status; int check; for (i = 0; i < reg_count; i++) { if ((reg[i].type == TYPE_CHECK) || (reg[i].type == TYPE_PLAIN)) { check = 0; status = notify_check(reg[i].token, &check); if ((status == NOTIFY_STATUS_OK) && (check != 0)) process_event(reg[i].token); } } if (reg_count == 0) exit(0); } static uint32_t do_register(const char *name, uint32_t type, uint32_t signum, uint32_t count) { int tid, check; uint32_t status; switch (type) { case TYPE_PORT: { status = notify_register_mach_port(name, &watch_port, port_flag, &tid); if (status != NOTIFY_STATUS_OK) return status; port_flag = NOTIFY_REUSE; if (port_src == NULL) { port_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, watch_port, 0, watch_queue); dispatch_source_set_event_handler(port_src, ^{ port_handler(watch_port); }); dispatch_resume(port_src); } break; } case TYPE_FILE: { status = notify_register_file_descriptor(name, &watch_file, file_flag, &tid); if (status != NOTIFY_STATUS_OK) return status; file_flag = NOTIFY_REUSE; if (file_src == NULL) { file_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)watch_file, 0, watch_queue); dispatch_source_set_event_handler(file_src, ^{ file_handler(watch_file); }); dispatch_resume(file_src); } break; } case TYPE_SIGNAL: { signal(signum, SIG_IGN); status = notify_register_signal(name, signum, &tid); if (status != NOTIFY_STATUS_OK) return status; status = notify_check(tid, &check); if (sig_src[signum] == NULL) { sig_src[signum] = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)signum, 0, watch_queue); dispatch_source_set_event_handler(sig_src[signum], ^{ signal_handler(signum); }); dispatch_resume(sig_src[signum]); } break; } case TYPE_DISPATCH: { status = notify_register_dispatch(name, &tid, watch_queue, ^(int x){ dispatch_handler(name); }); if (status != NOTIFY_STATUS_OK) return status; break; } case TYPE_CHECK: { status = notify_register_check(name, &tid); if (status != NOTIFY_STATUS_OK) return status; status = notify_check(tid, &check); if (timer_src == NULL) { timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue); dispatch_source_set_event_handler(timer_src, ^{ timer_handler(); }); dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0); dispatch_resume(timer_src); } break; } case TYPE_PLAIN: { status = notify_register_plain(name, &tid); if (status != NOTIFY_STATUS_OK) return status; status = notify_check(tid, &check); if (timer_src == NULL) { timer_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, watch_queue); dispatch_source_set_event_handler(timer_src, ^{ timer_handler(); }); dispatch_source_set_timer(timer_src, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), NSEC_PER_SEC / 10, 0); dispatch_resume(timer_src); } break; } default: return NOTIFY_STATUS_FAILED; } reg_add(tid, type, signum, count, name); return NOTIFY_STATUS_OK; } int main(int argc, const char *argv[]) { const char *name; uint32_t i, n, index, signum, ntype, status, opts, nap; int tid; uint64_t state; for (i = 0; i < __DARWIN_NSIG; i++) sig_src[i] = NULL; ntype = TYPE_PORT; signum = 1; watch_file = -1; watch_port = MACH_PORT_NULL; printopt = PRINT_KEY; opts = 0; nap = 100000; watch_queue = dispatch_queue_create("Watch Q", NULL); name = strrchr(argv[0], '/'); if (name == NULL) name = argv[0]; else name++; for (i = 1; i < argc; i++) { if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "-h"))) { usage(name); exit(0); } else if (!strcmp(argv[i], "-q")) { printopt = PRINT_QUIET; } else if (!strcmp(argv[i], "-v")) { printopt |= PRINT_VERBOSE; } else if (!strcmp(argv[i], "-M")) { opts |= NOTIFY_OPT_DEMUX; } else if (!strcmp(argv[i], "-R")) { opts |= NOTIFY_OPT_REGEN; } else if (!strcmp(argv[i], "-z")) { if ((i + 1) >= argc) { fprintf(stderr, "timer value must be supplied following -z\n"); usage(name); exit(1); } i++; if ((argv[i][0] < '0') || (argv[i][1] > '9')) { fprintf(stderr, "-z %s is invalid\n", argv[i]); fprintf(stderr, "timer value must be an integer\n"); usage(name); exit(1); } nap = 1000 * atoi(argv[i]); } else if (!strcmp(argv[i], "-port")) {} else if (!strcmp(argv[i], "-file")) {} else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal"))) { if ((i + 1) >= argc) continue; if (argv[i + 1][0] == '-') continue; i++; if ((argv[i][0] < '0') || (argv[i][1] > '9')) { fprintf(stderr, "-signal %s is invalid\n", argv[i]); fprintf(stderr, "signals must be specified as integer values\n"); usage(name); exit(1); } } else if (!strcmp(argv[i], "-dispatch")) {} else if (!strcmp(argv[i], "-check")) {} else if (!strcmp(argv[i], "-plain")) {} else if (!strcmp(argv[i], "-p")) { if ((i + 1) >= argc) { fprintf(stderr, "name required following -p\n"); usage(name); exit(1); } i++; } else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9')))) { if ((i + 1) >= argc) { fprintf(stderr, "name required following %s\n", argv[i]); usage(name); exit(1); } i++; } else if (!strcmp(argv[i], "-g")) { if ((i + 1) >= argc) { fprintf(stderr, "name required following -g\n"); usage(name); exit(1); } i++; } else if (!strcmp(argv[i], "-s")) { if ((i + 1) >= argc) { fprintf(stderr, "name required following -s\n"); usage(name); exit(1); } i++; if ((i + 1) >= argc) { fprintf(stderr, "value required following -s name\n"); usage(name); exit(1); } i++; state = atoll(argv[i]); if ((state == 0) && (strcmp(argv[i], "0"))) { fprintf(stderr, "value following -s name must be a 64-bit integer\n"); } } else { fprintf(stderr, "unrecognized option: %s\n", argv[i]); usage(name); exit(1); } } if (opts != 0) notify_set_options(opts); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-port")) { ntype = TYPE_PORT; } else if (!strcmp(argv[i], "-file")) { ntype = TYPE_FILE; } else if ((!strcmp(argv[i], "-sig")) || (!strcmp(argv[i], "-signal"))) { ntype = TYPE_SIGNAL; if (((i + 1) < argc) && (argv[i + 1][0] != '-')) { i++; signum = atoi(argv[i]); } } else if (!strcmp(argv[i], "-dispatch")) { ntype = TYPE_DISPATCH; } else if (!strcmp(argv[i], "-check")) { ntype = TYPE_CHECK; } else if (!strcmp(argv[i], "-plain")) { ntype = TYPE_PLAIN; } else if (!strcmp(argv[i], "-p")) { if ((i + 1) >= argc) { usage(name); exit(1); } i++; status = notify_post(argv[i]); if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); else if (nap > 0) usleep(nap); } else if ((argv[i][0] == '-') && ((argv[i][1] == 'w') || ((argv[i][1] >= '0') && (argv[i][1] <= '9')))) { if ((i + 1) >= argc) { usage(name); exit(1); } n = IndexNull; if (argv[i][1] != 'w') n = atoi(argv[i] + 1); i++; tid = IndexNull; index = reg_find_name(argv[i]); if (index != IndexNull) { fprintf(stderr, "Already watching for %s\n", argv[i]); continue; } status = do_register(argv[i], ntype, signum, n); if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); } else if (!strcmp(argv[i], "-g")) { if ((i + 1) >= argc) { usage(name); exit(1); } i++; state = 0; tid = IndexNull; status = notify_register_plain(argv[i], &tid); if (status == NOTIFY_STATUS_OK) { status = notify_get_state(tid, &state); notify_cancel(tid); } if (status == NOTIFY_STATUS_OK) printf("%s %llu\n", argv[i], (unsigned long long)state); else printf("%s: %s\n", argv[i], notify_status_strerror(status)); } else if (!strcmp(argv[i], "-s")) { if ((i + 2) >= argc) { usage(name); exit(1); } i++; tid = IndexNull; status = notify_register_plain(argv[i], &tid); if (status == NOTIFY_STATUS_OK) { state = atoll(argv[i + 1]); status = notify_set_state(tid, state); notify_cancel(tid); } if (status != NOTIFY_STATUS_OK) printf("%s: %s\n", argv[i], notify_status_strerror(status)); i++; } } if (reg_count == 0) exit(0); dispatch_main(); }