libcosmos
Linux C++ System Programming Library
All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
cosmos::ChildCloner Class Reference

Sub process creation facility. More...

#include <ChildCloner.hxx>

Public Types

typedef std::function< void(const ChildCloner &)> Callback
 callback function type used in setPostForkCB().
 

Public Member Functions

 ChildCloner ()=default
 Creates an instance with default settings.
 
 ChildCloner (const StringViewVector args)
 Creates an instance configured with the provided arguments.
 
bool hasExe () const
 Returns whether currently an executable is set.
 
auto & getExe ()
 Returns the currently set executable name.
 
const auto & getExe () const
 
void setExe (const std::string_view exe)
 Sets the path to the executable and argv0.
 
const auto & getArgs () const
 Returns the currently configured argument vector.
 
StringVectorgetArgs ()
 
void setArgs (const StringVector &sv)
 Sets the argument vector to be used including argv0.
 
void setArgsFromView (const StringViewVector &svv)
 
void clearArgs ()
 Clears any currently set parameters.
 
void setCWD (const std::string_view cwd)
 Set an explicit working directory the child process.
 
const auto & getCWD () const
 Returns the currently set CWD for sub process execution.
 
void setInheritCWD ()
 Clear a previously configured CWD and inherit it from the parent.
 
void setEnv (const StringVector &vars)
 Sets explicit environment variables for the child process.
 
void setInheritEnv ()
 Clears any previously set environment variables and let's to-be-started child processes inherit the parent's environment.
 
void setStdErr (FileDescriptor fd)
 Redirect the child's stderr to the given file descriptor.
 
void setStdOut (FileDescriptor fd)
 
void setStdIn (FileDescriptor fd)
 
void addInheritFD (FileDescriptor fd)
 Adds a file descriptor to inherit to the child process.
 
void resetStdFiles ()
 Restore the default inheritance behaviour for stdin/stderr/stdout.
 
template<typename SCHED_SETTING >
void setSchedulerSettings (const SCHED_SETTING &ss)
 Sets scheduler type and settings.
 
void setInheritSchedulerSettings ()
 clear previously set scheduler settings and inherit them from the parent instead
 
void setPostForkCB (Callback cb)
 Sets a callback function to be invoked in the child process context.
 
void resetPostForkCB ()
 Removes a previously stored post fork callback.
 
SubProc run ()
 Clone a new process and execute the currently configured program.
 

Protected Member Functions

void postFork ()
 Performs settings needed after forking i.e. in the child process but before exec()'ing.
 
void resetSignals ()
 restore a default signal mask in child process context.
 
void redirectFD (FileDescriptor orig, FileDescriptor redirect)
 Redirects the given orig file descriptor to redirect (used in child context).
 
void setArgv0 ()
 sets argv0 from the current executable name.
 
void setExeFromArgv0 ()
 

Protected Attributes

std::string m_executable
 Path to the child process executable to run.
 
