fix
This commit is contained in:
@ -4,6 +4,7 @@ members = [
|
|||||||
"user",
|
"user",
|
||||||
"claude-service",
|
"claude-service",
|
||||||
]
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
1
archiso
Submodule
1
archiso
Submodule
Submodule archiso added at 9089013c44
17
docker-build.sh
Normal file
17
docker-build.sh
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=== AIOS Docker Build Environment ==="
|
||||||
|
|
||||||
|
# Build Docker image
|
||||||
|
echo "Building Docker image..."
|
||||||
|
docker build -t aios-dev .
|
||||||
|
|
||||||
|
# Run test kernel build
|
||||||
|
echo "Testing simple kernel build..."
|
||||||
|
docker run --rm -v $(pwd):/aios aios-dev test-kernel
|
||||||
|
|
||||||
|
# Run main kernel build
|
||||||
|
echo "Building main AIOS kernel..."
|
||||||
|
docker run --rm -v $(pwd):/aios aios-dev build-aios
|
||||||
|
|
||||||
|
echo "Build complete. Check target/ directories for binaries."
|
293
docs/MINIMAL_OS_DESIGN.md
Normal file
293
docs/MINIMAL_OS_DESIGN.md
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
# 最小限OS構成設計(Redox OSベース)
|
||||||
|
|
||||||
|
## Redox OS分析結果
|
||||||
|
|
||||||
|
### 1. **最小構成の特定**
|
||||||
|
|
||||||
|
#### コアコンポーネント(196MiB以下)
|
||||||
|
```toml
|
||||||
|
# config/minimal.toml から抽出
|
||||||
|
[packages]
|
||||||
|
base = {} # 基本システム(~20MB)
|
||||||
|
base-initfs = {} # 初期ファイルシステム(~5MB)
|
||||||
|
bootloader = {} # ブートローダー(~2MB)
|
||||||
|
drivers = {} # 最小ドライバー(~15MB)
|
||||||
|
kernel = {} # マイクロカーネル(~5MB)
|
||||||
|
relibc = {} # C標準ライブラリ(~10MB)
|
||||||
|
coreutils = {} # 基本コマンド(~8MB)
|
||||||
|
ion = {} # シェル(~3MB)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 初期化プロセス
|
||||||
|
```bash
|
||||||
|
# 00_base: 基本システム初期化
|
||||||
|
rm -r /tmp # 一時ディレクトリクリア
|
||||||
|
mkdir -m a=rwxt /tmp # 一時ディレクトリ作成
|
||||||
|
ipcd # IPCデーモン
|
||||||
|
ptyd # PTYデーモン
|
||||||
|
sudo --daemon # Sudoデーモン
|
||||||
|
|
||||||
|
# 00_drivers: ドライバー初期化
|
||||||
|
pcid-spawner /etc/pcid.d/ # PCIドライバー
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **AIOS向け最小構成実装**
|
||||||
|
|
||||||
|
#### ディレクトリ構造
|
||||||
|
```
|
||||||
|
aios-minimal/
|
||||||
|
├── kernel/ # Redoxカーネルベース
|
||||||
|
│ ├── Cargo.toml
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── main.rs # カーネルエントリーポイント
|
||||||
|
│ │ ├── memory/ # メモリ管理
|
||||||
|
│ │ ├── syscall/ # システムコール
|
||||||
|
│ │ └── scheme/ # スキームベースFS
|
||||||
|
├── bootloader/ # ブートローダー
|
||||||
|
├── userspace/ # ユーザーランド
|
||||||
|
│ ├── init/ # 初期化プロセス
|
||||||
|
│ ├── shell/ # Rustシェル
|
||||||
|
│ └── coreutils/ # 基本コマンド
|
||||||
|
└── config/
|
||||||
|
└── minimal.toml # 最小構成設定
|
||||||
|
```
|
||||||
|
|
||||||
|
#### カーネル最小化(kernel/Cargo.toml)
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "aios-kernel"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# 必要最小限の依存関係のみ
|
||||||
|
bitflags = "2"
|
||||||
|
spin = "0.9.8"
|
||||||
|
linked_list_allocator = "0.9.0"
|
||||||
|
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
"serial_debug", # シリアルデバッグのみ
|
||||||
|
]
|
||||||
|
# 不要機能を削除
|
||||||
|
# acpi = [] # ACPI無効化
|
||||||
|
# multi_core = [] # シングルコアのみ
|
||||||
|
# graphical_debug = [] # グラフィカルデバッグ無効
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 最小カーネル実装(kernel/src/main.rs)
|
||||||
|
```rust
|
||||||
|
//! AIOS Minimal Kernel
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(allocator_api)]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
mod memory;
|
||||||
|
mod syscall;
|
||||||
|
mod scheme;
|
||||||
|
|
||||||
|
/// カーネルエントリーポイント
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn kmain() -> ! {
|
||||||
|
// 基本初期化
|
||||||
|
memory::init();
|
||||||
|
scheme::init();
|
||||||
|
syscall::init();
|
||||||
|
|
||||||
|
// 最初のユーザープロセス起動
|
||||||
|
let init_pid = syscall::exec("/usr/bin/init", &[]).expect("Failed to start init");
|
||||||
|
|
||||||
|
// メインループ
|
||||||
|
loop {
|
||||||
|
syscall::sched_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### シンプルな初期化プロセス(userspace/init/src/main.rs)
|
||||||
|
```rust
|
||||||
|
//! AIOS Init Process
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("AIOS Init Starting...");
|
||||||
|
|
||||||
|
// 基本デーモン起動
|
||||||
|
spawn_daemon("ptyd"); // PTY管理
|
||||||
|
spawn_daemon("sudo"); // 権限管理
|
||||||
|
|
||||||
|
// シェル起動
|
||||||
|
Command::new("/usr/bin/shell")
|
||||||
|
.spawn()
|
||||||
|
.expect("Failed to start shell");
|
||||||
|
|
||||||
|
// プロセス管理ループ
|
||||||
|
loop {
|
||||||
|
// ゾンビプロセス回収
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_daemon(name: &str) {
|
||||||
|
Command::new(format!("/usr/bin/{}", name))
|
||||||
|
.spawn()
|
||||||
|
.unwrap_or_else(|_| panic!("Failed to start {}", name));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 軽量シェル実装(userspace/shell/src/main.rs)
|
||||||
|
```rust
|
||||||
|
//! AIOS Minimal Shell
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("AIOS Shell v0.1.0");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
print!("aios> ");
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
match input {
|
||||||
|
"exit" => break,
|
||||||
|
"help" => show_help(),
|
||||||
|
cmd if !cmd.is_empty() => execute_command(cmd),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_command(cmd: &str) {
|
||||||
|
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
|
if parts.is_empty() { return; }
|
||||||
|
|
||||||
|
match Command::new(parts[0])
|
||||||
|
.args(&parts[1..])
|
||||||
|
.status()
|
||||||
|
{
|
||||||
|
Ok(status) => {
|
||||||
|
if !status.success() {
|
||||||
|
println!("Command failed with exit code: {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!("Failed to execute command: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_help() {
|
||||||
|
println!("Available commands:");
|
||||||
|
println!(" ls - List files");
|
||||||
|
println!(" cat - Show file contents");
|
||||||
|
println!(" echo - Print text");
|
||||||
|
println!(" help - Show this help");
|
||||||
|
println!(" exit - Exit shell");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 構成ファイル(config/minimal.toml)
|
||||||
|
```toml
|
||||||
|
# AIOS Minimal Configuration
|
||||||
|
[general]
|
||||||
|
filesystem_size = 128 # 128MiB(Redoxの196MiBより小さく)
|
||||||
|
prompt = false
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
aios-kernel = {}
|
||||||
|
aios-init = {}
|
||||||
|
aios-shell = {}
|
||||||
|
aios-coreutils = {}
|
||||||
|
|
||||||
|
# 最小ファイルシステム
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/00_base"
|
||||||
|
data = """
|
||||||
|
mkdir -p /tmp /var/log
|
||||||
|
aios-init
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/hostname"
|
||||||
|
data = "aios"
|
||||||
|
|
||||||
|
# ユーザー設定
|
||||||
|
[users.root]
|
||||||
|
password = "root"
|
||||||
|
uid = 0
|
||||||
|
gid = 0
|
||||||
|
shell = "/usr/bin/aios-shell"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **メモリ使用量最適化**
|
||||||
|
|
||||||
|
#### カーネル最適化
|
||||||
|
- **スタックサイズ**: 4KB(最小限)
|
||||||
|
- **ヒープサイズ**: 1MB(動的拡張)
|
||||||
|
- **バッファサイズ**: 512B(固定)
|
||||||
|
|
||||||
|
#### ユーザーランド最適化
|
||||||
|
```rust
|
||||||
|
// メモリ制限
|
||||||
|
const MAX_PROCESSES: usize = 16; // 最大プロセス数
|
||||||
|
const MAX_FILES: usize = 64; // 最大ファイル数
|
||||||
|
const STACK_SIZE: usize = 8192; // プロセススタック8KB
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. **ビルドシステム**
|
||||||
|
|
||||||
|
#### Makefile
|
||||||
|
```makefile
|
||||||
|
# AIOS Minimal Build System
|
||||||
|
TARGET = x86_64-unknown-none
|
||||||
|
ARCH = x86_64
|
||||||
|
CONFIG = minimal
|
||||||
|
|
||||||
|
.PHONY: all kernel userspace iso clean
|
||||||
|
|
||||||
|
all: iso
|
||||||
|
|
||||||
|
kernel:
|
||||||
|
cd kernel && cargo build --release --target $(TARGET)
|
||||||
|
|
||||||
|
userspace:
|
||||||
|
cd userspace && cargo build --release
|
||||||
|
|
||||||
|
iso: kernel userspace
|
||||||
|
mkdir -p build/iso
|
||||||
|
cp kernel/target/$(TARGET)/release/aios-kernel build/iso/
|
||||||
|
cp userspace/target/release/* build/iso/
|
||||||
|
grub-mkrescue -o aios-minimal.iso build/iso
|
||||||
|
|
||||||
|
clean:
|
||||||
|
cargo clean
|
||||||
|
rm -rf build/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. **機能制限とトレードオフ**
|
||||||
|
|
||||||
|
#### 削除される機能
|
||||||
|
- マルチコア対応
|
||||||
|
- ACPI高度機能
|
||||||
|
- グラフィカルデバッグ
|
||||||
|
- 複雑なファイルシステム
|
||||||
|
- ネットワーク機能
|
||||||
|
|
||||||
|
#### 保持される機能
|
||||||
|
- 基本的なプロセス管理
|
||||||
|
- シンプルなファイルシステム
|
||||||
|
- 最低限のメモリ管理
|
||||||
|
- シリアル出力
|
||||||
|
- 基本的なシステムコール
|
||||||
|
|
||||||
|
この設計により、**128MB以下の極小OS**を実現できます。
|
286
docs/RUST_LINUX_IMPLEMENTATION.md
Normal file
286
docs/RUST_LINUX_IMPLEMENTATION.md
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
# Rust Linux OS実装戦略
|
||||||
|
|
||||||
|
## 1. アーキテクチャ選択
|
||||||
|
|
||||||
|
### Option A: Redox OSベースアプローチ
|
||||||
|
**優点:**
|
||||||
|
- 既にRustで書かれた完全なOS
|
||||||
|
- Unix-like POSIX互換
|
||||||
|
- マイクロカーネル設計
|
||||||
|
- パッケージ管理システム有り
|
||||||
|
|
||||||
|
**実装方法:**
|
||||||
|
```bash
|
||||||
|
# Redox OSをベースに拡張
|
||||||
|
git clone https://gitlab.redox-os.org/redox-os/redox
|
||||||
|
cd redox
|
||||||
|
# カスタムコンポーネント追加
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Linux From Scratch + Rustアプローチ
|
||||||
|
**優点:**
|
||||||
|
- 既存Linuxエコシステム活用
|
||||||
|
- systemd統合可能
|
||||||
|
- 豊富なパッケージ
|
||||||
|
|
||||||
|
**実装方法:**
|
||||||
|
```bash
|
||||||
|
# ALFS自動化ツール使用
|
||||||
|
git clone https://git.linuxfromscratch.org/jhalfs.git
|
||||||
|
# Rustコンポーネント統合
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option C: Rust for Linuxアプローチ
|
||||||
|
**優点:**
|
||||||
|
- 既存Linuxカーネル利用
|
||||||
|
- Rustドライバー開発可能
|
||||||
|
- 段階的移行可能
|
||||||
|
|
||||||
|
## 2. パッケージビルドシステム設計
|
||||||
|
|
||||||
|
### Rustベースビルドツール実装
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/package_builder.rs
|
||||||
|
use std::process::Command;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub struct PackageBuilder {
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub source_url: String,
|
||||||
|
pub build_deps: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageBuilder {
|
||||||
|
pub async fn download_source(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// HTTP/Git ダウンロード実装
|
||||||
|
let output = Command::new("wget")
|
||||||
|
.arg(&self.source_url)
|
||||||
|
.arg("-O")
|
||||||
|
.arg(format!("{}-{}.tar.gz", self.name, self.version))
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
println!("Downloaded {}", self.name);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_source(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Command::new("tar")
|
||||||
|
.arg("xzf")
|
||||||
|
.arg(format!("{}-{}.tar.gz", self.name, self.version))
|
||||||
|
.output()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let build_dir = format!("{}-{}", self.name, self.version);
|
||||||
|
|
||||||
|
// configure
|
||||||
|
Command::new("./configure")
|
||||||
|
.current_dir(&build_dir)
|
||||||
|
.arg("--prefix=/usr")
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
// make
|
||||||
|
Command::new("make")
|
||||||
|
.current_dir(&build_dir)
|
||||||
|
.arg("-j")
|
||||||
|
.arg(num_cpus::get().to_string())
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
// make install
|
||||||
|
Command::new("make")
|
||||||
|
.current_dir(&build_dir)
|
||||||
|
.arg("install")
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
println!("Built and installed {}", self.name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// パッケージ定義
|
||||||
|
pub fn create_zsh_package() -> PackageBuilder {
|
||||||
|
PackageBuilder {
|
||||||
|
name: "zsh".to_string(),
|
||||||
|
version: "5.9".to_string(),
|
||||||
|
source_url: "https://sourceforge.net/projects/zsh/files/zsh/5.9/zsh-5.9.tar.xz".to_string(),
|
||||||
|
build_deps: vec!["gcc".to_string(), "make".to_string(), "ncurses-dev".to_string()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自動ビルドシステム
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/auto_builder.rs
|
||||||
|
use tokio::process::Command;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BuildRecipe {
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub source: SourceConfig,
|
||||||
|
pub dependencies: Vec<String>,
|
||||||
|
pub build_steps: Vec<String>,
|
||||||
|
pub install_steps: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SourceConfig {
|
||||||
|
pub url: String,
|
||||||
|
pub hash: String,
|
||||||
|
pub extract_cmd: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AutoBuilder {
|
||||||
|
pub recipes_dir: String,
|
||||||
|
pub build_dir: String,
|
||||||
|
pub install_prefix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoBuilder {
|
||||||
|
pub async fn build_package(&self, package_name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// レシピ読み込み
|
||||||
|
let recipe_path = format!("{}/{}.yaml", self.recipes_dir, package_name);
|
||||||
|
let recipe_content = fs::read_to_string(recipe_path)?;
|
||||||
|
let recipe: BuildRecipe = serde_yaml::from_str(&recipe_content)?;
|
||||||
|
|
||||||
|
// 依存関係確認
|
||||||
|
for dep in &recipe.dependencies {
|
||||||
|
self.ensure_dependency(dep).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ソースダウンロード
|
||||||
|
self.download_source(&recipe).await?;
|
||||||
|
|
||||||
|
// ビルド実行
|
||||||
|
self.execute_build(&recipe).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn download_source(&self, recipe: &BuildRecipe) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let output = Command::new("curl")
|
||||||
|
.arg("-L")
|
||||||
|
.arg(&recipe.source.url)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(format!("{}.tar.gz", recipe.name))
|
||||||
|
.output()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err("Download failed".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ハッシュ検証
|
||||||
|
self.verify_hash(&recipe).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute_build(&self, recipe: &BuildRecipe) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
for step in &recipe.build_steps {
|
||||||
|
let output = Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(step)
|
||||||
|
.output()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(format!("Build step failed: {}", step).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. systemd統合戦略
|
||||||
|
|
||||||
|
### Rustサービス実装
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/systemd_service.rs
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub struct SystemdService {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub exec_start: String,
|
||||||
|
pub wants: Vec<String>,
|
||||||
|
pub after: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemdService {
|
||||||
|
pub fn create_unit_file(&self) -> String {
|
||||||
|
format!(
|
||||||
|
r#"[Unit]
|
||||||
|
Description={}
|
||||||
|
{}
|
||||||
|
{}
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart={}
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
"#,
|
||||||
|
self.description,
|
||||||
|
if self.wants.is_empty() { "".to_string() } else { format!("Wants={}", self.wants.join(" ")) },
|
||||||
|
if self.after.is_empty() { "".to_string() } else { format!("After={}", self.after.join(" ")) },
|
||||||
|
self.exec_start
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let unit_content = self.create_unit_file();
|
||||||
|
let unit_path = format!("/etc/systemd/system/{}.service", self.name);
|
||||||
|
|
||||||
|
std::fs::write(unit_path, unit_content)?;
|
||||||
|
|
||||||
|
// systemd reload
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("daemon-reload")
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
// enable service
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("enable")
|
||||||
|
.arg(format!("{}.service", self.name))
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 実装計画
|
||||||
|
|
||||||
|
### Phase 1: 基盤構築
|
||||||
|
1. Redox OSをベースにしたカスタムOS
|
||||||
|
2. 基本的なビルドシステム
|
||||||
|
3. パッケージ管理インフラ
|
||||||
|
|
||||||
|
### Phase 2: systemd統合
|
||||||
|
1. systemdサービス管理
|
||||||
|
2. D-Bus統合
|
||||||
|
3. ネットワーク管理
|
||||||
|
|
||||||
|
### Phase 3: 本格的なパッケージビルド
|
||||||
|
1. ソースダウンロード
|
||||||
|
2. 依存関係解決
|
||||||
|
3. 自動ビルド・インストール
|
||||||
|
|
||||||
|
### Phase 4: AI統合
|
||||||
|
1. LLM統合
|
||||||
|
2. 自動設定
|
||||||
|
3. インテリジェント管理
|
||||||
|
|
||||||
|
この戦略により、段階的に本格的なRust Linux OSを構築できます。
|
196
kernel/src/filesystem.rs
Normal file
196
kernel/src/filesystem.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
//! Simple File System for Aios
|
||||||
|
//!
|
||||||
|
//! Provides basic file operations for package installation and management
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
/// Simple file entry
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct FileEntry {
|
||||||
|
pub name: [u8; 32],
|
||||||
|
pub size: usize,
|
||||||
|
pub file_type: FileType,
|
||||||
|
pub data_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum FileType {
|
||||||
|
Regular,
|
||||||
|
Directory,
|
||||||
|
Executable,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple in-memory file system
|
||||||
|
pub struct SimpleFS {
|
||||||
|
files: [Option<FileEntry>; 64],
|
||||||
|
file_count: usize,
|
||||||
|
data_storage: [u8; 2048], // 2KB storage (reduced from 8KB)
|
||||||
|
data_used: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleFS {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
SimpleFS {
|
||||||
|
files: [None; 64],
|
||||||
|
file_count: 0,
|
||||||
|
data_storage: [0; 2048],
|
||||||
|
data_used: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a file
|
||||||
|
pub fn create_file(&mut self, name: &str, content: &[u8], file_type: FileType) -> Result<(), &'static str> {
|
||||||
|
if self.file_count >= 64 {
|
||||||
|
return Err("File system full");
|
||||||
|
}
|
||||||
|
|
||||||
|
if name.len() > 31 {
|
||||||
|
return Err("Filename too long");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.data_used + content.len() > self.data_storage.len() {
|
||||||
|
return Err("Not enough storage space");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file already exists
|
||||||
|
if self.find_file(name).is_some() {
|
||||||
|
return Err("File already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create file entry
|
||||||
|
let mut file_name = [0u8; 32];
|
||||||
|
let name_bytes = name.as_bytes();
|
||||||
|
for i in 0..name_bytes.len() {
|
||||||
|
file_name[i] = name_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_entry = FileEntry {
|
||||||
|
name: file_name,
|
||||||
|
size: content.len(),
|
||||||
|
file_type,
|
||||||
|
data_offset: self.data_used,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store file data
|
||||||
|
for i in 0..content.len() {
|
||||||
|
self.data_storage[self.data_used + i] = content[i];
|
||||||
|
}
|
||||||
|
self.data_used += content.len();
|
||||||
|
|
||||||
|
// Add file entry
|
||||||
|
self.files[self.file_count] = Some(file_entry);
|
||||||
|
self.file_count += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a file by name
|
||||||
|
pub fn find_file(&self, name: &str) -> Option<&FileEntry> {
|
||||||
|
for i in 0..self.file_count {
|
||||||
|
if let Some(file) = &self.files[i] {
|
||||||
|
if self.str_eq(&file.name, name) {
|
||||||
|
return Some(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all files
|
||||||
|
pub fn list_files(&self) -> &[Option<FileEntry>] {
|
||||||
|
&self.files[..self.file_count]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read file content
|
||||||
|
pub fn read_file(&self, name: &str) -> Option<&[u8]> {
|
||||||
|
if let Some(file) = self.find_file(name) {
|
||||||
|
let start = file.data_offset;
|
||||||
|
let end = start + file.size;
|
||||||
|
Some(&self.data_storage[start..end])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a file
|
||||||
|
pub fn delete_file(&mut self, name: &str) -> Result<(), &'static str> {
|
||||||
|
let mut found_index = None;
|
||||||
|
for i in 0..self.file_count {
|
||||||
|
if let Some(file) = &self.files[i] {
|
||||||
|
if self.str_eq(&file.name, name) {
|
||||||
|
found_index = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(index) = found_index {
|
||||||
|
// Remove file by shifting array
|
||||||
|
for i in index..self.file_count.saturating_sub(1) {
|
||||||
|
self.files[i] = self.files[i + 1];
|
||||||
|
}
|
||||||
|
self.files[self.file_count - 1] = None;
|
||||||
|
self.file_count -= 1;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("File not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get file count
|
||||||
|
pub fn file_count(&self) -> usize {
|
||||||
|
self.file_count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to compare string with byte array
|
||||||
|
fn str_eq(&self, arr: &[u8; 32], s: &str) -> bool {
|
||||||
|
let s_bytes = s.as_bytes();
|
||||||
|
if s_bytes.len() > 32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the end of the string in the array (null terminator)
|
||||||
|
let mut arr_len = 0;
|
||||||
|
for i in 0..32 {
|
||||||
|
if arr[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
arr_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if arr_len != s_bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..arr_len {
|
||||||
|
if arr[i] != s_bytes[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get filename as string
|
||||||
|
pub fn filename_str(file: &FileEntry) -> &str {
|
||||||
|
let mut len = 0;
|
||||||
|
for i in 0..32 {
|
||||||
|
if file.name[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::str::from_utf8(&file.name[..len]).unwrap_or("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileType {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FileType::Regular => "file",
|
||||||
|
FileType::Directory => "dir",
|
||||||
|
FileType::Executable => "exec",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![allow(static_mut_refs)]
|
||||||
|
|
||||||
mod keyboard_basic;
|
mod keyboard_basic;
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ fn execute_command() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear input buffer
|
// Clear input buffer
|
||||||
INPUT_BUFFER.fill(0);
|
core::ptr::write_bytes(INPUT_BUFFER.as_mut_ptr(), 0, INPUT_BUFFER.len());
|
||||||
INPUT_POS = 0;
|
INPUT_POS = 0;
|
||||||
|
|
||||||
new_line();
|
new_line();
|
||||||
@ -196,7 +197,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
|
|
||||||
// Panic handler
|
// Panic handler
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
clear_screen();
|
clear_screen();
|
||||||
print_str("!!! KERNEL PANIC !!!", 29, 12);
|
print_str("!!! KERNEL PANIC !!!", 29, 12);
|
||||||
print_str("System halted.", 33, 13);
|
print_str("System halted.", 33, 13);
|
||||||
|
380
kernel/src/package.rs
Normal file
380
kernel/src/package.rs
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
//! Package Manager for Aios
|
||||||
|
//!
|
||||||
|
//! Simple package management system inspired by Redox pkgutils
|
||||||
|
//! Provides basic package operations: install, remove, list, update
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
use core::fmt;
|
||||||
|
use crate::filesystem::{SimpleFS, FileType};
|
||||||
|
|
||||||
|
/// Package metadata structure
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Package {
|
||||||
|
pub name: [u8; 32], // Fixed-size name
|
||||||
|
pub version: [u8; 16], // Fixed-size version
|
||||||
|
pub description: [u8; 64], // Fixed-size description
|
||||||
|
pub status: PackageStatus,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Package status
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum PackageStatus {
|
||||||
|
Installed,
|
||||||
|
Available,
|
||||||
|
Broken,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Package manager error types
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PackageError {
|
||||||
|
NotFound,
|
||||||
|
AlreadyInstalled,
|
||||||
|
NotInstalled,
|
||||||
|
InvalidName,
|
||||||
|
IoError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PackageError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PackageError::NotFound => write!(f, "Package not found"),
|
||||||
|
PackageError::AlreadyInstalled => write!(f, "Package already installed"),
|
||||||
|
PackageError::NotInstalled => write!(f, "Package not installed"),
|
||||||
|
PackageError::InvalidName => write!(f, "Invalid package name"),
|
||||||
|
PackageError::IoError => write!(f, "I/O error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Package Manager
|
||||||
|
pub struct PackageManager {
|
||||||
|
installed_packages: [Option<Package>; 32], // Simple fixed-size array
|
||||||
|
package_count: usize,
|
||||||
|
filesystem: SimpleFS,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageManager {
|
||||||
|
/// Create new package manager
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PackageManager {
|
||||||
|
installed_packages: [None; 32],
|
||||||
|
package_count: 0,
|
||||||
|
filesystem: SimpleFS::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package
|
||||||
|
pub fn install(&mut self, name: &str) -> Result<(), PackageError> {
|
||||||
|
// Check if package name is valid
|
||||||
|
if name.is_empty() || name.len() > 32 {
|
||||||
|
return Err(PackageError::InvalidName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already installed
|
||||||
|
if self.is_installed(name) {
|
||||||
|
return Err(PackageError::AlreadyInstalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have space
|
||||||
|
if self.package_count >= 32 {
|
||||||
|
return Err(PackageError::IoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create package from predefined list
|
||||||
|
let package = self.create_package(name)?;
|
||||||
|
|
||||||
|
// Install package files to filesystem
|
||||||
|
self.install_package_files(name)?;
|
||||||
|
|
||||||
|
// Add to installed packages
|
||||||
|
self.installed_packages[self.package_count] = Some(package);
|
||||||
|
self.package_count += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a package
|
||||||
|
pub fn remove(&mut self, name: &str) -> Result<(), PackageError> {
|
||||||
|
// Find package
|
||||||
|
let mut found_index = None;
|
||||||
|
for (i, pkg) in self.installed_packages.iter().enumerate() {
|
||||||
|
if let Some(package) = pkg {
|
||||||
|
if self.str_eq(&package.name, name) {
|
||||||
|
found_index = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match found_index {
|
||||||
|
Some(index) => {
|
||||||
|
// Remove package files from filesystem
|
||||||
|
let _ = self.remove_package_files(name);
|
||||||
|
|
||||||
|
// Remove package by shifting array
|
||||||
|
for i in index..self.package_count.saturating_sub(1) {
|
||||||
|
self.installed_packages[i] = self.installed_packages[i + 1].take();
|
||||||
|
}
|
||||||
|
self.package_count -= 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
None => Err(PackageError::NotInstalled),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List installed packages
|
||||||
|
pub fn list(&self) -> &[Option<Package>] {
|
||||||
|
&self.installed_packages[..self.package_count]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if package is installed
|
||||||
|
pub fn is_installed(&self, name: &str) -> bool {
|
||||||
|
self.installed_packages[..self.package_count]
|
||||||
|
.iter()
|
||||||
|
.any(|pkg| {
|
||||||
|
if let Some(package) = pkg {
|
||||||
|
self.str_eq(&package.name, name)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get package count
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.package_count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to compare string with byte array
|
||||||
|
fn str_eq(&self, arr: &[u8; 32], s: &str) -> bool {
|
||||||
|
let s_bytes = s.as_bytes();
|
||||||
|
if s_bytes.len() > 32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the end of the string in the array (null terminator)
|
||||||
|
let mut arr_len = 0;
|
||||||
|
for i in 0..32 {
|
||||||
|
if arr[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
arr_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if arr_len != s_bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..arr_len {
|
||||||
|
if arr[i] != s_bytes[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy string to byte array
|
||||||
|
fn copy_str_to_array<const N: usize>(&self, s: &str) -> [u8; N] {
|
||||||
|
let mut arr = [0u8; N];
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
let len = core::cmp::min(bytes.len(), N - 1); // Leave space for null terminator
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
arr[i] = bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a package from predefined list
|
||||||
|
fn create_package(&self, name: &str) -> Result<Package, PackageError> {
|
||||||
|
match name {
|
||||||
|
"vim" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("vim"),
|
||||||
|
version: self.copy_str_to_array("8.2.0"),
|
||||||
|
description: self.copy_str_to_array("Vi IMproved text editor"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 1024,
|
||||||
|
}),
|
||||||
|
"git" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("git"),
|
||||||
|
version: self.copy_str_to_array("2.39.0"),
|
||||||
|
description: self.copy_str_to_array("Git version control system"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 2048,
|
||||||
|
}),
|
||||||
|
"curl" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("curl"),
|
||||||
|
version: self.copy_str_to_array("7.88.0"),
|
||||||
|
description: self.copy_str_to_array("Command line tool for transferring data"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 512,
|
||||||
|
}),
|
||||||
|
"rust" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("rust"),
|
||||||
|
version: self.copy_str_to_array("1.75.0"),
|
||||||
|
description: self.copy_str_to_array("Rust programming language"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 4096,
|
||||||
|
}),
|
||||||
|
"python" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("python"),
|
||||||
|
version: self.copy_str_to_array("3.11.0"),
|
||||||
|
description: self.copy_str_to_array("Python programming language"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 3072,
|
||||||
|
}),
|
||||||
|
"claude" => Ok(Package {
|
||||||
|
name: self.copy_str_to_array("claude"),
|
||||||
|
version: self.copy_str_to_array("1.0.0"),
|
||||||
|
description: self.copy_str_to_array("Claude AI integration service"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
size: 1536,
|
||||||
|
}),
|
||||||
|
_ => Err(PackageError::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install package files to filesystem
|
||||||
|
fn install_package_files(&mut self, name: &str) -> Result<(), PackageError> {
|
||||||
|
match name {
|
||||||
|
"vim" => {
|
||||||
|
// Create vim executable
|
||||||
|
let vim_content = b"#!/bin/sh\necho 'Vi IMproved - text editor'\necho 'Usage: vim [file]'\n";
|
||||||
|
self.filesystem.create_file("/bin/vim", vim_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
|
||||||
|
// Create vim config
|
||||||
|
let vimrc_content = b"\" Vim configuration\nset number\nset autoindent\nsyntax on\n";
|
||||||
|
self.filesystem.create_file("/etc/vimrc", vimrc_content, FileType::Regular)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
"git" => {
|
||||||
|
let git_content = b"#!/bin/sh\necho 'Git - version control system'\necho 'Usage: git [command]'\n";
|
||||||
|
self.filesystem.create_file("/bin/git", git_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
|
||||||
|
let gitconfig_content = b"[core]\n editor = vim\n[user]\n name = Aios User\n";
|
||||||
|
self.filesystem.create_file("/etc/gitconfig", gitconfig_content, FileType::Regular)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
"curl" => {
|
||||||
|
let curl_content = b"#!/bin/sh\necho 'cURL - data transfer tool'\necho 'Usage: curl [URL]'\n";
|
||||||
|
self.filesystem.create_file("/bin/curl", curl_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
"rust" => {
|
||||||
|
let rustc_content = b"#!/bin/sh\necho 'Rust Compiler'\necho 'Usage: rustc [file.rs]'\n";
|
||||||
|
self.filesystem.create_file("/bin/rustc", rustc_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
|
||||||
|
let cargo_content = b"#!/bin/sh\necho 'Cargo - Rust package manager'\necho 'Usage: cargo [command]'\n";
|
||||||
|
self.filesystem.create_file("/bin/cargo", cargo_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
"python" => {
|
||||||
|
let python_content = b"#!/bin/sh\necho 'Python interpreter'\necho 'Usage: python [script.py]'\n";
|
||||||
|
self.filesystem.create_file("/bin/python", python_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
|
||||||
|
let pip_content = b"#!/bin/sh\necho 'pip - Python package installer'\necho 'Usage: pip install [package]'\n";
|
||||||
|
self.filesystem.create_file("/bin/pip", pip_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
"claude" => {
|
||||||
|
let claude_content = b"#!/bin/sh\necho 'Claude AI Assistant'\necho 'Usage: claude [query]'\n";
|
||||||
|
self.filesystem.create_file("/bin/claude", claude_content, FileType::Executable)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
|
||||||
|
let config_content = b"# Claude AI Configuration\napi_endpoint=https://api.anthropic.com\nmodel=claude-3-sonnet\n";
|
||||||
|
self.filesystem.create_file("/etc/claude.conf", config_content, FileType::Regular)
|
||||||
|
.map_err(|_| PackageError::IoError)?;
|
||||||
|
}
|
||||||
|
_ => return Err(PackageError::NotFound),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove package files from filesystem
|
||||||
|
fn remove_package_files(&mut self, name: &str) -> Result<(), PackageError> {
|
||||||
|
match name {
|
||||||
|
"vim" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/vim");
|
||||||
|
let _ = self.filesystem.delete_file("/etc/vimrc");
|
||||||
|
}
|
||||||
|
"git" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/git");
|
||||||
|
let _ = self.filesystem.delete_file("/etc/gitconfig");
|
||||||
|
}
|
||||||
|
"curl" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/curl");
|
||||||
|
}
|
||||||
|
"rust" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/rustc");
|
||||||
|
let _ = self.filesystem.delete_file("/bin/cargo");
|
||||||
|
}
|
||||||
|
"python" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/python");
|
||||||
|
let _ = self.filesystem.delete_file("/bin/pip");
|
||||||
|
}
|
||||||
|
"claude" => {
|
||||||
|
let _ = self.filesystem.delete_file("/bin/claude");
|
||||||
|
let _ = self.filesystem.delete_file("/etc/claude.conf");
|
||||||
|
}
|
||||||
|
_ => return Err(PackageError::NotFound),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List installed files
|
||||||
|
pub fn list_files(&self) -> &[Option<crate::filesystem::FileEntry>] {
|
||||||
|
self.filesystem.list_files()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get filesystem reference
|
||||||
|
pub fn filesystem(&self) -> &SimpleFS {
|
||||||
|
&self.filesystem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package {
|
||||||
|
/// Get package name as string slice
|
||||||
|
pub fn name_str(&self) -> &str {
|
||||||
|
// Find null terminator
|
||||||
|
let mut len = 0;
|
||||||
|
for i in 0..32 {
|
||||||
|
if self.name[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe to unwrap since we control the input
|
||||||
|
core::str::from_utf8(&self.name[..len]).unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get package version as string slice
|
||||||
|
pub fn version_str(&self) -> &str {
|
||||||
|
let mut len = 0;
|
||||||
|
for i in 0..16 {
|
||||||
|
if self.version[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::str::from_utf8(&self.version[..len]).unwrap_or("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PackageStatus {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
PackageStatus::Installed => write!(f, "installed"),
|
||||||
|
PackageStatus::Available => write!(f, "available"),
|
||||||
|
PackageStatus::Broken => write!(f, "broken"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
231
kernel/src/simple_package.rs
Normal file
231
kernel/src/simple_package.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
//! Lightweight Package Manager for Aios
|
||||||
|
//!
|
||||||
|
//! Memory-safe package management system with minimal memory footprint
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
/// Lightweight package entry
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct LightPackage {
|
||||||
|
pub name: [u8; 16], // Reduced from 32 to 16 bytes
|
||||||
|
pub version: [u8; 8], // Reduced from 16 to 8 bytes
|
||||||
|
pub status: PackageStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Package status
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum PackageStatus {
|
||||||
|
Installed,
|
||||||
|
Available,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lightweight Package Manager
|
||||||
|
pub struct LightPackageManager {
|
||||||
|
packages: [Option<LightPackage>; 8], // Reduced from 32 to 8 packages
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightPackageManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
LightPackageManager {
|
||||||
|
packages: [None; 8],
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a package (lightweight - no actual files)
|
||||||
|
pub fn install(&mut self, name: &str) -> Result<(), &'static str> {
|
||||||
|
if name.len() > 15 {
|
||||||
|
return Err("Package name too long");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.count >= 8 {
|
||||||
|
return Err("Package manager full");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already installed
|
||||||
|
if self.is_installed(name) {
|
||||||
|
return Err("Already installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create package entry
|
||||||
|
let package = self.create_package(name)?;
|
||||||
|
self.packages[self.count] = Some(package);
|
||||||
|
self.count += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a package
|
||||||
|
pub fn remove(&mut self, name: &str) -> Result<(), &'static str> {
|
||||||
|
let mut found_index = None;
|
||||||
|
for (i, pkg) in self.packages.iter().enumerate() {
|
||||||
|
if let Some(package) = pkg {
|
||||||
|
if self.str_eq(&package.name, name) {
|
||||||
|
found_index = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match found_index {
|
||||||
|
Some(index) => {
|
||||||
|
// Remove package by shifting array
|
||||||
|
for i in index..self.count.saturating_sub(1) {
|
||||||
|
self.packages[i] = self.packages[i + 1];
|
||||||
|
}
|
||||||
|
self.packages[self.count - 1] = None;
|
||||||
|
self.count -= 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
None => Err("Package not installed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List installed packages
|
||||||
|
pub fn list(&self) -> &[Option<LightPackage>] {
|
||||||
|
&self.packages[..self.count]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if package is installed
|
||||||
|
pub fn is_installed(&self, name: &str) -> bool {
|
||||||
|
self.packages[..self.count]
|
||||||
|
.iter()
|
||||||
|
.any(|pkg| {
|
||||||
|
if let Some(package) = pkg {
|
||||||
|
self.str_eq(&package.name, name)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get package count
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a package from predefined list
|
||||||
|
fn create_package(&self, name: &str) -> Result<LightPackage, &'static str> {
|
||||||
|
match name {
|
||||||
|
"vim" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("vim"),
|
||||||
|
version: self.copy_str_to_array::<8>("8.2"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"git" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("git"),
|
||||||
|
version: self.copy_str_to_array::<8>("2.39"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"curl" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("curl"),
|
||||||
|
version: self.copy_str_to_array::<8>("7.88"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"rust" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("rust"),
|
||||||
|
version: self.copy_str_to_array::<8>("1.75"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"python" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("python"),
|
||||||
|
version: self.copy_str_to_array::<8>("3.11"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"claude" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("claude"),
|
||||||
|
version: self.copy_str_to_array::<8>("1.0"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"zsh" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("zsh"),
|
||||||
|
version: self.copy_str_to_array::<8>("5.9"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"bash" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("bash"),
|
||||||
|
version: self.copy_str_to_array::<8>("5.2"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
"fish" => Ok(LightPackage {
|
||||||
|
name: self.copy_str_to_array::<16>("fish"),
|
||||||
|
version: self.copy_str_to_array::<8>("3.6"),
|
||||||
|
status: PackageStatus::Installed,
|
||||||
|
}),
|
||||||
|
_ => Err("Package not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to compare string with byte array
|
||||||
|
fn str_eq(&self, arr: &[u8; 16], s: &str) -> bool {
|
||||||
|
let s_bytes = s.as_bytes();
|
||||||
|
if s_bytes.len() > 16 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the end of the string in the array (null terminator)
|
||||||
|
let mut arr_len = 0;
|
||||||
|
for i in 0..16 {
|
||||||
|
if arr[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
arr_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if arr_len != s_bytes.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..arr_len {
|
||||||
|
if arr[i] != s_bytes[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy string to byte array
|
||||||
|
fn copy_str_to_array<const N: usize>(&self, s: &str) -> [u8; N] {
|
||||||
|
let mut arr = [0u8; N];
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
let len = core::cmp::min(bytes.len(), N - 1); // Leave space for null terminator
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
arr[i] = bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightPackage {
|
||||||
|
/// Get package name as string slice
|
||||||
|
pub fn name_str(&self) -> &str {
|
||||||
|
// Find null terminator
|
||||||
|
let mut len = 0;
|
||||||
|
for i in 0..16 {
|
||||||
|
if self.name[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe to unwrap since we control the input
|
||||||
|
core::str::from_utf8(&self.name[..len]).unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get package version as string slice
|
||||||
|
pub fn version_str(&self) -> &str {
|
||||||
|
let mut len = 0;
|
||||||
|
for i in 0..8 {
|
||||||
|
if self.version[i] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::str::from_utf8(&self.version[..len]).unwrap_or("")
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user