Discussion:
[launchd-dev] The MachService key
Jerry Krinock
2011-11-22 01:28:25 UTC
Permalink
In trying to understand how a process works, I found a launchd plist with a MachService key.

From launchd.plist(5), I learn that that this is to "specify Mach services to be registered with the Mach bootstrap sub-system".

I know a little about the Mach bootstrap sub-system, but I can't find much information about Mach services.

Does anyone know a book or other resource where I could learn about Mach services?

Thanks,

Jerry Krinock
Quinn "The Eskimo!"
2011-11-22 10:02:56 UTC
Permalink
Post by Jerry Krinock
Does anyone know a book or other resource where I could learn about Mach services?
Prior to the introduction of XPC in Mac OS X 10.7, registering a Mach service required that you do your IPC using Mach messaging. Mach messaging is /extremely/ tricky to get right, and I highly recommend that you avoid it.

XPC makes this much easier. Much of the focus of XPC is in providing XPC services, that is, small chunks of code that you include in an app that you can talk to from the app. This makes certain security tasks (sandboxing, privilege separation) much easier. However, it's also possible to use XPC to talk to a launchd daemon.

With the above in mind, is taking a dependency on 10.7 feasible or not? If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
Thomas Clement
2011-11-22 10:27:47 UTC
Permalink
Post by Quinn &quot;The Eskimo!&quot;
Post by Jerry Krinock
Does anyone know a book or other resource where I could learn about Mach services?
Prior to the introduction of XPC in Mac OS X 10.7, registering a Mach service required that you do your IPC using Mach messaging. Mach messaging is /extremely/ tricky to get right, and I highly recommend that you avoid it.
Actually there are also high level APIs that can be used to do that prior to Mac OS X 10.7: NSMachPort / CFMachPort or CFMessagePort can be used to do IPC through the advertised mach service.

Works well.


Thomas
Quinn &quot;The Eskimo!&quot;
2011-11-22 11:10:02 UTC
Permalink
Post by Thomas Clement
Actually there are also high level APIs that can be used to do that prior to Mac OS X 10.7: NSMachPort / CFMachPort or CFMessagePort can be used to do IPC through the advertised mach service.
I've not had a good experience with CFMessagePort. Maybe that's 'cause I work in DTS, and thus I only hear about stuff when it fails, but my experience is that, although CFMessagePort simplifies Mach messaging to the point where you can reasonably use it, there are still plenty of sharp edges.

But your point is well made: if you must support systems prior to 10.7, using CFMessagePort is vastly preferable than trying to do Mach messaging directly.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
Jerry Krinock
2011-11-22 12:42:02 UTC
Permalink
With the above in mind, is taking a dependency on 10.7 feasible?
Yes, we're all Lions now.
If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.
Yes, please do. That was going to be my next question.

