summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorJeongho Hwang <jbera.hwang@samsung.com>2012-09-19 13:40:18 +0900
committerJeongho Hwang <jbera.hwang@samsung.com>2012-09-19 13:40:18 +0900
commitd208c9cb79b228fd48d9f7adef432486389e1abe (patch)
tree1f8533ae700c789c992dcebb88c558a963e59efc /client
parent8701a5293e4e52a995b1ee0b683f234848d8d745 (diff)
downloadicecream-412658a1399f91b1cc2fb24a0a41ff33ef8bf5e9.tar.gz
icecream-412658a1399f91b1cc2fb24a0a41ff33ef8bf5e9.tar.bz2
icecream-412658a1399f91b1cc2fb24a0a41ff33ef8bf5e9.zip
Signed-off-by: Jeongho Hwang <jbera.hwang@samsung.com>
Diffstat (limited to 'client')
-rw-r--r--client/Makefile.am21
-rw-r--r--client/Makefile.in630
-rw-r--r--client/arg.cpp419
-rw-r--r--client/client.h61
-rw-r--r--client/cpp.cpp149
-rwxr-xr-xclient/icecc-create-env195
-rw-r--r--client/local.cpp287
-rw-r--r--client/main.cpp373
-rw-r--r--client/md5.c392
-rw-r--r--client/md5.h93
-rw-r--r--client/remote.cpp777
-rw-r--r--client/safeguard.cpp68
-rw-r--r--client/util.cpp231
-rw-r--r--client/util.h34
14 files changed, 3730 insertions, 0 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
new file mode 100644
index 0000000..56666c4
--- /dev/null
+++ b/client/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = -I$(srcdir)/../services
+
+bin_PROGRAMS = icecc
+pkglib_SCRIPTS = icecc-create-env
+icecc_SOURCES = main.cpp arg.cpp cpp.cpp local.cpp remote.cpp util.cpp md5.c safeguard.cpp
+icecc_LDADD = ../services/libicecc.la $(LIBRSYNC)
+noinst_HEADERS = client.h md5.h util.h
+
+EXTRA_DIST = icecc-create-env
+
+install-exec-local:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ for link in g++ gcc c++ cc icerun; do \
+ rm -f $(DESTDIR)$(bindir)/$$link ;\
+ $(LN_S) icecc $(DESTDIR)$(bindir)/$$link ;\
+ done
+
+uninstall-local:
+ for link in g++ gcc c++ cc; do \
+ rm $(DESTDIR)$(bindir)/$$link ;\
+ done
diff --git a/client/Makefile.in b/client/Makefile.in
new file mode 100644
index 0000000..187d695
--- /dev/null
+++ b/client/Makefile.in
@@ -0,0 +1,630 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in 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.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = icecc$(EXEEXT)
+subdir = client
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibdir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_icecc_OBJECTS = main.$(OBJEXT) arg.$(OBJEXT) cpp.$(OBJEXT) \
+ local.$(OBJEXT) remote.$(OBJEXT) util.$(OBJEXT) md5.$(OBJEXT) \
+ safeguard.$(OBJEXT)
+icecc_OBJECTS = $(am_icecc_OBJECTS)
+am__DEPENDENCIES_1 =
+icecc_DEPENDENCIES = ../services/libicecc.la $(am__DEPENDENCIES_1)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+SCRIPTS = $(pkglib_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(icecc_SOURCES)
+DIST_SOURCES = $(icecc_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBRSYNC = @LIBRSYNC@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIB_KINFO = @LIB_KINFO@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAR = @TAR@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I$(srcdir)/../services
+pkglib_SCRIPTS = icecc-create-env
+icecc_SOURCES = main.cpp arg.cpp cpp.cpp local.cpp remote.cpp util.cpp md5.c safeguard.cpp
+icecc_LDADD = ../services/libicecc.la $(LIBRSYNC)
+noinst_HEADERS = client.h md5.h util.h
+EXTRA_DIST = icecc-create-env
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign client/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign client/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+icecc$(EXEEXT): $(icecc_OBJECTS) $(icecc_DEPENDENCIES)
+ @rm -f icecc$(EXEEXT)
+ $(CXXLINK) $(icecc_OBJECTS) $(icecc_LDADD) $(LIBS)
+install-pkglibSCRIPTS: $(pkglib_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)"
+ @list='$(pkglib_SCRIPTS)'; test -n "$(pkglibdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkglibdir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkglibdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-pkglibSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkglib_SCRIPTS)'; test -n "$(pkglibdir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkglibdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkglibdir)" && rm -f $$files
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safeguard.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-pkglibSCRIPTS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-local \
+ uninstall-pkglibSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-pkglibSCRIPTS \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-local uninstall-pkglibSCRIPTS
+
+
+install-exec-local:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ for link in g++ gcc c++ cc icerun; do \
+ rm -f $(DESTDIR)$(bindir)/$$link ;\
+ $(LN_S) icecc $(DESTDIR)$(bindir)/$$link ;\
+ done
+
+uninstall-local:
+ for link in g++ gcc c++ cc; do \
+ rm $(DESTDIR)$(bindir)/$$link ;\
+ done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/client/arg.cpp b/client/arg.cpp
new file mode 100644
index 0000000..db16ee9
--- /dev/null
+++ b/client/arg.cpp
@@ -0,0 +1,419 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*-
+ *
+ * distcc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "client.h"
+
+using namespace std;
+
+#define CLIENT_DEBUG 0
+
+#if CLIENT_DEBUG
+static string concat_args( const list<string> &args )
+{
+ size_t len = args.size() - 1;
+ string result = "\"";
+ for ( list<string>::const_iterator it = args.begin();
+ it != args.end(); ++it, len-- ) {
+ result += *it;
+ if ( len )
+ result += ", ";
+ }
+ result += "\"";
+ return result;
+}
+#endif
+
+#define str_equal(a, b) (!strcmp((a), (b)))
+
+inline int str_startswith(const char *head, const char *worm)
+{
+ return !strncmp(head, worm, strlen(head));
+}
+
+static bool analyze_program(const char* name, CompileJob& job)
+{
+ string compiler_name = find_basename( name );
+
+ string::size_type pos = compiler_name.rfind('/');
+ if (pos != string::npos)
+ compiler_name = compiler_name.substr(pos);
+
+ job.setCompilerName( compiler_name );
+
+ string suffix = compiler_name;
+ if ( compiler_name.size() > 2)
+ suffix = compiler_name.substr(compiler_name.size()-2);
+
+ if (suffix == "++" || suffix == "CC")
+ job.setLanguage (CompileJob::Lang_CXX);
+ else if (suffix == "cc")
+ job.setLanguage (CompileJob::Lang_C);
+ else {
+ job.setLanguage( CompileJob::Lang_Custom );
+ job.setCompilerName( name ); // keep path
+ return true;
+ }
+
+ return false;
+}
+
+bool analyse_argv( const char * const *argv,
+ CompileJob &job, bool icerun )
+{
+ ArgumentsList args;
+ string ofile;
+
+#if CLIENT_DEBUG > 1
+ trace() << "scanning arguments ";
+ for ( int index = 0; argv[index]; index++ )
+ trace() << argv[index] << " ";
+ trace() << endl;
+#endif
+
+ bool had_cc = (job.compilerName().size() > 0);
+ bool always_local = analyze_program(had_cc ? job.compilerName().c_str() : argv[0], job);
+ bool seen_c = false;
+ bool seen_s = false;
+ bool seen_mf = false;
+ bool seen_md = false;
+ if( icerun ) {
+ always_local = true;
+ job.setLanguage( CompileJob::Lang_Custom );
+ }
+
+ for (int i = had_cc ? 2 : 1; argv[i]; i++) {
+ const char *a = argv[i];
+
+ if (icerun) {
+ args.append(a, Arg_Local);
+ } else if (a[0] == '-') {
+ if (!strcmp(a, "-E") || !strncmp(a, "-fdump", 6) || !strcmp(a, "-combine")) {
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else if (!strcmp(a, "-MD") || !strcmp(a, "-MMD")) {
+ seen_md = true;
+ args.append(a, Arg_Local);
+ /* These two generate dependencies as a side effect. They
+ * should work with the way we call cpp. */
+ } else if (!strcmp(a, "-MG") || !strcmp(a, "-MP")) {
+ args.append(a, Arg_Local);
+ /* These just modify the behaviour of other -M* options and do
+ * nothing by themselves. */
+ } else if (!strcmp(a, "-MF")) {
+ seen_mf = true;
+ args.append(a, Arg_Local);
+ args.append( argv[++i], Arg_Local );
+ /* as above but with extra argument */
+ } else if (!strcmp(a, "-MT") || !strcmp(a, "-MQ")) {
+ args.append(a, Arg_Local);
+ args.append( argv[++i], Arg_Local );
+ /* as above but with extra argument */
+ } else if (a[1] == 'M') {
+ /* -M(anything else) causes the preprocessor to
+ produce a list of make-style dependencies on
+ header files, either to stdout or to a local file.
+ It implies -E, so only the preprocessor is run,
+ not the compiler. There would be no point trying
+ to distribute it even if we could. */
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else if ( str_equal( "--param", a ) ) {
+ args.append( a, Arg_Remote );
+ /* skip next word, being option argument */
+ if (argv[i+1])
+ args.append( argv[++i], Arg_Remote );
+ } else if ( a[1] == 'B' ) {
+ /* -B overwrites the path where the compiler finds the assembler.
+ As we don't use that, better force local job.
+ */
+ always_local = true;
+ args.append( a, Arg_Local );
+ if ( str_equal( a, "-B" ) ) {
+ /* skip next word, being option argument */
+ if (argv[i+1])
+ args.append( argv[++i], Arg_Local );
+ }
+ } else if (str_startswith("-Wa,", a)) {
+ /* Options passed through to the assembler. The only one we
+ * need to handle so far is -al=output, which directs the
+ * listing to the named file and cannot be remote. There are
+ * some other options which also refer to local files,
+ * but most of them make no sense when called via the compiler,
+ * hence we only look for -a[a-z]*= and localize the job if we
+ * find it. */
+ const char *pos = a;
+ bool local = false;
+ while ((pos = strstr(pos+1, "-a"))) {
+ pos += 2;
+ while (*pos >= 'a' && *pos <= 'z')
+ pos++;
+ if (*pos == '=') {
+ local = true;
+ break;
+ }
+ if (!*pos)
+ break;
+ }
+ if (local) {
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else
+ args.append(a, Arg_Remote);
+ } else if (!strcmp(a, "-S")) {
+ seen_s = true;
+ } else if (!strcmp(a, "-fprofile-arcs")
+ || !strcmp(a, "-ftest-coverage")
+ || !strcmp( a, "-frepo" )
+ || !strcmp( a, "-fprofile-generate" )
+ || !strcmp( a, "-fprofile-use" )
+ || !strcmp( a, "-save-temps")
+ || !strcmp( a, "-fbranch-probabilities") ) {
+#if CLIENT_DEBUG
+ log_info() << "compiler will emit profile info; must be local" << endl;
+#endif
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else if (!strcmp(a, "-x")) {
+#if CLIENT_DEBUG
+ log_info() << "gcc's -x handling is complex; running locally" << endl;
+#endif
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else if (!strcmp(a, "-march=native") || !strcmp(a, "-mcpu=native") || !strcmp(a, "-mtune=native")) {
+#if CLIENT_DEBUG
+ log_info() << "-{march,mpcu,mtune}=native optimizes for local machine; must be local" << endl;
+#endif
+ always_local = true;
+ args.append(a, Arg_Local);
+ } else if (!strcmp(a, "-c")) {
+ seen_c = true;
+ } else if (str_startswith("-o", a)) {
+ if (!strcmp(a, "-o")) {
+ /* Whatever follows must be the output */
+ if ( argv[i+1] )
+ ofile = argv[++i];
+ } else {
+ a += 2;
+ ofile = a;
+ }
+ if (ofile == "-" ) {
+ /* Different compilers may treat "-o -" as either "write to
+ * stdout", or "write to a file called '-'". We can't know,
+ * so we just always run it locally. Hopefully this is a
+ * pretty rare case. */
+#if CLIENT_DEBUG
+ log_info() << "output to stdout? running locally" << endl;
+#endif
+ always_local = true;
+ }
+ } else if (str_equal("-include", a)) {
+ /* This has a duplicate meaning. it can either include a file
+ for preprocessing or a precompiled header. decide which one. */
+ if (argv[i+1]) {
+ ++i;
+ std::string p = argv[i];
+ if (access(p.c_str(), R_OK) && access((p + ".gch").c_str(), R_OK))
+ always_local = true; /* can't decide which one, let the compiler figure it
+ out. */
+ args.append(a, Arg_Local);
+ args.append(argv[i], Arg_Local);
+ }
+ } else if (str_equal("-D", a)
+ || str_equal("-I", a)
+ || str_equal("-U", a)
+ || str_equal("-L", a)
+ || str_equal("-l", a)
+ || str_equal("-MF", a)
+ || str_equal("-MT", a)
+ || str_equal("-MQ", a)
+ || str_equal("-imacros", a)
+ || str_equal("-iprefix", a)
+ || str_equal("-iwithprefix", a)
+ || str_equal("-isystem", a)
+ || str_equal("-iquote", a)
+ || str_equal("-imultilib", a)
+ || str_equal("-isysroot", a)
+ || str_equal("-include", a)
+ || str_equal("-iwithprefixbefore", a)
+ || str_equal("-idirafter", a) ) {
+ args.append(a, Arg_Local);
+ /* skip next word, being option argument */
+ if (argv[i+1]) {
+ ++i;
+ if (str_startswith("-O", argv[i]))
+ always_local = true;
+ args.append( argv[i], Arg_Local );
+ }
+ } else if (str_startswith("-Wp,", a)
+ || str_startswith("-D", a)
+ || str_startswith("-U", a)
+ || str_startswith("-I", a)
+ || str_startswith("-l", a)
+ || str_startswith("-L", a)) {
+ args.append(a, Arg_Local);
+ } else if (str_equal("-undef", a)
+ || str_equal("-nostdinc", a)
+ || str_equal("-nostdinc++", a)
+ || str_equal("-MD", a)
+ || str_equal("-MMD", a)
+ || str_equal("-MG", a)
+ || str_equal("-MP", a)) {
+ args.append(a, Arg_Local);
+ } else
+ args.append( a, Arg_Rest );
+ } else {
+ args.append( a, Arg_Rest );
+ }
+ }
+
+ if (!seen_c && !seen_s)
+ always_local = true;
+ else if ( seen_s ) {
+ if ( seen_c )
+ log_info() << "can't have both -c and -S, ignoring -c" << endl;
+ args.append( "-S", Arg_Remote );
+ } else {
+ args.append( "-c", Arg_Remote );
+ }
+
+ if ( !always_local ) {
+
+ ArgumentsList backup = args;
+
+ /* TODO: ccache has the heuristic of ignoring arguments that are not
+ * extant files when looking for the input file; that's possibly
+ * worthwile. Of course we can't do that on the server. */
+ string ifile;
+ for ( ArgumentsList::iterator it = args.begin();
+ it != args.end(); ) {
+ if ( it->first == "-") {
+ always_local = true;
+ break;
+ }
+ if ( it->second != Arg_Rest || it->first.at( 0 ) == '-' )
+ ++it;
+ else if ( ifile.empty() ) {
+#if CLIENT_DEBUG
+ log_info() << "input file: " << it->first << endl;
+#endif
+ job.setInputFile( it->first );
+ ifile = it->first;
+ it = args.erase( it );
+ } else {
+ log_info() << "found another non option on command line. Two input files? " << it->first << endl;
+ always_local = true;
+ args = backup;
+ job.setInputFile( string() );
+ break;
+ }
+ }
+
+ if ( ifile.find( '.' ) != string::npos ) {
+ string::size_type dot_index = ifile.find_last_of( '.' );
+ string ext = ifile.substr( dot_index + 1 );
+
+ if (ext == "cc"
+ || ext == "cpp" || ext == "cxx"
+ || ext == "cp" || ext == "c++"
+ || ext == "C" || ext == "ii") {
+#if CLIENT_DEBUG
+ if ( job.language() != CompileJob::Lang_CXX )
+ log_info() << "switching to C++ for " << ifile << endl;
+#endif
+ job.setLanguage( CompileJob::Lang_CXX );
+ } else if(ext == "mi" || ext == "m"
+ || ext == "mii" || ext == "mm"
+ || ext == "M" ) {
+ job.setLanguage( CompileJob::Lang_OBJC );
+ } else if ( ext == "s" || ext == "S" || // assembler
+ ext == "ads" || ext == "adb" || // ada
+ ext == "f" || ext == "for" || // fortran
+ ext == "FOR" || ext == "F" ||
+ ext == "fpp" || ext == "FPP" ||
+ ext == "r" ) {
+ always_local = true;
+ } else if ( ext != "c" && ext != "i" ) { // C is special, it depends on arg[0] name
+ log_warning() << "unknown extension " << ext << endl;
+ always_local = true;
+ }
+
+ if ( !always_local && ofile.empty() ) {
+ ofile = ifile.substr( 0, dot_index );
+ if ( seen_s )
+ ofile += ".s";
+ else
+ ofile += ".o";
+ string::size_type slash = ofile.find_last_of( '/' );
+ if ( slash != string::npos )
+ ofile = ofile.substr( slash + 1 );
+ }
+
+ if ( !always_local && seen_md && !seen_mf) {
+ string dfile = ofile.substr( 0, ofile.find_last_of( '.' ) ) + ".d";
+
+#if CLIENT_DEBUG
+ log_info() << "dep file: " << dfile << endl;
+#endif
+
+ args.append("-MF", Arg_Local);
+ args.append(dfile, Arg_Local);
+ }
+ }
+
+ } else {
+ job.setInputFile( string() );
+ }
+
+ struct stat st;
+ if ( ofile.empty() || (!stat( ofile.c_str(), &st ) && !S_ISREG( st.st_mode )))
+ always_local = true;
+
+ job.setFlags( args );
+ job.setOutputFile( ofile );
+
+#if CLIENT_DEBUG
+ trace() << "scanned result: local args=" << concat_args( job.localFlags() )
+ << ", remote args=" << concat_args( job.remoteFlags() )
+ << ", rest=" << concat_args( job.restFlags() )
+ << ", local=" << always_local
+ << ", compiler=" << job.compilerName()
+ << ", lang="
+ << (job.language() != CompileJob::Lang_Custom ?
+ (job.language() == CompileJob::Lang_CXX ? "C++" : "C" ) : "<custom>")
+ << endl;
+#endif
+
+ return always_local;
+}
diff --git a/client/client.h b/client/client.h
new file mode 100644
index 0000000..4eabcfe
--- /dev/null
+++ b/client/client.h
@@ -0,0 +1,61 @@
+/*
+ This file is part of icecc.
+
+ Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ 2004 Stephan Kulow <coolo@suse.de>
+
+ 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.
+*/
+
+#ifndef _CLIENT_H_
+#define _CLIENT_H_
+
+#include <job.h>
+#include <comm.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "exitcode.h"
+#include "logging.h"
+#include "util.h"
+
+class MsgChannel;
+
+extern std::string remote_daemon;
+
+/* in remote.cpp */
+extern std::string get_absfilename( const std::string &_file );
+
+/* In arg.cpp. */
+extern bool analyse_argv (const char * const *argv, CompileJob &job, bool icerun);
+
+/* In cpp.cpp. */
+extern pid_t call_cpp (CompileJob &job, int fdwrite, int fdread = -1);
+
+/* In local.cpp. */
+extern int build_local (CompileJob& job, MsgChannel *daemon, struct rusage *usage =0);
+extern std::string find_compiler( CompileJob::Language lang );
+
+/* In remote.cpp - permill is the probability it will be compiled three times */
+extern int build_remote (CompileJob &job, MsgChannel *scheduler, const Environments &envs, int permill);
+
+/* safeguard.cpp */
+extern void dcc_increment_safeguard(void);
+extern int dcc_recursion_safeguard(void);
+
+extern Environments parse_icecc_version( const std::string &target );
+
+#endif
diff --git a/client/cpp.cpp b/client/cpp.cpp
new file mode 100644
index 0000000..d4dc69c
--- /dev/null
+++ b/client/cpp.cpp
@@ -0,0 +1,149 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*-
+ *
+ * distcc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ *
+ * Run the preprocessor. Client-side only.
+ **/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "client.h"
+
+using namespace std;
+
+bool dcc_is_preprocessed(const string& sfile)
+{
+ if( sfile.size() < 3 )
+ return false;
+ int last = sfile.size() - 1;
+ if( sfile[last-1] == '.' && sfile[last] == 'i' )
+ return true; // .i
+ if( sfile[last-2] == '.' && sfile[last-1] == 'i' && sfile[last] == 'i' )
+ return true; // .ii
+ return false;
+}
+
+/**
+ * If the input filename is a plain source file rather than a
+ * preprocessed source file, then preprocess it to a temporary file
+ * and return the name in @p cpp_fname.
+ *
+ * The preprocessor may still be running when we return; you have to
+ * wait for @p cpp_fid to exit before the output is complete. This
+ * allows us to overlap opening the TCP socket, which probably doesn't
+ * use many cycles, with running the preprocessor.
+ **/
+pid_t call_cpp(CompileJob &job, int fdwrite, int fdread)
+{
+ flush_debug();
+ pid_t pid = fork();
+ if (pid == -1) {
+ log_perror("failed to fork:");
+ return -1; /* probably */
+ } else if (pid != 0) {
+ /* Parent. Close the write fd. */
+ if (fdwrite > -1)
+ close (fdwrite);
+ return pid;
+ } else {
+ /* Child. Close the read fd, in case we have one. */
+ if (fdread > -1)
+ close (fdread);
+ int ret = dcc_ignore_sigpipe(0);
+ if (ret) /* set handler back to default */
+ _exit(ret);
+
+ char **argv;
+ if ( dcc_is_preprocessed( job.inputFile() ) ) {
+ /* already preprocessed, great.
+ write the file to the fdwrite (using cat) */
+ argv = new char*[2+1];
+ argv[0] = strdup( "/bin/cat" );
+ argv[1] = strdup( job.inputFile().c_str() );
+ argv[2] = 0;
+ } else {
+ list<string> flags = job.localFlags();
+ /* This has a duplicate meaning. it can either include a file
+ for preprocessing or a precompiled header. decide which one. */
+ for (list<string>::iterator it = flags.begin();
+ it != flags.end();) {
+ if ((*it) == "-include") {
+ ++it;
+ if (it != flags.end()) {
+ std::string p = (*it);
+ if (access(p.c_str(), R_OK) && !access((p + ".gch").c_str(), R_OK)) {
+ list<string>::iterator o = --it;
+ it++;
+ flags.erase(o);
+ o = it++;
+ flags.erase(o);
+ }
+ }
+ }
+ else
+ ++it;
+ }
+
+ appendList( flags, job.restFlags() );
+ int argc = flags.size();
+ argc++; // the program
+ argc += 2; // -E file.i
+ argv = new char*[argc + 1];
+ argv[0] = strdup( find_compiler( job.language() ).c_str() );
+ int i = 1;
+ for ( list<string>::const_iterator it = flags.begin();
+ it != flags.end(); ++it) {
+ argv[i++] = strdup( it->c_str() );
+ }
+ argv[i++] = strdup( "-E" );
+ argv[i++] = strdup( job.inputFile().c_str() );
+ argv[i++] = 0;
+ }
+
+#if 0
+ printf( "forking " );
+ for ( int index = 0; argv[index]; index++ )
+ printf( "%s ", argv[index] );
+ printf( "\n" );
+#endif
+
+ if (fdwrite != STDOUT_FILENO) {
+ /* Ignore failure */
+ close(STDOUT_FILENO);
+ dup2(fdwrite, STDOUT_FILENO);
+ close(fdwrite);
+ }
+
+ dcc_increment_safeguard();
+
+ _exit(execv(argv[0], argv));
+ }
+}
diff --git a/client/icecc-create-env b/client/icecc-create-env
new file mode 100755
index 0000000..e99338c
--- /dev/null
+++ b/client/icecc-create-env
@@ -0,0 +1,195 @@
+#! /usr/bin/env bash
+# icecc -- A simple distributed compiler system
+#
+# Copyright (C) 2004 by the Icecream Authors
+# GPL
+
+target_files=
+
+is_darwin=0
+if test `uname` = Darwin; then
+ is_darwin=1
+fi
+
+is_contained ()
+{
+ case " $target_files " in
+ *" $1 "* ) return 0 ;;
+ *"=$1 "* ) return 0;;
+ * ) return 1 ;;
+ esac
+}
+
+add_file ()
+{
+ local name="$1"
+ local path="$1";
+ if test -n "$2"; then
+ name="$2"
+ fi
+ test -z "$name" && return
+ # ls -H isn't really the same as readlink, but
+ # readlink is not portable enough.
+ path=`ls -H $path`
+ toadd="$name=$path"
+ if test "$name" = "$path"; then
+ toadd=$path
+ fi
+ is_contained "$toadd" && return
+ echo "adding file $toadd"
+ target_files="$target_files $toadd"
+ if test -x "$path"; then
+ # Only call ldd when it makes sense
+ if file -L "$path" | grep 'ELF' > /dev/null 2>&1; then
+ if ! file -L "$path" | grep 'static' > /dev/null 2>&1; then
+ # ldd now outputs ld as /lib/ld-linux.so.xx on current nptl based glibc
+ # this regexp parse the outputs like:
+ # ldd /usr/bin/gcc
+ # linux-gate.so.1 => (0xffffe000)
+ # libc.so.6 => /lib/tls/libc.so.6 (0xb7e81000)
+ # /lib/ld-linux.so.2 (0xb7fe8000)
+ # covering both situations ( with => and without )
+ for lib in `ldd "$path" | sed -n 's,^[^/]*\(/[^ ]*\).*,\1,p'`; do
+ test -f "$lib" || continue
+ # Check wether the same library also exists in the parent directory,
+ # and prefer that on the assumption that it is a more generic one.
+ local baselib=`echo "$lib" | sed 's,\(/[^/]*\)/.*\(/[^/]*\)$,\1\2,'`
+ test -f "$baselib" && lib=$baselib
+ add_file "$lib"
+ done
+ fi
+ elif test "$is_darwin" = 1; then
+ for lib in `otool -L "$path" | sed -n 's,^[^/]*\(/[^ ]*\).*,\1,p'`; do
+ test -f "$lib" || continue
+ # Check wether the same library also exists in the parent directory,
+ # and prefer that on the assumption that it is a more generic one.
+ local baselib=`echo "$lib" | sed 's,\(/[^/]*\)/.*\(/[^/]*\)$,\1\2,'`
+ test -f "$baselib" && lib=$baselib
+ add_file "$lib"
+ done
+ fi
+ fi
+}
+
+# backward compat
+if test "$1" = "--respect-path"; then
+ shift
+fi
+
+added_gcc=$1
+shift
+added_gxx=$1
+
+if test -z "$added_gcc" || test -z "$added_gxx"; then
+ echo "usage: $0 <gcc_path> <g++_path>"
+ exit 1
+fi
+
+if ! test -x "$added_gcc" ; then
+ echo "'$added_gcc' is no executable."
+ exit 1
+fi
+
+if ! test -x "$added_gxx" ; then
+ echo "'$added_gcc' is no executable."
+ exit 1
+fi
+
+add_file $added_gcc /usr/bin/gcc
+add_file $added_gxx /usr/bin/g++
+add_file /usr/bin/as
+
+if test "$is_darwin" = 1; then
+ # add dynamic linker
+ add_file /usr/lib/dyld
+ real_file=`/usr/bin/gcc --version | head -n 1 2>&1 | cut -d" " -f1`
+ add_file /usr/bin/$real_file
+ real_file=`/usr/bin/g++ --version | head -n 1 2>&1 | cut -d" " -f1`
+ add_file /usr/bin/$real_file
+ real_file=`/usr/bin/as -micha -- < /dev/null 2>&1 | cut -d: -f1`
+ add_file $real_file
+fi
+
+
+add_file `$added_gcc -print-prog-name=cc1` /usr/bin/cc1
+add_file `$added_gxx -print-prog-name=cc1plus` /usr/bin/cc1plus
+specfile=`$added_gcc -print-file-name=specs`
+if test -n "$specfile" && test "$specfile" != "specs" && test -e "$specfile"; then
+ add_file "$specfile"
+fi
+
+plugin_name=liblto_plugin.so
+plugin=`$added_gcc -print-prog-name=$plugin_name`
+if test -n "$plugin" && test "$plugin" != "$plugin_name" && test -e "$plugin"; then
+ add_file "$plugin" "$plugin"
+fi
+
+# for ldconfig -r to work, ld.so.conf must not contain relative paths
+# in include directives. Make them absolute.
+tmp_ld_so_conf=`mktemp /tmp/icecc_ld_so_confXXXXXX`
+while read directive path; do
+ if [ "$directive" = "include" -a "${path:0:1}" != "/" ]; then
+ path="/etc/$path"
+ fi
+ echo "$directive $path"
+done </etc/ld.so.conf >$tmp_ld_so_conf
+add_file $tmp_ld_so_conf /etc/ld.so.conf
+
+tempdir=`mktemp -d /tmp/iceccenvXXXXXX`
+# special case for weird multilib setups
+for dir in /lib /lib64 /usr/lib /usr/lib64; do
+ test -L $dir && cp -p $dir $tempdir$dir
+done
+
+new_target_files=
+for i in $target_files; do
+ case $i in
+ *=/*)
+ target=`echo $i | cut -d= -f1`
+ path=`echo $i | cut -d= -f2`
+ ;;
+ *)
+ path=$i
+ target=$i
+ ;;
+ esac
+ mkdir -p $tempdir/`dirname $target`
+ cp -p $path $tempdir/$target
+ if test -f $tempdir/$target -a -x $tempdir/$target; then
+ strip -s $tempdir/$target 2>/dev/null
+ fi
+ target=`echo $target | cut -b2-`
+ new_target_files="$new_target_files $target"
+done
+
+if test -x /sbin/ldconfig; then
+ mkdir -p $tempdir/var/cache/ldconfig
+ /sbin/ldconfig -r $tempdir
+ new_target_files="$new_target_files etc/ld.so.cache"
+fi
+
+md5sum=NONE
+for file in /usr/bin/md5sum /bin/md5 /usr/bin/md5 /sbin/md5; do
+ if test -x $file; then
+ md5sum=$file
+ break
+ fi
+done
+
+# now sort the files in order to make the md5sums independent
+# of ordering
+target_files=`for i in $new_target_files; do echo $i; done | sort`
+md5=`for i in $target_files; do $md5sum $tempdir/$i; done | sed -e 's/ .*$//' | $md5sum | sed -e 's/ .*$//'` || {
+ echo "Couldn't compute MD5 sum."
+ exit 2
+}
+echo "creating $md5.tar.gz"
+mydir=`pwd`
+cd $tempdir
+tar -czhf "$mydir/$md5".tar.gz $target_files || {
+ echo "Couldn't create archive"
+ exit 3
+}
+cd ..
+rm -rf $tempdir
+rm -f $tmp_ld_so_conf
diff --git a/client/local.cpp b/client/local.cpp
new file mode 100644
index 0000000..2d29a8a
--- /dev/null
+++ b/client/local.cpp
@@ -0,0 +1,287 @@
+/*
+ This file is part of Icecream.
+
+ Copyright (c) 2004 Stephan Kulow <coolo@suse.de>
+ 2002, 2003 by Martin Pool <mbp@samba.org>
+
+ 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.
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include <comm.h>
+#include "client.h"
+
+using namespace std;
+
+extern const char * rs_program_name;
+
+#define CLIENT_DEBUG 0
+
+/*
+ * Get the name of the compiler depedant on the
+ * language of the job and the environment
+ * variable set. This is useful for native cross-compilers.
+ * (arm-linux-gcc for example)
+ */
+
+static string get_compiler_name( CompileJob::Language lang )
+{
+ string compiler_name = "gcc";
+
+ const char* env;
+ if ( (env = getenv( "ICECC_CC" )) )
+ compiler_name = env;
+
+ if (lang == CompileJob::Lang_CXX) {
+ compiler_name = "g++";
+ if ((env = getenv ("ICECC_CXX")))
+ compiler_name = env;
+ }
+
+ return compiler_name;
+}
+
+
+static string path_lookup(const string& compiler)
+{
+ if ( compiler.at( 0 ) == '/' )
+ return compiler;
+
+ string path = ::getenv( "PATH" );
+ string::size_type begin = 0;
+ string::size_type end = 0;
+ struct stat s;
+ bool after_selflink = false;
+ string best_match;
+
+ while ( end != string::npos ) {
+ end = path.find_first_of( ':', begin );
+ string part;
+ if ( end == string::npos )
+ part = path.substr( begin );
+ else
+ part = path.substr( begin, end - begin );
+ begin = end + 1;
+
+ part = part + '/' + compiler;
+ if ( !lstat( part.c_str(), &s ) ) {
+ if ( S_ISLNK( s.st_mode ) ) {
+ char buffer[PATH_MAX];
+ int ret = readlink( part.c_str(), buffer, PATH_MAX );
+ if ( ret == -1 ) {
+ log_error() << "readlink failed " << strerror( errno ) << endl;
+ continue;
+ }
+ buffer[ret] = 0;
+ string target = find_basename( buffer );
+ if ( target == rs_program_name
+ || (after_selflink
+ && (target == "tbcompiler" || target == "distcc"
+ || target == "colorgcc"))) {
+ // this is a link pointing to us, ignore it
+ after_selflink = true;
+ continue;
+ }
+ } else if ( !S_ISREG( s.st_mode ) ) {
+ // It's not a link and not a file, so just ignore it. We don't
+ // want to accidentially attempt to execute directories.
+ continue;
+ }
+ best_match = part;
+
+ if ( after_selflink )
+ return part;
+ }
+ }
+ if ( best_match.empty() )
+ log_error() << "couldn't find any " << compiler << endl;
+ return best_match;
+}
+
+string find_compiler( CompileJob::Language lang )
+{
+ string compiler = get_compiler_name( lang );
+
+ return path_lookup(compiler);
+}
+
+static volatile int lock_fd = 0;
+static volatile int user_break_signal = 0;
+static volatile pid_t child_pid;
+
+static void handle_user_break( int sig )
+{
+ if ( lock_fd )
+ dcc_unlock( lock_fd );
+ lock_fd = 0;
+ user_break_signal = sig;
+
+ if (child_pid != 0)
+ kill ( sig, child_pid);
+
+ signal( sig, handle_user_break );
+}
+
+/**
+ * Invoke a compiler locally. This is, obviously, the alternative to
+ * dcc_compile_remote().
+ *
+ * The server does basically the same thing, but it doesn't call this
+ * routine because it wants to overlap execution of the compiler with
+ * copying the input from the network.
+ *
+ * This routine used to exec() the compiler in place of distcc. That
+ * is slightly more efficient, because it avoids the need to create,
+ * schedule, etc another process. The problem is that in that case we
+ * can't clean up our temporary files, and (not so important) we can't
+ * log our resource usage.
+ *
+ **/
+int build_local(CompileJob& job, MsgChannel *local_daemon, struct rusage *used)
+{
+ list<string> arguments;
+
+ string compiler_name;
+ if (job.language() != CompileJob::Lang_Custom )
+ compiler_name = find_compiler( job.language() );
+ else
+ compiler_name = path_lookup(job.compilerName());
+
+ trace() << "invoking: " << compiler_name << endl;
+
+ if ( compiler_name.empty() ) {
+ log_error() << "could not find " << job.compilerName() << " in PATH." << endl;
+ return EXIT_NO_SUCH_FILE;
+ }
+
+ arguments.push_back( compiler_name );
+ appendList( arguments, job.allFlags() );
+
+ if ( !job.inputFile().empty() )
+ arguments.push_back( job.inputFile() );
+ if ( !job.outputFile().empty() ) {
+ arguments.push_back( "-o" );
+ arguments.push_back( job.outputFile() );
+ }
+ char **argv = new char*[arguments.size() + 1];
+ int argc = 0;
+ for ( list<string>::const_iterator it = arguments.begin();
+ it != arguments.end(); ++it )
+ argv[argc++] = strdup( it->c_str() );
+ argv[argc] = 0;
+#if CLIENT_DEBUG
+ trace() << "execing ";
+ for ( int i = 0; argv[i]; i++ )
+ trace() << argv[i] << " ";
+ trace() << endl;
+#endif
+
+ if ( !local_daemon ) {
+ int fd;
+ if ( !dcc_lock_host(fd ) ) {
+ log_error() << "can't lock for local job" << endl;
+ return EXIT_DISTCC_FAILED;
+ }
+ lock_fd = fd;
+ }
+
+ bool color_output = job.language() != CompileJob::Lang_Custom
+ && colorify_wanted();
+ int pf[2];
+
+ if (color_output && pipe(pf))
+ color_output = false;
+
+ if ( used || color_output ) {
+ flush_debug();
+ child_pid = fork();
+ }
+
+ if ( child_pid ) {
+ if (color_output)
+ close(pf[1]);
+
+ // setup interrupt signals, so that the JobLocalBeginMsg will
+ // have a matching JobLocalDoneMsg
+ void (*old_sigint)(int) = signal( SIGINT, handle_user_break );
+ void (*old_sigterm)(int) = signal( SIGTERM, handle_user_break );
+ void (*old_sigquit)(int) = signal( SIGQUIT, handle_user_break );
+ void (*old_sighup)(int) = signal( SIGHUP, handle_user_break );
+
+ if (color_output) {
+ string s_ccout;
+ char buf[250];
+ int r;
+ for(;;) {
+ while((r = read(pf[0], buf, sizeof(buf)-1)) > 0) {
+ buf[r] = '\0';
+ s_ccout.append(buf);
+ }
+ if (r == 0)
+ break;
+ if ( r < 0 && errno != EINTR)
+ break;
+ }
+ colorify_output(s_ccout);
+ }
+
+ int status = 1;
+ while( wait4( child_pid, &status, 0, used ) < 0 && errno == EINTR)
+ ;
+
+ status = WEXITSTATUS(status);
+
+ signal( SIGINT, old_sigint );
+ signal( SIGTERM, old_sigterm );
+ signal( SIGQUIT, old_sigquit );
+ signal( SIGHUP, old_sighup );
+ if( user_break_signal )
+ raise( user_break_signal );
+ if ( lock_fd )
+ dcc_unlock( lock_fd );
+ return status;
+ } else {
+ dcc_increment_safeguard();
+
+ if (color_output) {
+ close(pf[0]);
+ close(2);
+ dup2(pf[1], 2);
+ }
+
+ int ret = execv( argv[0], argv );
+ if ( lock_fd )
+ dcc_unlock( lock_fd );
+ if (ret) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "ICECC[%d]: %s:", getpid(), argv[0]);
+ log_perror(buf);
+ }
+ _exit ( ret );
+ }
+}
diff --git a/client/main.cpp b/client/main.cpp
new file mode 100644
index 0000000..d014835
--- /dev/null
+++ b/client/main.cpp
@@ -0,0 +1,373 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil -*-
+ *
+ * icecc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2003, 2004 by the Icecream Authors
+ *
+ * based on distcc
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+
+ /* 4: The noise of a multitude in the
+ * mountains, like as of a great people; a
+ * tumultuous noise of the kingdoms of nations
+ * gathered together: the LORD of hosts
+ * mustereth the host of the battle.
+ * -- Isaiah 13 */
+
+
+
+#include "config.h"
+
+// Required by strsignal() on some systems.
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <cassert>
+#include <limits.h>
+#include <sys/time.h>
+#include <comm.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/wait.h>
+
+#include "client.h"
+
+/* Name of this program, for trace.c */
+const char * rs_program_name = "icecc";
+
+using namespace std;
+
+static void dcc_show_usage(void)
+{
+ printf(
+"Usage:\n"
+" icecc [compile options] -o OBJECT -c SOURCE\n"
+" icecc --help\n"
+"\n"
+"Options:\n"
+" --help explain usage and exit\n"
+" --version show version and exit\n"
+" --build-native create icecc environment\n"
+"Environment Variables:\n"
+" ICECC if set to \"no\", just exec the real gcc\n"
+" ICECC_VERSION use a specific icecc environment, see create-env\n"
+" ICECC_DEBUG [info | warnings | debug]\n"
+" sets verboseness of icecream client.\n"
+" ICECC_LOGFILE if set, additional debug information is logged to the specified file\n"
+" ICECC_REPEAT_RATE the number of jobs out of 1000 that should be\n"
+" compiled on multiple hosts to ensure that they're\n"
+" producing the same output. The default is 10.\n"
+" ICECC_PREFERRED_HOST overrides scheduler decisions if set.\n"
+" IECCC_CC set C compiler name (default gcc).\n"
+" ICECC_CXX set C++ compiler name (default g++).\n"
+"\n");
+}
+
+static void icerun_show_usage(void)
+{
+ printf(
+"Usage:\n"
+" icerun [compile options] -o OBJECT -c SOURCE\n"
+" icerun --help\n"
+"\n"
+"Options:\n"
+" --help explain usage and exit\n"
+" --version show version and exit\n"
+"Environment Variables:\n"
+" ICECC if set to \"no\", just exec the real gcc\n"
+" ICECC_DEBUG [info | warnings | debug]\n"
+" sets verboseness of icecream client.\n"
+" ICECC_LOGFILE if set, additional debug information is logged to the specified file\n"
+"\n");
+}
+
+volatile bool local = false;
+
+static void dcc_client_signalled (int whichsig)
+{
+ if ( !local ) {
+#ifdef HAVE_STRSIGNAL
+ log_info() << rs_program_name << ": " << strsignal(whichsig) << endl;
+#else
+ log_info() << "terminated by signal " << whichsig << endl;
+#endif
+
+ // dcc_cleanup_tempfiles();
+ }
+
+ signal(whichsig, SIG_DFL);
+ raise(whichsig);
+}
+
+static void dcc_client_catch_signals(void)
+{
+ signal(SIGTERM, &dcc_client_signalled);
+ signal(SIGINT, &dcc_client_signalled);
+ signal(SIGHUP, &dcc_client_signalled);
+}
+
+static string read_output( const char *command )
+{
+ FILE *f = popen( command, "r" );
+ string output;
+ if ( !f ) {
+ log_error() << "no pipe " << strerror( errno ) << endl;
+ return output;
+ }
+ char buffer[PATH_MAX];
+ while ( !feof( f ) ) {
+ size_t bytes = fread( buffer, 1, PATH_MAX - 1, f );
+ buffer[bytes] = 0;
+ output += buffer;
+ }
+
+ pclose( f );
+ // get rid of the endline
+ return output.substr(0, output.length()-1);
+}
+
+static int create_native()
+{
+ struct stat st;
+ string gcc, gpp;
+
+ // perhaps we're on gentoo
+ if ( !lstat("/usr/bin/gcc-config", &st) ) {
+ string gccpath=read_output("/usr/bin/gcc-config -B") + "/";
+ gcc=gccpath + "gcc";
+ gpp=gccpath + "g++";
+ } else {
+ gcc = find_compiler( CompileJob::Lang_C );
+ gpp = find_compiler( CompileJob::Lang_CXX );
+ }
+
+ if ( gcc.empty() || gpp.empty())
+ return 1;
+
+ if ( lstat( PLIBDIR "/icecc-create-env", &st ) ) {
+ log_error() << PLIBDIR "/icecc-create-env does not exist\n";
+ return 1;
+ }
+
+ char **argv = new char*[4];
+ argv[0] = strdup( PLIBDIR "/icecc-create-env" );
+ argv[1] = strdup( gcc.c_str() );
+ argv[2] = strdup( gpp.c_str() );
+ argv[3] = NULL;
+
+ return execv(argv[0], argv);
+
+}
+
+int main(int argc, char **argv)
+{
+ char *env = getenv( "ICECC_DEBUG" );
+ int debug_level = Error;
+ if ( env ) {
+ if ( !strcasecmp( env, "info" ) ) {
+ debug_level |= Info|Warning;
+ } else if ( !strcasecmp( env, "warnings" ) ) {
+ debug_level |= Warning; // taking out warning
+ } else // any other value
+ debug_level |= Info|Debug|Warning;
+ }
+
+ std::string logfile;
+ if (const char* logfileEnv = getenv("ICECC_LOGFILE"))
+ logfile = logfileEnv;
+ setup_debug(debug_level, logfile, "ICECC");
+
+ CompileJob job;
+ bool icerun = false;
+
+ string compiler_name = argv[0];
+ dcc_client_catch_signals();
+
+ if ( find_basename( compiler_name ) == rs_program_name) {
+ if ( argc > 1 ) {
+ string arg = argv[1];
+ if ( arg == "--help" ) {
+ dcc_show_usage();
+ return 0;
+ }
+ if ( arg == "--version" ) {
+ printf( "ICECC " VERSION "\n" );
+ return 0;
+ }
+ if ( arg == "--build-native" )
+ return create_native();
+ if ( arg.size() > 0 && arg.at(0) == '/' )
+ job.setCompilerName(arg);
+ }
+ } else if ( find_basename( compiler_name ) == "icerun") {
+ icerun = true;
+ if ( argc > 1 ) {
+ string arg = argv[1];
+ if ( arg == "--help" ) {
+ icerun_show_usage();
+ return 0;
+ }
+ if ( arg == "--version" ) {
+ printf( "ICERUN " VERSION "\n" );
+ return 0;
+ }
+ if ( arg.size() > 0 )
+ job.setCompilerName(arg);
+ }
+ } else {
+ char buf[ PATH_MAX ];
+ buf[ PATH_MAX - 1 ] = '\0';
+ // check if it's a symlink to icerun
+ if( readlink( compiler_name.c_str(), buf, PATH_MAX - 1 ) >= 0 && find_basename( buf ) == "icerun" ) {
+ icerun = true;
+ }
+ }
+
+ int sg_level = dcc_recursion_safeguard();
+
+ if (sg_level > 0) {
+ log_error() << "icecream seems to have invoked itself recursively!" << endl;
+ return EXIT_RECURSION;
+ }
+
+ /* Ignore SIGPIPE; we consistently check error codes and will
+ * see the EPIPE. */
+ dcc_ignore_sigpipe(1);
+
+ local |= analyse_argv( argv, job, icerun );
+
+ /* if ICECC is set to no, then run job locally */
+ char* icecc = getenv("ICECC");
+ if ( icecc && !strcasecmp(icecc, "no") )
+ return build_local( job, 0 );
+
+ MsgChannel *local_daemon = Service::createChannel( "127.0.0.1", 10245, 0/*timeout*/);
+ if ( ! local_daemon ) {
+ log_warning() << "no local daemon found\n";
+ return build_local( job, 0 );
+ }
+
+ Environments envs;
+
+ if ( !local ) {
+ if ( getenv( "ICECC_VERSION" ) ) { // if set, use it, otherwise take default
+ try {
+ envs = parse_icecc_version( job.targetPlatform() );
+ } catch ( int x ) {
+ // we just build locally
+ }
+ } else {
+ if ( !local_daemon->send_msg( GetNativeEnvMsg() ) ) {
+ log_warning() << "failed to write get native environment\n";
+ goto do_local_error;
+ }
+
+ // the timeout is high because it creates the native version
+ Msg *umsg = local_daemon->get_msg(4 * 60);
+ string native;
+ if ( umsg && umsg->type == M_NATIVE_ENV )
+ native = static_cast<UseNativeEnvMsg*>( umsg )->nativeVersion;
+
+ if ( native.empty() || ::access( native.c_str(), R_OK ) ) {
+ log_warning() << "daemon can't determine native environment. Set $ICECC_VERSION to an icecc environment.\n";
+ } else {
+ envs.push_back(make_pair( job.targetPlatform(), native ) );
+ log_info() << "native " << native << endl;
+ }
+
+ delete umsg;
+ }
+
+ // we set it to local so we tell the local daemon about it - avoiding file locking
+ if ( envs.size() == 0 )
+ local = true;
+
+ for ( Environments::const_iterator it = envs.begin(); it != envs.end(); ++it ) {
+ trace() << "env: " << it->first << " '" << it->second << "'" << endl;
+ if ( ::access( it->second.c_str(), R_OK ) ) {
+ log_error() << "can't read environment " << it->second << endl;
+ local = true;
+ }
+ }
+ }
+
+ int ret;
+ if ( local ) {
+ log_block b("building_local");
+ struct rusage ru;
+ Msg* startme = 0L;
+
+ /* Inform the daemon that we like to start a job. */
+ if (local_daemon->send_msg( JobLocalBeginMsg( 0, get_absfilename( job.outputFile() )))) {
+ /* Now wait until the daemon gives us the start signal. 40 minutes
+ should be enough for all normal compile or link jobs. */
+ startme = local_daemon->get_msg (40*60);
+ }
+
+ /* If we can't talk to the daemon anymore we need to fall back
+ to lock file locking. */
+ if (!startme || startme->type != M_JOB_LOCAL_BEGIN)
+ goto do_local_error;
+ ret = build_local( job, local_daemon, &ru );
+ } else {
+ try {
+ // check if it should be compiled three times
+ const char *s = getenv( "ICECC_REPEAT_RATE" );
+ int rate = s ? atoi( s ) : 0;
+ ret = build_remote( job, local_daemon, envs, rate);
+ /* We have to tell the local daemon that everything is fine and
+ that the remote daemon will send the scheduler our done msg.
+ If we don't, the local daemon will have to assume the job failed
+ and tell the scheduler - and that fail message may arrive earlier
+ than the remote daemon's success msg. */
+ if (ret == 0)
+ local_daemon->send_msg (EndMsg());
+ } catch ( int error ) {
+ if (remote_daemon.size())
+ log_error() << "got exception " << error
+ << " (" << remote_daemon.c_str() << ") " << endl;
+ else
+ log_error() << "got exception " << error << " (this should be an exception!)" <<
+ endl;
+
+ /* currently debugging a client? throw an error then */
+ if (debug_level != Error)
+ return error;
+ goto do_local_error;
+ }
+ }
+ delete local_daemon;
+ return ret;
+
+do_local_error:
+ delete local_daemon;
+ return build_local( job, 0 );
+}
diff --git a/client/md5.c b/client/md5.c
new file mode 100644
index 0000000..e627358
--- /dev/null
+++ b/client/md5.c
@@ -0,0 +1,392 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+#include <string.h>
+main()
+{
+ static const char *const test[7] = {
+ "", /*d41d8cd98f00b204e9800998ecf8427e*/
+ "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+ "abc", /*900150983cd24fb0d6963f7d28e17f72*/
+ "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+ "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ /*d174ab98d277d9f5a5611c2c9f419d9f*/
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+ };
+ int i;
+
+ for (i = 0; i < 7; ++i) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ printf("MD5 (\"%s\") = ", test[i]);
+ for (di = 0; di < 16; ++di)
+ printf("%02x", digest[di]);
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ md5_word_t X[16];
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/client/md5.h b/client/md5.h
new file mode 100644
index 0000000..501cdbe
--- /dev/null
+++ b/client/md5.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints. Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+#ifdef P1
+void md5_init(P1(md5_state_t *pms));
+#else
+void md5_init(md5_state_t *pms);
+#endif
+
+/* Append a string to the message. */
+#ifdef P3
+void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes));
+#else
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+#endif
+
+/* Finish the message and return the digest. */
+#ifdef P2
+void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16]));
+#else
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#endif
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/client/remote.cpp b/client/remote.cpp
new file mode 100644
index 0000000..495d091
--- /dev/null
+++ b/client/remote.cpp
@@ -0,0 +1,777 @@
+/*
+ This file is part of Icecream.
+
+ Copyright (c) 2004 Stephan Kulow <coolo@suse.de>
+ 2002, 2003 by Martin Pool <mbp@samba.org>
+
+ 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.
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+
+#ifdef __FreeBSD__
+// Grmbl Why is this needed? We don't use readv/writev
+#include <sys/uio.h>
+#endif
+
+#include <fcntl.h>
+#include <signal.h>
+#include <limits.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <map>
+#include <algorithm>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef HAVE_RSYNC
+#include <librsync.h>
+#endif
+
+#include <comm.h>
+#include "client.h"
+#include "tempfile.h"
+#include "md5.h"
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+using namespace std;
+
+std::string remote_daemon;
+
+Environments
+parse_icecc_version(const string &target_platform )
+{
+ Environments envs;
+
+ string icecc_version = getenv( "ICECC_VERSION");
+ assert( !icecc_version.empty() );
+
+ // free after the C++-Programming-HOWTO
+ string::size_type lastPos = icecc_version.find_first_not_of(',', 0);
+ string::size_type pos = icecc_version.find_first_of(',', lastPos);
+
+ list<string> platforms;
+
+ while (pos != string::npos || lastPos != string::npos)
+ {
+ string couple = icecc_version.substr(lastPos, pos - lastPos);
+ string platform = target_platform;
+ string version = couple;
+ string::size_type colon = couple.find( ':' );
+ if ( colon != string::npos ) {
+ platform = couple.substr( 0, colon );
+ version = couple.substr( colon + 1, couple.length() );
+ }
+
+ // Skip delimiters. Note the "not_of"
+ lastPos = icecc_version.find_first_not_of(',', pos);
+ // Find next "non-delimiter"
+ pos = icecc_version.find_first_of(',', lastPos);
+
+ if (find(platforms.begin(), platforms.end(), platform) != platforms.end()) {
+ log_error() << "there are two environments for platform " << platform << " - ignoring " << version << endl;
+ continue;
+ }
+
+ if ( ::access( version.c_str(), R_OK ) ) {
+ log_error() << "$ICECC_VERSION has to point to an existing file to be installed " << version << endl;
+ continue;
+ }
+
+ struct stat st;
+ if ( lstat( version.c_str(), &st ) || !S_ISREG( st.st_mode ) || st.st_size < 500 ) {
+ log_error() << "$ICECC_VERSION has to point to an existing file to be installed " << version << endl;
+ continue;
+ }
+
+ envs.push_back(make_pair( platform, version ) );
+ platforms.push_back(platform);
+
+ }
+
+ return envs;
+}
+
+static bool
+endswith( const string &orig, const char *suff, string &ret )
+{
+ size_t len = strlen( suff );
+ if ( orig.size() > len && orig.substr( orig.size() - len ) == suff )
+ {
+ ret = orig.substr( 0, orig.size() - len );
+ return true;
+ }
+ return false;
+}
+
+static Environments
+rip_out_paths( const Environments &envs, map<string, string> &version_map, map<string, string> &versionfile_map )
+{
+ version_map.clear();
+
+ Environments env2;
+
+ static const char *suffs[] = { ".tar.bz2", ".tar.gz", ".tar", ".tgz" };
+
+ string versfile;
+
+ for ( Environments::const_iterator it = envs.begin(); it != envs.end(); ++it )
+ {
+ for ( int i = 0; i < 3; i++ )
+ if ( endswith( it->second, suffs[i], versfile ) )
+ {
+ versionfile_map[it->first] = it->second;
+ versfile = find_basename( versfile );
+ version_map[it->first] = versfile;
+ env2.push_back( make_pair( it->first, versfile ) );
+ }
+ }
+ return env2;
+}
+
+
+string
+get_absfilename( const string &_file )
+{
+ string file;
+
+ if ( _file.empty() )
+ return _file;
+
+ if ( _file.at( 0 ) != '/' ) {
+ static char buffer[PATH_MAX];
+
+#ifdef HAVE_GETCWD
+ getcwd(buffer, sizeof( buffer ) );
+#else
+ getwd(buffer);
+#endif
+ buffer[PATH_MAX - 1] = 0;
+ file = string( buffer ) + '/' + _file;
+ } else {
+ file = _file;
+ }
+
+ string::size_type idx = file.find( "/.." );
+ while ( idx != string::npos ) {
+ file.replace( idx, 3, "/" );
+ idx = file.find( "/.." );
+ }
+
+ idx = file.find( "/./" );
+ while ( idx != string::npos ) {
+ file.replace( idx, 3, "/" );
+ idx = file.find( "/./" );
+ }
+ idx = file.find( "//" );
+ while ( idx != string::npos ) {
+ file.replace( idx, 2, "/" );
+ idx = file.find( "//" );
+ }
+ return file;
+}
+
+static UseCSMsg *get_server( MsgChannel *local_daemon )
+{
+ Msg * umsg = local_daemon->get_msg(4 * 60);
+ if (!umsg || umsg->type != M_USE_CS)
+ {
+ log_warning() << "replied not with use_cs " << ( umsg ? ( char )umsg->type : '0' ) << endl;
+ delete umsg;
+ throw( 1 );
+ }
+
+ UseCSMsg *usecs = dynamic_cast<UseCSMsg *>(umsg);
+ return usecs;
+}
+
+static void check_for_failure( Msg *msg, MsgChannel *cserver )
+{
+ if ( msg && msg->type == M_STATUS_TEXT)
+ {
+ log_error() << static_cast<StatusTextMsg*>(msg)->text << " - compiled on " << cserver->name <<endl;
+ throw( 23 );
+ }
+}
+
+static void write_server_cpp(int cpp_fd, MsgChannel *cserver)
+{
+ unsigned char buffer[100000]; // some random but huge number
+ off_t offset = 0;
+ size_t uncompressed = 0;
+ size_t compressed = 0;
+
+#ifdef HAVE_RSYNC
+ unsigned char buffer_sig_out[256];
+ rs_job_t* sig_job = rs_sig_begin (RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN);
+ rs_buffers_t sig_buffer;
+
+ sig_buffer.next_in = (char*) buffer;
+ sig_buffer.avail_in = 0;
+
+ sig_buffer.next_out = (char*) buffer_sig_out;
+ sig_buffer.avail_out = sizeof(buffer_sig_out);
+#endif
+
+ do
+ {
+ ssize_t bytes;
+ do {
+ bytes = read(cpp_fd, buffer + offset, sizeof(buffer) - offset );
+ if (bytes < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ if (bytes < 0) {
+ log_perror( "reading from cpp_fd" );
+ close( cpp_fd );
+ throw( 16 );
+ }
+ break;
+ } while ( 1 );
+#ifdef HAVE_RSYNC
+ sig_buffer.avail_in += bytes;
+#endif
+ offset += bytes;
+ if (!bytes || offset == sizeof( buffer ) )
+ {
+ if ( offset )
+ {
+ FileChunkMsg fcmsg( buffer, offset );
+ if ( !cserver->send_msg( fcmsg ) )
+ {
+ Msg* m = cserver->get_msg(2);
+ check_for_failure(m, cserver);
+
+ log_error() << "write of source chunk to host " << cserver->name.c_str() << endl;
+ log_perror("failed ");
+ close( cpp_fd );
+ throw( 15 );
+ }
+ uncompressed += fcmsg.len;
+ compressed += fcmsg.compressed;
+ offset = 0;
+ }
+ if ( !bytes )
+ break;
+ }
+ } while ( 1 );
+
+ if (compressed)
+ trace() << "sent " << compressed << " bytes (" << (compressed * 100/uncompressed) <<
+ "%)" << endl;
+
+ close( cpp_fd );
+}
+
+
+static int build_remote_int(CompileJob &job, UseCSMsg *usecs, const string &environment, const string &version_file,
+ const char *preproc_file, bool output )
+{
+ string hostname = usecs->hostname;
+ unsigned int port = usecs->port;
+ int job_id = usecs->job_id;
+ bool got_env = usecs->got_env;
+ job.setJobID( job_id );
+ job.setEnvironmentVersion( environment ); // hoping on the scheduler's wisdom
+ trace() << "Have to use host " << hostname << ":" << port << " - Job ID: "
+ << job.jobID() << " - env: " << usecs->host_platform
+ << " - has env: " << (got_env ? "true" : "false")
+ << " - match j: " << usecs->matched_job_id
+ << "\n";
+
+ int status = 255;
+
+ MsgChannel *cserver = 0;
+
+ try {
+ cserver = Service::createChannel(hostname, port, 10);
+ if ( !cserver ) {
+ log_error() << "no server found behind given hostname " << hostname << ":" << port << endl;
+ throw ( 2 );
+ }
+
+ if ( !got_env ) {
+ log_block b("Transfer Environment");
+ // transfer env
+ struct stat buf;
+ if ( stat( version_file.c_str(), &buf ) ) {
+ log_perror( "error stat'ing version file" );
+ throw( 4 );
+ }
+
+ EnvTransferMsg msg( job.targetPlatform(), job.environmentVersion() );
+ if ( !cserver->send_msg( msg ) )
+ throw( 6 );
+
+ int env_fd = open( version_file.c_str(), O_RDONLY );
+ if (env_fd < 0)
+ throw ( 5 );
+
+ write_server_cpp( env_fd, cserver );
+
+ if ( !cserver->send_msg( EndMsg() ) ) {
+ log_error() << "write of environment failed" << endl;
+ throw( 8 );
+ }
+ }
+
+
+ CompileFileMsg compile_file( &job );
+ {
+ log_block b("send compile_file");
+ if ( !cserver->send_msg( compile_file ) ) {
+ log_info() << "write of job failed" << endl;
+ throw( 9 );
+ }
+ }
+
+ if ( !preproc_file ) {
+ int sockets[2];
+ if (pipe(sockets)) {
+ /* for all possible cases, this is something severe */
+ exit(errno);
+ }
+
+ /* This will fork, and return the pid of the child. It will not
+ return for the child itself. If it returns normally it will have
+ closed the write fd, i.e. sockets[1]. */
+ pid_t cpp_pid = call_cpp(job, sockets[1], sockets[0] );
+ if ( cpp_pid == -1 )
+ throw( 18 );
+
+ try {
+ log_block bl2("write_server_cpp from cpp");
+ write_server_cpp( sockets[0], cserver );
+ } catch ( int error ) {
+ kill( cpp_pid, SIGTERM );
+ throw ( error );
+ }
+
+ log_block wait_cpp("wait for cpp");
+ while(waitpid( cpp_pid, &status, 0) < 0 && errno == EINTR)
+ ;
+
+ if ( status ) { // failure
+ delete cserver;
+ cserver = 0;
+ return WEXITSTATUS( status );
+ }
+ } else {
+ int cpp_fd = open( preproc_file, O_RDONLY );
+ if ( cpp_fd < 0 )
+ throw ( 11 );
+
+ log_block cpp_block("write_server_cpp");
+ write_server_cpp( cpp_fd, cserver );
+ }
+
+ {
+ if ( !cserver->send_msg( EndMsg() ) ) {
+ log_info() << "write of end failed" << endl;
+ throw( 12 );
+ }
+ }
+
+ Msg *msg;
+ {
+ log_block wait_cs("wait for cs");
+ msg = cserver->get_msg( 12 * 60 );
+ if ( !msg )
+ throw( 14 );
+ }
+
+ check_for_failure( msg, cserver );
+ if ( msg->type != M_COMPILE_RESULT ) {
+ log_warning() << "waited for compile result, but got " << (char)msg->type << endl;
+ delete msg;
+ throw( 13 );
+ }
+
+ CompileResultMsg *crmsg = dynamic_cast<CompileResultMsg*>( msg );
+ assert ( crmsg );
+
+ status = crmsg->status;
+
+ if ( status && crmsg->was_out_of_memory ) {
+ delete crmsg;
+ log_info() << "the server ran out of memory, recompiling locally" << endl;
+ throw( 17 ); // recompile locally - TODO: handle this as a normal local job not an error case
+ }
+
+ if ( output )
+ {
+ write(STDOUT_FILENO, crmsg->out.c_str(), crmsg->out.size() );
+
+ if(colorify_wanted())
+ colorify_output(crmsg->err);
+ else
+ write(STDERR_FILENO, crmsg->err.c_str(), crmsg->err.size() );
+
+ if ( status && ( crmsg->err.length() || crmsg->out.length() ) )
+ {
+ log_error() << "Compiled on " << hostname << endl;
+ }
+ }
+ delete crmsg;
+
+ assert( !job.outputFile().empty() );
+
+ if( status == 0 ) {
+ string tmp_file = job.outputFile() + "_icetmp";
+ int obj_fd = open( tmp_file.c_str(), O_CREAT|O_TRUNC|O_WRONLY|O_LARGEFILE, 0666 );
+
+ if ( obj_fd == -1 ) {
+ std::string errmsg("can't create ");
+ errmsg += tmp_file + ":";
+ log_perror(errmsg.c_str());
+ return EXIT_DISTCC_FAILED;
+ }
+
+ msg = 0;
+ size_t uncompressed = 0;
+ size_t compressed = 0;
+ while ( 1 ) {
+ delete msg;
+
+ msg = cserver->get_msg(40);
+ if ( !msg ) { // the network went down?
+ unlink( tmp_file.c_str());
+ throw ( 19 );
+ }
+
+ check_for_failure( msg, cserver );
+
+ if ( msg->type == M_END )
+ break;
+
+ if ( msg->type != M_FILE_CHUNK ) {
+ unlink( tmp_file.c_str());
+ delete msg;
+ throw ( 20 );
+ }
+
+ FileChunkMsg *fcmsg = dynamic_cast<FileChunkMsg*>( msg );
+ compressed += fcmsg->compressed;
+ uncompressed += fcmsg->len;
+ if ( write( obj_fd, fcmsg->buffer, fcmsg->len ) != ( ssize_t )fcmsg->len ) {
+ unlink( tmp_file.c_str());
+ delete msg;
+ throw ( 21 );
+ }
+ }
+ if (uncompressed)
+ trace() << "got " << compressed << " bytes ("
+ << (compressed * 100 / uncompressed) << "%)" << endl;
+
+
+ delete msg;
+ if( close( obj_fd ) == 0 )
+ rename( tmp_file.c_str(), job.outputFile().c_str());
+ else
+ unlink( tmp_file.c_str());
+ }
+
+ } catch ( int x ) {
+ delete cserver;
+ cserver = 0;
+ throw( x );
+ }
+ delete cserver;
+ return status;
+}
+
+static string
+md5_for_file( const string & file )
+{
+ md5_state_t state;
+ string result;
+
+ md5_init(&state);
+ FILE *f = fopen( file.c_str(), "rb" );
+ if ( !f )
+ return result;
+
+ md5_byte_t buffer[40000];
+
+ while ( true ) {
+ size_t size = fread(buffer, 1, 40000, f);
+ if ( !size )
+ break;
+ md5_append(&state, buffer, size );
+ }
+ fclose(f);
+
+ md5_byte_t digest[16];
+ md5_finish(&state, digest);
+
+ char digest_cache[33];
+ for (int di = 0; di < 16; ++di)
+ sprintf(digest_cache + di * 2, "%02x", digest[di]);
+ digest_cache[32] = 0;
+ result = digest_cache;
+ return result;
+}
+
+static bool
+maybe_build_local (MsgChannel *local_daemon, UseCSMsg *usecs, CompileJob &job,
+ int &ret)
+{
+ remote_daemon = usecs->hostname;
+
+ if ( usecs->hostname == "127.0.0.1" ) {
+ trace() << "building myself, but telling localhost\n";
+ int job_id = usecs->job_id;
+ job.setJobID( job_id );
+ job.setEnvironmentVersion( "__client" );
+ CompileFileMsg compile_file( &job );
+ if ( !local_daemon->send_msg( compile_file ) ) {
+ log_info() << "write of job failed" << endl;
+ throw( 29 );
+ }
+ struct timeval begintv, endtv;
+ struct rusage ru;
+
+ gettimeofday(&begintv, 0 );
+ ret = build_local( job, local_daemon, &ru );
+ gettimeofday(&endtv, 0 );
+
+ // filling the stats, so the daemon can play proxy for us
+ JobDoneMsg msg( job_id, ret, JobDoneMsg::FROM_SUBMITTER );
+
+ msg.real_msec = ( endtv.tv_sec - begintv.tv_sec ) * 1000 + ( endtv.tv_usec - begintv.tv_usec ) / 1000;
+ struct stat st;
+ if ( !stat( job.outputFile().c_str(), &st ) )
+ msg.out_uncompressed = st.st_size;
+ msg.user_msec = ru.ru_utime.tv_sec * 1000 + ru.ru_utime.tv_usec / 1000;
+ msg.sys_msec = ru.ru_stime.tv_sec * 1000 + ru.ru_stime.tv_usec / 1000;
+ msg.pfaults = ru.ru_majflt + ru.ru_minflt + ru.ru_nswap ;
+ msg.exitcode = ret;
+
+ if (msg.user_msec > 50 && msg.out_uncompressed > 1024)
+ trace() << "speed=" << float(msg.out_uncompressed / msg.user_msec) << endl;
+
+ return local_daemon->send_msg( msg );
+ }
+ return false;
+}
+
+int build_remote(CompileJob &job, MsgChannel *local_daemon, const Environments &_envs, int permill )
+{
+ srand( time( 0 ) + getpid() );
+
+ int torepeat = 1;
+
+ // older compilers do not support the options we need to make it reproducable
+#if defined(__GNUC__) && ( ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 3) ) || (__GNUC__ >=4) )
+ if ( rand() % 1000 < permill)
+ torepeat = 3;
+#endif
+
+ trace() << job.inputFile() << " compiled " << torepeat << " times on " << job.targetPlatform() << "\n";
+
+ map<string, string> versionfile_map, version_map;
+ Environments envs = rip_out_paths( _envs, version_map, versionfile_map );
+ if (!envs.size()) {
+ log_error() << "$ICECC_VERSION needs to point to .tar files\n";
+ throw(22);
+ }
+
+ const char *preferred_host = getenv("ICECC_PREFERRED_HOST");
+ if ( torepeat == 1 ) {
+ string fake_filename;
+ list<string> args = job.remoteFlags();
+ for ( list<string>::const_iterator it = args.begin(); it != args.end(); ++it )
+ fake_filename += "/" + *it;
+ args = job.restFlags();
+ for ( list<string>::const_iterator it = args.begin(); it != args.end(); ++it )
+ fake_filename += "/" + *it;
+ fake_filename += get_absfilename( job.inputFile() );
+ GetCSMsg getcs (envs, fake_filename, job.language(), torepeat,
+ job.targetPlatform(), job.argumentFlags(),
+ preferred_host ? preferred_host : string() );
+ if (!local_daemon->send_msg (getcs)) {
+ log_warning() << "asked for CS\n";
+ throw( 24 );
+ }
+
+ UseCSMsg *usecs = get_server( local_daemon );
+ int ret;
+ if (!maybe_build_local (local_daemon, usecs, job, ret))
+ ret = build_remote_int( job, usecs,
+ version_map[usecs->host_platform],
+ versionfile_map[usecs->host_platform],
+ 0, true );
+ delete usecs;
+ return ret;
+ } else
+ {
+ char preproc[PATH_MAX];
+ dcc_make_tmpnam( "icecc", ".ix", preproc, 0 );
+ int cpp_fd = open(preproc, O_WRONLY );
+ /* When call_cpp returns normally (for the parent) it will have closed
+ the write fd, i.e. cpp_fd. */
+ pid_t cpp_pid = call_cpp(job, cpp_fd );
+ if ( cpp_pid == -1 ) {
+ ::unlink( preproc );
+ throw( 10 );
+ }
+ int status = 255;
+ waitpid( cpp_pid, &status, 0);
+ if ( status ) { // failure
+ ::unlink( preproc );
+ return WEXITSTATUS( status );
+ }
+
+ char rand_seed[400]; // "designed to be oversized" (Levi's)
+ sprintf( rand_seed, "-frandom-seed=%d", rand() );
+ job.appendFlag( rand_seed, Arg_Remote );
+
+ GetCSMsg getcs (envs, get_absfilename( job.inputFile() ), job.language(), torepeat,
+ job.targetPlatform(), job.argumentFlags(), preferred_host ? preferred_host : string() );
+
+
+ if (!local_daemon->send_msg (getcs)) {
+ log_warning() << "asked for CS\n";
+ throw( 0 );
+ }
+
+ map<pid_t, int> jobmap;
+ CompileJob *jobs = new CompileJob[torepeat];
+ UseCSMsg **umsgs = new UseCSMsg*[torepeat];
+
+ bool misc_error = false;
+ int *exit_codes = new int[torepeat];
+ for ( int i = 0; i < torepeat; i++ ) // init
+ exit_codes[i] = 42;
+
+
+ for ( int i = 0; i < torepeat; i++ ) {
+ jobs[i] = job;
+ char buffer[PATH_MAX];
+ if ( i ) {
+ dcc_make_tmpnam( "icecc", ".o", buffer, 0 );
+ jobs[i].setOutputFile( buffer );
+ } else
+ sprintf( buffer, "%s", job.outputFile().c_str() );
+
+ umsgs[i] = get_server( local_daemon );
+ remote_daemon = umsgs[i]->hostname;
+ trace() << "got_server_for_job " << umsgs[i]->hostname << endl;
+
+ flush_debug();
+ pid_t pid = fork();
+ if ( !pid ) {
+ int ret = 42;
+ try {
+ if (!maybe_build_local (local_daemon, umsgs[i], jobs[i], ret))
+ ret = build_remote_int(
+ jobs[i], umsgs[i],
+ version_map[umsgs[i]->host_platform],
+ versionfile_map[umsgs[i]->host_platform],
+ preproc, i == 0 );
+ } catch ( int error ) {
+ log_info() << "build_remote_int failed and has thrown " << error << endl;
+ kill( getpid(), SIGTERM );
+ return 0; // shouldn't matter
+ }
+ _exit( ret );
+ return 0; // doesn't matter
+ } else {
+ jobmap[pid] = i;
+ }
+ }
+ for ( int i = 0; i < torepeat; i++ ) {
+ pid_t pid = wait( &status );
+ if ( pid < 0 ) {
+ log_perror( "wait failed" );
+ status = -1;
+ } else {
+ if ( WIFSIGNALED( status ) )
+ {
+ // there was some misc error in processing
+ misc_error = true;
+ break;
+ }
+ exit_codes[jobmap[pid]] = WEXITSTATUS( status );
+ }
+ }
+
+ if (! misc_error ) {
+ string first_md5 = md5_for_file( jobs[0].outputFile() );
+
+ for ( int i = 1; i < torepeat; i++ ) {
+ if ( !exit_codes[0] ) { // if the first failed, we fail anyway
+ if ( exit_codes[i] == 42 ) // they are free to fail for misc reasons
+ continue;
+
+ if ( exit_codes[i] ) {
+ log_error() << umsgs[i]->hostname << " compiled with exit code " << exit_codes[i]
+ << " and " << umsgs[0]->hostname << " compiled with exit code " << exit_codes[0] << " - aborting!\n";
+ ::unlink( jobs[0].outputFile().c_str());
+ exit_codes[0] = -1; // overwrite
+ break;
+ }
+
+ string other_md5 = md5_for_file( jobs[i].outputFile() );
+
+ if ( other_md5 != first_md5 ) {
+ log_error() << umsgs[i]->hostname << " compiled " << jobs[0].outputFile() << " with md5 sum " << other_md5 << "(" << jobs[i].outputFile() << ")"
+ << " and " << umsgs[0]->hostname << " compiled with md5 sum " << first_md5 << " - aborting!\n";
+ rename( jobs[0].outputFile().c_str(), ( jobs[0].outputFile() + ".caught" ).c_str() );
+ rename( preproc, ( string( preproc ) + ".caught" ).c_str() );
+ exit_codes[0] = -1; // overwrite
+ break;
+ }
+ }
+
+ ::unlink( jobs[i].outputFile().c_str() );
+ delete umsgs[i];
+ }
+ } else {
+ ::unlink( jobs[0].outputFile().c_str() );
+ for ( int i = 1; i < torepeat; i++ ) {
+ ::unlink( jobs[i].outputFile().c_str());
+ delete umsgs[i];
+ }
+ }
+
+ delete umsgs[0];
+
+ ::unlink( preproc );
+
+ int ret = exit_codes[0];
+
+ delete [] umsgs;
+ delete [] jobs;
+ delete [] exit_codes;
+
+ if ( misc_error )
+ throw ( 27 );
+
+ return ret;
+ }
+
+
+ return 0;
+}
diff --git a/client/safeguard.cpp b/client/safeguard.cpp
new file mode 100644
index 0000000..5a067e2
--- /dev/null
+++ b/client/safeguard.cpp
@@ -0,0 +1,68 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*-
+ *
+ * distcc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+#include "logging.h"
+
+using namespace std;
+
+/**
+ * @file
+ * @brief Protect against unbounded recursion.
+ *
+ * It would be fairly easy for somebody to get confused in masquerade mode and
+ * try to get distcc to invoke itself in a loop. We can't always work out the
+ * right thing to do but we can at least flag an error.
+ *
+ * This environment variable is set to guard against distcc accidentally
+ * recursively invoking itself, thinking it's the real compiler.
+ **/
+
+static const char dcc_safeguard_name[] = "_ICECC_SAFEGUARD";
+static char dcc_safeguard_set[] = "_ICECC_SAFEGUARD=1";
+static int dcc_safeguard_level;
+
+int dcc_recursion_safeguard(void)
+{
+ char *env = getenv(dcc_safeguard_name);
+
+ if (env) {
+ //trace() << "safeguard: " << env << endl;
+ if (!(dcc_safeguard_level = atoi(env)))
+ dcc_safeguard_level = 1;
+ }
+ else
+ dcc_safeguard_level = 0;
+ //trace() << "safeguard level=" << dcc_safeguard_level << endl;
+
+ return dcc_safeguard_level;
+}
+
+
+void dcc_increment_safeguard(void)
+{
+ if (dcc_safeguard_level > 0)
+ dcc_safeguard_set[sizeof dcc_safeguard_set-2] = dcc_safeguard_level+'1';
+ //trace() << "setting safeguard: " << dcc_safeguard_set << endl;
+ if ((putenv(strdup(dcc_safeguard_set)) == -1)) {
+ log_error() << "putenv failed" << endl;
+ }
+}
diff --git a/client/util.cpp b/client/util.cpp
new file mode 100644
index 0000000..3448cfa
--- /dev/null
+++ b/client/util.cpp
@@ -0,0 +1,231 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78; -*-
+ *
+ * distcc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#include "exitcode.h"
+#include "logging.h"
+#include "util.h"
+
+using namespace std;
+
+/**
+ * Set the `FD_CLOEXEC' flag of DESC if VALUE is nonzero,
+ * or clear the flag if VALUE is 0.
+ *
+ * From the GNU C Library examples.
+ *
+ * @returns 0 on success, or -1 on error with `errno' set.
+ **/
+int set_cloexec_flag (int desc, int value)
+{
+ int oldflags = fcntl (desc, F_GETFD, 0);
+ /* If reading the flags failed, return error indication now. */
+ if (oldflags < 0)
+ return oldflags;
+ /* Set just the flag we want to set. */
+ if (value != 0)
+ oldflags |= FD_CLOEXEC;
+ else
+ oldflags &= ~FD_CLOEXEC;
+ /* Store modified flag word in the descriptor. */
+ return fcntl (desc, F_SETFD, oldflags);
+}
+
+/**
+ * Ignore or unignore SIGPIPE.
+ *
+ * The server and child ignore it, because distcc code wants to see
+ * EPIPE errors if something goes wrong. However, for invoked
+ * children it is set back to the default value, because they may not
+ * handle the error properly.
+ **/
+int dcc_ignore_sigpipe(int val)
+{
+ if (signal(SIGPIPE, val ? SIG_IGN : SIG_DFL) == SIG_ERR) {
+ log_warning() << "signal(SIGPIPE, " << ( val ? "ignore" : "default" ) << ") failed: "
+ << strerror(errno) << endl;
+ return EXIT_DISTCC_FAILED;
+ }
+ return 0;
+}
+
+/**
+ * Return a pointer to the basename of the file (everything after the
+ * last slash.) If there is no slash, return the whole filename,
+ * which is presumably in the current directory.
+ **/
+string find_basename(const string &sfile)
+{
+ size_t index = sfile.find_last_of( '/' );
+ if ( index == string::npos )
+ return sfile;
+ return sfile.substr( index + 1);
+}
+
+
+/**
+ * Get an exclusive, non-blocking lock on a file using whatever method
+ * is available on this system.
+ *
+ * @retval 0 if we got the lock
+ * @retval -1 with errno set if the file is already locked.
+ **/
+static int sys_lock(int fd, bool block)
+{
+#if defined(F_SETLK)
+ struct flock lockparam;
+
+ lockparam.l_type = F_WRLCK;
+ 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, LOCK_EX | (block ? 0 : LOCK_NB));
+#elif defined(HAVE_LOCKF)
+ return lockf(fd, block ? F_LOCK : F_TLOCK, 0);
+#else
+# error "No supported lock method. Please port this code."
+#endif
+}
+
+
+bool dcc_unlock(int lock_fd)
+{
+ /* All our current locks can just be closed */
+ if (close(lock_fd)) {
+ log_perror("close failed:");
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Open a lockfile, creating if it does not exist.
+ **/
+static bool dcc_open_lockfile(const string &fname, int &plockfd)
+{
+ /* Create if it doesn't exist. We don't actually do anything with
+ * the file except lock it.
+ *
+ * The file is created with the loosest permissions allowed by the user's
+ * umask, to give the best chance of avoiding problems if they should
+ * happen to use a shared lock dir. */
+ plockfd = open(fname.c_str(), O_WRONLY|O_CREAT, 0666);
+ if (plockfd == -1 && errno != EEXIST) {
+ log_error() << "failed to creat " << fname << ": " << strerror(errno) << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool dcc_lock_host(int &lock_fd)
+{
+ string fname = "/tmp/.icecream-";
+ struct passwd *pwd = getpwuid( getuid() );
+ if ( pwd )
+ fname += pwd->pw_name;
+ else {
+ char buffer[10];
+ sprintf( buffer, "%ld", ( long )getuid() );
+ fname += buffer;
+ }
+
+ if ( mkdir( fname.c_str(), 0700 ) && errno != EEXIST ) {
+ log_perror( "mkdir" );
+ return false;
+ }
+
+ fname += "/local_lock";
+
+ lock_fd = 0;
+ if (!dcc_open_lockfile(fname, lock_fd) )
+ return false;
+
+ if (sys_lock(lock_fd, true) == 0) {
+ return true;
+ } else {
+ switch (errno) {
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ case EACCES: /* HP-UX and Cygwin give this for exclusion */
+ trace() << fname << " is busy" << endl;
+ break;
+ default:
+ log_error() << "lock " << fname << " failed: " << strerror(errno) << endl;
+ break;
+ }
+
+ ::close(lock_fd);
+ return false;
+ }
+}
+
+bool colorify_wanted()
+{
+ const char* term_env = getenv("TERM");
+
+ return isatty(2) && !getenv("EMACS") && term_env && strcasecmp(term_env, "DUMB");
+}
+
+void colorify_output(const string& _s_ccout)
+{
+ string s_ccout(_s_ccout);
+ string::size_type end;
+
+ while ( (end = s_ccout.find('\n')) != string::npos ) {
+
+ string cline = s_ccout.substr(string::size_type(0), end );
+ s_ccout = s_ccout.substr(end+1);
+
+ if (cline.find(": error:") != string::npos)
+ fprintf(stderr, "\x1b[1;31m%s\x1b[0m\n", cline.c_str());
+ else if (cline.find(": warning:") != string::npos)
+ fprintf(stderr, "\x1b[36m%s\x1b[0m\n", cline.c_str());
+ else
+ fprintf(stderr, "%s\n", cline.c_str());
+ }
+ fprintf(stderr, "%s", s_ccout.c_str());
+}
+
+
diff --git a/client/util.h b/client/util.h
new file mode 100644
index 0000000..e538d93
--- /dev/null
+++ b/client/util.h
@@ -0,0 +1,34 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil -*-
+ *
+ * distcc -- A simple distributed compiler system
+ *
+ * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+ *
+ * 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
+ */
+
+#include <string>
+
+/* util.c */
+extern int set_cloexec_flag (int desc, int value);
+extern int dcc_ignore_sigpipe (int val);
+
+extern std::string find_basename(const std::string &sfile);
+extern void colorify_output(const std::string &s_ccout);
+extern bool colorify_wanted();
+
+extern bool dcc_unlock(int lock_fd);
+extern bool dcc_lock_host(int &lock_fd);