StringVector m_argv
 Argument vector including argv0 denoting the executable name (which can be different than m_executable.
 
std::string m_cwd
 Path to an explicit working directory, if any.
 
std::optional< StringVectorm_env
 Explicit environment child environment variables, if any.
 
std::optional< SchedulerSettingsVariant > m_sched_settings
 Scheduler policy settings, if any.
 
FileDescriptor m_stdout
 File descriptor to use as child's stdin.
 
FileDescriptor m_stderr
 File descriptor to use as child's stderr.
 
FileDescriptor m_stdin
 File descriptor to use as child's stdin.
 
std::vector< FileDescriptorm_inherit_fds
 Additional file descriptors to inherit to the child process.
 
Callback m_post_fork_cb = nullptr
 

Friends

std::ostream & operator<< (std::ostream &o, const ChildCloner &proc)
 Outputs a summary of the ChildCloner's configuration.
 

Detailed Description

Sub process creation facility.

This type allows to configure and create child processes. This is a rather heavy weight type that can be reused to create multiple child processes. The SubProc type returned from wait() is rather lightweight in contrast.

By default, created child processes will inherit the current process's stdout, stderr and stdin file descriptors. You can redirect the child's stdout, stderr and stdin file descriptors via the setStdErr(), setStdOut() and setStdIn() member functions. It is expected that all file descriptors used have the O_CLOEXEC flag set. The implementation will take care to unset this flag appropriately in a manner that allows the file descriptors to be inherited to the child but at the same time won't influence other threads in the current process (to avoid races if multiple threads invoke clone()).

Furthermore the child's environment variables, current working directory, scheduling policy and command line arguments can be configured.

For advanced usage a post fork callback can be installed that performs actions before the child process is replaced by the new target executable.

Definition at line 40 of file ChildCloner.hxx.

Member Typedef Documentation

◆ Callback

std::function<void (const ChildCloner&)> cosmos::ChildCloner::Callback

callback function type used in setPostForkCB().

Definition at line 44 of file ChildCloner.hxx.

Constructor & Destructor Documentation

◆ ChildCloner()

cosmos::ChildCloner::ChildCloner ( const StringViewVector args)
inlineexplicit

Creates an instance configured with the provided arguments.

This is a convenience constructor for simple execution of child processes without special settings. The executable path is taken from args[0].

Definition at line 57 of file ChildCloner.hxx.

57 {
58 setArgsFromView(args);
59 }
void setArgsFromView(const StringViewVector &svv)

Member Function Documentation

◆ addInheritFD()

void cosmos::ChildCloner::addInheritFD ( FileDescriptor fd)
inline

Adds a file descriptor to inherit to the child process.

Beyond the stdin, stdout and stderr file descriptor additional descriptors can be inherited into the child process context. The fd should have the O_CLOEXEC flag set. The implementation will adjust this flag appropriately to allow the fd to be inherited across execution of the new child process image.

The file descriptor number of fd will not be change in the child process. Therefore it must not be number 0, 1 or 2 (stdin, stdout, stderr), since these are already covered by the setStdErr(), setStdOut() and setStdIn() functions.

The ownership of fd remains with the caller. The caller must ensure that the file descriptor stays valid until run() is invoked. Otherwise the child process execution / descriptor inheritance will fail. The implementation will not alter the fd in the current process's context.

The child process must be instructed which FD to use and for which purpose. Some programs support command line arguments or evaluate environment variables to get this knowledge. Some programs may also be hardcoded to use certain file descriptor numbers.

Definition at line 184 of file ChildCloner.hxx.

184 {
185 if (fd.raw() <= FileNum::STDERR) {
186 cosmos_throw(UsageError{"added stdio or invalid FD as extra inherit FD"});
187 };
188 m_inherit_fds.push_back(fd);
189 }
std::vector< FileDescriptor > m_inherit_fds
Additional file descriptors to inherit to the child process.

◆ clearArgs()

void cosmos::ChildCloner::clearArgs ( )
inline

Clears any currently set parameters.

Clears all currently set arguments but keeps the executable and argv0.

Definition at line 113 of file ChildCloner.hxx.

113 {
114 if (m_argv.size() > 1) {
115 m_argv.erase(m_argv.begin() + 1);
116 }
117 }
StringVector m_argv
Argument vector including argv0 denoting the executable name (which can be different than m_executabl...

◆ getArgs() [1/2]

StringVector & cosmos::ChildCloner::getArgs ( )
inline
See also
getArgs() const

Definition at line 88 of file ChildCloner.hxx.

88{ return m_argv; }

◆ getArgs() [2/2]

const auto & cosmos::ChildCloner::getArgs ( ) const
inline

Returns the currently configured argument vector.

This vector is by convention including the executable name as first argument (argv0). You may change this argument using this function for special use cases (e.g. programs that behave differently depending on argv0).

Definition at line 85 of file ChildCloner.hxx.

85{ return m_argv; }

◆ getCWD()

const auto & cosmos::ChildCloner::getCWD ( ) const
inline

Returns the currently set CWD for sub process execution.

Definition at line 127 of file ChildCloner.hxx.

127{ return m_cwd; }
std::string m_cwd
Path to an explicit working directory, if any.

◆ getExe() [1/2]

auto & cosmos::ChildCloner::getExe ( )
inline

Returns the currently set executable name.

Definition at line 65 of file ChildCloner.hxx.

65{ return m_executable; }
std::string m_executable
Path to the child process executable to run.

◆ getExe() [2/2]

const auto & cosmos::ChildCloner::getExe ( ) const
inline

Definition at line 66 of file ChildCloner.hxx.

66{ return m_executable; }

◆ hasExe()

bool cosmos::ChildCloner::hasExe ( ) const
inline

Returns whether currently an executable is set.

Definition at line 62 of file ChildCloner.hxx.

62{ return !m_executable.empty(); }

◆ postFork()

void cosmos::ChildCloner::postFork ( )
protected

Performs settings needed after forking i.e. in the child process but before exec()'ing.

Definition at line 195 of file ChildCloner.cxx.

195 {
196 if (m_post_fork_cb) {
197 m_post_fork_cb(*this);
198 }
199
200 if (m_sched_settings) {
201 try {
202 std::visit([](auto &&sched_settings) {
203 sched_settings.apply(ProcessID::SELF);
204 }, *m_sched_settings);
205 } catch(const std::exception &ex) {
206 // treat this as non-critical, the process can still
207 // run, even if not prioritized.
208 print_child_error("sched_setscheduler", ex.what());
209 }
210 }
211
212 resetSignals();
213
214 if (!m_cwd.empty()) {
215 fs::change_dir(m_cwd);
216 }
217
218 redirectFD(cosmos::stdout, m_stdout);
219 redirectFD(cosmos::stderr, m_stderr);
220 redirectFD(cosmos::stdin, m_stdin);
221
222 for (auto fd: m_inherit_fds) {
223 fd.setCloseOnExec(false);
224 }
225}
void resetSignals()
restore a default signal mask in child process context.
std::optional< SchedulerSettingsVariant > m_sched_settings
Scheduler policy settings, if any.
FileDescriptor m_stderr
File descriptor to use as child's stderr.
void redirectFD(FileDescriptor orig, FileDescriptor redirect)
Redirects the given orig file descriptor to redirect (used in child context).
FileDescriptor m_stdout
File descriptor to use as child's stdin.
FileDescriptor m_stdin
File descriptor to use as child's stdin.

◆ redirectFD()

void cosmos::ChildCloner::redirectFD ( FileDescriptor orig,
FileDescriptor redirect )
protected

Redirects the given orig file descriptor to redirect (used in child context).

Parameters
[in]origThe file descriptor that should be replaced by redirect

Definition at line 227 of file ChildCloner.cxx.

227 {
228 if (redirect.invalid())
229 return;
230
231 redirect.duplicate(orig, CloseOnExec(false));
232}

◆ resetPostForkCB()

void cosmos::ChildCloner::resetPostForkCB ( )
inline

Removes a previously stored post fork callback.

Definition at line 232 of file ChildCloner.hxx.

232{ m_post_fork_cb = nullptr; }

◆ resetSignals()

void cosmos::ChildCloner::resetSignals ( )
protected

restore a default signal mask in child process context.

Definition at line 186 of file ChildCloner.cxx.

186 {
187 /*
188 * the blocked signal mask is inherited via execve(), thus we need to
189 * initialize defaults here again.
190 */
191 SigSet sigs{SigSet::filled};
192 signal::unblock(sigs);
193}

◆ resetStdFiles()

void cosmos::ChildCloner::resetStdFiles ( )
inline

Restore the default inheritance behaviour for stdin/stderr/stdout.

Any previously set file descriptor overrides will be reset and the child process will inherit the parent process's std file descriptors.

Definition at line 197 of file ChildCloner.hxx.

197 {
198 m_stderr.reset();
199 m_stdin.reset();
200 m_stdout.reset();
201 }
void reset()
Invalidates the stored file descriptor.

◆ run()

SubProc cosmos::ChildCloner::run ( )

Clone a new process and execute the currently configured program.

All settings made via member functions will come into effect. The configured executable will be invoked and passed the configured arguments.

The returned object is a move-only type that can be used to control the new sub process, communicate with it and evaluate its exit state.

It is mandatory to join the child process via SubProc::wait() before the SubProc object is destroyed.

Definition at line 77 of file ChildCloner.cxx.

77 {
78 if (m_executable.empty() || m_argv.empty()) {
79 cosmos_throw (UsageError(
80 "attempted to run a sub process w/o specifying an executable path and/or argv0"
81 ));
82 }
83
84 if (!cosmos::running_on_valgrind) {
85 // use clone3() instead of fork():
86 //
87 // clone() allows us to get a pid fd for the child in a race free
88 // fashion
89 //
90 // clone3() has fork() semantics and is easier to use for this case
91 // than the older clone syscalls.
92 //
93 // NOTE: clone3() is not yet supported in Valgrind, which
94 // means that is isn't possible to run programs that employ
95 // this system call through Valgrind anymore. clone2() is
96 // annoying to use because it doesn't have fork() semantics
97 // though ... maybe we can sit this out until Valgrind gets
98 // support for clone3().
99 PidFD pidfd;
100 CloneArgs clone_args;
101 clone_args.setPidFD(pidfd);
102 clone_args.setFlags({CloneFlag::CLEAR_SIGHAND, CloneFlag::PIDFD});
103
104 if (auto pid = proc::clone(clone_args); pid != std::nullopt) {
105 // parent process with child pid
106 return SubProc{*pid, pidfd};
107 }
108 } else {
109 // An alternative is using regular fork() and creating a
110 // pidfd from the child PID. As long as no one is collecting
111 // the child status in parallel in another thread via any of
112 // the wait() functions this is race free.
113 //
114 // This has some hairy error situations, though:
115 //
116 // - creating the pidfd after fork() can fail, in this case we
117 // cannot fulfill the API and need to end the child process
118 // again.
119 // - the child process may exit before we have a chance to
120 // create the pidfd. Basically a pidfd can also be obtained
121 // for a zombie child process. But this is not possible if
122 // the SIGCHLD handler is set so SIG_IGN. Since we cannot
123 // guarantee this in a general purpose library we need to
124 // deal with the worst case.
125 //
126 // To cover these situations we use an EventFile. The child
127 // process will not exec() before the parent process signals.
128 // If something goes wrong in the parent process then the
129 // child process is killed.
130
131 EventFile ev;
132
133 if (auto child = proc::fork(); child != std::nullopt) {
134 auto pid = *child;
135 try {
136 auto pidfile = UnsafeProcessFile{pid};
137 auto pidfd = pidfile.fd();
138 ev.signal();
139 pidfile.invalidate();
140 return SubProc{pid, pidfd};
141 } catch (...) {
142 try {
143 signal::send(pid, signal::KILL);
144 } catch (const std::exception &ex) {
145 std::cerr << "WARNING: failed to kill half-ready child process\n";
146 }
147 (void)proc::wait(pid);
148 throw;
149 }
150 } else {
151 try {
152 // wait for the parent to signal us to
153 // continue to exec()
154 ev.wait();
155 } catch (const std::exception &ex) {
156 print_child_error("post fork/ev wait", ex.what());
157 proc::exit(ExitStatus{3});
158 }
159 }
160 }
161
162 // the child process -- let's do something!
163
164 try {
165 postFork();
166
167 auto argv = setup_argv(m_argv);
168
169 if (!m_env) {
170 proc::exec(argv[0], &argv);
171 } else {
172 auto envp = setup_env(m_env.value());
173 proc::exec(argv[0], &argv, &envp);
174 }
175 } catch (const CosmosError &ce) {
176 print_child_error("post fork/exec", ce.what());
177 // use something else than "1" which might help debugging this
178 // situation a bit.
179 proc::exit(ExitStatus{3});
180 }
181
182 // should never be reached
183 return SubProc();
184}
void postFork()
Performs settings needed after forking i.e. in the child process but before exec()'ing.
std::optional< StringVector > m_env
Explicit environment child environment variables, if any.

◆ setArgs()

void cosmos::ChildCloner::setArgs ( const StringVector & sv)
inline

Sets the argument vector to be used including argv0.

This also sets a new executable path from sv[0], or clears the executable, if sv is empty.

Definition at line 95 of file ChildCloner.hxx.

95 {
96 m_argv = sv;
97 setExeFromArgv0();
98 }

◆ setArgsFromView()

void cosmos::ChildCloner::setArgsFromView ( const StringViewVector & svv)
inline
See also
setArgs(const StringVector &)

Definition at line 101 of file ChildCloner.hxx.

101 {
102 m_argv.clear();
103 for (const auto s: svv)
104 m_argv.push_back(std::string{s});
105 setExeFromArgv0();
106 }

◆ setArgv0()

void cosmos::ChildCloner::setArgv0 ( )
inlineprotected

sets argv0 from the current executable name.

Definition at line 264 of file ChildCloner.hxx.

264 {
265 if (m_argv.empty())
266 m_argv.emplace_back(m_executable);
267 else
268 m_argv[0] = m_executable;
269 }

◆ setCWD()

void cosmos::ChildCloner::setCWD ( const std::string_view cwd)
inline

Set an explicit working directory the child process.

If cwd is empty then the parent process's CWD is inherited to the child.

Definition at line 124 of file ChildCloner.hxx.

124{ m_cwd = cwd; }

◆ setEnv()

void cosmos::ChildCloner::setEnv ( const StringVector & vars)
inline

Sets explicit environment variables for the child process.

By default the parent process's environment is inherited to the child (see also setInheritEnv()).

Each entry in the provided vector should be of the form "name=value". The provided variables will make up the complete child process environment.

Definition at line 141 of file ChildCloner.hxx.

141{ m_env = vars; }

◆ setExe()

void cosmos::ChildCloner::setExe ( const std::string_view exe)
inline

Sets the path to the executable and argv0.

The actual executable path and argv0 will always be the same. You can change argv0, if necessary via getArgs().

Definition at line 73 of file ChildCloner.hxx.

73 {
74 m_executable = exe;
75 setArgv0();
76 }
void setArgv0()
sets argv0 from the current executable name.

◆ setExeFromArgv0()

void cosmos::ChildCloner::setExeFromArgv0 ( )
inlineprotected

Definition at line 271 of file ChildCloner.hxx.

271 {
272 if (m_argv.empty())
273 m_executable.clear();
274 else
275 m_executable = m_argv[0];
276 }

◆ setInheritCWD()

void cosmos::ChildCloner::setInheritCWD ( )
inline

Clear a previously configured CWD and inherit it from the parent.

Definition at line 130 of file ChildCloner.hxx.

130{ m_cwd.clear(); }

◆ setInheritEnv()

void cosmos::ChildCloner::setInheritEnv ( )
inline

Clears any previously set environment variables and let's to-be-started child processes inherit the parent's environment.

Definition at line 145 of file ChildCloner.hxx.

145{ m_env.reset(); }

◆ setInheritSchedulerSettings()

void cosmos::ChildCloner::setInheritSchedulerSettings ( )
inline

clear previously set scheduler settings and inherit them from the parent instead

Definition at line 213 of file ChildCloner.hxx.

213{ m_sched_settings.reset(); }

◆ setPostForkCB()

void cosmos::ChildCloner::setPostForkCB ( Callback cb)
inline

Sets a callback function to be invoked in the child process context.

This function will be invoked in the child process after the clone happened but before the new program is executed. It can be used to perform custom child process setup, but care should be taken not to interfere with the SubProc's internal child process setup.

This callback is invoked before any redirections or other settings are performed by the implementation.

Be aware that any exceptions thrown from this callback will prevent the child process from executing, but you will not be notified about this apart from premature exit of the child process.

Definition at line 229 of file ChildCloner.hxx.

229{ m_post_fork_cb = cb; }

◆ setSchedulerSettings()

template<typename SCHED_SETTING >
void cosmos::ChildCloner::setSchedulerSettings ( const SCHED_SETTING & ss)
inline

Sets scheduler type and settings.

By default the parent's scheduling settings will be inherited. If you want to explicitly change scheduling settings then apply the appropriate settings here.

Definition at line 210 of file ChildCloner.hxx.

210{ m_sched_settings = ss; };

◆ setStdErr()

void cosmos::ChildCloner::setStdErr ( FileDescriptor fd)
inline

Redirect the child's stderr to the given file descriptor.

This only affects yet to be started child processes. The file descriptor is expected to have the close-on-exec flag set, the inheritance to the child process will be performed appropriately by the implementation.

Definition at line 154 of file ChildCloner.hxx.

154{ m_stderr = fd; }

◆ setStdIn()

void cosmos::ChildCloner::setStdIn ( FileDescriptor fd)
inline
See also
setStderr()

Definition at line 158 of file ChildCloner.hxx.

158{ m_stdin = fd; }

◆ setStdOut()

void cosmos::ChildCloner::setStdOut ( FileDescriptor fd)
inline
See also
setStderr()

Definition at line 156 of file ChildCloner.hxx.

156{ m_stdout = fd; }

Friends And Related Symbol Documentation

◆ operator<<

std::ostream & operator<< ( std::ostream & o,
const ChildCloner & proc )
friend

Outputs a summary of the ChildCloner's configuration.

Definition at line 236 of file ChildCloner.cxx.

236 {
237 o << "Arguments: " << proc.getArgs() << "\n";
238 if (!proc.getCWD().empty())
239 o << "CWD: " << proc.getCWD() << "\n";
240
241 return o;
242}

Member Data Documentation

◆ m_argv

StringVector cosmos::ChildCloner::m_argv
protected

Argument vector including argv0 denoting the executable name (which can be different than m_executable.

Definition at line 283 of file ChildCloner.hxx.

◆ m_cwd

std::string cosmos::ChildCloner::m_cwd
protected

Path to an explicit working directory, if any.

Definition at line 285 of file ChildCloner.hxx.

◆ m_env

std::optional<StringVector> cosmos::ChildCloner::m_env
protected

Explicit environment child environment variables, if any.

Definition at line 287 of file ChildCloner.hxx.

◆ m_executable

std::string cosmos::ChildCloner::m_executable
protected

Path to the child process executable to run.

Definition at line 281 of file ChildCloner.hxx.

◆ m_inherit_fds

std::vector<FileDescriptor> cosmos::ChildCloner::m_inherit_fds
protected

Additional file descriptors to inherit to the child process.

Definition at line 298 of file ChildCloner.hxx.

◆ m_post_fork_cb

Callback cosmos::ChildCloner::m_post_fork_cb = nullptr
protected

Definition at line 300 of file ChildCloner.hxx.

◆ m_sched_settings

std::optional<SchedulerSettingsVariant> cosmos::ChildCloner::m_sched_settings
protected

Scheduler policy settings, if any.

Definition at line 289 of file ChildCloner.hxx.

◆ m_stderr

FileDescriptor cosmos::ChildCloner::m_stderr
protected

File descriptor to use as child's stderr.

Definition at line 294 of file ChildCloner.hxx.

◆ m_stdin

FileDescriptor cosmos::ChildCloner::m_stdin
protected

File descriptor to use as child's stdin.

Definition at line 296 of file ChildCloner.hxx.

◆ m_stdout

FileDescriptor cosmos::ChildCloner::m_stdout
protected

File descriptor to use as child's stdin.

Definition at line 292 of file ChildCloner.hxx.


The documentation for this class was generated from the following files: