support for PF_LOCAL, it works, but needs some more testing/cleanup old code
authorTero Marttila <terom@fixme.fi>
Sun, 06 Jul 2008 23:33:24 +0300
changeset 26 6d615203d963
parent 25 a1e271de54c2
child 27 1e79b4cc8f1b
support for PF_LOCAL, it works, but needs some more testing/cleanup old code

committer: Tero Marttila <terom@fixme.fi>
Makefile
common.c
common.h
config.c
config.h
node_main.c
remote_node.c
remote_node.h
remote_pool.c
remote_pool.h
render_net.h
render_remote.c
socket.c
socket.h
web_main.c
--- a/Makefile	Thu Jun 26 03:31:59 2008 +0300
+++ b/Makefile	Sun Jul 06 23:33:24 2008 +0300
@@ -7,10 +7,13 @@
 
 common.o: common.c common.h
 http.o: http.c http.h common.h
-remote_node.o: remote_node.c remote_node.h common.h
+config.o: config.c config.h
+socket.o: socket.c socket.h config.h common.h
+
+remote_node.o: remote_node.c remote_node.h common.h render_net.h socket.h config.h
 remote_pool.o: remote_pool.c remote_pool.h common.h
 render.o: render.c render.h common.h
-render_remote.o: render_remote.c render_remote.h common.h
+render_remote.o: render_remote.c render_remote.h common.h socket.h remote_node.h remote_pool.h
 render_png.o: render_png.c render_png.h common.h
 render_raw.o: render_raw.c render_raw.h common.h
 render_local.o: render_local.c render_local.h common.h render_png.h render_raw.h render_mandelbrot.h
@@ -21,12 +24,12 @@
 render_thread.o: render_thread.c render_thread.h common.h render_local.h
 
 file_main.o: file_main.c
-node_main.o: node_main.c render_net.h
-web_main.o: web_main.c
+node_main.o: node_main.c common.h render.h render_struct.h render_thread.h render_thread_struct.h render_net.h socket.h
+web_main.o: web_main.c common.h render.h render_struct.h remote_node.h remote_pool.h render_remote.h config.h
 
 file_main: file_main.o common.o render.o render_raw.o render_png.o render_local.o render_mandelbrot.o
-node_main: node_main.o common.o render.o render_thread.o render_local.o render_png.o render_raw.o render_mandelbrot.o
-web_main: web_main.o common.o http.o render.o render_png.o remote_node.o remote_pool.o render_remote.o render_multi.o render_slices.o
+node_main: node_main.o common.o config.o socket.o render.o render_thread.o render_local.o render_png.o render_raw.o render_mandelbrot.o
+web_main: web_main.o common.o config.o socket.o http.o render.o render_png.o remote_node.o remote_pool.o render_remote.o render_multi.o render_slices.o
 
 clean :
 	rm *.o ${EXECS}
--- a/common.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/common.c	Sun Jul 06 23:33:24 2008 +0300
@@ -1,9 +1,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 #include <errno.h>
-#include <string.h>
-#include <ctype.h>
 
 #include "common.h"
 
@@ -39,97 +38,3 @@
     exit(EXIT_FAILURE);
 }
 
