From 4a5c50ab765e962e86a480f1a45cddfa741efd2a Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Wed, 21 Jan 2026 17:08:35 +0000 Subject: [PATCH] feat: Add SparkOS Co-Authored-By: Claude Opus 4.5 --- sparkos/.dockerignore | 49 ++ sparkos/.github/workflows/build-image.yml | 267 ++++++++++ sparkos/.github/workflows/docker-publish.yml | 115 +++++ sparkos/.github/workflows/release.yml | 169 ++++++ sparkos/.gitignore | 74 +++ sparkos/ARCHITECTURE.md | 153 ++++++ sparkos/CONTRIBUTING.md | 76 +++ sparkos/Dockerfile | 48 ++ sparkos/Dockerfile.image | 36 ++ sparkos/INIT_VERIFICATION.md | 111 ++++ sparkos/LICENSE | 21 + sparkos/Makefile | 77 +++ sparkos/README.md | 482 ++++++++++++++++++ sparkos/RELEASING.md | 233 +++++++++ sparkos/config/RELEASE_README.md | 59 +++ sparkos/config/build.conf | 30 ++ sparkos/config/grub-embedded.cfg.in | 36 ++ sparkos/config/grub.cfg.in | 42 ++ sparkos/config/image-readme.txt | 23 + sparkos/config/image-release-readme.txt | 74 +++ sparkos/docker-compose.yml | 11 + sparkos/rootfs/README.txt | 41 ++ sparkos/rootfs/etc/fstab | 5 + sparkos/rootfs/etc/hostname | 1 + sparkos/rootfs/etc/hosts | 3 + sparkos/rootfs/etc/network/interfaces | 10 + sparkos/rootfs/etc/profile | 17 + sparkos/rootfs/etc/resolv.conf | 6 + sparkos/rootfs/home/spark/.profile | 12 + sparkos/rootfs/home/spark/clone-sparkos.sh | 50 ++ sparkos/rootfs/root/.profile | 12 + sparkos/scripts/build-image.sh | 67 +++ sparkos/scripts/build.sh | 37 ++ sparkos/scripts/create_image.sh | 132 +++++ .../scripts/docker-image-create-uefi-old.sh | 148 ++++++ sparkos/scripts/docker-image-create-uefi.sh | 129 +++++ .../scripts/docker-image-download-kernel.sh | 71 +++ sparkos/scripts/docker-image-install-tools.sh | 29 ++ sparkos/scripts/docker-install-packages.sh | 6 + sparkos/scripts/docker-release.sh | 183 +++++++ sparkos/scripts/docker-setup-config.sh | 19 + sparkos/scripts/docker-setup-rootfs.sh | 25 + sparkos/scripts/install.sh | 228 +++++++++ sparkos/scripts/setup_rootfs.sh | 120 +++++ sparkos/scripts/test.sh | 45 ++ sparkos/src/init.c | 261 ++++++++++ sparkos/src/qt6-app/CMakeLists.txt | 42 ++ sparkos/src/qt6-app/main.cpp | 188 +++++++ 48 files changed, 4073 insertions(+) create mode 100644 sparkos/.dockerignore create mode 100644 sparkos/.github/workflows/build-image.yml create mode 100644 sparkos/.github/workflows/docker-publish.yml create mode 100644 sparkos/.github/workflows/release.yml create mode 100644 sparkos/.gitignore create mode 100644 sparkos/ARCHITECTURE.md create mode 100644 sparkos/CONTRIBUTING.md create mode 100644 sparkos/Dockerfile create mode 100644 sparkos/Dockerfile.image create mode 100644 sparkos/INIT_VERIFICATION.md create mode 100644 sparkos/LICENSE create mode 100644 sparkos/Makefile create mode 100644 sparkos/README.md create mode 100644 sparkos/RELEASING.md create mode 100644 sparkos/config/RELEASE_README.md create mode 100644 sparkos/config/build.conf create mode 100644 sparkos/config/grub-embedded.cfg.in create mode 100644 sparkos/config/grub.cfg.in create mode 100644 sparkos/config/image-readme.txt create mode 100644 sparkos/config/image-release-readme.txt create mode 100644 sparkos/docker-compose.yml create mode 100644 sparkos/rootfs/README.txt create mode 100644 sparkos/rootfs/etc/fstab create mode 100644 sparkos/rootfs/etc/hostname create mode 100644 sparkos/rootfs/etc/hosts create mode 100644 sparkos/rootfs/etc/network/interfaces create mode 100644 sparkos/rootfs/etc/profile create mode 100644 sparkos/rootfs/etc/resolv.conf create mode 100644 sparkos/rootfs/home/spark/.profile create mode 100755 sparkos/rootfs/home/spark/clone-sparkos.sh create mode 100644 sparkos/rootfs/root/.profile create mode 100755 sparkos/scripts/build-image.sh create mode 100755 sparkos/scripts/build.sh create mode 100755 sparkos/scripts/create_image.sh create mode 100755 sparkos/scripts/docker-image-create-uefi-old.sh create mode 100755 sparkos/scripts/docker-image-create-uefi.sh create mode 100755 sparkos/scripts/docker-image-download-kernel.sh create mode 100755 sparkos/scripts/docker-image-install-tools.sh create mode 100755 sparkos/scripts/docker-install-packages.sh create mode 100755 sparkos/scripts/docker-release.sh create mode 100755 sparkos/scripts/docker-setup-config.sh create mode 100755 sparkos/scripts/docker-setup-rootfs.sh create mode 100755 sparkos/scripts/install.sh create mode 100755 sparkos/scripts/setup_rootfs.sh create mode 100644 sparkos/scripts/test.sh create mode 100644 sparkos/src/init.c create mode 100644 sparkos/src/qt6-app/CMakeLists.txt create mode 100644 sparkos/src/qt6-app/main.cpp diff --git a/sparkos/.dockerignore b/sparkos/.dockerignore new file mode 100644 index 000000000..8cfd48585 --- /dev/null +++ b/sparkos/.dockerignore @@ -0,0 +1,49 @@ +# Git +.git +.gitignore +.github + +# Documentation +*.md +!README.md +ARCHITECTURE.md +CONTRIBUTING.md +LICENSE + +# Build artifacts +init +*.o +*.a +*.so +*.img +*.iso +build/ + +# Generated rootfs contents (we build our own in Docker) +rootfs/bin/* +rootfs/sbin/init +rootfs/usr/ +rootfs/lib/ +rootfs/lib64/ +rootfs/boot/ + +# Keep rootfs structure +!rootfs/etc/ +!rootfs/root/ +!rootfs/home/ + +# Temporary files +*.tmp +*.bak +/tmp/ + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db diff --git a/sparkos/.github/workflows/build-image.yml b/sparkos/.github/workflows/build-image.yml new file mode 100644 index 000000000..d91532f80 --- /dev/null +++ b/sparkos/.github/workflows/build-image.yml @@ -0,0 +1,267 @@ +name: Build and Release Disk Image + +on: + push: + branches: + - main + - develop + tags: + - 'v*' + pull_request: + branches: + - main + - develop + workflow_dispatch: + +jobs: + build-image: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build disk image using Docker + run: | + echo "Building SparkOS disk image..." + # Build the image builder container + docker buildx build \ + --file Dockerfile.image \ + --target image-builder \ + --tag sparkos:image-builder \ + --load \ + . + + # Create a container and extract the image + echo "Extracting disk image..." + CONTAINER_ID=$(docker create sparkos:image-builder) + docker cp "$CONTAINER_ID:/output/sparkos.img.gz" ./sparkos.img.gz + docker rm "$CONTAINER_ID" + + echo "Disk image created successfully!" + ls -lh sparkos.img.gz + + - name: Verify disk image + run: | + echo "Verifying UEFI-bootable disk image..." + if [ ! -f sparkos.img.gz ]; then + echo "ERROR: Disk image not found!" + exit 1 + fi + + # Show file info + ls -lh sparkos.img.gz + file sparkos.img.gz + + # Decompress and check + echo "Decompressing to verify..." + gunzip -c sparkos.img.gz > sparkos.img + ls -lh sparkos.img + file sparkos.img + + # Show partition table (should be GPT) + echo "" + echo "=== Partition Table (GPT) ===" + sudo fdisk -l sparkos.img || true + sudo parted sparkos.img print || true + + # Try to mount and inspect partitions + echo "" + echo "=== Inspecting Partitions ===" + mkdir -p /tmp/sparkos_esp /tmp/sparkos_root + + # Set up loop device + LOOP_DEV=$(sudo losetup -fP --show sparkos.img) + echo "Loop device: $LOOP_DEV" + ls -la ${LOOP_DEV}* + + # Mount and check ESP + if sudo mount -o ro ${LOOP_DEV}p1 /tmp/sparkos_esp 2>/dev/null; then + echo "✓ EFI System Partition mounted" + echo "ESP contents:" + ls -laR /tmp/sparkos_esp/ + if [ -f /tmp/sparkos_esp/EFI/BOOT/BOOTX64.EFI ]; then + echo "✓ UEFI bootloader (GRUB) found" + fi + if [ -f /tmp/sparkos_esp/boot/vmlinuz ]; then + echo "✓ Kernel found" + fi + sudo umount /tmp/sparkos_esp + fi + + # Mount and check root partition + if sudo mount -o ro ${LOOP_DEV}p2 /tmp/sparkos_root 2>/dev/null; then + echo "✓ Root partition mounted" + echo "Root contents:" + ls -la /tmp/sparkos_root/ + if [ -f /tmp/sparkos_root/sbin/init ]; then + echo "✓ Init binary found" + ls -lh /tmp/sparkos_root/sbin/init + fi + if [ -f /tmp/sparkos_root/bin/busybox ]; then + echo "✓ Busybox found" + fi + sudo umount /tmp/sparkos_root + fi + + # Cleanup + sudo losetup -d $LOOP_DEV + + echo "" + echo "=== Verification Complete ===" + echo "✓ UEFI-bootable image with GPT partition table" + echo "✓ EFI System Partition with GRUB" + echo "✓ Root partition with init system and busybox" + + - name: Create release package + run: | + echo "Creating release package..." + mkdir -p release + + # Move the compressed image + mv sparkos.img.gz release/ + + # Copy the README for the release + cp config/image-release-readme.txt release/README.txt + + # Create a ZIP with the image and README + cd release || exit 1 + zip sparkos-image.zip sparkos.img.gz README.txt + cd .. || exit 1 + + echo "Release package created!" + ls -lh release/ + + - name: Upload image artifact + uses: actions/upload-artifact@v4 + with: + name: sparkos-image-${{ github.sha }} + path: release/sparkos-image.zip + retention-days: 90 + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: release/sparkos-image.zip + body: | + # SparkOS UEFI-Bootable Disk Image Release ${{ github.ref_name }} + + This release includes a **UEFI-bootable** disk image with SparkOS. + + ## What's Included + + - **sparkos-image.zip**: Complete package with compressed disk image and README + + ## Features + + ✅ **UEFI Boot Support** - Boot on modern UEFI systems + ✅ **GPT Partition Table** - Modern partitioning scheme + ✅ **GRUB Bootloader** - Reliable UEFI bootloader + ✅ **Linux Kernel** - Full kernel included + ✅ **SparkOS Init System** - Custom init with busybox + ✅ **Ready to Boot** - Write to USB and boot immediately + + ## Quick Start + + ### Write to USB and Boot + + ```bash + # Download and extract the package + wget https://github.com/johndoe6345789/SparkOS/releases/download/${{ github.ref_name }}/sparkos-image.zip + unzip sparkos-image.zip + + # Decompress the image + gunzip sparkos.img.gz + + # Write to USB drive (Linux - BE CAREFUL!) + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync + ``` + + **⚠️ WARNING**: Replace `/dev/sdX` with your actual USB device (e.g., `/dev/sdb`). This will **ERASE ALL DATA** on the target drive! + + ### Boot Instructions + + 1. Insert the USB drive into a UEFI-capable system + 2. Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) + 3. Select the USB drive as boot device + 4. SparkOS will boot automatically after 3 seconds + + ### Inspect Partitions (Advanced) + + ```bash + # Set up loop device + LOOP_DEV=$(sudo losetup -fP --show sparkos.img) + + # Mount ESP (EFI System Partition) + sudo mount ${LOOP_DEV}p1 /mnt + ls -la /mnt/EFI # View bootloader and kernel + sudo umount /mnt + + # Mount root partition + sudo mount ${LOOP_DEV}p2 /mnt + ls -la /mnt # View SparkOS filesystem + sudo umount /mnt + + # Cleanup + sudo losetup -d $LOOP_DEV + ``` + + ## Technical Details + + - **Size**: ~1GB (compressed) + - **Partition Table**: GPT + - **ESP**: 200MB FAT32 (EFI System Partition) + - **Root**: ~800MB ext4 + - **Bootloader**: GRUB (UEFI) + - **Kernel**: Linux kernel (from Ubuntu) + - **Init**: SparkOS custom init system + - **Shell**: Busybox + + ## Docker Image + + A Docker image is also available: + ```bash + docker pull ghcr.io/johndoe6345789/sparkos:${{ github.ref_name }} + ``` + + ## Documentation + + See [README.md](https://github.com/johndoe6345789/SparkOS/blob/main/README.md) for full documentation. + + ## What's Changed + + See commit history for detailed changes. + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Output summary + run: | + echo "### Disk Image Build Summary 💾" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** ✅ Success" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Disk Image:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh release/sparkos.img.gz >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Release Package:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh release/sparkos-image.zip >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + echo "**Release:** Created GitHub release for ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Download from: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + else + echo "**Artifact:** Uploaded as workflow artifact (available for 90 days)" >> $GITHUB_STEP_SUMMARY + fi diff --git a/sparkos/.github/workflows/docker-publish.yml b/sparkos/.github/workflows/docker-publish.yml new file mode 100644 index 000000000..9fc4df3cb --- /dev/null +++ b/sparkos/.github/workflows/docker-publish.yml @@ -0,0 +1,115 @@ +name: Docker Build and Publish + +on: + push: + branches: + - main + - develop + tags: + - 'v*' + pull_request: + branches: + - main + - develop + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-test: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Test Docker image + run: | + set -e # Exit immediately if a command exits with a non-zero status + echo "Testing SparkOS Docker image..." + # Get the first tag from the metadata output + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + echo "Using image: $IMAGE_TAG" + + # For PRs, image is loaded locally; for branches, pull from GHCR + if [ "${{ github.event_name }}" != "pull_request" ]; then + echo "Pulling image from GHCR for testing..." + docker pull "$IMAGE_TAG" + fi + + # Run the container and check output + echo "Running container entrypoint test..." + if ! docker run --rm "$IMAGE_TAG"; then + echo "ERROR: Container entrypoint test failed!" + exit 1 + fi + + # Verify init binary exists and is executable + echo "Verifying init binary is executable..." + if ! docker run --rm "$IMAGE_TAG" sh -c "test -x /sparkos/rootfs/sbin/init && echo 'Init binary is executable'"; then + echo "ERROR: Init binary verification failed!" + exit 1 + fi + + echo "Docker image test completed successfully!" + + - name: Output image details + if: github.event_name != 'pull_request' + run: | + echo "### Docker Image Published! 🐳" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Registry:** ${{ env.REGISTRY }}" >> $GITHUB_STEP_SUMMARY + echo "**Repository:** ${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Tags:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Pull command:**" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/sparkos/.github/workflows/release.yml b/sparkos/.github/workflows/release.yml new file mode 100644 index 000000000..5b514bb23 --- /dev/null +++ b/sparkos/.github/workflows/release.yml @@ -0,0 +1,169 @@ +name: Build and Release + +on: + push: + branches: + - main + - develop + tags: + - 'v*' + pull_request: + branches: + - main + - develop + workflow_dispatch: + +jobs: + build-release: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up build environment + run: | + sudo apt-get update + sudo apt-get install -y gcc make + + - name: Build init binary + run: | + echo "Building SparkOS init system..." + make init + echo "Build complete!" + + - name: Verify build + run: | + echo "Verifying init binary..." + if [ ! -f init ]; then + echo "ERROR: init binary not found!" + exit 1 + fi + ls -lh init + file init + ldd init 2>&1 || echo "Static binary (no dependencies)" + echo "Verification complete!" + + - name: Prepare release package + run: | + echo "Preparing release package..." + mkdir -p release/sparkos + + # Copy compiled binary + cp init release/sparkos/ + + # Copy essential files + cp README.md release/sparkos/ + cp LICENSE release/sparkos/ + cp ARCHITECTURE.md release/sparkos/ + cp CONTRIBUTING.md release/sparkos/ + cp Makefile release/sparkos/ + + # Copy source for reference + cp -r src release/sparkos/ + + # Copy scripts + cp -r scripts release/sparkos/ + + # Copy config + cp -r config release/sparkos/ + + # Copy rootfs structure (without generated content) + mkdir -p release/sparkos/rootfs + # Copy rootfs directories if they exist (some may not be populated) + for dir in etc root home; do + if [ -d "rootfs/$dir" ]; then + cp -r "rootfs/$dir" release/sparkos/rootfs/ + else + echo "Note: rootfs/$dir does not exist, skipping" + fi + done + + # Copy README for the release + cp config/RELEASE_README.md release/sparkos/RELEASE_README.md + + echo "Package prepared in release/sparkos/" + ls -la release/sparkos/ + + - name: Create release archive + run: | + echo "Creating release archive..." + cd release + zip -r ../sparkos-release.zip sparkos/ + cd .. + echo "Archive created!" + ls -lh sparkos-release.zip + unzip -l sparkos-release.zip | head -30 + + - name: Upload release artifact + uses: actions/upload-artifact@v4 + with: + name: sparkos-release-${{ github.sha }} + path: sparkos-release.zip + retention-days: 90 + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: sparkos-release.zip + body: | + # SparkOS Release ${{ github.ref_name }} + + This release includes: + - Pre-compiled init binary (statically linked) + - Complete source code + - Build scripts and configuration + - Root filesystem structure + - Documentation + + ## Quick Start + + 1. Download `sparkos-release.zip` + 2. Extract the archive + 3. Use the pre-built `init` binary or rebuild from source + 4. Follow README.md for complete setup instructions + + ## Docker Image + + A Docker image is also available: + ```bash + docker pull ghcr.io/johndoe6345789/sparkos:${{ github.ref_name }} + ``` + + ## What's Changed + + See commit history for detailed changes. + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Output summary + run: | + echo "### Build and Release Summary 📦" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** ✅ Success" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Init Binary:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh init >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Release Archive:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -lh sparkos-release.zip >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + echo "**Release:** Created GitHub release for ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + else + echo "**Artifact:** Uploaded as workflow artifact (available for 90 days)" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Archive Contents (preview):**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + unzip -l sparkos-release.zip | head -30 >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/sparkos/.gitignore b/sparkos/.gitignore new file mode 100644 index 000000000..a74c60bed --- /dev/null +++ b/sparkos/.gitignore @@ -0,0 +1,74 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# debug information files +*.dwo + +# Build artifacts +build/ +init +*.img +*.iso +*.zip +release/ + +# Temporary files +/tmp/ +*.tmp +*.bak + +# Root filesystem (generated) +rootfs/bin/* +rootfs/sbin/* +rootfs/usr/bin/* +rootfs/usr/sbin/* +rootfs/usr/lib/* +rootfs/usr/lib64/* +rootfs/lib/* +rootfs/lib64/* +rootfs/boot/* + +# Keep structure but ignore contents +!rootfs/README.txt +!rootfs/etc/ +!rootfs/root/ +!rootfs/home/ + +# CodeQL +_codeql_detected_source_root diff --git a/sparkos/ARCHITECTURE.md b/sparkos/ARCHITECTURE.md new file mode 100644 index 000000000..0a75a6e7f --- /dev/null +++ b/sparkos/ARCHITECTURE.md @@ -0,0 +1,153 @@ +# SparkOS Architecture + +## Overview + +SparkOS is a revolutionary operating system that uses the Linux kernel for hardware abstraction but ditches traditional Unix conventions. Instead of shells, users, and Unix utilities, SparkOS boots directly into a Qt6 GUI that interfaces with the kernel through standard Linux APIs. + +## Core Philosophy + +1. **No Unix Baggage**: No user/group system, no shells, no Unix utilities by default +2. **Direct Kernel Interface**: GUI communicates directly with Linux kernel +3. **Network-First**: Networking is a primary interface, not an afterthought +4. **GUI-Only**: No CLI unless explicitly needed for debugging +5. **Linux for Drivers**: Leverage Linux's excellent hardware support + +## System Architecture + +``` +Boot Sequence: +Hardware → UEFI/BIOS → GRUB → Linux Kernel → init (PID 1) → Qt6 GUI + +Stack Layers: +┌──────────────────────────────────────────┐ +│ Qt6 GUI Application │ ← User Interface +│ (sparkos-gui executable) │ +├──────────────────────────────────────────┤ +│ Custom Init System (PID 1) │ ← Process Manager +│ • Mounts filesystems │ +│ • Spawns/respawns GUI │ +│ • Reaps zombie processes │ +├──────────────────────────────────────────┤ +│ Linux Kernel │ ← Hardware Abstraction +│ • All device drivers │ +│ • Framebuffer driver │ +│ • Input device drivers │ +│ • Network stack & drivers │ +│ • File system support │ +├──────────────────────────────────────────┤ +│ Hardware │ +│ • Display, GPU, Input │ +│ • Network adapters │ +│ • Storage devices │ +└──────────────────────────────────────────┘ +``` + +## Design Decisions + +### Why Ditch Unix Conventions? + +Traditional Unix systems were designed for multi-user, time-sharing mainframes in the 1970s. Modern personal computing and embedded systems have different needs: + +- **Single User**: Most devices have one user - authentication overhead is unnecessary +- **GUI Primary**: Modern users expect graphical interfaces, not command lines +- **Network Central**: Modern computing is network-centric, not file-centric +- **Direct Access**: Applications should talk directly to kernel, not through layers of abstraction + +### Why Keep Linux Kernel? + +- **Hardware Support**: Linux has exceptional driver support for modern hardware +- **Driver Abstraction**: Well-tested, stable hardware abstraction layer +- **Network Stack**: Robust, high-performance networking +- **File Systems**: Mature support for various filesystems +- **Security**: SELinux, namespaces, cgroups for isolation +- **Community**: Active development and security updates + +### Why Qt6 GUI? + +- **Cross-Platform**: Qt works on many platforms (future portability) +- **Framebuffer Support**: Can render directly to Linux framebuffer without X11/Wayland +- **Modern**: Native look and feel, hardware acceleration support +- **Complete**: Rich widget set, networking APIs, file I/O +- **Performant**: Efficient rendering and event handling + +### Why No X11/Wayland? + +- **Direct Rendering**: Qt can render directly to framebuffer (/dev/fb0) +- **Less Overhead**: No display server running in between +- **Simpler**: Fewer processes, less memory usage +- **Embedded-Friendly**: Same approach used in embedded systems + +### Why Network-First? + +Modern computing is inherently networked. Instead of treating networking as an add-on: +- Network APIs exposed directly to GUI +- Cloud storage as primary storage paradigm +- Web technologies integrated (future: embedded browser) +- Real-time updates and communication built-in + +## Future Architecture + +### Planned Components + +1. **Qt6/QML GUI** + - Full-screen application + - Android-like interface design + - Desktop-oriented workflow + +2. **Wayland Compositor** + - Custom compositor for SparkOS + - Minimal resource usage + - Touch and mouse support + +3. **Network Management** + - Qt6 NetworkManager integration + - WiFi configuration UI + - VPN and advanced networking UI + +## Security Considerations + +- Static binaries reduce attack surface +- Minimal running processes +- Root filesystem can be read-only +- Sudo for privilege escalation +- Future: SELinux/AppArmor integration + +## Performance + +- Fast boot time (seconds, not minutes) +- Low memory footprint (~20MB base init system) +- No unnecessary background services +- Efficient init system (no external dependencies) + +## Portability + +- AMD64 (x86_64) and ARM64 (aarch64) architectures +- dd-able disk images +- USB flash drive ready +- Multi-architecture Docker images + +## Extension Points + +The architecture is designed for easy extension: + +1. **Init system**: Can be enhanced with service management +2. **Filesystem**: Can add more mount points and partitions +3. **Boot process**: Can integrate other bootloaders +4. **GUI**: Clean separation allows GUI to be optional + +## Development Workflow + +1. Modify source code in `src/` +2. Build with `make init` +3. Test init in isolation +4. Install to `rootfs/` with `make install` +5. Create test image with `sudo make image` +6. Test on real hardware or VM + +## References + +- Linux Kernel Documentation +- Filesystem Hierarchy Standard (FHS) +- POSIX Standards +- Qt6 Documentation +- Wayland Protocol Specification diff --git a/sparkos/CONTRIBUTING.md b/sparkos/CONTRIBUTING.md new file mode 100644 index 000000000..6d7241e34 --- /dev/null +++ b/sparkos/CONTRIBUTING.md @@ -0,0 +1,76 @@ +# Contributing to SparkOS + +Thank you for your interest in contributing to SparkOS! + +## Project Goals + +SparkOS aims to be: +- **Minimal**: Only essential components +- **Clean**: Well-documented, readable code +- **Portable**: dd-able to USB drives +- **Extensible**: Easy to add features incrementally + +## Development Setup + +1. Clone the repository: + ```bash + git clone https://github.com/johndoe6345789/SparkOS.git + cd SparkOS + ``` + +2. Build the system: + ```bash + ./scripts/build.sh + ``` + +3. Make your changes + +4. Test your changes: + ```bash + make clean + make all + ``` + +## Code Style + +- **C/C++ Code**: Follow Linux kernel style guidelines + - Use tabs for indentation + - Keep lines under 80 characters when reasonable + - Comment complex logic + +- **Shell Scripts**: Follow Google Shell Style Guide + - For runtime scripts (inside rootfs): Use `#!/bin/sh` for POSIX-compliant scripts (busybox compatibility) + - For build scripts (host system): Can use `#!/bin/bash` when bash-specific features are needed + - Quote variables + - Use meaningful variable names + +- **Documentation**: Write clear, concise documentation + - Update README when adding features + - Comment non-obvious code + - Include usage examples + +## Submitting Changes + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/my-feature`) +3. Make your changes +4. Test thoroughly +5. Commit with descriptive messages +6. Push to your fork +7. Open a Pull Request + +## What to Contribute + +Priority areas: +- Bug fixes +- Documentation improvements +- Build system enhancements +- Testing infrastructure +- Qt6/QML GUI components +- Wayland integration +- Package management +- Network configuration + +## Questions? + +Open an issue on GitHub for questions or discussions. diff --git a/sparkos/Dockerfile b/sparkos/Dockerfile new file mode 100644 index 000000000..d5e44f12a --- /dev/null +++ b/sparkos/Dockerfile @@ -0,0 +1,48 @@ +# SparkOS Docker Image +# Multi-stage build for minimal final image size + +# Build stage - use gcc image which has all build tools pre-installed +FROM gcc:13-bookworm AS builder + +# Set working directory +WORKDIR /build + +# Copy source files +COPY src/ ./src/ +COPY Makefile . +COPY scripts/ ./scripts/ + +# Build the init system +RUN make init + +# Runtime stage - use Alpine for minimal size +FROM alpine:3.19 + +# Install file command for testing init binary +# file package provides the file(1) command to determine file type +COPY scripts/docker-install-packages.sh /tmp/ +RUN /tmp/docker-install-packages.sh + +# SparkOS Philosophy: No CLI tools, GUI-only experience +# The init system is completely self-contained with no external dependencies +# All functionality is provided through direct system calls in C + +# Create minimal rootfs structure +COPY scripts/docker-setup-rootfs.sh /tmp/ +RUN /tmp/docker-setup-rootfs.sh + +# Copy built init binary from builder +COPY --from=builder /build/init /sparkos/rootfs/sbin/init + +# Set up basic configuration files +COPY scripts/docker-setup-config.sh /tmp/ +RUN /tmp/docker-setup-config.sh + +# Create a test entrypoint +COPY scripts/test.sh /sparkos/test.sh +RUN chmod +x /sparkos/test.sh + +WORKDIR /sparkos + +# Set entrypoint +ENTRYPOINT ["/sparkos/test.sh"] diff --git a/sparkos/Dockerfile.image b/sparkos/Dockerfile.image new file mode 100644 index 000000000..a51367848 --- /dev/null +++ b/sparkos/Dockerfile.image @@ -0,0 +1,36 @@ +# Dockerfile for building UEFI-bootable SparkOS image +# Creates a .img file with GPT partition table, ESP, and GRUB + +FROM ubuntu:22.04 AS image-builder + +# Install required tools (includes Qt6 and CMake) +COPY scripts/docker-image-install-tools.sh /tmp/ +RUN /tmp/docker-image-install-tools.sh + +WORKDIR /build + +# Copy source files +COPY src/ ./src/ +COPY Makefile . +COPY scripts/ ./scripts/ +COPY config/ ./config/ +COPY rootfs/ ./rootfs/ + +# Build the init binary and Qt6 GUI application +RUN make init +RUN make gui + +# Install to rootfs +RUN make install + +# Download a minimal Linux kernel (using Ubuntu's kernel for now) +COPY scripts/docker-image-download-kernel.sh /tmp/ +RUN /tmp/docker-image-download-kernel.sh + +# Create UEFI-bootable image with GPT partition table +COPY scripts/docker-image-create-uefi.sh /tmp/ +RUN /tmp/docker-image-create-uefi.sh + +# Final stage - export the image +FROM scratch AS export +COPY --from=image-builder /output/sparkos.img.gz /sparkos.img.gz diff --git a/sparkos/INIT_VERIFICATION.md b/sparkos/INIT_VERIFICATION.md new file mode 100644 index 000000000..61b915d77 --- /dev/null +++ b/sparkos/INIT_VERIFICATION.md @@ -0,0 +1,111 @@ +# SparkOS Init System Verification + +This document demonstrates SparkOS's self-contained init system with no external dependencies. + +## Docker Container Verification + +When you run the SparkOS Docker container, it automatically verifies the init system: + +```bash +docker run --rm ghcr.io/johndoe6345789/sparkos:latest +``` + +## Expected Output + +The container startup will display comprehensive init system verification: + +``` +SparkOS Docker Test Environment +================================ + +Verifying SparkOS init binary... +-------------------------------- +✓ Init binary exists +-rwxr-xr-x 1 root root 20.0K Jan 2 00:00 /sparkos/rootfs/sbin/init + +File type: +/sparkos/rootfs/sbin/init: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked + +Dependencies: + Static binary (no dependencies) + +Root filesystem structure: +-------------------------- +total 16 +drwxr-xr-x 5 root root 4096 Jan 2 00:00 . +drwxr-xr-x 1 root root 4096 Jan 2 00:00 .. +drwxr-xr-x 2 root root 4096 Jan 2 00:00 etc +drwxr-xr-x 2 root root 4096 Jan 2 00:00 sbin +drwxr-xr-x 2 root root 4096 Jan 2 00:00 usr + +================================ +✓ SparkOS is ready for testing! +================================ + +Summary: + - Init: Custom SparkOS init system (no external dependencies) + - Architecture: GUI-only, no CLI/shell + - Network: Direct C implementation via ioctl + - Philosophy: Pure GUI experience, network-first + +Note: SparkOS has no CLI tools (no busybox, no shell) + All functionality is provided through the Qt6 GUI + +To test the init system: + docker run --rm /sparkos/rootfs/sbin/init +``` + +## What This Proves + +The verification output demonstrates: + +1. **Self-contained init**: Statically linked binary with no external dependencies +2. **No CLI tools**: No busybox, no shell, no Unix utilities +3. **Minimal footprint**: Only essential files in root filesystem +4. **Pure C implementation**: All functionality (mounting, networking) via system calls + +## Key Init Features + +- **Filesystem mounting**: Direct mount() system calls (no mount binary) +- **Network initialization**: Direct ioctl calls (no ip/ifconfig/udhcpc) +- **Process management**: Built-in SIGCHLD handler for zombie reaping +- **GUI spawning**: Direct execve() of Qt6 GUI application +- **Overlay filesystem**: Immutable base with writable /var overlay + +## SparkOS Philosophy + +SparkOS eliminates traditional Unix layers: + +- **No busybox**: All functionality in init or Qt6 GUI +- **No shell**: Direct kernel-to-GUI communication +- **No CLI tools**: Everything through GUI interface +- **No users/authentication**: Single-user, direct boot to GUI +- **Network-first**: Networking integrated into GUI, not CLI + +## Init System Architecture + +``` +Init Process (PID 1) +├── Mount filesystems (proc, sys, dev, tmp) +├── Setup overlay filesystem (/var) +├── Initialize network interfaces (ioctl) +└── Spawn Qt6 GUI → Respawn on exit +``` + +All operations use direct system calls: +- `mount()` for filesystem mounting +- `mkdir()` for directory creation +- `socket()` + `ioctl()` for network initialization +- `fork()` + `execve()` for GUI spawning +- `waitpid()` for process reaping + +## Verification in Code + +The verification is performed by `/sparkos/test.sh` which: +1. Checks if init binary exists and is executable +2. Verifies it's statically linked (no dependencies) +3. Shows root filesystem structure +4. Confirms the GUI-only architecture + +This ensures that anyone running the SparkOS Docker container can immediately see proof that SparkOS uses a completely self-contained init system with no external dependencies. + diff --git a/sparkos/LICENSE b/sparkos/LICENSE new file mode 100644 index 000000000..3d0cd4b58 --- /dev/null +++ b/sparkos/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 johndoe6345789 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sparkos/Makefile b/sparkos/Makefile new file mode 100644 index 000000000..ce928dedb --- /dev/null +++ b/sparkos/Makefile @@ -0,0 +1,77 @@ +# SparkOS Makefile +# Builds the minimal Linux distribution + +CC = gcc +CFLAGS = -Wall -O2 -static +DESTDIR = rootfs +IMAGE = sparkos.img +IMAGE_SIZE = 512M + +.PHONY: all clean init gui image image-docker help install docker-release + +all: init gui + +help: + @echo "SparkOS Build System" + @echo "====================" + @echo "Targets:" + @echo " make init - Build the init system" + @echo " make gui - Build the Qt6 GUI application" + @echo " make all - Build both init and GUI (default)" + @echo " make install - Install init and GUI to rootfs" + @echo " make image - Create bootable dd-able image (requires root)" + @echo " make image-docker - Create bootable image using Docker (no root required)" + @echo " make docker-release - Build release package using Docker (no root required)" + @echo " make clean - Clean build artifacts" + @echo "" + @echo "Note: Creating a bootable image requires root privileges" + @echo " and various tools (debootstrap, syslinux, etc.)" + @echo "" + @echo "For easier image building, use Docker:" + @echo " make image-docker" + @echo " OR: ./scripts/build-image.sh" + +init: src/init.c + @echo "Building SparkOS init system..." + $(CC) $(CFLAGS) -o init src/init.c + @echo "Init system built successfully: ./init" + +gui: + @echo "Building SparkOS Qt6 GUI application..." + @mkdir -p build/gui + @cd build/gui && cmake ../../src/qt6-app -DCMAKE_INSTALL_PREFIX=$(DESTDIR)/usr + @cd build/gui && $(MAKE) + @echo "Qt6 GUI application built successfully: build/gui/sparkos-gui" + +install: init gui + @echo "Installing init to rootfs..." + install -D -m 755 init $(DESTDIR)/sbin/init + @echo "Init installed to $(DESTDIR)/sbin/init" + @echo "Installing Qt6 GUI application to rootfs..." + @cd build/gui && $(MAKE) install + @echo "GUI application installed to $(DESTDIR)/usr/bin/sparkos-gui" + +image: install + @echo "Creating bootable image..." + @if [ "$$(id -u)" -ne 0 ]; then \ + echo "ERROR: Image creation requires root privileges"; \ + echo "Run: sudo make image"; \ + exit 1; \ + fi + @./scripts/create_image.sh + +image-docker: + @echo "Building bootable image using Docker..." + @./scripts/build-image.sh + +docker-release: + @echo "Building release package using Docker..." + @./scripts/docker-release.sh + +clean: + @echo "Cleaning build artifacts..." + rm -f init + rm -f $(IMAGE) + rm -rf build/ + rm -rf release/ + @echo "Clean complete" diff --git a/sparkos/README.md b/sparkos/README.md new file mode 100644 index 000000000..25ac16b2d --- /dev/null +++ b/sparkos/README.md @@ -0,0 +1,482 @@ +# SparkOS + +A revolutionary operating system that ditches Unix conventions for a modern, network-first approach. SparkOS features: + +- **Direct Kernel Interface**: Qt6 GUI communicates directly with Linux kernel, bypassing Unix layers +- **Linux Driver Layer**: All hardware abstraction handled by Linux kernel drivers +- **No Unix User System**: No users, groups, passwords, or authentication - direct boot to GUI +- **Network-First Architecture**: Built around networking as the primary paradigm +- **Qt6 Full-Screen GUI**: Modern graphical interface from boot +- **Minimal footprint**: Lean system with only essential components +- **Portable**: dd-able disk image for USB flash drives +- **Custom init**: Lightweight C init system that launches GUI directly +- **Immutable base**: Read-only root filesystem with overlay for runtime data + +## Architecture + +``` +┌─────────────────────────────────────┐ +│ Qt6 GUI Application │ +│ (Direct Framebuffer Rendering) │ +└─────────────────────────────────────┘ + ↕ +┌─────────────────────────────────────┐ +│ Linux Kernel │ +│ • Framebuffer (/dev/fb0) │ +│ • Input devices (/dev/input/*) │ +│ • Network stack │ +│ • All hardware drivers │ +└─────────────────────────────────────┘ + ↕ +┌─────────────────────────────────────┐ +│ Hardware │ +│ • GPU, Display, Input devices │ +│ • Network adapters │ +│ • Storage, etc. │ +└─────────────────────────────────────┘ +``` + +**Key Design Principles:** +- Linux kernel provides complete hardware abstraction and driver support +- Qt6 interfaces directly with kernel through /dev, /proc, /sys interfaces +- No intermediate Unix layers (no systemd, no user management, no shells by default) +- Network-first: networking capabilities exposed directly to GUI + +## MVP Status + +The current MVP provides: +- ✅ Custom init system written in C (no external dependencies) +- ✅ GUI-only architecture (no CLI/shell) +- ✅ dd-able AMD64 image creation scripts +- ✅ Minimal root filesystem structure +- ✅ Build system (Makefile) +- ✅ Direct network initialization via C ioctl +- ✅ DNS configuration with public fallback servers +- ✅ Docker container for testing +- ✅ Automated builds and publishing to GHCR +- ✅ Multi-architecture Docker images (AMD64 and ARM64) +- ✅ CI/CD pipeline for compiled release packages +- ✅ GitHub releases with pre-built binaries + +## Prerequisites + +To build SparkOS, you need: + +- GCC compiler +- GNU Make +- Linux system (for building) + +To create bootable images (optional): +- Root privileges +- `syslinux` bootloader +- `parted` partitioning tool +- `losetup` for loop devices +- `mkfs.ext4` filesystem tools + +## Quick Start + +### Using UEFI-Bootable Disk Image (Recommended - Boot from USB) + +Download the UEFI-bootable image from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases): + +```bash +# Download the disk image (replace VERSION with actual version, e.g., v1.0.0) +wget https://github.com/johndoe6345789/SparkOS/releases/download/VERSION/sparkos.img.gz + +# Decompress the image +gunzip sparkos.img.gz + +# Write to USB drive (Linux - BE CAREFUL!) +sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync +``` + +**⚠️ WARNING**: Replace `/dev/sdX` with your actual USB device (e.g., `/dev/sdb`). This will **DESTROY ALL DATA** on the target drive! + +**Boot Instructions:** +1. Insert the USB drive into a UEFI-capable system +2. Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) +3. Select the USB drive as boot device +4. SparkOS will boot automatically + +The UEFI-bootable disk image includes: +- ✅ **UEFI boot support** with GRUB bootloader +- ✅ **GPT partition table** with ESP (EFI System Partition) +- ✅ **Linux kernel** ready to boot +- ✅ **SparkOS init system** (completely self-contained, no external dependencies) +- ✅ **Ready to boot** - Direct to Qt6 GUI, no CLI + +### Using Pre-built Binary Package + +Download the binary package from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases): + +```bash +# Download the latest release (replace VERSION with actual version, e.g., v1.0.0) +wget https://github.com/johndoe6345789/SparkOS/releases/download/VERSION/sparkos-release.zip + +# Extract the package +unzip sparkos-release.zip +cd sparkos/ + +# The init binary is already compiled and ready to use +ls -lh init + +# Copy to your rootfs or use directly +cp init /path/to/your/rootfs/sbin/init +``` + +The release package includes: +- Pre-compiled init binary (statically linked, ready to use) +- Complete source code +- Build scripts and configuration +- Root filesystem structure +- Full documentation + +### Using Docker (Recommended for Testing) + +The easiest way to test SparkOS is using the pre-built Docker image from GitHub Container Registry: + +```bash +# Pull and run the latest image (automatically selects the correct architecture) +docker pull ghcr.io/johndoe6345789/sparkos:latest +docker run --rm ghcr.io/johndoe6345789/sparkos:latest + +# Or build locally +docker build -t sparkos:local . +docker run --rm sparkos:local + +# Or use Docker Compose for even simpler testing +docker-compose up + +# Build for specific architecture +docker buildx build --platform linux/amd64 -t sparkos:amd64 --load . +docker buildx build --platform linux/arm64 -t sparkos:arm64 --load . +``` + +The Docker image includes: +- Pre-built init system binary +- Minimal root filesystem structure +- Test environment for validation +- **No CLI tools**: Pure GUI-only architecture +- **Multi-architecture support**: Available for both AMD64 (x86_64) and ARM64 (aarch64) architectures + +When you run the Docker image, it automatically verifies: +- Custom init system binary (statically linked, no dependencies) +- Root filesystem structure + +Images are automatically built and published to [GitHub Container Registry](https://github.com/johndoe6345789/SparkOS/pkgs/container/sparkos) on every push to main branch. + +**Building Releases with Docker (No Root Required):** + +Create release packages easily using Docker without needing root privileges or special tools: + +```bash +# Build a release package for version v1.0.0 +./scripts/docker-release.sh v1.0.0 + +# The release ZIP will be created in release/sparkos-release.zip +# This is the same artifact that GitHub Actions creates +``` + +### Building the Init System + +```bash +# Build the init binary +make init + +# Or use the quick build script +./scripts/build.sh +``` + +### Setting Up Root Filesystem + +```bash +# Create the root filesystem structure +./scripts/setup_rootfs.sh + +# Install init to rootfs +make install +``` + +### Creating a UEFI-Bootable Image + +**Using Docker (Recommended - No Root Required):** + +Build UEFI-bootable disk images easily using Docker without needing root privileges on your host: + +```bash +# Build the UEFI-bootable disk image using Docker +make image-docker + +# Or use the script directly +./scripts/build-image.sh + +# The compressed UEFI-bootable image will be in release/sparkos.img.gz +``` + +This creates a complete UEFI-bootable image with: +- GPT partition table +- EFI System Partition (ESP) with FAT32 +- GRUB UEFI bootloader +- Linux kernel +- SparkOS init system (no external dependencies) + +**Traditional Method (Requires Root):** + +⚠️ **Warning**: This method is for creating custom partitioned images and requires root privileges. + +```bash +# Install required tools (Ubuntu/Debian) +sudo apt-get install syslinux parted + +# Build everything and create image +make all +sudo make image +``` + +### Installing to USB Drive + +Once you have created the `sparkos.img` file, use the installation script to write it to a USB drive or storage device: + +```bash +# Use the installation script (RECOMMENDED) +sudo ./scripts/install.sh /dev/sdX + +# The script will: +# - Validate the target drive +# - Display warnings about data destruction +# - Require confirmation before proceeding +# - Show progress during installation +# - Verify successful installation +``` + +Replace `/dev/sdX` with your actual USB device (e.g., `/dev/sdb`, `/dev/nvme1n1`). + +**⚠️ WARNING**: This will permanently erase all data on the target drive! + +## Project Structure + +``` +SparkOS/ +├── .github/ # GitHub Actions workflows +│ └── workflows/ +│ └── docker-publish.yml # Docker build and publish workflow +├── config/ # Build configuration files +│ └── build.conf # Build parameters +├── scripts/ # Build and setup scripts +│ ├── build.sh # Quick build script +│ ├── setup_rootfs.sh # Root filesystem setup +│ ├── create_image.sh # Image creation script +│ └── install.sh # Installation script for USB drives +├── src/ # Source code +│ └── init.c # Custom init system +├── rootfs/ # Root filesystem (generated) +│ ├── bin/ # Essential binaries +│ ├── sbin/ # System binaries +│ ├── etc/ # Configuration files +│ └── ... # Standard FHS directories +├── Dockerfile # Docker image definition +├── Makefile # Build system +└── README.md # This file +``` + +## Architecture + +### Init System + +SparkOS uses a custom init system (`/sbin/init`) that: +- Mounts essential filesystems (proc, sys, dev, tmp) via direct system calls +- Initializes network interfaces via direct C ioctl calls +- Spawns Qt6 GUI application directly +- Handles process reaping +- Respawns GUI on exit +- **No external dependencies**: Completely self-contained + +### Root Filesystem + +Minimal filesystem structure for GUI-only OS: +- `/sbin`: Init binary only +- `/etc`: Minimal system configuration +- `/proc`, `/sys`, `/dev`: Kernel interfaces +- `/tmp`: Temporary files +- `/usr`: Qt6 GUI application and libraries +- `/var`: Variable data (overlay mount) +- `/root`: Root home + +### Networking + +SparkOS provides network initialization through direct C code: +- **Interface Management**: Direct ioctl calls to bring up network interfaces +- **DNS**: Fallback to public DNS servers (8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1) +- **DHCP**: Managed by Qt6 NetworkManager in GUI +- **WiFi**: Configured through Qt6 GUI + +## Development + +### CI/CD and Docker + +SparkOS uses GitHub Actions for continuous integration and delivery: + +**Automated Builds:** +- Docker images are automatically built on every push to main/develop branches +- Compiled release packages are automatically built on every push to main/develop branches +- Both are also built for pull requests (testing only, not published) +- Tagged releases automatically create versioned Docker images and GitHub releases with compiled binaries +- **Multi-architecture builds**: Images are built for both AMD64 (x86_64) and ARM64 (aarch64) + +**Compiled Releases:** +- Pre-compiled init binaries are available as GitHub releases for version tags +- Release packages include: compiled init binary, source code, build scripts, and documentation +- Download releases from the [GitHub Releases page](https://github.com/johndoe6345789/SparkOS/releases) +- Build artifacts are available for all workflow runs (retained for 90 days) + +**Container Registry:** +- Images are published to GitHub Container Registry (GHCR) +- Pull images: `docker pull ghcr.io/johndoe6345789/sparkos:latest` +- Available tags: `latest`, `main`, `develop`, version tags (e.g., `v1.0.0`) +- Docker will automatically select the correct architecture for your platform + +**Docker Development:** +```bash +# Build Docker image locally +docker build -t sparkos:dev . + +# Build for multiple architectures (requires Docker Buildx) +docker buildx build --platform linux/amd64,linux/arm64 -t sparkos:multiarch . + +# Test the image +docker run --rm sparkos:dev + +# Or use Docker Compose +docker-compose up + +# Inspect the init binary +docker run --rm sparkos:dev sh -c "ls -lh /sparkos/rootfs/sbin/init" +``` + +### Creating Releases + +**Using Docker (Recommended - No Root Required):** + +Build release packages locally using Docker without needing root privileges: + +```bash +# Build a release package +./scripts/docker-release.sh v1.0.0 + +# The release ZIP will be in release/sparkos-release.zip +# This is identical to what GitHub Actions creates +``` + +**Creating a GitHub Release:** + +1. **Commit and push your changes** to the main branch +2. **Create and push a version tag:** + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` +3. **GitHub Actions will automatically:** + - Build the init binary + - Create the release package ZIP + - Build and publish Docker images (AMD64 + ARM64) + - Create a GitHub Release with the artifacts + - Publish to GitHub Container Registry + +The release will be available at: +- **GitHub Releases:** https://github.com/johndoe6345789/SparkOS/releases +- **Docker Images:** `ghcr.io/johndoe6345789/sparkos:v1.0.0` + +**Manual Release Creation:** + +You can also create a release manually: +1. Go to https://github.com/johndoe6345789/SparkOS/releases/new +2. Choose or create a tag (e.g., `v1.0.0`) +3. Fill in the release title and description +4. Upload the `sparkos-release.zip` (built locally with `docker-release.sh`) +5. Publish the release + +For detailed instructions on creating releases, see [RELEASING.md](RELEASING.md). + +### Building Components + +```bash +# Build init system only +make init + +# Build release package using Docker +make docker-release + +# Install to rootfs +make install + +# Clean build artifacts +make clean + +# Show help +make help +``` + +### Adding Components to Root Filesystem + +To create a fully functional bootable system: + +```bash +# Required components: +# 1. Qt6 GUI application - Build with make gui +# 2. Qt6 libraries - Copy Qt6 runtime libraries to rootfs/usr/lib +# 3. Linux kernel - Include kernel binary for bootloader + +# Qt6 GUI is built and installed via: +make gui +make install # Installs to rootfs/usr/bin/sparkos-gui + +# Note: Qt6 must be compiled with linuxfb support for framebuffer rendering +``` + +## Future Roadmap + +- [ ] Qt6/QML full screen GUI implementation +- [ ] Wayland compositor integration +- [ ] Network management via Qt6 NetworkManager +- [ ] WiFi configuration through GUI +- [ ] Advanced network configuration UI +- [ ] System settings and configuration UI + +## Contributing + +Contributions are welcome! This is an early-stage project focused on: +1. Maintaining minimal footprint +2. Clean, readable code +3. Proper documentation +4. GUI-only architecture (no CLI/shell) + +## License + +See LICENSE file for details. + +## Notes + +This is an MVP implementation. The system currently provides: +- Custom init system with direct network initialization +- GUI-only architecture (no CLI/shell) +- Build infrastructure +- Image creation tooling +- Self-contained init with no external dependencies + +To create a fully bootable system, you'll also need: +- Linux kernel binary (`vmlinuz`) with framebuffer and networking support +- Qt6 GUI application (sparkos-gui) +- Qt6 runtime libraries +- Bootloader installation (handled by scripts) + +Minimum System Requirements: +- Kernel: Linux kernel with framebuffer and networking support +- Init: Custom SparkOS init (included, no dependencies) +- GUI: Qt6 application with linuxfb platform support +- Libraries: Qt6 runtime libraries for GUI + +System Philosophy: +- **No CLI tools**: Everything through Qt6 GUI +- **No shell**: Direct kernel-to-GUI communication +- **No busybox**: Self-contained init system +- **Network-first**: Networking integrated into GUI diff --git a/sparkos/RELEASING.md b/sparkos/RELEASING.md new file mode 100644 index 000000000..ed3340a0f --- /dev/null +++ b/sparkos/RELEASING.md @@ -0,0 +1,233 @@ +# SparkOS Release Guide + +This guide explains how to create and publish releases for SparkOS. + +## Release Methods + +SparkOS supports two ways to create releases: + +1. **Automatic (Recommended)**: Push a version tag to trigger GitHub Actions +2. **Manual**: Build locally and upload to GitHub + +## Method 1: Automatic Release (Recommended) + +This method uses GitHub Actions to automatically build and publish releases. + +### Prerequisites + +- Push access to the repository +- All changes committed and pushed to `main` branch + +### Steps + +1. **Ensure everything is ready** + ```bash + # Make sure you're on main and up to date + git checkout main + git pull origin main + + # Verify the build works + make init + ``` + +2. **Create and push a version tag** + ```bash + # Create a tag (use semantic versioning: v1.0.0, v2.1.3, etc.) + git tag v1.0.0 + + # Push the tag to GitHub + git push origin v1.0.0 + ``` + +3. **Wait for GitHub Actions** + - Go to https://github.com/johndoe6345789/SparkOS/actions + - Two workflows will run: + - **"Build and Release"**: Creates the release ZIP + - **"Docker Build and Publish"**: Publishes Docker images + - Both should complete successfully (green checkmarks) + +4. **Verify the release** + - Go to https://github.com/johndoe6345789/SparkOS/releases + - Your new release should appear with `sparkos-release.zip` attached + - Docker images are available at `ghcr.io/johndoe6345789/sparkos:v1.0.0` + +### What Gets Published + +When you push a version tag, GitHub Actions automatically: + +- ✅ Builds the init binary (statically linked) +- ✅ Creates a release package ZIP containing: + - Compiled init binary + - Complete source code + - Build scripts and configuration + - Root filesystem structure + - Full documentation +- ✅ Creates a GitHub Release at `/releases` +- ✅ Builds multi-architecture Docker images (AMD64 + ARM64) +- ✅ Publishes Docker images to GitHub Container Registry +- ✅ Tags Docker images with version number + +## Method 2: Manual Release + +Use this method if you need to build releases locally without pushing tags. + +### Prerequisites + +- Docker installed locally +- `zip` utility installed + +### Steps + +1. **Build the release package using Docker** + ```bash + # Build for a specific version + ./scripts/docker-release.sh v1.0.0 + + # Or use Make + make docker-release + + # The package will be created at: release/sparkos-release.zip + ``` + +2. **Verify the package** + ```bash + # Check the file was created + ls -lh release/sparkos-release.zip + + # List contents + unzip -l release/sparkos-release.zip | head -40 + ``` + +3. **Create a GitHub Release manually** + - Go to https://github.com/johndoe6345789/SparkOS/releases/new + - Fill in: + - **Tag**: Create a new tag (e.g., `v1.0.0`) + - **Title**: `SparkOS v1.0.0` (or similar) + - **Description**: List changes and features + - **Upload** the `sparkos-release.zip` file + - Click **"Publish release"** + +### What's Included + +The Docker-based release script creates the exact same package as GitHub Actions: + +- Compiled init binary (statically linked, ready to use) +- Complete source code +- Build scripts (including `docker-release.sh`) +- Configuration files +- Root filesystem structure +- Full documentation (README, ARCHITECTURE, CONTRIBUTING) + +## Testing Before Release + +Always test your changes before creating a release: + +### Test the Build + +```bash +# Test compilation +make clean && make init + +# Verify the binary +file init +ldd init # Should show "not a dynamic executable" (static) +``` + +### Test with Docker + +```bash +# Build the Docker image +docker build -t sparkos:test . + +# Run the test environment +docker run --rm sparkos:test + +# Or use Docker Compose +docker-compose up +``` + +### Test the Release Script + +```bash +# Build a test release package +./scripts/docker-release.sh test + +# Verify it was created +ls -lh release/sparkos-release.zip +``` + +## Release Checklist + +Before creating a release: + +- [ ] All changes are committed and pushed to `main` +- [ ] Build succeeds: `make init` works +- [ ] Docker build succeeds: `docker build -t sparkos:test .` works +- [ ] Documentation is up to date (README, ARCHITECTURE, etc.) +- [ ] Version follows semantic versioning (vMAJOR.MINOR.PATCH) +- [ ] CHANGELOG or release notes are prepared (if applicable) + +## Troubleshooting + +### GitHub Actions Fails + +1. Check the workflow logs at https://github.com/johndoe6345789/SparkOS/actions +2. Common issues: + - Build errors: Check if `make init` works locally + - Permission errors: Ensure `contents: write` permission in workflow + - Docker errors: Verify Dockerfile builds locally + +### Release Not Created + +- Ensure you pushed a tag starting with `v` (e.g., `v1.0.0`) +- Check that the workflow has `contents: write` permissions +- Verify the workflow completed successfully (green checkmark) + +### Docker Images Not Published + +- Check the "Docker Build and Publish" workflow logs +- Ensure the tag was pushed (not just created locally) +- Verify `packages: write` permission in workflow + +## Version Numbering + +Use semantic versioning for releases: + +- **v1.0.0**: First stable release +- **v1.1.0**: New features, backward compatible +- **v1.1.1**: Bug fixes only +- **v2.0.0**: Breaking changes + +## Release Artifacts + +Each release includes: + +1. **GitHub Release Page** + - Release notes + - Downloadable `sparkos-release.zip` + - Link to Docker images + +2. **Docker Images** (automatically published) + - `ghcr.io/johndoe6345789/sparkos:v1.0.0` (specific version) + - `ghcr.io/johndoe6345789/sparkos:1.0` (minor version) + - `ghcr.io/johndoe6345789/sparkos:1` (major version) + - `ghcr.io/johndoe6345789/sparkos:latest` (if released from main) + +3. **Multi-Architecture Support** + - Linux AMD64 (x86_64) + - Linux ARM64 (aarch64) + +## Next Steps + +After releasing: + +1. Announce the release (if applicable) +2. Update documentation links to point to the new version +3. Test the release on different platforms +4. Monitor for issues and prepare patches if needed + +## Support + +For questions or issues with releases: +- Open an issue: https://github.com/johndoe6345789/SparkOS/issues +- Check workflow logs: https://github.com/johndoe6345789/SparkOS/actions diff --git a/sparkos/config/RELEASE_README.md b/sparkos/config/RELEASE_README.md new file mode 100644 index 000000000..19e14f96d --- /dev/null +++ b/sparkos/config/RELEASE_README.md @@ -0,0 +1,59 @@ +# SparkOS Release Package + +This package contains the compiled SparkOS init system and all necessary files to run or build SparkOS. + +## Contents + +- `init` - The compiled init binary (statically linked) +- `src/` - Source code for the init system +- `scripts/` - Build and setup scripts +- `config/` - Configuration files +- `rootfs/` - Root filesystem structure +- `Makefile` - Build system +- Documentation files (README.md, ARCHITECTURE.md, etc.) + +## Quick Start + +### Using the Pre-built Binary + +The `init` binary is already compiled and ready to use: + +```bash +# Copy to your rootfs +cp init /path/to/your/rootfs/sbin/init +chmod 755 /path/to/your/rootfs/sbin/init +``` + +### Rebuilding from Source + +If you need to rebuild: + +```bash +# Build the init system +make init + +# Install to rootfs +make install +``` + +### Creating a Bootable System + +Follow the instructions in README.md to create a complete bootable system. + +## System Requirements + +- Linux system with kernel 3.x or later with framebuffer support +- Qt6 runtime libraries for GUI +- For building: GCC compiler, Make + +## Documentation + +See README.md for complete documentation, including: +- Building instructions +- Creating bootable images +- Network configuration +- Development guidelines + +## Support + +For issues and questions, visit: https://github.com/johndoe6345789/SparkOS diff --git a/sparkos/config/build.conf b/sparkos/config/build.conf new file mode 100644 index 000000000..96638e71b --- /dev/null +++ b/sparkos/config/build.conf @@ -0,0 +1,30 @@ +# SparkOS Configuration +# This file defines the build configuration for SparkOS + +# Target architecture +ARCH=x86_64 + +# Kernel version (if building custom kernel) +KERNEL_VERSION=6.1 + +# Image size for dd-able image +IMAGE_SIZE=512M + +# Bootloader +BOOTLOADER=syslinux + +# Init system +INIT=sparkos-init + +# Compiler flags +CFLAGS=-Wall -O2 -static +LDFLAGS=-static + +# Root filesystem location +ROOTFS=rootfs + +# Build directory +BUILDDIR=build + +# Output image +IMAGE=sparkos.img diff --git a/sparkos/config/grub-embedded.cfg.in b/sparkos/config/grub-embedded.cfg.in new file mode 100644 index 000000000..eae7f8df9 --- /dev/null +++ b/sparkos/config/grub-embedded.cfg.in @@ -0,0 +1,36 @@ +# Embedded bootstrap configuration for GRUB +# This config tells GRUB where to find the real configuration file + +# Set proper terminal and video output +terminal_input console +terminal_output console + +# Load essential modules +insmod part_gpt +insmod fat +insmod ext2 +insmod normal +insmod linux +insmod all_video +insmod video_bochs +insmod video_cirrus +insmod gfxterm + +# Set graphics mode for better compatibility +set gfxmode=auto +set gfxpayload=keep + +# Search for ESP by label +search --no-floppy --set=root --label @ESP_LABEL@ + +# Try to load the main config file +if [ -e /boot/grub/grub.cfg ]; then + configfile /boot/grub/grub.cfg +else + echo "Error: Could not find /boot/grub/grub.cfg" + echo "Root device: $root" + echo "Press any key to enter GRUB command line..." + # Sleep for a very long time (interruptible by any key press) + # This provides an effective "wait for keypress" in GRUB + sleep --verbose --interruptible 99999 +fi diff --git a/sparkos/config/grub.cfg.in b/sparkos/config/grub.cfg.in new file mode 100644 index 000000000..9c439f5d3 --- /dev/null +++ b/sparkos/config/grub.cfg.in @@ -0,0 +1,42 @@ +# GRUB Configuration for SparkOS + +# Load essential modules +insmod part_gpt +insmod fat +insmod ext2 +insmod linux +insmod all_video +insmod video_bochs +insmod video_cirrus +insmod gfxterm + +# Set terminal and video modes +terminal_input console +terminal_output console +set gfxmode=auto +set gfxpayload=keep + +# Boot menu settings +set timeout=5 +set default=0 + +# Show countdown message +echo "SparkOS Boot Menu - Starting in $timeout seconds..." + +menuentry "SparkOS" { + echo "Loading SparkOS kernel..." + linux /boot/vmlinuz root=LABEL=@ROOT_LABEL@ ro init=/sbin/init console=tty0 console=ttyS0,115200n8 + initrd /boot/initrd.img +} + +menuentry "SparkOS (Verbose Mode)" { + echo "Loading SparkOS kernel in verbose mode..." + linux /boot/vmlinuz root=LABEL=@ROOT_LABEL@ ro init=/sbin/init console=tty0 console=ttyS0,115200n8 debug loglevel=7 + initrd /boot/initrd.img +} + +menuentry "SparkOS (Recovery Mode)" { + echo "Loading SparkOS kernel in recovery mode..." + linux /boot/vmlinuz root=LABEL=@ROOT_LABEL@ rw init=/bin/sh console=tty0 console=ttyS0,115200n8 + initrd /boot/initrd.img +} diff --git a/sparkos/config/image-readme.txt b/sparkos/config/image-readme.txt new file mode 100644 index 000000000..c60294357 --- /dev/null +++ b/sparkos/config/image-readme.txt @@ -0,0 +1,23 @@ +SparkOS UEFI-Bootable Image + +This is a UEFI-bootable disk image with: +- GPT partition table +- EFI System Partition (ESP) with FAT32 filesystem +- GRUB UEFI bootloader +- Linux kernel +- SparkOS init system (self-contained, no external dependencies) +- Qt6 GUI application + +The image can be written to a USB drive and booted on UEFI systems: + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress + sudo sync + +Boot options: +- UEFI boot support (tested on x86_64 systems) +- Automatic boot after 3 seconds +- Direct boot to Qt6 GUI (no CLI) +- Console on tty1 (for debugging only) + +Philosophy: GUI-only, no CLI tools, network-first + +For more information, see: https://github.com/johndoe6345789/SparkOS diff --git a/sparkos/config/image-release-readme.txt b/sparkos/config/image-release-readme.txt new file mode 100644 index 000000000..67fc3b79b --- /dev/null +++ b/sparkos/config/image-release-readme.txt @@ -0,0 +1,74 @@ +SparkOS UEFI-Bootable Disk Image +================================== + +This package contains a UEFI-bootable disk image with SparkOS. + +Files: +- sparkos.img.gz - Compressed UEFI-bootable disk image (~1GB) + +What's Included: +--------------- +✓ GPT partition table +✓ EFI System Partition (ESP) with FAT32 filesystem +✓ GRUB UEFI bootloader +✓ Linux kernel +✓ SparkOS init system (self-contained, no dependencies) +✓ Qt6 GUI application +✓ Minimal filesystem structure + +Boot Support: +------------ +✓ UEFI boot (x86_64 systems) +✓ Automatic boot after 3 seconds +✓ Direct boot to Qt6 GUI (no CLI) +✓ Console on tty1 (for debugging only) + +Quick Start: +----------- + +1. Decompress the image: + gunzip sparkos.img.gz + +2. Write to USB drive (Linux - BE CAREFUL!): + sudo dd if=sparkos.img of=/dev/sdX bs=4M status=progress oflag=sync + + WARNING: Replace /dev/sdX with your actual USB drive device. + This will DESTROY all data on the target drive! + +3. Boot from USB: + - Insert the USB drive into a UEFI-capable system + - Enter BIOS/UEFI settings (usually F2, F12, DEL, or ESC at boot) + - Select the USB drive as boot device + - SparkOS should boot automatically + +Advanced - Inspect Partitions: +------------------------------ + +To mount and inspect the partitions: + +# Set up loop device +sudo losetup -fP --show sparkos.img +# Note the loop device name (e.g., /dev/loop0) + +# Mount ESP (EFI System Partition) +sudo mount /dev/loop0p1 /mnt +ls -la /mnt/EFI +sudo umount /mnt + +# Mount root partition +sudo mount /dev/loop0p2 /mnt +ls -la /mnt +sudo umount /mnt + +# Cleanup +sudo losetup -d /dev/loop0 + +Documentation: +------------- +See the full documentation at: +https://github.com/johndoe6345789/SparkOS + +Support: +------- +For issues and questions, visit: +https://github.com/johndoe6345789/SparkOS/issues diff --git a/sparkos/docker-compose.yml b/sparkos/docker-compose.yml new file mode 100644 index 000000000..26c688a28 --- /dev/null +++ b/sparkos/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.8' + +services: + sparkos: + build: + context: . + dockerfile: Dockerfile + image: sparkos:local + container_name: sparkos-test + # The container will run the test script and exit + # Use 'docker-compose up' to test the build diff --git a/sparkos/rootfs/README.txt b/sparkos/rootfs/README.txt new file mode 100644 index 000000000..d9a3e996a --- /dev/null +++ b/sparkos/rootfs/README.txt @@ -0,0 +1,41 @@ +SparkOS Root Filesystem +======================= + +This is the root filesystem for SparkOS, a GUI-only Linux distribution. + +SparkOS Philosophy: + - GUI-Only: No CLI tools, no shell, no Unix utilities + - Network-First: Networking integrated into Qt6 GUI + - Direct Kernel Interface: Qt6 communicates directly with Linux kernel + - No Unix Baggage: No users, groups, passwords, or authentication + +Minimal System: + - Linux Kernel (with networking and framebuffer support) + - SparkOS Init System (completely self-contained, no dependencies) + - Qt6 GUI Application (all user interaction) + +Directory Structure: + /sbin - Init binary only + /etc - Minimal configuration files + /proc, /sys, /dev - Kernel interfaces + /tmp - Temporary files + /usr - Qt6 GUI application and libraries + /var - Variable data (overlay mount) + /root - Root home directory + +Network Configuration: + - Managed entirely through Qt6 GUI + - Init brings up interfaces via direct ioctl calls + - DHCP and network management handled by Qt6 NetworkManager + - /etc/resolv.conf provides fallback DNS servers + +Boot Process: + 1. Linux kernel loads + 2. Init (PID 1) mounts filesystems + 3. Init brings up network interfaces + 4. Init spawns Qt6 GUI application + 5. All user interaction through GUI + +Note: This is a minimal, GUI-only system. + No shell, no CLI tools, no busybox. + All functionality is provided through the Qt6 GUI application. diff --git a/sparkos/rootfs/etc/fstab b/sparkos/rootfs/etc/fstab new file mode 100644 index 000000000..8d5b58a95 --- /dev/null +++ b/sparkos/rootfs/etc/fstab @@ -0,0 +1,5 @@ +# +proc /proc proc defaults 0 0 +sysfs /sys sysfs defaults 0 0 +devtmpfs /dev devtmpfs defaults 0 0 +tmpfs /tmp tmpfs defaults 0 0 diff --git a/sparkos/rootfs/etc/hostname b/sparkos/rootfs/etc/hostname new file mode 100644 index 000000000..b80ccaf33 --- /dev/null +++ b/sparkos/rootfs/etc/hostname @@ -0,0 +1 @@ +sparkos diff --git a/sparkos/rootfs/etc/hosts b/sparkos/rootfs/etc/hosts new file mode 100644 index 000000000..c7418bb10 --- /dev/null +++ b/sparkos/rootfs/etc/hosts @@ -0,0 +1,3 @@ +127.0.0.1 localhost +127.0.1.1 sparkos +::1 localhost ip6-localhost ip6-loopback diff --git a/sparkos/rootfs/etc/network/interfaces b/sparkos/rootfs/etc/network/interfaces new file mode 100644 index 000000000..a3b8cf1ef --- /dev/null +++ b/sparkos/rootfs/etc/network/interfaces @@ -0,0 +1,10 @@ +# SparkOS Network Configuration +# Wired networking only for bootstrapping + +# Loopback interface +auto lo +iface lo inet loopback + +# Primary wired interface (DHCP) +auto eth0 +iface eth0 inet dhcp diff --git a/sparkos/rootfs/etc/profile b/sparkos/rootfs/etc/profile new file mode 100644 index 000000000..9740db9e5 --- /dev/null +++ b/sparkos/rootfs/etc/profile @@ -0,0 +1,17 @@ +# SparkOS System Profile + +export PATH=/bin:/sbin:/usr/bin:/usr/sbin +export PS1='SparkOS:\w\$ ' +export TERM=linux + +# Set HOME based on user +if [ "$(id -u)" = "0" ]; then + export HOME=/root +else + export HOME=/home/$(whoami) +fi + +# Welcome message +echo "Welcome to SparkOS!" +echo "Type 'help' for available commands" +echo "" diff --git a/sparkos/rootfs/etc/resolv.conf b/sparkos/rootfs/etc/resolv.conf new file mode 100644 index 000000000..922d6a2c6 --- /dev/null +++ b/sparkos/rootfs/etc/resolv.conf @@ -0,0 +1,6 @@ +# SparkOS DNS Configuration +# Fallback to public DNS servers for reliability +nameserver 8.8.8.8 +nameserver 8.8.4.4 +nameserver 1.1.1.1 +nameserver 1.0.0.1 diff --git a/sparkos/rootfs/home/spark/.profile b/sparkos/rootfs/home/spark/.profile new file mode 100644 index 000000000..ee18357bb --- /dev/null +++ b/sparkos/rootfs/home/spark/.profile @@ -0,0 +1,12 @@ +# SparkOS User Shell Configuration + +# Set prompt +PS1='SparkOS:\w\$ ' + +# Aliases +alias ll='ls -lah' +alias ..='cd ..' + +# Environment +export EDITOR=vi +export PAGER=less diff --git a/sparkos/rootfs/home/spark/clone-sparkos.sh b/sparkos/rootfs/home/spark/clone-sparkos.sh new file mode 100755 index 000000000..471ca6baf --- /dev/null +++ b/sparkos/rootfs/home/spark/clone-sparkos.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# SparkOS CLI Installation Script +# This script clones the SparkOS CLI repository + +echo "SparkOS CLI Installation" +echo "========================" +echo "" + +SPARK_REPO="https://github.com/johndoe6345789/spark-cli.git" +INSTALL_DIR="$HOME/spark-cli" + +echo "This script will clone the SparkOS CLI to: $INSTALL_DIR" +echo "" + +# Check if git is available +if ! command -v git >/dev/null 2>&1; then + echo "Error: git is not installed" + echo "Please install git to continue" + exit 1 +fi + +# Check if directory already exists +if [ -d "$INSTALL_DIR" ]; then + echo "Warning: $INSTALL_DIR already exists" + echo -n "Do you want to remove it and re-clone? (y/N): " + read answer + if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then + rm -rf "$INSTALL_DIR" + else + echo "Installation cancelled" + exit 0 + fi +fi + +# Clone the repository +echo "Cloning spark CLI repository..." +if git clone "$SPARK_REPO" "$INSTALL_DIR"; then + echo "" + echo "SparkOS CLI cloned successfully!" + echo "" + echo "Next steps:" + echo " 1. cd $INSTALL_DIR" + echo " 2. Follow the installation instructions in the repository" + echo "" +else + echo "" + echo "Error: Failed to clone repository" + echo "Please check your network connection and try again" + exit 1 +fi diff --git a/sparkos/rootfs/root/.profile b/sparkos/rootfs/root/.profile new file mode 100644 index 000000000..145c5c50b --- /dev/null +++ b/sparkos/rootfs/root/.profile @@ -0,0 +1,12 @@ +# SparkOS Root Shell Configuration + +# Set prompt +PS1='SparkOS:\w# ' + +# Aliases +alias ll='ls -lah' +alias ..='cd ..' + +# Environment +export EDITOR=vi +export PAGER=less diff --git a/sparkos/scripts/build-image.sh b/sparkos/scripts/build-image.sh new file mode 100755 index 000000000..34749d21d --- /dev/null +++ b/sparkos/scripts/build-image.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Build SparkOS disk image using Docker +# This script builds a .img file without requiring root on the host + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +OUTPUT_DIR="$PROJECT_ROOT/release" + +echo "SparkOS Disk Image Builder (Docker)" +echo "====================================" +echo "" + +# Clean previous build +if [ -f "$OUTPUT_DIR/sparkos.img.gz" ]; then + echo "Cleaning previous image..." + rm -f "$OUTPUT_DIR/sparkos.img.gz" +fi + +mkdir -p "$OUTPUT_DIR" + +echo "Building disk image using Docker..." +echo "This may take a few minutes..." +echo "" + +# Build the image builder container +cd "$PROJECT_ROOT" +docker buildx build \ + --file Dockerfile.image \ + --target image-builder \ + --tag sparkos:image-builder \ + --load \ + . + +# Extract the image +echo "" +echo "Extracting disk image..." +CONTAINER_ID=$(docker create sparkos:image-builder) +docker cp "$CONTAINER_ID:/output/sparkos.img.gz" "$OUTPUT_DIR/sparkos.img.gz" +docker rm "$CONTAINER_ID" > /dev/null + +echo "" +echo "SUCCESS! Disk image created:" +echo " Location: $OUTPUT_DIR/sparkos.img.gz" +echo " Size: $(du -h "$OUTPUT_DIR/sparkos.img.gz" | cut -f1)" +echo "" + +# Show decompressed size +echo "Decompressed size:" +DECOMPRESSED_SIZE=$(gunzip -l "$OUTPUT_DIR/sparkos.img.gz" | tail -1 | awk '{print $2}') +echo " $(numfmt --to=iec-i --suffix=B $DECOMPRESSED_SIZE)" +echo "" + +echo "To write to a USB drive:" +echo " gunzip $OUTPUT_DIR/sparkos.img.gz" +echo " sudo dd if=$OUTPUT_DIR/sparkos.img of=/dev/sdX bs=4M status=progress" +echo "" +echo "WARNING: Replace /dev/sdX with your actual USB device!" +echo " This will DESTROY all data on the target drive!" +echo "" + +echo "To inspect the image:" +echo " gunzip -c $OUTPUT_DIR/sparkos.img.gz > /tmp/sparkos.img" +echo " sudo mount -o loop /tmp/sparkos.img /mnt" +echo " ls -la /mnt" +echo " sudo umount /mnt" diff --git a/sparkos/scripts/build.sh b/sparkos/scripts/build.sh new file mode 100755 index 000000000..8833dd860 --- /dev/null +++ b/sparkos/scripts/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Quick build script for SparkOS development +# Note: This script runs on the host system and uses bash for ${BASH_SOURCE} + +set -e + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$PROJECT_ROOT" + +echo "SparkOS Quick Build" +echo "===================" +echo "" + +# Build init +echo "Building init system..." +make init + +# Setup rootfs structure +echo "" +echo "Setting up root filesystem..." +./scripts/setup_rootfs.sh + +# Install init +echo "" +echo "Installing init to rootfs..." +make install + +echo "" +echo "Build complete!" +echo "" +echo "Next steps to create a full bootable system:" +echo " 1. Build Qt6 GUI: make gui" +echo " 2. Add a Linux kernel to rootfs/boot/vmlinuz" +echo " 3. Run: sudo make image" +echo "" +echo "Philosophy: No CLI tools, GUI-only experience" +echo "" diff --git a/sparkos/scripts/create_image.sh b/sparkos/scripts/create_image.sh new file mode 100755 index 000000000..c4f744d31 --- /dev/null +++ b/sparkos/scripts/create_image.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# SparkOS Image Creation Script +# Creates a bootable dd-able disk image + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +IMAGE_FILE="$PROJECT_ROOT/sparkos.img" +IMAGE_SIZE="512M" +MOUNT_POINT="/tmp/sparkos_mount" +ROOTFS_DIR="$PROJECT_ROOT/rootfs" +LOOP_DEV="" + +# Cleanup function +cleanup() { + local exit_code=$? + echo "Cleaning up..." + + # Unmount if mounted + if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then + umount "$MOUNT_POINT" 2>/dev/null || true + fi + + # Remove mount point + if [ -d "$MOUNT_POINT" ]; then + rmdir "$MOUNT_POINT" 2>/dev/null || true + fi + + # Detach loop device + if [ -n "$LOOP_DEV" ] && losetup "$LOOP_DEV" &>/dev/null; then + losetup -d "$LOOP_DEV" 2>/dev/null || true + fi + + if [ $exit_code -ne 0 ]; then + echo "ERROR: Image creation failed" + fi + + exit $exit_code +} + +# Set trap for cleanup on exit, interrupt, or error +trap cleanup EXIT INT TERM + +echo "SparkOS Image Builder" +echo "=====================" +echo "" + +# Check if running as root +if [ "$(id -u)" -ne 0 ]; then + echo "ERROR: This script must be run as root" + echo "Usage: sudo $0" + exit 1 +fi + +# Check for required tools +REQUIRED_TOOLS="dd losetup mkfs.ext4 syslinux" +for tool in $REQUIRED_TOOLS; do + if ! command -v "$tool" &> /dev/null; then + echo "ERROR: Required tool '$tool' is not installed" + exit 1 + fi +done + +echo "Creating disk image ($IMAGE_SIZE)..." +dd if=/dev/zero of="$IMAGE_FILE" bs=1M count=512 status=progress + +echo "Setting up loop device..." +LOOP_DEV=$(losetup -f) +losetup "$LOOP_DEV" "$IMAGE_FILE" + +echo "Creating partition table..." +parted -s "$LOOP_DEV" mklabel msdos +parted -s "$LOOP_DEV" mkpart primary ext4 1MiB 100% +parted -s "$LOOP_DEV" set 1 boot on + +# Reload partition table +partprobe "$LOOP_DEV" 2>/dev/null || true +sleep 1 + +# Get partition device +PART_DEV="${LOOP_DEV}p1" +if [ ! -e "$PART_DEV" ]; then + PART_DEV="${LOOP_DEV}1" +fi + +echo "Creating ext4 filesystem..." +mkfs.ext4 -F "$PART_DEV" + +echo "Mounting filesystem..." +mkdir -p "$MOUNT_POINT" +mount "$PART_DEV" "$MOUNT_POINT" + +echo "Copying rootfs..." +if [ -d "$ROOTFS_DIR" ]; then + cp -a "$ROOTFS_DIR"/* "$MOUNT_POINT/" +else + echo "WARNING: rootfs directory not found, creating minimal structure" + mkdir -p "$MOUNT_POINT"/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib},var,root,home} +fi + +echo "Installing bootloader..." +mkdir -p "$MOUNT_POINT/boot/syslinux" + +# Create syslinux config +cat > "$MOUNT_POINT/boot/syslinux/syslinux.cfg" << 'EOF' +DEFAULT linux +PROMPT 0 +TIMEOUT 50 + +LABEL linux + SAY Booting SparkOS... + KERNEL /boot/vmlinuz + APPEND ro root=/dev/sda1 init=/sbin/init console=tty1 +EOF + +# Install syslinux +syslinux --install "$PART_DEV" + +# Install MBR +dd if=/usr/lib/syslinux/mbr/mbr.bin of="$LOOP_DEV" bs=440 count=1 conv=notrunc 2>/dev/null || \ + dd if=/usr/share/syslinux/mbr.bin of="$LOOP_DEV" bs=440 count=1 conv=notrunc 2>/dev/null || \ + echo "WARNING: Could not install MBR, you may need to do this manually" + +echo "" +echo "SUCCESS! Bootable image created: $IMAGE_FILE" +echo "" +echo "To write to a USB drive:" +echo " sudo dd if=$IMAGE_FILE of=/dev/sdX bs=4M status=progress" +echo "" +echo "WARNING: Replace /dev/sdX with your actual USB drive device" +echo " This will DESTROY all data on the target drive!" diff --git a/sparkos/scripts/docker-image-create-uefi-old.sh b/sparkos/scripts/docker-image-create-uefi-old.sh new file mode 100755 index 000000000..957879e03 --- /dev/null +++ b/sparkos/scripts/docker-image-create-uefi-old.sh @@ -0,0 +1,148 @@ +#!/bin/bash +# Create UEFI-bootable SparkOS image with GPT partition table + +set -e + +mkdir -p /output /staging/esp /staging/root + +echo "=== Creating UEFI-bootable SparkOS image with GRUB ===" + +# Create 1GB disk image (larger for kernel + bootloader) +dd if=/dev/zero of=/output/sparkos.img bs=1M count=1024 + +# Create GPT partition table +echo "Creating GPT partition table..." +parted -s /output/sparkos.img mklabel gpt + +# Create EFI System Partition (ESP) - 200MB, FAT32 +echo "Creating EFI System Partition..." +parted -s /output/sparkos.img mkpart ESP fat32 1MiB 201MiB +parted -s /output/sparkos.img set 1 esp on + +# Create root partition - remaining space, ext4 +echo "Creating root partition..." +parted -s /output/sparkos.img mkpart primary ext4 201MiB 100% + +# Use libguestfs to format and populate partitions without loop devices +echo "Formatting partitions using guestfish..." +guestfish -a /output/sparkos.img <<'EOF' +run +mkfs vfat /dev/sda1 label:SPARKOSEFI +mkfs ext4 /dev/sda2 label:SparkOS +mount /dev/sda2 / +mkdir-p /boot +EOF + +# Prepare ESP contents +echo "Preparing ESP contents..." + +# Prepare ESP contents +echo "Preparing ESP contents..." +mkdir -p /staging/esp/EFI/BOOT +mkdir -p /staging/esp/boot/grub + +# Create GRUB EFI binary using grub-mkstandalone +grub-mkstandalone \ + --format=x86_64-efi \ + --output=/staging/esp/EFI/BOOT/BOOTX64.EFI \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=/dev/null" + +# Find the kernel +KERNEL_PATH=$(find /kernel/boot -name "vmlinuz-*" | head -1) +KERNEL_VERSION=$(basename $KERNEL_PATH | sed 's/vmlinuz-//') +INITRD_PATH=$(find /kernel/boot -name "initrd.img-*" | head -1) + +# Copy kernel and initrd to staging +echo "Copying kernel to staging..." +cp $KERNEL_PATH /staging/esp/boot/vmlinuz + +# Ensure initrd exists (required for booting) +if [ ! -f "$INITRD_PATH" ]; then + echo "ERROR: initrd not found. The kernel requires an initrd to boot." + echo "Expected to find: initrd.img-* in /kernel/boot/" + exit 1 +fi +echo "Copying initrd to staging..." +cp $INITRD_PATH /staging/esp/boot/initrd.img + +# Create GRUB configuration +printf '%s\n' \ + 'set timeout=3' \ + 'set default=0' \ + '' \ + 'menuentry "SparkOS" {' \ + ' linux /boot/vmlinuz root=LABEL=SparkOS rw init=/sbin/init console=tty1 quiet' \ + ' initrd /boot/initrd.img' \ + '}' \ + > /staging/esp/boot/grub/grub.cfg + +# Prepare root filesystem contents +echo "Preparing root filesystem..." +mkdir -p /staging/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} + +# Install SparkOS init +cp /build/init /staging/root/sbin/init +chmod 755 /staging/root/sbin/init + +# Create system configuration files +echo "sparkos" > /staging/root/etc/hostname +echo "127.0.0.1 localhost" > /staging/root/etc/hosts +echo "127.0.1.1 sparkos" >> /staging/root/etc/hosts + +# Copy README to root partition +cp /build/config/image-readme.txt /staging/root/README.txt + +# Copy ESP contents to the image +echo "Populating EFI System Partition..." +guestfish -a /output/sparkos.img <<'EOF' +run +mount /dev/sda1 / +mkdir-p /EFI +mkdir-p /EFI/BOOT +mkdir-p /boot +mkdir-p /boot/grub +EOF + +# Copy files using guestfish +echo "Copying bootloader files..." +guestfish -a /output/sparkos.img -m /dev/sda1 < /tmp/embedded_grub.cfg + +# Create GRUB EFI binary using grub-mkstandalone with embedded bootstrap config +# Include essential modules for better hardware compatibility +grub-mkstandalone \ + --format=x86_64-efi \ + --output=/staging/esp/EFI/BOOT/BOOTX64.EFI \ + --locales="" \ + --fonts="" \ + --modules="part_gpt part_msdos fat ext2 normal linux \ + all_video video_bochs video_cirrus gfxterm \ + search search_label search_fs_uuid" \ + "boot/grub/grub.cfg=/tmp/embedded_grub.cfg" + +# Find the kernel +KERNEL_PATH=$(find /kernel/boot -name "vmlinuz-*" | head -1) +KERNEL_VERSION=$(basename $KERNEL_PATH | sed 's/vmlinuz-//') +INITRD_PATH=$(find /kernel/boot -name "initrd.img-*" | head -1) + +# Copy kernel and initrd to staging +echo "Copying kernel to staging..." +cp $KERNEL_PATH /staging/esp/boot/vmlinuz + +# Ensure initrd exists (required for booting) +if [ ! -f "$INITRD_PATH" ]; then + echo "ERROR: initrd not found. The kernel requires an initrd to boot." + echo "Expected to find: initrd.img-* in /kernel/boot/" + exit 1 +fi +echo "Copying initrd to staging..." +cp $INITRD_PATH /staging/esp/boot/initrd.img + +# Create GRUB configuration from template +sed "s/@ROOT_LABEL@/$ROOT_LABEL/g" /build/config/grub.cfg.in > /staging/esp/boot/grub/grub.cfg + +# Prepare root filesystem contents +echo "Preparing root filesystem..." +mkdir -p /staging/root/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var/{log,run},root,home/spark,boot} + +# Install SparkOS init +cp /build/init /staging/root/sbin/init +chmod 755 /staging/root/sbin/init + +# Create system configuration files +echo "sparkos" > /staging/root/etc/hostname +echo "127.0.0.1 localhost" > /staging/root/etc/hosts +echo "127.0.1.1 sparkos" >> /staging/root/etc/hosts + +# Copy README to root partition +cp /build/config/image-readme.txt /staging/root/README.txt + +# Create 1GB disk image +echo "Creating disk image..." +dd if=/dev/zero of=/output/sparkos.img bs=1M count=1024 + +# Create GPT partition table using sgdisk +echo "Creating GPT partition table..." +sgdisk -Z /output/sparkos.img 2>/dev/null || true +sgdisk -n 1:2048:411647 -t 1:ef00 -c 1:"EFI System" /output/sparkos.img +sgdisk -n 2:411648:0 -t 2:8300 -c 2:"Linux filesystem" /output/sparkos.img + +# Extract partition regions using dd +echo "Extracting partition regions..." +dd if=/output/sparkos.img of=/tmp/esp.img bs=512 skip=2048 count=409600 2>/dev/null + +# Calculate exact size for root partition +ROOT_START=411648 +ROOT_END=$(sgdisk -p /output/sparkos.img 2>/dev/null | grep "^ 2" | awk '{print $3}') +ROOT_SIZE=$((ROOT_END - ROOT_START + 1)) +echo "Root partition: start=$ROOT_START, end=$ROOT_END, size=$ROOT_SIZE sectors" + +dd if=/output/sparkos.img of=/tmp/root.img bs=512 skip=$ROOT_START count=$ROOT_SIZE 2>/dev/null + +# Format ESP partition (FAT32) +echo "Formatting EFI System Partition (FAT32)..." +mkfs.vfat -F 32 -n "$ESP_LABEL" /tmp/esp.img >/dev/null + +# Populate ESP using mtools (no mount needed!) +echo "Populating ESP with bootloader and kernel..." +export MTOOLS_SKIP_CHECK=1 +mmd -i /tmp/esp.img ::/EFI +mmd -i /tmp/esp.img ::/EFI/BOOT +mmd -i /tmp/esp.img ::/boot +mmd -i /tmp/esp.img ::/boot/grub +mcopy -i /tmp/esp.img /staging/esp/EFI/BOOT/BOOTX64.EFI ::/EFI/BOOT/ +mcopy -i /tmp/esp.img /staging/esp/boot/vmlinuz ::/boot/ +mcopy -i /tmp/esp.img /staging/esp/boot/initrd.img ::/boot/ +mcopy -i /tmp/esp.img /staging/esp/boot/grub/grub.cfg ::/boot/grub/ + +# Format root partition (ext4) with directory contents (no mount needed!) +echo "Formatting root partition (ext4) and populating..." +mke2fs -t ext4 -L "$ROOT_LABEL" -d /staging/root /tmp/root.img >/dev/null 2>&1 + +# Write partitions back to image +echo "Writing partitions to image..." +dd if=/tmp/esp.img of=/output/sparkos.img bs=512 seek=2048 count=409600 conv=notrunc 2>/dev/null +dd if=/tmp/root.img of=/output/sparkos.img bs=512 seek=$ROOT_START count=$ROOT_SIZE conv=notrunc 2>/dev/null + +# Clean up temporary files +rm -f /tmp/esp.img /tmp/root.img + +# Finalize +echo "Finalizing image..." +sync + +# Compress the image +echo "Compressing image..." +gzip -9 /output/sparkos.img +echo "UEFI-bootable image created: /output/sparkos.img.gz" diff --git a/sparkos/scripts/docker-image-download-kernel.sh b/sparkos/scripts/docker-image-download-kernel.sh new file mode 100755 index 000000000..de33f0f9a --- /dev/null +++ b/sparkos/scripts/docker-image-download-kernel.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# Download a minimal Linux kernel for UEFI image + +set -e + +echo "=== Downloading Linux kernel from Ubuntu repositories ===" + +mkdir -p /kernel +apt-get update + +# Install initramfs-tools for generating initrd +echo "Installing initramfs-tools..." +apt-get install -y initramfs-tools + +# Get the actual kernel package name (not the metapackage) +echo "Finding latest kernel package..." +KERNEL_PKG=$(apt-cache depends linux-image-generic | grep -E 'Depends.*linux-image-[0-9]' | head -1 | awk '{print $2}') + +if [ -z "$KERNEL_PKG" ]; then + echo "ERROR: Could not determine kernel package name" + exit 1 +fi + +echo "Downloading kernel package: $KERNEL_PKG" +apt-get download "$KERNEL_PKG" + +# Extract the kernel package to /kernel +echo "Extracting kernel..." +dpkg -x "${KERNEL_PKG}"*.deb /kernel + +# Verify kernel was extracted +if [ ! -d /kernel/boot ]; then + echo "ERROR: Kernel boot directory not found after extraction" + exit 1 +fi + +KERNEL_FILE=$(find /kernel/boot -name "vmlinuz-*" | head -1) +if [ -z "$KERNEL_FILE" ]; then + echo "ERROR: No kernel image found" + exit 1 +fi + +echo "Kernel extracted successfully: $KERNEL_FILE" + +# Extract kernel version from the kernel filename +KERNEL_VERSION=$(basename "$KERNEL_FILE" | sed 's/vmlinuz-//') +echo "Kernel version: $KERNEL_VERSION" + +# Copy kernel modules to system location so mkinitramfs can find them +echo "Copying kernel modules to system location..." +if [ -d "/kernel/lib/modules/${KERNEL_VERSION}" ]; then + cp -r "/kernel/lib/modules/${KERNEL_VERSION}" /lib/modules/ +else + echo "WARNING: No modules found for kernel ${KERNEL_VERSION}" +fi + +# Generate initrd using mkinitramfs +echo "Generating initrd for kernel version $KERNEL_VERSION..." +mkinitramfs -o "/kernel/boot/initrd.img-${KERNEL_VERSION}" "${KERNEL_VERSION}" + +# Verify initrd was created +if [ ! -f "/kernel/boot/initrd.img-${KERNEL_VERSION}" ]; then + echo "ERROR: Failed to generate initrd" + exit 1 +fi + +echo "Initrd generated successfully: /kernel/boot/initrd.img-${KERNEL_VERSION}" +ls -lh /kernel/boot/ + +# Clean up +rm -rf /var/lib/apt/lists/* "${KERNEL_PKG}"*.deb diff --git a/sparkos/scripts/docker-image-install-tools.sh b/sparkos/scripts/docker-image-install-tools.sh new file mode 100755 index 000000000..ff61da711 --- /dev/null +++ b/sparkos/scripts/docker-image-install-tools.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Install required tools for building UEFI-bootable SparkOS image + +set -e + +apt-get update +apt-get install -y \ + gcc \ + g++ \ + make \ + cmake \ + qt6-base-dev \ + qt6-base-dev-tools \ + libqt6core6 \ + libqt6gui6 \ + libqt6widgets6 \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + dosfstools \ + mtools \ + e2fsprogs \ + parted \ + gdisk \ + grub-efi-amd64-bin \ + grub-common \ + wget \ + kmod \ + udev +rm -rf /var/lib/apt/lists/* diff --git a/sparkos/scripts/docker-install-packages.sh b/sparkos/scripts/docker-install-packages.sh new file mode 100755 index 000000000..2bc58b1d6 --- /dev/null +++ b/sparkos/scripts/docker-install-packages.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Install runtime packages for Alpine-based Docker image + +set -e + +apk add --no-cache file diff --git a/sparkos/scripts/docker-release.sh b/sparkos/scripts/docker-release.sh new file mode 100755 index 000000000..cd33d2848 --- /dev/null +++ b/sparkos/scripts/docker-release.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# SparkOS Docker-based Release Builder +# Build release artifacts using Docker (no root required) + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +RELEASE_DIR="$PROJECT_ROOT/release" +VERSION="${1:-dev}" + +echo "SparkOS Docker Release Builder" +echo "==============================" +echo "" +echo "Version: $VERSION" +echo "" + +# Clean previous release +if [ -d "$RELEASE_DIR" ]; then + echo "Cleaning previous release..." + rm -rf "$RELEASE_DIR" +fi + +mkdir -p "$RELEASE_DIR" + +# Build using Docker (multi-stage build) +echo "Building init binary using Docker..." +docker build -t sparkos:build-temp --target builder "$PROJECT_ROOT" + +# Extract the built binary +echo "Extracting init binary..." +CONTAINER_ID=$(docker create sparkos:build-temp) +docker cp "$CONTAINER_ID:/build/init" "$RELEASE_DIR/init" +docker rm "$CONTAINER_ID" > /dev/null + +# Verify the binary +echo "" +echo "Verifying init binary..." +if [ ! -f "$RELEASE_DIR/init" ]; then + echo "ERROR: Failed to extract init binary" + exit 1 +fi + +ls -lh "$RELEASE_DIR/init" +file "$RELEASE_DIR/init" + +# Create release package structure +echo "" +echo "Creating release package..." +mkdir -p "$RELEASE_DIR/sparkos" + +# Copy compiled binary +cp "$RELEASE_DIR/init" "$RELEASE_DIR/sparkos/" + +# Copy essential files +cp "$PROJECT_ROOT/README.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/LICENSE" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/ARCHITECTURE.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/CONTRIBUTING.md" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/Makefile" "$RELEASE_DIR/sparkos/" +cp "$PROJECT_ROOT/Dockerfile" "$RELEASE_DIR/sparkos/" + +# Copy source for reference +cp -r "$PROJECT_ROOT/src" "$RELEASE_DIR/sparkos/" + +# Copy scripts +cp -r "$PROJECT_ROOT/scripts" "$RELEASE_DIR/sparkos/" + +# Copy config +cp -r "$PROJECT_ROOT/config" "$RELEASE_DIR/sparkos/" + +# Copy rootfs structure (without generated content) +mkdir -p "$RELEASE_DIR/sparkos/rootfs" +for dir in etc root home; do + if [ -d "$PROJECT_ROOT/rootfs/$dir" ]; then + cp -r "$PROJECT_ROOT/rootfs/$dir" "$RELEASE_DIR/sparkos/rootfs/" + fi +done + +# Create README for the release +cat > "$RELEASE_DIR/sparkos/RELEASE_README.md" << 'EOF' +# SparkOS Release Package + +This package contains the compiled SparkOS init system and all necessary files to run or build SparkOS. + +## Contents + +- `init` - The compiled init binary (statically linked) +- `src/` - Source code for the init system +- `scripts/` - Build and setup scripts +- `config/` - Configuration files +- `rootfs/` - Root filesystem structure +- `Dockerfile` - Docker image definition +- `Makefile` - Build system +- Documentation files (README.md, ARCHITECTURE.md, etc.) + +## Quick Start + +### Using the Pre-built Binary + +The `init` binary is already compiled and ready to use: + +```bash +# Copy to your rootfs +cp init /path/to/your/rootfs/sbin/init +chmod 755 /path/to/your/rootfs/sbin/init +``` + +### Using Docker + +The easiest way to test SparkOS: + +```bash +# Build the Docker image +docker build -t sparkos . + +# Run the test environment +docker run --rm sparkos +``` + +### Rebuilding from Source + +If you need to rebuild: + +```bash +# Build the init system +make init + +# Install to rootfs +make install +``` + +## Using Docker for Releases + +Build release artifacts without needing root or special tools: + +```bash +# Build release package +./scripts/docker-release.sh v1.0.0 + +# The release package will be in release/sparkos-release.zip +``` + +## System Requirements + +- Linux system with kernel 3.x or later with framebuffer support +- Qt6 runtime libraries for GUI +- For building: Docker or GCC compiler and Make + +## Documentation + +See README.md for complete documentation, including: +- Building instructions +- Docker usage +- Network configuration +- Development guidelines + +## Support + +For issues and questions, visit: https://github.com/johndoe6345789/SparkOS +EOF + +# Create release archive +echo "" +echo "Creating release archive..." +cd "$RELEASE_DIR" +zip -q -r "sparkos-release.zip" sparkos/ +cd "$PROJECT_ROOT" + +echo "" +echo "SUCCESS! Release package created:" +echo " Location: $RELEASE_DIR/sparkos-release.zip" +echo " Size: $(du -h "$RELEASE_DIR/sparkos-release.zip" | cut -f1)" +echo "" +echo "Contents:" +ls -lh "$RELEASE_DIR/sparkos-release.zip" +echo "" +echo "To verify the contents:" +echo " unzip -l $RELEASE_DIR/sparkos-release.zip | head -40" +echo "" +echo "To test the Docker image:" +echo " docker build -t sparkos:$VERSION ." +echo " docker run --rm sparkos:$VERSION" diff --git a/sparkos/scripts/docker-setup-config.sh b/sparkos/scripts/docker-setup-config.sh new file mode 100755 index 000000000..d3a1336d2 --- /dev/null +++ b/sparkos/scripts/docker-setup-config.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Set up basic configuration files for SparkOS + +set -e + +# Create hostname +echo "sparkos" > /sparkos/rootfs/etc/hostname + +# Create hosts file +echo "127.0.0.1 localhost" > /sparkos/rootfs/etc/hosts +echo "127.0.1.1 sparkos" >> /sparkos/rootfs/etc/hosts + +# Create passwd file +echo "root:x:0:0:root:/root:/bin/sh" > /sparkos/rootfs/etc/passwd +echo "spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh" >> /sparkos/rootfs/etc/passwd + +# Create group file +echo "root:x:0:" > /sparkos/rootfs/etc/group +echo "spark:x:1000:" >> /sparkos/rootfs/etc/group diff --git a/sparkos/scripts/docker-setup-rootfs.sh b/sparkos/scripts/docker-setup-rootfs.sh new file mode 100755 index 000000000..f86bffe80 --- /dev/null +++ b/sparkos/scripts/docker-setup-rootfs.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Create minimal rootfs structure for SparkOS + +set -e + +# Create minimal rootfs structure +mkdir -p /sparkos/rootfs/bin \ + /sparkos/rootfs/sbin \ + /sparkos/rootfs/etc \ + /sparkos/rootfs/proc \ + /sparkos/rootfs/sys \ + /sparkos/rootfs/dev \ + /sparkos/rootfs/tmp \ + /sparkos/rootfs/usr/bin \ + /sparkos/rootfs/usr/sbin \ + /sparkos/rootfs/usr/lib \ + /sparkos/rootfs/var/log \ + /sparkos/rootfs/var/run \ + /sparkos/rootfs/root \ + /sparkos/rootfs/home/spark + +# Set proper permissions +chmod 1777 /sparkos/rootfs/tmp +chmod 700 /sparkos/rootfs/root +chmod 755 /sparkos/rootfs/home/spark diff --git a/sparkos/scripts/install.sh b/sparkos/scripts/install.sh new file mode 100755 index 000000000..6ff07d5a1 --- /dev/null +++ b/sparkos/scripts/install.sh @@ -0,0 +1,228 @@ +#!/bin/bash +# SparkOS Installation Script +# Writes the SparkOS image to a target drive +# Note: This script runs on the host system and uses bash for ${BASH_SOURCE} + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +IMAGE_FILE="$PROJECT_ROOT/sparkos.img" + +# Cleanup function +cleanup() { + local exit_code=$? + if [ $exit_code -ne 0 ]; then + echo "" + echo "ERROR: Installation failed" + fi + exit $exit_code +} + +# Set trap for cleanup on exit, interrupt, or error +trap cleanup EXIT INT TERM + +# Print usage +usage() { + cat << EOF +SparkOS Installation Script +=========================== + +Usage: $0 + +Arguments: + target_drive Block device to install to (e.g., /dev/sdb, /dev/nvme1n1) + +Example: + sudo $0 /dev/sdb + sudo $0 /dev/nvme1n1 + +WARNING: This will DESTROY all data on the target drive! + +EOF + exit 1 +} + +# Print header +echo "SparkOS Installation Script" +echo "===========================" +echo "" + +# Check if running as root +if [ "$(id -u)" -ne 0 ]; then + echo "ERROR: This script must be run as root" + echo "Usage: sudo $0 " + exit 1 +fi + +# Check for target drive argument +if [ $# -eq 0 ]; then + echo "ERROR: No target drive specified" + echo "" + usage +fi + +TARGET_DRIVE="$1" + +# Validate image file exists +if [ ! -f "$IMAGE_FILE" ]; then + echo "ERROR: SparkOS image not found: $IMAGE_FILE" + echo "" + echo "Please build the image first:" + echo " make all" + echo " sudo make image" + exit 1 +fi + +# Validate target drive exists +if [ ! -e "$TARGET_DRIVE" ]; then + echo "ERROR: Target drive does not exist: $TARGET_DRIVE" + echo "" + echo "Available block devices:" + lsblk -d -o NAME,SIZE,TYPE,MODEL | grep -E '^NAME|disk' || echo " No block devices found" + exit 1 +fi + +# Validate target is a block device +if [ ! -b "$TARGET_DRIVE" ]; then + echo "ERROR: Target is not a block device: $TARGET_DRIVE" + echo "" + echo "Please specify a block device (e.g., /dev/sdb, /dev/nvme1n1)" + exit 1 +fi + +# Check for required tool +if ! command -v dd &> /dev/null; then + echo "ERROR: Required tool 'dd' is not installed" + exit 1 +fi + +# Get drive information +DRIVE_SIZE=$(lsblk -b -d -n -o SIZE "$TARGET_DRIVE" 2>/dev/null || echo "unknown") +DRIVE_MODEL=$(lsblk -d -n -o MODEL "$TARGET_DRIVE" 2>/dev/null || echo "unknown") +IMAGE_SIZE=$(stat -c%s "$IMAGE_FILE" 2>/dev/null || echo "unknown") + +# Check if target drive is large enough +if [ "$DRIVE_SIZE" != "unknown" ] && [ "$IMAGE_SIZE" != "unknown" ]; then + if [ "$DRIVE_SIZE" -lt "$IMAGE_SIZE" ]; then + echo "ERROR: Target drive is too small" + echo " Drive size: $(numfmt --to=iec-i --suffix=B $DRIVE_SIZE 2>/dev/null || echo $DRIVE_SIZE)" + echo " Image size: $(numfmt --to=iec-i --suffix=B $IMAGE_SIZE 2>/dev/null || echo $IMAGE_SIZE)" + exit 1 + fi +fi + +# Display warning and drive information +echo "⚠️ WARNING: DATA DESTRUCTION IMMINENT ⚠️" +echo "" +echo "This will completely erase all data on the target drive!" +echo "" +echo "Target drive information:" +echo " Device: $TARGET_DRIVE" +echo " Model: $DRIVE_MODEL" +if [ "$DRIVE_SIZE" != "unknown" ]; then + echo " Size: $(numfmt --to=iec-i --suffix=B $DRIVE_SIZE 2>/dev/null || echo $DRIVE_SIZE)" +fi +echo "" +echo "Image information:" +echo " File: $IMAGE_FILE" +if [ "$IMAGE_SIZE" != "unknown" ]; then + echo " Size: $(numfmt --to=iec-i --suffix=B $IMAGE_SIZE 2>/dev/null || echo $IMAGE_SIZE)" +fi +echo "" + +# Show mounted partitions on target drive +MOUNTED=$(lsblk -n -o MOUNTPOINT "$TARGET_DRIVE" 2>/dev/null | grep -v '^[[:space:]]*$' || true) +if [ -n "$MOUNTED" ]; then + echo "WARNING: The following partitions on $TARGET_DRIVE are currently mounted:" + lsblk -o NAME,MOUNTPOINT "$TARGET_DRIVE" + echo "" + echo "Please unmount all partitions before proceeding" + exit 1 +fi + +# Require user confirmation +echo "Are you absolutely sure you want to proceed?" +echo -n "Type 'YES' (in all caps) to confirm: " +read CONFIRMATION + +if [ "$CONFIRMATION" != "YES" ]; then + echo "" + echo "Installation cancelled by user" + exit 0 +fi + +echo "" +echo "Starting installation..." +echo "" + +# Unmount any partitions (just to be safe) +# Use lsblk to get actual partition names (works for all device types including NVMe) +PARTITIONS=$(lsblk -ln -o NAME "$TARGET_DRIVE" 2>/dev/null | tail -n +2 | sed 's|^|/dev/|' || true) +if [ -n "$PARTITIONS" ]; then + for part in $PARTITIONS; do + if mountpoint -q "$part" 2>/dev/null; then + echo "Unmounting $part..." + umount "$part" 2>/dev/null || true + fi + done +fi + +# Write image to drive with progress +echo "Writing SparkOS image to $TARGET_DRIVE..." +echo "This may take several minutes..." +echo "" + +if dd if="$IMAGE_FILE" of="$TARGET_DRIVE" bs=4M status=progress conv=fsync 2>&1; then + echo "" + echo "Image write completed successfully" +else + echo "" + echo "ERROR: Failed to write image to drive" + exit 1 +fi + +# Sync to ensure all data is written +echo "" +echo "Syncing data to disk..." +sync + +# Verify installation by reading back the first few blocks +echo "Verifying installation..." +VERIFY_BLOCKS=1024 +VERIFY_TMP=$(mktemp -t sparkos_verify.XXXXXXXX) +SOURCE_TMP=$(mktemp -t sparkos_source.XXXXXXXX) + +# Ensure temp files are cleaned up on exit +trap "rm -f $VERIFY_TMP $SOURCE_TMP; cleanup" EXIT INT TERM + +dd if="$TARGET_DRIVE" of="$VERIFY_TMP" bs=512 count=$VERIFY_BLOCKS status=none 2>/dev/null +dd if="$IMAGE_FILE" of="$SOURCE_TMP" bs=512 count=$VERIFY_BLOCKS status=none 2>/dev/null + +if cmp -s "$VERIFY_TMP" "$SOURCE_TMP"; then + echo "✓ Verification successful - installation completed!" +else + echo "✗ Verification failed - installation may be corrupted" + rm -f "$VERIFY_TMP" "$SOURCE_TMP" + exit 1 +fi + +# Clean up verification files +rm -f "$VERIFY_TMP" "$SOURCE_TMP" + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "SUCCESS! SparkOS has been installed to $TARGET_DRIVE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "You can now:" +echo " 1. Safely remove the drive" +echo " 2. Boot from the drive" +echo " 3. Log in as user 'spark'" +echo "" +echo "First boot instructions:" +echo " - The system will boot with wired networking enabled" +echo " - Default user: spark (full sudo access)" +echo " - Run ~/clone-sparkos.sh to install spark CLI" +echo " - Use spark CLI to configure WiFi and system" +echo "" diff --git a/sparkos/scripts/setup_rootfs.sh b/sparkos/scripts/setup_rootfs.sh new file mode 100755 index 000000000..8a936c634 --- /dev/null +++ b/sparkos/scripts/setup_rootfs.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# SparkOS Setup Script +# Sets up a minimal rootfs for GUI-only SparkOS +# Note: This script runs on the host system and uses bash for ${BASH_SOURCE} + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +ROOTFS_DIR="$PROJECT_ROOT/rootfs" + +echo "SparkOS Root Filesystem Setup" +echo "==============================" +echo "" + +# Create directory structure +echo "Creating directory structure..." +mkdir -p "$ROOTFS_DIR"/{sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var,root} +mkdir -p "$ROOTFS_DIR/var"/{log,run} + +# Set permissions +chmod 1777 "$ROOTFS_DIR/tmp" +chmod 700 "$ROOTFS_DIR/root" + +# Create basic config files +echo "Creating configuration files..." + +# /etc/hostname +echo "sparkos" > "$ROOTFS_DIR/etc/hostname" + +# /etc/hosts +cat > "$ROOTFS_DIR/etc/hosts" << 'EOF' +127.0.0.1 localhost +127.0.1.1 sparkos +::1 localhost ip6-localhost ip6-loopback +EOF + +# /etc/fstab +cat > "$ROOTFS_DIR/etc/fstab" << 'EOF' +# +proc /proc proc defaults 0 0 +sysfs /sys sysfs defaults 0 0 +devtmpfs /dev devtmpfs defaults 0 0 +tmpfs /tmp tmpfs defaults 0 0 +EOF + +# /etc/resolv.conf - DNS configuration (managed by Qt6 GUI) +cat > "$ROOTFS_DIR/etc/resolv.conf" << 'EOF' +# SparkOS DNS Configuration +# Managed by Qt6 GUI NetworkManager +# Fallback to public DNS servers +nameserver 8.8.8.8 +nameserver 8.8.4.4 +nameserver 1.1.1.1 +nameserver 1.0.0.1 +EOF + +# Create README +cat > "$ROOTFS_DIR/README.txt" << 'EOF' +SparkOS Root Filesystem +======================= + +This is the root filesystem for SparkOS, a GUI-only Linux distribution. + +SparkOS Philosophy: + - GUI-Only: No CLI tools, no shell, no Unix utilities + - Network-First: Networking integrated into Qt6 GUI + - Direct Kernel Interface: Qt6 communicates directly with Linux kernel + - No Unix Baggage: No users, groups, passwords, or authentication + +Minimal System: + - Linux Kernel (with networking and framebuffer support) + - SparkOS Init System (completely self-contained, no dependencies) + - Qt6 GUI Application (all user interaction) + +Directory Structure: + /sbin - Init binary only + /etc - Minimal configuration files + /proc, /sys, /dev - Kernel interfaces + /tmp - Temporary files + /usr - Qt6 GUI application and libraries + /var - Variable data (overlay mount) + /root - Root home directory + +Network Configuration: + - Managed entirely through Qt6 GUI + - Init brings up interfaces via direct ioctl calls + - DHCP and network management handled by Qt6 NetworkManager + - /etc/resolv.conf provides fallback DNS servers + +Boot Process: + 1. Linux kernel loads + 2. Init (PID 1) mounts filesystems + 3. Init brings up network interfaces + 4. Init spawns Qt6 GUI application + 5. All user interaction through GUI + +Note: This is a minimal, GUI-only system. + No shell, no CLI tools, no busybox. + All functionality is provided through the Qt6 GUI application. +EOF + +echo "" +echo "Root filesystem structure created at: $ROOTFS_DIR" +echo "" +echo "SparkOS Configuration:" +echo " - Architecture: GUI-only, no CLI" +echo " - Init: Self-contained, no external dependencies" +echo " - Network: Direct C implementation via ioctl" +echo " - User Experience: Pure Qt6 GUI" +echo "" +echo "Next steps:" +echo " 1. Build init: make init" +echo " 2. Install init: make install" +echo " 3. Build Qt6 GUI: make gui" +echo " 4. Create bootable image: sudo make image" +echo "" +echo "Philosophy:" +echo " No busybox, no shell, no CLI tools" +echo " Everything is GUI-driven through Qt6" diff --git a/sparkos/scripts/test.sh b/sparkos/scripts/test.sh new file mode 100644 index 000000000..4ed84aab6 --- /dev/null +++ b/sparkos/scripts/test.sh @@ -0,0 +1,45 @@ +#!/bin/sh +echo "SparkOS Docker Test Environment" +echo "================================" +echo "" + +echo "Verifying SparkOS init binary..." +echo "--------------------------------" +if [ -f /sparkos/rootfs/sbin/init ]; then + echo "✓ Init binary exists" + ls -lh /sparkos/rootfs/sbin/init + echo "" + echo "File type:" + if command -v file >/dev/null 2>&1; then + file /sparkos/rootfs/sbin/init + else + echo " (file command not available)" + fi + echo "" + echo "Dependencies:" + ldd /sparkos/rootfs/sbin/init 2>&1 || echo " Static binary (no dependencies)" +else + echo "✗ Init binary not found!" + exit 1 +fi + +echo "" +echo "Root filesystem structure:" +echo "--------------------------" +ls -la /sparkos/rootfs/ +echo "" +echo "================================" +echo "✓ SparkOS is ready for testing!" +echo "================================" +echo "" +echo "Summary:" +echo " - Init: Custom SparkOS init system (no external dependencies)" +echo " - Architecture: GUI-only, no CLI/shell" +echo " - Network: Direct C implementation via ioctl" +echo " - Philosophy: Pure GUI experience, network-first" +echo "" +echo "Note: SparkOS has no CLI tools (no busybox, no shell)" +echo " All functionality is provided through the Qt6 GUI" +echo "" +echo "To test the init system:" +echo " docker run --rm /sparkos/rootfs/sbin/init" \ No newline at end of file diff --git a/sparkos/src/init.c b/sparkos/src/init.c new file mode 100644 index 000000000..59304b600 --- /dev/null +++ b/sparkos/src/init.c @@ -0,0 +1,261 @@ +/* + * SparkOS Init - Minimal init system for SparkOS + * This is the first process that runs after the kernel boots + * + * SparkOS Philosophy: GUI-only, no CLI, network-first + * - No shell spawning or CLI utilities + * - Direct boot to Qt6 GUI + * - Network initialization via direct C system calls + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void signal_handler(int sig) { + if (sig == SIGCHLD) { + // Reap zombie processes + while (waitpid(-1, NULL, WNOHANG) > 0); + } +} + +static void spawn_gui() { + pid_t pid = fork(); + + if (pid < 0) { + perror("fork failed"); + return; + } + + if (pid == 0) { + // Child process - exec Qt6 GUI application as root (no user switching) + + char *argv[] = {"/usr/bin/sparkos-gui", NULL}; + char *envp[] = { + "HOME=/root", + "PATH=/usr/bin:/usr/sbin", + "QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0", + "QT_QPA_FB_FORCE_FULLSCREEN=1", + "QT_QPA_FONTDIR=/usr/share/fonts", + NULL + }; + + execve("/usr/bin/sparkos-gui", argv, envp); + + perror("failed to exec GUI application"); + exit(1); + } + + // Parent process - wait for GUI to exit + int status; + waitpid(pid, &status, 0); +} + +/* + * Initialize network interface directly via ioctl + * No dependency on busybox or CLI tools + */ +static int init_network_interface(const char *ifname) { + int sock; + struct ifreq ifr; + + // Create socket for ioctl operations + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket creation failed"); + return -1; + } + + // Prepare interface request structure + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; // Ensure null termination + + // Get current flags + if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { + close(sock); + return -1; // Interface doesn't exist + } + + // Bring interface up + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) { + perror("failed to bring up interface"); + close(sock); + return -1; + } + + close(sock); + printf("Network interface %s brought up successfully\n", ifname); + return 0; +} + +/* + * Initialize networking without external dependencies + * Brings up loopback and first available ethernet interface + */ +static void init_network() { + const char *interfaces[] = {"lo", "eth0", "enp0s3", "enp0s8", "ens33", NULL}; + int i; + int eth_initialized = 0; + + printf("Initializing network interfaces...\n"); + + // Bring up loopback + if (init_network_interface("lo") == 0) { + printf("Loopback interface initialized\n"); + } + + // Try to bring up first available ethernet interface + for (i = 1; interfaces[i] != NULL && !eth_initialized; i++) { + if (init_network_interface(interfaces[i]) == 0) { + printf("Primary network interface %s initialized\n", interfaces[i]); + printf("Note: DHCP configuration should be handled by Qt6 GUI\n"); + eth_initialized = 1; + } + } + + if (!eth_initialized) { + fprintf(stderr, "Warning: No ethernet interface found or initialized\n"); + fprintf(stderr, "Network configuration will be available through GUI\n"); + } +} + +/* + * Mount filesystem using direct mount() system call + * No dependency on mount binary + */ +static int mount_fs(const char *source, const char *target, const char *fstype, unsigned long flags) { + if (mount(source, target, fstype, flags, NULL) < 0) { + return -1; + } + return 0; +} + +/* + * Create directory recursively + * No dependency on mkdir binary + */ +static int mkdir_p(const char *path) { + char tmp[PATH_MAX]; + char *p = NULL; + size_t len; + + len = strlen(path); + if (len >= PATH_MAX) { + errno = ENAMETOOLONG; + return -1; + } + + snprintf(tmp, sizeof(tmp), "%s", path); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + if (mkdir(tmp, 0755) < 0 && errno != EEXIST) { + return -1; + } + *p = '/'; + } + } + + if (mkdir(tmp, 0755) < 0 && errno != EEXIST) { + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) { + printf("SparkOS Init System Starting...\n"); + printf("================================\n"); + printf("Philosophy: GUI-Only, No CLI, Network-First\n"); + printf("================================\n\n"); + + // Make sure we're PID 1 + if (getpid() != 1) { + fprintf(stderr, "init must be run as PID 1\n"); + return 1; + } + + // Set up signal handlers + signal(SIGCHLD, signal_handler); + + // Mount essential filesystems using direct system calls + printf("Mounting essential filesystems...\n"); + if (mount_fs("proc", "/proc", "proc", 0) != 0) { + fprintf(stderr, "Warning: Failed to mount /proc\n"); + } + if (mount_fs("sysfs", "/sys", "sysfs", 0) != 0) { + fprintf(stderr, "Warning: Failed to mount /sys\n"); + } + if (mount_fs("devtmpfs", "/dev", "devtmpfs", 0) != 0) { + fprintf(stderr, "Warning: Failed to mount /dev\n"); + } + if (mount_fs("tmpfs", "/tmp", "tmpfs", 0) != 0) { + fprintf(stderr, "Warning: Failed to mount /tmp\n"); + } + + // Set up overlay filesystem for immutable base OS + printf("Setting up overlay filesystem for writable layer...\n"); + + // Create overlay directories in tmpfs + if (mkdir_p("/tmp/overlay/var-upper") != 0 || mkdir_p("/tmp/overlay/var-work") != 0) { + fprintf(stderr, "Warning: Failed to create overlay directories for /var\n"); + } + + // Mount overlay on /var for logs and runtime data + char overlay_opts[256]; + snprintf(overlay_opts, sizeof(overlay_opts), + "lowerdir=/var,upperdir=/tmp/overlay/var-upper,workdir=/tmp/overlay/var-work"); + if (mount("overlay", "/var", "overlay", 0, overlay_opts) != 0) { + fprintf(stderr, "Warning: Failed to mount overlay on /var - system may be read-only\n"); + } else { + printf("Overlay filesystem mounted on /var (base OS is immutable)\n"); + } + + // Mount tmpfs on /run for runtime data + if (mkdir_p("/run") == 0) { + if (mount_fs("tmpfs", "/run", "tmpfs", 0) != 0) { + fprintf(stderr, "Warning: Failed to mount /run\n"); + } + } + + // Initialize network interfaces + init_network(); + + printf("\nStarting Qt6 GUI application...\n"); + printf("Welcome to SparkOS!\n"); + printf("===================\n"); + printf("Base OS: Read-only (immutable)\n"); + printf("Writable: /tmp, /var (overlay), /run\n"); + printf("Mode: Qt6 GUI (Network-First Architecture)\n"); + printf("No Users/Authentication - Direct Boot to GUI\n"); + printf("No CLI/Shell - Pure GUI Experience\n\n"); + + // Main loop - keep respawning GUI application + while (1) { + spawn_gui(); + + // If GUI exits, respawn after a short delay + printf("\nGUI application exited. Restarting in 2 seconds...\n"); + sleep(2); + } + + return 0; +} diff --git a/sparkos/src/qt6-app/CMakeLists.txt b/sparkos/src/qt6-app/CMakeLists.txt new file mode 100644 index 000000000..31c699dc7 --- /dev/null +++ b/sparkos/src/qt6-app/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.16) + +project(sparkos-gui VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Set build type to Release if not specified +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Find Qt6 packages +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui) + +# Enable automatic MOC (Meta-Object Compiler) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +# Source files +set(SOURCES + main.cpp +) + +# Create executable +add_executable(sparkos-gui ${SOURCES}) + +# Link Qt6 libraries +target_link_libraries(sparkos-gui PRIVATE + Qt6::Core + Qt6::Widgets + Qt6::Gui +) + +# Set properties for static linking if desired +# target_link_options(sparkos-gui PRIVATE -static-libgcc -static-libstdc++) + +# Install target +install(TARGETS sparkos-gui + RUNTIME DESTINATION bin +) diff --git a/sparkos/src/qt6-app/main.cpp b/sparkos/src/qt6-app/main.cpp new file mode 100644 index 000000000..132ae6e40 --- /dev/null +++ b/sparkos/src/qt6-app/main.cpp @@ -0,0 +1,188 @@ +/* + * SparkOS Qt6 GUI Application + * Direct kernel interface - bypassing Unix conventions + * Network-first, GUI-only operating system + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SparkOSMainWindow : public QWidget { + Q_OBJECT + +public: + SparkOSMainWindow(QWidget *parent = nullptr) : QWidget(parent) { + setupUI(); + } + +private slots: + void showSystemInfo() { + // Direct kernel interface through /proc and /sys + QString info = "SparkOS - Direct Kernel Interface\n"; + info += "==================================\n\n"; + + // Read kernel version directly from /proc + QFile kernelFile("/proc/version"); + if (kernelFile.open(QIODevice::ReadOnly)) { + info += "Kernel: " + QString(kernelFile.readLine()).trimmed() + "\n\n"; + kernelFile.close(); + } + + // Read memory info directly from /proc + QFile meminfoFile("/proc/meminfo"); + if (meminfoFile.open(QIODevice::ReadOnly)) { + info += "Memory Info:\n"; + int lineCount = 0; + while (!meminfoFile.atEnd() && lineCount < 3) { + info += " " + QString(meminfoFile.readLine()).trimmed() + "\n"; + lineCount++; + } + meminfoFile.close(); + } + + info += "\n"; + info += "Architecture: Network-First OS\n"; + info += "No Unix user/group system\n"; + info += "Direct Qt6 GUI to Kernel interface\n"; + + QTextEdit *infoDialog = new QTextEdit(); + infoDialog->setReadOnly(true); + infoDialog->setPlainText(info); + infoDialog->setWindowTitle("System Information"); + infoDialog->resize(600, 400); + infoDialog->show(); + } + +private: + void setupUI() { + // Set window properties + setWindowTitle("SparkOS"); + + // Create main layout + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setAlignment(Qt::AlignCenter); + mainLayout->setSpacing(30); + + // Create welcome label + QLabel *titleLabel = new QLabel("Welcome to SparkOS", this); + QFont titleFont; + titleFont.setPointSize(48); + titleFont.setBold(true); + titleLabel->setFont(titleFont); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setStyleSheet("color: #2196F3;"); + + // Create subtitle label + QLabel *subtitleLabel = new QLabel("Direct Kernel • Network-First • No Unix", this); + QFont subtitleFont; + subtitleFont.setPointSize(24); + subtitleLabel->setFont(subtitleFont); + subtitleLabel->setAlignment(Qt::AlignCenter); + subtitleLabel->setStyleSheet("color: #666;"); + + // Create status label + QLabel *statusLabel = new QLabel("✓ System Initialized • GUI Active", this); + QFont statusFont; + statusFont.setPointSize(16); + statusLabel->setFont(statusFont); + statusLabel->setAlignment(Qt::AlignCenter); + statusLabel->setStyleSheet("color: #4CAF50;"); + + // Create info label + QLabel *infoLabel = new QLabel("Qt6 GUI ↔ Linux Kernel (Direct Interface)", this); + QFont infoFont; + infoFont.setPointSize(14); + infoLabel->setFont(infoFont); + infoLabel->setAlignment(Qt::AlignCenter); + infoLabel->setStyleSheet("color: #999;"); + + // Create system info button + QPushButton *infoButton = new QPushButton("System Info", this); + infoButton->setMinimumSize(200, 60); + QFont buttonFont; + buttonFont.setPointSize(16); + infoButton->setFont(buttonFont); + infoButton->setStyleSheet( + "QPushButton {" + " background-color: #2196F3;" + " color: white;" + " border: none;" + " border-radius: 5px;" + " padding: 10px;" + "}" + "QPushButton:hover {" + " background-color: #1976D2;" + "}" + "QPushButton:pressed {" + " background-color: #0D47A1;" + "}" + ); + connect(infoButton, &QPushButton::clicked, this, &SparkOSMainWindow::showSystemInfo); + + // Create exit button + QPushButton *exitButton = new QPushButton("Power Off", this); + exitButton->setMinimumSize(200, 60); + exitButton->setFont(buttonFont); + exitButton->setStyleSheet( + "QPushButton {" + " background-color: #f44336;" + " color: white;" + " border: none;" + " border-radius: 5px;" + " padding: 10px;" + "}" + "QPushButton:hover {" + " background-color: #da190b;" + "}" + "QPushButton:pressed {" + " background-color: #a31408;" + "}" + ); + + // Connect exit button + connect(exitButton, &QPushButton::clicked, this, &QApplication::quit); + + // Add widgets to layout + mainLayout->addStretch(); + mainLayout->addWidget(titleLabel); + mainLayout->addWidget(subtitleLabel); + mainLayout->addSpacing(40); + mainLayout->addWidget(statusLabel); + mainLayout->addWidget(infoLabel); + mainLayout->addSpacing(40); + mainLayout->addWidget(infoButton, 0, Qt::AlignCenter); + mainLayout->addWidget(exitButton, 0, Qt::AlignCenter); + mainLayout->addStretch(); + + // Set background color + setStyleSheet("QWidget { background-color: #f5f5f5; }"); + + // Make fullscreen on Linux + showFullScreen(); + } +}; + +int main(int argc, char *argv[]) { + // Direct framebuffer rendering - no X11/Wayland server needed + // The Qt application interfaces directly with the Linux kernel framebuffer + qputenv("QT_QPA_PLATFORM", "linuxfb"); + + QApplication app(argc, argv); + + SparkOSMainWindow window; + window.show(); + + return app.exec(); +} + +#include "main.moc"