Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Process.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "../File/File.h"
6#include "../Foundation/AlignedStorage.h"
7#include "../Foundation/Internal/IGrowableBuffer.h"
8#include "../Foundation/StringPath.h"
9
10namespace SC
11{
12struct SC_COMPILER_EXPORT ProcessChain;
13
14using ProcessDescriptor = FileDescriptor;
15
18{
19 int32_t status = -1;
20};
21
24
27
30{
31 int32_t pid = 0;
32};
33
66
67struct SC_COMPILER_EXPORT Process
68{
69 struct SC_COMPILER_EXPORT Options
70 {
72 Options();
73 };
74
75 struct StdStream
76 {
78 template <typename T>
79 StdStream(T& destination)
80 {
81 GrowableBuffer<T>& gbuf = growableBufferStorage.reinterpret_as<GrowableBuffer<T>>();
82 placementNew(gbuf, destination);
83 growableBuffer = &gbuf;
84 operation = Operation::GrowableBuffer;
85 }
87 {
88 if (growableBuffer)
89 {
90 growableBuffer->~IGrowableBuffer();
91 }
92 }
93
96 {
97 operation = Operation::FileDescriptor;
98 (void)file.get(fileDescriptor, Result::Error("Invalid redirection file descriptor"));
99 file.detach();
100 }
101
103 {
104 operation = Operation::ExternalPipe;
105 pipeDescriptor = &pipe;
106 }
107
108 StdStream(const StdStream&) = delete;
109 StdStream(StdStream&&) = delete;
110 StdStream& operator=(const StdStream&) = delete;
111 StdStream& operator=(StdStream&&) = delete;
112
113 protected:
115 {
116 };
117 StdStream() = default;
118 StdStream(AlreadySetup) { operation = Operation::AlreadySetup; }
119 friend struct Process;
120 friend struct ProcessChain;
121
122 enum class Operation
123 {
124 AlreadySetup,
125 Inherit,
126 Ignore,
127 ExternalPipe,
129 GrowableBuffer,
130 WritableSpan,
131 ReadableSpan
132 };
133 Operation operation = Operation::Inherit;
134
135 Span<const char> readableSpan;
136 Span<char> writableSpan;
137
138 IGrowableBuffer* growableBuffer = nullptr;
139 AlignedStorage<6 * sizeof(void*)> growableBufferStorage;
140
141 FileDescriptor::Handle fileDescriptor;
142
143 PipeDescriptor* pipeDescriptor;
144 };
145
146 struct StdOut : public StdStream
147 {
148 // clang-format off
149 struct Ignore{};
150 struct Inherit{};
151
153 StdOut(Ignore) { operation = Operation::Ignore; }
154
156 StdOut(Inherit) { operation = Operation::Inherit; }
157
159 StdOut(Span<char> span) { operation = Operation::WritableSpan; writableSpan = span; }
160
161 using StdStream::StdStream;
162 friend struct ProcessChain;
163 // clang-format on
164 };
165
166 using StdErr = StdOut;
167
168 struct StdIn : public StdStream
169 {
170 // clang-format off
171 struct Inherit{};
172
174 StdIn(Inherit) { operation = Operation::Inherit; }
175
177 template <int N> StdIn(const char (&item)[N]) : StdIn(StringSpan({item, N - 1}, true, StringEncoding::Ascii)) {}
178
180 StdIn(StringSpan string) : StdIn(string.toCharSpan()) {}
181
183 StdIn(Span<const char> span) { operation = Operation::ReadableSpan; readableSpan = span;}
184
185 using StdStream::StdStream;
186 friend struct ProcessChain;
187 // clang-format on
188 };
189
193
196
205 const StdOut& stdOut = StdOut::Inherit{}, //
206 const StdIn& stdIn = StdIn::Inherit{}, //
207 const StdErr& stdErr = StdErr::Inherit{})
208 {
209 SC_TRY(formatArguments(cmd));
210 return launch(stdOut, stdIn, stdErr);
211 }
212
221 const StdOut& stdOut = StdOut::Inherit{}, //
222 const StdIn& stdIn = StdIn::Inherit{}, //
223 const StdErr& stdErr = StdErr::Inherit{})
224 {
225 SC_TRY(launch(cmd, stdOut, stdIn, stdErr));
226 return waitForExitSync();
227 }
228
230 int32_t getExitStatus() const { return exitStatus.status; }
231
233 Result setWorkingDirectory(StringSpan processWorkingDirectory);
234
236 void inheritParentEnvironmentVariables(bool inherit) { inheritEnv = inherit; }
237
239 Result setEnvironment(StringSpan environmentVariable, StringSpan value);
240
242 [[nodiscard]] static size_t getNumberOfProcessors();
243
245 [[nodiscard]] static bool isWindowsConsoleSubsystem();
246
248 [[nodiscard]] static bool isWindowsEmulatedProcess();
249
253 Process(Span<native_char_t> commandMemory = {}, Span<native_char_t> environmentMemory = {})
254 : command({commandMemory}), environment({environmentMemory})
255 {
256 if (commandMemory.empty())
257 command = {commandStorage};
258 if (environmentMemory.empty())
259 environment = {environmentStorage};
260 }
261
262 private:
263 ProcessExitStatus exitStatus;
264
265 FileDescriptor stdInFd;
266 FileDescriptor stdOutFd;
267 FileDescriptor stdErrFd;
268
269 Result launch(const StdOut& stdOutput, const StdIn& stdInput, const StdErr& stdError);
270
271 Result formatArguments(Span<const StringSpan> cmd);
272
273 StringPath currentDirectory;
274
275 // On Windows command holds the concatenation of executable and arguments.
276 // On Posix command holds the concatenation of executable and arguments SEPARATED BY null-terminators (\0).
277 // This is done so that in this single buffer with no allocation (under 255) or a single allocation (above 255)
278 // we can track all arguments to be passed to execve.
279 native_char_t commandStorage[StringPath::MaxPath + 1024];
280 StringSpan::NativeWritable command;
281#if !SC_PLATFORM_WINDOWS // On Posix we need to track the "sub-strings" hidden in command
282 static constexpr size_t MAX_NUM_ARGUMENTS = 64;
283 size_t commandArgumentsByteOffset[MAX_NUM_ARGUMENTS]; // Tracking length of each argument in the command string
284 size_t commandArgumentsNumber = 0; // Counts number of arguments (including executable name)
285#endif
286
287 native_char_t environmentStorage[4096 * 4]; // 16K (w)chars of storage for environment variables
288 StringSpan::NativeWritable environment;
289
290 static constexpr size_t MAX_NUM_ENVIRONMENT = 256;
291
292 size_t environmentByteOffset[MAX_NUM_ENVIRONMENT]; // Tracking length of each environment variable
293 size_t environmentNumber = 0; // Counts number of environment variable
294
295 bool inheritEnv = true;
296
297 friend struct ProcessChain;
298 ProcessChain* parent = nullptr;
299
300 Process* next = nullptr;
301 Process* prev = nullptr;
302 struct Internal;
303 friend struct ProcessFork;
304 Result launchImplementation();
305};
306
323struct SC_COMPILER_EXPORT ProcessChain
324{
325 Process::Options options;
331
336 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
337 const Process::StdErr& stdErr = Process::StdErr::Inherit{});
338
342
346 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
347 const Process::StdErr& stdErr = Process::StdErr::Inherit{})
348 {
349 SC_TRY(launch(stdOut, stdIn, stdErr));
350 return waitForExitSync();
351 }
352
353 private:
354 // Trimmed duplicate of IntrusiveDoubleLinkedList<T>
355 struct ProcessLinkedList
356 {
357 Process* back = nullptr; // has no next
358 Process* front = nullptr; // has no prev
359
360 [[nodiscard]] bool isEmpty() const { return front == nullptr; }
361
362 void clear();
363 void queueBack(Process& process);
364 };
365 ProcessLinkedList processes;
366};
367
373{
376
377 ProcessEnvironment(const ProcessEnvironment&) = delete;
379 ProcessEnvironment& operator=(const ProcessEnvironment&) = delete;
380 ProcessEnvironment& operator=(ProcessEnvironment&&) = delete;
381
383 [[nodiscard]] size_t size() const { return numberOfEnvironment; }
384
389 [[nodiscard]] bool get(size_t index, StringSpan& name, StringSpan& value) const;
390
395 [[nodiscard]] bool contains(StringSpan variableName, size_t* index = nullptr);
396
397 private:
398 size_t numberOfEnvironment = 0;
399#if SC_PLATFORM_WINDOWS
400 static constexpr size_t MAX_ENVIRONMENTS = 256;
401
402 StringSpan envStrings[MAX_ENVIRONMENTS];
403 wchar_t* environment = nullptr;
404#else
405 char** environment = nullptr;
406#endif
407};
408
434struct SC_COMPILER_EXPORT ProcessFork
435{
436 ProcessFork();
437 ~ProcessFork();
438 ProcessFork(const ProcessFork&) = delete;
439 ProcessFork* operator=(const ProcessFork&) = delete;
440
441 enum Side
442 {
445 };
446
448 [[nodiscard]] Side getSide() const { return side; }
449
450 enum State
451 {
454 };
455
458
461
464
466 int32_t getExitStatus() const { return exitStatus.status; }
467
470
473
474 private:
475 Side side = ForkParent;
476#if SC_PLATFORM_WINDOWS
477 ProcessDescriptor::Handle processHandle = ProcessDescriptor::Invalid;
478 ProcessDescriptor::Handle threadHandle = ProcessDescriptor::Invalid;
479#else
480 ProcessID processID;
481#endif
482 ProcessExitStatus exitStatus;
483
484 PipeDescriptor parentToFork;
485 PipeDescriptor forkToParent;
486};
488
489} // namespace SC
int int32_t
Platform independent (4) bytes signed int.
Definition PrimitiveTypes.h:46
char native_char_t
The native char for the platform (wchar_t (4 bytes) on Windows, char (1 byte) everywhere else )
Definition PrimitiveTypes.h:34
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition Result.h:48
A buffer of bytes with given alignment.
Definition AlignedStorage.h:29
[UniqueHandleDeclaration2Snippet]
Definition File.h:78
Read / Write pipe (Process stdin/stdout and IPC communication)
Definition File.h:215
Execute multiple child processes chaining input / output between them.
Definition Process.h:324
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:345
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 StringSpan > cmd)
Add a process to the chain, with given arguments.
Result waitForExitSync()
Waits (blocking) for entire process chain to exit.
Reads current process environment variables.
Definition Process.h:373
bool contains(StringSpan variableName, size_t *index=nullptr)
Checks if an environment variable exists in current process.
bool get(size_t index, StringSpan &name, StringSpan &value) const
Get the environment variable at given index, returning its name and value.
size_t size() const
Returns the total number of environment variables for current process.
Definition Process.h:383
Wraps the code returned by a process that has exited.
Definition Process.h:18
Forks current process exiting child at end of process A fork duplicates a parent process execution st...
Definition Process.h:435
Result waitForChild()
Waits for child fork to finish execution.
State
Definition Process.h:451
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition Process.h:452
@ Immediate
Start the forked process immediately.
Definition Process.h:453
Side getSide() const
Obtain process parent / fork side.
Definition Process.h:448
FileDescriptor & getWritePipe()
Gets the descriptor to "write" something to the other side.
Side
Definition Process.h:442
@ ForkParent
Parent side of the fork.
Definition Process.h:443
@ ForkChild
Child side of the fork.
Definition Process.h:444
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:466
Native os handle to a process identifier.
Definition Process.h:30
Definition Process.h:70
bool windowsHide
[Windows] Hides child process window (default == Process::isWindowsConsoleSubsystem)
Definition Process.h:71
Definition Process.h:171
Definition Process.h:169
StdIn(const char(&item)[N])
Fills standard input with content of a C-String.
Definition Process.h:177
StdIn(StringSpan string)
Fills standard input with content of a StringSpan.
Definition Process.h:180
StdIn(Span< const char > span)
Fills standard input with content of a Span.
Definition Process.h:183
StdIn(Inherit)
Inherits child process Input from parent process.
Definition Process.h:174
Definition Process.h:149
Definition Process.h:150
Definition Process.h:147
StdOut(Span< char > span)
Read the process standard output/error into the given Span.
Definition Process.h:159
StdOut(Ignore)
Ignores child process standard output/error (child process output will be silenced)
Definition Process.h:153
StdOut(Inherit)
Inherits child process standard output/error (child process will print into parent process console)
Definition Process.h:156
Definition Process.h:115
Definition Process.h:76
StdStream(T &destination)
Read the process standard output/error into the given String / Buffer.
Definition Process.h:79
StdStream(FileDescriptor &&file)
Redirects child process standard output/error to a given file descriptor.
Definition Process.h:95
Execute a child process with standard file descriptors redirection.
Definition Process.h:68
int32_t getExitStatus() const
gets the return code from the exited child process (valid only after exec or waitForExitSync)
Definition Process.h:230
Result setEnvironment(StringSpan environmentVariable, StringSpan value)
Sets the environment variable for the newly spawned child process.
Result exec(Span< const StringSpan > 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:220
Result launch(Span< const StringSpan > 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:204
Process(Span< native_char_t > commandMemory={}, Span< native_char_t > environmentMemory={})
Constructs a Process object passing (optional) memory storage for command and environment variables.
Definition Process.h:253
Options options
Options for the child process (hide console window etc.)
Definition Process.h:192
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:236
ProcessID processID
ID of the process (can be the same as handle on some OS)
Definition Process.h:191
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:190
Result setWorkingDirectory(StringSpan processWorkingDirectory)
Sets the starting working directory of the process that will be launched / executed.
Result waitForExitSync()
Waits (blocking) for process to exit after launch. It can only be called if Process::launch succeeded...
An ascii string used as boolean result. SC_TRY macro forwards errors to caller.
Definition Result.h:12
View over a contiguous sequence of items (pointer + size in elements).
Definition Span.h:29
An read-only view over a string (to avoid including Strings library when parsing is not needed).
Definition StringSpan.h:37