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/Function.h"
8#include "../Foundation/Internal/IGrowableBuffer.h"
9#include "../Foundation/StringPath.h"
10
11namespace SC
12{
13struct SC_COMPILER_EXPORT ProcessChain;
14
15using ProcessDescriptor = FileDescriptor;
16
19{
20 int32_t status = -1;
21};
22
25
28
31{
32 int32_t pid = 0;
33};
34
67
68struct SC_COMPILER_EXPORT Process
69{
70 struct SC_COMPILER_EXPORT Options
71 {
73 Options();
74 };
75
76 struct StdStream
77 {
79 template <typename T>
80 StdStream(T& destination)
81 {
82 GrowableBuffer<T>& gbuf = growableBufferStorage.reinterpret_as<GrowableBuffer<T>>();
83 placementNew(gbuf, destination);
84 growableBuffer = &gbuf;
85 operation = Operation::GrowableBuffer;
86 }
88 {
89 if (growableBuffer)
90 {
91 growableBuffer->~IGrowableBuffer();
92 }
93 }
94
97 {
98 operation = Operation::FileDescriptor;
99 (void)file.get(fileDescriptor, Result::Error("Invalid redirection file descriptor"));
100 file.detach();
101 }
102
104 {
105 operation = Operation::ExternalPipe;
106 pipeDescriptor = &pipe;
107 }
108
109 StdStream(const StdStream&) = delete;
110 StdStream(StdStream&&) = delete;
111 StdStream& operator=(const StdStream&) = delete;
112 StdStream& operator=(StdStream&&) = delete;
113
114 protected:
116 {
117 };
118 StdStream() = default;
119 StdStream(AlreadySetup) { operation = Operation::AlreadySetup; }
120 friend struct Process;
121 friend struct ProcessChain;
122
123 enum class Operation
124 {
125 AlreadySetup,
126 Inherit,
127 Ignore,
128 ExternalPipe,
130 GrowableBuffer,
131 WritableSpan,
132 ReadableSpan
133 };
134 Operation operation = Operation::Inherit;
135
136 Span<const char> readableSpan;
137 Span<char> writableSpan;
138
139 IGrowableBuffer* growableBuffer = nullptr;
140 AlignedStorage<3 * sizeof(void*)> growableBufferStorage;
141
142 FileDescriptor::Handle fileDescriptor;
143
144 PipeDescriptor* pipeDescriptor;
145 };
146
147 struct StdOut : public StdStream
148 {
149 // clang-format off
150 struct Ignore{};
151 struct Inherit{};
152
154 StdOut(Ignore) { operation = Operation::Ignore; }
155
157 StdOut(Inherit) { operation = Operation::Inherit; }
158
160 StdOut(Span<char> span) { operation = Operation::WritableSpan; writableSpan = span; }
161
162 using StdStream::StdStream;
163 friend struct ProcessChain;
164 // clang-format on
165 };
166
167 using StdErr = StdOut;
168
169 struct StdIn : public StdStream
170 {
171 // clang-format off
172 struct Inherit{};
173
175 StdIn(Inherit) { operation = Operation::Inherit; }
176
178 template <int N> StdIn(const char (&item)[N]) : StdIn(StringSpan({item, N - 1}, true, StringEncoding::Ascii)) {}
179
181 StdIn(StringSpan string) : StdIn(string.toCharSpan()) {}
182
184 StdIn(Span<const char> span) { operation = Operation::ReadableSpan; readableSpan = span;}
185
186 using StdStream::StdStream;
187 friend struct ProcessChain;
188 // clang-format on
189 };
190
194
197
206 const StdOut& stdOut = StdOut::Inherit{}, //
207 const StdIn& stdIn = StdIn::Inherit{}, //
208 const StdErr& stdErr = StdErr::Inherit{})
209 {
210 SC_TRY(formatArguments(cmd));
211 return launch(stdOut, stdIn, stdErr);
212 }
213
222 const StdOut& stdOut = StdOut::Inherit{}, //
223 const StdIn& stdIn = StdIn::Inherit{}, //
224 const StdErr& stdErr = StdErr::Inherit{})
225 {
226 SC_TRY(launch(cmd, stdOut, stdIn, stdErr));
227 return waitForExitSync();
228 }
229
231 int32_t getExitStatus() const { return exitStatus.status; }
232
234 Result setWorkingDirectory(StringSpan processWorkingDirectory);
235
237 void inheritParentEnvironmentVariables(bool inherit) { inheritEnv = inherit; }
238
240 Result setEnvironment(StringSpan environmentVariable, StringSpan value);
241
243 [[nodiscard]] static size_t getNumberOfProcessors();
244
246 [[nodiscard]] static bool isWindowsConsoleSubsystem();
247
249 [[nodiscard]] static bool isWindowsEmulatedProcess();
250
254 Process(Span<native_char_t> commandMemory = {}, Span<native_char_t> environmentMemory = {})
255 : command({commandMemory}), environment({environmentMemory})
256 {
257 if (commandMemory.empty())
258 command = {commandStorage};
259 if (commandMemory.empty())
260 environment = {environmentStorage};
261 }
262
263 private:
264 ProcessExitStatus exitStatus;
265
266 FileDescriptor stdInFd;
267 FileDescriptor stdOutFd;
268 FileDescriptor stdErrFd;
269
270 Result launch(const StdOut& stdOutput, const StdIn& stdInput, const StdErr& stdError);
271
272 Result formatArguments(Span<const StringSpan> cmd);
273
274 StringPath currentDirectory;
275
276 // On Windows command holds the concatenation of executable and arguments.
277 // On Posix command holds the concatenation of executable and arguments SEPARATED BY null-terminators (\0).
278 // This is done so that in this single buffer with no allocation (under 255) or a single allocation (above 255)
279 // we can track all arguments to be passed to execve.
280 native_char_t commandStorage[StringPath::MaxPath + 1024];
281 StringSpan::NativeWritable command;
282#if !SC_PLATFORM_WINDOWS // On Posix we need to track the "sub-strings" hidden in command
283 static constexpr size_t MAX_NUM_ARGUMENTS = 64;
284 size_t commandArgumentsByteOffset[MAX_NUM_ARGUMENTS]; // Tracking length of each argument in the command string
285 size_t commandArgumentsNumber = 0; // Counts number of arguments (including executable name)
286#endif
287
288 native_char_t environmentStorage[4096 * 4]; // 16K (w)chars of storage for environment variables
289 StringSpan::NativeWritable environment;
290
291 static constexpr size_t MAX_NUM_ENVIRONMENT = 256;
292
293 size_t environmentByteOffset[MAX_NUM_ENVIRONMENT]; // Tracking length of each environment variable
294 size_t environmentNumber = 0; // Counts number of environment variable
295
296 bool inheritEnv = true;
297
298 friend struct ProcessChain;
299 ProcessChain* parent = nullptr;
300
301 Process* next = nullptr;
302 Process* prev = nullptr;
303 struct Internal;
304 friend struct ProcessFork;
305 Result launchImplementation();
306};
307
324struct SC_COMPILER_EXPORT ProcessChain
325{
326 Process::Options options;
332
337 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
338 const Process::StdErr& stdErr = Process::StdErr::Inherit{});
339
343
347 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
348 const Process::StdErr& stdErr = Process::StdErr::Inherit{})
349 {
350 SC_TRY(launch(stdOut, stdIn, stdErr));
351 return waitForExitSync();
352 }
353
354 private:
355 // Trimmed duplicate of IntrusiveDoubleLinkedList<T>
356 struct ProcessLinkedList
357 {
358 Process* back = nullptr; // has no next
359 Process* front = nullptr; // has no prev
360
361 [[nodiscard]] bool isEmpty() const { return front == nullptr; }
362
363 void clear();
364 void queueBack(Process& process);
365 };
366 ProcessLinkedList processes;
367};
368
374{
377
378 ProcessEnvironment(const ProcessEnvironment&) = delete;
380 ProcessEnvironment& operator=(const ProcessEnvironment&) = delete;
381 ProcessEnvironment& operator=(ProcessEnvironment&&) = delete;
382
384 [[nodiscard]] size_t size() const { return numberOfEnvironment; }
385
390 [[nodiscard]] bool get(size_t index, StringSpan& name, StringSpan& value) const;
391
396 [[nodiscard]] bool contains(StringSpan variableName, size_t* index = nullptr);
397
398 private:
399 size_t numberOfEnvironment = 0;
400#if SC_PLATFORM_WINDOWS
401 static constexpr size_t MAX_ENVIRONMENTS = 256;
402
403 StringSpan envStrings[MAX_ENVIRONMENTS];
404 wchar_t* environment = nullptr;
405#else
406 char** environment = nullptr;
407#endif
408};
409
435struct SC_COMPILER_EXPORT ProcessFork
436{
437 ProcessFork();
438 ~ProcessFork();
439 ProcessFork(const ProcessFork&) = delete;
440 ProcessFork* operator=(const ProcessFork&) = delete;
441
442 enum Side
443 {
446 };
447
449 [[nodiscard]] Side getSide() const { return side; }
450
451 enum State
452 {
455 };
456
459
462
465
467 int32_t getExitStatus() const { return exitStatus.status; }
468
471
474
475 private:
476 Side side = ForkParent;
477#if SC_PLATFORM_WINDOWS
478 ProcessDescriptor::Handle processHandle = ProcessDescriptor::Invalid;
479 ProcessDescriptor::Handle threadHandle = ProcessDescriptor::Invalid;
480#else
481 ProcessID processID;
482#endif
483 ProcessExitStatus exitStatus;
484
485 PipeDescriptor parentToFork;
486 PipeDescriptor forkToParent;
487};
489
490} // 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:205
Execute multiple child processes chaining input / output between them.
Definition Process.h:325
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:346
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:374
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:384
Wraps the code returned by a process that has exited.
Definition Process.h:19
Forks current process exiting child at end of process A fork duplicates a parent process execution st...
Definition Process.h:436
Result waitForChild()
Waits for child fork to finish execution.
State
Definition Process.h:452
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition Process.h:453
@ Immediate
Start the forked process immediately.
Definition Process.h:454
Side getSide() const
Obtain process parent / fork side.
Definition Process.h:449
FileDescriptor & getWritePipe()
Gets the descriptor to "write" something to the other side.
Side
Definition Process.h:443
@ ForkParent
Parent side of the fork.
Definition Process.h:444
@ ForkChild
Child side of the fork.
Definition Process.h:445
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:467
Native os handle to a process identifier.
Definition Process.h:31
Definition Process.h:71
bool windowsHide
[Windows] Hides child process window (default == Process::isWindowsConsoleSubsystem)
Definition Process.h:72
Definition Process.h:172
Definition Process.h:170
StdIn(const char(&item)[N])
Fills standard input with content of a C-String.
Definition Process.h:178
StdIn(StringSpan string)
Fills standard input with content of a StringSpan.
Definition Process.h:181
StdIn(Span< const char > span)
Fills standard input with content of a Span.
Definition Process.h:184
StdIn(Inherit)
Inherits child process Input from parent process.
Definition Process.h:175
Definition Process.h:150
Definition Process.h:151
Definition Process.h:148
StdOut(Span< char > span)
Read the process standard output/error into the given Span.
Definition Process.h:160
StdOut(Ignore)
Ignores child process standard output/error (child process output will be silenced)
Definition Process.h:154
StdOut(Inherit)
Inherits child process standard output/error (child process will print into parent process console)
Definition Process.h:157
Definition Process.h:116
Definition Process.h:77
StdStream(T &destination)
Read the process standard output/error into the given String / Buffer.
Definition Process.h:80
StdStream(FileDescriptor &&file)
Redirects child process standard output/error to a given file descriptor.
Definition Process.h:96
Execute a child process with standard file descriptors redirection.
Definition Process.h:69
int32_t getExitStatus() const
gets the return code from the exited child process (valid only after exec or waitForExitSync)
Definition Process.h:231
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:221
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:205
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:254
Options options
Options for the child process (hide console window etc.)
Definition Process.h:193
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:237
ProcessID processID
ID of the process (can be the same as handle on some OS)
Definition Process.h:192
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:191
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