# cc Rust Bindings
Rust bindings for the cc virtualization library, providing a safe Rust interface to cc's virtualization primitives.
## Build Requirements
**IMPORTANT**: Building this crate requires Go 1.21+ to compile the native `libcc` library.
| Rust | 1.70+ | Stable toolchain |
| Go | 1.21+ | Required to build libcc |
The build.rs script will automatically compile libcc using Go if `LIBCC_PATH` is not set.
## Installation
### From crates.io
```toml
[dependencies]
cc-vm = "0.1"
```
Ensure Go is installed and available in your PATH:
```bash
# macOS
brew install go
# Ubuntu/Debian
sudo apt install golang
# Verify installation
go version
```
### From Source
1. Build the `libcc` shared library:
```bash
cd /path/to/cc
./tools/build.go -bindings-c
```
2. Set the library path (or copy `libcc.dylib`/`libcc.so` to a standard location):
```bash
export LIBCC_PATH=/path/to/cc/build/libcc.dylib
```
3. Add to Cargo.toml:
```toml
[dependencies]
cc-vm = { path = "/path/to/cc/bindings/rust" }
```
## Quick Start
```rust
use cc::{OciClient, Instance, InstanceOptions};
fn main() -> cc::Result<()> {
// Initialize the library
cc::init()?;
// Check for hypervisor support
if cc::supports_hypervisor()? {
println!("Hypervisor available!");
} else {
println!("No hypervisor - some features may be limited");
cc::shutdown();
return Ok(());
}
// Query system capabilities
let caps = cc::query_capabilities()?;
println!("Architecture: {}", caps.architecture);
// Pull an image and create an instance
let client = OciClient::new()?;
let source = client.pull("alpine:latest", None, None)?;
// Get image config
let config = source.get_config()?;
println!("Image architecture: {:?}", config.architecture);
// Create and run instance
let opts = InstanceOptions {
memory_mb: 512,
cpus: 2,
..Default::default()
};
let inst = Instance::new(source, Some(opts))?;
println!("Instance ID: {}", inst.id());
// Run a command
let output = inst.command("echo", &["Hello from Rust!"])?.output()?;
println!("Output: {}", String::from_utf8_lossy(&output.stdout));
// File operations
inst.write_file("/tmp/test.txt", b"Hello, World!", 0o644)?;
let data = inst.read_file("/tmp/test.txt")?;
println!("Read: {}", String::from_utf8_lossy(&data));
// Directory operations
inst.mkdir("/tmp/mydir", 0o755)?;
for entry in inst.read_dir("/tmp")? {
println!(" {} (dir={})", entry.name, entry.is_dir);
}
// Cleanup happens automatically on drop
cc::shutdown();
Ok(())
}
```
## API Reference
### Module Functions
- `cc::init()` - Initialize the library (required before any other call)
- `cc::shutdown()` - Shutdown and release resources
- `cc::api_version()` - Get API version string
- `cc::api_version_compatible(major, minor)` - Check version compatibility
- `cc::supports_hypervisor()` - Check if hypervisor is available
- `cc::query_capabilities()` - Get system capabilities
- `cc::guest_protocol_version()` - Get guest protocol version
### Structs
#### `OciClient`
OCI image client for pulling and managing container images.
```rust
let client = OciClient::new()?;
let client = OciClient::with_cache_dir("/custom/cache")?;
let source = client.pull("alpine:latest", None, None)?;
let source = client.load_tar("/path/to/image.tar", None)?;
let source = client.load_dir("/path/to/extracted", None)?;
client.export_dir(&source, "/path/to/output")?;
```
#### `Instance`
A running VM instance with filesystem, command, and network access.
```rust
let inst = Instance::new(source, Some(opts))?;
// Properties
inst.id(); // Instance ID
inst.is_running(); // Check if running
// Lifecycle
inst.wait(None)?; // Wait for termination
inst.close()?; // Close and cleanup
// Filesystem
inst.read_file(path)?;
inst.write_file(path, data, mode)?;
inst.stat(path)?;
inst.mkdir(path, mode)?;
inst.remove(path)?;
inst.read_dir(path)?;
// File handles (implement std::io::Read/Write/Seek)
let mut f = inst.open(path)?;
let mut f = inst.create(path)?;
// Commands
let output = inst.command("echo", &["hello"])?.output()?;
let exit_code = inst.command("ls", &["-la"])?.run()?;
// Networking
let listener = inst.listen("tcp", ":8080")?;
// Snapshots
let snapshot = inst.snapshot(None)?;
```
#### `Cmd`
Command builder for execution in instances.
```rust
let cmd = inst.command("env", &[])?;
let cmd = cmd.dir("/tmp")?;
let cmd = cmd.env("MY_VAR", "value")?;
// Synchronous execution
let exit_code = cmd.run()?;
// Capture output
let output = cmd.output()?;
let combined = cmd.combined_output()?;
// Async execution
let mut cmd = inst.command("sleep", &["10"])?;
cmd.start()?;
// ... do other work ...
let exit_code = cmd.wait()?;
```
#### `File`
File handle for read/write operations. Implements `std::io::Read`, `Write`, and `Seek`.
```rust
use std::io::{Read, Write, Seek};
let mut f = inst.open(path)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
let mut f = inst.create(path)?;
f.write_all(b"data")?;
f.seek(std::io::SeekFrom::Start(0))?;
```
### Types
- `InstanceOptions` - VM configuration (memory_mb, cpus, timeout_seconds, user, mounts)
- `PullOptions` - Image pull options (platform, auth, policy)
- `PullPolicy` - Pull policy enum (IfNotPresent, Always, Never)
- `FileInfo` - File metadata (name, size, mode, is_dir, is_symlink)
- `DirEntry` - Directory entry (name, is_dir, mode)
- `ImageConfig` - OCI image configuration
- `Capabilities` - System capabilities
- `SeekWhence` - Seek origin (Set, Current, End)
- `CommandOutput` - Command output with stdout and exit_code
### File Open Flags
```rust
use cc::flags::*;
O_RDONLY // Read only
O_WRONLY // Write only
O_RDWR // Read and write
O_APPEND // Append mode
O_CREATE // Create if not exists
O_TRUNC // Truncate to zero
O_EXCL // Exclusive create (fail if exists)
```
### Error Handling
All operations return `cc::Result<T>`, which is `Result<T, cc::Error>`.
```rust
use cc::Error;
match result {
Err(Error::InvalidHandle) => println!("Handle is invalid"),
Err(Error::InvalidArgument(msg)) => println!("Invalid argument: {}", msg),
Err(Error::NotRunning) => println!("Instance has terminated"),
Err(Error::AlreadyClosed) => println!("Resource already closed"),
Err(Error::Timeout) => println!("Operation timed out"),
Err(Error::HypervisorUnavailable(msg)) => println!("No hypervisor: {}", msg),
Err(Error::Io { message, op, path }) => println!("I/O error: {}", message),
Err(Error::Network(msg)) => println!("Network error: {}", msg),
Err(Error::Cancelled) => println!("Operation was cancelled"),
Err(Error::Unknown(msg)) => println!("Unknown error: {}", msg),
Ok(value) => { /* success */ }
}
```
## Code Signing
**Your Rust binary does NOT need code signing.**
The libcc shared library handles virtualization through cc-helper, which is already signed with the necessary entitlements. Your application simply links against libcc and does not require any special entitlements or code signing.
## Running Tests
```bash
# Build libcc first
./tools/build.go -bindings-c
# Set library path
export LIBCC_PATH=$(pwd)/build/libcc.dylib # or .so on Linux
# Run tests (non-VM tests only)
cd bindings/rust
cargo test
# Run all tests including VM tests (requires hypervisor)
CC_RUN_VM_TESTS=1 cargo test
```
## Running Examples
```bash
# Set library path
export LIBCC_PATH=$(pwd)/build/libcc.dylib
# Run basic example
cargo run --example basic
```
## Thread Safety
All types in this crate implement `Send` and `Sync`, allowing them to be used across threads. However, operations on a single instance should be synchronized externally if accessed from multiple threads simultaneously.
## License
MIT