# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: rousskov@measurement-factory.com-20100701225845-\
#   aag59oouzt15rooa
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk
# testament_sha1: ed276a8bb90cc89b3eefb9cb9203b2291eea5e5a
# timestamp: 2010-07-01 17:01:42 -0600
# base_revision_id: squid3@treenet.co.nz-20100303093849-\
#   ndhnc129fhg9xuie
# 
# Begin patch
=== modified file 'configure.in'
--- configure.in	2010-02-03 12:36:21 +0000
+++ configure.in	2010-03-30 21:54:40 +0000
@@ -4,7 +4,7 @@
 dnl
 dnl
 dnl
-AC_INIT([Squid Web Proxy],[3.HEAD-BZR],[http://www.squid-cache.org/bugs/],[squid])
+AC_INIT([Squid Web Proxy],[3.HEAD-SMP-BZR],[http://www.squid-cache.org/bugs/],[squid])
 AC_PREREQ(2.61)
 AC_CONFIG_HEADERS([include/autoconf.h])
 AC_CONFIG_AUX_DIR(cfgaux)
@@ -4233,6 +4233,7 @@
 	src/ident/Makefile \
 	src/ip/Makefile \
 	src/log/Makefile \
+	src/ipc/Makefile \
 	contrib/Makefile \
 	snmplib/Makefile \
 	icons/Makefile \

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-01-12 21:16:29 +0000
+++ src/Makefile.am	2010-03-30 21:54:40 +0000
@@ -34,7 +34,7 @@
 	LoadableModules.h \
 	LoadableModules.cc
 
-SUBDIRS	= base comm eui acl fs repl auth ip icmp ident log
+SUBDIRS	= base comm eui acl fs repl auth ip icmp ident log ipc
 
 if USE_ADAPTATION
 SUBDIRS += adaptation
@@ -164,7 +164,8 @@
 	auth/libauth.la \
 	acl/libapi.la \
 	ip/libip.la \
-	fs/libfs.la
+	fs/libfs.la \
+	ipc/libipc.la
 
 EXTRA_PROGRAMS = \
 	DiskIO/DiskDaemon/diskd \

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2010-02-06 00:13:14 +0000
+++ src/cache_cf.cc	2010-03-30 21:54:40 +0000
@@ -69,6 +69,7 @@
 #include "StoreFileSystem.h"
 #include "SwapDir.h"
 #include "wordlist.h"
+#include "ipc/Kids.h"
 
 #if HAVE_GLOB_H
 #include <glob.h>
@@ -258,6 +259,110 @@
     return error_count;
 }
 
+static void
+ReplaceSubstr(char*& str, int& len, unsigned substrIdx, unsigned substrLen, const char* newSubstr)
+{
+    assert(str != NULL);
+    assert(newSubstr != NULL);
+
+    unsigned newSubstrLen = strlen(newSubstr);
+    if (newSubstrLen > substrLen)
+        str = (char*)realloc(str, len - substrLen + newSubstrLen + 1);
+
+    // move tail part including zero
+    memmove(str + substrIdx + newSubstrLen, str + substrIdx + substrLen, len - substrIdx - substrLen + 1);
+    // copy new substring in place
+    memcpy(str + substrIdx, newSubstr, newSubstrLen);
+
+    len = strlen(str);
+}
+
+static void
+SubstituteMacro(char*& line, int& len, const char* macroName, const char* substStr)
+{
+    assert(line != NULL);
+    assert(macroName != NULL);
+    assert(substStr != NULL);
+    unsigned macroNameLen = strlen(macroName);
+    while (const char* macroPos = strstr(line, macroName)) // we would replace all occurrences
+        ReplaceSubstr(line, len, macroPos - line, macroNameLen, substStr);
+}
+
+static void
+ProcessMacros(char*& line, int& len)
+{
+    SubstituteMacro(line, len, "${process_name}", KidName);
+    SubstituteMacro(line, len, "${process_number}", xitoa(KidIdentifier));
+}
+
+static void
+trim_trailing_ws(char* str)
+{
+    assert(str != NULL);
+    unsigned i = strlen(str);
+    while ((i > 0) && xisspace(str[i - 1]))
+        --i;
+    str[i] = '\0';
+}
+
+static const char*
+FindStatement(const char* line, const char* statement)
+{
+    assert(line != NULL);
+    assert(statement != NULL);
+
+    const char* str = skip_ws(line);
+    unsigned len = strlen(statement);
+    if (strncmp(str, statement, len) == 0) {
+        str += len;
+        if (*str == '\0')
+            return str;
+        else if (xisspace(*str))
+            return skip_ws(str);
+    }
+
+    return NULL;
+}
+
+static bool
+StrToInt(const char* str, long& number)
+{
+    assert(str != NULL);
+
+    char* end;
+    number = strtol(str, &end, 0);
+
+    return (end != str) && (*end == '\0'); // returns true if string contains nothing except number
+}
+
+static bool
+EvalBoolExpr(const char* expr)
+{
+    assert(expr != NULL);
+    if (strcmp(expr, "true") == 0) {
+        return true;
+    } else if (strcmp(expr, "false") == 0) {
+        return false;
+    } else if (const char* equation = strchr(expr, '=')) {
+        const char* rvalue = skip_ws(equation + 1);
+        char* lvalue = (char*)xmalloc(equation - expr + 1);
+        xstrncpy(lvalue, expr, equation - expr + 1);
+        trim_trailing_ws(lvalue);
+
+        long number1;
+        if (!StrToInt(lvalue, number1))
+            fatalf("String is not a integer number: '%s'\n", lvalue);
+        long number2;
+        if (!StrToInt(rvalue, number2))
+            fatalf("String is not a integer number: '%s'\n", rvalue);
+
+        xfree(lvalue);
+        return number1 == number2;
+    }
+    fatalf("Unable to evaluate expression '%s'\n", expr);
+    return false; // this place cannot be reached
+}
+
 static int
 parseOneConfigFile(const char *file_name, unsigned int depth)
 {
@@ -298,6 +403,7 @@
 
     config_lineno = 0;
 
+    Vector<bool> if_states;
     while (fgets(config_input_line, BUFSIZ, fp)) {
         config_lineno++;
 
@@ -357,20 +463,38 @@
             continue;
         }
 
+        trim_trailing_ws(tmp_line);
+        ProcessMacros(tmp_line, tmp_line_len);
         debugs(3, 5, "Processing: '" << tmp_line << "'");
 
-        /* Handle includes here */
-        if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
-            err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
-        } else if (!parse_line(tmp_line)) {
-            debugs(3, 0, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
-            err_count++;
+        if (const char* expr = FindStatement(tmp_line, "if")) {
+            if_states.push_back(EvalBoolExpr(expr)); // store last if-statement meaning
+        } else if (FindStatement(tmp_line, "endif")) {
+            if (!if_states.empty())
+                if_states.pop_back(); // remove last if-statement meaning
+            else
+                fatalf("'endif' without 'if'\n");
+        } else if (FindStatement(tmp_line, "else")) {
+            if (!if_states.empty())
+                if_states.back() = !if_states.back();
+            else
+                fatalf("'else' without 'if'\n");
+        } else if (if_states.empty() || if_states.back()) { // test last if-statement meaning if present
+            /* Handle includes here */
+            if (tmp_line_len >= 9 && strncmp(tmp_line, "include", 7) == 0 && xisspace(tmp_line[7])) {
+                err_count += parseManyConfigFiles(tmp_line + 8, depth + 1);
+            } else if (!parse_line(tmp_line)) {
+                debugs(3, 0, HERE << cfg_filename << ":" << config_lineno << " unrecognized: '" << tmp_line << "'");
+                err_count++;
+            }
         }
 
         safe_free(tmp_line);
         tmp_line_len = 0;
 
     }
+    if (!if_states.empty())
+        fatalf("if-statement without 'endif'\n");
 
     if (is_pipe) {
         int ret = pclose(fp);

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2010-03-03 09:38:49 +0000
+++ src/cf.data.pre	2010-03-26 13:52:36 +0000
@@ -57,6 +57,43 @@
   This arbitrary restriction is to prevent recursive include references
   from causing Squid entering an infinite loop whilst trying to load
   configuration files.
+
+
+  Conditional configuration
+
+	If-statements can be used to make configuration directives
+	depend on conditions:
+
+	    if <CONDITION>
+	        ... regular configuration directives ...
+	    [else
+	        ... regular configuration directives ...]
+	    endif
+
+	The else part is optional. The keywords "if", "else", and "endif"
+	must be typed on their own lines, as if they were regular
+	configuration directives.
+
+	These individual conditions types are supported:
+
+	    true
+		Always evaluates to true.
+	    false
+		Always evaluates to false.
+	    <integer> = <integer>
+	        Equality comparison of two integer numbers.
+
+
+  SMP-Related Macros
+
+	The following SMP-related preprocessor macros can be used.
+
+	${process_name} expands to the current Squid process "name"
+	(e.g., squid1, squid2, or cache1).
+
+	${process_number} expands to the current Squid process
+	identifier, which is an integer number (e.g., 1, 2, 3) unique
+	across all Squid processes.
 COMMENT_END
 
 COMMENT_START
@@ -6807,4 +6844,15 @@
 	Whether to lookup the EUI or MAC address of a connected client.
 DOC_END
 
+NAME: main_processes
+TYPE: int
+LOC: Config.main_processes
+DEFAULT: 1
+DOC_START
+	Number of main Squid processes to fork.
+	0: "no daemon" mode, like running "squid -N ..."
+	1: "no SMP" mode, start one main Squid process daemon (default)
+	N: start N main Squid process daemons
+DOC_END
+
 EOF

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2010-01-29 01:13:09 +0000
+++ src/client_side.cc	2010-07-01 21:20:38 +0000
@@ -102,6 +102,7 @@
 #include "ident/Config.h"
 #include "ident/Ident.h"
 #include "ip/IpIntercept.h"
+#include "ipc/StartListening.h"
 #include "MemBuf.h"
 #include "MemObject.h"
 #include "ProtoPort.h"
@@ -113,6 +114,33 @@
 #define comm_close comm_lingering_close
 #endif
 
+/// dials clientHttpConnectionOpened or clientHttpsConnectionOpened call
+class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
+{
+public:
+    typedef void (*Handler)(int fd, int errNo, http_port_list *portCfg);
+    ListeningStartedDialer(Handler aHandler, http_port_list *aPortCfg):
+        handler(aHandler), portCfg(aPortCfg) {}
+
+    virtual void print(std::ostream &os) const { startPrint(os) <<
+        ", port=" << (void*)portCfg << ')'; }
+
+    virtual bool canDial(AsyncCall &) const { return true; }
+    virtual void dial(AsyncCall &) { (handler)(fd, errNo, portCfg); }
+
+public:
+    Handler handler;
+
+private:
+    http_port_list *portCfg; ///< from Config.Sockaddr.http
+};
+
+
+static void clientHttpConnectionOpened(int fd, int errNo, http_port_list *s);
+#if USE_SSL
+static void clientHttpsConnectionOpened(int fd, int errNo, http_port_list *s);
+#endif
+
 /* our socket-related context */
 
 
@@ -3329,12 +3357,39 @@
 
 #endif /* USE_SSL */
 
+/// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
+static bool
+OpenedHttpSocket(int fd, const char *msgIfFail)
+{
+    if (fd < 0) {
+        Must(NHttpSockets > 0); // we tried to open some
+        --NHttpSockets; // there will be fewer sockets than planned
+        Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
+
+        if (!NHttpSockets) // we could not open any listen sockets at all
+            fatal(msgIfFail);
+
+        return false;
+    }
+    return true;
+}
+
+/// find any unused HttpSockets[] slot and store fd there or return false
+static bool
+AddOpenedHttpSocket(int fd)
+{
+    bool found = false;
+    for (int i = 0; i < NHttpSockets && !found; i++) {
+        if ((found = HttpSockets[i] < 0))
+            HttpSockets[i] = fd;
+    }
+    return found;
+}
 
 static void
 clientHttpConnectionsOpen(void)
 {
     http_port_list *s = NULL;
-    int fd = -1;
 #if USE_SSL
     int bumpCount = 0; // counts http_ports with sslBump option
 #endif
@@ -3358,18 +3413,34 @@
 
         /* AYJ: 2009-12-27: bit bumpy. new ListenStateData(...) should be doing all the Comm:: stuff ... */
 
-        enter_suid();
-
-        if (s->spoof_client_ip) {
-            fd = comm_open_listener(SOCK_STREAM, IPPROTO_TCP, s->s, (COMM_NONBLOCKING|COMM_TRANSPARENT), "HTTP Socket");
-        } else {
-            fd = comm_open_listener(SOCK_STREAM, IPPROTO_TCP, s->s, COMM_NONBLOCKING, "HTTP Socket");
-        }
-
-        leave_suid();
-
-        if (fd < 0)
-            continue;
+        const int openFlags = COMM_NONBLOCKING |
+            (s->spoof_client_ip ? COMM_TRANSPARENT : 0);
+
+        AsyncCall::Pointer callback = asyncCall(33,2,
+            "clientHttpConnectionOpened",
+            ListeningStartedDialer(&clientHttpConnectionOpened, s));
+        Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->s, openFlags,
+            Ipc::fdnHttpSocket, callback);
+
+        HttpSockets[NHttpSockets++] = -1; // set in clientHttpConnectionOpened
+    }
+
+#if USE_SSL
+    if (bumpCount && !Config.accessList.ssl_bump)
+        debugs(33, 1, "WARNING: http_port(s) with SslBump found, but no " <<
+               std::endl << "\tssl_bump ACL configured. No requests will be " <<
+               "bumped.");
+#endif
+}
+
+/// process clientHttpConnectionsOpen result
+static void
+clientHttpConnectionOpened(int fd, int, http_port_list *s)
+{
+    if (!OpenedHttpSocket(fd, "Cannot open HTTP Port"))
+        return;
+
+    Must(s);
 
         AsyncCall::Pointer call = commCbCall(5,5, "SomeCommAcceptHandler(httpAccept)",
                                              CommAcceptCbPtrFun(httpAccept, s));
@@ -3384,15 +3455,7 @@
                << " HTTP connections at " << s->s
                << ", FD " << fd << "." );
 
-        HttpSockets[NHttpSockets++] = fd;
-    }
-
-#if USE_SSL
-    if (bumpCount && !Config.accessList.ssl_bump)
-        debugs(33, 1, "WARNING: http_port(s) with SslBump found, but no " <<
-               std::endl << "\tssl_bump ACL configured. No requests will be " <<
-               "bumped.");
-#endif
+    Must(AddOpenedHttpSocket(fd)); // otherwise, we have received a fd we did not ask for
 }
 
 #if USE_SSL
@@ -3400,7 +3463,6 @@
 clientHttpsConnectionsOpen(void)
 {
     https_port_list *s;
-    int fd;
 
     for (s = Config.Sockaddr.https; s; s = (https_port_list *)s->http.next) {
         if (MAXHTTPPORTS == NHttpSockets) {
@@ -3415,25 +3477,33 @@
             continue;
         }
 
-        enter_suid();
-        fd = comm_open_listener(SOCK_STREAM,
-                                IPPROTO_TCP,
-                                s->http.s,
-                                COMM_NONBLOCKING, "HTTPS Socket");
-        leave_suid();
-
-        if (fd < 0)
-            continue;
+        AsyncCall::Pointer call = asyncCall(33, 2, "clientHttpsConnectionOpened",
+            ListeningStartedDialer(&clientHttpsConnectionOpened, &s->http));
+
+        Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->http.s, COMM_NONBLOCKING,
+            Ipc::fdnHttpsSocket, call);
+
+        HttpSockets[NHttpSockets++] = -1;
+    }
+}
+
+/// process clientHttpsConnectionsOpen result
+static void
+clientHttpsConnectionOpened(int fd, int, http_port_list *s)
+{
+    if (!OpenedHttpSocket(fd, "Cannot open HTTPS Port"))
+        return;
+
+    Must(s);
 
         AsyncCall::Pointer call = commCbCall(5,5, "SomeCommAcceptHandler(httpsAccept)",
                                              CommAcceptCbPtrFun(httpsAccept, s));
 
         s->listener = new Comm::ListenStateData(fd, call, true);
 
-        debugs(1, 1, "Accepting HTTPS connections at " << s->http.s << ", FD " << fd << ".");
+        debugs(1, 1, "Accepting HTTPS connections at " << s->s << ", FD " << fd << ".");
 
-        HttpSockets[NHttpSockets++] = fd;
-    }
+    Must(AddOpenedHttpSocket(fd)); // otherwise, we have received a fd we did not ask for
 }
 
 #endif
