Upstream-Status: Pending --- 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 . +# +# 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 .])], + [$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 ]) +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 +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_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 ], [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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +}