summaryrefslogtreecommitdiff
path: root/client/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/main.cpp')
-rw-r--r--client/main.cpp373
1 files changed, 373 insertions, 0 deletions
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 );
+}