--- distcc-2.18.3.orig/Makefile.in 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/Makefile.in 2005-08-04 11:02:57.000000000 -0500 @@ -21,11 +21,11 @@ # These autoconf variables may contain recursive Make expansions, and # so they have to be done here rather than written into config.h. -CFLAGS = @CFLAGS@ +CFLAGS = @CFLAGS@ -I/usr/include/howl LDFLAGS = @LDFLAGS@ CC = @CC@ CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ ${DIR_DEFS} -Isrc -I$(srcdir)/lzo +CPPFLAGS = @CPPFLAGS@ ${DIR_DEFS} -Isrc -I$(srcdir)/lzo -DHAVE_HOWL srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -56,7 +56,7 @@ GNOME_CFLAGS = @GNOME_CFLAGS@ GNOME_LIBS = @GNOME_LIBS@ -LIBS = @LIBS@ +LIBS = @LIBS@ -lhowl -lpthread DESTDIR = @@ -180,6 +180,7 @@ src/ssh.o src/state.o src/strip.o \ src/timefile.o src/traceenv.o \ src/where.o \ + src/zeroconf.o \ $(common_obj) distccd_obj = src/access.o \ @@ -187,6 +188,7 @@ src/ncpus.o \ src/prefork.o \ src/serve.o src/setuid.o src/srvnet.o src/srvrpc.o src/state.o \ + src/zeroconf-reg.o \ $(common_obj) @BUILD_POPT@ # Objects that need to be linked in to build monitors --- distcc-2.18.3.orig/aclocal.m4 1969-12-31 18:00:00.000000000 -0600 +++ distcc-2.18.3.orig/aclocal.m4 2005-08-04 11:02:25.000000000 -0500 @@ -0,0 +1,57 @@ + +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN(PKG_CHECK_MODULES, [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + --- distcc-2.18.3.orig/configure.ac 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/configure.ac 2005-08-04 11:02:25.000000000 -0500 @@ -386,6 +386,20 @@ AC_DEFINE(HAVE_SOCKADDR_STORAGE, 1, [define if you have struct sockaddr_storage]),, [#include ]) +dnl check for howl +PKG_CHECK_MODULES(HOWL, [howl], +[AC_DEFINE(HAVE_HOWL, 1, [defined if howl is available]) +CFLAGS="$CFLAGS $HOWL_CFLAGS" +LIBS="$LIBS $HOWL_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) + +AC_DEFINE(HAVE_HOWL, 1, [defined because I do]) + dnl ##### Output AC_SUBST(docdir) AC_SUBST(CFLAGS) --- distcc-2.18.3.orig/src/distcc.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/distcc.c 2005-08-04 11:02:25.000000000 -0500 @@ -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,6 +138,47 @@ 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); +} + #define MAXNEWFLAGS 32 #define MAXFLAGLEN 127 @@ -282,6 +326,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; + } if(!(newargv = getNewArgvFromEnv(argv))) { ret = EXIT_OUT_OF_MEMORY; --- distcc-2.18.3.orig/src/distcc.h 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/distcc.h 2005-08-04 11:02:25.000000000 -0500 @@ -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; --- distcc-2.18.3.orig/src/dopt.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/dopt.c 2005-08-04 11:02:25.000000000 -0500 @@ -93,6 +93,9 @@ opt_log_level }; +#ifdef HAVE_HOWL +int opt_zeroconf = 0; +#endif const struct poptOption options[] = { { "allow", 'a', POPT_ARG_STRING, 0, 'a', 0, 0 }, @@ -115,6 +118,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_HOWL + { "zeroconf", 0, POPT_ARG_NONE, &opt_zeroconf, 0, 0, 0 }, +#endif { 0, 0, 0, 0, 0, 0, 0 } }; @@ -137,6 +143,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_HOWL +" --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" --- distcc-2.18.3.orig/src/dopt.h 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/dopt.h 2005-08-04 11:02:25.000000000 -0500 @@ -38,3 +38,5 @@ extern int opt_lifetime; extern char *opt_listen_addr; extern int opt_niceness; + +extern int opt_zeroconf; --- distcc-2.18.3.orig/src/dparent.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/dparent.c 2005-08-04 11:02:25.000000000 -0500 @@ -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_HOWL + sw_discovery discovery; +#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_HOWL + /* Zeroconf registration */ + if (opt_zeroconf) { + if ((ret = dcc_zeroconf_register(&discovery, arg_port, n_cpus)) != 0) + 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_HOWL + /* Remove zeroconf registration */ + if (opt_zeroconf) { + if ((ret = dcc_zeroconf_unregister(discovery)) != 0) + return EXIT_CONNECT_FAILED; + } +#endif + + return ret; } --- distcc-2.18.3.orig/src/help.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/help.c 2005-08-04 11:02:25.000000000 -0500 @@ -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_HOWL +"\nBuilt with Zeroconf support.\n" +#endif "\n" , prog, PACKAGE_VERSION, GNU_HOST, DISTCC_DEFAULT_PORT, --- distcc-2.18.3.orig/src/hostfile.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/hostfile.c 2005-08-04 11:02:25.000000000 -0500 @@ -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); --- distcc-2.18.3.orig/src/hosts.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/hosts.c 2005-08-04 11:02:25.000000000 -0500 @@ -96,6 +96,10 @@ #include "hosts.h" #include "exitcode.h" #include "snprintf.h" +#ifdef HAVE_HOWL +#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_HOWL + 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_HOWL + skip: +#endif + /* continue to next token if any */ where = token_start + token_len; - prev = curr; - (*ret_nhosts)++; } if (*ret_nhosts) { --- distcc-2.18.3.orig/src/io.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/io.c 2005-08-04 11:02:25.000000000 -0500 @@ -160,7 +160,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)); @@ -202,9 +202,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; --- distcc-2.18.3.orig/src/tempfile.c 2005-08-04 10:27:18.000000000 -0500 +++ distcc-2.18.3.orig/src/tempfile.c 2005-08-04 11:02:25.000000000 -0500 @@ -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; --- distcc-2.18.3.orig/src/zeroconf-reg.c 1969-12-31 18:00:00.000000000 -0600 +++ distcc-2.18.3.orig/src/zeroconf-reg.c 2005-08-04 11:02:25.000000000 -0500 @@ -0,0 +1,110 @@ +/* -*- c-file-style: "java"; indent-tabs-mode: nil -*- */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "distcc.h" +#include "zeroconf.h" +#include "trace.h" +#include "exitcode.h" + +/* Called when publishing of service data completes */ +static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) { + int *done = extra; + *done = 1; + + if (status != SW_DISCOVERY_PUBLISH_STARTED && status != SW_DISCOVERY_PUBLISH_STOPPED) + rs_log_crit("zeroconf publishing failed (%i)", status); + + return SW_OKAY; +} + +/* register a distcc service in DNS-SD/mDNS with the given port and number of CPUs */ +int dcc_zeroconf_register(sw_discovery *discovery, int port, int n_cpus) { + sw_salt salt; + sw_discovery_oid oid; + int done = 0; + char service[256], hn[256], t[16]; + sw_text_record txt; + + if (sw_discovery_init(discovery) != SW_OKAY) { + rs_log_crit("sw_discovery_init() failed.\n"); + return EXIT_CONNECT_FAILED; + } + + /* Prepare TXT RR */ + if (sw_text_record_init(&txt) != SW_OKAY) { + rs_log_crit("sw_text_record_init() failed.\n"); + sw_discovery_fina(*discovery); + return EXIT_CONNECT_FAILED; + } + + if (sw_discovery_salt(*discovery, &salt) != SW_OKAY) { + rs_log_crit("sw_discovery_salt() failed.\n"); + goto fail; + } + + /* Prepare service name */ + gethostname(hn, sizeof(hn)-1); + hn[sizeof(hn)-1] = 0; + snprintf(service, sizeof(service), "distcc@%s", hn); + + /* Prepare TXT RR */ + snprintf(t, sizeof(t), "cpus=%i", n_cpus); + sw_text_record_add_string(txt, t); + + /* Publish the info */ + if (sw_discovery_publish( + *discovery, + 0, + service, + "_distcc._tcp", + NULL, + NULL, + (sw_port) port, + sw_text_record_bytes(txt), + sw_text_record_len(txt), + publish_reply, + &done, + &oid) != SW_OKAY) { + + rs_log_crit("sw_discovery_publish() failed.\n"); + goto fail; + } + + /* Wait until the publishing is finished */ + while (!done) { + if (sw_salt_step(salt, NULL) != SW_OKAY) { + rs_log_crit("sw_salt() failed.\n"); + goto fail; + } + + } + + rs_log_info("zeroconf publishing complete"); + + sw_text_record_fina(txt); + + return 0; + + fail: + + sw_discovery_fina(*discovery); + sw_text_record_fina(txt); + return EXIT_CONNECT_FAILED; +} + +/* Unregister this server from DNS-SD/mDNS */ +int dcc_zeroconf_unregister(sw_discovery discovery) { + + sw_discovery_fina(discovery); + return 0; +} --- distcc-2.18.3.orig/src/zeroconf.c 1969-12-31 18:00:00.000000000 -0600 +++ distcc-2.18.3.orig/src/zeroconf.c 2005-08-04 11:02:25.000000000 -0500 @@ -0,0 +1,493 @@ +/* -*- c-file-style: "java"; indent-tabs-mode: nil -*- */ + +#include "config.h" + +#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) + +/* Zeroconf service wrapper */ +struct host { + struct host *next; + sw_ipv4_address address; + sw_port port; + sw_uint32 iface; + char *service; + char *domain; + int n_cpus; +}; + +/* General daemon data */ +struct daemon_data { + struct host *hosts; + int fd; + int n_slots; +}; + +/* 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[16]; + snprintf(t, sizeof(t), "%s:%u/%i\n", sw_ipv4_address_name(h->address, a, sizeof(a)), 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); + free(h->service); + free(h->domain); + free(h); +} + +/* Remove a service from the host list */ +static void remove_service(struct daemon_data *d, sw_uint32 iface, const char *name, const char *domain) { + struct host *h, *p = NULL; + assert(d); + + for (h = d->hosts; h; h = h->next) { + if (h->iface == iface && !strcmp(h->service, name) && !strcmp(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 sw_result resolve_reply( + sw_discovery discovery, + sw_discovery_oid oid, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_ipv4_address address, + sw_port port, + sw_octets text_record, + sw_ulong text_record_len, + sw_opaque extra) { + + struct host *h; + struct daemon_data *d = extra; + int n_cpus = 1; + sw_text_record_iterator it; + + /* Look for the number of CPUs in TXT RRs */ + if (sw_text_record_iterator_init(&it, text_record, text_record_len) == SW_OKAY) { + sw_char key[255]; + sw_octet val[256]; + sw_ulong val_len = sizeof(val)-1; + + memset(val, 0, sizeof(val)); + + while (sw_text_record_iterator_next(it, key, val, &val_len) == SW_OKAY) { + if (!strcmp(key, "cpus")) { + if ((n_cpus = atoi((char*) val)) <= 0) + n_cpus = 1; + } + } + + sw_text_record_iterator_fina(it); + } + + /* Add a new host */ + h = malloc(sizeof(struct host)); + assert(h); + h->service = strdup(name); + assert(h->service); + h->domain = strdup(domain); + assert(h->domain); + h->address = address; + h->port = port; + h->iface = interface_index; + h->next = d->hosts; + h->n_cpus = n_cpus; + d->hosts = h; + + /* Write modified hosts file */ + write_hosts(d); + + /* Let's cancel this resolve request, we only need a single adress for this service */ + sw_discovery_cancel(discovery, oid); + + return SW_OKAY; +} + +/* Called whenever a new service is found or removed */ +static sw_result browse_reply( + sw_discovery discovery, + sw_discovery_oid oid, + sw_discovery_browse_status status, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_opaque extra) { + + struct daemon_data *d = extra; + assert(d); + + switch (status) { + case SW_DISCOVERY_BROWSE_ADD_SERVICE: { + sw_discovery_oid noid; + + rs_log_info("new service: %s\n", name); + + sw_discovery_resolve(discovery, 0, name, type, domain, resolve_reply, extra, &noid); + break; + } + case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: { + + rs_log_info("removed service: %s\n", name); + + remove_service(d, interface_index, name, domain); + write_hosts(d); + break; + } + default: + ; + } + + return SW_OKAY; +} + +/* The main function of the background daemon */ +static void daemon_proc(const char *host_file, const char *lock_file, int n_slots) { + sw_discovery_oid oid; + sw_discovery discovery; + int ret = 1; + int lock_fd = -1; + struct daemon_data d; + sw_salt salt; + time_t clip_time; + + /* Prepare daemon data structure */ + d.fd = -1; + d.hosts = NULL; + d.n_slots = n_slots; + 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 (sw_discovery_init(&discovery) != SW_OKAY) { + rs_log_crit("sw_discovery_init() failed.\n"); + goto finish; + } + + /* Start discovery */ + if (sw_discovery_browse(discovery, 0, "_distcc._tcp", NULL, browse_reply, &d, &oid) != SW_OKAY) { + rs_log_crit("sw_discovery_browse() failed.\n"); + goto finish; + } + + /* Get "salt" object for the main loop */ + if (sw_discovery_salt(discovery, &salt) != SW_OKAY) { + rs_log_crit("sw_discovery_salt() failed.\n"); + goto finish; + } + + /* Check whether the host file has been used recently */ + while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) { + sw_ulong msecs = 500; + + /* Iterate the main loop for 500ms */ + if (sw_salt_step(salt, &msecs) != SW_OKAY) { + rs_log_crit("sw_salt_step() failed.\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); + } + + 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); /* 100 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; +} + --- distcc-2.18.3.orig/src/zeroconf.h 1969-12-31 18:00:00.000000000 -0600 +++ distcc-2.18.3.orig/src/zeroconf.h 2005-08-04 11:02:25.000000000 -0500 @@ -0,0 +1,22 @@ +#ifndef foozeroconfhfoo +#define foozeroconfhfoo + +#include + +/* HOWL 0.9.6 defines PACKAGE_NAME and friends. This is broken. As a + * workaround we undfined those macros here */ + +#undef PACKAGE_NAME +#undef PACKAGE_BUGREPORT +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef PACKAGE_STRING + +#include + +int dcc_zeroconf_add_hosts(struct dcc_hostdef **re_list, int *ret_nhosts, int slots, struct dcc_hostdef **ret_prev); + +int dcc_zeroconf_register(sw_discovery *discovery, int port, int n_cpus); +int dcc_zeroconf_unregister(sw_discovery discovery); + +#endif