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
14struct SC_COMPILER_EXPORT ProcessDescriptor
15{
16 using Handle = detail::FileDescriptorDefinition::Handle;
17 static constexpr auto Invalid = detail::FileDescriptorDefinition::Invalid;
18};
19
22{
23 int32_t status = -1;
24};
25
28
31
34{
35 int32_t pid = 0;
36};
37
70
71struct SC_COMPILER_EXPORT Process
72{
73 struct SC_COMPILER_EXPORT Options
74 {
76 Options();
77 };
78
79 struct StdStream
80 {
82 StdStream(IGrowableBuffer& destination)
83 {
84 growableBuffer = &destination;
85 operation = Operation::GrowableBuffer;
86 }
87
89 StdStream(GrowableBuffer<FileDescriptor>& file)
90 {
91 operation = Operation::FileDescriptor;
92 (void)file.content.get(fileDescriptor, Result::Error("Invalid redirection file descriptor"));
93 file.content.detach();
94 }
95
96 StdStream(GrowableBuffer<PipeDescriptor>& pipe)
97 {
98 operation = Operation::ExternalPipe;
99 pipeDescriptor = &pipe.content;
100 }
101
102 StdStream(const StdStream&) = delete;
103 StdStream(StdStream&&) = delete;
104 StdStream& operator=(const StdStream&) = delete;
105 StdStream& operator=(StdStream&&) = delete;
106
107 protected:
109 {
110 };
111 StdStream() = default;
112 StdStream(AlreadySetup) { operation = Operation::AlreadySetup; }
113 friend struct Process;
114 friend struct ProcessChain;
115
116 enum class Operation
117 {
118 AlreadySetup,
119 Inherit,
120 Ignore,
121 ExternalPipe,
123 GrowableBuffer,
124 WritableSpan,
125 ReadableSpan
126 };
127 Operation operation = Operation::Inherit;
128
129 Span<const char> readableSpan;
130 Span<char>* writableSpan = nullptr;
131
132 IGrowableBuffer* growableBuffer = nullptr;
133
134 FileDescriptor::Handle fileDescriptor;
135
136 PipeDescriptor* pipeDescriptor;
137 };
138
139 struct StdOut : public StdStream
140 {
141 // clang-format off
142 struct Ignore{};
143 struct Inherit{};
144
145
147 StdOut(GrowableBuffer<StdOut::Ignore>) { operation = Operation::Ignore; }
148
150 StdOut(GrowableBuffer<StdOut::Inherit>) { operation = Operation::Inherit; }
151 StdOut(GrowableBuffer<StdOut>) { operation = Operation::Inherit; }
152 StdOut() { operation = Operation::Inherit; }
153
155 StdOut(GrowableBuffer<Span<char>>& span) { operation = Operation::WritableSpan; writableSpan = &span.content; }
156
157 using StdStream::StdStream;
158 friend struct ProcessChain;
159 // clang-format on
160 };
161
162 using StdErr = StdOut;
163
164 struct StdIn : public StdStream
165 {
166 // clang-format off
167 struct Inherit{};
168
170 StdIn(GrowableBuffer<Inherit>) { operation = Operation::Inherit; }
171 StdIn(GrowableBuffer<StdIn>) { operation = Operation::Inherit; }
172 StdIn() { operation = Operation::Inherit; }
173
175 template <int N> StdIn(GrowableBuffer<const char [N]>& item) { operation = Operation::ReadableSpan; readableSpan = {item.content, N - 1}; }
176
178 StdIn(GrowableBuffer<StringSpan> string) { operation = Operation::ReadableSpan; readableSpan = string.content.toCharSpan();}
179
181 StdIn(GrowableBuffer<Span<const char>> span) { operation = Operation::ReadableSpan; readableSpan = span.content;}
182
183 using StdStream::StdStream;
184 friend struct ProcessChain;
185 // clang-format on
186 };
187
190
191 ProcessDescriptor::Handle handle = ProcessDescriptor::Invalid;
192
195
203 template <typename Out = StdOut, typename In = StdIn, typename Err = StdErr>
204 Result launch(Span<const StringSpan> cmd, Out&& stdOut = Out(), In&& stdIn = In(), Err&& stdErr = Err())
205 {
206 SC_TRY(formatArguments(cmd));
207 GrowableBuffer<typename TypeTraits::RemoveReference<Out>::type> gbOut = {stdOut};
208 GrowableBuffer<typename TypeTraits::RemoveReference<In>::type> gbIn = {stdIn};
209 GrowableBuffer<typename TypeTraits::RemoveReference<Err>::type> gbErr = {stdErr};
210 return launch(StdOut(gbOut), StdIn(gbIn), StdErr(gbErr));
211 }
212
220 template <typename Out = StdOut, typename In = StdIn, typename Err = StdErr>
221 Result exec(Span<const StringSpan> cmd, Out&& stdOut = Out(), In&& stdIn = In(), Err&& stdErr = Err())
222 {
223 SC_TRY(launch(cmd, stdOut, stdIn, stdErr));
224 return waitForExitSync();
225 }
226
228 int32_t getExitStatus() const { return exitStatus.status; }
229
231 Result setWorkingDirectory(StringSpan processWorkingDirectory);
232
234 void inheritParentEnvironmentVariables(bool inherit) { inheritEnv = inherit; }
235
237 Result setEnvironment(StringSpan environmentVariable, StringSpan value);
238
240 [[nodiscard]] static size_t getNumberOfProcessors();
241
243 [[nodiscard]] static bool isWindowsConsoleSubsystem();
244
246 [[nodiscard]] static bool isWindowsEmulatedProcess();
247
251 Process(Span<native_char_t> commandMemory = {}, Span<native_char_t> environmentMemory = {})
252 : command({commandMemory}), environment({environmentMemory})
253 {
254 if (commandMemory.empty())
255 command = {commandStorage};
256 if (environmentMemory.empty())
257 environment = {environmentStorage};
258 }
259
260 private:
261 ProcessExitStatus exitStatus;
262
263 FileDescriptor stdInFd;
264 FileDescriptor stdOutFd;
265 FileDescriptor stdErrFd;
266
267 Result launch(const StdOut& stdOutput, const StdIn& stdInput, const StdErr& stdError);
268
269 Result formatArguments(Span<const StringSpan> cmd);
270
271 StringPath currentDirectory;
272
273 // On Windows command holds the concatenation of executable and arguments.
274 // On Posix command holds the concatenation of executable and arguments SEPARATED BY null-terminators (\0).
275 // This is done so that in this single buffer with no allocation (under 255) or a single allocation (above 255)
276 // we can track all arguments to be passed to execve.
277 native_char_t commandStorage[StringPath::MaxPath + 1024];
278 StringSpan::NativeWritable command;
279#if !SC_PLATFORM_WINDOWS // On Posix we need to track the "sub-strings" hidden in command
280 static constexpr size_t MAX_NUM_ARGUMENTS = 64;
281 size_t commandArgumentsByteOffset[MAX_NUM_ARGUMENTS]; // Tracking length of each argument in the command string
282 size_t commandArgumentsNumber = 0; // Counts number of arguments (including executable name)
283#endif
284
285 native_char_t environmentStorage[4096 * 4]; // 16K (w)chars of storage for environment variables
286 StringSpan::NativeWritable environment;
287
288 static constexpr size_t MAX_NUM_ENVIRONMENT = 256;
289
290 size_t environmentByteOffset[MAX_NUM_ENVIRONMENT]; // Tracking length of each environment variable
291 size_t environmentNumber = 0; // Counts number of environment variable
292
293 bool inheritEnv = true;
294
295 friend struct ProcessChain;
296 ProcessChain* parent = nullptr;
297
298 Process* next = nullptr;
299 Process* prev = nullptr;
300 struct Internal;
301 struct InternalFork;
302 friend struct ProcessFork;
303 Result launchImplementation();
304 Result launchForkChild(PipeDescriptor& pipe);
305 Result launchForkParent(PipeDescriptor& pipe, const void* previousSignals);
306};
307
324struct SC_COMPILER_EXPORT ProcessChain
325{
326 Process::Options options;
332
336 template <typename Out = Process::StdOut, typename In = Process::StdIn, typename Err = Process::StdErr>
337 Result launch(Out&& stdOut = Out(), In&& stdIn = In(), Err&& stdErr = Err())
338 {
339 GrowableBuffer<typename TypeTraits::RemoveReference<Out>::type> gbOut = {stdOut};
340 GrowableBuffer<typename TypeTraits::RemoveReference<In>::type> gbIn = {stdIn};
341 GrowableBuffer<typename TypeTraits::RemoveReference<Err>::type> gbErr = {stdErr};
342 return internalLaunch(gbOut, gbIn, gbErr);
343 }
344
348
351 template <typename Out = Process::StdOut, typename In = Process::StdIn, typename Err = Process::StdErr>
352 Result exec(Out&& stdOut = Out(), In&& stdIn = In(), Err&& stdErr = Err())
353 {
354 SC_TRY(launch(stdOut, stdIn, stdErr));
355 return waitForExitSync();
356 }
357
358 private:
359 Result internalLaunch(const Process::StdOut& stdOut, const Process::StdIn& stdIn, const Process::StdErr& stdErr);
360 // Trimmed duplicate of IntrusiveDoubleLinkedList<T>
361 struct ProcessLinkedList
362 {
363 Process* back = nullptr; // has no next
364 Process* front = nullptr; // has no prev
365
366 [[nodiscard]] bool isEmpty() const { return front == nullptr; }
367
368 void clear();
369 void queueBack(Process& process);
370 };
371 ProcessLinkedList processes;
372};
373
379{
382
383 ProcessEnvironment(const ProcessEnvironment&) = delete;
385 ProcessEnvironment& operator=(const ProcessEnvironment&) = delete;
386 ProcessEnvironment& operator=(ProcessEnvironment&&) = delete;
387
389 [[nodiscard]] size_t size() const { return numberOfEnvironment; }
390
395 [[nodiscard]] bool get(size_t index, StringSpan& name, StringSpan& value) const;
396
401 [[nodiscard]] bool contains(StringSpan variableName, size_t* index = nullptr);
402
403 private:
404 size_t numberOfEnvironment = 0;
405#if SC_PLATFORM_WINDOWS
406 static constexpr size_t MAX_ENVIRONMENTS = 256;
407
408 StringSpan envStrings[MAX_ENVIRONMENTS];
409 wchar_t* environment = nullptr;
410#else
411 char** environment = nullptr;
412#endif
413};
414
440struct SC_COMPILER_EXPORT ProcessFork
441{
442 ProcessFork();
443 ~ProcessFork();
444 ProcessFork(const ProcessFork&) = delete;
445 ProcessFork* operator=(const ProcessFork&) = delete;
446
447 enum Side
448 {
451 };
452
454 [[nodiscard]] Side getSide() const { return side; }
455
456 enum State
457 {
460 };
461
464
467
470
472 int32_t getExitStatus() const { return exitStatus.status; }
473
476
479
480 private:
481 Side side = ForkParent;
482#if SC_PLATFORM_WINDOWS
483 ProcessDescriptor::Handle processHandle = ProcessDescriptor::Invalid;
484 ProcessDescriptor::Handle threadHandle = ProcessDescriptor::Invalid;
485#else
486 ProcessID processID;
487#endif
488 ProcessExitStatus exitStatus;
489
490 PipeDescriptor parentToFork;
491 PipeDescriptor forkToParent;
492};
494
495} // 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
[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:325
Result launch(Out &&stdOut=Out(), In &&stdIn=In(), Err &&stdErr=Err())
Launch the entire chain of processes.
Definition Process.h:337
Result pipe(Process &process, const Span< const StringSpan > cmd)
Add a process to the chain, with given arguments.
Result exec(Out &&stdOut=Out(), In &&stdIn=In(), Err &&stdErr=Err())
Launch the entire chain of processes and waits for the results (calling ProcessChain::waitForExitSync...
Definition Process.h:352
Result waitForExitSync()
Waits (blocking) for entire process chain to exit.
Definition Process.h:15
Reads current process environment variables.
Definition Process.h:379
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:389
Wraps the code returned by a process that has exited.
Definition Process.h:22
Forks current process exiting child at end of process A fork duplicates a parent process execution st...
Definition Process.h:441
Result waitForChild()
Waits for child fork to finish execution.
State
Definition Process.h:457
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition Process.h:458
@ Immediate
Start the forked process immediately.
Definition Process.h:459
Side getSide() const
Obtain process parent / fork side.
Definition Process.h:454
FileDescriptor & getWritePipe()
Gets the descriptor to "write" something to the other side.
Side
Definition Process.h:448
@ ForkParent
Parent side of the fork.
Definition Process.h:449
@ ForkChild
Child side of the fork.
Definition Process.h:450
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:472
Native os handle to a process identifier.
Definition Process.h:34
Definition Process.h:74
bool windowsHide
[Windows] Hides child process window (default == Process::isWindowsConsoleSubsystem)
Definition Process.h:75
Definition Process.h:167
Definition Process.h:165
StdIn(GrowableBuffer< Span< const char > > span)
Fills standard input with content of a Span.
Definition Process.h:181
StdIn(GrowableBuffer< Inherit >)
Inherits child process Input from parent process.
Definition Process.h:170
StdIn(GrowableBuffer< const char[N]> &item)
Fills standard input with content of a C-String.
Definition Process.h:175
StdIn(GrowableBuffer< StringSpan > string)
Fills standard input with content of a StringSpan.
Definition Process.h:178
Definition Process.h:142
Definition Process.h:143
Definition Process.h:140
StdOut(GrowableBuffer< Span< char > > &span)
Read the process standard output/error into the given Span.
Definition Process.h:155
StdOut(GrowableBuffer< StdOut::Inherit >)
Inherits child process standard output/error (child process will print into parent process console)
Definition Process.h:150
StdOut(GrowableBuffer< StdOut::Ignore >)
Ignores child process standard output/error (child process output will be silenced)
Definition Process.h:147
Definition Process.h:109
Definition Process.h:80
StdStream(GrowableBuffer< FileDescriptor > &file)
Redirects child process standard output/error to a given file descriptor.
Definition Process.h:89
StdStream(IGrowableBuffer &destination)
Read the process standard output/error into the given String / Buffer.
Definition Process.h:82
Execute a child process with standard file descriptors redirection.
Definition Process.h:72
int32_t getExitStatus() const
gets the return code from the exited child process (valid only after exec or waitForExitSync)
Definition Process.h:228
Result exec(Span< const StringSpan > cmd, Out &&stdOut=Out(), In &&stdIn=In(), Err &&stdErr=Err())
Executes a child process with the given arguments, waiting (blocking) until it's fully finished.
Definition Process.h:221
Result setEnvironment(StringSpan environmentVariable, StringSpan value)
Sets the environment variable for the newly spawned child process.
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:251
Options options
Options for the child process (hide console window etc.)
Definition Process.h:189
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:234
ProcessID processID
ID of the process (can be the same as handle on Posix)
Definition Process.h:188
static bool isWindowsConsoleSubsystem()
Returns true only under Windows if executable is compiled with /SUBSYSTEM:Console
Result launch(Span< const StringSpan > cmd, Out &&stdOut=Out(), In &&stdIn=In(), Err &&stdErr=Err())
Launch child process with the given arguments.
Definition Process.h:204
static size_t getNumberOfProcessors()
Returns number of (virtual) processors available.
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