Launchd socket input

command linelaunchd

I am trying to run a network server that is supposed to receive very short newline-terminated udp messages. The service plist looks like this

<?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>local.listener</string>
        <key>Program</key>
        <string>/bin/cat</string>
        <key>Sockets</key>
        <dict>
            <key>Listeners</key>
            <dict>
                 <key>SockType</key>
                 <string>dgram</string>
                 <key>SockNodeName</key>
                 <string>0.0.0.0</string>
                 <key>SockServiceName</key>
                 <integer>9999</integer>
            </dict>
        </dict>
        <key>inetdCompatibility</key>
        <dict>
            <key>Wait</key>
            <true/>
        </dict>
        <key>StandardOutPath</key>
        <string>/tmp/test.stdout</string>
</dict>

and it loads and does what it is supposed to do (write message strings to /tmp/test.stdout). However, I fail at replacing /bin/cat with a shell script
that reads the message into a variable and lets me do something with it. I
thought that something like this should work:

#!/bin/sh
read MSG
echo $MSG

but this appears to block, and so does

#!/bin/sh
/bin/cat

while

#!/bin/sh
exec /bin/cat

still works. On the command line all three variants do about the same thing, e.g.,

$ echo 123 | ./mycat.sh
123

and nothing blocks. Any insight into these subtle differences would be appreciated.

Best Answer

You're almost there. Wrap your first script in a while loop and you are set:

#!/bin/sh

while true; do
    read MSG
    echo $MSG
done

This is necessary to keep the process alive. Without the loop the script exits after the first line of input.

The exec approach works because you replace the current shell with /bin/cat. It is the same as calling /bin/cat directly.