--- upstream/aclocal.m4	1970-01-01 01:00:00.000000000 +0100
+++ lennart/aclocal.m4	2005-11-18 04:19:18.000000000 +0100
@@ -0,0 +1,171 @@
+# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005  Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+		
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$PKG_CONFIG"; then
+    if test -n "$$1"; then
+        pkg_cv_[]$1="$$1"
+    else
+        PKG_CHECK_EXISTS([$3],
+                         [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+			 [pkg_failed=yes])
+    fi
+else
+	pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+		[$4])
+elif test $pkg_failed = untried; then
+	ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])],
+		[$4])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+m4_include([acinclude.m4])
--- upstream/Makefile.in	2005-11-18 16:15:40.000000000 +0100
+++ lennart/Makefile.in	2005-11-18 01:14:43.000000000 +0100
@@ -171,6 +171,7 @@
 	src/ssh.o src/state.o src/strip.o				\
 	src/timefile.o src/traceenv.o					\
 	src/where.o							\
+	@ZEROCONF_DISTCC_OBJS@						\
 	$(common_obj)
 
 distccd_obj = src/access.o						\
@@ -178,6 +179,7 @@
 	src/ncpus.o							\
 	src/prefork.o							\
 	src/serve.o src/setuid.o src/srvnet.o src/srvrpc.o src/state.o	\
+	@ZEROCONF_DISTCCD_OBJS@						\
 	$(common_obj) @BUILD_POPT@
 
 # Objects that need to be linked in to build monitors
--- upstream/src/distcc.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/distcc.c	2005-11-18 01:14:43.000000000 +0100
@@ -83,6 +83,9 @@
 "   COMPILER                   defaults to \"cc\"\n"
 "   --help                     explain usage and exit\n"
 "   --version                  show version and exit\n"
+"   --show-hosts               show host list and exit\n"
+"   -j                         calculate the concurrency level from\n"
+"                              the host list.\n"
 "\n"
 "Environment variables:\n"
 "   See the manual page for a complete list.\n"
@@ -135,7 +138,46 @@
     signal(SIGHUP, &dcc_client_signalled);
 }
 
+static void dcc_free_hostlist(struct dcc_hostdef *list) {
+    while (list) {
+        struct dcc_hostdef *l = list;
+        list = list->next;
+        dcc_free_hostdef(l);
+    }
+}
+
+static void dcc_show_hosts(void) {
+    struct dcc_hostdef *list, *l;
+    int nhosts;
+    
+    if (dcc_get_hostlist(&list, &nhosts) != 0) {
+        rs_log_crit("Failed to get host list");
+        return;
+    }
+
+    for (l = list; l; l = l->next)
+        printf("%s\n", l->hostdef_string);
+
+    dcc_free_hostlist(list);
+}
+
+static void dcc_concurrency_level(void) {
+    struct dcc_hostdef *list, *l;
+    int nhosts;
+    int nslots = 0;
+    
+    if (dcc_get_hostlist(&list, &nhosts) != 0) {
+        rs_log_crit("Failed to get host list");
+        return;
+    }
+
+    for (l = list; l; l = l->next)
+        nslots += l->n_slots;
 
+    dcc_free_hostlist(list);
+
+    printf("%i\n", nslots);
+}
 
 /**
  * distcc client entry point.
@@ -182,6 +224,18 @@
             ret = 0;
             goto out;
         }
+
+        if (!strcmp(argv[1], "--show-hosts")) {
+            dcc_show_hosts();
+            ret = 0;
+            goto out;
+        }
+
+        if (!strcmp(argv[1], "-j")) {
+            dcc_concurrency_level();
+            ret = 0;
+            goto out;
+        }
         
         dcc_find_compiler(argv, &compiler_args);
         /* compiler_args is now respectively either "cc -c hello.c" or
--- upstream/src/distcc.h	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/distcc.h	2005-11-18 01:14:43.000000000 +0100
@@ -112,7 +112,7 @@
                         int *ret_nhosts);
 int dcc_parse_hosts(const char *where, const char *source_name,
                     struct dcc_hostdef **ret_list,
-                    int *ret_nhosts);
+                    int *ret_nhosts, struct dcc_hostdef **ret_prev);
 
 /* ncpu.c */
 int dcc_ncpus(int *);
@@ -226,6 +226,7 @@
 int dcc_make_tmpnam(const char *, const char *suffix, char **);
 
 int dcc_mkdir(const char *path) WARN_UNUSED;
+int dcc_get_subdir(const char *name, char **path_ret) WARN_UNUSED;
 int dcc_get_lock_dir(char **path_ret) WARN_UNUSED;
 int dcc_get_state_dir(char **path_ret) WARN_UNUSED;
 int dcc_get_top_dir(char **path_ret) WARN_UNUSED;
--- upstream/src/dopt.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/dopt.c	2005-11-18 04:14:26.000000000 +0100
@@ -93,6 +93,10 @@
     opt_log_level
 };
 
