Example #1
0
int main(int argc, char** argv) {
#ifndef _USING_FCGI_
    FILE* fin, *fout;
#else
    FCGI_FILE *fin, *fout;
#endif
    int i, retval;
    char req_path[MAXPATHLEN], reply_path[MAXPATHLEN];
    char log_path[MAXPATHLEN], path[MAXPATHLEN];
    unsigned int counter=0;
    char* code_sign_key;
    int length = -1;
    log_messages.pid = getpid();
    bool debug_log = false;

    for (i=1; i<argc; i++) {
        if (!strcmp(argv[i], "--batch")) {
            batch = true;
            continue;
        } else if (!strcmp(argv[i], "--mark_jobs_done")) {
            mark_jobs_done = true;
        } else if (!strcmp(argv[i], "--debug_log")) {
            debug_log = true;
#ifdef GCL_SIMULATOR
        } else if (!strcmp(argv[i], "--simulator")) {
            if(!argv[++i]) {
                log_messages.printf(MSG_CRITICAL, "%s requires an argument\n\n", argv[--i]);
                usage(argv[0]);
                exit(1);
            }
            simtime = atof(argv[i]);
#endif 
        } else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
            usage(argv[0]);
            exit(0);
        } else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
            printf("%s\n", SVN_VERSION);
            exit(0);
        } else if (strlen(argv[i])){
            log_messages.printf(MSG_CRITICAL, "unknown command line argument: %s\n\n", argv[i]);
            usage(argv[0]);
            exit(1);
        }
    }

    // install a signal handler that catches SIGTERMS sent by Apache if the CGI
    // times out.
    //
    signal(SIGTERM, sigterm_handler);

    if (debug_log) {
        if (!freopen("debug_log", "w", stderr)) {
            fprintf(stderr, "Can't redirect stderr\n");
            exit(1);
        }
    } else {
        char *stderr_buffer;
        get_log_path(path, "scheduler.log");
#ifndef _USING_FCGI_
        char buf[256];
        if (!freopen(path, "a", stderr)) {
            fprintf(stderr, "Can't redirect stderr\n");
            sprintf(buf, "Server can't open log file (%s)", path);
            send_message(buf, 3600);
            exit(1);
        }
#else
        FCGI_FILE* f = FCGI::fopen(path, "a");
        if (f) {
            log_messages.redirect(f);
        } else {
            char buf[256];
            fprintf(stderr, "Can't redirect FCGI log messages\n");
            sprintf(buf, "Server can't open log file for FCGI (%s)", path);
            send_message(buf, 3600);
            exit(1);
        }
#endif
        // install a larger buffer for stderr.  This ensures that
        // log information from different scheduler requests running
        // in parallel aren't intermingled in the log file.
        //
        if (config.scheduler_log_buffer) {
            stderr_buffer = (char*)malloc(config.scheduler_log_buffer);
            if (!stderr_buffer) {
                log_messages.printf(MSG_CRITICAL,
                    "Unable to allocate stderr buffer\n"
                );
            } else {
#ifdef _USING_FCGI_
                retval = setvbuf(
                    f->stdio_stream, stderr_buffer, _IOFBF,
                    config.scheduler_log_buffer
                );
#else
                retval = setvbuf(
                    stderr, stderr_buffer, _IOFBF, config.scheduler_log_buffer
                );
#endif
                if (retval) {
                    log_messages.printf(MSG_CRITICAL,
                        "Unable to change stderr buffering\n"
                    );
                }
            }
        }
    }

    srand(time(0)+getpid());
    log_messages.set_debug_level(DEBUG_LEVEL);

#if DUMP_CORE_ON_SEGV
    set_core_dump_size_limit();
#endif

    retval = config.parse_file();
    if (retval) {
        log_messages.printf(MSG_CRITICAL,
            "Can't parse config.xml: %s\n", boincerror(retval)
        );
        send_message("Server can't parse configuration file", 3600);
        exit(0);
    }

    log_messages.set_debug_level(config.sched_debug_level);
    if (config.sched_debug_level == 4) g_print_queries = true;

    gui_urls.init();
    project_files.init();
    init_file_delete_regex();

    sprintf(path, "%s/code_sign_public", config.key_dir);
    retval = read_file_malloc(path, code_sign_key);
    if (retval) {
        log_messages.printf(MSG_CRITICAL,
            "Can't read code sign key file (%s)\n", path
        );
        send_message("Server can't find key file", 3600);
        exit(0);
    }
    strip_whitespace(code_sign_key);


    g_pid = getpid();
