int kill_family(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc > 2) { fprintf(stderr, "error: argument synopsis for %s: [<pid>]\n", argv[0]); return 1; } pid_t pid = 0; if (argc == 2) { pid = atoi(argv[1]); if (pid == 0) { fprintf(stderr, "error: invalid pid: %s\n", argv[1]); return 1; } } bool success; if (!pfc.kill_family(pid, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
static int track_by_associated_cgroup(ProcFamilyClient& pfc, int argc, char* argv[]) { if ((argc != 2) && (argc != 3)) { fprintf(stderr, "error: argument synopsis for %s: <cgroup> [<pid>]\n", argv[0]); return 1; } const char * cgroup = argv[1]; pid_t pid = 0; if (argc == 3) { pid = atoi(argv[2]); if (pid <= 0) { fprintf(stderr, "error: invalid pid: %s\n", argv[2]); return 1; } } bool success; if (!pfc.track_family_via_cgroup(pid, cgroup, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
int signal_process(ProcFamilyClient& pfc, int argc, char* argv[]) { if ((argc != 2) && (argc != 3)) { fprintf(stderr, "error: argument synopsis for %s: <signal> [<pid>]\n", argv[0]); return 1; } int signal = atoi(argv[1]); if (signal == 0) { fprintf(stderr, "invalid signal: %s\n", argv[1]); return 1; } pid_t pid = 0; if (argc == 3) { pid = atoi(argv[2]); if (pid == 0) { fprintf(stderr, "error: invalid pid: %s\n", argv[2]); return 1; } } bool success; if (!pfc.signal_process(pid, signal, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
static int register_family(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc != 4) { fprintf(stderr, "error: argument synopsis for %s: " "<pid> <watcher_pid> <max_snapshot interval>\n", argv[0]); return 1; } pid_t pid = atoi(argv[1]); pid_t watcher = atoi(argv[2]); int max_snapshot_interval = atoi(argv[3]); bool success; if (!pfc.register_subfamily(pid, watcher, max_snapshot_interval, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
// This function assumes that privledge separation has already been determined // to be available. void UserProc::send_sig_privsep( int sig ) { ProcFamilyClient pfc; char *tmp = NULL; bool response; bool ret; tmp = param("PROCD_ADDRESS"); if (tmp != NULL) { ret = pfc.initialize(tmp); if (ret == false) { EXCEPT("Failure to initialize the ProcFamilyClient object"); } free(tmp); } else { EXCEPT("privsep is enabled, but PROCD_ADDRESS is not defined!"); } // continue the family first ret = pfc.continue_family(pid, response); if (ret == false) { EXCEPT("UserProc::send_sig_privsep(): Couldn't talk to procd while " "trying to continue the user job."); } if (response == false) { EXCEPT("UserProc::send_sig_privsep(): " "Procd refused to continue user job"); } // now (unless it is a continue) send the requested signal if (sig != SIGCONT) { ret = pfc.signal_process(pid, sig, response); if (ret == false) { EXCEPT("UserProc::send_sig_privsep(): Couldn't talk to procd while " "trying to send signal %d to the user job.", sig); } if (response == false) { EXCEPT("UserProc::send_sig_privsep(): " "Procd refused to send signal %d to user job", sig); } } }
static int dump(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc > 2) { fprintf(stderr, "error: argument synopsis for %s: [<pid>]\n", argv[0]); return 1; } pid_t pid = 0; if (argc == 2) { pid = atoi(argv[1]); if (pid == 0) { fprintf(stderr, "error: invalid pid: %s\n", argv[1]); return 1; } } bool response; std::vector<ProcFamilyDump> vec; if (!pfc.dump(pid, response, vec)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!response) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } for (size_t i = 0; i < vec.size(); ++i) { printf("%u %u %u %d\n", (unsigned)vec[i].parent_root, (unsigned)vec[i].root_pid, (unsigned)vec[i].watcher_pid, (int)vec[i].procs.size()); for (size_t j = 0; j < vec[i].procs.size(); ++j) { printf("%u %u " PROCAPI_BIRTHDAY_FORMAT " %ld %ld\n", (unsigned)vec[i].procs[j].pid, (unsigned)vec[i].procs[j].ppid, vec[i].procs[j].birthday, vec[i].procs[j].user_time, vec[i].procs[j].sys_time); } } return 0; }
static int get_usage(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc > 2) { fprintf(stderr, "error: argument synopsis for %s: [<pid>]\n", argv[0]); return 1; } pid_t pid = 0; if (argc == 2) { pid = atoi(argv[1]); if (pid == 0) { fprintf(stderr, "error: invalid pid: %s\n", argv[1]); return 1; } } ProcFamilyUsage pfu; bool success; if (!pfc.get_usage(pid, pfu, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } printf("Number of Processes: %d\n", pfu.num_procs); printf("User CPU Time (s): %ld\n", pfu.user_cpu_time); printf("System CPU Time (s): %ld\n", pfu.sys_cpu_time); printf("CPU Percentage (%%): %f\n", pfu.percent_cpu); printf("Maximum Image Size (KB): %lu\n", pfu.max_image_size); printf("Total Image Size(KB): %lu\n", pfu.total_image_size); if (pfu.total_proportional_set_size_available) { printf("Proportional Set Size (KB): %lu\n", pfu.total_proportional_set_size); } if (pfu.block_read_bytes >= 0) printf("Bytes read from block devices (KB): %lu\n", pfu.block_read_bytes/1024); if (pfu.block_write_bytes >= 0) printf("Bytes written to block devices (KB): %lu\n", pfu.block_write_bytes/1024); return 0; }
int quit(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc != 1) { fprintf(stderr, "error: no arguments required for %s\n", argv[0]); return 1; } bool success; if (!pfc.quit(success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
int unregister_family(ProcFamilyClient& pfc, int argc, char* argv[]) { if (argc != 2) { fprintf(stderr, "error: argument synopsis for %s: <pid>\n", argv[0]); return 1; } pid_t pid = atoi(argv[1]); bool success; if (!pfc.unregister_family(pid, success)) { fprintf(stderr, "error: communication error with ProcD\n"); return 1; } if (!success) { fprintf(stderr, "error: %s command failed with ProcD\n", argv[0]); return 1; } return 0; }
int main(int argc, char* argv[]) { char *procd_address = NULL; myDistro->Init(argc, argv); if (argc < 2) { fprintf(stderr, "Usage: %s <options w/arguments> <command> [<arg> ...]\n", argv[0]); list_commands(); return 1; } config(); Termlog = 1; dprintf_config("TOOL", get_param_functions()); int cmd_argc = argc - 1; char** cmd_argv = argv + 1; // Process the first set of options. while(cmd_argv[0] != NULL && cmd_argv[0][0] == '-') { if (strcmp(cmd_argv[0], "-A") == MATCH) { cmd_argc--; cmd_argv++; if (cmd_argc == 0) { fprintf(stderr, "error: -A needs an argument\n"); list_commands(); return 1; } // store the argument to -A as the file we'll use. procd_address = cmd_argv[0]; cmd_argc--; cmd_argv++; continue; } else if (strcmp(cmd_argv[0], "-h") == MATCH) { cmd_argc--; cmd_argv++; fprintf(stderr, "Usage: %s <options w/arguments> <command> [<arg> ...]\n", argv[0]); list_commands(); return 1; continue; } // This is the failure case if we manage to pass all checks above. fprintf(stderr, "error: Don't understand option %s\n", cmd_argv[0]); list_commands(); return 1; } // If there are no command line arguments left, then there is no // command specified after the options, which is a failure. if (cmd_argc == 0) { fprintf(stderr, "Please specify a command.\n" "Usage: %s <options w/arguments> <command> [<arg> ...]\n", argv[0]); list_commands(); return 1; } // If a procd address wasn't specified on the command line, see if we // have an entry in a config file to use. if (procd_address == NULL) { procd_address = param("PROCD_ADDRESS"); if (procd_address == NULL) { fprintf(stderr, "error: PROCD_ADDRESS not defined\n"); return 1; } } ProcFamilyClient pfc; if (!pfc.initialize(procd_address)) { fprintf(stderr, "error: failed to initialize ProcD client\n"); return 1; } // Process this single command we should be performing if (strcasecmp(cmd_argv[0], "REGISTER_FAMILY") == 0) { return register_family(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "TRACK_BY_ASSOCIATED_GID") == 0) { return track_by_associated_gid(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "TRACK_BY_ASSOCIATED_CGROUP") == 0) { return track_by_associated_cgroup(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "GET_USAGE") == 0) { return get_usage(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "DUMP") == 0) { return dump(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "LIST") == 0) { return list(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "SIGNAL_PROCESS") == 0) { return signal_process(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "SUSPEND_FAMILY") == 0) { return suspend_family(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "CONTINUE_FAMILY") == 0) { return continue_family(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "KILL_FAMILY") == 0) { return kill_family(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "UNREGISTER_FAMILY") == 0) { return unregister_family(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "SNAPSHOT") == 0) { return snapshot(pfc, cmd_argc, cmd_argv); } else if (strcasecmp(cmd_argv[0], "QUIT") == 0) { return quit(pfc, cmd_argc, cmd_argv); } else { fprintf(stderr, "error: invalid command: %s\n", cmd_argv[0]); list_commands(); return 1; } }
void UserProc::execute() { ArgList new_args; char **argv; char **argp; char **envp; sigset_t sigmask; MyString a_out_name; MyString shortname; int user_syscall_fd = -1; const int READ_END = 0; const int WRITE_END = 1; int pipe_fds[2]; FILE *cmd_fp; char buf[128]; ReliSock *new_reli = NULL; pipe_fds[0] = -1; pipe_fds[1] = -1; shortname.formatstr( "condor_exec.%d.%d", cluster, proc ); a_out_name.formatstr( "%s/%s/%s", Execute, local_dir, shortname.Value() ); // Set up arg vector according to class of job switch( job_class ) { case CONDOR_UNIVERSE_STANDARD: if( pipe(pipe_fds) < 0 ) { EXCEPT( "pipe()" );} dprintf( D_ALWAYS, "Pipe built\n" ); // The user process should not try to read commands from // 0, 1, or 2 since we'll be using the commands to redirect // those. if( pipe_fds[READ_END] < 14 ) { dup2( pipe_fds[READ_END], 14 ); close( pipe_fds[READ_END] ); pipe_fds[READ_END] = 14; } dprintf( D_ALWAYS, "New pipe_fds[%d,%d]\n", pipe_fds[0], pipe_fds[1] ); sprintf( buf, "%d", pipe_fds[READ_END] ); dprintf( D_ALWAYS, "cmd_fd = %s\n", buf ); new_args.AppendArg(shortname); new_args.AppendArg("-_condor_cmd_fd"); new_args.AppendArg(buf); break; case CONDOR_UNIVERSE_PVM: #if 1 EXCEPT( "Don't know how to deal with PVM jobs" ); #else new_args.AppendArg(shortname); new_args.AppendArg("-1"); new_args.AppendArg(in); new_args.AppendArg(out); new_args.AppendArg(err); #endif break; case CONDOR_UNIVERSE_VANILLA: if (privsep_enabled()) { EXCEPT("Don't know how to deal with Vanilla jobs"); } new_args.AppendArg(shortname.Value()); break; } new_args.AppendArgsFromArgList(args); // take care of USER_JOB_WRAPPER support_job_wrapper(a_out_name,&new_args); MyString exec_name; exec_name = a_out_name; // If privsep is turned on, then we need to use the PrivSep // Switchboard to launch the job // FILE* switchboard_in_fp; FILE* switchboard_err_fp; int switchboard_child_in_fd; int switchboard_child_err_fd; if (privsep_enabled()) { // create the pipes that we'll use to communicate // if (!privsep_create_pipes(switchboard_in_fp, switchboard_child_in_fd, switchboard_err_fp, switchboard_child_err_fd)) { EXCEPT("can't launch job: privsep_create_pipes failure"); } } argv = new_args.GetStringArray(); // Set an environment variable that tells the job where it may put scratch data // even if it moves to a different directory. // get the environment vector envp = env_obj.getStringArray(); // We may run more than one of these, so each needs its own // remote system call connection to the shadow if( job_class == CONDOR_UNIVERSE_PVM ) { new_reli = NewConnection( v_pid ); user_syscall_fd = new_reli->get_file_desc(); } // print out arguments to execve dprintf( D_ALWAYS, "Calling execve( \"%s\"", exec_name.Value() ); for( argp = argv; *argp; argp++ ) { // argv dprintf( D_ALWAYS | D_NOHEADER, ", \"%s\"", *argp ); } dprintf( D_ALWAYS | D_NOHEADER, ", 0" ); for( argp = envp; *argp; argp++ ) { // envp dprintf( D_ALWAYS | D_NOHEADER, ", \"%s\"", *argp ); } dprintf( D_ALWAYS | D_NOHEADER, ", 0 )\n" ); if( (pid = fork()) < 0 ) { EXCEPT( "fork" ); } if( pid == 0 ) { // the child // Block only these 3 signals which have special meaning for // checkpoint/restart purposes. Leave other signals ublocked // so that if we get an exception during the restart process, // we will get a core file to debug. sigemptyset( &sigmask ); // for some reason if we block these, the user process is unable // to unblock some or all of them. #if 0 sigaddset( &sigmask, SIGUSR1 ); sigaddset( &sigmask, SIGUSR2 ); sigaddset( &sigmask, SIGTSTP ); #endif sigprocmask( SIG_SETMASK, &sigmask, 0 ); // renice renice_self( "JOB_RENICE_INCREMENT" ); // make certain the syscall sockets which are being passed // to the user job are setup to be blocking sockets. this // is done by calling timeout(0) CEDAR method. // we must do this because the syscall lib does _not_ // expect to see any failures due to errno EAGAIN... if ( SyscallStream ) { SyscallStream->timeout(0); } if ( new_reli ) { new_reli->timeout(0); } // If I'm using privledge separation, connect to the procd. // we need to register a family with the procd for the newly // created process, so that the ProcD will allow us to send // signals to it // if (privsep_enabled() == true) { MyString procd_address; bool response; bool ret; ProcFamilyClient pfc; procd_address = get_procd_address(); ret = pfc.initialize(procd_address.Value()); if (ret == false) { EXCEPT("Failure to initialize the ProcFamilyClient object"); } ret = pfc.register_subfamily(getpid(), getppid(), 60, response); if (ret == false) { EXCEPT("Could not communicate with procd. Aborting."); } if (response == false) { EXCEPT("Procd refused to register job subfamily. Aborting."); } } // If there is a requested coresize for this job, enforce it. // Do it before the set_priv_final to ensure root can alter // the coresize to the requested amount. Otherwise, just // use whatever the current default is. if (coredump_limit_exists == TRUE) { limit( RLIMIT_CORE, coredump_limit, CONDOR_HARD_LIMIT, "max core size" ); } // child process should have only it's submitting uid, and cannot // switch back to root or some other uid. // It'd be nice to check for errors here, but // unfortunately, we can't, since this only returns the // previous priv state, not whether it worked or not. // -Derek Wright 4/30/98 set_user_priv_final(); switch( job_class ) { case CONDOR_UNIVERSE_STANDARD: // if we're using PrivSep, the chdir here could fail. instead, // we pass the job's IWD to the switchboard via pipe // if (!privsep_enabled()) { if( chdir(local_dir) < 0 ) { EXCEPT( "chdir(%s)", local_dir ); } } close( pipe_fds[WRITE_END] ); break; case CONDOR_UNIVERSE_PVM: if( chdir(local_dir) < 0 ) { EXCEPT( "chdir(%s)", local_dir ); } close( pipe_fds[WRITE_END] ); dup2( user_syscall_fd, RSC_SOCK ); break; case CONDOR_UNIVERSE_VANILLA: set_iwd(); open_std_file( 0 ); open_std_file( 1 ); open_std_file( 2 ); (void)close( RSC_SOCK ); (void)close( CLIENT_LOG ); break; } // Make sure we're not root if( getuid() == 0 ) { // EXCEPT( "We're about to start as root, aborting." ); // You can't see this error message at all. So, just // exit(4), which is what EXCEPT normally gives. exit( 4 ); } #if defined( LINUX ) && (defined(I386) || defined(X86_64)) // adjust the execution domain of the child to be suitable for // checkpointing. patch_personality(); #endif // if we're using privsep, we'll exec the PrivSep Switchboard // first, which is setuid; it will then setuid to the user we // give it and exec the real job // if (privsep_enabled()) { close(fileno(switchboard_in_fp)); close(fileno(switchboard_err_fp)); privsep_get_switchboard_command("exec", switchboard_child_in_fd, switchboard_child_err_fd, exec_name, new_args); deleteStringArray(argv); argv = new_args.GetStringArray(); } // Everything's ready, start it up... errno = 0; execve( exec_name.Value(), argv, envp ); // A successful call to execve() never returns, so it is an // error if we get here. A number of errors are possible // but the most likely is that there is insufficient swap // space to start the new process. We don't try to log // anything, since we have the UID/GID of the job's owner // and cannot write into the log files... exit( JOB_EXEC_FAILED ); } // The parent // PrivSep - we have at this point only spawned the switchboard // with the "exec" command. we need to use our pipe to it in // order to tell it how to execute the user job, and then use // the error pipe to make sure everything worked // if (privsep_enabled()) { close(switchboard_child_in_fd); close(switchboard_child_err_fd); privsep_exec_set_uid(switchboard_in_fp, uid); privsep_exec_set_path(switchboard_in_fp, exec_name.Value()); privsep_exec_set_args(switchboard_in_fp, new_args); privsep_exec_set_env(switchboard_in_fp, env_obj); privsep_exec_set_iwd(switchboard_in_fp, local_dir); privsep_exec_set_inherit_fd(switchboard_in_fp, pipe_fds[0]); privsep_exec_set_inherit_fd(switchboard_in_fp, RSC_SOCK); privsep_exec_set_inherit_fd(switchboard_in_fp, CLIENT_LOG); privsep_exec_set_is_std_univ(switchboard_in_fp); fclose(switchboard_in_fp); if (!privsep_get_switchboard_response(switchboard_err_fp)) { EXCEPT("error starting job: " "privsep get_switchboard_response failure"); } } dprintf( D_ALWAYS, "Started user job - PID = %d\n", pid ); if( job_class != CONDOR_UNIVERSE_VANILLA ) { // Send the user process its startup environment conditions close( pipe_fds[READ_END] ); cmd_fp = fdopen( pipe_fds[WRITE_END], "w" ); dprintf( D_ALWAYS, "cmd_fp = %p\n", cmd_fp ); if( is_restart() ) { #if 1 fprintf( cmd_fp, "restart\n" ); dprintf( D_ALWAYS, "restart\n" ); #else fprintf( cmd_fp, "restart %s\n", target_ckpt ); dprintf( D_ALWAYS, "restart %s\n", target_ckpt ); #endif fprintf( cmd_fp, "end\n" ); dprintf( D_ALWAYS, "end\n" ); } else { fprintf( cmd_fp, "end\n" ); dprintf( D_ALWAYS, "end\n" ); } fclose( cmd_fp ); } deleteStringArray(argv); deleteStringArray(envp); state = EXECUTING; if( new_reli ) { delete new_reli; } // removed some vanilla-specific code here // ASSERT(job_class != CONDOR_UNIVERSE_VANILLA); }