|
3 | 3 | #include "file.h"
|
4 | 4 | #include <sys/wait.h>
|
5 | 5 | #include <signal.h>
|
| 6 | +#include <cerrno> |
| 7 | +#include <boost/algorithm/string.hpp> |
6 | 8 |
|
7 |
| -template <> module::Module type<process::Process_>::module = process::module; |
| 9 | +using namespace std; |
8 | 10 |
|
9 |
| -static void start (bool clear, process::Process proc) { |
10 |
| - FDW out = openFile (proc->outFilename(), O_WRONLY | O_CREAT | (clear ? O_TRUNC : O_APPEND), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
11 |
| - program::IO io (0, out, 2); |
12 |
| - proc->pid = program::start (clear, proc->program, io); |
13 |
| - closeFd (out); // child process already duplicated it |
14 |
| - std::cout << (clear ? "launch " : "restart ") << proc->id << "(" << proc->pid << ")" << ": "; |
15 |
| - std::cout << proc->program << (clear ? " > " : " >> ") << proc->outFilename() << std::endl; |
| 11 | +template <> module::Module type<process::Process>::module = process::module; |
| 12 | + |
| 13 | +/** Log filename is derived (using md5) from program name its options, so each program will have a different but reproducible log filename. Pid is not included because we don't want the log filename to change when the process is restarted. */ |
| 14 | +string process::logFilename (program::Program program) { |
| 15 | + return program::md5Name (program) + ".log"; |
16 | 16 | }
|
17 | 17 |
|
18 |
| -static volatile unsigned nextProcessId; |
| 18 | +namespace process { |
| 19 | +Process start (bool clear, program::Program program) { |
| 20 | + string logFile = process::logFilename (program); |
| 21 | + FDW log = openFile (logFile, O_WRONLY | O_CREAT | (clear ? O_TRUNC : O_APPEND), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| 22 | + program::IO io (0, log, log); |
| 23 | + pid_t pid = program::start (clear, program, io); |
| 24 | + closeFd (log); // child process already duplicated it |
| 25 | + cout << (clear ? "launch " : "restart ") << pid << " " << program << (clear ? " &> " : " &>> ") << logFile << endl; |
| 26 | + return process::Process (program, pid); |
| 27 | +} |
| 28 | +} |
19 | 29 |
|
20 | 30 | /** Launch program with its stdout redirected to a local file named executable-id */
|
21 |
| -process::Process process::launch (program::Program program) { |
22 |
| - Process proc (new Process_ (program, ++ nextProcessId)); |
23 |
| - start (true, proc); |
24 |
| - return proc; |
25 |
| -} |
| 31 | +process::Process process::launch (program::Program program) {return process::start (true, program);} |
26 | 32 |
|
27 | 33 | /** Restart program under same process object. Process must not already be running. */
|
28 |
| -void process::restart (Process process) {start (false, process);} |
| 34 | +process::Process process::restart (program::Program program) {return process::start (false, program);} |
29 | 35 |
|
30 | 36 | /** Wait for process to terminate returning its exit code */
|
31 | 37 | int process::waitFor (Process process) {
|
32 | 38 | int status;
|
33 |
| - waitpid (process->pid, &status, 0); |
| 39 | + waitpid (process.pid, &status, 0); |
34 | 40 | return status;
|
35 | 41 | }
|
36 | 42 |
|
37 |
| -void process::signal (Signal s, Process p) {kill (p->pid, s);} |
| 43 | +void process::signal (Signal s, Process p) {kill (p.pid, s);} |
38 | 44 |
|
39 | 45 | /** Kill process. No-op if already dead */
|
40 | 46 | void process::terminate (Process p) {signal (SIGTERM, p);}
|
| 47 | + |
| 48 | +static const program::Program ps ("", "ps", program::options ("e", "", "o", "pid=", "o", "command=")); |
| 49 | + |
| 50 | +static boost::optional<process::Process> parsePSLine (string line) { |
| 51 | + if (line.empty()) return boost::none; |
| 52 | + vector<string> args; |
| 53 | + boost::split (args, line, boost::is_any_of (" ")); |
| 54 | + args.erase (remove (args.begin(), args.end(), ""), args.end()); |
| 55 | + if (args.size() < 2) throw runtime_error (to_string (ps) + " produced unexpected output: " + line); |
| 56 | + pid_t pid = parse_string<pid_t> (args[0]); |
| 57 | + string executable = args[1]; |
| 58 | + if (executable[0] == '(') return boost::none; // dead processes are surrounded by parens (at least on Mac OSX) |
| 59 | + args.erase (args.begin(), args.begin() + 2); |
| 60 | + program::Options options = program::parseArgs (args); |
| 61 | + return process::Process (program::Program ("", executable, options), pid); |
| 62 | +} |
| 63 | + |
| 64 | +/** Return all processes running on local machine. Note, they will be missing their program prepCommand. */ |
| 65 | +vector<process::Process> process::allProcesses () { |
| 66 | + Pipe pipe = makePipe(); |
| 67 | + program::IO io (0, pipe.writeEnd, 2); |
| 68 | + pid_t pid = program::start (false, ps, io); |
| 69 | + closeFd (pipe.writeEnd); |
| 70 | + FILE* in = fdopen (pipe.readEnd, "r"); |
| 71 | + vector<process::Process> procs; |
| 72 | + while (boost::optional<string> line = fGetLine (in)) { |
| 73 | + boost::optional<process::Process> p = parsePSLine (*line); |
| 74 | + if (p) procs.push_back (*p); |
| 75 | + } |
| 76 | + fclose (in); |
| 77 | + return procs; |
| 78 | +} |
0 commit comments