+#ifdef HAVE_AVAHI
+/* Flag for enabling/disabling Zeroconf using Avahi */
+int opt_zeroconf = 0;
+#endif
 
 const struct poptOption options[] = {
     { "allow", 'a',      POPT_ARG_STRING, 0, 'a', 0, 0 },
@@ -115,6 +119,9 @@
     { "verbose", 0,      POPT_ARG_NONE, 0, 'v', 0, 0 },
     { "version", 0,      POPT_ARG_NONE, 0, 'V', 0, 0 },
     { "wizard", 'W',     POPT_ARG_NONE, 0, 'W', 0, 0 },
+#ifdef HAVE_AVAHI    
+    { "zeroconf", 0,     POPT_ARG_NONE, &opt_zeroconf, 0, 0, 0 },
+#endif    
     { 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -137,6 +144,9 @@
 "    -p, --port PORT            TCP port to listen on\n"
 "    --listen ADDRESS           IP address to listen on\n"
 "    -a, --allow IP[/BITS]      client address access control\n"
+#ifdef HAVE_AVAHI
+"    --zeroconf                 register via mDNS/DNS-SD\n"
+#endif
 "  Debug and trace:\n"
 "    --log-level=LEVEL          set detail level for log file\n"
 "      levels: critical, error, warning, notice, info, debug\n"
--- upstream/src/dopt.h	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/dopt.h	2005-11-18 02:27:45.000000000 +0100
@@ -38,3 +38,7 @@
 extern int opt_lifetime;
 extern char *opt_listen_addr;
 extern int opt_niceness;
+
+#ifdef HAVE_AVAHI
+extern int opt_zeroconf;
+#endif
--- upstream/src/dparent.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/dparent.c	2005-11-18 04:13:23.000000000 +0100
@@ -70,6 +70,7 @@
 #include "types.h"
 #include "daemon.h"
 #include "netutil.h"
+#include "zeroconf.h"
 
 static void dcc_nofork_parent(int listen_fd) NORETURN;
 static void dcc_detach(void);
@@ -94,6 +95,9 @@
     int listen_fd;
     int n_cpus;
     int ret;
+#ifdef HAVE_AVAHI
+    void *avahi = NULL;
+#endif
 
     if ((ret = dcc_socket_listen(arg_port, &listen_fd, opt_listen_addr)) != 0)
         return ret;
@@ -131,6 +135,14 @@
     /* Don't catch signals until we've detached or created a process group. */
     dcc_daemon_catch_signals();
 
+#ifdef HAVE_AVAHI      
+    /* Zeroconf registration */
+    if (opt_zeroconf) {
+        if (!(avahi = dcc_zeroconf_register((uint16_t) arg_port, n_cpus)))
+            return EXIT_CONNECT_FAILED;
+    }
+#endif
+        
     /* This is called in the master daemon, whether that is detached or
      * not.  */
     dcc_master_pid = getpid();
@@ -138,10 +150,21 @@
     if (opt_no_fork) {
         dcc_log_daemon_started("non-forking daemon");   
         dcc_nofork_parent(listen_fd);
+        ret = 0;
     } else {
         dcc_log_daemon_started("preforking daemon");
-        return dcc_preforking_parent(listen_fd);
+        ret = dcc_preforking_parent(listen_fd);
     }
+
+#ifdef HAVE_AVAHI
+    /* Remove zeroconf registration */
+    if (opt_zeroconf) {
+        if (dcc_zeroconf_unregister(avahi) != 0)
+            return EXIT_CONNECT_FAILED;
+    }
+#endif
+    
+    return ret;
 }
 
 
--- upstream/src/help.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/help.c	2005-11-18 02:27:45.000000000 +0100
@@ -62,6 +62,9 @@
 "distcc comes with ABSOLUTELY NO WARRANTY.  distcc is free software, and\n"
 "you may use, modify and redistribute it under the terms of the GNU \n"
 "General Public License version 2 or later.\n"
+#ifdef HAVE_AVAHI
+"\nBuilt with Zeroconf support.\n"
+#endif
 "\n"
            ,
            prog, PACKAGE_VERSION, GNU_HOST, DISTCC_DEFAULT_PORT,
--- upstream/src/hostfile.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/hostfile.c	2005-11-18 01:14:43.000000000 +0100
@@ -59,7 +59,7 @@
     if ((ret = dcc_load_file_string(fname, &body)) != 0)
         return ret;
 
-    ret = dcc_parse_hosts(body, fname, ret_list, ret_nhosts);
+    ret = dcc_parse_hosts(body, fname, ret_list, ret_nhosts, NULL);
 
     free(body);
 
--- upstream/src/hosts.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/hosts.c	2005-11-18 02:27:45.000000000 +0100
@@ -96,6 +96,10 @@
 #include "hosts.h"
 #include "exitcode.h"
 #include "snprintf.h"
+#ifdef HAVE_AVAHI
+#include "zeroconf.h"
+#define ZEROCONF_MAGIC "+zeroconf"
+#endif
 
 const int dcc_default_port = DISTCC_DEFAULT_PORT;
 
@@ -134,9 +138,12 @@
     char *path, *top;
     int ret;
 
+    *ret_list = NULL;
+    *ret_nhosts = 0;
+
     if ((env = getenv("DISTCC_HOSTS")) != NULL) {
         rs_trace("read hosts from environment");
-        return dcc_parse_hosts(env, "$DISTCC_HOSTS", ret_list, ret_nhosts);
+        return dcc_parse_hosts(env, "$DISTCC_HOSTS", ret_list, ret_nhosts, NULL);
     }
 
     /* $DISTCC_DIR or ~/.distcc */
@@ -163,7 +170,7 @@
         rs_trace("not reading %s: %s", path, strerror(errno));
         free(path);
     }
