// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2020 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 . #include #ifdef _WIN32 #include "diagnostics.h" #include "boinc_win.h" #include "win_util.h" #else #include "config.h" #include #include #include #include #include #if HAVE_CSIGNAL #include #elif HAVE_SYS_SIGNAL_H #include #elif HAVE_SIGNAL_H #include #endif #endif #include "common_defs.h" #include "procinfo.h" #include "str_util.h" #include "util.h" #include "proc_control.h" using std::vector; //#define DEBUG #ifdef DEBUG #include #endif static void get_descendants_aux(PROC_MAP& pm, int pid, vector& pids) { PROC_MAP::iterator i = pm.find(pid); if (i == pm.end()) return; PROCINFO& p = i->second; if (p.scanned) return; // avoid infinite recursion p.scanned = true; for (unsigned int j=0; j& pids) { int retval; PROC_MAP pm; pids.clear(); retval = procinfo_setup(pm); if (retval) return; get_descendants_aux(pm, pid, pids); #ifdef DEBUG fprintf(stderr, "descendants of %d:\n", pid); for (unsigned int i=0; ipids, DWORD calling_thread_id, bool resume, bool check_exempt ) { HANDLE threads, thread; THREADENTRY32 te = {0}; int retval = 0; DWORD n; static vector suspended_threads; #ifdef DEBUG fprintf(stderr, "start: check_exempt %d %s\n", check_exempt, precision_time_to_string(dtime())); fprintf(stderr, "%s processes", resume?"resume":"suspend"); for (unsigned int i=0; i& pids) { int status; for (unsigned int i=0; i= 0) { return true; } } return false; } #endif void kill_all(vector& pids) { for (unsigned int i=0; i descendants; // on Win, kill descendants directly // get_descendants(GetCurrentProcessId(), descendants); kill_all(descendants); } #else // Same, but if child_pid is nonzero, // give it a chance to exit gracefully on Unix // void kill_descendants(int child_pid) { vector descendants; // on Unix, ask main process nicely. // it descendants still exist after 10 sec, use the nuclear option // get_descendants(getpid(), descendants); if (child_pid) { ::kill(child_pid, SIGTERM); for (int i=0; i<10; i++) { if (!any_process_exists(descendants)) { return; } boinc_sleep(1); } kill_all(descendants); // kill any processes that might have been created // in the last 10 secs get_descendants(getpid(), descendants); } kill_all(descendants); } #endif // suspend/resume the descendants of the calling process. // Unix version lets you choose the stop signal: // SIGSTOP (the default) can't be caught. // SIGTSTP can be caught, but it has no effect for processes without a TTY. // So it's useful only for programs that are wrappers of some sort; // they must catch and handle it. // #ifdef _WIN32 void suspend_or_resume_descendants(bool resume) { vector descendants; int pid = GetCurrentProcessId(); get_descendants(pid, descendants); suspend_or_resume_threads(descendants, 0, resume, false); } #else void suspend_or_resume_descendants(bool resume, bool use_tstp) { vector descendants; int pid = getpid(); get_descendants(pid, descendants); for (unsigned int i=0; i pids; pids.push_back(pid); suspend_or_resume_threads(pids, 0, resume, false); } #else void suspend_or_resume_process(int pid, bool resume, bool use_tstp) { ::kill(pid, resume?SIGCONT:(use_tstp?SIGTSTP:SIGSTOP)); } #endif // return OS-specific value associated with priority code // int process_priority_value(int priority) { #ifdef _WIN32 switch (priority) { case PROCESS_PRIORITY_LOWEST: return IDLE_PRIORITY_CLASS; case PROCESS_PRIORITY_LOW: return BELOW_NORMAL_PRIORITY_CLASS; case PROCESS_PRIORITY_NORMAL: return NORMAL_PRIORITY_CLASS; case PROCESS_PRIORITY_HIGH: return ABOVE_NORMAL_PRIORITY_CLASS; case PROCESS_PRIORITY_HIGHEST: return HIGH_PRIORITY_CLASS; } return 0; #else switch (priority) { case PROCESS_PRIORITY_LOWEST: return PROCESS_IDLE_PRIORITY; case PROCESS_PRIORITY_LOW: return PROCESS_MEDIUM_PRIORITY; case PROCESS_PRIORITY_NORMAL: return PROCESS_NORMAL_PRIORITY; case PROCESS_PRIORITY_HIGH: return PROCESS_ABOVE_NORMAL_PRIORITY; case PROCESS_PRIORITY_HIGHEST: return PROCESS_HIGH_PRIORITY; } return 0; #endif }