-int parse_hostport (char *hostport, char **host, char **port) {
-    char *c;
-
-    char brace = 0, colon = 0;
-
-    *host = *port = NULL;
-
-    for (c = hostport; *c != '\0'; c++) {
-        if (isspace(*c))
-            continue;
-
-        switch (*c) {
-            case '[':
-                if (c > hostport) {
-                    error("parse_hostport: %c must be first char, if any: %s", *c, hostport);
-                    return -1;
-                }
-                
-                brace = *c;
-                break;
-
-            case ']':
-                if (!brace) {
-                    error("parse_hostport: %c without matching brace: %s", *c, hostport);
-                    return -1;
-                }
-
-                if (!*host) {
-                    error("parse_hostport: empty hostname: %s", hostport);
-                    return -1;
-                }
-
-                brace = 0;
-                break;
-            
-            case ':':
-                if (brace) {
-                    // colons inside braces are left as-is
-                    continue;
-                }
-
-                if (!*host) {
-                    error("parse_hostport: colon before hostname: %s", hostport);
-                    return -1;
-                }
-
-                if (*port) {
-                    error("parse_hostport: colon after port: %s", hostport);
-                    return -1;
-                }
-
-                if (colon) {
-                    error("parse_hostport: too many colons: %s", hostport);
-                    return -1;
-                };
-
-                // finished parsing the host part, move on to the port part
-                colon = ':';
-                *c = '\0';
-                break;
-
-            default:
-                if (!*host) {
-                    // first char of the host
-                    *host = c;
-
-                } else if (colon && !*port) {
-                    // first char of the port
-                    *port = c;
-
-                } // else a part of either, don't care about it
-
-                break;
-        }
-    }
-
-    if (!*host) {
-        error("parse_hostport: missing hostname: %s", hostport);
-        return -1;
-    }
-
-    if (brace) {
-        error("parse_hostport: missing close-brace for %c: %s", brace, hostport);
-        return -1;
-    }
-
-    if (colon && !*port) {
-        error("parse_hostport: missing port: %s", hostport);
-        return -1;
-    }
-
-    return 0;
-}
-
--- a/common.h	Thu Jun 26 03:31:59 2008 +0300
+++ b/common.h	Sun Jul 06 23:33:24 2008 +0300
@@ -62,21 +62,3 @@
 #define INFO(...) (void) (0)
 #endif
 