@@ -3447,7 +3517,7 @@
 #endif
 
     if (NHttpSockets < 1)
-        fatal("Cannot open HTTP Port");
+        fatal("No HTTP or HTTPS ports configured");
 }
 
 void

=== modified file 'src/comm.cc'
--- src/comm.cc	2010-01-02 10:17:00 +0000
+++ src/comm.cc	2010-05-04 15:34:46 +0000
@@ -71,6 +71,8 @@
 
 static void commStopHalfClosedMonitor(int fd);
 static IOCB commHalfClosedReader;
+static void comm_init_opened(int new_socket, IpAddress &addr, unsigned char TOS, const char *note, struct addrinfo *AI);
+static int comm_apply_flags(int new_socket, IpAddress &addr, int flags, struct addrinfo *AI);
 
 
 struct comm_io_callback_t {
@@ -687,7 +689,6 @@
             const char *note)
 {
     int new_socket;
-    fde *F = NULL;
     int tos = 0;
     struct addrinfo *AI = NULL;
 
@@ -743,6 +744,29 @@
 
 #endif
 
+    comm_init_opened(new_socket, addr, TOS, note, AI);
+    new_socket = comm_apply_flags(new_socket, addr, flags, AI);
+
+    addr.FreeAddrInfo(AI);
+
+    PROF_stop(comm_open);
+
+    return new_socket;
+}
+
+/// update FD tables after a local or remote (IPC) comm_openex();
+void
+comm_init_opened(int new_socket,
+            IpAddress &addr,
+            unsigned char TOS,
+            const char *note,
+            struct addrinfo *AI)
+{
+    assert(new_socket >= 0);
+    assert(AI);
+
+    fde *F = NULL;
+
     /* update fdstat */
     debugs(5, 5, "comm_open: FD " << new_socket << " is a new socket");
 
@@ -760,6 +784,19 @@
     F->tos = TOS;
 
     F->sock_family = AI->ai_family;
+}
+
+/// apply flags after a local comm_open*() call;
+/// returns new_socket or -1 on error
+static int
+comm_apply_flags(int new_socket,
+            IpAddress &addr,
+            int flags,
+            struct addrinfo *AI)
+{
+    assert(new_socket >= 0);
+    assert(AI);
+    const int sock_type = AI->ai_socktype;
 
     if (!(flags & COMM_NOCLOEXEC))
         commSetCloseOnExec(new_socket);
@@ -790,18 +827,14 @@
 
         if (commBind(new_socket, *AI) != COMM_OK) {
             comm_close(new_socket);
-            addr.FreeAddrInfo(AI);
             return -1;
-            PROF_stop(comm_open);
         }
     }
 
-    addr.FreeAddrInfo(AI);
-
     if (flags & COMM_NONBLOCKING)
         if (commSetNonBlocking(new_socket) == COMM_ERROR) {
+            comm_close(new_socket);
             return -1;
-            PROF_stop(comm_open);
         }
 
 #ifdef TCP_NODELAY
@@ -813,11 +846,50 @@
     if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
         commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
 
-    PROF_stop(comm_open);
-
     return new_socket;
 }
 
+void
+comm_import_opened(int fd,
+            IpAddress &addr,
+            int flags,
+            const char *note,
+            struct addrinfo *AI)
+{
+    debugs(5, 2, HERE << " FD " << fd << " at " << addr);
+    assert(fd >= 0);
+    assert(AI);
+
+    comm_init_opened(fd, addr, 0, note, AI);
+
+    if (!(flags & COMM_NOCLOEXEC))
+        fd_table[fd].flags.close_on_exec = 1;
+
+    if (addr.GetPort() > (u_short) 0) {
+#ifdef _SQUID_MSWIN_
+        if (sock_type != SOCK_DGRAM)
+#endif
+            fd_table[fd].flags.nolinger = 1;
+    }
+
+    if ((flags & COMM_TRANSPARENT))
+        fd_table[fd].flags.transparent = 1;
+
+    if (flags & COMM_NONBLOCKING)
+        fd_table[fd].flags.nonblocking = 1;
+
+#ifdef TCP_NODELAY
+    if (AI->ai_socktype == SOCK_STREAM)
+        fd_table[fd].flags.nodelay = 1;
+#endif
+
+    /* no fd_table[fd].flags. updates needed for these conditions:
+     * if ((flags & COMM_REUSEADDR)) ...
+     * if ((flags & COMM_DOBIND) ...) ...
+     */
+}
+
+
 CBDATA_CLASS_INIT(ConnectStateData);
 
 void *
@@ -2409,3 +2481,97 @@
         return EVENT_ERROR;
     };
 }
+
+/// Create a unix-domain socket (UDS) that only supports FD_MSGHDR I/O.
+int
+comm_open_uds(int sock_type,
+              int proto,
+              struct sockaddr_un* addr,
+              int flags)
+{
+    // TODO: merge with comm_openex() when IpAddress becomes NetAddress
+
+    int new_socket;
+
+    PROF_start(comm_open);
+    /* Create socket for accepting new connections. */
+    statCounter.syscalls.sock.sockets++;
+
+    /* Setup the socket addrinfo details for use */
+    struct addrinfo AI;
+    AI.ai_flags = 0;
+    AI.ai_family = PF_UNIX;
+    AI.ai_socktype = sock_type;
+    AI.ai_protocol = proto;
+    AI.ai_addrlen = SUN_LEN(addr);
+    AI.ai_addr = (sockaddr*)addr;
+    AI.ai_canonname = NULL;
+    AI.ai_next = NULL;
+
+    debugs(50, 3, HERE << "Attempt open socket for: " << addr->sun_path);
+
+    if ((new_socket = socket(AI.ai_family, AI.ai_socktype, AI.ai_protocol)) < 0) {
+        /* Increase the number of reserved fd's if calls to socket()
+         * are failing because the open file table is full.  This
+         * limits the number of simultaneous clients */
+
+        if (limitError(errno)) {
+            debugs(50, DBG_IMPORTANT, HERE << "socket failure: " << xstrerror());
+            fdAdjustReserved();
+        } else {
+            debugs(50, DBG_CRITICAL, HERE << "socket failure: " << xstrerror());
+        }
+
+        PROF_stop(comm_open);
+        return -1;
+    }
+
+    debugs(50, 3, HERE "Opened UDS FD " << new_socket << " : family=" << AI.ai_family << ", type=" << AI.ai_socktype << ", protocol=" << AI.ai_protocol);
+
+    /* update fdstat */
+    debugs(50, 5, HERE << "FD " << new_socket << " is a new socket");
+
+    assert(!isOpen(new_socket));
+    fd_open(new_socket, FD_MSGHDR, NULL);
+
+    fdd_table[new_socket].close_file = NULL;
+
+    fdd_table[new_socket].close_line = 0;
+
+    fd_table[new_socket].sock_family = AI.ai_family;
+
+    if (!(flags & COMM_NOCLOEXEC))
+        commSetCloseOnExec(new_socket);
+
+    if (flags & COMM_REUSEADDR)
+        commSetReuseAddr(new_socket);
+
+    if (flags & COMM_NONBLOCKING) {
+        if (commSetNonBlocking(new_socket) != COMM_OK) {
+            comm_close(new_socket);
+            PROF_stop(comm_open);
+            return -1;
+        }
+    }
+
+    if (flags & COMM_DOBIND) {
+        if (commBind(new_socket, AI) != COMM_OK) {
+            comm_close(new_socket);
+            PROF_stop(comm_open);
+            return -1;
+        }
+    }
+
+#ifdef TCP_NODELAY
+    if (sock_type == SOCK_STREAM)
+        commSetTcpNoDelay(new_socket);
+
+#endif
+
+    if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)
+        commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);
+
+    PROF_stop(comm_open);
+
+    return new_socket;
+}

=== modified file 'src/comm.h'
--- src/comm.h	2009-12-31 02:35:01 +0000
+++ src/comm.h	2010-05-02 01:42:47 +0000
@@ -55,6 +55,9 @@
 SQUIDCEXTERN void comm_exit(void);
 
 SQUIDCEXTERN int comm_open(int, int, IpAddress &, int, const char *note);
+SQUIDCEXTERN int comm_open_uds(int sock_type, int proto, struct sockaddr_un* addr, int flags);
+/// update Comm state after getting a comm_open() FD from another process
+SQUIDCEXTERN void comm_import_opened(int fd, IpAddress &addr, int flags, const char *note, struct addrinfo *AI);
 
 /**
  * Open a port specially bound for listening or sending through a specific port.

=== modified file 'src/debug.cc'
--- src/debug.cc	2009-11-22 20:37:27 +0000
+++ src/debug.cc	2010-07-01 22:01:14 +0000
@@ -36,6 +36,7 @@
 #include "Debug.h"
 #include "SquidTime.h"
 #include "util.h"
+#include "ipc/Kids.h"
 
 /* for shutting_down flag in xassert() */
 #include "globals.h"
@@ -52,6 +53,7 @@
 static char *debug_log_file = NULL;
 static int Ctx_Lock = 0;
 static const char *debugLogTime(void);
+static const char *debugLogKid(void);
 static void ctx_print(void);
 #if HAVE_SYSLOG
 #ifdef LOG_LOCAL4
@@ -117,8 +119,9 @@
     va_start(args2, format);
     va_start(args3, format);
 
-    snprintf(f, BUFSIZ, "%s| %s",
+    snprintf(f, BUFSIZ, "%s%s| %s",
              debugLogTime(),
+             debugLogKid(),
              format);
 
     _db_print_file(f, args1);
@@ -543,6 +546,19 @@
     return buf;
 }
 
+static const char *
+debugLogKid(void)
+{
+    if (KidIdentifier != 0) {
+		static char buf[16];
+        if (!*buf) // optimization: fill only once after KidIdentifier is set
+            snprintf(buf, sizeof(buf), " kid%d", KidIdentifier);
+		return buf;
+    }
+
+    return "";
+}
+
 void
 xassert(const char *msg, const char *file, int line)
 {

=== modified file 'src/enums.h'
--- src/enums.h	2010-01-01 21:16:57 +0000
+++ src/enums.h	2010-04-29 20:12:03 +0000
@@ -69,6 +69,7 @@
     FD_FILE,
     FD_SOCKET,
     FD_PIPE,
+    FD_MSGHDR,
     FD_UNKNOWN
 };
 

=== modified file 'src/fd.cc'
--- src/fd.cc	2009-03-06 13:26:57 +0000
+++ src/fd.cc	2010-04-29 20:12:03 +0000
@@ -45,6 +45,9 @@
 int socket_write_method(int, const char *, int);
 int file_read_method(int, char *, int);
 int file_write_method(int, const char *, int);
+#else
+int msghdr_read_method(int, char *, int);
+int msghdr_write_method(int, const char *, int);
 #endif
 
 const char *fdTypeStr[] = {
@@ -53,6 +56,7 @@
     "File",
     "Socket",
     "Pipe",
+    "MsgHdr",
     "Unknown"
 };
 
@@ -169,6 +173,24 @@
     return i;
 }
 
+int
+msghdr_read_method(int fd, char *buf, int len)
+{
+    PROF_start(read);
+    const int i = recvmsg(fd, reinterpret_cast<msghdr*>(buf), MSG_DONTWAIT);
+    PROF_stop(read);
+    return i;
+}
+
+int
+msghdr_write_method(int fd, const char *buf, int len)
+{
+    PROF_start(write);
+    const int i = sendmsg(fd, reinterpret_cast<const msghdr*>(buf), MSG_NOSIGNAL);
+    PROF_stop(write);
+    return i > 0 ? len : i; // len is imprecise but the caller expects a match
+}
+
 #endif
 
 void
@@ -213,9 +235,18 @@
     }
 
 #else
-    F->read_method = &default_read_method;
-
-    F->write_method = &default_write_method;
+    switch (type) {
+
+    case FD_MSGHDR:
+        F->read_method = &msghdr_read_method;
+        F->write_method = &msghdr_write_method;
+        break;
+
+    default:
+        F->read_method = &default_read_method;
+        F->write_method = &default_write_method;
+        break;
+    }
 
 #endif
 

=== modified file 'src/htcp.cc'
--- src/htcp.cc	2010-02-11 23:48:51 +0000
+++ src/htcp.cc	2010-06-14 21:22:01 +0000
@@ -46,6 +46,24 @@
 #include "http.h"
 #include "icmp/net_db.h"
 #include "AccessLogEntry.h"
+#include "ipc/StartListening.h"
+
+/// dials htcpIncomingConnectionOpened call
+class HtcpListeningStartedDialer: public CallDialer,
+    public Ipc::StartListeningCb
+{
+public:
+    typedef void (*Handler)(int fd, int errNo);
+    HtcpListeningStartedDialer(Handler aHandler): handler(aHandler) {}
+
+    virtual void print(std::ostream &os) const { startPrint(os) << ')'; }
+
+    virtual bool canDial(AsyncCall &) const { return true; }
+    virtual void dial(AsyncCall &) { (handler)(fd, errNo); }
+
+public:
+    Handler handler;
+};
 
 typedef struct _Countstr Countstr;
 
