Skip to content
Snippets Groups Projects
Select Git revision
  • bd378f922976ad4a812548d6bfd1fefd5c3b7bcf
  • master default
2 results

test_LLO.py

Blame
  • Forked from finesse / pykat
    Source project has a limited visibility.
    app_start.cpp 30.89 KiB
    // This file is part of BOINC.
    // http://boinc.berkeley.edu
    // Copyright (C) 2008 University of California
    //
    // BOINC is free software; you can redistribute it and/or modify it
    // under the terms of the GNU Lesser General Public License
    // as published by the Free Software Foundation,
    // either version 3 of the License, or (at your option) any later version.
    //
    // BOINC 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 Lesser General Public License for more details.
    //
    // You should have received a copy of the GNU Lesser General Public License
    // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
    
    // initialization and starting of applications
    
    #include "cpp.h"
    
    #ifdef _WIN32
    #include "boinc_win.h"
    #include "win_util.h"
    #ifdef _MSC_VER
    #define snprintf _snprintf
    #define strdup   _strdup
    #endif
    #else
    #include "config.h"
    #ifdef HAVE_SCHED_SETSCHEDULER
    #include <sched.h>
    #endif
    #if HAVE_SYS_TIME_H
    #include <sys/time.h>
    #endif
    #if HAVE_SYS_RESOURCE_H
    #include <sys/resource.h>
    #endif
    #if HAVE_SYS_IPC_H
    #include <sys/ipc.h>
    #endif
    #if HAVE_SYS_WAIT_H
    #include <sys/wait.h>
    #endif
    #include <unistd.h>
    #include <cerrno>
    #include <sys/stat.h>
    #include <string>
    #endif
    
    #ifdef __EMX__
    #include <process.h>
    #endif
    
    #if (defined (__APPLE__) && (defined(__i386__) || defined(__x86_64__)))
    #include <mach-o/loader.h>
    #include <mach-o/fat.h>
    #include <mach/machine.h>
    #include <libkern/OSByteOrder.h>
    #endif
    
    #if(!defined (_WIN32) && !defined (__EMX__))
    #include <fcntl.h>
    #endif
    
    #include <vector>
    
    using std::vector;
    using std::string;
    
    #include "filesys.h"
    #include "error_numbers.h"
    #include "util.h"
    #include "str_util.h"
    #include "str_replace.h"
    #include "shmem.h"
    #include "client_msgs.h"
    #include "client_state.h"
    #include "file_names.h"
    #include "base64.h"
    #include "sandbox.h"
    #include "unix_util.h"
    
    
    #ifdef _WIN32
    #include "proc_control.h"
    #endif
    
    #include "app.h"
    
    
    #ifdef _WIN32
    // Dynamically link to these functions at runtime;
    // otherwise BOINC cannot run on Win98
    
    // CreateEnvironmentBlock
    typedef BOOL (WINAPI *tCEB)(LPVOID *lpEnvironment, HANDLE hToken, BOOL bInherit);
    // DestroyEnvironmentBlock
    typedef BOOL (WINAPI *tDEB)(LPVOID lpEnvironment);
    
    #endif
    
    // Goes through an array of strings, and prints each string
    //
    #ifndef _WIN32
    static void debug_print_argv(char** argv) {
        msg_printf(0, MSG_INFO, "[task_debug] Arguments:");
        for (int i=0; argv[i]; i++) {
            msg_printf(0, MSG_INFO,
                "[task_debug]    argv[%d]: %s\n", i, argv[i]
            );
        }
    }
    #endif
    
    // For apps that use coprocessors, append "--device x" to the command line.
    //
    static void coproc_cmdline(
        int rsc_type, RESULT* rp, double ninstances, char* cmdline
    ) {
        COPROC* coproc = (rsc_type==RSC_TYPE_CUDA)?(COPROC*)coproc_cuda:(COPROC*)coproc_ati;
        for (int j=0; j<ninstances; j++) {
            int k = rp->coproc_indices[j];
            // sanity check
            //
            if (k < 0 || k >= coproc->count) {
                *(int*)1 = 0;
            }
            char buf[256];
            sprintf(buf, " --device %d", coproc->device_nums[k]);
            strcat(cmdline, buf);
        }
    }
    
    // Make a unique key for core/app shared memory segment.
    // Windows: also create and attach to the segment.
    //
    int ACTIVE_TASK::get_shmem_seg_name() {
    #ifdef _WIN32
        int i;
        char seg_name[256];
    
        bool try_global = (sandbox_account_service_token != NULL);
        for (i=0; i<1024; i++) {
            sprintf(seg_name, "%sboinc_%d", SHM_PREFIX, i);
            shm_handle = create_shmem(
                seg_name, sizeof(SHARED_MEM), (void**)&app_client_shm.shm,
                try_global
            );
            if (shm_handle) break;
        }
        if (!shm_handle) return ERR_SHMGET;
        sprintf(shmem_seg_name, "boinc_%d", i);
    #else
        char init_data_path[256];
    #ifndef __EMX__
        // shmem_seg_name is not used with mmap() shared memory 
        if (app_version->api_major_version() >= 6) {
            shmem_seg_name = -1;
            return 0;
        }
    #endif
        sprintf(init_data_path, "%s/%s", slot_dir, INIT_DATA_FILE);
    
        // ftok() only works if there's a file at the given location
        //
        FILE* f = boinc_fopen(init_data_path, "w");
        if (f) fclose(f);
        shmem_seg_name = ftok(init_data_path, 1);
        if (shmem_seg_name == -1) return ERR_SHMEM_NAME;
    #endif
        return 0;
    }
    
    // write the app init file.
    // This is done before starting the app,
    // and when project prefs have changed during app execution
    //
    int ACTIVE_TASK::write_app_init_file() {
        APP_INIT_DATA aid;
        FILE *f;
        char init_data_path[256], project_dir[256], project_path[256];
        int retval;
    
        aid.major_version = BOINC_MAJOR_VERSION;
        aid.minor_version = BOINC_MINOR_VERSION;
        aid.release = BOINC_RELEASE;
        aid.app_version = app_version->version_num;
        safe_strcpy(aid.app_name, wup->app->name);
        safe_strcpy(aid.symstore, wup->project->symstore);
        safe_strcpy(aid.acct_mgr_url, gstate.acct_mgr_info.acct_mgr_url);
        if (wup->project->project_specific_prefs.length()) {
            aid.project_preferences = strdup(wup->project->project_specific_prefs.c_str());
        }
        aid.hostid = wup->project->hostid;
        safe_strcpy(aid.user_name, wup->project->user_name);
        safe_strcpy(aid.team_name, wup->project->team_name);
        get_project_dir(wup->project, project_dir, sizeof(project_dir));
        relative_to_absolute(project_dir, project_path);
        strcpy(aid.project_dir, project_path);
        relative_to_absolute("", aid.boinc_dir);
        strcpy(aid.authenticator, wup->project->authenticator);
        aid.slot = slot;
        strcpy(aid.wu_name, wup->name);
        aid.user_total_credit = wup->project->user_total_credit;
        aid.user_expavg_credit = wup->project->user_expavg_credit;
        aid.host_total_credit = wup->project->host_total_credit;
        aid.host_expavg_credit = wup->project->host_expavg_credit;
        double rrs = gstate.runnable_resource_share();
        if (rrs) {
            aid.resource_share_fraction = wup->project->resource_share/rrs;
        } else {
            aid.resource_share_fraction = 1;
        }
        aid.rsc_fpops_est = wup->rsc_fpops_est;
        aid.rsc_fpops_bound = wup->rsc_fpops_bound;
        aid.rsc_memory_bound = wup->rsc_memory_bound;
        aid.rsc_disk_bound = wup->rsc_disk_bound;
        aid.computation_deadline = result->computation_deadline();
        aid.checkpoint_period = gstate.global_prefs.disk_interval;
        aid.fraction_done_start = 0;
        aid.fraction_done_end = 1;
    #ifdef _WIN32
        strcpy(aid.shmem_seg_name, shmem_seg_name);
    #else
        aid.shmem_seg_name = shmem_seg_name;
    #endif
        aid.wu_cpu_time = checkpoint_cpu_time;
        aid.starting_elapsed_time = checkpoint_elapsed_time;
    
        sprintf(init_data_path, "%s/%s", slot_dir, INIT_DATA_FILE);
        f = boinc_fopen(init_data_path, "w");
        if (!f) {
            msg_printf(wup->project, MSG_INTERNAL_ERROR,
                "Failed to open init file %s",
                init_data_path
            );
            return ERR_FOPEN;
        }
    
        aid.host_info = gstate.host_info;
        aid.global_prefs = gstate.global_prefs;
        aid.proxy_info = gstate.proxy_info;
        retval = write_init_data_file(f, aid);
        fclose(f);
        return retval;
    }
    
    // set up a file reference, given a slot dir and project dir.
    // This means:
    // 1) copy the file to slot dir, if reference is by copy
    // 2) else make a soft link
    //
    static int setup_file(
        PROJECT* project, FILE_INFO* fip, FILE_REF& fref,
        char* file_path, char* slot_dir, bool input
    ) {
        char link_path[256], rel_file_path[256];
        int retval;
    
        sprintf(link_path,
            "%s/%s",
            slot_dir, strlen(fref.open_name)?fref.open_name:fip->name
        );
        sprintf(rel_file_path, "../../%s", file_path );
    
        // if anonymous platform, this is called even if not first time,
        // so link may already be there
        //
        if (input && project->anonymous_platform && boinc_file_exists(link_path)) {
            return 0;
        }
    
        if (fref.copy_file) {
            if (input) {
                retval = boinc_copy(file_path, link_path);
                if (retval) {
                    msg_printf(project, MSG_INTERNAL_ERROR,
                        "Can't copy %s to %s: %s", file_path, link_path,
                        boincerror(retval)
                    );
                    return retval;
                }
    #ifdef SANDBOX
                return set_to_project_group(link_path);
    #endif
            }
            return 0;
        }
    
    #ifdef _WIN32
        retval = make_soft_link(project, link_path, rel_file_path);
        if (retval) return retval;
    #else
        if (project->use_symlinks) {
            retval = symlink(rel_file_path, link_path);
        } else {
            retval = make_soft_link(project, link_path, rel_file_path);
        }
        if (retval) return retval;
    #endif
    #ifdef SANDBOX
        return set_to_project_group(link_path);
    #endif
        return 0;
    }
    
    int ACTIVE_TASK::link_user_files() {
        PROJECT* project = wup->project;
        unsigned int i;
        FILE_REF fref;
        FILE_INFO* fip;
        char file_path[1024];
    
        for (i=0; i<project->user_files.size(); i++) {
            fref = project->user_files[i];
            fip = fref.file_info;
            if (fip->status != FILE_PRESENT) continue;
            get_pathname(fip, file_path, sizeof(file_path));
            setup_file(project, fip, fref, file_path, slot_dir, true);
        }
        return 0;
    }
    
    int ACTIVE_TASK::copy_output_files() {
        char slotfile[256], projfile[256];
        unsigned int i;
        for (i=0; i<result->output_files.size(); i++) {
            FILE_REF& fref = result->output_files[i];
            if (!fref.copy_file) continue;
            FILE_INFO* fip = fref.file_info;
            sprintf(slotfile, "%s/%s", slot_dir, fref.open_name);
            get_pathname(fip, projfile, sizeof(projfile));
    #if 1
            boinc_rename(slotfile, projfile);
    #else
            int retval = boinc_rename(slotfile, projfile);
            // this isn't a BOINC error.
            // it just means the app didn't create an output file
            // that it was supposed to.
            //
            if (retval) {
                msg_printf(wup->project, MSG_INTERNAL_ERROR,
                    "Can't rename output file %s to %s: %s",
                    fip->name, projfile, boincerror(retval)
                );
            }
    #endif
        }
        return 0;
    }
    
    // Start a task in a slot directory.
    // This includes setting up soft links,
    // passing preferences, and starting the process
    //
    // Current dir is top-level BOINC dir
    //
    // postcondition:
    // If any error occurs
    //   ACTIVE_TASK::task_state is PROCESS_COULDNT_START
    //   report_result_error() is called
    //  else
    //   ACTIVE_TASK::task_state is PROCESS_EXECUTING
    //
    int ACTIVE_TASK::start(bool first_time) {
        char exec_name[256], file_path[256], buf[256], exec_path[256];
        char cmdline[8192];
        unsigned int i;
        FILE_REF fref;
        FILE_INFO* fip;
        int retval;
    
        // if this job less than one CPU, run it at above idle priority
        //
        bool high_priority = (app_version->avg_ncpus < 1);
    
        if (first_time && log_flags.task) {
            msg_printf(wup->project, MSG_INFO,
                "Starting %s", result->name
            );
        }
        if (log_flags.cpu_sched) {
            msg_printf(wup->project, MSG_INFO,
                "[cpu_sched] Starting %s%s", result->name, first_time?" (initial)":"(resume)"
            );
        }
    
        if (wup->project->verify_files_on_app_start) {
            fip=0;
            retval = gstate.input_files_available(result, true, &fip);
            if (retval) {
                if (fip) {
                    snprintf(
                        buf, sizeof(buf), "Input file %s missing or invalid: %d", fip->name, retval
                    );
                } else {
                    strcpy(buf, "Input file missing or invalid");
                }
                goto error;
            }
        }
    
        current_cpu_time = checkpoint_cpu_time;
    
        graphics_request_queue.init(result->name);        // reset message queues
        process_control_queue.init(result->name);
    
        if (!app_client_shm.shm) {
            retval = get_shmem_seg_name();
            if (retval) {
                sprintf(buf,
                    "Can't get shared memory segment name: %s",
                    boincerror(retval)
                );
                goto error;
            }
        }
    
        // this must go AFTER creating shmem name,
        // since the shmem name is part of the file
        //
        retval = write_app_init_file();
        if (retval) {
            sprintf(buf, "Can't write init file: %d", retval);
            goto error;
        }
    
        // set up applications files
        //
        strcpy(exec_name, "");
        for (i=0; i<app_version->app_files.size(); i++) {
            fref = app_version->app_files[i];
            fip = fref.file_info;
            get_pathname(fip, file_path, sizeof(file_path));
            if (fref.main_program) {
                if (is_image_file(fip->name)) {
                    sprintf(buf, "Main program %s is an image file", fip->name);
                    retval = ERR_NO_SIGNATURE;
                    goto error;
                }
                if (!fip->executable && !wup->project->anonymous_platform) {
                    sprintf(buf, "Main program %s is not executable", fip->name);
                    retval = ERR_NO_SIGNATURE;
                    goto error;
                }
                safe_strcpy(exec_name, fip->name);
                safe_strcpy(exec_path, file_path);
            }
            // anonymous platform may use different files than
            // when the result was started, so link files even if not first time
            //
            if (first_time || wup->project->anonymous_platform) {
                retval = setup_file(result->project, fip, fref, file_path, slot_dir, true);
                if (retval) {
                    strcpy(buf, "Can't link input file");
                    goto error;
                }
            }
        }
        if (!strlen(exec_name)) {
            strcpy(buf, "No main program specified");
            retval = ERR_NOT_FOUND;
            goto error;
        }
    
        // set up input, output files
        //
        if (first_time) {
            for (i=0; i<wup->input_files.size(); i++) {
                fref = wup->input_files[i];
                fip = fref.file_info;
                get_pathname(fref.file_info, file_path, sizeof(file_path));
                retval = setup_file(result->project, fip, fref, file_path, slot_dir, true);
                if (retval) {
                    strcpy(buf, "Can't link input file");
                    goto error;
                }
            }
            for (i=0; i<result->output_files.size(); i++) {
                fref = result->output_files[i];
                if (fref.copy_file) continue;
                fip = fref.file_info;
                get_pathname(fref.file_info, file_path, sizeof(file_path));
                retval = setup_file(result->project, fip, fref, file_path, slot_dir, false);
                if (retval) {
                    strcpy(buf, "Can't link output file");
                    goto error;
                }
            }
        }
    
        link_user_files();
    
        if (gstate.exit_before_start) {
            exit(0);
        }
    
    #ifdef _WIN32
        PROCESS_INFORMATION process_info;
        STARTUPINFO startup_info;
        LPVOID environment_block = NULL;
        char slotdirpath[256];
        char error_msg[1024];
        char error_msg2[1024];
    
        memset(&process_info, 0, sizeof(process_info));
        memset(&startup_info, 0, sizeof(startup_info));
        startup_info.cb = sizeof(startup_info);
    
        // suppress 2-sec rotating hourglass cursor on startup
        //
        startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK;
    
        app_client_shm.reset_msgs();
    
        if (config.run_apps_manually) {
            // fill in core client's PID so we won't think app has exited
            //
            pid = GetCurrentProcessId();
            pid_handle = GetCurrentProcess();
            set_task_state(PROCESS_EXECUTING, "start");
            return 0;
        }
    
        sprintf(cmdline, "%s %s %s",
            exec_path, wup->command_line.c_str(), app_version->cmdline
        );
        if (app_version->ncudas) {
            coproc_cmdline(RSC_TYPE_CUDA, result, app_version->ncudas, cmdline);
        }
        if (app_version->natis) {
            coproc_cmdline(RSC_TYPE_ATI, result, app_version->natis, cmdline);
        }
    
        relative_to_absolute(slot_dir, slotdirpath);
        bool success = false;
        int prio_mask;
        if (config.no_priority_change) {
            prio_mask = 0;
        } else if (high_priority) {
            prio_mask = BELOW_NORMAL_PRIORITY_CLASS;
        } else {
            prio_mask = IDLE_PRIORITY_CLASS;
        }
    
        for (i=0; i<5; i++) {
            if (sandbox_account_service_token != NULL) {
                // Find CreateEnvironmentBlock/DestroyEnvironmentBlock pointers
                tCEB    pCEB = NULL;
                tDEB    pDEB = NULL;
                HMODULE hUserEnvLib = NULL;
    
                hUserEnvLib = LoadLibrary("userenv.dll");
                if (hUserEnvLib) {
                    pCEB = (tCEB) GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock");
                    pDEB = (tDEB) GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock");
                }
    
                if (!pCEB(&environment_block, sandbox_account_service_token, FALSE)) {
                    if (log_flags.task) {
                        windows_error_string(error_msg, sizeof(error_msg));
                        msg_printf(wup->project, MSG_INFO,
                            "Process environment block creation failed: %s", error_msg
                        );
                    }
                }
    
                if (CreateProcessAsUser(
                    sandbox_account_service_token,
                    exec_path,
                    cmdline,
                    NULL,
                    NULL,
                    FALSE,
                    CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW|prio_mask|CREATE_UNICODE_ENVIRONMENT,
                    environment_block,
                    slotdirpath,
                    &startup_info,
                    &process_info
                )) {
                    success = true;
                    break;
                } else {
                    windows_error_string(error_msg, sizeof(error_msg));
                    msg_printf(wup->project, MSG_INTERNAL_ERROR,
                        "Process creation failed: %s", error_msg
                    );
                }
    
                if (!pDEB(environment_block)) {
                    if (log_flags.task) {
                        windows_error_string(error_msg, sizeof(error_msg2));
                        msg_printf(wup->project, MSG_INFO,
                            "Process environment block cleanup failed: %s",
                            error_msg2
                        );
                    }
                }
    
                if (hUserEnvLib) {
                    pCEB = NULL;
                    pDEB = NULL;
                    FreeLibrary(hUserEnvLib);
                }
    
            } else {
                if (CreateProcess(
                    exec_path,
                    cmdline,
                    NULL,
                    NULL,
                    FALSE,
                    CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW|prio_mask,
                    NULL,
                    slotdirpath,
                    &startup_info,
                    &process_info
                )) {
                    success = true;
                    break;
                } else {
                    windows_error_string(error_msg, sizeof(error_msg));
                    msg_printf(wup->project, MSG_INTERNAL_ERROR,
                        "Process creation failed: %s", error_msg
                    );
                }
            }
            boinc_sleep(drand());
        }
    
        if (!success) {
            sprintf(buf, "CreateProcess() failed - %s", error_msg);
            retval = ERR_EXEC;
            goto error;
        }
        pid = process_info.dwProcessId;
        pid_handle = process_info.hProcess;
        CloseHandle(process_info.hThread);  // thread handle is not used
    #elif defined(__EMX__)
    
        char* argv[100];
        char current_dir[_MAX_PATH];
    
        // Set up core/app shared memory seg if needed
        //
        if (!app_client_shm.shm) {
            retval = create_shmem(
                shmem_seg_name, sizeof(SHARED_MEM), (void**)&app_client_shm.shm
            );
            if (retval) {
                return retval;
            }
        }
        app_client_shm.reset_msgs();
    
        // save current dir
        getcwd(current_dir, sizeof(current_dir));
    
        // chdir() into the slot directory
        //
        retval = chdir(slot_dir);
        if (retval) {
            sprintf(buf, "Can't change directory: %s", slot_dir, boincerror(retval));
            goto error;
        }
    
        // hook up stderr to a specially-named file
        //
        //freopen(STDERR_FILE, "a", stderr);
    
        argv[0] = exec_name;
        char cmdline[8192];
        strcpy(cmdline, wup->command_line.c_str());
        if (strlen(result->cmdline)) {
            strcat(cmdline, " ");
            strcat(cmdline, result->cmdline);
        }
        parse_command_line(cmdline, argv+1);
        if (log_flags.task_debug) {
            debug_print_argv(argv);
        }
        sprintf(buf, "../../%s", exec_path );
        pid = spawnv(P_NOWAIT, buf, argv);
        if (pid == -1) {
            sprintf(buf, "Process creation failed: %s\n", buf, boincerror(retval));
            chdir(current_dir);
            retval = ERR_EXEC;
            goto error;
        }
    
        // restore current dir
        chdir(current_dir);
    
        if (log_flags.task_debug) {
            msg_printf(wup->project, MSG_INFO,
                "[task_debug] ACTIVE_TASK::start(): forked process: pid %d\n", pid
            );
        }
    
        if (!config.no_priority_change) {
            if (setpriority(PRIO_PROCESS, pid,
                high_priority?PROCESS_MEDIUM_PRIORITY:PROCESS_IDLE_PRIORITY)
            ) {
                perror("setpriority");
            }
        }
    
    #else
        // Unix/Linux/Mac case
    
        char* argv[100];
        char current_dir[1024];
    
        getcwd(current_dir, sizeof(current_dir));
    
        sprintf(cmdline, "%s %s",
            wup->command_line.c_str(), app_version->cmdline
        );
        if (app_version->ncudas) {
            coproc_cmdline(RSC_TYPE_CUDA, result, app_version->ncudas, cmdline);
        }
        if (app_version->natis) {
            coproc_cmdline(RSC_TYPE_ATI, result, app_version->natis, cmdline);
        }
    
        // Set up core/app shared memory seg if needed
        //
        if (!app_client_shm.shm) {
            if (app_version->api_major_version() >= 6) {
                // Use mmap() shared memory
                sprintf(buf, "%s/%s", slot_dir, MMAPPED_FILE_NAME);
                if (g_use_sandbox) {
                    if (!boinc_file_exists(buf)) {
                        int fd = open(buf, O_RDWR | O_CREAT, 0660);
                        if (fd >= 0) {
                            close (fd);
    #ifdef SANDBOX
                            set_to_project_group(buf);
    #endif
                        }
                    }
                }
                retval = create_shmem_mmap(
                    buf, sizeof(SHARED_MEM), (void**)&app_client_shm.shm
                );
            } else {
                // Use shmget() shared memory
                retval = create_shmem(
                    shmem_seg_name, sizeof(SHARED_MEM), gstate.boinc_project_gid,
                    (void**)&app_client_shm.shm
                );
    
                if (retval) {
                    needs_shmem = true;
                    destroy_shmem(shmem_seg_name);
                    return retval;
                }
            }
            needs_shmem = false;
        }
        app_client_shm.reset_msgs();
    
    #if (defined (__APPLE__) && (defined(__i386__) || defined(__x86_64__)))
        // PowerPC apps emulated on i386 Macs crash if running graphics
        powerpc_emulated_on_i386 = ! is_native_i386_app(exec_path);
    #endif
        if (config.run_apps_manually) {
            pid = getpid();     // use the client's PID
            set_task_state(PROCESS_EXECUTING, "start");
            return 0;
        }
        pid = fork();
        if (pid == -1) {
            sprintf(buf, "fork() failed: %s", strerror(errno));
            retval = ERR_FORK;
            goto error;
        }
        if (pid == 0) {
            // from here on we're running in a new process.
            // If an error happens,
            // exit nonzero so that the core client knows there was a problem.
    
            // don't pass stdout to the app
            //
            int fd = open("/dev/null", O_RDWR);
            dup2(fd, STDOUT_FILENO);
            close(fd);
    
            // prepend to library path:
            // - the project dir (../../projects/X)
            // - the slot dir (.)
            // - the BOINC dir (../..)
            // We use relative paths in case higher-level dirs
            // are not readable to the account under which app runs
            //
            char libpath[8192];
            get_project_dir(wup->project, buf, sizeof(buf));
            char* p = getenv("LD_LIBRARY_PATH");
            if (p) {
                sprintf(libpath, "../../%s:.:../..:%s", buf, p);
            } else {
                sprintf(libpath, "../../%s:.:../..", buf);
            }
            setenv("LD_LIBRARY_PATH", libpath, 1);
    
            // On the Mac, do the same for DYLIB_LIBRARY_PATH
            //
    #ifdef __APPLE__
            p = getenv("DYLIB_LIBRARY_PATH");
            if (p) {
                sprintf(libpath, "../../%s:.:../..:%s", buf, p);
            } else {
                sprintf(libpath, "../../%s:.:../..", buf);
            }
            setenv("DYLIB_LIBRARY_PATH", libpath, 1);
    #endif
    
            retval = chdir(slot_dir);
            if (retval) {
                perror("chdir");
                fflush(NULL);
                _exit(errno);
            }
    
    #if 0
            // set stack size limit to the max.
            // Some BOINC apps have reported problems with exceeding
            // small stack limits (e.g. 8 MB)
            // and it seems like the best thing to raise it as high as possible
            //
            struct rlimit rlim;
    #define MIN_STACK_LIMIT 64000000
            getrlimit(RLIMIT_STACK, &rlim);
            if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur <= MIN_STACK_LIMIT) {
                if (rlim.rlim_max == RLIM_INFINITY || rlim.rlim_max > MIN_STACK_LIMIT) {
                    rlim.rlim_cur = MIN_STACK_LIMIT;
                } else {
                    rlim.rlim_cur = rlim.rlim_max;
                }
                setrlimit(RLIMIT_STACK, &rlim);
            }
    #endif
    
            // hook up stderr to a specially-named file
            //
            freopen(STDERR_FILE, "a", stderr);
    
            if (!config.no_priority_change) {
    #ifdef HAVE_SETPRIORITY
                if (setpriority(PRIO_PROCESS, 0,
                    high_priority?PROCESS_MEDIUM_PRIORITY:PROCESS_IDLE_PRIORITY)
                ) {
                    perror("setpriority");
                }
    #endif
    #ifdef HAVE_SCHED_SETSCHEDULER
                if (!high_priority) {
                    struct sched_param p;
                    p.sched_priority = 0;
                    if (sched_setscheduler(0, SCHED_BATCH, &p)) {
                        perror("sched_setscheduler");
                    }
                }
    #endif
            }
            sprintf(buf, "../../%s", exec_path );
            if (g_use_sandbox) {
                char switcher_path[100];
                sprintf(switcher_path, "../../%s/%s",
                    SWITCHER_DIR, SWITCHER_FILE_NAME
                );
                argv[0] = SWITCHER_FILE_NAME;
                argv[1] = buf;
                argv[2] = exec_name;
                parse_command_line(cmdline, argv+3);
                if (log_flags.task_debug) {
                    debug_print_argv(argv);
                }
                // Files written by projects have user boinc_project
                // and group boinc_project, 
                // so they must be world-readable so BOINC CLient can read them 
                //
                umask(2);
                retval = execv(switcher_path, argv);
            } else {
                argv[0] = buf;
                parse_command_line(cmdline, argv+1);
                retval = execv(buf, argv);
            }
            msg_printf(wup->project, MSG_INTERNAL_ERROR,
                "Process creation (%s) failed: %s, errno=%d\n",
                buf, boincerror(retval), errno
            );
            perror("execv");
            fflush(NULL);
            _exit(errno);
        }
    
        if (log_flags.task_debug) {
            msg_printf(wup->project, MSG_INFO,
                "[task_debug] ACTIVE_TASK::start(): forked process: pid %d\n", pid
            );
        }
    
    #endif
        set_task_state(PROCESS_EXECUTING, "start");
        return 0;
    
        // go here on error; "buf" contains error message, "retval" is nonzero
        //
    error:
    
        // if something failed, it's possible that the executable was munged.
        // Verify it to trigger another download.
        //
        gstate.input_files_available(result, true);
        gstate.report_result_error(*result, buf);
        if (log_flags.task_debug) {
            msg_printf(wup->project, MSG_INFO,
                "[task_debug] couldn't start app: %s", buf
            );
        }
        set_task_state(PROCESS_COULDNT_START, "start");
        return retval;
    }
    
    // Resume the task if it was previously running; otherwise start it
    // Postcondition: "state" is set correctly
    //
    int ACTIVE_TASK::resume_or_start(bool first_time) {
        const char* str = "??";
        int retval;
    
        switch (task_state()) {
        case PROCESS_UNINITIALIZED:
            if (first_time) {
                retval = start(true);
                str = "Starting";
            } else {
                retval = start(false);
                str = "Restarting";
            }
            if ((retval == ERR_SHMGET) || (retval == ERR_SHMAT)) {
                return retval;
            }
            if (retval) {
                set_task_state(PROCESS_COULDNT_START, "resume_or_start1");
                return retval;
            }
            break;
        case PROCESS_SUSPENDED:
            retval = unsuspend();
            if (retval) {
                msg_printf(wup->project, MSG_INTERNAL_ERROR,
                    "Couldn't resume task %s", result->name
                );
                set_task_state(PROCESS_COULDNT_START, "resume_or_start2");
                return retval;
            }
            str = "Resuming";
            break;
        default:
            msg_printf(result->project, MSG_INTERNAL_ERROR,
                "Unexpected state %d for task %s", task_state(), result->name
            );
            return 0;
        }
        if (log_flags.task) {
            msg_printf(result->project, MSG_INFO,
                "%s task %s using %s version %d",
                str,
                result->name,
                app_version->app->name,
                app_version->version_num
            );
        }
        return 0;
    }
    
    #if (defined (__APPLE__) && (defined(__i386__) || defined(__x86_64__)))
    
    union headeru {
        fat_header fat;
        mach_header mach;
    };
    
    // Read the mach-o headers to determine the architectures
    // supported by executable file.
    // Returns 1 if application can run natively on i386 / x86_64 Macs, else returns 0.
    //
    int ACTIVE_TASK::is_native_i386_app(char* exec_path) {
        FILE *f;
        int result = 0;
        
        headeru myHeader;
        fat_arch fatHeader;
        
        uint32_t n, i, len;
        uint32_t theMagic;
        integer_t theType;
        
        f = boinc_fopen(exec_path, "rb");
        if (!f) {
            return result;          // Should never happen
        }
        
        myHeader.fat.magic = 0;
        myHeader.fat.nfat_arch = 0;
        
        fread(&myHeader, 1, sizeof(fat_header), f);
        theMagic = myHeader.mach.magic;
        switch (theMagic) {
        case MH_CIGAM:
        case MH_MAGIC:
        case MH_MAGIC_64:
        case MH_CIGAM_64:
           theType = myHeader.mach.cputype;
            if ((theMagic == MH_CIGAM) || (theMagic == MH_CIGAM_64)) {
                theType = OSSwapInt32(theType);
            }
            if ((theType == CPU_TYPE_I386) || (theType == CPU_TYPE_X86_64)) {
                result = 1;        // Single-architecture i386or x86_64 file
            }
            break;
        case FAT_MAGIC:
        case FAT_CIGAM:
            n = myHeader.fat.nfat_arch;
            if (theMagic == FAT_CIGAM) {
                n = OSSwapInt32(myHeader.fat.nfat_arch);
            }
               // Multiple architecture (fat) file
            for (i=0; i<n; i++) {
                len = fread(&fatHeader, 1, sizeof(fat_arch), f);
                if (len < sizeof(fat_arch)) {
                    break;          // Should never happen
                }
                theType = fatHeader.cputype;
                if (theMagic == FAT_CIGAM) {
                    theType = OSSwapInt32(theType);
                }
                if ((theType == CPU_TYPE_I386) || (theType == CPU_TYPE_X86_64)) {
                    result = 1;
                    break;
                }
            }
            break;
        default:
            break;
        }
    
        fclose (f);
        return result;
    }
    #endif
    
    const char *BOINC_RCSID_be8bae8cbb = "$Id$";