コード例 #1
0
int do_gzip(const char* strGZ, const char* strInput)
{
	// take an input file (strInput) and turn it into a compressed file (strGZ)
	// get rid of the input file after 

	FILE* fIn = boinc_fopen(strInput, "rb");
	if (!fIn)  return 1; //error
	gzFile fOut = gzopen(strGZ, "wb");
	if (!fOut) return 1; //error

	fseek(fIn, 0, SEEK_SET);  // go to the top of the files
	gzseek(fOut, 0, SEEK_SET);
	unsigned char buf[1024];
	long lRead = 0, lWrite = 0;
	while (!feof(fIn)) { // read 1KB at a time until end of file
		memset(buf, 0x00, 1024);
		lRead = 0;
		lRead = fread(buf, 1, 1024, fIn);
		lWrite = gzwrite(fOut, buf, lRead);
		if (lRead != lWrite) break;
	}
	gzclose(fOut);
	fclose(fIn);

	if (lRead != lWrite) return 1;  //error -- read bytes != written bytes

	// if we made it here, it compressed OK, can erase strInput and leave
	boinc_delete_file(strInput);
	return 0;
}
コード例 #2
0
ファイル: cs_benchmark.cpp プロジェクト: niclaslockner/boinc
// check a running benchmark thread/process.
//
void check_benchmark(BENCHMARK_DESC& desc) {
#ifdef _WIN32
    DWORD exit_code = 0;
    GetExitCodeThread(desc.handle, &exit_code);
    if (exit_code != STILL_ACTIVE) {
        CloseHandle(desc.handle);
        desc.done = true;
    }
#else
    int retval;
    int exit_code = 0;
    retval = waitpid(desc.pid, &exit_code, WNOHANG);
    if (retval) {
        desc.done = true;
        FILE* f = fopen(desc.filename, "r");
        if (!f) {
            desc.error = true;
            return;
        }
        retval = desc.host_info.parse_cpu_benchmarks(f);
        fclose(f);
        boinc_delete_file(desc.filename);
        if (retval) {
            desc.error = true;
        }
    }
#endif
}
コード例 #3
0
ファイル: x_opengl.cpp プロジェクト: FpgaAtHome/seti_fpga
// exit handler: really only meant to handle exits() called by GLUT...
//
void restart() {
    // don't do anything except when exit is called from the graphics-thread
    if (!pthread_equal(pthread_self(), graphics_thread))  {
        if (debug) fprintf(stderr, "exit() was called from worker-thread\n");
        return;
    }
    if (debug) fprintf(stderr, "restart: exit() called from graphics-thread.\n");

    // if we are standalone and glut was initialized,
    // we assume user pressed 'close', and we exit the app
    //
    // 
    if (glut_is_initialized ) {
        if (boinc_is_standalone()) {
            if (debug) fprintf(stderr,
                "Assuming user pressed 'close'... means we're exiting now.\n"
            );
            if (boinc_delete_file(LOCKFILE) != 0) {
                perror ("Failed to remove lockfile..\n");
            }
            return;
        }
    }

    // re-install the  exit-handler to catch glut's notorious exits()...
    //
    atexit(restart);

    // jump back to entry-point in xwin_graphics_event_loop();
    //
    if (debug) fprintf(stderr, "restart: Jumping back to event_loop.\n");
    longjmp(jbuf, JUMP_EXIT);
}
コード例 #4
0
ファイル: gui_http.C プロジェクト: phenix3443/synecdoche
int GUI_HTTP::do_rpc_post(GUI_HTTP_OP* op, std::string url, std::string input_file, std::string output_file) {
    int retval;

    if (state != GUI_HTTP_STATE_IDLE) {
        return ERR_RETRY;
    }

    http_op.set_proxy(&gstate.proxy_info);
    boinc_delete_file(output_file.c_str());
    retval = http_op.init_post(url.c_str(), input_file.c_str(), output_file.c_str());
    if (!retval) retval = gstate.http_ops->insert(&http_op);
    if (!retval) {
        gui_http_op = op;
        state = GUI_HTTP_STATE_BUSY;
    }
    return retval;
}
コード例 #5
0
int GUI_HTTP::do_rpc_post(
    GUI_HTTP_OP* op, char* url,
    const char* input_file, const char* output_file,
    bool is_bkgd
) {
    int retval;

    if (gui_http_state != GUI_HTTP_STATE_IDLE) {
        return ERR_RETRY;
    }

    boinc_delete_file(output_file);
    retval = http_op.init_post(0, url, input_file, output_file);
    if (retval) return retval;
    gstate.http_ops->insert(&http_op);
    gui_http_op = op;
    gui_http_state = GUI_HTTP_STATE_BUSY;
    http_op.is_background = is_bkgd;
    return 0;
}
コード例 #6
0
int GUI_HTTP::do_rpc(
    GUI_HTTP_OP* op, const char* url, const char* output_file, bool is_bkgd
) {
    int retval;

    // this check should be done at a higher level.
    // Do it here too just in case
    //
    if (gui_http_state != GUI_HTTP_STATE_IDLE) {
        return ERR_RETRY;
    }

    boinc_delete_file(output_file);
    retval = http_op.init_get(0, url, output_file, true);
    if (retval) return retval;
    gstate.http_ops->insert(&http_op);
    gui_http_op = op;
    gui_http_state = GUI_HTTP_STATE_BUSY;
    http_op.is_background = is_bkgd;
    return 0;
}
コード例 #7
0
ファイル: acct_setup.cpp プロジェクト: freehal/boinc-freehal
int PROJECT_INIT::remove() {
    clear();
    return boinc_delete_file(PROJECT_INIT_FILENAME);
}
コード例 #8
0
ファイル: cs_benchmark.cpp プロジェクト: niclaslockner/boinc
static void remove_benchmark_file(int which) {
    boinc_delete_file(file_names[which]);
}
コード例 #9
0
ファイル: cs_statefile.cpp プロジェクト: Wizardofoddz/boinc
// Write the client_state.xml file
//
int CLIENT_STATE::write_state_file() {
    MFILE mf;
    int retval, ret1, ret2, attempt;
#ifdef _WIN32
    char win_error_msg[4096];
#endif

    for (attempt=1; attempt<=MAX_STATE_FILE_WRITE_ATTEMPTS; attempt++) {
        if (attempt > 1) boinc_sleep(1.0);
            
        if (log_flags.statefile_debug) {
            msg_printf(0, MSG_INFO,
                "[statefile] Writing state file"
            );
        }
#ifdef _WIN32
        retval = mf.open(STATE_FILE_NEXT, "wc");
#else
        retval = mf.open(STATE_FILE_NEXT, "w");
#endif
        if (retval) {
            if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
                msg_printf(0, MSG_INTERNAL_ERROR,
                    "Can't open %s: %s",
                    STATE_FILE_NEXT, boincerror(retval)
                );
            }
            if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
            return ERR_FOPEN;
        }
        MIOFILE miof;
        miof.init_mfile(&mf);
        ret1 = write_state(miof);
        ret2 = mf.close();
        if (ret1) {
            if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
                msg_printf(NULL, MSG_INTERNAL_ERROR,
                    "Couldn't write state file: %s", boincerror(retval)
                );
            }
            if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
            return ret1;
        }
        if (ret2) {
            if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
            return ret2;
        }

        // only attempt to rename the current state file if it exists.
        //
        if (boinc_file_exists(STATE_FILE_NAME)) {
            if (boinc_file_exists(STATE_FILE_PREV)) {
                retval = boinc_delete_file(STATE_FILE_PREV);
                if (retval) {
                    if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
#ifdef _WIN32
                        msg_printf(0, MSG_INFO,
                            "Can't delete previous state file; %s",
                            windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
                        );
#else
                        msg_printf(0, MSG_INFO,
                            "Can't delete previous state file: %s",
                            strerror(errno)
                        );
#endif
                    }
                    if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
                }
            }
            
            retval = boinc_rename(STATE_FILE_NAME, STATE_FILE_PREV);
            if (retval) {
                if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
#ifdef _WIN32
                    msg_printf(0, MSG_INFO,
                        "Can't rename current state file to previous state file; %s",
                        windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
                    );
#else
                    msg_printf(0, MSG_INFO, 
                        "Can't rename current state file to previous state file: %s", 
                        strerror(errno)
                    );
#endif
                }
                if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
            }
        }

        retval = boinc_rename(STATE_FILE_NEXT, STATE_FILE_NAME);
        if (log_flags.statefile_debug) {
            msg_printf(0, MSG_INFO,
                "[statefile] Done writing state file"
            );
        }
        if (!retval) break;     // Success!
        
        if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
