Skip to content

Commit

Permalink
add session and daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
doriancodes committed Dec 11, 2024
1 parent c504187 commit cab6747
Show file tree
Hide file tree
Showing 7 changed files with 492 additions and 74 deletions.
45 changes: 40 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ tokio = { version = "1", features = ["full"] }
clap = { version = "4.4", features = ["derive"] }
log = "0.4"
env_logger = "0.10"
nix = "0.26"

[dev-dependencies]
tempfile = "3.2"
Expand Down
116 changes: 51 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,6 @@

Simple file system implementation using the 9P protocol

## Bind
The P9 protocol for file systems requires a unique feature called `bind`, which allows for flexible control over the namespace
(file hierarchy). The bind operation maps a file, directory, or another namespace tree into a new location in the namespace.
It supports three binding modes: Before, After, and Replace. Each mode provides different behaviors for resolving file
lookups when multiple resources are mapped to the same namespace.

### Replace Binding: `frg bind src mountpoint`
This mode replaces whatever was previously mounted at the mountpoint with the src. Only the new src is visible at the specified
mountpoint.
- **Behavior**: The `src` completely overrides any existing content at the `mountpoint`.
- **Example use cases**:
- Temporarily replacing a default configuration directory with a test or alternative version
```shell
frg bind /test/config /etc
```
After this, processes see `/test/config` contents instead of the original `/etc`.
- Redirecting access to `/bin` to a custom toolchain directory for development:
```shell
frg bind /custom/tools/bin /bin
```

### Before binding `frg bind -b src mountpoint`
In this mode the `src` is placed *before* the existing contents of the mountpoint. When a lookup occurs,
a Plan9 file system searches `src` first, and if the file isn't found there, it searches the original `mountpoint`.
- **Behavior**: Adds `src` at a higher priority, leaving the existing content accessible as a fallback.
- **Use case**:
- Overlaying new tools or files over existing directories without completely replacing them. For example,
adding custom binaries that take precedence over system binaries:
```shell
frg bind -b /custom/bin /bin
```
In this case, `/custom/bin/ls` will be used instead of `/bin/ls` if both exist.
- **Example**: Temporarily prioritizing a new set of libraries or data over the default paths for testing or debugging.

### After binding `frg bind -a src mountpoint`
This mode appends the `src` to the `mountpoint`'s search path. Plan 9 resolves lookups by searching the original
`mountpoint` first, and if the file isn't found there, it checks the `src`.
- **Behavior**: Adds `src` as a fallback while maintaining the existing content's priority.
- **Use case**:
- Adding extra directories to extend a namespace without interfering with its current operation. For example, appending a directory with additional fonts:
```shell
frg bind -a /extra/fonts /fonts
```
Here, `/fonts` will use default system fonts first and fall back to `/extra/fonts` if needed.
- **Example**: Supplementing a default configuration directory with additional files:
```shell
frg bind -a /additional/config /etc
```
This ensures `/etc` retains its default behavior but gains the additional configuration files if the defaults don’t exist.

### Union directories
Using `frg bind -b` and `frg bind -a`, you can create union directories where files from multiple sources appear merged.
For example:
```shell
frg bind -b /local/bin /bin
frg bind -a /backup/bin /bin
```
This setup prioritizes `/local/bin`, followed by `/bin`, and finally `/backup/bin`.

### Custom environments
For isolating environments, such as creating chroot-like environments or managing per-process views of namespaces.

## User Guide

