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/Internal/IntrusiveDoubleLinkedList.h"
7#include "../Strings/String.h"
8
9namespace SC
10{
11struct SC_COMPILER_EXPORT ProcessChain;
12
13using ProcessDescriptor = FileDescriptor;
14
17{
18 int32_t status = -1;
19};
20
23
26
29{
30 int32_t pid = 0;
31};
32
65
67{
69 {
71 Options();
72 };
73
74 struct StdStream
75 {
76 // clang-format off
78 StdStream(String& externalString) { operation = Operation::String; string = &externalString;}
79
81 StdStream(Buffer& externalBuffer) { operation = Operation::Vector; buffer = &externalBuffer; }
82 // clang-format on
83
86 {
87 operation = Operation::FileDescriptor;
88 (void)file.get(fileDescriptor, Result::Error("Invalid redirection file descriptor"));
89 file.detach();
90 }
91
93 {
94 operation = Operation::ExternalPipe;
95 pipeDescriptor = &pipe;
96 }
97
98 protected:
100 {
101 };
102 StdStream() = default;
103 StdStream(AlreadySetup) { operation = Operation::AlreadySetup; }
104 friend struct Process;
105 friend struct ProcessChain;
106
107 enum class Operation
108 {
109 AlreadySetup,
110 Inherit,
111 Ignore,
112 ExternalPipe,
114 Vector,
115 String,
116 WritableSpan,
117 ReadableSpan
118 };
119 Operation operation = Operation::Inherit;
120
121 Span<const char> readableSpan;
122 Span<char> writableSpan;
123
124 String* string;
125 Buffer* buffer;
126
127 FileDescriptor::Handle fileDescriptor;
128
129 PipeDescriptor* pipeDescriptor;
130 };
131
132 struct StdOut : public StdStream
133 {
134 // clang-format off
135 struct Ignore{};
136 struct Inherit{};
137
139 StdOut(Ignore) { operation = Operation::Ignore; }
140
142 StdOut(Inherit) { operation = Operation::Inherit; }
143
145 StdOut(Span<char> span) { operation = Operation::WritableSpan; writableSpan = span; }
146
147 using StdStream::StdStream;
148 friend struct ProcessChain;
149 // clang-format on
150 };
151
152 using StdErr = StdOut;
153
154 struct StdIn : public StdStream
155 {
156 // clang-format off
157 struct Inherit{};
158
160 StdIn(Inherit) { operation = Operation::Inherit; }
161
163 template <int N> StdIn(const char (&item)[N]) : StdIn(StringView({item, N - 1}, true, StringEncoding::Ascii)) {}
164
166 StdIn(StringView string) : StdIn(string.toCharSpan()) {}
167
169 StdIn(Span<const char> span) { operation = Operation::ReadableSpan; readableSpan = span;}
170
171 using StdStream::StdStream;
172 friend struct ProcessChain;
173 // clang-format on
174 };
175
179
181 [[nodiscard]] Result waitForExitSync();
182
191 const StdOut& stdOut = StdOut::Inherit{}, //
192 const StdIn& stdIn = StdIn::Inherit{}, //
193 const StdErr& stdErr = StdErr::Inherit{})
194 {
195 SC_TRY(formatArguments(cmd));
196 return launch(stdOut, stdIn, stdErr);
197 }
198
206 [[nodiscard]] Result exec(Span<const StringView> cmd, //
207 const StdOut& stdOut = StdOut::Inherit{}, //
208 const StdIn& stdIn = StdIn::Inherit{}, //
209 const StdErr& stdErr = StdErr::Inherit{})
210 {
211 SC_TRY(launch(cmd, stdOut, stdIn, stdErr));
212 return waitForExitSync();
213 }
214
216 int32_t getExitStatus() const { return exitStatus.status; }
217
219 [[nodiscard]] Result setWorkingDirectory(StringView processWorkingDirectory);
220
222 void inheritParentEnvironmentVariables(bool inherit) { inheritEnv = inherit; }
223
225 [[nodiscard]] Result setEnvironment(StringView environmentVariable, StringView value);
226
228 [[nodiscard]] static size_t getNumberOfProcessors();
229
231 [[nodiscard]] static bool isWindowsConsoleSubsystem();
232
234 [[nodiscard]] static bool isWindowsEmulatedProcess();
235
236 private:
237 ProcessExitStatus exitStatus;
238
239 FileDescriptor stdInFd;
240 FileDescriptor stdOutFd;
241 FileDescriptor stdErrFd;
242
243 [[nodiscard]] Result launch(const StdOut& stdOutput, const StdIn& stdInput, const StdErr& stdError);
244
245 [[nodiscard]] Result formatArguments(Span<const StringView> cmd);
246
247 StringNative<255> currentDirectory = StringEncoding::Native;
248
249 // On Windows command holds the concatenation of executable and arguments.
250 // On Posix command holds the concatenation of executable and arguments SEPARATED BY null-terminators (\0).
251 // This is done so that in this single buffer with no allocation (under 255) or a single allocation (above 255)
252 // we can track all arguments to be passed to execve.
253 StringNative<255> command = StringEncoding::Native;
254#if !SC_PLATFORM_WINDOWS // On Posix we need to track the "sub-strings" hidden in command
255 static constexpr size_t MAX_NUM_ARGUMENTS = 64;
256 size_t commandArgumentsByteOffset[MAX_NUM_ARGUMENTS]; // Tracking length of each argument in the command string
257 size_t commandArgumentsNumber = 0; // Counts number of arguments (including executable name)
258#endif
259
260 StringNative<1024> environment = StringEncoding::Native;
261
262 static constexpr size_t MAX_NUM_ENVIRONMENT = 256;
263
264 size_t environmentByteOffset[MAX_NUM_ENVIRONMENT]; // Tracking length of each environment variable
265 size_t environmentNumber = 0; // Counts number of environment variable
266
267 bool inheritEnv = true;
268
269 friend struct IntrusiveDoubleLinkedList<Process>;
270 friend struct ProcessChain;
271 ProcessChain* parent = nullptr;
272
273 Process* next = nullptr;
274 Process* prev = nullptr;
275 struct Internal;
276 friend struct ProcessFork;
277 Result launchImplementation();
278};
279
297{
298 Process::Options options;
303 [[nodiscard]] Result pipe(Process& process, const Span<const StringView> cmd);
304
308 [[nodiscard]] Result launch(const Process::StdOut& stdOut = Process::StdOut::Inherit{}, //
309 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
310 const Process::StdErr& stdErr = Process::StdErr::Inherit{});
311
314 [[nodiscard]] Result waitForExitSync();
315
318 [[nodiscard]] Result exec(const Process::StdOut& stdOut = Process::StdOut::Inherit{}, //
319 const Process::StdIn& stdIn = Process::StdIn::Inherit{}, //
320 const Process::StdErr& stdErr = Process::StdErr::Inherit{})
321 {
322 SC_TRY(launch(stdOut, stdIn, stdErr));
323 return waitForExitSync();
324 }
325
326 private:
327 IntrusiveDoubleLinkedList<Process> processes;
328};
329
335{
338
339 ProcessEnvironment(const ProcessEnvironment&) = delete;
341 ProcessEnvironment& operator=(const ProcessEnvironment&) = delete;
342 ProcessEnvironment& operator=(ProcessEnvironment&&) = delete;
343
345 [[nodiscard]] size_t size() const { return numberOfEnvironment; }
346
351 [[nodiscard]] bool get(size_t index, StringView& name, StringView& value) const;
352
357 [[nodiscard]] bool contains(StringView variableName, size_t* index = nullptr);
358
359 private:
360 size_t numberOfEnvironment = 0;
361#if SC_PLATFORM_WINDOWS
362 static constexpr size_t MAX_ENVIRONMENTS = 256;
363
364 StringView envStrings[MAX_ENVIRONMENTS];
365 wchar_t* environment = nullptr;
366#else
367 char** environment = nullptr;
368#endif
369};
370
397{
398 ProcessFork();
399 ~ProcessFork();
400 ProcessFork(const ProcessFork&) = delete;
401 ProcessFork* operator=(const ProcessFork&) = delete;
402
403 enum Side
404 {
407 };
408
410 [[nodiscard]] Side getSide() const { return side; }
411
412 enum State
413 {
416 };
417
420
423
426
428 int32_t getExitStatus() const { return exitStatus.status; }
429
432
435
436 private:
437 Side side = ForkParent;
438#if SC_PLATFORM_WINDOWS
439 ProcessDescriptor::Handle processHandle = ProcessDescriptor::Invalid;
440 ProcessDescriptor::Handle threadHandle = ProcessDescriptor::Invalid;
441#else
442 ProcessID processID;
443#endif
444 ProcessExitStatus exitStatus;
445
446 PipeDescriptor parentToFork;
447 PipeDescriptor forkToParent;
448};
450
451} // namespace SC
#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
An heap allocated byte buffer that can optionally use an inline buffer.
Definition Buffer.h:29
Open, read and write to/from a file descriptor (like a file or pipe).
Definition File.h:76
Read / Write pipe (Process stdin/stdout and IPC communication)
Definition File.h:195
Execute multiple child processes chaining input / output between them.
Definition Process.h:297
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:318
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.
Reads current process environment variables.
Definition Process.h:335
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:345
Wraps the code returned by a process that has exited.
Definition Process.h:17
Forks current process exiting child at end of process A fork duplicates a parent process execution st...
Definition Process.h:397
Result waitForChild()
Waits for child fork to finish execution.
State
Definition Process.h:413
@ Suspended
Start the forked process suspended (resume it with ProcessFork::resumeChildFork)
Definition Process.h:414
@ Immediate
Start the forked process immediately.
Definition Process.h:415
Side getSide() const
Obtain process parent / fork side.
Definition Process.h:410
FileDescriptor & getWritePipe()
Gets the descriptor to "write" something to the other side.
Side
Definition Process.h:404
@ ForkParent
Parent side of the fork.
Definition Process.h:405
@ ForkChild
Child side of the fork.
Definition Process.h:406
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:428
Native os handle to a process identifier.
Definition Process.h:29
Definition Process.h:69
bool windowsHide
[Windows] Hides child process window (default == Process::isWindowsConsoleSubsystem)
Definition Process.h:70
Definition Process.h:157
Definition Process.h:155
StdIn(StringView string)
Fills standard input with content of a StringView.
Definition Process.h:166
StdIn(const char(&item)[N])
Fills standard input with content of a C-String.
Definition Process.h:163
StdIn(Span< const char > span)
Fills standard input with content of a Span.
Definition Process.h:169
StdIn(Inherit)
Inherits child process Input from parent process.
Definition Process.h:160
Definition Process.h:135
Definition Process.h:136
Definition Process.h:133
StdOut(Span< char > span)
Read the process standard output/error into the given Span.
Definition Process.h:145
StdOut(Ignore)
Ignores child process standard output/error (child process output will be silenced)
Definition Process.h:139
StdOut(Inherit)
Inherits child process standard output/error (child process will print into parent process console)
Definition Process.h:142
Definition Process.h:100
Definition Process.h:75
StdStream(Buffer &externalBuffer)
Read the process standard output/error into the given Vector.
Definition Process.h:81
StdStream(String &externalString)
Read the process standard output/error into the given String.
Definition Process.h:78
StdStream(FileDescriptor &&file)
Redirects child process standard output/error to a given file descriptor.
Definition Process.h:85
Execute a child process with standard file descriptors redirection.
Definition Process.h:67
int32_t getExitStatus() const
gets the return code from the exited child process (valid only after exec or waitForExitSync)
Definition Process.h:216
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:206
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:190
Options options
Options for the child process (hide console window etc.)
Definition Process.h:178
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:222
ProcessID processID
ID of the process (can be the same as handle on some OS)
Definition Process.h:177
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:176
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
String with compile time configurable inline storage (small string optimization)
Definition StringFormat.h:12
View over a contiguous sequence of items (pointer + size in elements).
Definition Span.h:29
Non-owning view over a range of characters with UTF Encoding.
Definition StringView.h:48
A non-modifiable owning string with associated encoding.
Definition String.h:29
A contiguous sequence of heap allocated elements.
Definition Vector.h:189