#ifdef _WIN32
            msg_printf(0, MSG_INFO,
                "rename error: %s",
                windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
            );
#elif defined (__APPLE__)
            if (log_flags.statefile_debug) {
                system("ls -al /Library/Application\\ Support/BOINC\\ Data/client*.*");
            }
#endif
        }
        if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
        return ERR_RENAME;
    }
    return 0;
}
コード例 #10
0
ファイル: acct_mgr.cpp プロジェクト: freehal/boinc-freehal
// do an account manager RPC;
// if URL is null, detach from current account manager
//
int ACCT_MGR_OP::do_rpc(
    std::string _url, std::string name, std::string password_hash,
    bool _via_gui
) {
    int retval;
    unsigned int i;
    char url[256], password[256], buf[256];
    FILE *pwdf;

    strlcpy(url, _url.c_str(), sizeof(url));

    error_num = ERR_IN_PROGRESS;
    via_gui = _via_gui;
    if (global_prefs_xml) {
        free(global_prefs_xml);
        global_prefs_xml = 0;
    }

    // if null URL, detach from current AMS
    //
    if (!strlen(url) && strlen(gstate.acct_mgr_info.acct_mgr_url)) {
        msg_printf(NULL, MSG_INFO, "Removing account manager info");
        gstate.acct_mgr_info.clear();
        boinc_delete_file(ACCT_MGR_URL_FILENAME);
        boinc_delete_file(ACCT_MGR_LOGIN_FILENAME);
        error_num = 0;
        for (i=0; i<gstate.projects.size(); i++) {
            PROJECT* p = gstate.projects[i];
            p->attached_via_acct_mgr = false;
            p->ams_resource_share = -1;
        }
        return 0;
    }

    canonicalize_master_url(url);
    if (!valid_master_url(url)) {
        error_num = ERR_INVALID_URL;
        return 0;
    }

    strlcpy(ami.acct_mgr_url, url, sizeof(ami.acct_mgr_url));
    strlcpy(ami.acct_mgr_name, "", sizeof(ami.acct_mgr_name));
    strlcpy(ami.login_name, name.c_str(), sizeof(ami.login_name));
    strlcpy(ami.password_hash, password_hash.c_str(), sizeof(ami.password_hash));

    FILE* f = boinc_fopen(ACCT_MGR_REQUEST_FILENAME, "w");
    if (!f) return ERR_FOPEN;
    fprintf(f,
        "<acct_mgr_request>\n"
        "   <name>%s</name>\n"
        "   <password_hash>%s</password_hash>\n"
        "   <host_cpid>%s</host_cpid>\n"
        "   <domain_name>%s</domain_name>\n"
        "   <client_version>%d.%d.%d</client_version>\n"
        "   <run_mode>%s</run_mode>\n",
        name.c_str(), password_hash.c_str(),
        gstate.host_info.host_cpid,
        gstate.host_info.domain_name,
        gstate.core_client_version.major,
        gstate.core_client_version.minor,
        gstate.core_client_version.release,
        run_mode_name[gstate.run_mode.get_perm()]
    );
    if (strlen(gstate.acct_mgr_info.previous_host_cpid)) {
        fprintf(f,
            "   <previous_host_cpid>%s</previous_host_cpid>\n",
            gstate.acct_mgr_info.previous_host_cpid
        );
    }

    // If the AMS requested it, send GUI RPC port and password hash.
    // This is for the "farm" account manager so it
    // can know where to send GUI RPC requests to
    // without having to configure each host
    //
    if (gstate.acct_mgr_info.send_gui_rpc_info) {
        if (gstate.cmdline_gui_rpc_port) {
            fprintf(f,"   <gui_rpc_port>%d</gui_rpc_port>\n", gstate.cmdline_gui_rpc_port);
        } else {
            fprintf(f,"   <gui_rpc_port>%d</gui_rpc_port>\n", GUI_RPC_PORT);
        }
        if (boinc_file_exists(GUI_RPC_PASSWD_FILE)) {
            strcpy(password, "");
            pwdf = fopen(GUI_RPC_PASSWD_FILE, "r");
            if (pwdf) {
                if (fgets(password, 256, pwdf)) {
                    strip_whitespace(password);
                }
                fclose(pwdf);
            }
            fprintf(f,"   <gui_rpc_password>%s</gui_rpc_password>\n", password);
        }
    }
    for (i=0; i<gstate.projects.size(); i++) {
        PROJECT* p = gstate.projects[i];
        fprintf(f,
            "   <project>\n"
            "      <url>%s</url>\n"
            "      <project_name>%s</project_name>\n"
            "      <suspended_via_gui>%d</suspended_via_gui>\n"
            "      <account_key>%s</account_key>\n"
            "      <hostid>%d</hostid>\n"
            "%s"
            "   </project>\n",
            p->master_url,
            p->project_name,
            p->suspended_via_gui,
            p->authenticator,
            p->hostid,
            p->attached_via_acct_mgr?"      <attached_via_acct_mgr/>\n":""
        );
    }
    if (boinc_file_exists(GLOBAL_PREFS_FILE_NAME)) {
        FILE* fprefs = fopen(GLOBAL_PREFS_FILE_NAME, "r");
        if (fprefs) {
            copy_stream(fprefs, f);
            fclose(fprefs);
        }
    }
	if (strlen(gstate.acct_mgr_info.opaque)) {
		fprintf(f,
			"   <opaque>\n%s\n"
			"   </opaque>\n",
			gstate.acct_mgr_info.opaque
		);
	}
    fprintf(f, "</acct_mgr_request>\n");
    fclose(f);
    sprintf(buf, "%srpc.php", url);
    retval = gstate.gui_http.do_rpc_post(
        this, buf, ACCT_MGR_REQUEST_FILENAME, ACCT_MGR_REPLY_FILENAME
    );
    if (retval) {
        error_num = retval;
        return retval;
    }
    msg_printf(NULL, MSG_INFO, "Contacting account manager at %s", url);

    return 0;
}
コード例 #11
0
ファイル: acct_mgr.cpp プロジェクト: drshawnkwang/boinc
// do an account manager RPC;
// if URL is null, detach from current account manager
//
int ACCT_MGR_OP::do_rpc(ACCT_MGR_INFO& _ami, bool _via_gui) {
    int retval;
    unsigned int i;
    char buf[256];

    ami = _ami;

    error_num = ERR_IN_PROGRESS;
    error_str = "";
    via_gui = _via_gui;
    global_prefs_xml = "";

    // if null URL, detach from current AMS
    //
    if (!strlen(ami.master_url) && strlen(gstate.acct_mgr_info.master_url)) {
        msg_printf(NULL, MSG_INFO, "Removing account manager info");
        gstate.acct_mgr_info.clear();
        boinc_delete_file(ACCT_MGR_URL_FILENAME);
        boinc_delete_file(ACCT_MGR_LOGIN_FILENAME);
        error_num = 0;
        for (i=0; i<gstate.projects.size(); i++) {
            gstate.projects[i]->detach_ams();
        }
        ::rss_feeds.update_feed_list();
        gstate.set_client_state_dirty("detach from AMS");
        return 0;
    }

    canonicalize_master_url(ami.master_url, sizeof(ami.master_url));
    if (!valid_master_url(ami.master_url)) {
        error_num = ERR_INVALID_URL;
        return 0;
    }

    FILE* f = boinc_fopen(ACCT_MGR_REQUEST_FILENAME, "w");
    if (!f) return ERR_FOPEN;
    fprintf(f,
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
        "<acct_mgr_request>\n"
    );
    if (strlen(ami.authenticator)) {
        fprintf(f,
            "   <authenticator>%s</authenticator>\n",
            ami.authenticator
        );
    } else {
        fprintf(f,
            "   <name>%s</name>\n"
            "   <password_hash>%s</password_hash>\n",
            ami.login_name, ami.password_hash
        );
    }
    fprintf(f,
        "   <host_cpid>%s</host_cpid>\n"
        "   <domain_name>%s</domain_name>\n"
        "   <client_version>%d.%d.%d</client_version>\n"
        "   <run_mode>%s</run_mode>\n",
        gstate.host_info.host_cpid,
        gstate.host_info.domain_name,
        gstate.core_client_version.major,
        gstate.core_client_version.minor,
        gstate.core_client_version.release,
        run_mode_name[gstate.cpu_run_mode.get_perm()]
    );
    gstate.write_platforms(NULL, f);
    if (strlen(gstate.acct_mgr_info.previous_host_cpid)) {
        fprintf(f,
            "   <previous_host_cpid>%s</previous_host_cpid>\n",
            gstate.acct_mgr_info.previous_host_cpid
        );
    }

    // If the AMS requested it, send GUI RPC port and password.
    // This is for the "farm" account manager so it
    // can know where to send GUI RPC requests to
    // without having to configure each host
    //
    if (gstate.acct_mgr_info.send_gui_rpc_info) {
        if (gstate.cmdline_gui_rpc_port) {
            fprintf(f,"   <gui_rpc_port>%d</gui_rpc_port>\n", gstate.cmdline_gui_rpc_port);
        } else {
            fprintf(f,"   <gui_rpc_port>%d</gui_rpc_port>\n", GUI_RPC_PORT);
        }
        if (boinc_file_exists(GUI_RPC_PASSWD_FILE)) {
            char gui_rpc_password[256];
            safe_strcpy(gui_rpc_password, "");
            FILE* pwdf = fopen(GUI_RPC_PASSWD_FILE, "r");
            if (pwdf) {
                if (fgets(gui_rpc_password, 256, pwdf)) {
                    strip_whitespace(gui_rpc_password);
                }
                fclose(pwdf);
            }
            fprintf(f,
                "   <gui_rpc_password>%s</gui_rpc_password>\n",
                gui_rpc_password
            );
        }
    }
    for (i=0; i<gstate.projects.size(); i++) {
        PROJECT* p = gstate.projects[i];
        double not_started_dur, in_progress_dur;
        p->get_task_durs(not_started_dur, in_progress_dur);
        fprintf(f,
            "   <project>\n"
            "      <url>%s</url>\n"
            "      <project_name>%s</project_name>\n"
            "      <suspended_via_gui>%d</suspended_via_gui>\n"
            "      <hostid>%d</hostid>\n"
            "      <not_started_dur>%f</not_started_dur>\n"
            "      <in_progress_dur>%f</in_progress_dur>\n"
            "      <attached_via_acct_mgr>%d</attached_via_acct_mgr>\n"
            "      <dont_request_more_work>%d</dont_request_more_work>\n"
            "      <detach_when_done>%d</detach_when_done>\n"
            "      <ended>%d</ended>\n"
            "      <resource_share>%f</resource_share>\n"
            "      <disk_usage>%f</disk_usage>\n"
            "      <disk_share>%f</disk_share>\n",
            p->master_url,
            p->project_name,
            p->suspended_via_gui?1:0,
            p->hostid,
            not_started_dur,
            in_progress_dur,
            p->attached_via_acct_mgr?1:0,
            p->dont_request_more_work?1:0,
            p->detach_when_done?1:0,
            p->ended?1:0,
            p->resource_share,
            p->disk_usage,
            p->disk_share
        );
        
        // send work and starvation-related info
        //
        if (ami.dynamic) {
            fprintf(f,
                "      <nrpc_failures>%d</nrpc_failures>\n"
                "      <cpu_ec>%f</cpu_ec>\n"
                "      <cpu_time>%f</cpu_time>\n"
                "      <gpu_ec>%f</gpu_ec>\n"
                "      <gpu_time>%f</gpu_time>\n"
                "      <njobs_success>%d</njobs_success>\n"
                "      <njobs_error>%d</njobs_error>\n",
                p->nrpc_failures,
                p->cpu_ec,
                p->cpu_time,
                p->gpu_ec,
                p->gpu_time,
                p->njobs_success,
                p->njobs_error
            );
            for (int j=0; j<coprocs.n_rsc; j++) {
                if (p->sched_req_no_work[j]) {
                    fprintf(f,
                        "    <sched_req_no_work>%s</sched_req_no_work>\n",
                        coprocs.coprocs[j].type
                    );
                }
            }
        }

        if (p->attached_via_acct_mgr) {
            fprintf(f,
                "      <account_key>%s</account_key>\n",
                p->authenticator
            );
        }
        fprintf(f,
            "   </project>\n"
        );
    }
    MIOFILE mf;
    mf.init_file(f);

    // send working prefs
    //
    fprintf(f, "<working_global_preferences>\n");
    gstate.global_prefs.write(mf);
    fprintf(f, "</working_global_preferences>\n");

    if (boinc_file_exists(GLOBAL_PREFS_FILE_NAME)) {
        FILE* fprefs = fopen(GLOBAL_PREFS_FILE_NAME, "r");
        if (fprefs) {
            copy_stream(fprefs, f);
            fclose(fprefs);
        }
    }
    gstate.host_info.write(mf, !cc_config.suppress_net_info, true);
    if (strlen(gstate.acct_mgr_info.opaque)) {
        fprintf(f,
            "   <opaque>\n%s\n"
            "   </opaque>\n",
            gstate.acct_mgr_info.opaque
        );
    }
    gstate.time_stats.write(mf, true);
    gstate.net_stats.write(mf);
    fprintf(f, "</acct_mgr_request>\n");
    fclose(f);
    snprintf(buf, sizeof(buf), "%srpc.php", ami.master_url);
    retval = gui_http->do_rpc_post(
        this, buf, ACCT_MGR_REQUEST_FILENAME, ACCT_MGR_REPLY_FILENAME, true
    );
    if (retval) {
        error_num = retval;
        return retval;
    }
    msg_printf(NULL, MSG_INFO, "Contacting account manager at %s", ami.master_url);

    return 0;
}
コード例 #12
0
ファイル: AddRemoveUser.cpp プロジェクト: drshawnkwang/boinc
int main(int argc, char *argv[])
{
    long                brandID = 0;
    Boolean             AddUsers = false;
    Boolean             SetSavers = false;
    Boolean             isBMGroupMember, isBPGroupMember;
    Boolean             saverIsSet = false;
    passwd              *pw;
    uid_t               saved_uid;
    group               grpBOINC_master, *grpBOINC_masterPtr;
    group               grpBOINC_project, *grpBOINC_projectPtr;
    char                bmBuf[32768];
    char                bpBuf[32768];
    char                loginName[256];
    short               index, i;
    FILE                *f;
    int                 flag;
    char                *p;
    char                s[1024], buf[1024];
    OSStatus            err;
    
    brandID = GetBrandID();

#ifndef _DEBUG
    if (getuid() != 0) {
        printf("This program must be run as root\n");
        printUsage(brandID);
        return 0;
    }
#endif
    saved_uid = geteuid();

    if (argc < 3) {
        printUsage(brandID);
        return 0;
    }
    
    if (strcmp(argv[1], "-a") == 0) {
        AddUsers = true;
    } else if (strcmp(argv[1], "-s") == 0) {
        AddUsers = true;
        SetSavers = true;
    } else if (strcmp(argv[1], "-r") != 0) {
        printUsage(brandID);
        return 0;
    }

    printf("\n");

    if (!check_branding_arrays(s, sizeof(s))) {
        printf("Branding array has too few entries: %s\n", s);
        return -1;
    }

    loginName[0] = '\0';
    strncpy(loginName, getenv("USER"), sizeof(loginName)-1);
    
    err = getgrnam_r("boinc_master", &grpBOINC_master, bmBuf, sizeof(bmBuf), &grpBOINC_masterPtr);
    if (err) {          // Should never happen unless buffer too small
        puts("getgrnam(\"boinc_master\") failed\n");
        return -1;
    }

    err = getgrnam_r("boinc_project", &grpBOINC_project, bpBuf, sizeof(bpBuf), &grpBOINC_projectPtr);
    if (err) {          // Should never happen unless buffer too small
        puts("getgrnam(\"boinc_project\") failed\n");
        return -1;
    }

    for (index=2; index<argc; index++) {
        // getpwnam works with either the full / login name (pw->pw_gecos) 
        // or the short / Posix name (pw->pw_name)
        pw = getpwnam(argv[index]);
        if ((pw == NULL) || (pw->pw_uid < 501)) {
            printf("User %s not found.\n\n", argv[index]);
            continue;
        }

        flag = 0;
        sprintf(s, "dscl . -read \"/Users/%s\" NFSHomeDirectory", pw->pw_name);    
        f = popen(s, "r");
        if (!f) {
            flag = 1;
            } else {
            while (PersistentFGets(buf, sizeof(buf), f)) {
                p = strrchr(buf, ' ');
                if (p) {
                    if (strstr(p, "/var/empty") != NULL) {
                        flag = 1;
                        break;
                    }
                }
            }
            pclose(f);
        }

        if (flag) {
            sprintf(s, "dscl . -read \"/Users/%s\" UserShell", pw->pw_name);    
            f = popen(s, "r");
            if (!f) {
                flag |= 2;
            } else {
                while (PersistentFGets(buf, sizeof(buf), f)) {
                    p = strrchr(buf, ' ');
                    if (p) {
                        if (strstr(p, "/usr/bin/false") != NULL) {
                            flag |= 2;
                            break;
                        }
                    }
                }
                pclose(f);
            }
        }
    
        if (flag == 3) { // if (Home Directory == "/var/empty") && (UserShell == "/usr/bin/false")
            printf("%s is not a valid user name.\n\n", argv[index]);
            continue;
        }

        printf("%s user %s (/Users/%s)\n", AddUsers? "Adding" : "Removing", pw->pw_gecos, pw->pw_name);

        isBMGroupMember = false;
        i = 0;
        while ((p = grpBOINC_master.gr_mem[i]) != NULL) {  // Step through all users in group boinc_master
            if (strcmp(p, pw->pw_name) == 0) {      // Only the short / Posix names are in the list
                // User is a member of group boinc_master
                isBMGroupMember = true;
                break;
            }
            ++i;
        }

        isBPGroupMember = false;
        i = 0;
        while ((p = grpBOINC_project.gr_mem[i]) != NULL) {  // Step through all users in group boinc_project
            if (strcmp(p, pw->pw_name) == 0) {      // Only the short / Posix names are in the list
                // User is a member of group boinc_master
                isBPGroupMember = true;
                break;
            }
            ++i;
        }

        if ((!isBMGroupMember) && AddUsers) {
            sprintf(s, "dscl . -merge /groups/boinc_master GroupMembership %s", pw->pw_name);
            callPosixSpawn(s);
        }
        
        if ((!isBPGroupMember) && AddUsers) {
            sprintf(s, "dscl . -merge /groups/boinc_project GroupMembership %s", pw->pw_name);
            callPosixSpawn(s);
        }
        
        if (isBMGroupMember && (!AddUsers)) {
            sprintf(s, "dscl . -delete /Groups/boinc_master GroupMembership %s", pw->pw_name);
            callPosixSpawn(s);
        }

        if (isBPGroupMember && (!AddUsers)) {
            sprintf(s, "dscl . -delete /Groups/boinc_project GroupMembership %s", pw->pw_name);
            callPosixSpawn(s);
        }

        if (!AddUsers) {
            // Delete per-user BOINC Manager and screensaver files
            sprintf(s, "rm -fR \"/Users/%s/Library/Application Support/BOINC\"", pw->pw_name);
            callPosixSpawn (s);
        }
    
        // Set or remove login item for this user
        bool useOSASript = false;
        
        if ((compareOSVersionTo(10, 13) < 0)
            || (strcmp(loginName, pw->pw_name) == 0) 
                || (strcmp(loginName, pw->pw_gecos) == 0)) {
            useOSASript = true;
        }
#if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS
        if (! useOSASript) {
            useOSASript = IsUserLoggedIn(pw->pw_name);
        }
#endif
       if (useOSASript) {
            snprintf(s, sizeof(s), "/Users/%s/Library/LaunchAgents/edu.berkeley.boinc.plist", pw->pw_name);
            boinc_delete_file(s);
            SetLoginItemOSAScript(brandID, !AddUsers, pw->pw_name);
        } else {
            SetLoginItemLaunchAgent(brandID, !AddUsers, pw);
        }

        saverIsSet = false;
        err = GetCurrentScreenSaverSelection(pw, s, sizeof(s) -1);
#if VERBOSE
        fprintf(stderr, "Current Screensaver Selection for user %s is: \"%s\"\n", pw->pw_name, s);
#endif
        if (err == noErr) {
            if (!strcmp(s, saverName[brandID])) {
                saverIsSet = true;
            }
        }
        
        if (SetSavers) {
            if (saverIsSet) {
                printf("Screensaver already set to %s for user %s (/Users/%s)\n", saverName[brandID], pw->pw_gecos, pw->pw_name);
            } else {
                printf("Setting screensaver to %s for user %s (/Users/%s)\n", saverName[brandID], pw->pw_gecos, pw->pw_name);
            }
        }
        
        if ((!saverIsSet) && SetSavers) {
            seteuid(pw->pw_uid);    // Temporarily set effective uid to this user
            sprintf(s, "/Library/Screen Savers/%s.saver", saverName[brandID]);
            err = SetScreenSaverSelection(pw, saverName[brandID], s, 0);
#if VERBOSE
            fprintf(stderr, "SetScreenSaverSelection for user %s (uid %d) to \"%s\" returned error %d\n", pw->pw_name, geteuid(), saverName[brandID], err);
#endif
            seteuid(saved_uid);     // Set effective uid back to privileged user
            // This seems to work also:
            // sprintf(buf, "su -l \"%s\" -c 'defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName \"%s\" path \"%s\ type 0'", pw->pw_name, saverName[brandID], s);
            // callPosixSpawn(s);
        }
        
        if (saverIsSet && (!AddUsers)) {
            printf("Setting screensaver to Flurry for user %s (/Users/%s)\n", pw->pw_gecos, pw->pw_name);
            seteuid(pw->pw_uid);    // Temporarily set effective uid to this user
            err = SetScreenSaverSelection(pw, "Flurry", "/System/Library/Screen Savers/Flurry.saver", 0);
#if VERBOSE
            fprintf(stderr, "SetScreenSaverSelection for user %s (%d) to Flurry returned error %d\n", pw->pw_name, geteuid(), err);
#endif
            seteuid(saved_uid);     // Set effective uid back to privileged user
        }

        seteuid(saved_uid);                         // Set effective uid back to privileged user
        
        printf("\n");
    }

    printf("WARNING: Changes may require a system restart to take effect.\n");
    
    return 0;
}