@@ -225,6 +243,8 @@
     RR_RESPONSE
 };
 
+static void htcpIncomingConnectionOpened(int fd, int errNo);
+
 static u_int32_t msg_id_counter = 0;
 static int htcpInSocket = -1;
 static int htcpOutSocket = -1;
@@ -1483,20 +1503,15 @@
     IpAddress incomingAddr = Config.Addrs.udp_incoming;
     incomingAddr.SetPort(Config.Port.htcp);
 
-    enter_suid();
-    htcpInSocket = comm_open_listener(SOCK_DGRAM,
+    AsyncCall::Pointer call = asyncCall(31, 2,
+        "htcpIncomingConnectionOpened",
+        HtcpListeningStartedDialer(&htcpIncomingConnectionOpened));
+
+    Ipc::StartListening(SOCK_DGRAM,
                                       IPPROTO_UDP,
                                       incomingAddr,
                                       COMM_NONBLOCKING,
-                                      "HTCP Socket");
-    leave_suid();
-
-    if (htcpInSocket < 0)
-        fatal("Cannot open HTCP Socket");
-
-    commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
-
-    debugs(31, 1, "Accepting HTCP messages on port " << Config.Port.htcp << ", FD " << htcpInSocket << ".");
+                                      Ipc::fdnInHtcpSocket, call);
 
     if (!Config.Addrs.udp_outgoing.IsNoAddr()) {
         IpAddress outgoingAddr = Config.Addrs.udp_outgoing;
@@ -1518,8 +1533,6 @@
         debugs(31, 1, "Outgoing HTCP messages on port " << Config.Port.htcp << ", FD " << htcpOutSocket << ".");
 
         fd_note(htcpInSocket, "Incoming HTCP socket");
-    } else {
-        htcpOutSocket = htcpInSocket;
     }
 
     if (!htcpDetailPool) {
@@ -1527,6 +1540,22 @@
     }
 }
 
+static void
+htcpIncomingConnectionOpened(int fd, int errNo)
+{
+    htcpInSocket = fd;
+
+    if (htcpInSocket < 0)
+        fatal("Cannot open HTCP Socket");
+
+    commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
+
+    debugs(31, 1, "Accepting HTCP messages on port " << Config.Port.htcp << ", FD " << htcpInSocket << ".");
+
+    if (Config.Addrs.udp_outgoing.IsNoAddr())
+        htcpOutSocket = htcpInSocket;
+}
+
 int
 htcpQuery(StoreEntry * e, HttpRequest * req, peer * p)
 {

=== modified file 'src/icp_v2.cc'
--- src/icp_v2.cc	2009-11-09 11:25:11 +0000
+++ src/icp_v2.cc	2010-06-14 21:22:01 +0000
@@ -48,8 +48,31 @@
 #include "SwapDir.h"
 #include "icmp/net_db.h"
 #include "ip/IpAddress.h"
+#include "ipc/StartListening.h"
 #include "rfc1738.h"
 
+/// dials icpIncomingConnectionOpened call
+class IcpListeningStartedDialer: public CallDialer,
+    public Ipc::StartListeningCb
+{
+public:
+    typedef void (*Handler)(int fd, int errNo, IpAddress& addr);
+    IcpListeningStartedDialer(Handler aHandler, IpAddress& anAddr):
+        handler(aHandler), addr(anAddr) {}
+
+    virtual void print(std::ostream &os) const { startPrint(os) <<
+        ", address=" << addr << ')'; }
+
+    virtual bool canDial(AsyncCall &) const { return true; }
+    virtual void dial(AsyncCall &) { (handler)(fd, errNo, addr); }
+
+public:
+    Handler handler;
+    IpAddress addr;
+};
+
+static void icpIncomingConnectionOpened(int fd, int errNo, IpAddress& addr);
+
 /// \ingroup ServerProtocolICPInternal2
 static void icpLogIcp(const IpAddress &, log_type, int, const char *, int);
 
@@ -656,35 +679,22 @@
 
     struct addrinfo *xai = NULL;
     int x;
-    wordlist *s;
 
     if ((port = Config.Port.icp) <= 0)
         return;
 
-    enter_suid();
-
     addr = Config.Addrs.udp_incoming;
     addr.SetPort(port);
-    theInIcpConnection = comm_open_listener(SOCK_DGRAM,
+
+    AsyncCall::Pointer call = asyncCall(12, 2,
+        "icpIncomingConnectionOpened",
+        IcpListeningStartedDialer(&icpIncomingConnectionOpened, addr));
+
+    Ipc::StartListening(SOCK_DGRAM,
                                             IPPROTO_UDP,
                                             addr,
                                             COMM_NONBLOCKING,
-                                            "ICP Socket");
-    leave_suid();
-
-    if (theInIcpConnection < 0)
-        fatal("Cannot open ICP Port");
-
-    commSetSelect(theInIcpConnection,
-                  COMM_SELECT_READ,
-                  icpHandleUdp,
-                  NULL,
-                  0);
-
-    for (s = Config.mcast_group_list; s; s = s->next)
-        ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL);
-
-    debugs(12, 1, "Accepting ICP messages at " << addr << ", FD " << theInIcpConnection << ".");
+                                            Ipc::fdnInIcpSocket, call);
 
     addr.SetEmpty(); // clear for next use.
     addr = Config.Addrs.udp_outgoing;
@@ -710,10 +720,6 @@
         debugs(12, 1, "Outgoing ICP messages on port " << addr.GetPort() << ", FD " << theOutIcpConnection << ".");
 
         fd_note(theOutIcpConnection, "Outgoing ICP socket");
-
-        fd_note(theInIcpConnection, "Incoming ICP socket");
-    } else {
-        theOutIcpConnection = theInIcpConnection;
     }
 
     theOutICPAddr.SetEmpty();
@@ -730,6 +736,31 @@
     theOutICPAddr.FreeAddrInfo(xai);
 }
 
+static void
+icpIncomingConnectionOpened(int fd, int errNo, IpAddress& addr)
+{
+    theInIcpConnection = fd;
+
+    if (theInIcpConnection < 0)
+        fatal("Cannot open ICP Port");
+
+    commSetSelect(theInIcpConnection,
+                  COMM_SELECT_READ,
+                  icpHandleUdp,
+                  NULL,
+                  0);
+
+    for (const wordlist *s = Config.mcast_group_list; s; s = s->next)
+        ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL);
+
+    debugs(12, 1, "Accepting ICP messages at " << addr << ", FD " << theInIcpConnection << ".");
+
+        fd_note(theInIcpConnection, "Incoming ICP socket");
+
+    if (Config.Addrs.udp_outgoing.IsNoAddr())
+        theOutIcpConnection = theInIcpConnection;
+}
+
 /**
  * icpConnectionShutdown only closes the 'in' socket if it is
  * different than the 'out' socket.

=== modified file 'src/ip/IpAddress.cc'
--- src/ip/IpAddress.cc	2010-03-01 01:13:17 +0000
+++ src/ip/IpAddress.cc	2010-05-02 01:20:05 +0000
@@ -879,6 +879,11 @@
     return 0;
 }
 
+int IpAddress::compareWhole(const IpAddress &rhs) const
+{
+    return memcmp(this, &rhs, sizeof(*this));
+}
+
 bool IpAddress::operator ==(const IpAddress &s) const
 {
     return (0 == matchIPAddr(s));
@@ -1041,9 +1046,9 @@
 
     p += ToHostname(p, blen);
 
-    if (m_SocketAddr.sin6_port > 0 && p < (buf+blen-6) ) {
-        /* 6 is max length of expected ':port' (short int) */
-        snprintf(p, 6,":%d", GetPort() );
+    if (m_SocketAddr.sin6_port > 0 && p <= (buf+blen-7) ) {
+        // ':port' (short int) needs at most 6 bytes plus 1 for 0-terminator
+        snprintf(p, 7, ":%d", GetPort() );
     }
 
     // force a null-terminated string

=== modified file 'src/ip/IpAddress.h'
--- src/ip/IpAddress.h	2010-02-28 22:04:23 +0000
+++ src/ip/IpAddress.h	2010-05-02 01:20:05 +0000
@@ -326,6 +326,13 @@
      */
     int matchIPAddr(const IpAddress &rhs) const;
 
+    /** Compare taking IP, port, protocol, etc. into account. Returns an
+        integer  less  than,  equal  to,  or greater than zero if the object
+        is found, respectively, to be less than, to match, or to be greater
+        than rhs. The exact ordering algorithm is not specified and may change.
+    */
+    int compareWhole(const IpAddress &rhs) const;
+
     /**
      *  Get RFC 3493 addrinfo structure from the IpAddress data
      *  for protocol-neutral socket operations.

=== added directory 'src/ipc'
=== added file 'src/ipc/Coordinator.cc'
--- src/ipc/Coordinator.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Coordinator.cc	2010-05-02 18:49:25 +0000
@@ -0,0 +1,172 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+
+#include "config.h"
+#include "comm.h"
+#include "ipc/Coordinator.h"
+#include "ipc/FdNotes.h"
+#include "ipc/SharedListen.h"
+
+
+CBDATA_NAMESPACED_CLASS_INIT(Ipc, Coordinator);
+Ipc::Coordinator* Ipc::Coordinator::TheInstance = NULL;
+
+
+Ipc::Coordinator::Coordinator():
+    Port(coordinatorAddr)
+{
+}
+
+void Ipc::Coordinator::start()
+{
+    Port::start();
+}
+
+Ipc::StrandCoord* Ipc::Coordinator::findStrand(int kidId)
+{
+    typedef Strands::iterator SI;
+    for (SI iter = strands.begin(); iter != strands.end(); ++iter) {
+        if (iter->kidId == kidId)
+            return &(*iter);
+    }
+    return NULL;
+}
+
+void Ipc::Coordinator::registerStrand(const StrandCoord& strand)
+{
+    if (StrandCoord* found = findStrand(strand.kidId))
+        *found = strand;
+    else
+        strands.push_back(strand);
+}
+
+void Ipc::Coordinator::receive(const TypedMsgHdr& message)
+{
+    switch (message.type()) {
+    case mtRegistration:
+        debugs(54, 6, HERE << "Registration request");
+        handleRegistrationRequest(StrandCoord(message));
+        break;
+
+    case mtSharedListenRequest:
+        debugs(54, 6, HERE << "Shared listen request");
+        handleSharedListenRequest(SharedListenRequest(message));
+        break;
+
+    case mtDescriptorGet:
+        debugs(54, 6, HERE << "Descriptor get request");
+        handleDescriptorGet(Descriptor(message));
+        break;
+
+    default:
+        debugs(54, 1, HERE << "Unhandled message type: " << message.type());
+        break;
+    }
+}
+
+void Ipc::Coordinator::handleRegistrationRequest(const StrandCoord& strand)
+{
+    registerStrand(strand);
+
+    // send back an acknowledgement; TODO: remove as not needed?
+    TypedMsgHdr message;
+    strand.pack(message);
+    SendMessage(MakeAddr(strandAddrPfx, strand.kidId), message);
+}
+
+void
+Ipc::Coordinator::handleSharedListenRequest(const SharedListenRequest& request)
+{
+    debugs(54, 4, HERE << "kid" << request.requestorId <<
+        " needs shared listen FD for " << request.params.addr);
+    Listeners::const_iterator i = listeners.find(request.params);
+    int errNo = 0;
+    const int sock = (i != listeners.end()) ?
+        i->second : openListenSocket(request, errNo);
+
+    debugs(54, 3, HERE << "sending shared listen FD " << sock << " for " <<
+        request.params.addr << " to kid" << request.requestorId <<
+        " mapId=" << request.mapId);
+
+    SharedListenResponse response(sock, errNo, request.mapId);
+    TypedMsgHdr message;
+    response.pack(message);
+    SendMessage(MakeAddr(strandAddrPfx, request.requestorId), message);
+}
+
+int
+Ipc::Coordinator::openListenSocket(const SharedListenRequest& request,
+        int &errNo)
+{
+    const OpenListenerParams &p = request.params;
+
+    debugs(54, 6, HERE << "opening listen FD at " << p.addr << " for kid" <<
+        request.requestorId);
+
+    IpAddress addr = p.addr; // comm_open_listener may modify it
+
+    enter_suid();
+    const int sock = comm_open_listener(p.sock_type, p.proto, addr, p.flags,
+        FdNote(p.fdNote));
+    errNo = (sock >= 0) ? 0 : errno;
+    leave_suid();
+
+    // cache positive results
+    if (sock >= 0)
+        listeners[request.params] = sock;
+
+    return sock;
+}
+
+void Ipc::Coordinator::handleDescriptorGet(const Descriptor& request)
+{
+    // XXX: hack: create descriptor here
+    char buffer[64];
+    snprintf(buffer, sizeof(buffer), "/tmp/squid_shared_file.txt");
+    static int fd = -1;
+    if (fd < 0) {
+        fd = open(buffer, O_CREAT | O_RDWR | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+        int n = snprintf(buffer, sizeof(buffer), "coord: created %d\n", fd);
+        ssize_t bytes = write(fd, buffer, n);
+        Must(bytes == n);
+        debugs(54, 6, "Created FD " << fd << " for kid" << request.fromKid);
+    } else {
+        int n = snprintf(buffer, sizeof(buffer), "coord: updated %d\n", fd);
+        ssize_t bytes = write(fd, buffer, n);
+        Must(bytes == n);
+    }
+
+    debugs(54, 6, "Sending FD " << fd << " to kid" << request.fromKid);
+
+    Descriptor response(-1, fd);
+    TypedMsgHdr message;
+    response.pack(message);
+    SendMessage(MakeAddr(strandAddrPfx, request.fromKid), message);
+
+    // XXX: close(fd); fd should be opened until the message has not reached rec iver 
+}
+
+void Ipc::Coordinator::broadcastSignal(int sig) const
+{
+    typedef Strands::const_iterator SCI;
+    for (SCI iter = strands.begin(); iter != strands.end(); ++iter) {
+        debugs(54, 5, HERE << "signal " << sig << " to kid" << iter->kidId <<
+            ", PID=" << iter->pid);
+        kill(iter->pid, sig);
+    }
+}
+
+Ipc::Coordinator* Ipc::Coordinator::Instance()
+{
+    if (!TheInstance)
+        TheInstance = new Coordinator;
+    // XXX: if the Coordinator job quits, this pointer will become invalid
+    // we could make Coordinator death fatal, except during exit, but since
+    // Strands do not re-register, even process death would be pointless.
+    return TheInstance;
+}

=== added file 'src/ipc/Coordinator.h'
--- src/ipc/Coordinator.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Coordinator.h	2010-07-01 22:58:45 +0000
@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_COORDINATOR_H
+#define SQUID_IPC_COORDINATOR_H
+
+
+#include "Array.h"
+#include <map>
+#include "ipc/Port.h"
+#include "ipc/Messages.h"
+#include "ipc/SharedListen.h"
+
+namespace Ipc
+{
+
+///  Coordinates shared activities of Strands (Squid processes or threads)
+class Coordinator: public Port
+{
+public:
+    static Coordinator* Instance();
+
+public:
+    Coordinator();
+
+    void broadcastSignal(int sig) const; ///< send sig to registered strands
+
+protected:
+    virtual void start(); // Port (AsyncJob) API
+    virtual void receive(const TypedMsgHdr& message); // Port API
+
+    StrandCoord* findStrand(int kidId); ///< registered strand or NULL
+    void registerStrand(const StrandCoord &); ///< adds or updates existing
+    void handleRegistrationRequest(const StrandCoord &); ///< register,ACK
+
+    /// returns cached socket or calls openListenSocket()
+    void handleSharedListenRequest(const SharedListenRequest& request);
+    void handleDescriptorGet(const Descriptor& request);
+
+    /// calls comm_open_listener()
+    int openListenSocket(const SharedListenRequest& request, int &errNo);
+
+private:
+    typedef Vector<StrandCoord> Strands; ///< unsorted strands
+    Strands strands; ///< registered processes and threads
+
+    typedef std::map<OpenListenerParams, int> Listeners; ///< params:fd map
+    Listeners listeners; ///< cached comm_open_listener() results
+
+    static Coordinator* TheInstance; ///< the only class instance in existence
+
+private:
+    Coordinator(const Coordinator&); // not implemented
+    Coordinator& operator =(const Coordinator&); // not implemented
+
+    CBDATA_CLASS2(Coordinator);
+};
+
+
+} // namespace Ipc
+
+#endif /* SQUID_IPC_COORDINATOR_H */

