diff options
author | Jeongho Hwang <jbera.hwang@samsung.com> | 2012-09-19 13:40:18 +0900 |
---|---|---|
committer | Jeongho Hwang <jbera.hwang@samsung.com> | 2012-09-19 13:40:18 +0900 |
commit | d208c9cb79b228fd48d9f7adef432486389e1abe (patch) | |
tree | 1f8533ae700c789c992dcebb88c558a963e59efc /client | |
parent | 8701a5293e4e52a995b1ee0b683f234848d8d745 (diff) | |
download | icecream-2.0alpha.tar.gz icecream-2.0alpha.tar.bz2 icecream-2.0alpha.zip |
Signed-off-by: Jeongho Hwang <jbera.hwang@samsung.com>
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.am | 21 | ||||
-rw-r--r-- | client/Makefile.in | 630 | ||||
-rw-r--r-- | client/arg.cpp | 419 | ||||
-rw-r--r-- | client/client.h | 61 | ||||
-rw-r--r-- | client/cpp.cpp | 149 | ||||
-rwxr-xr-x | client/icecc-create-env | 195 | ||||
-rw-r--r-- | client/local.cpp | 287 | ||||
-rw-r--r-- | client/main.cpp | 373 | ||||
-rw-r--r-- | client/md5.c | 392 | ||||
-rw-r--r-- | client/md5.h | 93 | ||||
-rw-r--r-- | client/remote.cpp | 777 | ||||
-rw-r--r-- | client/safeguard.cpp | 68 | ||||
-rw-r--r-- | client/util.cpp | 231 | ||||
-rw-r--r-- | client/util.h | 34 |
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); |