[launchd-dev] Distributed Objects communication with a launchd "on-demand" daemon

Frank Rizzo jerky.frank.rizzo at gmail.com
Wed Dec 16 21:09:04 PST 2009


My dear fellow Cocoa programmers,

I am trying to create a launchd daemon that is started "on-demand" by a
client call to a TCP port number and then communicate with the client via
Distributed Objects.

I am having problems combining the "on-demand" launchd feature with the
particular choice of Distributed Objects for communication.  I have no
problem with using Distributed Objects with a daemon that is always running
or communicating with with  "on-demand" launchd daemons using techniques
other than Distributed Objects.

(1) So my first question, is it indeed possible to use Distributed Objects
with "on-demand" launchd daemons?

Here I want to note that this launchd-dev thread implies it is:
http://old.nabble.com/Diagnosing-launchd-daemon-failure-to-launch-td20344390.html


... and so does this blog entry by Chris Hanson:
http://chanson.livejournal.com/179229.html



Here the code that I am testing with:

// TestDaemonLaunchd.plist

<dict>
<key>ServiceIPC</key>
<true/>
<key>Sockets</key>
<dict>
<key>Socket</key>
<dict>
<key>SockServiceName</key>
<string>8081</string>
</dict>
</dict>
<key>OnDemand</key>
<true/>
<key>Label</key>
<string>com.example.TestDaemon</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/TestDaemon</string>
</array>
</dict>


//  TestDaemon.m

int main (int argc, const char * argv[]) {

    NSAutoreleasePool *pool = [NSAutoreleasePool new];

// NOTE: Getting the TCP socket file descriptor (fd) from launchd.
     launch_data_t checkinRequest =
launch_data_new_string(LAUNCH_KEY_CHECKIN);
launch_data_t checkinResponse = launch_msg(checkinRequest);
launch_data_t socketsDict = launch_data_dict_lookup(checkinResponse,
LAUNCH_JOBKEY_SOCKETS);
launch_data_t fdArray = launch_data_dict_lookup(socketsDict, "Socket");
launch_data_t  fdData = launch_data_array_get_index(fdArray, 0);

int fd = launch_data_get_fd(fdData);

        launch_data_free(checkinResponse);
        launch_data_free(checkinRequest);

NSSocketPort *receivePort = [[NSSocketPort alloc]
initWithProtocolFamily:PF_INET socketType:SOCK_STREAM protocol:IPPROTO_TCP
socket:fd];
NSConnection *connection = [NSConnection
connectionWithReceivePort:receivePort sendPort:nil];
[receivePort release];

id testObject = [NSObject new];
[connection setRootObject:testObject];

[[NSRunLoop currentRunLoop] run];

[testObject release];
     [pool release];
    return 0;
}


//  TestClient.m
int main (int argc, const char * argv[]) {

    NSAutoreleasePool *pool = [NSAutoreleasePool new];

NSSocketPort *sendPort = [[NSSocketPort alloc] initRemoteWithTCPPort:8081
host:@"127.0.0.1"];

NSConnection *connection = [NSConnection connectionWithReceivePort:nil
sendPort:sendPort];

[connection setRequestTimeout:10.0];
[connection setReplyTimeout:10.0];

[sendPort release];

// NOTE: Here the -rootProxy method always raises an NSPortTimeoutException
when used with an "on-demand" launchd daemon.
id proxy = [connection rootProxy];
[pool release];
    return 0;
}




(2) When I use launchctl to the TestDaemonLaunchd.plist above, I always get
an NSPortTimeoutException in TestClient.m when calling [connection
rootProxy].  If I remove the Sockets dictionary from the
TestDaemonLaunchd.plist (so launchd does not interfere with normal
communication from the client to the dameon) and set "OnDemand" to false (so
the daemon runs all the time), there is no problem.  [connection rootProxy]
return a valid NSDistantObject for the testObject set in TestDaemon.m.  Why
am I getting NSPortTimeoutException when I try running it "OnDmeand"?

I suspect that answer has to do with the fact I am not a setting up a kqueue
and then getting socket file descriptors subsequent kevent as is down in
Apple's SampleD example:

http://developer.apple.com/mac/library/samplecode/SampleD/listing3.html
    ...

    /*
     * Initialize a new kernel event.  This will trigger when
     * a connection occurs on our listener socket.
     *
     */
    for (i = 0; i < launch_data_array_get_count(listening_fd_array); i++) {
        launch_data_t this_listening_fd =
launch_data_array_get_index(listening_fd_array, i);

        EV_SET(&kev_init, launch_data_get_fd(this_listening_fd),
EVFILT_READ, EV_ADD, 0, 0, NULL);
        if (kevent(kq, &kev_init, 1, NULL, 0, NULL) == -1) {
            asl_log(asl, log_msg, ASL_LEVEL_DEBUG, "kevent(): %m");
            retval = EXIT_FAILURE;
            goto done;
        }
    }

    launch_data_free(checkin_response);

    for (;;) {
        FILE *the_stream;
        int filedesc, gai_r;
        char nodename[1024];
        char servname[1024];

        /*
         *
         * Get the next event from the kernel event queue.
         *
         */
        if ((filedesc = kevent(kq, NULL, 0, &kev_listener, 1, NULL)) == -1)
{
            asl_log(asl, log_msg, ASL_LEVEL_ERR, "kevent(): %m");
            retval = EXIT_FAILURE;
            goto done;
        } else if (filedesc == 0) {
            retval = EXIT_SUCCESS;
            goto done;
        }

        /*
         *
         * Accept an incoming connection.
         *
         */
        if ((filedesc = accept(kev_listener.ident, (struct sockaddr *)&ss,
&slen)) == -1) {
            asl_log(asl, log_msg, ASL_LEVEL_ERR, "accept(): %m");
            continue; /* this isn't fatal */
        }

...

Is that sort of code required to make Distributed Objects work with
"OnDemand" launchd daemons?  If so, what I am supposed to do with the
additional sock file descriptors (filedesc)?  I can certainly create
NSSocketPorts and then NSConnections with them, but isn't Distributed
Objects supposed to do that automatically?  Is it even possible to do it for
Distributed Objects manually?


Thanks in advance for any help,
- Frank Rizzo
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20091217/b9303e33/attachment.html>


More information about the launchd-dev mailing list