Sane C++ Libraries
C++ Platform Abstraction Libraries
Process.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "../Containers/IntrusiveDoubleLinkedList.h"
6#include "../File/FileDescriptor.h"
7#include "../Strings/String.h"
8#include "ProcessDescriptor.h"
9
10namespace SC
11{
12struct SC_COMPILER_EXPORT Process;
13struct SC_COMPILER_EXPORT ProcessChain;
14struct SC_COMPILER_EXPORT ProcessFork;
15struct ProcessID;
16struct ProcessEnvironment;
17} // namespace SC
18
21
24
27{
28 int32_t pid = 0;
29};
30
63
65{
67 {
69 Options();
70 };
71
72 struct StdStream
73 {
74 // clang-format off
76 StdStream(String& externalString) { operation = Operation::String; string = &externalString;}
77
79 StdStream(Buffer& externalBuffer) { operation = Operation::Vector; buffer = &externalBuffer; }
80 // clang-format on
81
84 {
85 operation = Operation::FileDescriptor;
86 (void)file.get(fileDescriptor, Result::Error("Invalid redirection file descriptor"));
87 file.detach();
88 }
89
91 {
92 operation = Operation::ExternalPipe;
93 pipeDescriptor = &pipe;
94 }
95
96 protected:
98 {
99 };
100 StdStream() = default;
101 StdStream(AlreadySetup) { operation = Operation::AlreadySetup; }
102 friend struct Process;
103 friend struct ProcessChain;
104
105 enum class Operation
106 {
107 AlreadySetup,
108 Inherit,
109 Ignore,
110 ExternalPipe,
112 Vector,
113 String,
114 WritableSpan,
115 ReadableSpan
116 };
117 Operation operation = Operation::Inherit;
118
119 Span<const char> readableSpan;
120 Span<char> writableSpan;
121
122 String* string;
123 Buffer* buffer;
124
125 FileDescriptor::Handle fileDescriptor;
126
127 PipeDescriptor* pipeDescriptor;
128 };
129
130 struct StdOut : public StdStream
131 {
132 // clang-format off
133 struct Ignore{};
134 struct Inherit{};
135
137 StdOut(Ignore) { operation = Operation::Ignore; }
138
140 StdOut(Inherit) { operation = Operation::Inherit; }
141
143 StdOut(Span<char> span) { operation = Operation::WritableSpan; writableSpan = span; }
144
145 using StdStream::StdStream;
146 friend struct ProcessChain;
147 // clang-format on
148 };
149
150 using StdErr = StdOut;
151
152 struct StdIn : public StdStream
153 {
154 // clang-format off
155 struct Inherit{};
156
158 StdIn(Inherit) { operation = Operation::Inherit; }
159
161 template <int N> StdIn(const char (&item)[N]) : StdIn(StringView({item, N - 1}, true, StringEncoding::Ascii)) {}
162
164 StdIn(StringView string) : StdIn(string.toCharSpan()) {}
165
167 StdIn(Span<const char> span) { operation = Operation::ReadableSpan; readableSpan = span;}
168
169 using StdStream::StdStream;
170 friend struct ProcessChain;
171 // clang-format on
172 };
173
177
179 [[nodiscard]] Result waitForExitSync();
180
189 const StdOut& stdOut = StdOut::Inherit{}, //
190 const StdIn& stdIn = StdIn::Inherit{}, //
191 const StdErr& stdErr = StdErr::Inherit{})
192 {
193 SC_TRY(formatArguments(cmd));
194 return launch(stdOut, stdIn, stdErr);
195 }
196
204 [[nodiscard]] Result exec(Span<const StringView> cmd, //
205 const StdOut& stdOut = StdOut::Inherit{}, //
206 const StdIn& stdIn = StdIn::Inherit{}, //
207 const StdErr& stdErr = StdErr::Inherit{})
208 {
209 SC_TRY(launch(cmd, stdOut, stdIn, stdErr));
210 return waitForExitSync();
211 }
212
214 int32_t getExitStatus() const { return exitStatus.status; }
215
217 [[nodiscard]] Result setWorkingDirectory(StringView processWorkingDirectory);
218
220 void inheritParentEnvironmentVariables(bool inherit) { inheritEnv = inherit; }
221
223 [[nodiscard]] Result setEnvironment(StringView environmentVariable, StringView value);
224
226 [[nodiscard]] static size_t getNumberOfProcessors();
227
229 [[nodiscard]] static bool isWindowsConsoleSubsystem();
230
232 [[nodiscard]] static bool isWindowsEmulatedProcess();
233
234 private:
236
237 FileDescriptor stdInFd;
238 FileDescriptor stdOutFd;
239 FileDescriptor stdErrFd;
240
241 [[nodiscard]] Result launch(const StdOut& stdOutput, const StdIn& stdInput, const StdErr& stdError);
242
243 [[nodiscard]] Result formatArguments(Span<const StringView> cmd);
244
245 StringNative<255> currentDirectory = StringEncoding::Native;
246
247 // On Windows command holds the concatenation of executable and arguments.
248 // On Posix command holds the concatenation of executable and arguments SEPARATED BY null-terminators (\0).
249 // This is done so that in this single buffer with no allocation (under 255) or a single allocation (above 255)
250 // we can track all arguments to be passed to execve.
252#if !SC_PLATFORM_WINDOWS // On Posix we need to track the "sub-strings" hidden in command
253 static constexpr size_t MAX_NUM_ARGUMENTS = 64;
254 size_t commandArgumentsByteOffset[MAX_NUM_ARGUMENTS]; // Tracking length of each argument in the command string
255 size_t commandArgumentsNumber = 0; // Counts number of arguments (including executable name)
256#endif
257
259
260 static constexpr size_t MAX_NUM_ENVIRONMENT = 256;
261
262 size_t environmentByteOffset[MAX_NUM_ENVIRONMENT]; // Tracking length of each environment variable
263 size_t environmentNumber = 0; // Counts number of environment variable
264
265 bool inheritEnv = true;
266
267 friend struct IntrusiveDoubleLinkedList<Process>;
268 friend struct ProcessChain;
269 ProcessChain* parent = nullptr;
270
271 Process* next = nullptr;
272 Process* prev = nullptr;
273 struct Internal;
274 friend struct ProcessFork;
275 Result launchImplementation();
276};
277
295{
296 Process::Options options;
301 [[nodiscard]] Result pipe(Process& process, const Span<const StringView> cmd);
302
306 [[nodiscard]] Result launch(const Process::StdOut& stdOut = Process::StdOut::Inherit{}, //
307 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
308 const Process::StdErr& stdErr = Process::StdErr::Inherit{});
309
312 [[nodiscard]] Result waitForExitSync();
313
316 [[nodiscard]] Result exec(const Process::StdOut& stdOut = Process::StdOut::Inherit{}, //
317 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
318 const Process::StdErr& stdErr = Process::StdErr::Inherit{})
319 {
320 SC_TRY(launch(stdOut, stdIn, stdErr));
321 return waitForExitSync();
322 }
323
324 private:
325 IntrusiveDoubleLinkedList<Process> processes;
326};
327
333{
336
337 ProcessEnvironment(const ProcessEnvironment&) = delete;
339 ProcessEnvironment& operator=(const ProcessEnvironment&) = delete;
340 ProcessEnvironment& operator=(ProcessEnvironment&&) = delete;
341
343 [[nodiscard]] size_t size() const { return numberOfEnvironment; }
344
349 [[nodiscard]] bool get(size_t index, StringView& name, StringView& value) const;
350
355 [[nodiscard]] bool contains(StringView variableName, size_t* index = nullptr);
356
357 private:
358 size_t numberOfEnvironment = 0;
359#if SC_PLATFORM_WINDOWS
360 static constexpr size_t MAX_ENVIRONMENTS = 256;
361
362 StringView envStrings[MAX_ENVIRONMENTS];
363 wchar_t* environment = nullptr;
364#else
365 char** environment = nullptr;
366#endif
367};
368
395{
396 ProcessFork();
397 ~ProcessFork();
398 ProcessFork(const ProcessFork&) = delete;
399 ProcessFork* operator=(const ProcessFork&) = delete;
400
401 enum Side
402 {
405 };
406
408 [[nodiscard]] Side getSide() const { return side; }
409
410 enum State
411 {
414 };
415
418
421
424
426 int32_t getExitStatus() const { return exitStatus.status; }
427
430
433
434 private:
435 Side side = ForkParent;
436#if SC_PLATFORM_WINDOWS
437 detail::ProcessDescriptorDefinition::Handle processHandle = detail::ProcessDescriptorDefinition::Invalid;
438 detail::ProcessDescriptorDefinition::Handle threadHandle = detail::ProcessDescriptorDefinition::Invalid;
439#else
440 ProcessID processID;
441#endif
443
444 PipeDescriptor parentToFork;
445 PipeDescriptor forkToParent;
446};
#define SC_COMPILER_EXPORT
Macro for symbol visibility in non-MSVC compilers.
Definition: Compiler.h:78
int int32_t
Platform independent (4) bytes signed int.
Definition: PrimitiveTypes.h:46
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition: Result.h:48
@ Ascii
Encoding is ASCII.
@ Native
Encoding is UTF8.
An heap allocated byte buffer that can optionally use an inline buffer.
Definition: Buffer.h:28
File Descriptor (use SC::File to open and use it with strings and buffers).
Definition: FileDescriptor.h:52
An Intrusive Double Linked List.
Definition: IntrusiveDoubleLinkedList.h:24
Read / Write pipe (Process stdin/stdout and IPC communication)
Definition: FileDescriptor.h:153
Definition: Process.h:67
bool windowsHide
[Windows] Hides child process window (default == Process::isWindowsConsoleSubsystem)
Definition: Process.h:68
Definition: Process.h:155
Definition: Process.h:153
StdIn(StringView string)
Fills standard input with content of a StringView.
Definition: Process.h:164
StdIn(const char(&item)[N])
Fills standard input with content of a C-String.
Definition: Process.h:161
StdIn(Span< const char > span)
Fills standard input with content of a Span.
Definition: Process.h:167
StdIn(Inherit)
Inherits child process Input from parent process.
Definition: Process.h:158
Definition: Process.h:133
Definition: Process.h:134
Definition: Process.h:131
StdOut(Span< char > span)
Read the process standard output/error into the given Span.
Definition: Process.h:143
StdOut(Ignore)
Ignores child process standard output/error (child process output will be silenced)
Definition: Process.h:137
StdOut(Inherit)
Inherits child process standard output/error (child process will print into parent process console)
Definition: Process.h:140
Definition: Process.h:98
Definition: Process.h:73
StdStream(Buffer &externalBuffer)
Read the process standard output/error into the given Vector.
Definition: Process.h:79
StdStream(String &externalString)
Read the process standard output/error into the given String.
Definition: Process.h:76
StdStream(FileDescriptor &&file)
Redirects child process standard output/error to a given file descriptor.
Definition: Process.h:83
Execute multiple child processes chaining input / output between them.
Definition: Process.h:295
Result exec(const Process::StdOut &stdOut=Process::StdOut::Inherit{}, const Process::StdIn &stdIn=Process::StdIn::Inherit{}, const Process::StdErr &stdErr=Process::StdErr::Inherit{})
Launch the entire chain of processes and waits for the results (calling ProcessChain::waitForExitSync...
Definition: Process.h:316
Result launch(const Process::StdOut &stdOut=Process::StdOut::Inherit{}, const Process::StdIn &stdIn=Process::StdIn::Inherit{}, const Process::StdErr &stdErr=Process::StdErr::Inherit{})
Launch the entire chain of processes.
Result pipe(Process &process, const Span< const StringView > cmd)
Add a process to the chain, with given arguments.
Result waitForExitSync()
Waits (blocking) for entire process chain to exit.
Definition: ProcessDescriptor.h:44
Wraps an OS Process descriptor.
Definition: ProcessDescriptor.h:42
Reads current process environment variables.
Definition: Process.h:333
bool get(size_t index, StringView &name, StringView &value) const
Get the environment variable at given index, returning its name and value.
bool contains(StringView variableName, size_t *index=nullptr)
Checks if an environment variable exists in current process.
size_t size() const
Returns the total number of environment variables for current process.
Definition: Process.h:343
Forks current process exiting child at end of process A fork duplicates a parent process execution st...
Definition: Process.h:395
Result waitForChild()
Waits for child fork to finish execution.
State
Definition: Process.h:411
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition: Process.h:412
@ Immediate
Start the forked process immediately.
Definition: Process.h:413
Side getSide() const
Obtain process parent / fork side.
Definition: Process.h:408
FileDescriptor & getWritePipe()
Gets the descriptor to "write" something to the other side.
Side
Definition: Process.h:402
@ ForkParent
Parent side of the fork.
Definition: Process.h:403
@ ForkChild
Child side of the fork.
Definition: Process.h:404
Result resumeChildFork()
Sends 1 byte on parentToFork to resume State::Paused child fork.
FileDescriptor & getReadPipe()
Gets the descriptor to "read" something from the other side.
Result fork(State state)
Forks current process (use ForkProcess::getType to know the side)
int32_t getExitStatus() const
Gets the return code from the exited child fork.
Definition: Process.h:426
Execute a child process with standard file descriptors redirection.
Definition: Process.h:65
int32_t getExitStatus() const
gets the return code from the exited child process (valid only after exec or waitForExitSync)
Definition: Process.h:214
Result exec(Span< const StringView > cmd, const StdOut &stdOut=StdOut::Inherit{}, const StdIn &stdIn=StdIn::Inherit{}, const StdErr &stdErr=StdErr::Inherit{})
Executes a child process with the given arguments, waiting (blocking) until it's fully finished.
Definition: Process.h:204
Result setWorkingDirectory(StringView processWorkingDirectory)
Sets the starting working directory of the process that will be launched / executed.
Result setEnvironment(StringView environmentVariable, StringView value)
Sets the environment variable for the newly spawned child process.
Result launch(Span< const StringView > cmd, const StdOut &stdOut=StdOut::Inherit{}, const StdIn &stdIn=StdIn::Inherit{}, const StdErr &stdErr=StdErr::Inherit{})
Launch child process with the given arguments.
Definition: Process.h:188
Options options
Options for the child process (hide console window etc.)
Definition: Process.h:176
static bool isWindowsEmulatedProcess()
Returns true if we're emulating x64 on ARM64 or the inverse on Windows.
void inheritParentEnvironmentVariables(bool inherit)
Controls if the newly spawned child process will inherit parent process environment variables.
Definition: Process.h:220
ProcessID processID
ID of the process (can be the same as handle on some OS)
Definition: Process.h:175
static bool isWindowsConsoleSubsystem()
Returns true only under Windows if executable is compiled with /SUBSYSTEM:Console
static size_t getNumberOfProcessors()
Returns number of (virtual) processors available.
ProcessDescriptor handle
Handle to the OS process.
Definition: Process.h:174
Result waitForExitSync()
Waits (blocking) for process to exit after launch. It can only be called if Process::launch succeeded...
Native os handle to a process identifier.
Definition: Process.h:27
An ascii string used as boolean result. SC_TRY macro forwards errors to caller.
Definition: Result.h:12
static constexpr Result Error(const char(&msg)[numChars])
Constructs an Error from a pointer to an ASCII string literal.
Definition: Result.h:24
String with compile time configurable inline storage (small string optimization)
Definition: String.h:168
View over a contiguous sequence of items (pointer + size in elements).
Definition: Span.h:32
A non-modifiable owning string with associated encoding.
Definition: String.h:29
Non-owning view over a range of characters with UTF Encoding.
Definition: StringView.h:47
A contiguous sequence of heap allocated elements.
Definition: Vector.h:189