Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Icod.Processes

PR Staging buildMain Release validation

Icod.Processes is a cross-platform .NET library for safe child-process execution and neutral process-control primitives. It provides reusable process mechanisms without tying callers to a command suite such as CoreUtils or ProcPs.

The library is the standalone successor to the process infrastructure that was originally incubated under Icod.CommandFramework.Processes.

Features

  • exact argument-vector child launching without shell quoting;
  • explicit inherited, empty, and modified child environments;
  • executable lookup using explicit working-directory and environment snapshots;
  • asynchronous standard-input, standard-output, and standard-error forwarding;
  • captured output with concurrent draining of both output streams;
  • monotonic execution timeouts through Icod.Timing;
  • cancellation policies for the child, process tree, or leave-running behavior;
  • process identities with optional PID-reuse protection;
  • process, process-group, session, and POSIX priority-selector targets;
  • arbitrary-process liveness observation and asynchronous waiting;
  • portable signal parsing and translation, including Linux real-time signals;
  • signal delivery with controlled platform substitutions;
  • Linux signal disposition and blocked-mask observations;
  • POSIX queued signal delivery for individual processes;
  • POSIX nice-value operations and Windows priority-class substitutions;
  • POSIX launch-time signal disposition/mask policy;
  • atomic POSIX child process-group creation when the native launch path is used;
  • ordered native POSIX file-descriptor duplication and closure at child launch; and
  • opt-in POSIX current-process replacement with reversible descriptor actions and execvp-compatible executable-text fallback.

Release highlights

1.2.0 — POSIX current-process replacement

Version 1.2.0 adds an opt-in process-image replacement path for Unix-like hosts. Set ProcessRunOptions.ReplaceCurrentProcess to request native execve behavior instead of creating and supervising a child process.

On successful replacement, RunAsync does not return: the calling process is replaced by the requested executable and keeps its process identity. This is important for Unix-style wrapper commands where PID, job-control, signal, and standard-descriptor semantics belong directly to the target program rather than to a long-lived managed parent.

The replacement path supports:

  • exact argument vectors and an explicit native argv[0];
  • exact environment snapshots and executable lookup;
  • an optional working directory;
  • launch-time POSIX signal disposition and mask policy;
  • unreadable standard input for commands such as nohup;
  • ordered PosixFileDescriptorDuplications immediately before execve;
  • restoration of descriptor and launch state when replacement fails; and
  • the traditional execvp behavior of retrying executable text through /bin/sh when the initial exec fails with ENOEXEC.

For example, an exec-style wrapper can request replacement without changing the public process-execution abstraction:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ArgumentZero="program",Environment=ProcessEnvironment.CreateInheritedBuilder().Build(),ReplaceCurrentProcess=true,ResolveExecutable=true,ReturnLaunchFailureResult=true};options.Arguments.Add("argument");ProcessResultresult=awaitProcessRunner.RunAsync(options);// Reached only if replacement did not succeed.

Current-process replacement is a POSIX capability and is unsupported on Windows. It cannot be combined with managed standard-stream redirection or capture, creation of a new child process group, a managed execution timeout, or a ProcessStarted callback. Callers that require those supervisory features should continue to use normal child-process execution.

This capability is intended for wrapper implementations such as env, nice, nohup, and stdbuf, where successful Unix execution traditionally replaces the wrapper process rather than leaving a supervisor behind.

1.1.0 — Native POSIX file-descriptor actions

Version 1.1.0 added ordered native POSIX file-descriptor duplication to ProcessRunOptions. PosixFileDescriptorDuplication describes a dup2-style source-to-destination mapping and can optionally close the source descriptor after the duplication.

Actions execute in list order. This permits later actions to refer to descriptor state established by earlier actions. For example, a wrapper can redirect standard output to an already-open file descriptor and then make standard error refer to that same open-file description:

usingIcod.Processes;varoptions=newProcessRunOptions("program"){ResolveExecutable=true,ReturnLaunchFailureResult=true};options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(outputFileDescriptor,1,closeSource:true));options.PosixFileDescriptorDuplications.Add(newPosixFileDescriptorDuplication(1,2));ProcessResultresult=awaitProcessRunner.RunAsync(options);