-/*
- * Parse a host:port string.
- *
- * Valid formats:
- *  host
- *  host:port
- *  [host]
- *  [host]:port
- *
- * The contents of the given hostport string *will* be modified.
- *
- * The value of *port will be set to NULL if no port was given.
- *
- * Returns 0 and sets *host if succesfull, nonzero otherwise.
- *
- */
-int parse_hostport (char *hostport, char **host, char **port);
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.c	Sun Jul 06 23:33:24 2008 +0300
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "common.h"
+
+void endpoint_init (struct config_endpoint *endpoint, unsigned short default_port) {
+    // snprintf the default port as a string
+    snprintf(endpoint->_data.default_port, PORT_TEXT_LEN, "%hu", default_port);
+
+    // set up defaults
+    endpoint->family = PF_INET;
+    endpoint->af.inet.addr = NULL;
+    endpoint->af.inet.port = endpoint->_data.default_port;
+}
+
+/*
+ * Parse an endpoint address in human-readable format into the given endpoint. You can then use the socket_*
+ * functions to work with endpoints and sockets.
+ *
+ * Formats supported:
+ *  =foo.sock, =/quux/bar.sock
+ *  hostname, 12.34.56.78, [::1]
+ *  hostname:port, 12.34.56.78:port, [::1]:port
+ */
+int endpoint_parse (struct config_endpoint *endpoint, const char *addr_spec) {
+    // for empty addr specs, just return
+    if (!addr_spec || *addr_spec == '\0')
+        return 1;
+
+    if (sscanf(addr_spec, "=%" PATH_TEXT_LEN_STR "s", endpoint->_data.path) == 1) {
+        // local unix socket
+        endpoint->family = PF_LOCAL;
+        endpoint->af.local.path =  endpoint->_data.path;
+
+    } else if (
+            sscanf(addr_spec, "[%" ADDR_TEXT_LEN_STR "[^]] ]:%" PORT_TEXT_LEN_STR "s", endpoint->_data.addr, endpoint->_data.port) == 2
+        ||  sscanf(addr_spec, "%" ADDR_TEXT_LEN_STR "[^:] :%" PORT_TEXT_LEN_STR "s", endpoint->_data.addr, endpoint->_data.port) == 2
+    ) {
+        endpoint->family = PF_INET;
+        endpoint->af.inet.addr = endpoint->_data.addr;
+        endpoint->af.inet.port = endpoint->_data.port;
+
+    } else if (
+            sscanf(addr_spec, "[%" ADDR_TEXT_LEN_STR "[^]] ]", endpoint->_data.addr) == 1
+        ||  sscanf(addr_spec, "%" ADDR_TEXT_LEN_STR "[^:]", endpoint->_data.addr) == 1
+    ) {
+        endpoint->family = PF_INET;
+        endpoint->af.inet.addr = endpoint->_data.addr;
+
+    } else {
+        // unknown foramt
+        ERROR("unkown address format: %s", addr_spec);
+    }
+
+    return 0;
+
+error:
+    return -1;
+}
+
+int parse_hostport (char *hostport, char **host, char **port) {
+    char *c;
+
+    int brace = 0, colon = 0;
+    *host = *port = NULL;
+
+    for (c = hostport; *c != '\0'; c++) {
+        if (isspace(*c))
+            continue;
+
+        switch (*c) {
+            case '[':
+                if (c > hostport) {
+                    error("parse_hostport: %c must be first char, if any: %s", *c, hostport);
+                    return -1;
+                }
+                
+                brace = *c;
+                break;
+
+            case ']':
+                if (!brace) {
+                    error("parse_hostport: %c without matching brace: %s", *c, hostport);
+                    return -1;
+                }
+
+                if (!*host) {
+                    error("parse_hostport: empty hostname: %s", hostport);
+                    return -1;
+                }
+
+                brace = 0;
+                break;
+            
+            case ':':
+                if (brace) {
+                    // colons inside braces are left as-is
+                    continue;
+                }
+
+                if (!*host) {
+                    error("parse_hostport: colon before hostname: %s", hostport);
+                    return -1;
+                }
+
+                if (*port) {
+                    error("parse_hostport: colon after port: %s", hostport);
+                    return -1;
+                }
+
+                if (colon) {
+                    error("parse_hostport: too many colons: %s", hostport);
+                    return -1;
+                };
+
+                // finished parsing the host part, move on to the port part
+                colon = ':';
+                *c = '\0';
+                break;
+
+            default:
+                if (!*host) {
+                    // first char of the host
+                    *host = c;
+
+                } else if (colon && !*port) {
+                    // first char of the port
+                    *port = c;
+
+                } // else a part of either, don't care about it
+
+                break;
+        }
+    }
+
+    if (!*host) {
+        error("parse_hostport: missing hostname: %s", hostport);
+        return -1;
+    }
+
+    if (brace) {
+        error("parse_hostport: missing close-brace for %c: %s", brace, hostport);
+        return -1;
+    }
+
+    if (colon && !*port) {
+        error("parse_hostport: missing port: %s", hostport);
+        return -1;
+    }
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.h	Sun Jul 06 23:33:24 2008 +0300
@@ -0,0 +1,64 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define ADDR_TEXT_LEN 128    
+#define ADDR_TEXT_LEN_STR "128"
+#define PORT_TEXT_LEN 32
+#define PORT_TEXT_LEN_STR "32"
+#define PATH_TEXT_LEN 108   /* from sys/un.h */
+#define PATH_TEXT_LEN_STR "108" /* from sys/un.h */
+
+struct config_endpoint {
+    // one of AF_LOCAL, AF_INET, AF_INET6
+    int family;
+
+    // buffers to store the extracted textual addres/port into
+    // these /should/ be long enough
+    union {
+        // PF_INET - may be a numeric IPv4 address, numeric IPv6 address, or hostname
+        struct {
+            const char *addr;
+            const char *port;
+        } inet;
+        
+        // PF_LOCAL
+        struct {
+            const char *path;
+        } local;
+
+    } af;
+
+    struct {
+        char addr[ADDR_TEXT_LEN];
+        char port[PORT_TEXT_LEN];
+        char default_port[PORT_TEXT_LEN];
+        char path[PATH_TEXT_LEN];
+    } _data;
+};
+
+void endpoint_init (struct config_endpoint *endpoint, unsigned short default_port);
+int endpoint_parse (struct config_endpoint *endpoint, const char *addr_spec);
+
+/*
+ * Parse a host:port string.
+ *
+ * Valid formats:
+ *  host
+ *  host:port
+ *  [host]
+ *  [host]:port
+ *
+ * The contents of the given hostport string *will* be modified.
+ *
+ * The value of *port will be set to NULL if no port was given.
+ *
+ * Returns 0 and sets *host if succesfull, nonzero otherwise.
+ *
+ */
+int parse_hostport (char *hostport, char **host, char **port);
+
+#endif /* CONFIG_H */
+
--- a/node_main.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/node_main.c	Sun Jul 06 23:33:24 2008 +0300
@@ -1,7 +1,6 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <arpa/inet.h>
-#include <sys/socket.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <string.h>
@@ -15,6 +14,7 @@
 #include <event2/bufferevent.h>
 
 #include "common.h"
+#include "socket.h"
 #include "render.h"
 #include "render_struct.h"
 #include "render_net.h"
@@ -242,22 +242,21 @@
 
 int main (int argc, char** argv) {
     struct event_base *ev_base;
+    struct config_endpoint endpoint;
     int ssock;
-    struct sockaddr_in addr;
-    
+
     // parse arguments
     int opt;
-    const char *port_name = NULL;
+    const char *listen_spec = NULL;
     int enable_debug = 0;
-    unsigned short port;
 
     while ((opt = getopt(argc, argv, "l:")) != -1) {
         switch (opt) {
             case 'l':
-                if (port_name)
+                if (listen_spec)
                     ERROR("only specify -l once");
 
-                port_name = optarg;
+                 listen_spec = optarg;
                 break;
 
             case 'd':
@@ -266,7 +265,7 @@
                 break;
 
             default:
-                err_exit("Usage: %s [-l port] [-d]", argv[0]);
+                err_exit("Usage: %s [-l addr_spec] [-d]", argv[0]);
         }
     }
     
@@ -274,31 +273,19 @@
     if (!(ev_base = event_init()))
         FATAL("event_init");
 
-    // post-process arguments
-    if (!port_name)
-        port_name = RENDER_PORT_NAME;
-
-    if (!(port = atoi(port_name)))
-        ERROR("invalid port: %s", port_name);
-    
     // per default it is enabled
     if (!enable_debug)
         event_set_log_callback(&log_null);
 
     // create the socket
-    if ((ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
-        PERROR("socket");
+    endpoint_init(&endpoint, RENDER_PORT);
 
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = INADDR_ANY;
+    if (endpoint_parse(&endpoint, listen_spec))
+        goto error;
 
-    if (bind(ssock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1)
-        PERROR("bind");
-    
-    if (listen(ssock, 1) == -1)
-        PERROR("listen");
-    
+    if ((ssock = socket_listen(&endpoint, SOCK_STREAM)) == -1)
+        goto error;
+
     // create the listen event
     struct event listen_ev;
 
@@ -311,7 +298,7 @@
     sigpipe_ignore();
     
     // run the libevent mainloop
-    INFO("run: %s:%hu", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+    INFO("run");
  
     if (event_base_dispatch(ev_base))
         WARNING("event_dispatch");
--- a/remote_node.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/remote_node.c	Sun Jul 06 23:33:24 2008 +0300
@@ -4,45 +4,23 @@
 #include <netdb.h>
 #include <assert.h>
 
+#include "remote_node.h"
 #include "common.h"
 #include "render_net.h"
-#include "remote_node.h"
+#include "socket.h"
 
-int remote_node_init (struct remote_node *node_info, const char *hostname, const char *portname) {
+int remote_node_init (struct remote_node *node_info, const char *addr_spec) {
     // zero out the struct
     memset(node_info, 0, sizeof(*node_info));
 
     // XXX: currently, this is hardcoded to one, but should be automagically discovered
     node_info->parallel_renders = 1;
 
-    // lookup the host:port
-    portname = portname ? portname : RENDER_PORT_NAME;
-    
-    // XXX: stat it and look for a PF_UNIX socket!
-    
-    // PF_INET
-    
-    struct addrinfo hints, *results, *result;
-
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_socktype = SOCK_STREAM;
-    hints.ai_flags = AI_ADDRCONFIG;     // attempt to get a working address as the first result
+    // build the endpoint
+    endpoint_init(&node_info->endpoint, RENDER_PORT);
 
-    int err = getaddrinfo(hostname, portname, &hints, &results);
-    
-    if (err != 0) {
-        error("getaddrinfo: [%s].%s: %s", hostname, portname, gai_strerror(err));
-
+    if (endpoint_parse(&node_info->endpoint, addr_spec))
         return -1;
-    }
-    
-    assert(results != NULL); // we expect at least one result
-
-    // XXX: only use the first result, discard the rest.
-    result = results;
-    memcpy(&node_info->addr, result->ai_addr, result->ai_addrlen);
-
-    freeaddrinfo(results);
     
     // success!
     node_info->valid = 1;
--- a/remote_node.h	Thu Jun 26 03:31:59 2008 +0300
+++ b/remote_node.h	Sun Jul 06 23:33:24 2008 +0300
@@ -3,6 +3,8 @@
 
 #include <sys/socket.h>
 
+#include "config.h"
+
 /*
  * Information about a single remote render node.
  */
@@ -13,9 +15,9 @@
     /*
      * Static information
      */
-
-    // the remote address, note how this can both AF_INET and AF_LOCAL
-    struct sockaddr_storage addr;
+    
+    // the remote endpoint
+    struct config_endpoint endpoint;
 
     // how many render requests this node can process concurrently
     int parallel_renders;
@@ -29,14 +31,12 @@
 };
 
 /*
- * Initialize the given remote_node struct to work with the render node at the given host/port.
- *
- * Portname may also be NULL, in which case the default port is used.
+ * Initialize the given remote_node struct to work with the render node at the given address (à la parse_address).
  *
  * Note that this is a blocking function, and should only be called at startup.
  *
  * returns nonzero on error, zero on success.
  */
-int remote_node_init (struct remote_node *node_info, const char *hostname, const char *portname);
+int remote_node_init (struct remote_node *node_info, const char *addr_spec);
 
 #endif /* REMOTE_NODE_H */
--- a/remote_pool.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/remote_pool.c	Sun Jul 06 23:33:24 2008 +0300
@@ -9,7 +9,7 @@
     memset(pool_info, 0, sizeof(*pool_info));
 }
 
-int remote_pool_add (struct remote_pool *pool_info, const char *hostname, const char *portname) {
+int remote_pool_add (struct remote_pool *pool_info, const char *addr_spec) {
     int i;
     struct remote_node *node_info = NULL;
     
@@ -21,12 +21,13 @@
         }
     }
 
-    if (!node_info) {
-        error("remote_pool_add: pool full, consider increasing REMOTE_POOL_MAX");
-        return -1;
-    }
+    if (!node_info)
+        ERROR("remote_pool_add: pool full, consider increasing REMOTE_POOL_MAX");
 
-    return remote_node_init(node_info, hostname, portname);
+    return remote_node_init(node_info, addr_spec);
+
+error:
+    return -1;
 }
 
 int remote_pool_load (struct remote_pool *pool_info, const char *filename) {
@@ -39,7 +40,7 @@
     }
     
     // read it in line-by-line
-    char line_buf[POOL_FILE_LINE_LENGTH], *hostname, *portname, *c;
+    char line_buf[POOL_FILE_LINE_LENGTH], *c;
     
     // non-ferror error indicator
     int error_flag = 0;
@@ -64,15 +65,9 @@
 
         if (*c == '\0')
             continue;
-        
-        // parse
-        if (!parse_hostport(line_buf, &hostname, &portname)) {
-            error_flag = 1;
-            break;
-        }
 
         // add it to the pool
-        if (remote_pool_add(pool_info, hostname, portname)) {
+        if (remote_pool_add(pool_info, line_buf)) {
             error_flag = 1;
             break;
         }
--- a/remote_pool.h	Thu Jun 26 03:31:59 2008 +0300
+++ b/remote_pool.h	Sun Jul 06 23:33:24 2008 +0300
@@ -22,7 +22,7 @@
 /*
  * Add a remote_node to the pool, see remote_node_init
  */
-int remote_pool_add (struct remote_pool *pool_info, const char *hostname, const char *portname);
+int remote_pool_add (struct remote_pool *pool_info, const char *addr_spec);
 
 /*
  * Adds remote_nodes to the pool based on the contents of the given file
--- a/render_net.h	Thu Jun 26 03:31:59 2008 +0300
+++ b/render_net.h	Sun Jul 06 23:33:24 2008 +0300
@@ -1,5 +1,6 @@
 #include <sys/types.h>
 
+#define RENDER_PORT 6159
 #define RENDER_PORT_NAME "6159"
 
 #pragma pack(push)
--- a/render_remote.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/render_remote.c	Sun Jul 06 23:33:24 2008 +0300
@@ -5,14 +5,17 @@
 #include <errno.h>
 #include <assert.h>
 
+#include <sys/un.h> /* XXX: for SUN_LEN */
+
 #include <event2/event.h>
 #include <event2/event_struct.h>
 #include <event2/bufferevent.h>
 
-#include "render_struct.h"
 #include "render_remote.h"
+#include "common.h"
+#include "render_struct.h"
 #include "render_net.h"
-#include "common.h"
+#include "socket.h"
 
 struct render_remote {
     // the socket fd
@@ -201,18 +204,8 @@
     ctx->render_cmd.y2 = render->y2;
 
     // create the socket
-    if ((ctx->sock = socket(node_info->addr.ss_family, SOCK_STREAM, 0)) < 0)
-        PERROR("socket");
-
-    // mark it as nonblocking
-    if (fcntl(ctx->sock, F_SETFL, O_NONBLOCK) == -1)
-        PERROR("fcntl");
-    
-    // initiate the connect
-    int err = connect(ctx->sock, (struct sockaddr *) &node_info->addr, sizeof(node_info->addr));
-
-    if (err != -1 || errno != EINPROGRESS)
-        PERROR("connect");
+    if ((ctx->sock = socket_connect_async(&node_info->endpoint, SOCK_STREAM)) == -1)
+        goto error;
 
     // return the raw ctx
     return ctx;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/socket.c	Sun Jul 06 23:33:24 2008 +0300
@@ -0,0 +1,184 @@
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+
+#include "socket.h"
+#include "common.h"
+
+#define _SOCKOP_SOCKET 0x01
+#define _SOCKOP_ENDPOINT_BIND 0x02
+#define _SOCKOP_ENDPOINT_CONNECT 0x04
+#define _SOCKOP_LISTEN 0x08
+#define _SOCKOP_FLAGS_PASSIVE 0x10
+#define _SOCKOP_FCNTL_NONBLOCK 0x20
+
+enum socket_op {
+    /*
+     * Just resolve the address, don't create a socket
+     */
+    SOCKOP_RESOLVE = _SOCKOP_FLAGS_PASSIVE,
+
+    /*
+     * Create a socket whose local end is bound to the given endpoint, and listening
+     */
+    SOCKOP_LISTEN = _SOCKOP_FLAGS_PASSIVE | _SOCKOP_SOCKET | _SOCKOP_ENDPOINT_BIND | _SOCKOP_LISTEN,
+
+    /*
+     * Create a socket whose remote end is connected to the given endpoint, and connected
+     */
+    SOCKOP_CONNECT = _SOCKOP_SOCKET | _SOCKOP_ENDPOINT_CONNECT,
+
+    /*
+     * Create a socket and initiate a nonblocking connect to the given endpoint
+     */
+    SOCKOP_CONNECT_ASYNC = _SOCKOP_SOCKET | _SOCKOP_FCNTL_NONBLOCK | _SOCKOP_ENDPOINT_CONNECT,
+};
+
+static int _socket_do (struct config_endpoint *endpoint, int *sock, int sock_type, struct sockaddr_storage *addr, enum socket_op sockop);
+
+/*
+ * Create a socket of the given type that's listening on the given endpoint
+ */
+int socket_listen (struct config_endpoint *endpoint, int sock_type) {
+    int sock = -1;
+    struct sockaddr_storage addr;
+    
+    // and then _socket_do
+    if (_socket_do(endpoint, &sock, sock_type, &addr, SOCKOP_LISTEN))
+        return -1;
+    
+    // just return the socket, discard addr
+    return sock;
+}
+
+/*
+ * Initiate an async connect to the given endpoint for the given socket type. This should work for
+ * PF_INET and PF_LOCAL sockets, and in both cases, socket writeability should indicate that the
+ * connect succeeded.
+ *
+ * XXX: Currently it looks up the endpoint each time - the working addrinfo should be cached
+ */
+int socket_connect_async (struct config_endpoint *endpoint, int sock_type) {
+    int sock = -1;
+    struct sockaddr_storage addr;
+    
+    // and then _socket_do
+    if (_socket_do(endpoint, &sock, sock_type, &addr, SOCKOP_CONNECT_ASYNC))
+        return -1;
+    
+    // return the socket
+    return sock;
+}
+
+
+/*
+ * Do something to apply an endpoint to a socket
+ */
+static int _socket_do (struct config_endpoint *endpoint, int *sock, int sock_type, struct sockaddr_storage *addr, enum socket_op sockop) {
+    struct addrinfo *res = NULL, *info, _fake_res;
+    struct sockaddr_un _fake_addr_un;
+
+    if (endpoint->family == PF_UNIX) {
+        // getaddrinfo doesn't handle PF_UNIX, so we need to build a fake result
+        
+        // build the sockaddr_un
+        _fake_addr_un.sun_family = endpoint->family;
+        memcpy(_fake_addr_un.sun_path, endpoint->af.local.path, sizeof(_fake_addr_un.sun_path));
+        
+        // build the fake addrinfo res
+        _fake_res.ai_flags = 0;
+        _fake_res.ai_family = PF_UNIX;
+        _fake_res.ai_socktype = sock_type;
+        _fake_res.ai_protocol = 0;
+        _fake_res.ai_addrlen = SUN_LEN(&_fake_addr_un);
+        _fake_res.ai_addr = (struct sockaddr *) &_fake_addr_un;
+        _fake_res.ai_canonname = (char *) endpoint->af.local.path;  /* XXX: not const */
+        _fake_res.ai_next = NULL;
+
+        res = &_fake_res;
+
+    } else if (endpoint->family == PF_INET) {
+        // use the real getaddrinfo
+
+        struct addrinfo hints;
+        memset(&hints, 0, sizeof(hints));
+        
+        hints.ai_socktype = sock_type;
+        hints.ai_protocol = 0;
+        hints.ai_flags = (sockop & _SOCKOP_FLAGS_PASSIVE ? AI_PASSIVE : 0) | AI_CANONNAME;
+
+        int err;
+
+        // check that we have a service, doesn't make any sense without
+        if (!endpoint->af.inet.port)
+            ERROR("no service specified");
+
+        if ((err = getaddrinfo(endpoint->af.inet.addr, endpoint->af.inet.port, &hints, &res)) != 0)
+            ERROR("getaddrinfo: %s#%s: %s", endpoint->af.inet.addr, endpoint->af.inet.port, gai_strerror(err));
+
+    } else {
+        ERROR("invalid endpoint");
+    }
+
+    for (info = res; info; info = info->ai_next) {
+        if (sockop & _SOCKOP_SOCKET && (*sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) == -1) {
+            PWARNING("socket(%d, %s)", info->ai_family, info->ai_canonname);
+            continue;
+        }
+
+        if (sockop & _SOCKOP_FCNTL_NONBLOCK && fcntl(*sock, F_SETFL, O_NONBLOCK) == -1) {
+            PWARNING("fcntl(%d, %s, F_SETFL O_NONBLOCK)", info->ai_family, info->ai_canonname);
+            close(*sock); *sock = -1;
+            continue;
+        }
+        
+        if (sockop & _SOCKOP_ENDPOINT_BIND && bind(*sock, info->ai_addr, info->ai_addrlen) == -1) {
+            PWARNING("bind(%d, %s)", info->ai_family, info->ai_canonname);
+            close(*sock); *sock = -1;
+            continue;
+        }
+
+        if (sockop & _SOCKOP_LISTEN && listen(*sock, SOCKET_LISTEN_BACKLOG) == -1) {
+            PWARNING("listen(%d, %s)", info->ai_family, info->ai_canonname);
+            close(*sock); *sock = -1;
+            continue;
+        }
+
+        if (sockop & _SOCKOP_ENDPOINT_CONNECT && connect(*sock, info->ai_addr, info->ai_addrlen) == -1) {
+            if (sockop & _SOCKOP_FCNTL_NONBLOCK && errno == EINPROGRESS) {
+                /* to be expected */
+
+            } else {
+                PWARNING("listen(%d, %s)", info->ai_family, info->ai_canonname);
+                close(*sock); *sock = -1;
+                continue;
+            }
+        }
+
+        // copy the succesfull address over
+        memcpy(addr, info->ai_addr, info->ai_addrlen);
+        
+        // exit the loop with a valid socket
+        assert(*sock != -1);
+        break;
+    }
+
+    if (*sock == -1)
+        ERROR("no working results from getaddrinfo: %s#%s", endpoint->af.inet.addr, endpoint->af.inet.port);
+
+    return 0;
+
+error:
+    if (res != 0 && res != &_fake_res)
+        freeaddrinfo(res);
+
+    return -1;
+}   
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/socket.h	Sun Jul 06 23:33:24 2008 +0300
@@ -0,0 +1,11 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include "config.h"
+
+#define SOCKET_LISTEN_BACKLOG 2
+
+int socket_listen (struct config_endpoint *endpoint, int sock_type);
+int socket_connect_async (struct config_endpoint *endpoint, int sock_type);
+
+#endif /* SOCKET_H */
--- a/web_main.c	Thu Jun 26 03:31:59 2008 +0300
+++ b/web_main.c	Sun Jul 06 23:33:24 2008 +0300
@@ -21,10 +21,8 @@
 #include "render.h"
 #include "remote_node.h"
 #include "remote_pool.h"
-
-// XXX: replace with a common one
 #include "render_remote.h"
-#include "render_multi.h"
+#include "config.h"
 
 #define MIN_CHUNK_SIZE 4096
 #define OVERFLOW_BUFFER 4096
@@ -276,7 +274,6 @@
     // process arguments
     int opt;
     int enable_debug = 0;
-    char *host, *port;
 
     while ((opt = getopt(argc, argv, "dp:r:")) != -1) {
         switch (opt) {
@@ -289,10 +286,7 @@
 
             case 'r':
                 // add the given render node to the pool
-                if (
-                        parse_hostport(optarg, &host, &port)
-                     || remote_pool_add(&remote_pool, host, port)
-                )
+                if (remote_pool_add(&remote_pool, optarg))
                     return 1;
 
                 break;