=== added file 'src/ipc/FdNotes.cc'
--- src/ipc/FdNotes.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/FdNotes.cc	2010-06-14 21:22:01 +0000
@@ -0,0 +1,31 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "Debug.h"
+#include "ipc/FdNotes.h"
+
+
+const char *
+Ipc::FdNote(int fdNoteId)
+{
+    static const char *FdNotes[Ipc::fdnEnd] = {
+        "None", // fdnNone
+        "HTTP Socket", // fdnHttpSocket
+        "HTTPS Socket", // fdnHttpsSocket
+        "Incoming SNMP Socket", // fdnInSnmpSocket
+        "Outgoing SNMP Socket", // fdnOutSnmpSocket
+        "Incoming ICP Socket", // fdnInIcpSocket
+        "Incoming HTCP Socket" // fdnInHtcpSocket
+    };
+
+    if (fdnNone < fdNoteId && fdNoteId < fdnEnd)
+        return FdNotes[fdNoteId];
+
+    debugs(54, 1, HERE << "salvaged bug: wrong fd_note ID: " << fdNoteId);
+    return FdNotes[fdnNone];
+}

=== added file 'src/ipc/FdNotes.h'
--- src/ipc/FdNotes.h	1970-01-01 00:00:00 +0000
+++ src/ipc/FdNotes.h	2010-06-14 21:22:01 +0000
@@ -0,0 +1,26 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_FD_NOTES_H
+#define SQUID_IPC_FD_NOTES_H
+
+namespace Ipc
+{
+
+/// We cannot send char* FD notes to other processes. Pass int IDs and convert.
+
+/// fd_note() label ID
+typedef enum { fdnNone, fdnHttpSocket, fdnHttpsSocket,
+               fdnInSnmpSocket, fdnOutSnmpSocket, 
+               fdnInIcpSocket, fdnInHtcpSocket, fdnEnd } FdNoteId;
+
+extern const char *FdNote(int fdNodeId); ///< converts FdNoteId into a string
+
+} // namespace Ipc;
+
+
+#endif /* SQUID_IPC_FD_NOTES_H */

=== added file 'src/ipc/Kid.cc'
--- src/ipc/Kid.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Kid.cc	2010-03-30 21:54:40 +0000
@@ -0,0 +1,122 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "ipc/Kid.h"
+
+Kid::Kid():
+    badFailures(0),
+    pid(-1),
+    startTime(0),
+    isRunning(false)
+{
+}
+
+Kid::Kid(const String& kid_name):
+    theName(kid_name),
+    badFailures(0),
+    pid(-1),
+    startTime(0),
+    isRunning(false)
+{
+}
+
+/// called when this kid got started, records PID
+void Kid::start(pid_t cpid)
+{
+    assert(!running());
+    assert(cpid > 0);
+
+    isRunning = true;
+    pid = cpid;
+    time(&startTime);
+}
+
+/// called when kid terminates, sets exiting status
+void Kid::stop(status_type exitStatus)
+{
+    assert(running());
+    assert(startTime != 0);
+
+    isRunning = false;
+
+    time_t stop_time;
+    time(&stop_time);
+    if ((stop_time - startTime) < fastFailureTimeLimit)
+        badFailures++;
+    else
+        badFailures = 0; // the failures are not "frequent" [any more]
+
+    status = exitStatus;
+}
+
+/// returns true if tracking of kid is stopped
+bool Kid::running() const
+{
+    return isRunning;
+}
+
+/// returns current pid for a running kid and last pid for a stopped kid
+pid_t Kid::getPid() const
+{
+    assert(pid > 0);
+    return pid;
+}
+
+/// whether the failures are "repeated and frequent"
+bool Kid::hopeless() const
+{
+    return badFailures > badFailureLimit;
+}
+
+/// returns true if the process terminated normally
+bool Kid::calledExit() const
+{
+    return (pid > 0) && !running() && WIFEXITED(status);
+}
+
+/// returns the exit status of the process
+int Kid::exitStatus() const
+{
+    return WEXITSTATUS(status);
+}
+
+/// whether the process exited with a given exit status code
+bool Kid::calledExit(int code) const
+{
+    return calledExit() && (exitStatus() == code);
+}
+
+/// whether the process exited with code 0
+bool Kid::exitedHappy() const
+{
+    return calledExit(0);
+}
+
+/// returns true if the kid was terminated by a signal
+bool Kid::signaled() const
+{
+    return (pid > 0) && !running() && WIFSIGNALED(status);
+}
+
+/// returns the number of the signal that caused the kid to terminate
+int Kid::termSignal() const
+{
+    return WTERMSIG(status);
+}
+
+/// whether the process was terminated by a given signal
+bool Kid::signaled(int sgnl) const
+{
+    return signaled() && (termSignal() == sgnl);
+}
+
+/// returns kid name
+const String& Kid::name() const
+{
+    return theName;
+}

=== added file 'src/ipc/Kid.h'
--- src/ipc/Kid.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Kid.h	2010-03-30 21:54:40 +0000
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_IPC_KID_H
+#define SQUID_IPC_KID_H
+
+#include "SquidString.h"
+
+
+/// Squid child, including current forked process info and
+/// info persistent across restarts
+class Kid
+{
+public:
+#ifdef _SQUID_NEXT_
+    typedef union wait status_type;
+#else
+    typedef int status_type;
+#endif
+
+    /// keep restarting until the number of bad failures exceed this limit
+    enum { badFailureLimit = 4 };
+
+    /// slower start failures are not "frequent enough" to be counted as "bad"
+    enum { fastFailureTimeLimit = 10 }; // seconds
+
+public:
+    Kid();
+
+    Kid(const String& kid_name);
+
+    /// called when this kid got started, records PID
+    void start(pid_t cpid);
+
+    /// called when kid terminates, sets exiting status
+    void stop(status_type exitStatus);
+
+    /// returns true if tracking of kid is stopped
+    bool running() const;
+
+    /// returns current pid for a running kid and last pid for a stopped kid
+    pid_t getPid() const;
+
+    /// whether the failures are "repeated and frequent"
+    bool hopeless() const;
+
+    /// returns true if the process terminated normally
+    bool calledExit() const;
+
+    /// returns the exit status of the process
+    int exitStatus() const;
+
+    /// whether the process exited with a given exit status code
+    bool calledExit(int code) const;
+
+    /// whether the process exited with code 0
+    bool exitedHappy() const;
+
+    /// returns true if the kid was terminated by a signal
+    bool signaled() const;
+
+    /// returns the number of the signal that caused the kid to terminate
+    int termSignal() const;
+
+    /// whether the process was terminated by a given signal
+    bool signaled(int sgnl) const;
+
+    /// returns kid name
+    const String& name() const;
+
+private:
+    // Information preserved across restarts
+    String theName; ///< process name
+    int badFailures; ///< number of "repeated frequent" failures
+
+    // Information specific to a running or stopped kid
+    pid_t  pid; ///< current (for a running kid) or last (for stopped kid) PID
+    time_t startTime; ///< last start time
+    bool   isRunning; ///< whether the kid is assumed to be alive
+    status_type status; ///< exit status of a stopped kid
+};
+
+#endif /* SQUID_IPC_KID_H */

=== added file 'src/ipc/Kids.cc'
--- src/ipc/Kids.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Kids.cc	2010-04-26 07:09:03 +0000
@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "ipc/Kids.h"
+
+Kids TheKids;
+char KidName[NAME_MAX];
+int  KidIdentifier;
+
+Kids::Kids()
+{
+}
+
+/// maintain n kids
+void Kids::init(size_t n)
+{
+    assert(n > 0);
+
+    if (storage.size() > 0)
+        storage.clean();
+
+    storage.reserve(n);
+
+    char kid_name[32];
+
+    // add Kid records for all n main strands
+    for (size_t i = 1; i <= n; ++i) {
+        snprintf(kid_name, sizeof(kid_name), "(squid-%d)", (int)i);
+        storage.push_back(Kid(kid_name));
+    }
+
+    // if coordination is needed, add a Kid record for Coordinator
+    if (n > 1) {
+        snprintf(kid_name, sizeof(kid_name), "(squid-coord-%d)", (int)(n + 1));
+        storage.push_back(Kid(kid_name));
+    }
+}
+
+/// returns kid by pid
+Kid* Kids::find(pid_t pid)
+{
+    assert(pid > 0);
+    assert(count() > 0);
+
+    for (size_t i = 0; i < storage.size(); ++i) {
+        if (storage[i].getPid() == pid)
+            return &storage[i];
+    }
+    return NULL;
+}
+
+/// returns the kid by index, useful for kids iteration
+Kid& Kids::get(size_t i)
+{
+    assert(i >= 0 && i < count());
+    return storage[i];
+}
+
+/// whether all kids are hopeless
+bool Kids::allHopeless() const
+{
+    for (size_t i = 0; i < storage.size(); ++i) {
+        if (!storage[i].hopeless())
+            return false;
+    }
+    return true;
+}
+
+/// whether all kids called exited happy
+bool Kids::allExitedHappy() const
+{
+    for (size_t i = 0; i < storage.size(); ++i) {
+        if (!storage[i].exitedHappy())
+            return false;
+    }
+    return true;
+}
+
+/// whether all kids died from a given signal
+bool Kids::allSignaled(int sgnl) const
+{
+    for (size_t i = 0; i < storage.size(); ++i) {
+        if (!storage[i].signaled(sgnl))
+            return false;
+    }
+    return true;
+}
+
+/// returns the number of kids
+size_t Kids::count() const
+{
+    return storage.size();
+}

=== added file 'src/ipc/Kids.h'
--- src/ipc/Kids.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Kids.h	2010-03-30 21:54:40 +0000
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_IPC_KIDS_H
+#define SQUID_IPC_KIDS_H
+
+#include "Array.h"
+#include "ipc/Kid.h"
+
+
+/// a collection of kids
+class Kids
+{
+public:
+    Kids ();
+
+private:
+    Kids (const Kids&); ///< not implemented
+    Kids& operator= (const Kids&); ///< not implemented
+
+public:
+    /// maintain n kids
+    void init(size_t n);
+
+    /// returns kid by pid
+    Kid* find(pid_t pid);
+
+    /// returns the kid by index, useful for kids iteration
+    Kid& get(size_t i);
+
+    /// whether all kids are hopeless
+    bool allHopeless() const;
+
+    /// whether all kids called exited happy
+    bool allExitedHappy() const;
+
+    /// whether all kids died from a given signal
+    bool allSignaled(int sgnl) const;
+
+    /// returns the number of kids
+    size_t count() const;
+
+private:
+    Vector<Kid> storage;
+};
+
+extern Kids TheKids; ///< All kids being maintained
+
+extern char KidName[NAME_MAX]; ///< current Squid process name (e.g., squid2)
+extern int KidIdentifier; ///< current Squid process number (e.g., 4)
+
+
+#endif /* SQUID_IPC_KIDS_H */

=== added file 'src/ipc/Makefile.am'
--- src/ipc/Makefile.am	1970-01-01 00:00:00 +0000
+++ src/ipc/Makefile.am	2010-05-02 18:49:25 +0000
@@ -0,0 +1,30 @@
+include $(top_srcdir)/src/Common.am
+include $(top_srcdir)/src/TestHeaders.am
+
+noinst_LTLIBRARIES = libipc.la
+
+libipc_la_SOURCES = \
+	FdNotes.cc \
+	FdNotes.h \
+	Kid.cc \
+	Kid.h \
+	Kids.cc \
+	Kids.h \
+	Messages.cc \
+	Messages.h \
+	StartListening.cc \
+	StartListening.h \
+	SharedListen.cc \
+	SharedListen.h \
+	TypedMsgHdr.cc \
+	TypedMsgHdr.h \
+	Coordinator.cc \
+	Coordinator.h \
+	UdsOp.cc \
+	UdsOp.h \
+	Port.cc \
+	Port.h \
+	Strand.cc \
+	Strand.h
+
+DEFS += -DDEFAULT_PREFIX=\"$(prefix)\"