Unlike managed stream forwarding, these actions modify the child's native file descriptors at launch. The child therefore observes the actual descriptor type, seekability, open-file-description identity, and inheritance semantics rather than a parent-managed pipe. This is particularly important for Unix wrappers such as nohup and for programs that inspect their own standard descriptors.

Native descriptor actions are supported on Linux and macOS and are unsupported on Windows. They cannot be combined with managed standard-stream redirection or output capture.

Requirements

The current 1.2.0 release targets .NET 10.0. The implementation uses process launch capabilities provided by the .NET 10 runtime and intentionally does not add compatibility shims for older target frameworks.

The only runtime package dependency is Icod.Timing 1.0.0.

Installation

Install-Package Icod.Processes -Version 1.2.0

or:

dotnet add package Icod.Processes --version 1.2.0

Example

usingIcod.Processes;varoptions=newProcessRunOptions("dotnet"){CaptureStandardOutput=true,ResolveExecutable=true,ReturnLaunchFailureResult=true,Timeout=TimeSpan.FromSeconds(10)};options.Arguments.Add("--version");ProcessResultresult=awaitProcessRunner.RunAsync(options);Console.WriteLine(result.StandardOutput);

A larger runnable example is available under samples/Icod.Processes.Sample.

Platform capabilities

Icod.Processes exposes neutral contracts, but not every operating system has the same native process-control facilities. Providers report unsupported operations explicitly rather than fabricating Unix semantics.

CapabilityWindowsLinuxmacOS
Child execution, streams, environment, and working directoryYesYesYes
Process identity, PID-reuse observation, liveness, and waitingYesYesYes
New process group at child launchYesYesYes
Custom native argv[0]UnsupportedYesYes
Native child file-descriptor duplicationUnsupportedYesYes
Current-process replacement (execve with descriptor actions and shell fallback)UnsupportedYesYes
Process-group target controlUnsupportedYesYes
Signal deliveryTermination substitutionNativeNative
Signal disposition observationUnsupportedYesUnsupported
Blocked-signal observationUnsupportedYesUnsupported
Queued signal valuesUnsupportedYesUnsupported
Priority operationsPriority-class approximationNative nice valuesNative nice values

Applications should inspect provider capabilities and operation results where a feature can vary by host.

Migrating from Icod.CommandFramework.Processes

Code that currently consumes the process layer from Icod.CommandFramework can migrate without taking a dependency on ProcPs or CoreUtils.

Replace the package dependency with:

<PackageReferenceInclude="Icod.Processes"Version="1.2.0" />

and replace:

usingIcod.CommandFramework.Processes;

with:

usingIcod.Processes;

The standalone package owns the neutral process execution and control contracts. ProcPs-specific enumeration, /proc parsing, metrics, matching, personalities, and presentation remain outside this library.

Design boundary

Icod.Processes owns general process execution and control mechanisms. It does not own ProcPs-specific process enumeration, /proc field parsing, selection grammar, metrics, personalities, or command presentation. Those remain in Icod.ProcPs.Shared.

Likewise, command-line parsing, diagnostics, and other command-hosting concerns remain outside this package.

Build and CI/CD lifecycle

Local development uses Debug:

build.cmd

or, on Unix-like hosts:

./build.sh

Both scripts delegate to packaging/Invoke-Build.ps1. With no argument they run:

clean -> restore -> build -> test -> pack -> validate

The repository lifecycle is:

local development -> Debug
pull request -> Staging on Windows/Linux/macOS
main -> validation-only Release on six OS/architecture runners
v<semver> tag -> Release publication, when the tagged commit is contained in main

main never publishes. Tagged releases verify the exact Icod.Processes.nupkg and .snupkg, including the Icod.Timing 1.0.0 dependency and portable PDB payload, before NuGet.org and GitHub Packages publish the same package in parallel. The final GitHub Release contains the package, symbols, and a SHA-256 manifest.

Debug, Staging, and Release all use portable debug information. Common configuration properties are declared once in each project instead of being repeated across configuration-specific property groups.

Author

Timothy J. Bruce uniblab@hotmail.com

Copyright (c) 2026 Timothy J. Bruce.

License

Licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE for the complete license text.

About

Cross-platform .NET process execution and control primitives for safe child launching, process identity, signals, priorities, liveness, waiting, cancellation, and timeouts.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

Contributors

Languages