static void SBA_refresh(void){ // still valid if (springBoardAccessMessagePort && !CFMessagePortIsValid(springBoardAccessMessagePort)){ CFRelease(springBoardAccessMessagePort); springBoardAccessMessagePort = NULL; } // create new one if (!springBoardAccessMessagePort) { springBoardAccessMessagePort = CFMessagePortCreateRemote(NULL, CFSTR(SBA_MessagePortName)); } }
static void __CFMessagePortDeallocate(CFTypeRef cf) { CFMessagePortRef ms = (CFMessagePortRef)cf; __CFMessagePortSetIsDeallocing(ms); CFMessagePortInvalidate(ms); // Delay cleanup of _replies until here so that invalidation during // SendRequest does not cause _replies to disappear out from under that function. if (NULL != ms->_replies) { CFRelease(ms->_replies); } if (NULL != ms->_name) { CFRelease(ms->_name); } if (NULL != ms->_port) { if (__CFMessagePortExtraMachRef(ms)) { mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_SEND, -1); mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_RECEIVE, -1); } CFMachPortInvalidate(ms->_port); CFRelease(ms->_port); } // A remote message port for a local message port in the same process will get the // same mach port, and the remote port will keep the mach port from being torn down, // thus keeping the remote port from getting any sort of death notification and // auto-invalidating; so we manually implement the 'auto-invalidation' here by // tickling each remote port to check its state after any message port is destroyed, // but most importantly after local message ports are destroyed. __CFLock(&__CFAllMessagePortsLock); CFMessagePortRef *remotePorts = NULL; CFIndex cnt = 0; if (NULL != __CFAllRemoteMessagePorts) { cnt = CFDictionaryGetCount(__CFAllRemoteMessagePorts); remotePorts = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFMessagePortRef), __kCFAllocatorGCScannedMemory); CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts, NULL, (const void **)remotePorts); for (CFIndex idx = 0; idx < cnt; idx++) { CFRetain(remotePorts[idx]); } } __CFUnlock(&__CFAllMessagePortsLock); if (remotePorts) { for (CFIndex idx = 0; idx < cnt; idx++) { // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid CFMessagePortIsValid(remotePorts[idx]); CFRelease(remotePorts[idx]); } CFAllocatorDeallocate(kCFAllocatorSystemDefault, remotePorts); } }
SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) { struct __CFMessagePortMachMessage *sendmsg; CFRunLoopRef currentRL = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = NULL; CFDataRef reply = NULL; int64_t termTSR; uint32_t sendOpts = 0, sendTimeOut = 0; int32_t desiredReply; Boolean didRegister = false; kern_return_t ret; //#warning CF: This should be an assert // if (!__CFMessagePortIsRemote(remote)) return -999; if (!__CFMessagePortIsValid(remote)) return kCFMessagePortIsInvalid; __CFMessagePortLock(remote); if (NULL == remote->_replyPort) { CFMachPortContext context; context.version = 0; context.info = remote; context.retain = (const void *(*)(const void *))CFRetain; context.release = (void (*)(const void *))CFRelease; context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL); } remote->_convCounter++; desiredReply = -remote->_convCounter; CFDictionarySetValue(remote->_replies, (void *)desiredReply, NULL); sendmsg = __CFMessagePortCreateMessage(CFGetAllocator(remote), false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0)); __CFMessagePortUnlock(remote); if (replyMode != NULL) { source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100); didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode); if (didRegister) { CFRunLoopAddSource(currentRL, source, replyMode); } } if (sendTimeout < 10.0*86400) { // anything more than 10 days is no timeout! sendOpts = MACH_SEND_TIMEOUT; sendTimeout *= 1000.0; if (sendTimeout < 1.0) sendTimeout = 0.0; sendTimeOut = floor(sendTimeout); } ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->head.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL); CFAllocatorDeallocate(CFGetAllocator(remote), sendmsg); if (KERN_SUCCESS != ret) { if (didRegister) { CFRunLoopRemoveSource(currentRL, source, replyMode); CFRelease(source); } if (MACH_SEND_TIMED_OUT == ret) return kCFMessagePortSendTimeout; return kCFMessagePortTransportError; } if (replyMode == NULL) { return kCFMessagePortSuccess; } CFRetain(remote); // retain during run loop to avoid invalidation causing freeing _CFMachPortInstallNotifyPort(currentRL, replyMode); termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout); for (;;) { CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true); // warning: what, if anything, should be done if remote is now invalid? reply = CFDictionaryGetValue(remote->_replies, (void *)desiredReply); if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) { break; } if (!CFMessagePortIsValid(remote)) { // no reason that reply port alone should go invalid so we don't check for that break; } } // Should we uninstall the notify port? A complex question... if (didRegister) { CFRunLoopRemoveSource(currentRL, source, replyMode); CFRelease(source); } if (NULL == reply) { CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); CFRelease(remote); return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5; } if (NULL != returnDatap) { *returnDatap = ((void *)0xffffffff == reply) ? NULL : reply; } else if ((void *)0xffffffff != reply) { CFRelease(reply); } CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); CFRelease(remote); return kCFMessagePortSuccess; }
int main(int argc, char * argv[]) { signal(SIGINT, sigint_handler); bool israw = false; bool readstdin = false; char* code = NULL; bool usecolors = true; int ch; while ((ch = getopt(argc, argv, "nirc:t:sh")) != -1) { switch (ch) { case 'n': usecolors = false; break; case 'i': break; case 'r': israw = true; break; case 'c': code = optarg; break; case 't': recvTimeout = strtod(optarg, NULL); break; case 's': readstdin = true; break; case 'h': case '?': default: usage(argv[0]); } } if (optind != argc) usage(argv[0]); argc -= optind; argv += optind; CFMessagePortRef port = CFMessagePortCreateRemote(NULL, CFSTR("Hammerspoon")); if (!port) { fprintf(stderr, "error: can't access Hammerspoon; is it running with the ipc module loaded?\n"); return 1; } CFMutableStringRef str = CFStringCreateMutable(NULL, 0); if (readstdin) { target_setprefix(str, israw); char buffer[BUFSIZ]; while (fgets(buffer, BUFSIZ, stdin)) CFStringAppendCString(str, buffer, kCFStringEncodingUTF8); if (ferror(stdin)) { perror("error reading from stdin."); exit(3); } target_send(port, str); } else if (code) { target_setprefix(str, israw); CFStringAppendCString(str, code, kCFStringEncodingUTF8); target_send(port, str); } else { if (usecolors) setupcolors(); printf("%sHammerspoon interactive prompt.%s\n", COLOR_INITIAL, COLOR_RESET); while (1) { printf("\n%s", COLOR_INPUT); char* input = readline("> "); printf("%s", COLOR_RESET); if (!input) { printf("\n") ; exit(0); } add_history(input); if (!CFMessagePortIsValid(port)) { fprintf(stderr, "%sMessage port has become invalid. Attempting to re-establish.%s\n", COLOR_INITIAL, COLOR_RESET); port = CFMessagePortCreateRemote(NULL, CFSTR("Hammerspoon")); if (!port) { fprintf(stderr, "error: can't access Hammerspoon; is it running?\n"); exit(1); } } target_setprefix(str, israw); CFStringAppendCString(str, input, kCFStringEncodingUTF8); target_send(port, str); CFStringDelete(str, CFRangeMake(0, CFStringGetLength(str))); free(input); } } return 0; }