=== added file 'src/ipc/Messages.cc'
--- src/ipc/Messages.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Messages.cc	2010-04-30 18:01:29 +0000
@@ -0,0 +1,61 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+
+#include "config.h"
+#include "comm.h"
+#include "ipc/Messages.h"
+#include "ipc/TypedMsgHdr.h"
+
+
+Ipc::StrandCoord::StrandCoord(): kidId(-1), pid(0)
+{
+}
+
+Ipc::StrandCoord::StrandCoord(int aKidId, pid_t aPid): kidId(aKidId), pid(aPid)
+{
+}
+
+Ipc::StrandCoord::StrandCoord(const TypedMsgHdr &hdrMsg): kidId(-1), pid(0)
+{
+    hdrMsg.getData(mtRegistration, this, sizeof(*this));
+}
+
+void Ipc::StrandCoord::pack(TypedMsgHdr &hdrMsg) const
+{
+    hdrMsg.putData(mtRegistration, this, sizeof(*this));
+}
+
+
+Ipc::Descriptor::Descriptor(): fromKid(-1), fd(-1)
+{
+}
+
+Ipc::Descriptor::Descriptor(int aFromKid, int aFd): fromKid(aFromKid), fd(aFd)
+{
+}
+
+Ipc::Descriptor::Descriptor(const TypedMsgHdr &hdrMsg): fromKid(-1), fd(-1)
+{
+    if (hdrMsg.type() == mtDescriptorGet) {
+        hdrMsg.getData(mtDescriptorGet, this, sizeof(*this));
+        fd = -1;
+    } else {
+        hdrMsg.getData(mtDescriptorPut, this, sizeof(*this));
+        fd = hdrMsg.getFd();
+    }
+}
+
+void Ipc::Descriptor::pack(TypedMsgHdr &hdrMsg) const
+{
+    if (fd >= 0) {
+        hdrMsg.putData(mtDescriptorPut, this, sizeof(*this));
+        hdrMsg.putFd(fd);
+    } else {
+        hdrMsg.putData(mtDescriptorGet, this, sizeof(*this));
+    }
+}

=== added file 'src/ipc/Messages.h'
--- src/ipc/Messages.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Messages.h	2010-05-02 18:49:25 +0000
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_MESSAGES_H
+#define SQUID_IPC_MESSAGES_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/// Declare IPC messages. These classes translate between high-level
+/// information and low-level TypedMsgHdr (i.e., struct msghdr) buffers.
+
+namespace Ipc
+{
+
+class TypedMsgHdr;
+
+typedef enum { mtNone = 0, mtRegistration,
+    mtSharedListenRequest, mtSharedListenResponse,
+    mtDescriptorGet, mtDescriptorPut } MessageType;
+
+/// Strand location details
+class StrandCoord {
+public:
+    StrandCoord(); ///< unknown location
+    StrandCoord(int akidId, pid_t aPid); ///< from registrant
+    explicit StrandCoord(const TypedMsgHdr &hdrMsg); ///< from recvmsg()
+    void pack(TypedMsgHdr &hdrMsg) const; ///< prepare for sendmsg()
+
+public:
+    int kidId; ///< internal Squid process number
+    pid_t pid; ///< OS process or thread identifier
+};
+
+/// a [socket] descriptor information
+class Descriptor
+{
+public:
+    Descriptor(); ///< unknown descriptor
+    Descriptor(int fromKid, int fd); ///< from descriptor sender or requestor
+    explicit Descriptor(const TypedMsgHdr &hdrMsg); ///< from recvmsg()
+    void pack(TypedMsgHdr &hdrMsg) const; ///< prepare for sendmsg()
+
+public:
+    int fromKid; /// the source of this message
+    int fd; ///< raw descriptor value
+};
+
+
+} // namespace Ipc;
+
+
+#endif /* SQUID_IPC_MESSAGES_H */

=== added file 'src/ipc/Port.cc'
--- src/ipc/Port.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Port.cc	2010-04-29 22:35:11 +0000
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+
+#include "config.h"
+#include "CommCalls.h"
+#include "ipc/Port.h"
+
+const char Ipc::coordinatorAddr[] = DEFAULT_PREFIX "/var/run/coordinator.ipc";
+const char Ipc::strandAddrPfx[] = DEFAULT_PREFIX "/var/run/squid";
+
+
+Ipc::Port::Port(const String& aListenAddr):
+    UdsOp(aListenAddr)
+{
+    setOptions(COMM_NONBLOCKING | COMM_DOBIND);
+}
+
+void Ipc::Port::start()
+{
+    UdsOp::start();
+    listen();
+}
+
+void Ipc::Port::listen()
+{
+    debugs(54, 6, HERE);
+    buf.prepForReading();
+    AsyncCall::Pointer readHandler = asyncCall(54, 6, "Ipc::Port::noteRead",
+        CommCbMemFunT<Port, CommIoCbParams>(this, &Port::noteRead));
+    comm_read(fd(), buf.raw(), buf.size(), readHandler);
+}
+
+bool Ipc::Port::doneAll() const
+{
+    return false; // listen forever
+}
+
+String Ipc::Port::MakeAddr(const char* pathAddr, int id)
+{
+    assert(id >= 0);
+    String addr = pathAddr;
+    addr.append('-');
+    addr.append(xitoa(id));
+    addr.append(".ipc");
+    return addr;
+}
+
+void Ipc::Port::noteRead(const CommIoCbParams& params)
+{
+    debugs(54, 6, HERE << "FD " << params.fd << " flag " << params.flag <<
+        " [" << this << ']');
+    if (params.flag == COMM_OK) {
+        assert(params.buf == buf.raw());
+        receive(buf);
+    }
+    // TODO: if there was a fatal error on our socket, close the socket before
+    // trying to listen again and print a level-1 error message.
+
+    listen();
+}

=== added file 'src/ipc/Port.h'
--- src/ipc/Port.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Port.h	2010-04-29 20:12:03 +0000
@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_PORT_H
+#define SQUID_IPC_PORT_H
+
+
+#include "SquidString.h"
+#include "ipc/UdsOp.h"
+
+
+namespace Ipc
+{
+
+
+/// Waits for and receives incoming IPC messages; kids handle the messages
+class Port: public UdsOp
+{
+public:
+    Port(const String &aListenAddr);
+
+protected:
+    /// calculates IPC message address for strand #id at path
+    static String MakeAddr(const char *path, int id);
+
+    virtual void start() = 0; // UdsOp (AsyncJob) API; has body
+    virtual bool doneAll() const; // UdsOp (AsyncJob) API
+
+    /// read the next incoming message
+    void listen();
+
+    /// handle IPC message just read
+    virtual void receive(const TypedMsgHdr& message) = 0;
+
+private:
+    void noteRead(const CommIoCbParams &params); // Comm callback API
+
+private:
+    TypedMsgHdr buf; ///< msghdr struct filled by Comm
+};
+
+
+extern const char coordinatorAddr[]; ///< where coordinator listens
+extern const char strandAddrPfx[]; ///< strand's listening address prefix
+
+} // namespace Ipc
+
+
+#endif /* SQUID_IPC_PORT_H */

=== added file 'src/ipc/SharedListen.cc'
--- src/ipc/SharedListen.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/SharedListen.cc	2010-05-02 18:49:25 +0000
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include <map>
+#include "comm.h"
+#include "ipc/Port.h"
+#include "ipc/Messages.h"
+#include "ipc/Kids.h"
+#include "ipc/TypedMsgHdr.h"
+#include "ipc/StartListening.h"
+#include "ipc/SharedListen.h"
+
+
+/// holds information necessary to handle JoinListen response
+class PendingOpenRequest
+{
+public:
+    Ipc::OpenListenerParams params; ///< actual comm_open_sharedListen() parameters
+    AsyncCall::Pointer callback; // who to notify
+};
+
+/// maps ID assigned at request time to the response callback
+typedef std::map<int, PendingOpenRequest> SharedListenRequestMap;
+static SharedListenRequestMap TheSharedListenRequestMap;
+
+static int
+AddToMap(const PendingOpenRequest &por)
+{
+    // find unused ID using linear seach; there should not be many entries
+    for (int id = 0; true; ++id) {
+        if (TheSharedListenRequestMap.find(id) == TheSharedListenRequestMap.end()) {
+            TheSharedListenRequestMap[id] = por;
+            return id;
+        }
+    }
+    assert(false); // not reached
+    return -1;
+}
+
+Ipc::OpenListenerParams::OpenListenerParams()
+{
+    xmemset(this, 0, sizeof(*this));
+}
+
+bool
+Ipc::OpenListenerParams::operator <(const OpenListenerParams &p) const
+{
+    if (sock_type != p.sock_type)
+        return sock_type < p.sock_type;
+
+    if (proto != p.proto)
+        return proto < p.proto;
+
+    // ignore flags and fdNote differences because they do not affect binding
+
+    return addr.compareWhole(p.addr) < 0;
+}
+
+
+
+Ipc::SharedListenRequest::SharedListenRequest(): requestorId(-1), mapId(-1)
+{
+    // caller will then set public data members
+}
+
+Ipc::SharedListenRequest::SharedListenRequest(const TypedMsgHdr &hdrMsg)
+{
+    hdrMsg.getData(mtSharedListenRequest, this, sizeof(*this));
+}
+
+void Ipc::SharedListenRequest::pack(TypedMsgHdr &hdrMsg) const
+{
+    hdrMsg.putData(mtSharedListenRequest, this, sizeof(*this));
+}
+
+
+Ipc::SharedListenResponse::SharedListenResponse(int aFd, int anErrNo, int aMapId):
+    fd(aFd), errNo(anErrNo), mapId(aMapId)
+{
+}
+
+Ipc::SharedListenResponse::SharedListenResponse(const TypedMsgHdr &hdrMsg):
+    fd(-1), errNo(0), mapId(-1)
+{
+    hdrMsg.getData(mtSharedListenResponse, this, sizeof(*this));
+    fd = hdrMsg.getFd();
+}
+
+void Ipc::SharedListenResponse::pack(TypedMsgHdr &hdrMsg) const
+{
+    hdrMsg.putData(mtSharedListenResponse, this, sizeof(*this));
+    hdrMsg.putFd(fd);
+}
+
+
+void Ipc::JoinSharedListen(const OpenListenerParams &params,
+    AsyncCall::Pointer &callback)
+{
+    PendingOpenRequest por;
+    por.params = params;
+    por.callback = callback;
+
+    SharedListenRequest request;
+    request.requestorId = KidIdentifier;
+    request.params = por.params;
+    request.mapId = AddToMap(por);
+
+    debugs(54, 3, HERE << "getting listening FD for " << request.params.addr <<
+        " mapId=" << request.mapId);
+
+    TypedMsgHdr message;
+    request.pack(message);
+    SendMessage(coordinatorAddr, message);
+}
+
+void Ipc::SharedListenJoined(const SharedListenResponse &response)
+{
+    const int fd = response.fd;
+
+    debugs(54, 3, HERE << "got listening FD " << fd << " errNo=" <<
+        response.errNo << " mapId=" << response.mapId);
+
+    Must(TheSharedListenRequestMap.find(response.mapId) != TheSharedListenRequestMap.end());
+    PendingOpenRequest por = TheSharedListenRequestMap[response.mapId];
+    Must(por.callback != NULL);
+    TheSharedListenRequestMap.erase(response.mapId);
+
+    if (fd >= 0) {
+        OpenListenerParams &p = por.params;
+        struct addrinfo *AI = NULL;
+        p.addr.GetAddrInfo(AI);
+        AI->ai_socktype = p.sock_type;
+        AI->ai_protocol = p.proto;
+        comm_import_opened(fd, p.addr, p.flags, FdNote(p.fdNote), AI);
+        p.addr.FreeAddrInfo(AI);
+    }
+
+    StartListeningCb *cbd =
+        dynamic_cast<StartListeningCb*>(por.callback->getDialer());
+    Must(cbd);
+    cbd->fd = fd;
+    cbd->errNo = response.errNo;
+    ScheduleCallHere(por.callback);
+}

=== added file 'src/ipc/SharedListen.h'
--- src/ipc/SharedListen.h	1970-01-01 00:00:00 +0000
+++ src/ipc/SharedListen.h	2010-05-02 18:49:25 +0000
@@ -0,0 +1,74 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_SHARED_LISTEN_H
+#define SQUID_IPC_SHARED_LISTEN_H
+
+#include "base/AsyncCall.h"
+
+namespace Ipc
+{
+
+/// "shared listen" is when concurrent processes are listening on the same fd
+
+/// comm_open_listener() parameters holder
+class OpenListenerParams
+{
+public:
+    OpenListenerParams();
+
+    bool operator <(const OpenListenerParams &p) const; ///< useful for map<>
+
+    int sock_type;
+    int proto;
+    IpAddress addr; ///< will be memset and memcopied
+    int flags;
+    int fdNote; ///< index into fd_note() comment strings
+};
+
+class TypedMsgHdr;
+
+/// a request for a listen socket with given parameters
+class SharedListenRequest
+{
+public:
+    SharedListenRequest(); ///< from OpenSharedListen() which then sets public data
+    explicit SharedListenRequest(const TypedMsgHdr &hdrMsg); ///< from recvmsg()
+    void pack(TypedMsgHdr &hdrMsg) const; ///< prepare for sendmsg()
+
+public:
+    int requestorId; ///< kidId of the requestor
+
+    OpenListenerParams params; ///< actual comm_open_sharedListen() parameters
+
+    int mapId; ///< to map future response to the requestor's callback
+};
+
+/// a response to SharedListenRequest
+class SharedListenResponse
+{
+public:
+    SharedListenResponse(int fd, int errNo, int mapId);
+    explicit SharedListenResponse(const TypedMsgHdr &hdrMsg); ///< from recvmsg()
+    void pack(TypedMsgHdr &hdrMsg) const; ///< prepare for sendmsg()
+
+public:
+    int fd; ///< opened listening socket or -1
+    int errNo; ///< errno value from comm_open_sharedListen() call
+    int mapId; ///< to map future response to the requestor's callback
+};
+
+/// prepare and send SharedListenRequest to Coordinator
+extern void JoinSharedListen(const OpenListenerParams &, AsyncCall::Pointer &);
+
+/// process Coordinator response to SharedListenRequest
+extern void SharedListenJoined(const SharedListenResponse &response);
+
+} // namespace Ipc;
+
+
+#endif /* SQUID_IPC_SHARED_LISTEN_H */

=== added file 'src/ipc/StartListening.cc'
--- src/ipc/StartListening.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/StartListening.cc	2010-06-14 20:45:53 +0000
@@ -0,0 +1,58 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "comm.h"
+#include "TextException.h"
+#include "ipc/SharedListen.h"
+#include "ipc/StartListening.h"
+
+
+Ipc::StartListeningCb::StartListeningCb(): fd(-1), errNo(0)
+{
+}
+
+Ipc::StartListeningCb::~StartListeningCb()
+{
+}
+
+std::ostream &Ipc::StartListeningCb::startPrint(std::ostream &os) const
+{
+    return os << "(FD " << fd << ", err=" << errNo;
+}
+
+
+void Ipc::StartListening(int sock_type, int proto, IpAddress &addr,
+    int flags, FdNoteId fdNote, AsyncCall::Pointer &callback)
+{
+    OpenListenerParams p;
+    p.sock_type = sock_type;
+    p.proto = proto;
+    p.addr = addr;
+    p.flags = flags;
+    p.fdNote = fdNote;
+
+    if (UsingSmp()) { // if SMP is on, share
+        Ipc::JoinSharedListen(p, callback);
+        return; // wait for the call back
+    }
+
+    enter_suid();
+    const int sock = comm_open_listener(p.sock_type, p.proto, p.addr, p.flags,
+        FdNote(p.fdNote));
+    const int errNo = (sock >= 0) ? 0 : errno;
+    leave_suid();
+
+    debugs(54, 3, HERE << "opened listen FD " << sock << " for " << p.addr);
+
+    StartListeningCb *cbd =
+        dynamic_cast<StartListeningCb*>(callback->getDialer());
+    Must(cbd);
+    cbd->fd = sock;
+    cbd->errNo = errNo;
+    ScheduleCallHere(callback);
+}