The user guide is available online at [doriancodes.github.io/froggr](https://doriancodes.github.io/froggr/) and can be built locally using [mdBook](https://rust-lang.github.io/mdBook/). To build and serve the documentation locally:
Expand All @@ -75,10 +13,58 @@ mdbook serve docs/
This will serve the documentation at [localhost:3000](http://localhost:3000).

### Commands:
- `bind`: Bind a source directory to a target directory
- `mount`: Mount a directory to a mount point

For more details, use `frg --help`
#### bind
Bind a source directory to a target directory
- Options:
- `-b, --before`: Bind before existing bindings (default)
- `-a, --after`: Bind after existing bindings
- `-r, --replace`: Replace existing bindings
- `-c, --create`: Create new binding
- `-v, --verbose`: Enable verbose logging

#### mount
Mount a directory to a mount point
- Options:
- `--node-id <ID>`: Node identifier (defaults to "localhost")
- `-v, --verbose`: Enable verbose logging

#### session
Start a new session daemon
- Options:
- `-r, --root <PATH>`: Root directory for the session (defaults to current directory)
- `--pid-file <PATH>`: Custom PID file location
- `--privileged`: Run with elevated privileges (requires root)
- `-v, --verbose`: Enable verbose logging

### Examples:

```bash
# Bind Operations
frg bind /source/dir /target/dir # Bind with default mode (before)
frg bind -a /source/dir /target/dir # Bind after existing bindings
frg bind -r /source/dir /target/dir # Replace existing bindings
frg bind -c /source/dir /target/dir # Create new binding
frg bind -v /source/dir /target/dir # Bind with verbose logging

# Mount Operations
frg mount /source/dir /mount/point # Mount with default node-id
frg mount /source/dir /mount/point mynode # Mount with custom node-id
frg mount -v /source/dir /mount/point # Mount with verbose logging

# Session Operations
frg session # Start session in current directory
frg session -v # Start session with verbose logging
frg session --root /path/to/dir # Start session in specific directory
sudo frg session --privileged # Start privileged session
frg session --pid-file /path/to/pid # Start session with custom PID file
```

For detailed help on any command, use:
```bash
frg --help # General help
frg <command> --help # Command-specific help
```

## License

Expand Down
92 changes: 88 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ use anyhow::Result;
use clap::{Parser, Subcommand};
use env_logger;
use froggr::modules::namespace::BindMode;
use froggr::FilesystemManager;
use froggr::NineP;
use log::info;
use froggr::modules::session::Session;
use froggr::{FilesystemManager, NineP};
use log::{info, error, LevelFilter};
use std::path::PathBuf;
use std::env;
use tokio::signal;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Enable verbose logging
#[arg(short, long, global = true)]
verbose: bool,

#[command(subcommand)]
command: Commands,
}
Expand Down Expand Up @@ -45,14 +51,92 @@ enum Commands {
#[arg(default_value = "localhost")]
node_id: String,
},
/// Start a new session daemon
Session {
/// Root directory for the session (defaults to current directory)
#[arg(short = 'r', long = "root")]
root: Option<PathBuf>,
/// PID file location (defaults to /tmp/froggr.pid, or /var/run/froggr.pid if running as root)
#[arg(long)]
pid_file: Option<PathBuf>,
/// Run with elevated privileges (stores PID in /var/run)
#[arg(long)]
privileged: bool,
},
}

#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
let cli = Cli::parse();

// Initialize logger based on verbose flag
let log_level = if cli.verbose {
LevelFilter::Debug
} else {
LevelFilter::Info
};

env_logger::Builder::new()
.filter_level(log_level)
.init();

info!("Starting froggr...");

match &cli.command {
Commands::Session { root, pid_file, privileged } => {
info!("Initializing session...");

// Use provided root or current directory
let root_path = match root {
Some(path) => path.clone(),
None => env::current_dir()?,
};

// Determine PID file location
let pid_path = match (pid_file, privileged) {
(Some(path), false) => path.clone(),
(Some(path), true) => {
if !nix::unistd::Uid::effective().is_root() {
return Err(anyhow::anyhow!("Privileged mode requires root permissions"));
}
path.clone()
},
(None, true) => {
if !nix::unistd::Uid::effective().is_root() {
return Err(anyhow::anyhow!("Privileged mode requires root permissions"));
}
PathBuf::from("/var/run/froggr.pid")
},
(None, false) => PathBuf::from("/tmp/froggr.pid"),
};

info!("Using root directory: {}", root_path.display());

// Start a new session
let session = Session::new(&root_path)?;
info!("Session started with root directory: {}", root_path.display());

// Write PID file
let pid = std::process::id().to_string().into_bytes();
std::fs::write(&pid_path, pid)?;
info!("PID file written to: {}", pid_path.display());

info!("Session running. Press Ctrl+C to stop.");

// Wait for shutdown signal
signal::ctrl_c().await?;
info!("Received shutdown signal");

// Clean shutdown
session.shutdown()?;

// Cleanup PID file
if pid_path.exists() {
std::fs::remove_file(pid_path)?;
}

info!("Session terminated");
}
Commands::Bind {
before,
after,
Expand Down
Loading

0 comments on commit cab6747

Please sign in to comment.