예제 #1
0
파일: remote.cpp 프로젝트: bozaro/icecream
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") {
        // If this is a test build, do local builds on the local daemon
        // that has --no-remote, use remote building for the remaining ones.
        if (getenv("ICECC_TEST_REMOTEBUILD") && usecs->port != 0 )
            return false;
        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;
}
예제 #2
0
파일: remote.cpp 프로젝트: liangqi/icecream
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 reproducible
#if defined(__GNUC__) && ( ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 3) ) || (__GNUC__ >=4) )
    if (!compiler_is_clang(job)) {
        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(),
		        ignore_unverified());
        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, local_daemon,
	    			    version_map[usecs->host_platform],
				    versionfile_map[usecs->host_platform],
				    0, true );
        delete usecs;
        return ret;
    } else
    {
        char *preproc = 0;
        dcc_make_tmpnam( "icecc", ".ix", &preproc, 0 );
        const CharBufferDeleter preproc_holder(preproc);
        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 ( shell_exit_status(status) ) { // failure
            ::unlink( preproc );
            return shell_exit_status( 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(),
                ignore_unverified());


        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 = 0;
            if ( i ) {
                dcc_make_tmpnam( "icecc", ".o", &buffer, 0 );
                jobs[i].setOutputFile( buffer );
            } else
                buffer = strdup( job.outputFile().c_str() );
            const CharBufferDeleter buffer_holder( buffer );

            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], local_daemon,
				  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]] = shell_exit_status( 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;
}
예제 #3
0
파일: remote.cpp 프로젝트: liangqi/icecream
static int build_remote_int(CompileJob &job, UseCSMsg *usecs, MsgChannel *local_daemon, 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 );
        }

        if ( IS_PROTOCOL_31( cserver )) {
            VerifyEnvMsg verifymsg( job.targetPlatform(), job.environmentVersion() );
            if ( !cserver->send_msg( verifymsg ) )
                throw( 22 );
            Msg *msg = cserver->get_msg(60);
            if ( msg && msg->type == M_VERIFY_ENV_RESULT ) {
                if( !static_cast<VerifyEnvResultMsg*>( msg )->ok ) {
                    // The remote can't handle the environment at all (e.g. kernel too old),
                    // mark it as never to be used again for this environment.
                    log_info() << "Host " << hostname << " did not successfully verify environment." << endl;
                    BlacklistHostEnvMsg blacklist( job.targetPlatform(), job.environmentVersion(), hostname );
                    local_daemon->send_msg( blacklist );
                    throw( 24 );
                } else
                    trace() << "Verified host " << hostname << " for environment " << job.environmentVersion()
                        << " (" << job.targetPlatform() << ")" << endl;
            } else
                throw( 25 );
        }
    }

    if( !IS_PROTOCOL_31( cserver ) && ignore_unverified()) {
        log_warning() << "Host " << hostname << " cannot be verified." << endl;
        throw( 26 );
    }

    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 ( shell_exit_status(status) != 0 ) { // failure
            delete cserver;
            cserver = 0;
            return shell_exit_status( 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(job))
            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;
}
예제 #4
0
파일: main.cpp 프로젝트: bozaro/icecream
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();

    char cwd[ PATH_MAX ];
    if( getcwd( cwd, PATH_MAX ) != NULL )
        job.setWorkingDirectory( cwd );

    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(argv + 2);
            }

            if (arg.size() > 0) {
                job.setCompilerName(arg);
                job.setCompilerPathname(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);
                job.setCompilerPathname(arg);
            }
        }
    } else {
        std::string resolved;

        // check if it's a symlink to icerun
        if (resolve_link(compiler_name, resolved) == 0 && find_basename(resolved) == "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);

    list<string> extrafiles;
    local |= analyse_argv(argv, job, icerun, &extrafiles);

    /* If ICECC is set to disable, then run job locally, without contacting
       the daemon at all. Because of file-based locking that is used in this
       case, all calls will be serialized and run by one.
       If ICECC is set to no, the job is run locally as well, but it is
       serialized using the daemon, so several may be run at once.
     */
    char *icecc = getenv("ICECC");

    if (icecc && !strcasecmp(icecc, "disable")) {
        return build_local(job, 0);
    }

    if (icecc && !strcasecmp(icecc, "no")) {
        local = true;
    }

    if (const char *extrafilesenv = getenv("ICECC_EXTRAFILES")) {
        for (;;) {
            const char *colon = strchr(extrafilesenv, ':');
            string file;

            if (colon == NULL) {
                file = extrafilesenv;
            } else {
                file = string(extrafilesenv, colon - extrafilesenv);
            }

            file = get_absfilename(file);

            struct stat st;
            if (stat(file.c_str(), &st) == 0) {
                extrafiles.push_back(file);
            } else {
                log_warning() << "File in ICECC_EXTRAFILES not found: " << file << endl;
                return build_local(job, 0);
            }

            if (colon == NULL) {
                break;
            }

            extrafilesenv = colon + 1;
        }
    }

    MsgChannel *local_daemon;
    if (getenv("ICECC_TEST_SOCKET") == NULL) {
        /* try several options to reach the local daemon - 3 sockets, one TCP */
        local_daemon = Service::createChannel("/var/run/icecc/iceccd.socket");

        if (!local_daemon) {
            local_daemon = Service::createChannel("/var/run/iceccd.socket");
        }

        if (!local_daemon && getenv("HOME")) {
            string path = getenv("HOME");
            path += "/.iceccd.socket";
            local_daemon = Service::createChannel(path);
        }

        if (!local_daemon) {
            local_daemon = Service::createChannel("127.0.0.1", 10245, 0/*timeout*/);
        }
    } else {
        local_daemon = Service::createChannel(getenv("ICECC_TEST_SOCKET"));
        if (!local_daemon) {
            log_error() << "test socket error" << endl;
            return EXIT_TEST_SOCKET_ERROR;
        }
    }

    if (!local_daemon) {
        log_warning() << "no local daemon found" << endl;
        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(), find_prefix(job.compilerName()));
            } catch (int x) {
                // we just build locally
            }
        } else if (!extrafiles.empty() && !IS_PROTOCOL_32(local_daemon)) {
            log_warning() << "Local daemon is too old to handle compiler plugins." << endl;
            local = true;
        } else {
            if (!local_daemon->send_msg(GetNativeEnvMsg(compiler_is_clang(job)
                                        ? "clang" : "gcc", extrafiles))) {
                log_warning() << "failed to write get native environment" << endl;
                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 (error >= 100) {
                log_info() << "local build forced by error " << error << endl;
                goto do_local_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);
}
예제 #5
0
파일: local.cpp 프로젝트: abael/icecream
/**
 * 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 = find_compiler(job);

    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(job);
    int pf[2];

    if (color_output && pipe(pf)) {
        color_output = false;
    }

    if (used || color_output) {
        flush_debug();
        child_pid = fork();
    }

    if (!child_pid) {
        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);
    }

    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 = shell_exit_status(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;
}
예제 #6
0
/**
 * 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 = find_compiler(job);

    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.dwarfFissionEnabled()) {
        arguments.push_back("-gsplit-dwarf");
    }

    if (!job.inputFile().empty()) {
        arguments.push_back(job.inputFile());
    }

    if (!job.outputFile().empty()) {
        arguments.push_back("-o");
        arguments.push_back(job.outputFile());
    }

    vector<char*> argv; 
    string argstxt;

    for (list<string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
        argv.push_back(strdup(it->c_str()));
        argstxt += ' ';
        argstxt += *it;
    }

    argv.push_back(0);

    trace() << "invoking:" << argstxt << endl;

    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(job);
    int pf[2];

    if (color_output && pipe(pf)) {
        color_output = false;
    }

    if (used || color_output) {
        flush_debug();
        child_pid = fork();
    }

    if (child_pid == -1){
        log_perror("fork failed");
    }

    if (!child_pid) {
        dcc_increment_safeguard(job.language() == CompileJob::Lang_Custom ? SafeguardStepCustom : SafeguardStepCompiler);

        if (color_output) {
            if ((-1 == close(pf[0])) && (errno != EBADF)){
                log_perror("close failed");
            }
            if ((-1 == close(2)) && (errno != EBADF)){
                log_perror("close failed");
            }
            if (-1 == dup2(pf[1], 2)){
                log_perror("dup2 failed");
            }
        }

        execv(argv[0], &argv[0]);
        int exitcode = ( errno == ENOENT ? 127 : 126 );
        log_perror("execv failed");

        if (lock_fd) {
            dcc_unlock(lock_fd);
        }

        {
            char buf[256];
            snprintf(buf, sizeof(buf), "ICECC[%d]: %s:", getpid(), argv[0]);
            log_perror(buf);
        }

        _exit(exitcode);
    }
    for(vector<char*>::const_iterator i = argv.begin(); i != argv.end(); ++i){
        free(*i);
    }
    argv.clear();

    if (color_output) {
        if ((-1 == close(pf[1])) && (errno != EBADF)){
            log_perror("close failed");
        }
    }

    // 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];

        for (;;) {
	    int r;
            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 = shell_exit_status(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;
}
예제 #7
0
파일: remote.cpp 프로젝트: bozaro/icecream
static int build_remote_int(CompileJob &job, UseCSMsg *usecs, MsgChannel *local_daemon,
                            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);
            }

            if (IS_PROTOCOL_31(cserver)) {
                VerifyEnvMsg verifymsg(job.targetPlatform(), job.environmentVersion());

                if (!cserver->send_msg(verifymsg)) {
                    throw(22);
                }

                Msg *verify_msg = cserver->get_msg(60);

                if (verify_msg && verify_msg->type == M_VERIFY_ENV_RESULT) {
                    if (!static_cast<VerifyEnvResultMsg*>(verify_msg)->ok) {
                        // The remote can't handle the environment at all (e.g. kernel too old),
                        // mark it as never to be used again for this environment.
                        log_info() << "Host " << hostname
                                   << " did not successfully verify environment."
                                   << endl;
                        BlacklistHostEnvMsg blacklist(job.targetPlatform(),
                                                      job.environmentVersion(), hostname);
                        local_daemon->send_msg(blacklist);
                        throw(24);
                    } else
                        trace() << "Verified host " << hostname << " for environment "
                                << job.environmentVersion() << " (" << job.targetPlatform() << ")"
                                << endl;
                } else {
                    throw(25);
                }
            }
        }

        if (!IS_PROTOCOL_31(cserver) && ignore_unverified()) {
            log_warning() << "Host " << hostname << " cannot be verified." << endl;
            throw(26);
        }

        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 (shell_exit_status(status) != 0) {   // failure
                delete cserver;
                cserver = 0;
                return shell_exit_status(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(101);
        }

        if (output) {
            if ((!crmsg->out.empty() || !crmsg->err.empty()) && output_needs_workaround(job)) {
                delete crmsg;
                log_info() << "command needs stdout/stderr workaround, recompiling locally" << endl;
                throw(102);
            }

            write(STDOUT_FILENO, crmsg->out.c_str(), crmsg->out.size());

            if (colorify_wanted(job)) {
                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) {
            receive_file(job.outputFile(), cserver);
        }

    } catch (int x) {
        // Handle pending status messages, if any.
        if(cserver) {
            while(Msg* msg = cserver->get_msg(0)) {
                if(msg->type == M_STATUS_TEXT)
                    log_error() << "Remote status (compiled on " << cserver->name << "): "
                                << static_cast<StatusTextMsg*>(msg)->text << endl;
                delete msg;
            }
            delete cserver;
            cserver = 0;
        }

        throw(x);
    }

    delete cserver;
    return status;
}