=== added file 'src/ipc/StartListening.h'
--- src/ipc/StartListening.h	1970-01-01 00:00:00 +0000
+++ src/ipc/StartListening.h	2010-05-02 18:49:25 +0000
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_START_LISTENING_H
+#define SQUID_IPC_START_LISTENING_H
+
+#include <iosfwd>
+#include "ipc/FdNotes.h"
+#include "base/AsyncCall.h"
+
+class IpAddress;
+
+namespace Ipc
+{
+
+/// common API for all StartListening() callbacks
+class StartListeningCb
+{
+public:
+    StartListeningCb();
+    virtual ~StartListeningCb();
+
+    /// starts printing arguments, return os
+    std::ostream &startPrint(std::ostream &os) const;
+
+public:
+    int fd; ///< opened listening socket or -1
+    int errNo; ///< errno value from the comm_open_listener() call
+};
+
+/// Depending on whether SMP is on, either ask Coordinator to send us
+/// the listening FD or call comm_open_listener() directly.
+extern void StartListening(int sock_type, int proto, IpAddress &addr,
+    int flags, FdNoteId fdNote, AsyncCall::Pointer &callback);
+
+} // namespace Ipc;
+
+
+#endif /* SQUID_IPC_START_LISTENING_H */

=== added file 'src/ipc/Strand.cc'
--- src/ipc/Strand.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/Strand.cc	2010-05-02 18:49:25 +0000
@@ -0,0 +1,97 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "ipc/Strand.h"
+#include "ipc/Messages.h"
+#include "ipc/SharedListen.h"
+#include "ipc/Kids.h"
+
+
+CBDATA_NAMESPACED_CLASS_INIT(Ipc, Strand);
+
+
+Ipc::Strand::Strand():
+    Port(MakeAddr(strandAddrPfx, KidIdentifier)),
+    isRegistered(false)
+{
+}
+
+void Ipc::Strand::start()
+{
+    Port::start();
+    registerSelf();
+}
+
+void Ipc::Strand::registerSelf()
+{
+    debugs(54, 6, HERE);
+    Must(!isRegistered);
+    TypedMsgHdr message;
+    StrandCoord(KidIdentifier, getpid()).pack(message);
+    SendMessage(coordinatorAddr, message);
+    setTimeout(6, "Ipc::Strand::timeoutHandler"); // TODO: make 6 configurable?
+}
+
+void Ipc::Strand::receive(const TypedMsgHdr &message)
+{
+    debugs(54, 6, HERE << message.type());
+    switch (message.type()) {
+
+    case mtRegistration:
+        handleRegistrationResponse(StrandCoord(message));
+        break;
+
+    case mtSharedListenResponse:
+        SharedListenJoined(SharedListenResponse(message));
+        break;
+
+    case mtDescriptorPut:
+        putDescriptor(Descriptor(message));
+        break;
+
+    default:
+        debugs(54, 1, HERE << "Unhandled message type: " << message.type());
+        break;
+    }
+}
+
+void Ipc::Strand::handleRegistrationResponse(const StrandCoord &strand)
+{
+    // handle registration response from the coordinator; it could be stale
+    if (strand.kidId == KidIdentifier && strand.pid == getpid()) {
+        debugs(54, 6, "kid" << KidIdentifier << " registered");
+        clearTimeout(); // we are done
+
+        debugs(54, 6, HERE << "requesting FD");
+        Descriptor request(KidIdentifier, -1);
+        TypedMsgHdr message;
+        request.pack(message);
+        SendMessage(coordinatorAddr, message);
+    } else {
+        // could be an ACK to the registration message of our dead predecessor
+        debugs(54, 6, "kid" << KidIdentifier << " is not yet registered");
+        // keep listening, with a timeout
+    }
+}
+
+/// receive descriptor we asked for
+void Ipc::Strand::putDescriptor(const Descriptor &message)
+{
+    debugs(54, 6, HERE << "got FD " << message.fd);
+    char buffer[64];
+    const int n = snprintf(buffer, sizeof(buffer), "strand: kid%d wrote using FD %d\n", KidIdentifier, message.fd);
+    ssize_t bytes = write(message.fd, buffer, n);
+    Must(bytes == n);
+}
+
+void Ipc::Strand::timedout()
+{
+    debugs(54, 6, HERE << isRegistered);
+    if (!isRegistered)
+        fatalf("kid%d registration timed out", KidIdentifier);
+}

=== added file 'src/ipc/Strand.h'
--- src/ipc/Strand.h	1970-01-01 00:00:00 +0000
+++ src/ipc/Strand.h	2010-07-01 22:58:45 +0000
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_STRAND_H
+#define SQUID_IPC_STRAND_H
+
+#include "ipc/Port.h"
+
+
+namespace Ipc
+{
+
+class StrandCoord;
+class Descriptor;
+
+/// Receives coordination messages on behalf of its process or thread
+class Strand: public Port
+{
+public:
+    Strand();
+
+    virtual void start(); // Port (AsyncJob) API
+
+protected:
+    virtual void timedout(); // Port (UsdOp) API
+    virtual void receive(const TypedMsgHdr &message); // Port API
+
+private:
+    void registerSelf(); /// let Coordinator know this strand exists
+    void handleRegistrationResponse(const StrandCoord &strand);
+    void putDescriptor(const Descriptor &message);
+
+private:
+    bool isRegistered; ///< whether Coordinator ACKed registration (unused)
+
+private:
+    Strand(const Strand&); // not implemented
+    Strand& operator =(const Strand&); // not implemented
+
+    CBDATA_CLASS2(Strand);
+};
+
+
+}
+
+
+#endif /* SQUID_IPC_STRAND_H */

=== added file 'src/ipc/TypedMsgHdr.cc'
--- src/ipc/TypedMsgHdr.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/TypedMsgHdr.cc	2010-05-01 23:47:46 +0000
@@ -0,0 +1,167 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+
+#include "config.h"
+#include <string.h>
+#include "TextException.h"
+#include "ipc/TypedMsgHdr.h"
+
+Ipc::TypedMsgHdr::TypedMsgHdr()
+{
+	xmemset(this, 0, sizeof(*this));
+	sync();
+}
+
+Ipc::TypedMsgHdr::TypedMsgHdr(const TypedMsgHdr &tmh)
+{
+	xmemcpy(this, &tmh, sizeof(*this));
+	sync();
+}
+
+Ipc::TypedMsgHdr &Ipc::TypedMsgHdr::operator =(const TypedMsgHdr &tmh)
+{
+	if (this != &tmh) { // skip assignment to self
+		xmemcpy(this, &tmh, sizeof(*this));
+		sync();
+	}
+	return *this;
+}
+
+// update msghdr and ios pointers based on msghdr counters
+void Ipc::TypedMsgHdr::sync()
+{
+	if (msg_name) { // we have a name
+		msg_name = &name;
+	} else {
+		Must(!msg_namelen && !msg_name);
+	}
+
+	if (msg_iov) { // we have a data component
+		Must(msg_iovlen == 1);
+		msg_iov = ios;
+		ios[0].iov_base = &data;
+		Must(ios[0].iov_len == sizeof(data));
+	} else {
+		Must(!msg_iovlen && !msg_iov);
+	}
+
+	if (msg_control) { // we have a control component
+		Must(msg_controllen > 0);
+		msg_control = &ctrl;
+	} else {
+		Must(!msg_controllen && !msg_control);
+	}
+}
+
+
+
+int
+Ipc::TypedMsgHdr::type() const
+{
+	Must(msg_iovlen == 1);
+	return data.type_;
+}
+
+void
+Ipc::TypedMsgHdr::address(const struct sockaddr_un& addr)
+{
+	allocName();
+	name = addr;
+    msg_name = &name;
+    msg_namelen = SUN_LEN(&name);
+}
+
+void
+Ipc::TypedMsgHdr::getData(int destType, void *raw, size_t size) const
+{
+	Must(type() == destType);
+	Must(size == data.size);
+	xmemcpy(raw, data.raw, size);
+}
+
+void
+Ipc::TypedMsgHdr::putData(int aType, const void *raw, size_t size)
+{
+	Must(size <= sizeof(data.raw));
+	allocData();
+	data.type_ = aType;
+	data.size = size;
+	xmemcpy(data.raw, raw, size);
+}
+
+void
+Ipc::TypedMsgHdr::putFd(int fd)
+{
+	Must(fd >= 0);
+	allocControl();
+
+	const int fdCount = 1;
+
+	struct cmsghdr *cmsg = CMSG_FIRSTHDR(this);
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
+
+    int *fdStore = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+	xmemcpy(fdStore, &fd, fdCount * sizeof(int));
+    msg_controllen = cmsg->cmsg_len;
+}
+
+int
+Ipc::TypedMsgHdr::getFd() const
+{
+	Must(msg_control && msg_controllen);
+
+	struct cmsghdr *cmsg = CMSG_FIRSTHDR(this);
+	Must(cmsg->cmsg_level == SOL_SOCKET);
+	Must(cmsg->cmsg_type == SCM_RIGHTS);
+
+	const int fdCount = 1;
+    const int *fdStore = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+	int fd = -1;
+	xmemcpy(&fd, fdStore, fdCount * sizeof(int));
+	return fd;
+}
+
+void
+Ipc::TypedMsgHdr::prepForReading()
+{
+	xmemset(this, 0, sizeof(*this));
+	allocName();
+	allocData();
+	allocControl();
+}
+
+/// initialize io vector with one io record
+void
+Ipc::TypedMsgHdr::allocData()
+{
+	Must(!msg_iovlen && !msg_iov);
+	msg_iovlen = 1;
+	msg_iov = ios;
+	ios[0].iov_base = &data;
+	ios[0].iov_len = sizeof(data);
+	data.type_ = 0;
+	data.size = 0;
+}
+
+void
+Ipc::TypedMsgHdr::allocName()
+{
+	Must(!msg_name && !msg_namelen);
+    msg_name = &name;
+    msg_namelen = sizeof(name); // is that the right size?
+}
+
+void
+Ipc::TypedMsgHdr::allocControl()
+{
+	Must(!msg_control && !msg_controllen);
+	msg_control = &ctrl;
+	msg_controllen = sizeof(ctrl);
+}

=== added file 'src/ipc/TypedMsgHdr.h'
--- src/ipc/TypedMsgHdr.h	1970-01-01 00:00:00 +0000
+++ src/ipc/TypedMsgHdr.h	2010-05-02 17:58:46 +0000
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_TYPED_MSG_HDR_H
+#define SQUID_IPC_TYPED_MSG_HDR_H
+
+#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+namespace Ipc
+{
+
+/// struct msghdr with a known type, fixed-size I/O and control buffers
+class TypedMsgHdr: public msghdr
+{
+public:
+    TypedMsgHdr();
+    TypedMsgHdr(const TypedMsgHdr &tmh);
+    TypedMsgHdr &operator =(const TypedMsgHdr &tmh);
+
+    // type-safe access to message details
+    int type() const; ///< returns stored type or zero if none
+    void address(const struct sockaddr_un& addr); ///< sets [dest.] address
+    void getData(int ofType, void *raw, size_t size) const; ///< checks type
+    void putData(int aType, const void *raw, size_t size); ///< stores type
+	void putFd(int aFd); ///< stores descriptor
+	int getFd() const; ///< returns descriptor
+
+    /// raw, type-independent access for I/O
+	void prepForReading(); ///< reset and provide all buffers
+	char *raw() { return reinterpret_cast<char*>(this); }
+	const char *raw() const { return reinterpret_cast<const char*>(this); }
+    size_t size() const { return sizeof(*this); } ///< not true message size
+
+private:
+	void sync();
+	void allocData();
+	void allocName();
+	void allocControl();
+
+private:
+	struct sockaddr_un name; ///< same as .msg_name
+
+	struct iovec ios[1]; ///< same as .msg_iov[]
+
+	struct DataBuffer {
+		int type_; ///< Message kind, uses MessageType values
+		size_t size; ///< actual raw data size (for sanity checks)
+		char raw[250]; ///< buffer with type-specific data
+	} data; ///< same as .msg_iov[0].iov_base
+
+	struct CtrlBuffer {
+		char raw[CMSG_SPACE(sizeof(int))]; ///< control buffer space for one fd
+	} ctrl; ///< same as .msg_control
+};
+
+} // namespace Ipc
+
+#endif /* SQUID_IPC_TYPED_MSG_HDR_H */

