Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LinuxCompanion.jl

LinuxCompanion.jl is a Linux-first Julia library for explicit process execution. Commands, pipelines, standard I/O, environment changes, lifecycle policy, and results are values the caller can inspect. No shell is invoked implicitly.

Command/Pipeline -> ExecutionPlan -> ProcessHandle -> ProcessResult

This is an alpha process-orchestration library. It is not a shell replacement, security sandbox, service manager, or container runtime.

Features

  • Executable names and arguments kept as separate values, never shell-parsed.
  • Bounded captured output by default.
  • Explicit environment and standard I/O policy.
  • Supervision with timeouts, cancellation, signals, and cleanup results.
  • Dry-run plan inspection before spawning.
  • Typed statuses instead of parsed exit strings.
  • JSON-producing Linux tools without a JSON dependency in this package.

Requirements

  • Linux is the supported host.
  • Julia 1.9 or newer, as declared in Project.toml.
  • No system daemon is required.
  • No runtime Julia dependencies outside the standard library.

Installation

From the Julia package prompt:

using Pkg
Pkg.add(url="https://github.com/naranyala/LinuxCompanion.jl.git")

For local development:

using Pkg
Pkg.develop(path="/path/to/LinuxCompanion.jl")

Quick start

using LinuxCompanion

result = run(Command("printf", ["%s\\n", "hello from Julia"]);
             stdout=Capture(max_bytes=4096),
             stderr=Capture(max_bytes=4096))

if result.ok
    print(text(result.stdout))
else
    @error "command failed" status=status_name(result.status) error=result.error
end

Command never interprets argv through a shell. Spaces, quotes, newlines, and shell metacharacters in an argument stay data. To use a shell explicitly, pass it as the executable with trusted input only.

Inspect before running

job = pipeline(
    Command("printf", ["%s\\n", "TODO", "FIXME", "TODO"]),
    Command("sort", ["-u"]),
)

println(render(plan(job); timeout=30.0))
result = run(job; timeout=30.0,
             stdout=Capture(max_bytes=4096),
             stderr=Capture(max_bytes=4096))
println(text(result.stdout))

render spawns nothing, writes no files, sends no signals, and changes no kernel policy. Environment values are redacted by default.

Supervise a process

token = CancellationToken()
handle = spawn(Command("sleep", ["30"];
                       lifecycle=LifecyclePolicy(grace_period=0.25));
               cancellation=token)

@async begin
    sleep(0.1)
    cancel!(token)
end

result = wait(handle)
@assert result.status === Cancelled
@assert result.cleanup.reaped

Use run_checked when a non-success result should throw its typed error:

run_checked(Command("/bin/sh", ["-c", "exit 2"]))

JSON-producing tools

JSON parsing stays an application concern, which keeps this package dependency-free. Pass a caller-owned parser to run_json:

using JSON3
using LinuxCompanion

command = hyprctl("-j", "monitors"; json=true)
println(render(plan(command)))

result, monitors = run_json(command; parser=JSON3.read,
                             timeout=5.0,
                             stdout=Capture(max_bytes=64_000),
                             stderr=Capture(max_bytes=4_096))

result.ok || error(result.error)

json=true marks the command for plan review; it does not parse output. run_json returns (result, nothing) for failed execution or empty stdout, and parser exceptions propagate to the caller.

Host diagnostics

report = capabilities()
for feature in report.features
    println(feature.name, ": ", feature.available, " - ", feature.reason)
end

machine_report = diagnostics_json(report)

Capability reporting is read-only: it creates no cgroups, enters no namespaces, elevates no privileges, and mutates no kernel state. The epoll and inotify entries are platform indications, not permission guarantees.

Output and resource policies

bounded = run(Command("yes", ["output"]);
              stdout=Capture(max_bytes=4096),
              stderr=Null(),
              timeout=1.0)
@assert bounded.status === OutputLimitExceeded

buffer = IOBuffer()
streamed = run(Command("printf", ["streamed"]);
               stdout=Stream(buffer), stderr=Null())
@assert String(take!(buffer)) == "streamed"

On Linux, ProcessResult.usage is populated from getrusage(RUSAGE_CHILDREN) after reap. CPU values are seconds and RSS is reported in bytes. This is aggregate accounting for reaped Julia child processes, not isolated per-process measurement.

Documentation

Runnable examples are in examples/: quickstart.jl, pipeline.jl, and supervised.jl.

Planned work (filesystem operations, event loops, cgroup policies, CLI) is tracked in docs/roadmap.md and TODOS.md.

Development

Run the package tests from the repository root:

julia --project=. -e 'using Pkg; Pkg.test()'

The suite covers command validation, argv boundaries, environment policy, pipelines, capture and stream sinks, stdin input, spawn failures, lifecycle statuses, timeouts, cancellation, termination, reaping, resource fields, JSON helpers, command metadata, and capability diagnostics.

Safety and scope

The package does not elevate privileges, invoke a shell implicitly, or silently drop requested controls. Future filesystem, cgroup, namespace, and capability APIs will be explicitly opt-in and will report unsupported or permission-denied outcomes instead of degrading silently.

Read Safety and portability before using the library for long-lived, privileged, or high-volume workloads.

About

LinuxCompanion.jl is a Linux-first Julia library for explicit process execution. It turns commands, pipelines, stdio, environment changes, lifecycle policy, and results into inspectable values without invoking a shell implicitly.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages