Skip to content

Commit 2b767fa

Browse files
author
Tony Hannan
committed
Process value (instead of stateful object). Can find all process on machine via ps
1 parent 701542a commit 2b767fa

File tree

10 files changed

+193
-72
lines changed

10 files changed

+193
-72
lines changed

Jamroot

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
project : source-location src : requirements <include>/opt/local/include <variant>release ;
22

33
lib dl : : <name>dl ;
4+
lib cr : : <name>crypto ;
45
lib sys : : <name>boost_system-mt <search>/opt/local/lib ;
56
lib fs : : <name>boost_filesystem-mt <search>/opt/local/lib ;
67
lib th : : <name>boost_thread-mt <search>/opt/local/lib ;
@@ -25,7 +26,7 @@ cpp-pch unit : unit.h : <optimization>off ;
2526
cpp-pch util : util.h : <optimization>off ;
2627
cpp-pch vector : vector.h : <optimization>off ;
2728

28-
lib 10util : [ glob *.cpp ] dl sys fs th ser ;
29+
lib 10util : [ glob *.cpp ] dl cr sys fs th ser ;
2930

3031
install ilib : 10util : <location>/usr/local/lib ;
3132
install ihead : [ glob *.h ]

SConstruct

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ lib = SharedLibrary (libname, Glob('src/*.cpp'),
44
CCFLAGS = ['-pg', '-rdynamic'],
55
CPPPATH = ['.', '/opt/local/include'],
66
LIBPATH = ['/opt/local/lib'],
7-
LIBS = Split ('dl boost_system-mt boost_thread-mt boost_filesystem-mt boost_serialization-mt') )
7+
LIBS = Split ('dl crypto boost_system-mt boost_thread-mt boost_filesystem-mt boost_serialization-mt') )
88

99
Alias ('install', '/usr/local')
1010
Install ('/usr/local/lib', lib)

src/file.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <unistd.h>
66
#include <stdexcept>
77

8+
using namespace std;
9+
810
Pipe makePipe () {
911
int pipeFds[2];
1012
int ok = pipe (pipeFds);
@@ -15,7 +17,7 @@ Pipe makePipe () {
1517
return p;
1618
}
1719

18-
FD openFile (std::string filename, int openFlags, int openMode) {
20+
FD openFile (string filename, int openFlags, int openMode) {
1921
FD fd = open (filename.c_str(), openFlags, openMode);
2022
if (fd == -1) throw std::runtime_error ("Failure opening " + filename + ": " + to_string(errno));
2123
return fd;
@@ -30,3 +32,16 @@ void dupFd (FD from, FD to) {
3032
int ok = dup2 (from, to);
3133
if (ok == -1) throw std::runtime_error ("Failure duplicating FD from " + to_string(from) + " to " + to_string(to) + ": " + to_string(errno));
3234
}
35+
36+
/** Return next line or NULL if EOF */
37+
boost::optional<string> fGetLine (FILE* file) {
38+
string s;
39+
int c;
40+
while (true) {
41+
c = fgetc (file);
42+
if (c == '\n' || c == EOF) break;
43+
s.push_back (c);
44+
}
45+
if (c == EOF && s.empty()) return boost::none;
46+
return s;
47+
}

src/file.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include <string>
66
#include <fcntl.h>
7+
#include <cstdio>
8+
#include <boost/optional.hpp>
79

810
typedef int FD; // File descriptor
911
typedef FD FDW; // Write-only file descriptor
@@ -24,3 +26,11 @@ void closeFd (FD fd);
2426

2527
/** Throw exception on error */
2628
void dupFd (FD from, FD to);
29+
30+
/** Convert FD to FILE*. mode must be same that FD was open it ("r" or "w") */
31+
// FILE* fdopen (FD fd, const char *mode);
32+
33+
// fclose (FILE*)
34+
35+
/** Return next line or NULL if EOF */
36+
boost::optional<std::string> fGetLine (FILE* file);

src/process.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,76 @@
33
#include "file.h"
44
#include <sys/wait.h>
55
#include <signal.h>
6+
#include <cerrno>
7+
#include <boost/algorithm/string.hpp>
68

7-
template <> module::Module type<process::Process_>::module = process::module;
9+
using namespace std;
810

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";
1616
}
1717

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+
}
1929

2030
/** 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);}
2632

2733
/** 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);}
2935

3036
/** Wait for process to terminate returning its exit code */
3137
int process::waitFor (Process process) {
3238
int status;
33-
waitpid (process->pid, &status, 0);
39+
waitpid (process.pid, &status, 0);
3440
return status;
3541
}
3642

37-
void process::signal (Signal s, Process p) {kill (p->pid, s);}
43+
void process::signal (Signal s, Process p) {kill (p.pid, s);}
3844

3945
/** Kill process. No-op if already dead */
4046
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+
}

src/process.h

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#include <signal.h>
66
#include <iostream>
7-
#include <boost/algorithm/string.hpp>
8-
#include <boost/shared_ptr.hpp>
97
#include "program.h"
108
#include "util.h" // to_string
119
#include "module.h"
@@ -14,44 +12,36 @@ namespace process {
1412

1513
const module::Module module ("10util", "10util/process.h");
1614

17-
/** Process running a Program */
18-
class Process_ {
19-
public:
15+
struct Process {
2016
program::Program program;
21-
unsigned id; // our id, not OS id
22-
pid_t pid; // OS id
23-
Process_ (program::Program program, unsigned id) : program(program), id(id) {}
24-
Process_ () {} // for serialization
25-
std::string shortName () const {
26-
std::vector<std::string> parts;
27-
boost::split (parts, program.executable, boost::is_any_of ("/"));
28-
return *--parts.end() + "-" + to_string (id);
29-
}
30-
std::string outFilename () const {return shortName() + ".out";}
17+
pid_t pid;
18+
Process (program::Program program, pid_t pid) : program(program), pid(pid) {}
3119
};
3220

33-
typedef boost::shared_ptr<Process_> Process;
21+
/** 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. */
22+
std::string logFilename (program::Program);
3423

3524
/** Launch program with its stdout redirected to a local file named executable-id */
36-
Process launch (program::Program program);
25+
Process launch (program::Program);
3726

3827
/** Restart program (does not run prepCommand first) */
39-
void restart (Process deadProcess);
28+
Process restart (program::Program);
4029

4130
/** Wait for process to terminate returning its exit code */
42-
int waitFor (Process process);
31+
int waitFor (Process);
4332

4433
typedef int Signal;
4534

4635
/** Send signal to process. No-op if process dead */
4736
void signal (Signal, Process);
4837

4938
/** Kill process. No-op if already dead */
50-
void terminate (Process p);
39+
void terminate (Process);
5140

52-
/** Program process is running */
53-
inline program::Program program (Process process) {return process->program;}
41+
/** Return all processes running on local machine. They will be missing their program prepCommand. */
42+
std::vector<Process> allProcesses ();
5443

5544
}
5645

57-
inline std::ostream& operator<< (std::ostream& out, const process::Process_& p) {out << p.shortName(); return out;}
46+
inline std::ostream& operator<< (std::ostream& out, const process::Process& p) {
47+
out << p.pid << " " << p.program; return out;}

0 commit comments

Comments
 (0)