=== added file 'src/ipc/UdsOp.cc'
--- src/ipc/UdsOp.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/UdsOp.cc	2010-04-29 20:12:03 +0000
@@ -0,0 +1,132 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+
+#include "config.h"
+#include "comm.h"
+#include "CommCalls.h"
+#include "ipc/UdsOp.h"
+
+
+Ipc::UdsOp::UdsOp(const String& pathAddr):
+    AsyncJob("Ipc::UdsOp"),
+    address(PathToAddress(pathAddr)),
+    options(COMM_NONBLOCKING),
+    fd_(-1)
+{
+    debugs(54, 5, HERE << '[' << this << "] pathAddr=" << pathAddr);
+}
+
+Ipc::UdsOp::~UdsOp()
+{
+    debugs(54, 5, HERE << '[' << this << ']');
+    if (fd_ >= 0)
+        comm_close(fd_);
+}
+
+void Ipc::UdsOp::setOptions(int newOptions)
+{
+    options = newOptions;
+}
+
+int Ipc::UdsOp::fd()
+{
+    if (fd_ < 0) {
+        if (options & COMM_DOBIND)
+            unlink(address.sun_path);
+        fd_ = comm_open_uds(SOCK_DGRAM, 0, &address, options);
+        Must(fd_ >= 0);
+    }
+    return fd_;
+}
+
+void Ipc::UdsOp::setTimeout(int seconds, const char *handlerName)
+{
+    AsyncCall::Pointer handler = asyncCall(54,5, handlerName,
+        CommCbMemFunT<UdsOp, CommTimeoutCbParams>(this,
+            &UdsOp::noteTimeout));
+    commSetTimeout(fd(), seconds, handler);
+}
+
+void Ipc::UdsOp::clearTimeout()
+{
+    commSetTimeout(fd(), -1, NULL, NULL); // TODO: add Comm::ClearTimeout(fd)
+}
+
+void Ipc::UdsOp::noteTimeout(const CommTimeoutCbParams &)
+{
+    timedout(); // our kid handles communication timeout
+}
+
+
+struct sockaddr_un
+Ipc::PathToAddress(const String& pathAddr)
+{
+    assert(pathAddr.size() != 0);
+    struct sockaddr_un unixAddr;
+    memset(&unixAddr, 0, sizeof(unixAddr));
+    unixAddr.sun_family = AF_LOCAL;
+    xstrncpy(unixAddr.sun_path, pathAddr.termedBuf(), sizeof(unixAddr.sun_path));
+    return unixAddr;
+}
+
+
+CBDATA_NAMESPACED_CLASS_INIT(Ipc, UdsSender);
+
+Ipc::UdsSender::UdsSender(const String& pathAddr, const TypedMsgHdr& aMessage):
+    UdsOp(pathAddr),
+    message(aMessage),
+    retries(10), // TODO: make configurable?
+    timeout(10), // TODO: make configurable?
+    writing(false)
+{
+    message.address(address);
+}
+
+void Ipc::UdsSender::start()
+{
+    UdsOp::start();
+    write();
+    if (timeout > 0)
+        setTimeout(timeout, "Ipc::UdsSender::noteTimeout");
+}
+
+bool Ipc::UdsSender::doneAll() const
+{
+    return !writing && UdsOp::doneAll();
+}
+
+void Ipc::UdsSender::write()
+{
+    debugs(54, 5, HERE);
+    AsyncCall::Pointer writeHandler = asyncCall(54, 5, "Ipc::UdsSender::wrote",
+        CommCbMemFunT<UdsSender, CommIoCbParams>(this, &UdsSender::wrote));
+    comm_write(fd(), message.raw(), message.size(), writeHandler);
+    writing = true;
+}
+
+void Ipc::UdsSender::wrote(const CommIoCbParams& params)
+{
+    debugs(54, 5, HERE << "FD " << params.fd << " flag " << params.flag << " [" << this << ']');
+    writing = false;
+    if (params.flag != COMM_OK && retries-- > 0) {
+        sleep(1); // do not spend all tries at once; XXX: use an async timed event instead of blocking here; store the time when we started writing so that we do not sleep if not needed?
+        write(); // XXX: should we close on error so that fd() reopens?
+    }
+}
+
+void Ipc::UdsSender::timedout()
+{
+    debugs(54, 5, HERE);
+    mustStop("timedout");
+}
+
+
+void Ipc::SendMessage(const String& toAddress, const TypedMsgHdr &message)
+{
+    AsyncJob::AsyncStart(new UdsSender(toAddress, message));
+}

=== added file 'src/ipc/UdsOp.h'
--- src/ipc/UdsOp.h	1970-01-01 00:00:00 +0000
+++ src/ipc/UdsOp.h	2010-07-01 22:58:45 +0000
@@ -0,0 +1,97 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54    Interprocess Communication
+ *
+ */
+
+#ifndef SQUID_IPC_ASYNCUDSOP_H
+#define SQUID_IPC_ASYNCUDSOP_H
+
+
+#include "SquidString.h"
+#include "base/AsyncJob.h"
+#include "ipc/TypedMsgHdr.h"
+
+class CommTimeoutCbParams;
+class CommIoCbParams;
+
+namespace Ipc
+{
+
+/// code shared by unix-domain socket senders (e.g., UdsSender or Coordinator)
+/// and receivers (e.g. Port or Coordinator)
+class UdsOp: public AsyncJob
+{
+public:
+    UdsOp(const String &pathAddr);
+    virtual ~UdsOp();
+
+public:
+    struct sockaddr_un address; ///< UDS address from path; treat as read-only
+
+protected:
+    virtual void timedout() {} ///< called after setTimeout() if timed out
+
+    int fd(); ///< creates if needed and returns raw UDS socket descriptor
+
+    /// call timedout() if no UDS messages in a given number of seconds
+    void setTimeout(int seconds, const char *handlerName);
+    void clearTimeout(); ///< remove previously set timeout, if any
+
+	void setOptions(int newOptions); ///< changes socket options
+
+private:
+    /// Comm timeout callback; calls timedout()
+    void noteTimeout(const CommTimeoutCbParams &p);
+
+private:
+    int options; ///< UDS options
+    int fd_; ///< UDS descriptor
+
+private:
+    UdsOp(const UdsOp &); // not implemented
+    UdsOp &operator= (const UdsOp &); // not implemented
+};
+
+/// converts human-readable filename path into UDS address
+extern struct sockaddr_un PathToAddress(const String &pathAddr);
+
+
+
+// XXX: move UdsSender code to UdsSender.{cc,h}
+/// attempts to send an IPC message a few times, with a timeout
+class UdsSender: public UdsOp
+{
+public:
+    UdsSender(const String& pathAddr, const TypedMsgHdr& aMessage);
+
+protected:
+    virtual void start(); // UdsOp (AsyncJob) API
+    virtual bool doneAll() const; // UdsOp (AsyncJob) API
+    virtual void timedout(); // UdsOp API
+
+private:
+    void write(); ///< schedule writing
+    void wrote(const CommIoCbParams& params); ///< done writing or error
+
+private:
+    TypedMsgHdr message; ///< what to send
+    int retries; ///< how many times to try after a write error
+    int timeout; ///< total time to send the message
+    bool writing; ///< whether Comm started and did not finish writing
+
+private:
+    UdsSender(const UdsSender&); // not implemented
+    UdsSender& operator= (const UdsSender&); // not implemented
+
+    CBDATA_CLASS2(UdsSender);
+};
+
+
+void SendMessage(const String& toAddress, const TypedMsgHdr& message);
+
+
+}
+
+#endif /* SQUID_IPC_ASYNCUDSOP_H */

=== modified file 'src/main.cc'
--- src/main.cc	2009-12-21 12:00:15 +0000
+++ src/main.cc	2010-06-05 19:08:44 +0000
@@ -55,6 +55,9 @@
 #include "StoreFileSystem.h"
 #include "DiskIO/DiskIOModule.h"
 #include "comm.h"
+#include "ipc/Kids.h"
+#include "ipc/Coordinator.h"
+#include "ipc/Strand.h"
 #if USE_EPOLL
 #include "comm_epoll.h"
 #endif
@@ -124,6 +127,10 @@
 static volatile int do_shutdown = 0;
 static volatile int shutdown_status = 0;
 
+static int RotateSignal = -1;
+static int ReconfigureSignal = -1;
+static int ShutdownSignal = -1;
+
 static void mainRotate(void);
 static void mainReconfigureStart(void);
 static void mainReconfigureFinish(void*);
@@ -195,6 +202,10 @@
         doShutdown(do_shutdown > 0 ? (int) Config.shutdownLifetime : 0);
         do_shutdown = 0;
     }
+    BroadcastSignalIfAny(DebugSignal);
+    BroadcastSignalIfAny(RotateSignal);
+    BroadcastSignalIfAny(ReconfigureSignal);
+    BroadcastSignalIfAny(ShutdownSignal);
 
     PROF_stop(SignalEngine_checkEvents);
     return EVENT_IDLE;