As to what a Mach Service is, I gather from the discussion so far that a Mach Service is the underlying IPC thing upon which CFMessagePort, NSPort, etc. (both of which I've used and am familiar with) are built. Is "Mach Service" maybe just another name for "Mach Port"?

Thank you,

Jerry
Quinn &quot;The Eskimo!&quot;
2011-11-22 15:49:28 UTC
Permalink
Post by Jerry Krinock
Is "Mach Service" maybe just another name for "Mach Port"?
Well, yes, and no. Mach messaging is a capability-based, so the kernel has to explicitly grant you permission to access a particular Mach port. There are various ways to do this, but the most common is for each service provider to register its port with the Mach bootstrap service (every process inherits the capability to address the bootstrap service from its parent). An entry in the "MachServices" key tells launchd to create a Mach port and register it with the bootstrap service with the given string. Clients can then ask the bootstrap service to look up that string and thus obtain the capability to send a message to that port. When a client sends a message to that port, launchd starts the daemon and the daemon can then process the message.
Post by Jerry Krinock
Post by Quinn &quot;The Eskimo!&quot;
If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.
Yes, please do. That was going to be my next question.
The basic strategy is:

o In the server, create an XPC listening connection using xpc_connection_create_mach_service.

o Set an event handler using xpc_connection_set_event_handler.

o Call xpc_connection_resume to enable the connection.

o Call dispatch_main to have the server wait for incoming connections.

o When a connection comes in, XPC will call your event handler with an object of type XPC_TYPE_CONNECTION. You should enable this connection by setting an event handler (xpc_connection_set_event_handler) and resuming it (xpc_connection_resume). It's this event handler that will be called when the client sends you a message.

o In the client, create an XPC connection using xpc_connection_create_mach_service. If the client is a normal app and the service is vended by a daemon, pass in XPC_CONNECTION_MACH_SERVICE_PRIVILEGED.

o From then on, in both the client and the server, following the standard XPC techniques.

<http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html>

<http://developer.apple.com/library/mac/#documentation/System/Reference/XPCServicesFW/index.html#//apple_ref/doc/uid/TP40010357>

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
Thomas Clement
2011-11-22 16:30:49 UTC
Permalink
Post by Quinn &quot;The Eskimo!&quot;
Post by Jerry Krinock
Post by Quinn &quot;The Eskimo!&quot;
If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.
Yes, please do. That was going to be my next question.
o In the server, create an XPC listening connection using xpc_connection_create_mach_service.
And pass the flag XPC_CONNECTION_MACH_SERVICE_LISTENER for the last argument.


Thomas
Rainer Brockerhoff
2011-11-22 15:56:22 UTC
Permalink
Date: Tue, 22 Nov 2011 10:02:56 +0000
From: "Quinn \"The Eskimo!\"" <eskimo1 at apple.com>
Message-ID: <86B0B3BF-8627-40C7-9B29-24A38F3608E8 at apple.com>
XPC makes this much easier. Much of the focus of XPC is in providing XPC services, that is, small chunks of code that you include in an app that you can talk to from the app. This makes certain security tasks (sandboxing, privilege separation) much easier. However, it's also possible to use XPC to talk to a launchd daemon.
With the above in mind, is taking a dependency on 10.7 feasible or not? If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.
I'll have a situation - 10.7 (and successors) only - where I'd like to talk to my launchd daemon, both from an app and perhaps from an XPC service launched by that app. Messages could be either NS/CFDictionaries or a blob of binary data.

I'm not particularly set on using MachServices, or CFMessagePort, or whatever; but it would be helpful if you, in that explanation, could mention what the easiest (or more reliable?) API for doing so on Lion.

Thanks,
--
Rainer Brockerhoff <rainer at brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."
Weblog: http://www.brockerhoff.net/blog
Quinn &quot;The Eskimo!&quot;
2011-11-22 17:25:09 UTC
Permalink
Post by Rainer Brockerhoff
but it would be helpful if you, in that explanation, could mention what the easiest (or more reliable?) API for doing so on Lion
XPC.

You're already using it to talk to your XPC service, so you know the API already, and it's the best, most forward-looking IPC API we have.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
Damien Sorresso
2011-11-22 18:47:46 UTC
Permalink
Post by Rainer Brockerhoff
Date: Tue, 22 Nov 2011 10:02:56 +0000
From: "Quinn \"The Eskimo!\"" <eskimo1 at apple.com>
Message-ID: <86B0B3BF-8627-40C7-9B29-24A38F3608E8 at apple.com>
XPC makes this much easier. Much of the focus of XPC is in providing XPC services, that is, small chunks of code that you include in an app that you can talk to from the app. This makes certain security tasks (sandboxing, privilege separation) much easier. However, it's also possible to use XPC to talk to a launchd daemon.
With the above in mind, is taking a dependency on 10.7 feasible or not? If it is, I can explain how you can use XPC to talk to a launchd daemon via the MachServices key.
I'll have a situation - 10.7 (and successors) only - where I'd like to talk to my launchd daemon, both from an app and perhaps from an XPC service launched by that app. Messages could be either NS/CFDictionaries or a blob of binary data.
I'm not particularly set on using MachServices, or CFMessagePort, or whatever; but it would be helpful if you, in that explanation, could mention what the easiest (or more reliable?) API for doing so on Lion.
Rainer,

Check out the XPC man pages for more complete documentation about how to use XPC to talk to MachServices. Specifically, xpc_connection_create_mach_service(3). The man pages shipped with Xcode 4.2. There is also extensive HeaderDoc in /usr/include/xpc.
--
Damien Sorresso
dsorresso at apple.com
Damien Sorresso
2011-11-22 18:57:50 UTC
Permalink
Post by Jerry Krinock
In trying to understand how a process works, I found a launchd plist with a MachService key.
From launchd.plist(5), I learn that that this is to "specify Mach services to be registered with the Mach bootstrap sub-system".
I know a little about the Mach bootstrap sub-system, but I can't find much information about Mach services.
Does anyone know a book or other resource where I could learn about Mach services?
There's really not much to learn. It's a hierarchical namespace that provides way more flexibility than has shown to be practically needed or desirable.

The standard workflow was that, when a daemon advertising MachServices is launched on-demand, it used bootstrap_check_in() to obtain the receive rights to the ports corresponding to those services. It could then dequeue messages from the ports from there, either using mach_msg() directly or MIG.

Anyone who wished to send a message to one of those services used bootstrap_look_up() to obtain a send right to that service, and from there they could send messages. That act of sending a message (NOT the act of looking up the name) would launch the daemon on-demand if it's not already running.

There were a whole host of other APIs that were of dubious value after the introduction of launchd and the MachServices key.

XPC has pretty much rendered all of those APIs obsolete though.
--
Damien Sorresso
dsorresso at apple.com
Jerry Krinock
2011-11-23 06:07:06 UTC
Permalink
Well, I read all the advice in this thread, all the docs and headers I could find, watched WWDC 2011 Session 206 and came up with the following "Hello World" Mach Service Demo.

Not surprisingly, it doesn't work. If any of you guys could point out the mistakes I'd appreciate it. I know there are multiple problems?

? Launchd doesn't even try to launch the Server when Client sends message.
? Run loops never time out ? wrong modes?

Sorry if this is like shooting fish in a bowl, but there's a lot of new stuff in here for me.


*********** Client-main.m ********************************

#import <xpc/xpc.h>

int main(int argc, const char *argv[]) {
@autoreleasepool {
NSLog(@"MachServiceDemo Client has launched") ;

// Create the service
const char* svcName = "com.machservicedemo.client" ;
xpc_connection_t listener ;
listener = xpc_connection_create_mach_service(
svcName,
NULL,
0
) ;
// Dispatch queue is NULL per WWDC Session 206 ?!?!

// Set an Event Handler
xpc_connection_set_event_handler(listener, ^(xpc_object_t event) {
// This is just a placeholder until we get the server working
}) ;
xpc_connection_resume(listener) ;

// Send a "Hello World!" message
xpc_object_t msgDic ;
msgDic = xpc_dictionary_create(NULL, NULL, 0) ;
xpc_dictionary_set_string(msgDic, "Msg", "Hello, World!") ;
xpc_connection_send_message(listener, msgDic) ;
xpc_release(msgDic) ;

// Run for a few seconds
NSTimeInterval doneSeconds = 10 ;
NSDate* doneDate = [NSDate dateWithTimeIntervalSinceNow:doneSeconds] ;
NSLog(@"MachServiceDemo Client will run until %@", doneDate) ;

while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:doneDate]) {
}
}

