I have a launch daemon which I want to ask for status information from a user app. I implemented a client-server model (with the daemon as server) using unix sockets as described here: OS X - Communication between launch daemon and launch agent
In fact it works well, when I run the daemon as a user process (for debugging), but it will fail when it is actually launched as root.
I have read the TN on Daemons and Agents and the Daemon & Services Programming Guide. However, I could not find decent information how the socket must be used in a launch daemon.
I am confused by several things:
Must I specify the socket in the launch daemon plist file? And how?
If the socket is specified in the plist, does that change the way I need to create the socket in code?
What path would be good for the unix socket? The Technical Note recommends
/var/run
but I guess a user process may not write there, or can it?Is there maybe a easier way to do IPC between daemon and client?
What is the best way to log the daemon output. I tried NSLog but it seems not work...
I am also unsure if my socket code is correct. Maybe someone more experienced can tell me if I'm on the right track here. I have the following code in the daemon to initialize the unix socket:
#define SOCKETNAME "/var/run/com.company.myApp.socket"
- (void) startServer {
//remove any prev socket
unlink(SOCKETNAME);
CFSocketContext CTX = { 0, (__bridge void *)(self), NULL, NULL, NULL };
CFSocketRef unixSocket = CFSocketCreate(NULL, PF_UNIX, SOCK_STREAM, 0,
kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
if (unixSocket == NULL) {/*log and return*/}
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCKETNAME);
addr.sun_len = strlen(addr.sun_path) + sizeof (addr.sun_family);
NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
if (CFSocketSetAddress(unixSocket, (__bridge CFDataRef) address) != kCFSocketSuccess) {
NSLog(@"CFSocketSetAddress() failed\n");
CFRelease(unixSocket);
}
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, unixSocket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
CFRunLoopRun();
}
void AcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
CTServerController* selfServerController = (__bridge CTServerController*) info;
//NSLog(@"acceptCallBack");
//...
}