@@ -553,6 +564,7 @@
 rotate_logs(int sig)
 {
     do_rotate = 1;
+    RotateSignal = sig;
 #ifndef _SQUID_MSWIN_
 #if !HAVE_SIGACTION
 
@@ -566,6 +578,7 @@
 reconfigure(int sig)
 {
     do_reconfigure = 1;
+    ReconfigureSignal = sig;
 #ifndef _SQUID_MSWIN_
 #if !HAVE_SIGACTION
 
@@ -578,6 +591,7 @@
 shut_down(int sig)
 {
     do_shutdown = sig == SIGINT ? -1 : 1;
+    ShutdownSignal = sig;
 #ifdef SIGTTIN
 
     if (SIGTTIN == sig)
@@ -607,6 +621,19 @@
 static void
 serverConnectionsOpen(void)
 {
+    if (IamPrimaryProcess()) {
+#if USE_WCCP
+
+    wccpConnectionOpen();
+#endif
+
+#if USE_WCCPv2
+
+    wccp2ConnectionOpen();
+#endif
+    }
+    // Coordinator does not start proxying services
+    if (!IamCoordinatorProcess()) {
     clientOpenListenSockets();
     icpConnectionsOpen();
 #if USE_HTCP
@@ -617,15 +644,6 @@
 
     snmpConnectionOpen();
 #endif
-#if USE_WCCP
-
-    wccpConnectionOpen();
-#endif
-
-#if USE_WCCPv2
-
-    wccp2ConnectionOpen();
-#endif
 
     clientdbInit();
     icmpEngine.Open();
@@ -637,12 +655,25 @@
     carpInit();
     peerUserHashInit();
     peerSourceHashInit();
+    }
 }
 
 static void
 serverConnectionsClose(void)
 {
     assert(shutting_down || reconfiguring);
+
+    if (IamPrimaryProcess()) {
+#if USE_WCCP
+
+    wccpConnectionClose();
+#endif
+#if USE_WCCPv2
+
+    wccp2ConnectionClose();
+#endif
+    }
+    if (!IamCoordinatorProcess()) {
     clientHttpConnectionsClose();
     icpConnectionShutdown();
 #if USE_HTCP
@@ -655,16 +686,9 @@
 
     snmpConnectionShutdown();
 #endif
-#if USE_WCCP
-
-    wccpConnectionClose();
-#endif
-#if USE_WCCPv2
-
-    wccp2ConnectionClose();
-#endif
 
     asnFreeMemory();
+    }
 }
 
 static void
@@ -721,10 +745,17 @@
         Config2.onoff.enable_purge = 2;
 
     // parse the config returns a count of errors encountered.
+    const int oldMainProcesses = Config.main_processes;
     if ( parseConfigFile(ConfigFile) != 0) {
         // for now any errors are a fatal condition...
         self_destruct();
     }
+    if (oldMainProcesses != Config.main_processes) {
+        debugs(1, DBG_CRITICAL, "WARNING: Changing 'main_processes' (from " <<
+            oldMainProcesses << " to " << Config.main_processes <<
+            ") is not supported and ignored");
+        Config.main_processes = oldMainProcesses;
+    }
 
     setUmask(Config.umask);
     Mem::Report();
@@ -753,6 +784,8 @@
     redirectInit();
     authenticateInit(&Config.authConfiguration);
     externalAclInit();
+
+    if (IamPrimaryProcess()) {
 #if USE_WCCP
 
     wccpInit();
@@ -761,6 +794,7 @@
 
     wccp2Init();
 #endif
+    }
 
     serverConnectionsOpen();
 
@@ -1015,6 +1049,7 @@
         //   moved to PconnModule::PconnModule()
     }
 
+    if (IamPrimaryProcess()) {
 #if USE_WCCP
     wccpInit();
 
@@ -1024,6 +1059,7 @@
     wccp2Init();
 
 #endif
+    }
 
     serverConnectionsOpen();
 
@@ -1149,9 +1185,28 @@
     return -1; // not reached
 }
 
+/// computes name and ID for the current kid process
+static void
+ConfigureCurrentKid(const char *processName)
+{
+	// kids are marked with parenthesis around their process names
+    if (processName && processName[0] == '(') {
+        if (const char *idStart = strrchr(processName, '-')) {
+            KidIdentifier = atoi(idStart + 1);
+            const int nameLen = idStart - (processName + 1);
+            xstrncpy(KidName, processName + 1, nameLen + 1);
+        }
+    } else {
+        xstrncpy(KidName, APP_SHORTNAME, sizeof(KidName));
+        KidIdentifier = 0;
+    }
+}
+
 int
 SquidMain(int argc, char **argv)
 {
+    ConfigureCurrentKid(argv[0]);
+
 #ifdef _SQUID_WIN32_
 
     int WIN32_init_err;
@@ -1320,7 +1375,7 @@
         return 0;
     }
 
-    if (!opt_no_daemon)
+    if (!opt_no_daemon && Config.main_processes > 0)
         watch_child(argv);
 
     setMaxFD();
@@ -1376,6 +1431,11 @@
 
     mainLoop.setTimeService(&time_engine);
 
+    if (IamCoordinatorProcess())
+        AsyncJob::AsyncStart(Ipc::Coordinator::Instance());
+    else if (UsingSmp() && IamWorkerProcess())
+        AsyncJob::AsyncStart(new Ipc::Strand);
+
     /* at this point we are finished the synchronous startup. */
     starting_up = 0;
 
@@ -1477,11 +1537,11 @@
         do {
 #ifdef _SQUID_NEXT_
             union wait status;
-            rpid = wait3(&status, 0, NULL);
+            rpid = wait4(cpid, &status, 0, NULL);
 #else
 
             int status;
-            rpid = waitpid(-1, &status, 0);
+            rpid = waitpid(cpid, &status, 0);
 #endif
 
         } while (rpid != cpid);
@@ -1493,6 +1553,10 @@
 static int
 checkRunningPid(void)
 {
+    // master process must start alone, but its kids processes may co-exist
+    if (!IamMasterProcess())
+        return 0;
+
     pid_t pid;
 
     if (!debug_log)
@@ -1516,9 +1580,6 @@
 {
 #ifndef _SQUID_MSWIN_
     char *prog;
-    int failcount = 0;
-    time_t start;
-    time_t stop;
 #ifdef _SQUID_NEXT_
 
     union wait status;
@@ -1535,7 +1596,7 @@
 
     int nullfd;
 
-    if (*(argv[0]) == '(')
+    if (!IamMasterProcess())
         return;
 
     openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
@@ -1577,25 +1638,39 @@
         dup2(nullfd, 2);
     }
 
+    if (Config.main_processes > 128) {
+        syslog(LOG_ALERT, "Suspiciously high main_processes value: %d",
+            Config.main_processes);
+        // but we keep going in hope that user knows best
+	}
+    TheKids.init(Config.main_processes);
+
+    // keep [re]starting kids until it is time to quit
     for (;;) {
         mainStartScript(argv[0]);
 
-        if ((pid = fork()) == 0) {
-            /* child */
-            openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
-            prog = xstrdup(argv[0]);
-            argv[0] = xstrdup("(squid)");
-            execvp(prog, argv);
-            syslog(LOG_ALERT, "execvp failed: %s", xstrerror());
+        // start each kid that needs to be [re]started; once
+        for (int i = TheKids.count() - 1; i >= 0; --i) {
+            Kid& kid = TheKids.get(i);
+            if (kid.hopeless() || kid.exitedHappy() || kid.running())
+                continue;
+
+            if ((pid = fork()) == 0) {
+                /* child */
+                openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
+                prog = xstrdup(argv[0]);    /* XXX: leak */
+                argv[0] = const_cast<char*>(kid.name().termedBuf());
+                execvp(prog, argv);
+                syslog(LOG_ALERT, "execvp failed: %s", xstrerror());
+            }
+
+            kid.start(pid);
+            syslog(LOG_NOTICE, "Squid Parent: child process %d started", pid);
         }
 
         /* parent */
         openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
 
-        syslog(LOG_NOTICE, "Squid Parent: child process %d started", pid);
-
-        time(&start);
-
         squid_signal(SIGINT, SIG_IGN, SA_RESTART);
 
 #ifdef _SQUID_NEXT_
@@ -1607,51 +1682,48 @@
         pid = waitpid(-1, &status, 0);
 
 #endif
-
-        time(&stop);
-
-        if (WIFEXITED(status)) {
-            syslog(LOG_NOTICE,
-                   "Squid Parent: child process %d exited with status %d",
-                   pid, WEXITSTATUS(status));
-        } else if (WIFSIGNALED(status)) {
-            syslog(LOG_NOTICE,
-                   "Squid Parent: child process %d exited due to signal %d with status %d",
-                   pid, WTERMSIG(status), WEXITSTATUS(status));
-        } else {
-            syslog(LOG_NOTICE, "Squid Parent: child process %d exited", pid);
+        // Loop to collect all stopped kids before we go to sleep below.
+        do
+        {
+            Kid* kid = TheKids.find(pid);
+            if (kid) {
+                kid->stop(status);
+                if (kid->calledExit()) {
+                    syslog(LOG_NOTICE,
+                       "Squid Parent: child process %d exited with status %d",
+                        kid->getPid(), kid->exitStatus());
+                } else if (kid->signaled()) {
+                    syslog(LOG_NOTICE,
+                       "Squid Parent: child process %d exited due to signal %d with status %d",
+                        kid->getPid(), kid->termSignal(), kid->exitStatus());
+                } else {
+                    syslog(LOG_NOTICE, "Squid Parent: child process %d exited", kid->getPid());
+                }
+            } else {
+                syslog(LOG_NOTICE, "Squid Parent: unknown child process %d exited", pid);
+            }
+#ifdef _SQUID_NEXT_
+        } while ((pid = wait3(&status, WNOHANG, NULL)) > 0);
+#else
+        } while ((pid = waitpid(-1, &status, WNOHANG)) > 0);
+#endif
+
+        if (TheKids.allExitedHappy()) {
+            exit(0);
         }
 
-        if (stop - start < 10)
-            failcount++;
-        else
-            failcount = 0;
-
-        if (failcount == 5) {
+        if (TheKids.allHopeless()) {
             syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
             exit(1);
         }
 
-        if (WIFEXITED(status))
-            if (WEXITSTATUS(status) == 0)
-                exit(0);
-
-        if (WIFSIGNALED(status)) {
-            switch (WTERMSIG(status)) {
-
-            case SIGKILL:
-                exit(0);
-                break;
-
-            case SIGINT:
-            case SIGTERM:
-                syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
-                exit(1);
-                break;
-
-            default:
-                break;
-            }
+        if (TheKids.allSignaled(SIGKILL)) {
+            exit(0);
+        }
+
+        if (TheKids.allSignaled(SIGINT) || TheKids.allSignaled(SIGTERM)) {
+            syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
+            exit(1);
         }
 
         squid_signal(SIGINT, SIG_DFL, SA_RESTART);
@@ -1789,10 +1861,12 @@
 
 #endif
 
-    if (Config.pidFilename && strcmp(Config.pidFilename, "none") != 0) {
-        enter_suid();
-        safeunlink(Config.pidFilename, 0);
-        leave_suid();
+    if (IamPrimaryProcess()) {
+        if (Config.pidFilename && strcmp(Config.pidFilename, "none") != 0) {
+            enter_suid();
+            safeunlink(Config.pidFilename, 0);
+            leave_suid();
+        }
     }
 
     debugs(1, 1, "Squid Cache (Version " << version_string << "): Exiting normally.");

=== modified file 'src/protos.h'
--- src/protos.h	2010-01-14 09:18:00 +0000
+++ src/protos.h	2010-06-05 19:08:44 +0000
@@ -575,6 +575,22 @@
 SQUIDCEXTERN pid_t readPidFile(void);
 SQUIDCEXTERN void keepCapabilities(void);
 
+SQUIDCEXTERN void BroadcastSignalIfAny(int& sig);
+/// whether the current process is the parent of all other Squid processes
+SQUIDCEXTERN bool IamMasterProcess();
+/** 
+    whether the current process is dedicated to doing things that only
+    a single process should do, such as PID file maintenance and WCCP
+*/
+SQUIDCEXTERN bool IamPrimaryProcess();
+/// whether the current process coordinates worker processes
+SQUIDCEXTERN bool IamCoordinatorProcess();
+/// whether the current process handles HTTP transactions and such
+SQUIDCEXTERN bool IamWorkerProcess();
+/// Whether there should be more than one worker process running
+SQUIDCEXTERN bool UsingSmp(); // try using specific Iam*() checks above first
+SQUIDCEXTERN int DebugSignal;
+
 /* AYJ debugs function to show locations being reset with memset() */
 SQUIDCEXTERN void *xmemset(void *dst, int, size_t);
 

=== modified file 'src/snmp_core.cc'
--- src/snmp_core.cc	2010-01-14 09:18:00 +0000
+++ src/snmp_core.cc	2010-06-05 18:32:22 +0000
@@ -34,10 +34,30 @@
 #include "cache_snmp.h"
 #include "acl/FilledChecklist.h"
 #include "ip/IpAddress.h"
+#include "ipc/StartListening.h"
 
 #define SNMP_REQUEST_SIZE 4096
 #define MAX_PROTOSTAT 5
 
+
+/// dials snmpConnectionOpened call
+class SnmpListeningStartedDialer: public CallDialer,
+    public Ipc::StartListeningCb
+{
+public:
+    typedef void (*Handler)(int fd, int errNo);
+    SnmpListeningStartedDialer(Handler aHandler): handler(aHandler) {}
+
+    virtual void print(std::ostream &os) const { startPrint(os) << ')'; }
+
+    virtual bool canDial(AsyncCall &) const { return true; }
+    virtual void dial(AsyncCall &) { (handler)(fd, errNo); }
+
+public:
+    Handler handler;
+};
+
+
 IpAddress theOutSNMPAddr;
 
 typedef struct _mib_tree_entry mib_tree_entry;
@@ -58,6 +78,9 @@
 mib_tree_entry *mib_tree_head;
 mib_tree_entry *mib_tree_last;
 
+static void snmpIncomingConnectionOpened(int fd, int errNo);
+static void snmpOutgoingConnectionOpened(int fd, int errNo);
+
 static mib_tree_entry * snmpAddNodeStr(const char *base_str, int o, oid_ParseFn * parsefunction, instance_Fn * instancefunction);
 static mib_tree_entry *snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, int children,...);
 static oid *snmpCreateOid(int length,...);
@@ -278,54 +301,71 @@
 void
 snmpConnectionOpen(void)
 {
-    struct addrinfo *xaddr = NULL;
-    int x;
-
     debugs(49, 5, "snmpConnectionOpen: Called");
 
     if (Config.Port.snmp > 0) {
         Config.Addrs.snmp_incoming.SetPort(Config.Port.snmp);
-        enter_suid();
-        theInSnmpConnection = comm_open_listener(SOCK_DGRAM,
+
+        AsyncCall::Pointer call = asyncCall(49, 2,
+            "snmpIncomingConnectionOpened",
+            SnmpListeningStartedDialer(&snmpIncomingConnectionOpened));
+
+        Ipc::StartListening(SOCK_DGRAM,
                               IPPROTO_UDP,
                               Config.Addrs.snmp_incoming,
                               COMM_NONBLOCKING,
-                              "SNMP Port");
-        leave_suid();
-
-        if (theInSnmpConnection < 0)
-            fatal("Cannot open SNMP Port");
-
-        commSetSelect(theInSnmpConnection, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
-
-        debugs(1, 1, "Accepting SNMP messages on " << Config.Addrs.snmp_incoming << ", FD " << theInSnmpConnection << ".");
+                              Ipc::fdnInSnmpSocket, call);
 
         if (!Config.Addrs.snmp_outgoing.IsNoAddr()) {
             Config.Addrs.snmp_outgoing.SetPort(Config.Port.snmp);
-            enter_suid();
-            theOutSnmpConnection = comm_open_listener(SOCK_DGRAM,
+
+            AsyncCall::Pointer call = asyncCall(49, 2,
+                "snmpOutgoingConnectionOpened",
+                SnmpListeningStartedDialer(&snmpOutgoingConnectionOpened));
+
+             Ipc::StartListening(SOCK_DGRAM,
                                    IPPROTO_UDP,
                                    Config.Addrs.snmp_outgoing,
                                    COMM_NONBLOCKING,
-                                   "SNMP Port");
-            leave_suid();
-
-            if (theOutSnmpConnection < 0)
-                fatal("Cannot open Outgoing SNMP Port");
-
-            commSetSelect(theOutSnmpConnection,
-                          COMM_SELECT_READ,
-                          snmpHandleUdp,
-                          NULL, 0);
-
-            debugs(1, 1, "Outgoing SNMP messages on " << Config.Addrs.snmp_outgoing << ", FD " << theOutSnmpConnection << ".");
-
-            fd_note(theOutSnmpConnection, "Outgoing SNMP socket");
-
-            fd_note(theInSnmpConnection, "Incoming SNMP socket");
-        } else {
-            theOutSnmpConnection = theInSnmpConnection;
+                                   Ipc::fdnOutSnmpSocket, call);
         }
+    }
+}
+
+static void
+snmpIncomingConnectionOpened(int fd, int errNo)
+{
+    theInSnmpConnection = fd;
+    if (theInSnmpConnection < 0)
+        fatal("Cannot open Incoming SNMP Port");
+
+    commSetSelect(theInSnmpConnection, COMM_SELECT_READ, snmpHandleUdp, NULL,
+        0);
+
+    debugs(1, 1, "Accepting SNMP messages on " << Config.Addrs.snmp_incoming <<
+        ", FD " << theInSnmpConnection << ".");
+
+    if (Config.Addrs.snmp_outgoing.IsNoAddr())
+        theOutSnmpConnection = theInSnmpConnection;
+}
+
+static void
+snmpOutgoingConnectionOpened(int fd, int errNo)
+{
+    theOutSnmpConnection = fd;
+    if (theOutSnmpConnection < 0)
+        fatal("Cannot open Outgoing SNMP Port");
+
+    commSetSelect(theOutSnmpConnection, COMM_SELECT_READ, snmpHandleUdp, NULL,
+        0);
+
+    debugs(1, 1, "Outgoing SNMP messages on " << Config.Addrs.snmp_outgoing <<
+        ", FD " << theOutSnmpConnection << ".");
+
+    {
+        struct addrinfo *xaddr = NULL;
+        int x;
+
 
         theOutSNMPAddr.SetEmpty();
 

=== modified file 'src/structs.h'
--- src/structs.h	2010-03-03 09:38:49 +0000
+++ src/structs.h	2010-03-04 06:25:18 +0000
@@ -617,6 +617,7 @@
 
     char *accept_filter;
     int umask;
+    int main_processes;
 
 #if USE_LOADABLE_MODULES
     wordlist *loadable_module_names;

=== modified file 'src/tools.cc'
--- src/tools.cc	2009-12-02 22:39:11 +0000
+++ src/tools.cc	2010-06-05 19:08:44 +0000
@@ -41,6 +41,8 @@
 #include "SquidMath.h"
 #include "SquidTime.h"
 #include "ip/IpIntercept.h"
+#include "ipc/Kids.h"
+#include "ipc/Coordinator.h"
 
 #if HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>
@@ -64,6 +66,7 @@
 extern void log_trace_init(char *);
 #endif
 static void restoreCapabilities(int keep);
+int DebugSignal = -1;
 
 #ifdef _SQUID_LINUX_
 /* Workaround for crappy glic header files */
@@ -397,6 +400,15 @@
     abort();
 }
 
+void
+BroadcastSignalIfAny(int& sig)
+{
+    if (sig > 0) {
+        if (IamCoordinatorProcess())
+            Ipc::Coordinator::Instance()->broadcastSignal(sig);
+        sig = -1;
+    }
+}
 
 void
 sigusr2_handle(int sig)
@@ -404,6 +416,8 @@
     static int state = 0;
     /* no debugs() here; bad things happen if the signal is delivered during _db_print() */
 
+    DebugSignal = sig;
+
     if (state == 0) {
 #ifndef MEM_GEN_TRACE
         Debug::parseOptions("ALL,7");
@@ -790,6 +804,50 @@
 #endif
 }
 
+bool
+IamMasterProcess()
+{
+    return KidIdentifier == 0;
+}
+
+bool
+IamWorkerProcess()
+{
+    // when there is only one process, it has to be the worker
+    if (opt_no_daemon || Config.main_processes == 0)
+        return true;
+
+    return 0 < KidIdentifier && KidIdentifier <= Config.main_processes;
+}
+
+bool
+UsingSmp()
+{
+    return !opt_no_daemon && Config.main_processes > 1;
+}
+
+bool
+IamCoordinatorProcess()
+{
+    return UsingSmp() && KidIdentifier == Config.main_processes + 1;
+}
+
+bool
+IamPrimaryProcess()
+{
+    // when there is only one process, it has to be primary
+    if (opt_no_daemon || Config.main_processes == 0)
+        return true;
+
+    // when there is a master and worker process, the master delegates
+    // primary functions to its only kid
+    if (Config.main_processes == 1)
+        return IamWorkerProcess();
+
+    // in SMP mode, multiple kids delegate primary functions to the coordinator
+    return IamCoordinatorProcess();
+}
+
 void
 writePidFile(void)
 {
@@ -798,6 +856,9 @@
     mode_t old_umask;
     char buf[32];
 
+    if (!IamPrimaryProcess())
+        return;
+
     if ((f = Config.pidFilename) == NULL)
         return;
 

=== modified file 'test-suite/testheaders.sh'
--- test-suite/testheaders.sh	2009-11-19 23:32:21 +0000
+++ test-suite/testheaders.sh	2010-05-02 18:09:21 +0000
@@ -16,6 +16,8 @@
 	dir="${2}"
 fi
 
+exitCode=0
+
 for f in `cd ${dir} && ls -1 *.h 2>/dev/null`; do
 	echo -n "Testing ${dir}/${f} ..."
 	hdr=`echo "${f}" | sed s/.h//`
@@ -32,11 +34,14 @@
 	fi
 	if [ ! -f testHeaderDeps_${hdr}.o ]; then
 		rm testHeaders
-		exit 1
-	fi
+		exitCode=1
+	else
 	echo "OK."
 	# unit-tests require an app to run.
 	# our most-recent object suits this purpose.
 	# let's link or some tests will fail
 	${cc} ./testHeaderDeps_${hdr}.o -o ./testHeaders
+	fi
 done
+
+exit $exitCode

# Begin bundle
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