NSLog(@"MachServiceDemo Client is exitting.") ;

return (EXIT_SUCCESS) ;
}


*********** Server-main.m ********************************

#import <xpc/xpc.h>

int main(int argc, const char *argv[]) {
@autoreleasepool {
NSLog(@"MachServiceDemo Server has launched") ;

dispatch_queue_t myQ = dispatch_queue_create("myQ", NULL) ;

// Create the service
const char* svcName = "com.machservicedemo.client" ;
uint64_t flags = XPC_CONNECTION_MACH_SERVICE_LISTENER ;
xpc_connection_t listener ;
listener = xpc_connection_create_mach_service(
svcName,
myQ,
flags
) ;

// Set an Event Handler
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
NSLog(@"MachServiceDemo Server got connection: %s", desc) ;
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
NSLog(@"MachServiceDemo Server got message: %s", desc) ;
free(desc) ;
}) ;
}) ;
xpc_connection_resume(listener) ;

// Run for a few seconds
NSTimeInterval doneSeconds = 10 ;
NSDate* doneDate = [NSDate dateWithTimeIntervalSinceNow:doneSeconds] ;
NSLog(@"MachServiceDemo Server will run until %@", doneDate) ;
while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:doneDate]) {
}

dispatch_release(myQ) ;
}

NSLog(@"MachServiceDemo Server is exitting") ;

return (EXIT_SUCCESS) ;
}


*********** com.machservicedemo.plist *****************

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.machservicedemo</string>
<key>MachServices</key>
<dict>
<key>com.machservicedemo.client</key>
<true/>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Users/jk/Desktop/MachServiceDemo-Server</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>


