MacOS – Loading User LaunchAgent Successfully

launchdmacosplistssh

I have created a plist file to replace an xinetd process that I used successfully in Windows (Cygwin) and Linux to stream an imapd connection from a remote server via ssh on a port on the localhost.

The plutil command says my configuration is OK. launchctl load path-to-plist runs without errors.

When I run launchctl list the agent does not display. When I try to connect to the port on the localhost, I get connection refused.

Here is my plist file:

<?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.my.ssh_tunnel</string>
        <key>Program</key>
            <string>/usr/bin/ssh</string>
        <key>ProgramArguments</key>
            <array>
                    <string>-F /Users/userx/.ssh/config</string>
                    <string>dname /usr/sbin/imapd</string>
            </array>
        <key>Sockets</key>
            <dict>
                <key>Listeners</key>
                <dict>
                    <key>SockServiceName</key>
                        <string>dname-imapd</string>
                    <key>SockType</key>
                        <string>stream</string>
                    <key>SockProtocol</key>
                        <string>TCP</string>
                </dict>
            </dict>
        <key>inetdCompatibility</key>
            <dict>
                <key>Disabled</key>
                <false/>
                <key>Wait</key>
                <false/>
            </dict>
        <key>EnvironmentVariables</key>
            <dict>
                <key>SSH_AUTH_SOCK</key>
                    <string>/Users/userx/.ssh-agent.sock</string>
            </dict>
    </dict>
</plist>

I have added a line to /etc/services for dname-imapd and an unassigned port (49022). Ultimately I will be using this in conjunction with ssh-agent (to make this work 'passwordless') and Thunderbird to retrieve mail from a very locked down system.

There are essentially two questions here. One, how do I know my agent is being loaded (and how do I confirm that)? Two, is my plist actually correct?

Here's my original xinetd config:

service imapssh
{
        disable         = no
        type            = UNLISTED
        port            = 2208
        socket_type     = stream
        protocol        = tcp
        wait            = no
        server          = /usr/bin/ssh
        server_args     = dname /usr/sbin/imapd
        user            = userx
}

Thanks for any help. This is driving me a little crazy.

This is on an El Capitan Macbook Pro.

Also, I've tried a straight ssh tunnel but that doesn't really work (before anyone suggests it). Unless someone knows how to make it work like inetd and spawn every time the mail client connects to the port.

Best Answer

OK, addressing your second question first (is my plist actually correct?), plutil (in its default invocation) "lints" (checks) plist files for correctness:

plutil ~/Library/LaunchAgents/local.brew.update.plist

Equivalent to:

plutil -lint ~/Library/LaunchAgents/local.brew.update.plist

Turning to your first question (how do I know my agent is being loaded (and how do I confirm that)?), here's a few things to help check that.

First, I'd recommend adding logging to aid debugging. I log to ~/Library/Logs, so add something like this to my plist:

<key>StandardOutPath</key>
<string>/Users/userx/Library/Logs/local.brew.update.stdout</string>
<key>StandardErrorPath</key>
<string>/Users/userx/Library/Logs/local.brew.update.stderr</string>

Try re-loading your plist and then check your logs:

less ~/Library/Logs/local.brew.update.stdout
less ~/Library/Logs/local.brew.update.stderr

A few additional observations:

My understanding of your inetdCompatibility is that the agent should start when it's loaded. However, you say that launchctl list doesn't show the agent. Does that change if you try launchctl start com.my.ssh_tunnel after loading the plist? Do the logs show anything new?

I don't entirely understand the relationship between Program and ProgramArguments - could just be me, but I find man launchd.plist's explanation of ProgramArguments hard-going. I'd be inclined to try adding /usr/bin/ssh as a first string in ProgramArguments, and removing the entry for Program. (Then re-load and check the logs. Re-start and check the logs if needed).

Finally, I'm pretty certain that @daniel-Azuelos is correct, and you need to specify your ProgramArguments like this (I've added usr/bin/ssh as mentioned above):

<key>ProgramArguments</key>
<array>
    <string>/usr/bin/ssh</string>
    <string>-F</string>
    <string>/Users/userx/.ssh/config</string>
    <string>dname</string>
    <string>/usr/sbin/imapd</string>
</array>

My reasoning for this is, the few plists I can find on my system that use (more than one) ProgramArguments use this idiom:

<key>Label</key>
<string>com.divx.uninstall.preferences</string>
<key>ProgramArguments</key>
<array>
    <string>/bin/bash</string>
    <string>-c</string>
    <string>if [[ ! -e "/Applications/DivX/DivX Preferences.app" ]] ; then open "/Library/Application Support/DivX/Uninstall DivX for Mac.app"; fi</string>
</array>

Note how bash's -c option and the corresponding command_string are in separate arguments.