-    
+
     /* FIXME: Clearer message? */
     rs_log_warning("no hostlist is set; can't distribute work");
 
@@ -346,17 +353,19 @@
  **/
 int dcc_parse_hosts(const char *where, const char *source_name,
                     struct dcc_hostdef **ret_list,
-                    int *ret_nhosts)
+                    int *ret_nhosts, struct dcc_hostdef **ret_prev)
 {
     int ret;
-    struct dcc_hostdef *prev, *curr;
+    struct dcc_hostdef *curr, *_prev;
+
+    if (!ret_prev) {
+        ret_prev = &_prev;
+        _prev = NULL;
+    }
 
     /* TODO: Check for '/' in places where it might cause trouble with
      * a lock file name. */
 
-    prev = NULL;
-    *ret_list = NULL;
-    *ret_nhosts = 0;
     /* A simple, hardcoded scanner.  Some of the GNU routines might be
      * useful here, but they won't work on less capable systems.
      *
@@ -390,6 +399,15 @@
         token_start = where;
         token_len = strcspn(where, " #\t\n\f\r");
 
+#ifdef HAVE_AVAHI
+        if (token_len == sizeof(ZEROCONF_MAGIC)-1 && 
+            !strncmp(token_start, ZEROCONF_MAGIC, (unsigned) token_len)) {
+            if ((ret = dcc_zeroconf_add_hosts(ret_list, ret_nhosts, 4, ret_prev) != 0))
+                return ret;
+            goto skip;
+        }
+#endif
+
         /* Allocate new list item */
         curr = calloc(1, sizeof(struct dcc_hostdef));
         if (!curr) {
@@ -404,8 +422,8 @@
         }
 
         /* Link into list */
-        if (prev) {
-            prev->next = curr;
+        if (*ret_prev) {
+            (*ret_prev)->next = curr;
         } else {
             *ret_list = curr;   /* first */
         }
@@ -434,10 +452,15 @@
                 return ret;
         }
 
+        (*ret_nhosts)++;
+        *ret_prev = curr;
+
+#ifdef HAVE_AVAHI
+        skip:
+#endif
+        
         /* continue to next token if any */
         where = token_start + token_len;
-        prev = curr;
-        (*ret_nhosts)++;
     }
     
     if (*ret_nhosts) {
--- upstream/src/io.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/io.c	2005-11-18 01:14:43.000000000 +0100
@@ -163,7 +163,7 @@
                 return ret;
             else
                 continue;
-        } else if (r == -1 && errno == EAGAIN) {
+        } else if (r == -1 && errno == EINTR) {
             continue;
         } else if (r == -1) {
 	    rs_log_error("failed to read: %s", strerror(errno));
@@ -205,9 +205,6 @@
         } else if (r == -1) {
             rs_log_error("failed to write: %s", strerror(errno));
             return EXIT_IO_ERROR;
-        } else if (r == 0) {
-            rs_log_error("unexpected eof on fd%d", fd);
-            return EXIT_TRUNCATED;
         } else {
             buf = &((char *) buf)[r];
             len -= r;
--- upstream/src/tempfile.c	2005-11-18 16:15:40.000000000 +0100
+++ lennart/src/tempfile.c	2005-11-18 01:14:43.000000000 +0100
@@ -161,7 +161,7 @@
  * Return a subdirectory of the DISTCC_DIR of the given name, making
  * sure that the directory exists.
  **/
-static int dcc_get_subdir(const char *name,
+int dcc_get_subdir(const char *name,
                           char **dir_ret)
 {
     int ret;
--- upstream/configure.ac	2005-11-18 16:15:40.000000000 +0100
+++ lennart/configure.ac	2005-11-18 04:18:07.000000000 +0100
@@ -388,6 +388,23 @@
     AC_DEFINE(HAVE_SOCKADDR_STORAGE, 1, [define if you have struct sockaddr_storage]),,
     [#include <sys/socket.h>])
 
+dnl check for avahi
+PKG_CHECK_MODULES(AVAHI, [avahi-client >= 0.6],
+[AC_DEFINE(HAVE_AVAHI, 1, [defined if Avahi is available])
+CFLAGS="$CFLAGS $AVAHI_CFLAGS"
+LIBS="$LIBS $AVAHI_LIBS"
+ZEROCONF_DISTCC_OBJS="src/zeroconf.o"
+ZEROCONF_DISTCCD_OBJS="src/zeroconf-reg.o"],
+[ZEROCONF_DISTCC_OBJS=""
+ZEROCONF_DISTCCD_OBJS=""])
+AC_SUBST(ZEROCONF_DISTCC_OBJS)
+AC_SUBST(ZEROCONF_DISTCCD_OBJS)
+
+ACX_PTHREAD
+LIBS="$PTHREAD_LIBS $LIBS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+CC="$PTHREAD_CC"
+
 dnl ##### Output
 AC_SUBST(docdir)
 AC_SUBST(CFLAGS)
--- upstream/acinclude.m4	1970-01-01 01:00:00.000000000 +0100
+++ lennart/acinclude.m4	2005-11-18 04:17:08.000000000 +0100
@@ -0,0 +1,235 @@
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl        LIBS="$PTHREAD_LIBS $LIBS"
+dnl        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl        CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2005-01-14
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test x"$acx_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads pthread none -Kthread -kthread lthread -pthread -pthreads -mthreads --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+        *solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthread or
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+        ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+		pthread-config)
+		AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+		if test x"$acx_pthread_config" = xno; then continue; fi
+		PTHREAD_CFLAGS="`pthread-config --cflags`"
+		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+		;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_TRY_LINK([#include <pthread.h>],
+                    [pthread_t th; pthread_join(th, 0);
+                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
+                     pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+                    [acx_pthread_ok=yes])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test "x$acx_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+	AC_MSG_CHECKING([for joinable pthread attribute])
+	attr_name=unknown
+	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr;],
+                        [attr_name=$attr; break])
+	done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case "${host_cpu}-${host_os}" in
+            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with cc_r
+        AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        acx_pthread_ok=no
+        $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
--- upstream/src/zeroconf.h	1970-01-01 01:00:00.000000000 +0100
+++ lennart/src/zeroconf.h	2005-11-18 04:06:29.000000000 +0100
@@ -0,0 +1,13 @@
+#ifndef foozeroconfhfoo
+#define foozeroconfhfoo
+
+#include <inttypes.h>
+
+int dcc_zeroconf_add_hosts(struct dcc_hostdef **re_list, int *ret_nhosts, int slots, struct dcc_hostdef **ret_prev);
+
+void * dcc_zeroconf_register(uint16_t port, int n_cpus);
+int dcc_zeroconf_unregister(void*);
+
+#define DCC_DNS_SERVICE_TYPE "_distcc._tcp"
+
+#endif
--- upstream/src/zeroconf.c	1970-01-01 01:00:00.000000000 +0100
+++ lennart/src/zeroconf.c	2005-11-18 15:51:45.000000000 +0100
@@ -0,0 +1,602 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil -*- */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/error.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/address.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-client/lookup.h>
+
+#include "distcc.h"
+#include "hosts.h"
+#include "zeroconf.h"
+#include "trace.h"
+#include "exitcode.h"
+
+/* How long shall the background daemon be idle before i terminates itself? */
+#define MAX_IDLE_TIME 20
+
+/* Maxium size of host file to load */
+#define MAX_FILE_SIZE (1024*100)
+
+/* General daemon data */
+struct daemon_data {
+    struct host *hosts;
+    int fd;
+    int n_slots;
+
+    AvahiClient *client;
+    AvahiServiceBrowser *browser;
+    AvahiSimplePoll *simple_poll;
+};
+
+/* Zeroconf service wrapper */
+struct host {
+    struct daemon_data *daemon_data;
+    struct host *next;
+    
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    char *service;
+    char *domain;
+
+    AvahiAddress address;
+    uint16_t port;
+    int n_cpus;
+
+    AvahiServiceResolver *resolver;
+};
+
+/* A generic, system independant lock routine, similar to sys_lock,
+ * but more powerful:
+ *        rw:         if non-zero: r/w lock instead of r/o lock
+ *        enable:     lock or unlock
+ *        block:      block when locking */
+static int generic_lock(int fd, int rw, int enable, int block) {
+#if defined(F_SETLK)
+    struct flock lockparam;
+
+    lockparam.l_type = enable ? (rw ? F_WRLCK : F_RDLCK) : F_UNLCK;
+    lockparam.l_whence = SEEK_SET;
+    lockparam.l_start = 0;
+    lockparam.l_len = 0;        /* whole file */
+    
+    return fcntl(fd, block ? F_SETLKW : F_SETLK, &lockparam);
+#elif defined(HAVE_FLOCK)
+    return flock(fd, (enable ? (rw ? LOCK_EX : LOCK_SH) : LOCK_UN) | (block ? LOCK_NB : 0));
+#elif defined(HAVE_LOCKF)
+    return lockf(fd, (enable ? (block ? F_LOCK : F_TLOCK) : F_ULOCK));
+#else
+#  error "No supported lock method.  Please port this code."
+#endif
+}
+
+/* Return the number of seconds, when the specified file was last
+ * read. If the atime of that file is < clip_time, use clip_time
+ * instead */
+static time_t fd_last_used(int fd, time_t clip_time) {
+    struct stat st;
+    time_t now, ft;
+    assert(fd >= 0);
+
+    if (fstat(fd, &st) < 0) {
+        rs_log_crit("fstat() failed: %s\n", strerror(errno));
+        return -1;
+    }
+    
+    if ((now = time(NULL)) == (time_t) -1) {
+        rs_log_crit("time() failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    ft = clip_time ? (st.st_atime < clip_time ? clip_time : st.st_atime) : st.st_atime;
+    assert(ft <= now);
+
+    return now - ft;
+}
+
+/* Write host data to host file */
+static int write_hosts(struct daemon_data *d) {
+    struct host *h;
+    int r = 0;
+    assert(d);
+
+    rs_log_info("writing zeroconf data.\n");
+
+    if (generic_lock(d->fd, 1, 1, 1) < 0) {
+        rs_log_crit("lock failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    if (lseek(d->fd, 0, SEEK_SET) < 0) {
+        rs_log_crit("lseek() failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    if (ftruncate(d->fd, 0) < 0) {
+        rs_log_crit("ftruncate() failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    for (h = d->hosts; h; h = h->next) {
+        char t[256], a[AVAHI_ADDRESS_STR_MAX];
+
+        if (h->resolver)
+            /* Not yet fully resolved */
+            continue;
+        
+        snprintf(t, sizeof(t), "%s:%u/%i\n", avahi_address_snprint(a, sizeof(a), &h->address), h->port, d->n_slots * h->n_cpus);
+
+        if (dcc_writex(d->fd, t, strlen(t)) != 0) {
+            rs_log_crit("write() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+    }
+
+    r = 0;
+    
+finish:
+
+    generic_lock(d->fd, 1, 0, 1);
+    return r;
+    
+};
+
+/* Free host data */
+static void free_host(struct host *h) {
+    assert(h);
+
+    if (h->resolver)
+        avahi_service_resolver_free(h->resolver);
+    
+    free(h->service);
+    free(h->domain);
+    free(h);
+}
+
+/* Remove a service from the host list */
+static void remove_service(struct daemon_data *d, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *domain) {
+    struct host *h, *p = NULL;
+    assert(d);
+
+    for (h = d->hosts; h; h = h->next) {
+        if (h->interface == interface &&
+            h->protocol == protocol &&
+            !strcmp(h->service, name) &&
+            avahi_domain_equal(h->domain, domain)) {
+
+            if (p)
+                p->next = h->next;
+            else
+                d->hosts = h->next;
+
+            free_host(h);
+            
+            break;
+        } else
+            p = h;
+    }
+}
+
+/* Called when a resolve call completes */
+static void resolve_reply(AvahiServiceResolver *UNUSED(r),
+                          AvahiIfIndex UNUSED(interface),
+                          AvahiProtocol UNUSED(protocol),
+                          AvahiResolverEvent event,
+                          const char *name,
+                          const char *UNUSED(type),
+                          const char *UNUSED(domain),
+                          const char *UNUSED(host_name),
+                          const AvahiAddress *a,
+                          uint16_t port,
+                          AvahiStringList *txt,
+                          AvahiLookupResultFlags UNUSED(flags),
+                          void *userdata) {
+    
+    struct host *h = userdata;
+
+    switch (event) {
+        
+    case AVAHI_RESOLVER_FOUND: {
+        AvahiStringList *i;
+        
+        /* Look for the number of CPUs in TXT RRs */
+        for (i = txt; i; i = i->next) {
+            char *key, *value;
+            
+            if (avahi_string_list_get_pair(i, &key, &value, NULL) < 0)
+                continue;
+            
+            if (!strcmp(key, "cpus"))
+                if ((h->n_cpus = atoi(value)) <= 0)
+                    h->n_cpus = 1;
+            
+            avahi_free(key);
+            avahi_free(value);
+        }
+
+        h->address = *a;
+        h->port = port;
+        
+        avahi_service_resolver_free(h->resolver);
+        h->resolver = NULL;
+
+        /* Write modified hosts file */
+        write_hosts(h->daemon_data);
+
+        break;
+    }
+
+    case AVAHI_RESOLVER_FAILURE:
+
+        rs_log_warning("Failed to resolve service '%s': %s\n", name,
+                    avahi_strerror(avahi_client_errno(h->daemon_data->client)));
+
+        free_host(h);
+        break;
+    }
+
+}
+
+/* Called whenever a new service is found or removed */
+static void browse_reply(AvahiServiceBrowser *UNUSED(b),
+                         AvahiIfIndex interface,
+                         AvahiProtocol protocol,
+                         AvahiBrowserEvent event,
+                         const char *name,
+                         const char *type,
+                         const char *domain,
+                         AvahiLookupResultFlags UNUSED(flags),
+                         void *userdata) {
+     
+    struct daemon_data *d = userdata;
+    assert(d);
+    
+    switch (event) {
+    case AVAHI_BROWSER_NEW: {
+        struct host *h;
+
+        h = malloc(sizeof(struct host));
+        assert(h);
+        
+        rs_log_info("new service: %s\n", name);
+
+        if (!(h->resolver = avahi_service_resolver_new(d->client,
+                                                       interface,
+                                                       protocol,
+                                                       name,
+                                                       type,
+                                                       domain,
+                                                       AVAHI_PROTO_UNSPEC, 
+                                                       0,
+                                                       resolve_reply,
+                                                       h))) {
+            rs_log_warning("Failed to create service resolver for '%s': %s\n", name,
+                           avahi_strerror(avahi_client_errno(d->client)));
+
+            free(h);
+            
+        } else {
+
+            /* Fill in missing data */
+            h->service = strdup(name);
+            assert(h->service);
+            h->domain = strdup(domain);
+            assert(h->domain);
+            h->daemon_data = d;
+            h->interface = interface;
+            h->protocol = protocol;
+            h->next = d->hosts;
+            h->n_cpus = 1;
+            d->hosts = h;
+        }
+
+        break;
+    }
+        
+    case AVAHI_BROWSER_REMOVE:
+
+        rs_log_info("Removed service: %s\n", name);
+
+        remove_service(d, interface, protocol, name, domain);
+        write_hosts(d);
+        break;
+
+    case AVAHI_BROWSER_FAILURE:
+        rs_log_crit("Service Browser failure '%s': %s\n", name,
+                       avahi_strerror(avahi_client_errno(d->client)));
+
+        avahi_simple_poll_quit(d->simple_poll);
+        break;
+        
+    case AVAHI_BROWSER_CACHE_EXHAUSTED:
+    case AVAHI_BROWSER_ALL_FOR_NOW:
+        ;
+
+    }
+}
+
+static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) {
+    struct daemon_data *d = userdata;
+
+    switch (state) {
+        
+    case AVAHI_CLIENT_FAILURE:
+        rs_log_crit("Client failure: %s\n", avahi_strerror(avahi_client_errno(client)));
+        avahi_simple_poll_quit(d->simple_poll);
+        break;
+
+    case AVAHI_CLIENT_S_COLLISION:
+    case AVAHI_CLIENT_S_REGISTERING:
+    case AVAHI_CLIENT_S_RUNNING:
+    case AVAHI_CLIENT_CONNECTING:
+        ;
+    }
+}
+
+/* The main function of the background daemon */
+static void daemon_proc(const char *host_file, const char *lock_file, int n_slots) {
+    int ret = 1;
+    int lock_fd = -1;
+    struct daemon_data d;
+    time_t clip_time;
+    int error;
+
+    rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0);
+
+    /* Prepare daemon data structure */
+    d.fd = -1;
+    d.hosts = NULL;
+    d.n_slots = n_slots;
+    d.simple_poll = NULL;
+    d.browser = NULL;
+    d.client = NULL;
+    clip_time = time(NULL);
+
+    rs_log_info("Zeroconf daemon running.\n");
+
+    /* Open daemon lock file and lock it */
+    if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) {
+        rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno));
+        goto finish;
+    }
+
+    if (generic_lock(lock_fd, 1, 1, 0) < 0) {
+        /* lock failed, there's probably already another daemon running */
+        goto finish;
+    }
+
+    /* Open host file */
+    if ((d.fd = open(host_file, O_RDWR|O_CREAT, 0666)) < 0) {
+        rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno));
+        goto finish;
+    }
+
+    /* Clear host file */
+    write_hosts(&d);
+
+    if (!(d.simple_poll = avahi_simple_poll_new())) {
+        rs_log_crit("Failed to create simple poll object.\n");
+        goto finish;
+    }
+    
+    if (!(d.client = avahi_client_new(avahi_simple_poll_get(d.simple_poll),
+                                    0,
+                                    client_callback,
+                                    &d,
+                                    &error))) {
+        rs_log_crit("Failed to create Avahi client object: %s\n", avahi_strerror(error));
+        goto finish;
+    }
+
+    if (!(d.browser = avahi_service_browser_new(d.client,
+                                    AVAHI_IF_UNSPEC,
+                                    AVAHI_PROTO_UNSPEC,
+                                    DCC_DNS_SERVICE_TYPE,
+                                    NULL,
+                                    0,
+                                    browse_reply,
+                                    &d))) {
+        rs_log_crit("Failed to create service browser object: %s\n", avahi_strerror(avahi_client_errno(d.client)));
+        goto finish;
+    }
+    
+    /* Check whether the host file has been used recently */
+    while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) {
+
+        /* Iterate the main loop for 500ms */
+        if (avahi_simple_poll_iterate(d.simple_poll, 500) != 0) {
+            rs_log_crit("Event loop exited abnormaly.\n");
+            goto finish;
+        }
+    }
+
+    /* Wer are idle */
+    rs_log_info("Zeroconf daemon unused.\n");
+    
+    ret = 0;
+    
+finish:
+
+    /* Cleanup */
+    if (lock_fd >= 0) {
+        generic_lock(lock_fd, 1, 0, 0);
+        close(lock_fd);
+    }
+    
+    if (d.fd >= 0)
+        close(d.fd);
+
+    while (d.hosts) {
+        struct host *h = d.hosts;
+        d.hosts = d.hosts->next;
+        free_host(h);
+    }
+
+    if (d.client)
+        avahi_client_free(d.client);
+
+    if (d.simple_poll)
+        avahi_simple_poll_free(d.simple_poll);
+    
+    rs_log_info("zeroconf daemon ended.\n");
+    
+    _exit(ret);
+}
+
+/* Return path to the zeroconf directory in ~/.distcc */
+static int get_zeroconf_dir(char **dir_ret) {
+    static char *cached;
+    int ret;
+    
+    if (cached) {
+        *dir_ret = cached;
+        return 0;
+    } else {
+        ret = dcc_get_subdir("zeroconf", dir_ret);
+        if (ret == 0)
+            cached = *dir_ret;
+        return ret;
+    }
+}
+
+/* Get the host list from zeroconf */
+int dcc_zeroconf_add_hosts(struct dcc_hostdef **ret_list, int *ret_nhosts, int n_slots, struct dcc_hostdef **ret_prev) {
+    char host_file[PATH_MAX], lock_file[PATH_MAX], *s = NULL;
+    int lock_fd = -1, host_fd = -1;
+    int fork_daemon = 0;
+    int r = -1;
+    char *dir;
+    struct stat st;
+
+    if (get_zeroconf_dir(&dir) != 0) {
+        rs_log_crit("failed to get zeroconf dir.\n");
+        goto finish;
+    }
+    
+    snprintf(lock_file, sizeof(lock_file), "%s/lock", dir);
+    snprintf(host_file, sizeof(host_file), "%s/hosts", dir);
+
+    /* Open lock file */
+    if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) {
+        rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno));
+        goto finish;
+    }
+
+    /* Try to lock the lock file */
+    if (generic_lock(lock_fd, 1, 1, 0) >= 0) {
+        /* The lock succeeded => there's no daemon running yet! */
+        fork_daemon = 1;
+        generic_lock(lock_fd, 1, 0, 0);
+    }
+    
+    close(lock_fd);
+
+    /* Shall we fork a new daemon? */
+    if (fork_daemon) {
+        pid_t pid;
+
+        rs_log_info("Spawning zeroconf daemon.\n");
+
+        if ((pid = fork()) == -1) {
+            rs_log_crit("fork() failed: %s\n", strerror(errno));
+            goto finish;
+        } else if (pid == 0) {
+            int fd;
+            /* Child */
+
+            /* Close file descriptors and replace them by /dev/null */
+            close(0);
+            close(1);
+            close(2);
+            fd = open("/dev/null", O_RDWR);
+            assert(fd == 0);
+            fd = dup(0);
+            assert(fd == 1);
+            fd = dup(0);
+            assert(fd == 2);
+
+#ifdef HAVE_SETSID
+            setsid();
+#endif
+            
+            chdir("/");
+            rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0);
+            daemon_proc(host_file, lock_file, n_slots);
+        }
+
+        /* Parent */
+
+        /* Wait some time for initial host gathering */
+        usleep(1000000);         /* 1000 ms */
+
+    }
+
+    /* Open host list read-only */
+    if ((host_fd = open(host_file, O_RDONLY)) < 0) {
+        rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno));
+        goto finish;
+    }
+
+    /* A read lock */
+    if (generic_lock(host_fd, 0, 1, 1) < 0) {
+        rs_log_crit("lock failed: %s\n", strerror(errno));
+        goto finish;
+    }
+
+    /* Get file size */
+    if (fstat(host_fd, &st) < 0) {
+        rs_log_crit("stat() failed: %s\n", strerror(errno));
+        goto finish;
+    }
+
+    if (st.st_size >= MAX_FILE_SIZE) {
+        rs_log_crit("file too large.\n");
+        goto finish;
+    }
+
+    /* read file data */
+    s = malloc((size_t) st.st_size+1);
+    assert(s);
+
+    if (dcc_readx(host_fd, s, (size_t) st.st_size) != 0) {
+        rs_log_crit("failed to read from file.\n");
+        goto finish;
+    }
+    s[st.st_size] = 0;
+
+    /* Parse host data */
+    if (dcc_parse_hosts(s, host_file, ret_list, ret_nhosts, ret_prev) != 0) {
+        rs_log_crit("failed to parse host file.\n");
+        goto finish;
+    }
+
+    r = 0;
+
+finish:
+    if (host_fd >= 0) {
+        generic_lock(host_fd, 0, 0, 1);
+        close(host_fd);
+    }
+
+    free(s);
+
+    return r;
+}
+
--- upstream/src/zeroconf-reg.c	1970-01-01 01:00:00.000000000 +0100
+++ lennart/src/zeroconf-reg.c	2005-11-18 15:34:00.000000000 +0100
@@ -0,0 +1,297 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil -*- */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/error.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/malloc.h>
+#include <avahi-client/publish.h>
+
+#include "distcc.h"
+#include "zeroconf.h"
+#include "trace.h"
+#include "exitcode.h"
+
+struct context {
+    int thread_running;
+    pthread_t thread_id;
+    pthread_mutex_t mutex;
+    char *name;
+    AvahiSimplePoll *simple_poll;
+    AvahiClient *client;
+    AvahiEntryGroup *group;
+    uint16_t port;
+    int n_cpus;
+};
+
+static void publish_reply(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata);
+
+static void register_stuff(struct context *ctx) {
+
+    if (!ctx->group) {
+
+        if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+            rs_log_crit("Failed to create entry group: %s\n", avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+        
+    }
+
+    if (avahi_entry_group_is_empty(ctx->group)) {
+        char cpus[32];
+
+        snprintf(cpus, sizeof(cpus), "cpus=%i", ctx->n_cpus);
+
+        /* Register our service */
+        
+        if (avahi_entry_group_add_service(ctx->group,
+                                          AVAHI_IF_UNSPEC,
+                                          AVAHI_PROTO_UNSPEC,
+                                          0,
+                                          ctx->name,
+                                          DCC_DNS_SERVICE_TYPE,
+                                          NULL,
+                                          NULL,
+                                          ctx->port,
+                                          "txtvers=1",
+                                          cpus,
+                                          "distcc="PACKAGE_VERSION,
+                                          "gnuhost="GNU_HOST,
+                                          NULL) < 0) {
+            rs_log_crit("Failed to add service: %s\n", avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+
+        if (avahi_entry_group_commit(ctx->group) < 0) {
+            rs_log_crit("Failed to commit entry group: %s\n", avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+    }
+
+    return;
+    
+    fail:
+    avahi_simple_poll_quit(ctx->simple_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *UNUSED(g), AvahiEntryGroupState state, void *userdata) {
+    struct context *ctx = userdata;
+
+    switch (state) {
+        
+    case AVAHI_ENTRY_GROUP_COLLISION: {
+        char *n;
+
+        /* Pick a new name for our service */
+        
+        n = avahi_alternative_service_name(ctx->name);
+        assert(n);
+        
+        avahi_free(ctx->name);
+        ctx->name = n;
+
+        register_stuff(ctx);
+        break;
+    }
+
+    case AVAHI_ENTRY_GROUP_FAILURE: 
+        rs_log_crit("Failed to register service: %s\n", avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_simple_poll_quit(ctx->simple_poll);
+        break;
+        
+    case AVAHI_ENTRY_GROUP_UNCOMMITED:
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+    case AVAHI_ENTRY_GROUP_ESTABLISHED:
+        ;
+    }
+}
+
+static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) {
+    struct context *ctx = userdata;
+
+    ctx->client = client;
+    
+    switch (state) {
+
+    case AVAHI_CLIENT_S_RUNNING:
+
+        register_stuff(ctx);
+        break;
+
+    case AVAHI_CLIENT_S_COLLISION:
+
+        if (ctx->group)
+            avahi_entry_group_reset(ctx->group);
+        break;
+
+    case AVAHI_CLIENT_FAILURE:
+
+        if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+            int error;
+
+            avahi_client_free(ctx->client);
+            ctx->client = NULL;
+            ctx->group = NULL;
+
+            /* Reconnect to the server */
+
+            if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll),
+                                                 AVAHI_CLIENT_NO_FAIL,
+                                                 client_callback,
+                                                 ctx,
+                                                 &error))) {
+                
+                rs_log_crit("Failed to contact server: %s\n", avahi_strerror(error));
+                avahi_simple_poll_quit(ctx->simple_poll);
+            }
+            
+        } else {
+            rs_log_crit("Client failure: %s\n", avahi_strerror(avahi_client_errno(client)));
+            avahi_simple_poll_quit(ctx->simple_poll);
+        }
+
+        break;
+        
+    case AVAHI_CLIENT_S_REGISTERING:
+    case AVAHI_CLIENT_CONNECTING:
+        ;
+    }
+}
+
+static void* thread(void *userdata) {
+    struct context *ctx = userdata;
+    sigset_t mask;
+    int r;
+
+    /* Make sure that signals are delivered to the main thread */
+    sigfillset(&mask);
+    pthread_sigmask(SIG_BLOCK, &mask, NULL);
+    
+    pthread_mutex_lock(&ctx->mutex);
+
+    /* Run the main loop */
+    r = avahi_simple_poll_loop(ctx->simple_poll);
+
+    /* Cleanup some stuff */
+    if (ctx->client)
+        avahi_client_free(ctx->client);
+    ctx->client = NULL;
+    ctx->group = NULL;
+    
+    pthread_mutex_unlock(&ctx->mutex);
+    
+    return NULL;
+}
+
+static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
+    pthread_mutex_t *mutex = userdata;
+    int r;
+
+    /* Before entering poll() we unlock the mutex, so that
+     * avahi_simple_poll_quit() can succeed from another thread. */
+    
+    pthread_mutex_unlock(mutex);
+    r = poll(ufds, nfds, timeout);
+    pthread_mutex_lock(mutex);
+
+    return r;
+}
+
+/* register a distcc service in DNS-SD/mDNS with the given port and number of CPUs */
+void* dcc_zeroconf_register(uint16_t port, int n_cpus) {
+    struct context *ctx = NULL;
+    
+    char service[256] = "distcc@";
+    int error, ret;
+
+    ctx = malloc(sizeof(struct context));
+    assert(ctx);
+    ctx->client = NULL;
+    ctx->group = NULL;
+    ctx->simple_poll = NULL;
+    ctx->thread_running = 0;
+    ctx->port = port;
+    ctx->n_cpus = n_cpus;
+    pthread_mutex_init(&ctx->mutex, NULL);
+    
+    /* Prepare service name */
+    gethostname(service+7, sizeof(service)-8);
+    service[sizeof(service)-1] = 0;
+
+    ctx->name = strdup(service);
+    assert(ctx->name);
+
+    if (!(ctx->simple_poll = avahi_simple_poll_new())) {
+        rs_log_crit("Failed to create event loop object.\n");
+        goto fail;
+    }
+
+    avahi_simple_poll_set_func(ctx->simple_poll, poll_func, &ctx->mutex);
+    
+    if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, ctx, &error))) {
+        rs_log_crit("Failed to create client object: %s\n", avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    }
+
+    /* Create the mDNS event handler */
+    if ((ret = pthread_create(&ctx->thread_id, NULL, thread, ctx)) < 0) {
+        rs_log_crit("Failed to create thread: %s\n", strerror(ret));
+        goto fail;
+    }
+
+    ctx->thread_running = 1;
+    
+    return ctx;
+    
+ fail:
+
+    if (ctx)
+        dcc_zeroconf_unregister(ctx);
+    
+    return NULL;
+}
+
+/* Unregister this server from DNS-SD/mDNS */
+int dcc_zeroconf_unregister(void *u) {
+    struct context *ctx = u;
+
+    if (ctx->thread_running) {
+        pthread_mutex_lock(&ctx->mutex);
+        avahi_simple_poll_quit(ctx->simple_poll);
+        pthread_mutex_unlock(&ctx->mutex);
+        
+        pthread_join(ctx->thread_id, NULL);
+        ctx->thread_running = 0;
+    }
+
+    avahi_free(ctx->name);
+
+    if (ctx->client)
+        avahi_client_free(ctx->client);
+
+    if (ctx->simple_poll)
+        avahi_simple_poll_free(ctx->simple_poll);
+
+    pthread_mutex_destroy(&ctx->mutex);
+
+    free(ctx);
+    
+    return 0;
+}
