问题
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");
//...
}
回答1:
- you should not specify the socket in the plist, you know the concrete place, the server knows, the client knows, that should be enought
- the socket specified in the plist is for launching the deamon/agent ondemand, you do not need that in a basic case
- you can use any path, if your daemon starts first (usually the case) it has all the privilege to set the correct rights on the socket to let anybody (or a given user, group) read, read and write or any given rights you wish, i'm sure you just forgot to let the client write/read the unix socket
- i think on OSX the unix socket IPC is a perfect, easy solution (you have many other choices too of course, xpc, mach messages, etc.)
- you can define where to go the stdout and stderr of a daemon in it's plist (StandardOutPath, StandardErrorPath keys)
来源:https://stackoverflow.com/questions/13858035/daemon-client-ipc-with-unix-sockets