*********** Run Script Build Phase *****************

# Copy Server to Desktop
cp $CONFIGURATION_BUILD_DIR/MachServiceDemo-Server $HOME/Desktop/MachServiceDemo-Server

# Copy launchd plist to LaunchAgents
cp $PROJECT_DIR/MachServiceDemo/com.machservicedemo.plist $HOME/Library/LaunchAgents/com.machservicedemo.plist

# (Re)load the launchd job
cd $HOME/Library/LaunchAgents/
launchctl unload com.machservicedemo.plist
sleep 2
launchctl load com.machservicedemo.plist
echo Did reload launchd job


****************************************************


My Xcode project has two targets, Server and Client.
Client target depends on Server target.
Run Script Build Phase is in the Client target.
To test, Build and Run the Client target/scheme.

Thanks again,

Jerry
Jerry Krinock
2011-11-23 12:58:54 UTC
Permalink
Update:

In the Run Script Build Phase, added another "sleep 2" *after* loading the job. This caused the Server to start getting the connection?

11/11/23 4:50:30.832 AM MachServiceDemo-Server: MachServiceDemo Server got connection: <connection: 0x7fef3ae01240> { name = com.machservicedemo.client (peer), listener = false, PID = 61222, EUID = 501, EGID = 20, ASID = 100004 }

But there is still no sign of the subsequent message.

In the plist, I deleted RunAtLoad so that the Server will not launch until the Client demands it. Also I added EnableTransactions=true. Adding EnableTransactions had no effect.
Damien Sorresso
2011-11-23 18:28:13 UTC
Permalink
Post by Jerry Krinock
// Set an Event Handler
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
free(desc) ;
}) ;
}) ;
xpc_connection_resume(listener) ;
That inner xpc_connection_set_event_handler(3) should target 'object' (the new incoming connection), not 'listener'.
--
Damien Sorresso
dsorresso at apple.com
Damien Sorresso
2011-11-23 18:39:19 UTC
Permalink
Post by Damien Sorresso
Post by Jerry Krinock
// Set an Event Handler
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
char* desc = xpc_copy_description(object);
free(desc) ;
}) ;
}) ;
xpc_connection_resume(listener) ;
That inner xpc_connection_set_event_handler(3) should target 'object' (the new incoming connection), not 'listener'.
Oh, and you also need to resume 'object'.
Post by Damien Sorresso
xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
assert(xpc_get_type(peer) == XPC_TYPE_CONNECTION);
xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
if (event == XPC_ERROR_CONNECTION_INVALID) {
// Peer has gone away.
} else {
assert(xpc_get_type(event) == XPC_TYPE_DICTIONARY);
// Handle message.
}
});
xpc_connection_resume(peer);
});
Hope this helps. I highly recommend checking out the man pages.

$ man xpc
--
Damien Sorresso
dsorresso at apple.com
Jerry Krinock
2011-11-23 20:37:29 UTC
Permalink
Thank you, Damien. With the two corrections you noted in your last two messages, my demo works now.
Post by Damien Sorresso
I highly recommend checking out the man pages.
$ man xpc
Ah, I see now ? man pageS ? plural. Although "man xpc" does not say very much, the SEE ALSO section at the bottom references a man page for each function. These pages contain lots of how-to information, although some of them bring up the same page.
Damien Sorresso
2011-11-23 20:46:18 UTC
Permalink
Post by Jerry Krinock
Thank you, Damien. With the two corrections you noted in your last two messages, my demo works now.
Post by Damien Sorresso
I highly recommend checking out the man pages.
$ man xpc
Ah, I see now ? man pageS ? plural. Although "man xpc" does not say very much, the SEE ALSO section at the bottom references a man page for each function. These pages contain lots of how-to information, although some of them bring up the same page.
A single man page can contain references to many APIs. There are a handful of man pages that contain everything, and then there are hard links to those pages for the other various APIs. For example, xpc_connection_create(3) and xpc_connection_create_mach_service(3) are both discussed in /usr/share/man/man3/xpc_connection_create.3, so /usr/share/man/man3/xpc_connection_create_mach_service.3 is just a hard link to /usr/share/man/man3/xpc_connection_create.3.

Glad to hear things are working for you.
--
Damien Sorresso
dsorresso at apple.com
Loading...