#ifdef _USING_FCGI_
    //while(FCGI_Accept() >= 0 && counter < MAX_FCGI_COUNT) {
    while(FCGI_Accept() >= 0) {
        counter++;
        log_messages.set_indent_level(0);
#endif
    if (config.debug_request_headers) {
        log_request_headers(length);
    }

    if (!debug_log && check_stop_sched()) {
        send_message("Project is temporarily shut down for maintenance", 3600);
        goto done;
    }

    if (!ssp) {
        attach_to_feeder_shmem();
    }
    if (!ssp) {
        send_message("Server error: can't attach shared memory", 3600);
        goto done;
    }

    if (strlen(config.debug_req_reply_dir)) {
        struct stat statbuf;
        // the code below is convoluted because,
        // instead of going from stdin to stdout directly,
        // we go via a pair of disk files
        // (this makes it easy to save the input,
        // and to know the length of the output).
        // NOTE: to use this, you must create group-writeable dirs
        // boinc_req and boinc_reply in the project dir
        //
        sprintf(req_path, "%s/%d_%u_sched_request.xml", config.debug_req_reply_dir, g_pid, counter);
        sprintf(reply_path, "%s/%d_%u_sched_reply.xml", config.debug_req_reply_dir, g_pid, counter);

        // keep an own 'log' per PID in case general logging fails
        // this allows to associate at leas the scheduler request with the client
        // IP address (as shown in httpd error log) in case of a crash
        sprintf(log_path, "%s/%d_%u_sched.log", config.debug_req_reply_dir, g_pid, counter);
#ifndef _USING_FCGI_
        fout = fopen(log_path, "a");
#else
        fout = FCGI::fopen(log_path,"a");
#endif
        fprintf(fout, "PID: %d Client IP: %s\n", g_pid, get_remote_addr());
        fclose(fout);

        log_messages.printf(MSG_DEBUG,
            "keeping sched_request in %s, sched_reply in %s, custom log in %s\n",
            req_path, reply_path, log_path
        );
#ifndef _USING_FCGI_
        fout = fopen(req_path, "w");
#else
        fout = FCGI::fopen(req_path,"w");
#endif
        if (!fout) {
            log_messages.printf(MSG_CRITICAL,
                "can't write request file\n"
            );
            exit(1);
        }
        copy_stream(stdin, fout);
        fclose(fout);
        stat(req_path, &statbuf);
        if (length>=0 && (statbuf.st_size != length)) {
            log_messages.printf(MSG_CRITICAL,
                "Request length %d != CONTENT_LENGTH %d\n",
                (int)statbuf.st_size, length
            );
        }

#ifndef _USING_FCGI_
        fin = fopen(req_path, "r");
#else
        fin = FCGI::fopen(req_path,"r");
#endif
        if (!fin) {
            log_messages.printf(MSG_CRITICAL,
                "can't read request file\n"
            );
            exit(1);
        }
#ifndef _USING_FCGI_
        fout = fopen(reply_path, "w");
#else
        fout = FCGI::fopen(reply_path, "w");
#endif
        if (!fout) {
            log_messages.printf(MSG_CRITICAL,
                "can't write reply file\n"
            );
            exit(1);
        }

        handle_request(fin, fout, code_sign_key);
        fclose(fin);
        fclose(fout);
#ifndef _USING_FCGI_
        fin = fopen(reply_path, "r");
#else
        fin = FCGI::fopen(reply_path, "r");
#endif
        if (!fin) {
            log_messages.printf(MSG_CRITICAL,
                "can't read reply file\n"
            );
            exit(1);
        }
        copy_stream(fin, stdout);
        fclose(fin);

        // if not contacted from a client, don't keep the log files
        /* not sure what lead to the assumption of a client setting
           CONTENT_LENGTH, but it's wrong at least on our current
           project / Apache / Client configuration. Commented out.
        if (getenv("CONTENT_LENGTH")) {
          unlink(req_path);
          unlink(reply_path);
        }
        */

#ifndef _USING_FCGI_
    } else if (batch) {
        while (!feof(stdin)) {
            handle_request(stdin, stdout, code_sign_key);
            fflush(stdout);
        }
#endif
    } else {
        handle_request(stdin, stdout, code_sign_key);
        fflush(stderr);
    }
done:
#ifdef _USING_FCGI_
        if (config.debug_fcgi) {
            log_messages.printf(MSG_NORMAL,
                "FCGI: counter: %d\n", counter
            );
            log_messages.flush();
        }
    }   // do()
    if (counter == MAX_FCGI_COUNT) {
        fprintf(stderr, "FCGI: counter passed MAX_FCGI_COUNT - exiting..\n");
    } else {
        fprintf(stderr, "FCGI: FCGI_Accept failed - exiting..\n");
    }
    // when exiting, write headers back to apache so it won't complain
    // about "incomplete headers"
    fprintf(stdout,"Content-type: text/plain\n\n");
