Skip to content

Commit

Permalink
Support mac os x's launch_activate_socket (#1003)
Browse files Browse the repository at this point in the history
In Mac OS X 10.10, `launch_activate_socket` was introduced.
So whenever the port is requested, the `launchd` can launch the program
dynamically. A possible plist may 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>com.github.shadowsocks</string>
        <key>ProgramArguments</key>
        <array>
                <string>/path/to/ss-local</string>
                <string>-s</string>
                <string>server host</string>
                <string>-p</string>
                <string>server port</string>
                <!-- the -l and -b can be ommited, as launchd takes over it -->
                <string>-k</string>
                <string>password</string>
                <string>-m</string>
                <string>method</string>
                <string>-t</string>
                <string>timeout</string>
                <string>-A</string>
        </array>
        <key>Sockets</key>
        <dict>
                <!-- the name is hard coded in ss-local -->
                <key>Listeners</key>
                <dict>
                        <key>SockNodeName</key>
                        <string>127.0.0.1</string>
                        <key>SockServiceName</key>
                        <string>socks</string>
                </dict>
        </dict>
</dict>
</plist>
```

The magic is `launchd` will listen to the `SockServiceName`, when the port
is requested, the `ss-local` will be launched.
  • Loading branch information
liudongmiao authored and madeye committed Dec 22, 2016
1 parent 134a35f commit d2b7e67
Showing 1 changed file with 56 additions and 1 deletion.
57 changes: 56 additions & 1 deletion src/local.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@
#include "tls.h"
#include "local.h"

#ifndef LIB_ONLY
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#if defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
#include <launch.h>
#define HAVE_LAUNCHD
#endif
#endif
#endif

#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
#endif
Expand Down Expand Up @@ -112,6 +122,9 @@ static void accept_cb(EV_P_ ev_io *w, int revents);
static void signal_cb(EV_P_ ev_signal *w, int revents);

static int create_and_bind(const char *addr, const char *port);
#ifdef HAVE_LAUNCHD
static int launch_or_create(const char *addr, const char *port);
#endif
static remote_t *create_remote(listen_ctx_t *listener, struct sockaddr *addr);
static void free_remote(remote_t *remote);
static void close_and_free_remote(EV_P_ remote_t *remote);
Expand Down Expand Up @@ -190,6 +203,36 @@ create_and_bind(const char *addr, const char *port)
return listen_sock;
}

#ifdef HAVE_LAUNCHD
int
launch_or_create(const char *addr, const char *port)
{
int *fds;
size_t cnt;
int error = launch_activate_socket("Listeners", &fds, &cnt);
if (error == 0) {
if (cnt == 1) {
return fds[0];
} else {
FATAL("please don't specify multi entry");
}
} else if (error == ESRCH || error == ENOENT) {
/* ESRCH: The calling process is not managed by launchd(8).
* ENOENT: The socket name specified does not exist
* in the caller's launchd.plist(5).
*/
if (port == NULL) {
usage();
exit(EXIT_FAILURE);
}
return create_and_bind(addr, port);
} else {
FATAL("launch_activate_socket() error");
}
return -1;
}
#endif

static void
free_connections(struct ev_loop *loop)
{
Expand Down Expand Up @@ -1316,7 +1359,10 @@ main(int argc, char **argv)
}

if (remote_num == 0 || remote_port == NULL ||
local_port == NULL || password == NULL) {
#ifndef HAVE_LAUNCHD
local_port == NULL ||
#endif
password == NULL) {
usage();
exit(EXIT_FAILURE);
}
Expand Down Expand Up @@ -1415,7 +1461,11 @@ main(int argc, char **argv)
if (mode != UDP_ONLY) {
// Setup socket
int listenfd;
#ifdef HAVE_LAUNCHD
listenfd = launch_or_create(local_addr, local_port);
#else
listenfd = create_and_bind(local_addr, local_port);
#endif
if (listenfd == -1) {
FATAL("bind() error");
}
Expand All @@ -1437,6 +1487,11 @@ main(int argc, char **argv)
get_sockaddr_len(listen_ctx.remote_addr[0]), mtu, m, auth, listen_ctx.timeout, iface);
}

#ifdef HAVE_LAUNCHD
if (local_port == NULL)
LOGI("listening through launchd");
else
#endif
if (strcmp(local_addr, ":") > 0)
LOGI("listening at [%s]:%s", local_addr, local_port);
else
Expand Down

0 comments on commit d2b7e67

Please sign in to comment.