lyngio: added processes and docs for it, JVM and linux
This commit is contained in:
1 parent
d91acd593a
commit
660a80a26b
22 files changed
+1563
-3
No files matched your search
+7
-3
@@ -8,7 +8,7 @@ This module provides a uniform, suspend-first filesystem API to Lyng scripts, ba
|
||||
|
||||
It exposes a Lyng class `Path` with methods for file and directory operations, including streaming readers for large files.
|
||||
|
||||
It is a separate library because access to teh filesystem is a security risk we compensate with a separate API that user must explicitly include to the dependency and allow. Together with `FsAceessPolicy` that is required to `createFs()` which actually adds the filesystem to the scope, the security risk is isolated.
|
||||
It is a separate library because access to the filesystem is a security risk we compensate with a separate API that user must explicitly include to the dependency and allow. Together with `FsAccessPolicy` that is required to `createFs()` which actually adds the filesystem to the scope, the security risk is isolated.
|
||||
|
||||
Also, it helps keep Lyng core small and focused.
|
||||
|
||||
@@ -23,7 +23,7 @@ dependencies {
|
||||
implementation("net.sergeych:lyngio:0.0.1-SNAPSHOT")
|
||||
}
|
||||
```
|
||||
Note on maven repository. Lyngio uses ths same maven as Lyng code (`lynglib`) so it is most likely already in your project. If ont, add it to the proper section of your `build.gradle.kts` or settings.gradle.kts:
|
||||
Note on maven repository. Lyngio uses the same maven as Lyng code (`lynglib`) so it is most likely already in your project. If not, add it to the proper section of your `build.gradle.kts` or settings.gradle.kts:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
@@ -43,9 +43,13 @@ This brings in:
|
||||
|
||||
The filesystem module is not installed automatically. You must explicitly register it in the scope’s `ImportManager` using the installer. You can customize access control via `FsAccessPolicy`.
|
||||
|
||||
Kotlin (host) bootstrap example (imports omitted for brevity):
|
||||
Kotlin (host) bootstrap example:
|
||||
|
||||
```kotlin
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.io.fs.createFs
|
||||
import net.sergeych.lyngio.fs.security.PermitAllAccessPolicy
|
||||
|
||||
val scope: Scope = Scope.new()
|
||||
val installed: Boolean = createFs(PermitAllAccessPolicy, scope)
|
||||
// installed == true on first registration in this ImportManager, false on repeats
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
### lyng.io.process — Process execution and control for Lyng scripts
|
||||
|
||||
This module provides a way to run external processes and shell commands from Lyng scripts. It is designed to be multiplatform and uses coroutines for non-blocking execution.
|
||||
|
||||
> **Note:** `lyngio` is a separate library module. It must be explicitly added as a dependency to your host application and initialized in your Lyng scopes.
|
||||
|
||||
---
|
||||
|
||||
#### Add the library to your project (Gradle)
|
||||
|
||||
If you use this repository as a multi-module project, add a dependency on `:lyngio`:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation("net.sergeych:lyngio:0.0.1-SNAPSHOT")
|
||||
}
|
||||
```
|
||||
|
||||
For external projects, ensure you have the appropriate Maven repository configured (see `lyng.io.fs` documentation).
|
||||
|
||||
---
|
||||
|
||||
#### Install the module into a Lyng Scope
|
||||
|
||||
The process module is not installed automatically. You must explicitly register it in the scope’s `ImportManager` using `createProcessModule`. You can customize access control via `ProcessAccessPolicy`.
|
||||
|
||||
Kotlin (host) bootstrap example:
|
||||
|
||||
```kotlin
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.io.process.createProcessModule
|
||||
import net.sergeych.lyngio.process.security.PermitAllProcessAccessPolicy
|
||||
|
||||
// ... inside a suspend function or runBlocking
|
||||
val scope: Scope = Script.newScope()
|
||||
createProcessModule(PermitAllProcessAccessPolicy, scope)
|
||||
|
||||
// In scripts (or via scope.eval), import the module:
|
||||
scope.eval("import lyng.io.process")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Using from Lyng scripts
|
||||
|
||||
```lyng
|
||||
import lyng.io.process
|
||||
|
||||
// Execute a process with arguments
|
||||
val p = Process.execute("ls", ["-l", "/tmp"])
|
||||
for (line in p.stdout) {
|
||||
println("OUT: " + line)
|
||||
}
|
||||
val exitCode = p.waitFor()
|
||||
println("Process exited with: " + exitCode)
|
||||
|
||||
// Run a shell command
|
||||
val sh = Process.shell("echo 'Hello from shell' | wc -w")
|
||||
for (line in sh.stdout) {
|
||||
println("Word count: " + line.trim())
|
||||
}
|
||||
|
||||
// Platform information
|
||||
val details = Platform.details()
|
||||
println("OS: " + details.name + " " + details.version + " (" + details.arch + ")")
|
||||
if (details.kernelVersion != null) {
|
||||
println("Kernel: " + details.kernelVersion)
|
||||
}
|
||||
|
||||
if (Platform.isSupported()) {
|
||||
println("Processes are supported!")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### API Reference
|
||||
|
||||
##### `Process` (static methods)
|
||||
- `execute(executable: String, args: List<String>): RunningProcess` — Start an external process.
|
||||
- `shell(command: String): RunningProcess` — Run a command through the system shell (e.g., `/bin/sh` or `cmd.exe`).
|
||||
|
||||
##### `RunningProcess` (instance methods)
|
||||
- `stdout: Flow` — Standard output stream as a Lyng Flow of lines.
|
||||
- `stderr: Flow` — Standard error stream as a Lyng Flow of lines.
|
||||
- `waitFor(): Int` — Wait for the process to exit and return the exit code.
|
||||
- `signal(name: String)` — Send a signal to the process (e.g., `"SIGINT"`, `"SIGTERM"`, `"SIGKILL"`).
|
||||
- `destroy()` — Forcefully terminate the process.
|
||||
|
||||
##### `Platform` (static methods)
|
||||
- `details(): Map` — Get platform details. Returned map keys: `name`, `version`, `arch`, `kernelVersion`.
|
||||
- `isSupported(): Bool` — True if process execution is supported on the current platform.
|
||||
|
||||
---
|
||||
|
||||
#### Security Policy
|
||||
|
||||
Process execution is a sensitive operation. `lyngio` uses `ProcessAccessPolicy` to control access to `execute` and `shell` operations.
|
||||
|
||||
- `ProcessAccessPolicy` — Interface for custom policies.
|
||||
- `PermitAllProcessAccessPolicy` — Allows all operations.
|
||||
- `ProcessAccessOp` (sealed) — Operations to check:
|
||||
- `Execute(executable, args)`
|
||||
- `Shell(command)`
|
||||
|
||||
Example of a restricted policy in Kotlin:
|
||||
|
||||
```kotlin
|
||||
import net.sergeych.lyngio.fs.security.AccessDecision
|
||||
import net.sergeych.lyngio.fs.security.Decision
|
||||
import net.sergeych.lyngio.process.security.ProcessAccessOp
|
||||
import net.sergeych.lyngio.process.security.ProcessAccessPolicy
|
||||
|
||||
val restrictedPolicy = object : ProcessAccessPolicy {
|
||||
override suspend fun check(op: ProcessAccessOp, ctx: AccessContext): AccessDecision {
|
||||
return when (op) {
|
||||
is ProcessAccessOp.Execute -> {
|
||||
if (op.executable == "ls") AccessDecision(Decision.Allow)
|
||||
else AccessDecision(Decision.Deny, "Only 'ls' is allowed")
|
||||
}
|
||||
is ProcessAccessOp.Shell -> AccessDecision(Decision.Deny, "Shell is forbidden")
|
||||
}
|
||||
}
|
||||
}
|
||||
createProcessModule(restrictedPolicy, scope)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Platform Support
|
||||
|
||||
- **JVM:** Full support using `ProcessBuilder`.
|
||||
- **Native (Linux/macOS):** Support via POSIX.
|
||||
- **Windows:** Support planned.
|
||||
- **Android/JS/iOS/Wasm:** Currently not supported; `isSupported()` returns `false` and attempts to run processes will throw `UnsupportedOperationException`.
|
||||
@@ -0,0 +1,87 @@
|
||||
### lyngio — Extended I/O and System Library for Lyng
|
||||
|
||||
`lyngio` is a separate library that extends the Lyng core (`lynglib`) with powerful, multiplatform, and secure I/O capabilities.
|
||||
|
||||
#### Why a separate module?
|
||||
|
||||
1. **Security:** I/O and process execution are sensitive operations. By keeping them in a separate module, we ensure that the Lyng core remains 100% safe by default. You only enable what you explicitly need.
|
||||
2. **Footprint:** Not every script needs filesystem or process access. Keeping these as a separate module helps minimize the dependency footprint for small embedded projects.
|
||||
3. **Control:** `lyngio` provides fine-grained security policies (`FsAccessPolicy`, `ProcessAccessPolicy`) that allow you to control exactly what a script can do.
|
||||
|
||||
#### Included Modules
|
||||
|
||||
- **[lyng.io.fs](lyng.io.fs.md):** Async filesystem access. Provides the `Path` class for file/directory operations, streaming, and globbing.
|
||||
- **[lyng.io.process](lyng.io.process.md):** External process execution and shell commands. Provides `Process`, `RunningProcess`, and `Platform` information.
|
||||
|
||||
---
|
||||
|
||||
#### Quick Start: Embedding lyngio
|
||||
|
||||
##### 1. Add Dependencies (Gradle)
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Both are required for full I/O support
|
||||
implementation("net.sergeych:lynglib:0.0.1-SNAPSHOT")
|
||||
implementation("net.sergeych:lyngio:0.0.1-SNAPSHOT")
|
||||
}
|
||||
```
|
||||
|
||||
##### 2. Initialize in Kotlin (JVM or Native)
|
||||
|
||||
To use `lyngio` modules in your scripts, you must install them into your Lyng scope and provide a security policy.
|
||||
|
||||
```kotlin
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.io.fs.createFs
|
||||
import net.sergeych.lyng.io.process.createProcessModule
|
||||
import net.sergeych.lyngio.fs.security.PermitAllAccessPolicy
|
||||
import net.sergeych.lyngio.process.security.PermitAllProcessAccessPolicy
|
||||
|
||||
suspend fun runMyScript() {
|
||||
val scope = Script.newScope()
|
||||
|
||||
// Install modules with policies
|
||||
createFs(PermitAllAccessPolicy, scope)
|
||||
createProcessModule(PermitAllProcessAccessPolicy, scope)
|
||||
|
||||
// Now scripts can import them
|
||||
scope.eval("""
|
||||
import lyng.io.fs
|
||||
import lyng.io.process
|
||||
|
||||
println("Working dir: " + Path(".").readUtf8())
|
||||
println("OS: " + Platform.details().name)
|
||||
""")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Security Tools
|
||||
|
||||
`lyngio` is built with a "Secure by Default" philosophy. Every I/O or process operation is checked against a policy.
|
||||
|
||||
- **Filesystem Security:** Implement `FsAccessPolicy` to restrict access to specific paths or operations (e.g., read-only access to a sandbox directory).
|
||||
- **Process Security:** Implement `ProcessAccessPolicy` to restrict which executables can be run or to disable shell execution entirely.
|
||||
|
||||
For more details, see the specific module documentation:
|
||||
- [Filesystem Security Details](lyng.io.fs.md#access-policy-security)
|
||||
- [Process Security Details](lyng.io.process.md#security-policy)
|
||||
|
||||
---
|
||||
|
||||
#### Platform Support Overview
|
||||
|
||||
| Platform | lyng.io.fs | lyng.io.process |
|
||||
| :--- | :---: | :---: |
|
||||
| **JVM** | ✅ | ✅ |
|
||||
| **Native (Linux/macOS)** | ✅ | ✅ |
|
||||
| **Native (Windows)** | ✅ | 🚧 (Planned) |
|
||||
| **Android** | ✅ | ❌ |
|
||||
| **NodeJS** | ✅ | ❌ |
|
||||
| **Browser / Wasm** | ✅ (In-memory) | ❌ |
|
||||
Reference in new issue
Block a user