#endif
    if (db_opened) {
        boinc_db.close();
    }
}
int main(int argc, char** argv) {
#ifndef _USING_FCGI_
    FILE* fin, *fout;
#else
    FCGI_FILE *fin, *fout;
#endif
    int i, retval;
    char req_path[256], reply_path[256], path[256];
    unsigned int counter=0;
    char* code_sign_key;
    int length=-1;
    log_messages.pid = getpid();
    bool debug_log = false;

    for (i=1; i<argc; i++) {
        if (!strcmp(argv[i], "--batch")) {
            batch = true;
            continue;
        } else if (!strcmp(argv[i], "--mark_jobs_done")) {
            mark_jobs_done = true;
        } else if (!strcmp(argv[i], "--debug_log")) {
            debug_log = true;
#ifdef GCL_SIMULATOR
        } else if (!strcmp(argv[i], "--simulator")) {
            simtime = atof(argv[++i]);
#endif 
        } else {
            usage(argv[0]);
        }
    }

    // install a signal handler that catches SIGTERMS sent by Apache if the cgi
    // times out.
    //
    signal(SIGTERM, sigterm_handler);

    if (debug_log) {
        freopen("debug_log", "w", stderr);
    } else {
        char *stderr_buffer, buf[256];
        get_log_path(path, "scheduler.log");
#ifndef _USING_FCGI_
        if (!freopen(path, "a", stderr)) {
            fprintf(stderr, "Can't redirect stderr\n");
            sprintf(buf, "Server can't open log file (%s)", path);
            send_message(buf, 3600);
            exit(1);
        }
        // install a larger buffer for stderr.  This ensures that
        // log information from different scheduler requests running
        // in parallel don't collide in the log file and appear intermingled.
        //
        if (!(stderr_buffer=(char *)malloc(32768)) || setvbuf(stderr, stderr_buffer, _IOFBF, 32768)) {
            log_messages.printf(MSG_CRITICAL,
                "Unable to change stderr buffering preferences\n"
            );
        }
#else
        FCGI_FILE* f = FCGI::fopen(path, "a");
        if (f) {
           log_messages.redirect(f);
        } else {
            char buf[256];
            fprintf(stderr, "Can't redirect FCGI log messages\n");
            sprintf(buf, "Server can't open log file for FCGI (%s)", path);
            send_message(buf, 3600);
            exit(1);
        }
        // set buffer as above, note that f is really a struct from fcgi_stdio.h
        if (!(stderr_buffer=(char *)malloc(32768)) || setvbuf(f->stdio_stream, stderr_buffer, _IOFBF, 32768)) {
            log_messages.printf(MSG_CRITICAL,
                "Unable to change stderr FCGI buffering preferences\n"
            );
        }
#endif
    }

    srand(time(0)+getpid());
    log_messages.set_debug_level(DEBUG_LEVEL);

#if DUMP_CORE_ON_SEGV
    set_core_dump_size_limit();
#endif

    retval = config.parse_file();
    if (retval) {
        log_messages.printf(MSG_CRITICAL,
            "Can't parse config.xml: %s\n", boincerror(retval)
        );
        send_message("Server can't parse configuration file", 3600);
        exit(0);
    }

    log_messages.set_debug_level(config.sched_debug_level);

    gui_urls.init();
    project_files.init();

    sprintf(path, "%s/code_sign_public", config.key_dir);
    retval = read_file_malloc(path, code_sign_key);
    if (retval) {
        log_messages.printf(MSG_CRITICAL,
            "Can't read code sign key file (%s)\n", path
        );
        send_message("Server can't find key file", 3600);
        exit(0);
    }


    g_pid = getpid();
#ifdef _USING_FCGI_
    //while(FCGI_Accept() >= 0 && counter < MAX_FCGI_COUNT) {
    while(FCGI_Accept() >= 0) {
        counter++;
        log_messages.set_indent_level(0);
#endif
    if (config.debug_request_headers) {
        log_request_headers(length);
    }

    if (check_stop_sched()) {
        send_message("Project is temporarily shut down for maintenance", 3600);
        goto done;
    }

    if (!ssp) {
        attach_to_feeder_shmem();
    }
    if (!ssp) {
        send_message("Server error: can't attach shared memory", 3600);
        goto done;
    }

    if (use_files) {
        struct stat statbuf;
        // the code below is convoluted because,
        // instead of going from stdin to stdout directly,
        // we go via a pair of disk files
        // (this makes it easy to save the input,
        // and to know the length of the output).
        // NOTE: to use this, you must create group-writeable dirs
        // boinc_req and boinc_reply in the project dir
        //
        sprintf(req_path, "%s%d_%u", config.project_path(REQ_FILE_PREFIX), g_pid, counter);
        sprintf(reply_path, "%s%d_%u", config.project_path(REPLY_FILE_PREFIX), g_pid, counter);
#ifndef _USING_FCGI_
        fout = fopen(req_path, "w");
#else
        fout = FCGI::fopen(req_path,"w");
#endif
        if (!fout) {
            log_messages.printf(MSG_CRITICAL,
                "can't write request file\n"
            );
            exit(1);
        }
        copy_stream(stdin, fout);
        fclose(fout);
        stat(req_path, &statbuf);
        if (length>=0 && (statbuf.st_size != length)) {
            log_messages.printf(MSG_CRITICAL,
                "Request length %d != CONTENT_LENGTH %d\n",
                (int)statbuf.st_size, length
            );
        }

#ifndef _USING_FCGI_
        fin = fopen(req_path, "r");
#else
        fin = FCGI::fopen(req_path,"r");
#endif
        if (!fin) {
            log_messages.printf(MSG_CRITICAL,
                "can't read request file\n"
            );
            exit(1);
        }
#ifndef _USING_FCGI_
        fout = fopen(reply_path, "w");
#else
        fout = FCGI::fopen(reply_path, "w");
#endif
        if (!fout) {
            log_messages.printf(MSG_CRITICAL,
                "can't write reply file\n"
            );
            exit(1);
        }

        handle_request(fin, fout, code_sign_key);
        fclose(fin);
        fclose(fout);
#ifndef _USING_FCGI_
        fin = fopen(reply_path, "r");
#else
        fin = FCGI::fopen(reply_path, "r");
#endif
        if (!fin) {
            log_messages.printf(MSG_CRITICAL,
                "can't read reply file\n"
            );
            exit(1);
        }
        copy_stream(fin, stdout);
        fclose(fin);
#ifdef EINSTEIN_AT_HOME
        if (getenv("CONTENT_LENGTH")) unlink(req_path);
        if (getenv("CONTENT_LENGTH")) unlink(reply_path);
#else
        // unlink(req_path);
        // unlink(reply_path);
#endif
#ifndef _USING_FCGI_
    } else if (batch) {
        while (!feof(stdin)) {
            handle_request(stdin, stdout, code_sign_key);
            fflush(stdout);
        }
#endif
    } else {
        handle_request(stdin, stdout, code_sign_key);
    }
done:
#ifdef _USING_FCGI_
        log_messages.printf(MSG_DEBUG,
            "FCGI: counter: %d\n", counter
        );
        log_messages.flush();
    }   // do()
    if (counter == MAX_FCGI_COUNT) {
        fprintf(stderr, "FCGI: counter passed MAX_FCGI_COUNT - exiting..\n");
    } else {
        fprintf(stderr, "FCGI: FCGI_Accept failed - exiting..\n");
    }
    // when exiting, write headers back to apache so it won't complain
    // about "incomplete headers"
    fprintf(stdout,"Content-type: text/plain\n\n");
#endif
    if (db_opened) {
        boinc_db.close();
    }
}