mirror of
https://github.com/johndoe6345789/SparkOS.git
synced 2026-04-24 13:34:56 +00:00
Remove busybox and CLI dependencies from init system and scripts
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
@@ -23,9 +23,9 @@ FROM alpine:3.19
|
|||||||
COPY scripts/docker-install-packages.sh /tmp/
|
COPY scripts/docker-install-packages.sh /tmp/
|
||||||
RUN /tmp/docker-install-packages.sh
|
RUN /tmp/docker-install-packages.sh
|
||||||
|
|
||||||
# Alpine Linux includes BusyBox by default
|
# SparkOS Philosophy: No CLI tools, GUI-only experience
|
||||||
# BusyBox provides: shell (sh), networking (udhcpc, ip, ping, wget), and core utilities
|
# The init system is completely self-contained with no external dependencies
|
||||||
# This is verified by the test.sh script which shows BusyBox version and available applets
|
# All functionality is provided through direct system calls in C
|
||||||
|
|
||||||
# Create minimal rootfs structure
|
# Create minimal rootfs structure
|
||||||
COPY scripts/docker-setup-rootfs.sh /tmp/
|
COPY scripts/docker-setup-rootfs.sh /tmp/
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
SparkOS Root Filesystem
|
SparkOS Root Filesystem
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
This is the root filesystem for SparkOS, a minimal Linux distribution.
|
This is the root filesystem for SparkOS, a GUI-only Linux distribution.
|
||||||
|
|
||||||
Minimal System Packages:
|
SparkOS Philosophy:
|
||||||
- Linux Kernel (with networking support)
|
- GUI-Only: No CLI tools, no shell, no Unix utilities
|
||||||
- SparkOS Init System (custom)
|
- Network-First: Networking integrated into Qt6 GUI
|
||||||
- Busybox (shell, utilities, networking)
|
- Direct Kernel Interface: Qt6 communicates directly with Linux kernel
|
||||||
- Git (for installing spark CLI)
|
- No Unix Baggage: No users, groups, passwords, or authentication
|
||||||
- Sudo (privilege elevation)
|
|
||||||
|
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:
|
Directory Structure:
|
||||||
/bin, /sbin - Essential binaries
|
/sbin - Init binary only
|
||||||
/etc - Configuration files
|
/etc - Minimal configuration files
|
||||||
/proc, /sys, /dev - Kernel interfaces
|
/proc, /sys, /dev - Kernel interfaces
|
||||||
/tmp - Temporary files
|
/tmp - Temporary files
|
||||||
/usr - User programs
|
/usr - Qt6 GUI application and libraries
|
||||||
/var - Variable data
|
/var - Variable data (overlay mount)
|
||||||
/root - Root home directory
|
/root - Root home directory
|
||||||
/home/spark - Default user home directory
|
|
||||||
|
|
||||||
Default User:
|
|
||||||
Username: spark (UID 1000)
|
|
||||||
Home: /home/spark
|
|
||||||
Sudo: Full access without password
|
|
||||||
Scripts: ~/clone-sparkos.sh for installing spark CLI
|
|
||||||
|
|
||||||
Network Configuration:
|
Network Configuration:
|
||||||
/etc/network/interfaces - Wired network (DHCP)
|
- Managed entirely through Qt6 GUI
|
||||||
/etc/resolv.conf - DNS configuration (8.8.8.8, 1.1.1.1)
|
- Init brings up interfaces via direct ioctl calls
|
||||||
/sbin/init-network - Network initialization script
|
- DHCP and network management handled by Qt6 NetworkManager
|
||||||
|
- /etc/resolv.conf provides fallback DNS servers
|
||||||
|
|
||||||
Bootstrap Process:
|
Boot Process:
|
||||||
1. System boots as 'spark' user with wired networking (DHCP)
|
1. Linux kernel loads
|
||||||
2. Run ~/clone-sparkos.sh to install spark CLI
|
2. Init (PID 1) mounts filesystems
|
||||||
3. Use spark CLI to configure WiFi and system
|
3. Init brings up network interfaces
|
||||||
4. Install additional packages via spark CLI
|
4. Init spawns Qt6 GUI application
|
||||||
5. Use 'sudo' for any root-level operations
|
5. All user interaction through GUI
|
||||||
|
|
||||||
Note: This is a minimal system. You'll need to populate /bin and /usr/bin
|
Note: This is a minimal, GUI-only system.
|
||||||
with actual binaries (busybox, git, sudo) from a proper Linux system
|
No shell, no CLI tools, no busybox.
|
||||||
or by cross-compiling.
|
All functionality is provided through the Qt6 GUI application.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# SparkOS Setup Script
|
# SparkOS Setup Script
|
||||||
# Sets up a minimal rootfs with busybox and essential utilities
|
# Sets up a minimal rootfs for GUI-only SparkOS
|
||||||
# Note: This script runs on the host system and uses bash for ${BASH_SOURCE}
|
# Note: This script runs on the host system and uses bash for ${BASH_SOURCE}
|
||||||
# The target system uses busybox sh instead.
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -16,16 +15,12 @@ echo ""
|
|||||||
|
|
||||||
# Create directory structure
|
# Create directory structure
|
||||||
echo "Creating directory structure..."
|
echo "Creating directory structure..."
|
||||||
mkdir -p "$ROOTFS_DIR"/{bin,sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var,root,home}
|
mkdir -p "$ROOTFS_DIR"/{sbin,etc,proc,sys,dev,tmp,usr/{bin,sbin,lib,lib64},var,root}
|
||||||
mkdir -p "$ROOTFS_DIR/etc"/{init.d,network}
|
|
||||||
mkdir -p "$ROOTFS_DIR/var"/{log,run}
|
mkdir -p "$ROOTFS_DIR/var"/{log,run}
|
||||||
mkdir -p "$ROOTFS_DIR/home/spark"
|
|
||||||
mkdir -p "$ROOTFS_DIR/etc/sudoers.d"
|
|
||||||
|
|
||||||
# Set permissions
|
# Set permissions
|
||||||
chmod 1777 "$ROOTFS_DIR/tmp"
|
chmod 1777 "$ROOTFS_DIR/tmp"
|
||||||
chmod 700 "$ROOTFS_DIR/root"
|
chmod 700 "$ROOTFS_DIR/root"
|
||||||
chmod 755 "$ROOTFS_DIR/home/spark"
|
|
||||||
|
|
||||||
# Create basic config files
|
# Create basic config files
|
||||||
echo "Creating configuration files..."
|
echo "Creating configuration files..."
|
||||||
@@ -40,19 +35,6 @@ cat > "$ROOTFS_DIR/etc/hosts" << 'EOF'
|
|||||||
::1 localhost ip6-localhost ip6-loopback
|
::1 localhost ip6-localhost ip6-loopback
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# /etc/passwd
|
|
||||||
cat > "$ROOTFS_DIR/etc/passwd" << 'EOF'
|
|
||||||
root:x:0:0:root:/root:/bin/sh
|
|
||||||
spark:x:1000:1000:SparkOS User:/home/spark:/bin/sh
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# /etc/group
|
|
||||||
cat > "$ROOTFS_DIR/etc/group" << 'EOF'
|
|
||||||
root:x:0:
|
|
||||||
spark:x:1000:
|
|
||||||
sudo:x:27:spark
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# /etc/fstab
|
# /etc/fstab
|
||||||
cat > "$ROOTFS_DIR/etc/fstab" << 'EOF'
|
cat > "$ROOTFS_DIR/etc/fstab" << 'EOF'
|
||||||
# <file system> <mount point> <type> <options> <dump> <pass>
|
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||||
@@ -62,320 +44,77 @@ devtmpfs /dev devtmpfs defaults 0 0
|
|||||||
tmpfs /tmp tmpfs defaults 0 0
|
tmpfs /tmp tmpfs defaults 0 0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# /etc/resolv.conf - DNS configuration
|
# /etc/resolv.conf - DNS configuration (managed by Qt6 GUI)
|
||||||
cat > "$ROOTFS_DIR/etc/resolv.conf" << 'EOF'
|
cat > "$ROOTFS_DIR/etc/resolv.conf" << 'EOF'
|
||||||
# SparkOS DNS Configuration
|
# SparkOS DNS Configuration
|
||||||
# Fallback to public DNS servers for reliability
|
# Managed by Qt6 GUI NetworkManager
|
||||||
|
# Fallback to public DNS servers
|
||||||
nameserver 8.8.8.8
|
nameserver 8.8.8.8
|
||||||
nameserver 8.8.4.4
|
nameserver 8.8.4.4
|
||||||
nameserver 1.1.1.1
|
nameserver 1.1.1.1
|
||||||
nameserver 1.0.0.1
|
nameserver 1.0.0.1
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# /etc/sudoers - Sudo configuration
|
|
||||||
cat > "$ROOTFS_DIR/etc/sudoers" << 'EOF'
|
|
||||||
# SparkOS Sudoers Configuration
|
|
||||||
# Allow spark user to run any command without password
|
|
||||||
|
|
||||||
# Default settings
|
|
||||||
Defaults env_reset
|
|
||||||
Defaults secure_path="/bin:/sbin:/usr/bin:/usr/sbin"
|
|
||||||
|
|
||||||
# Root can run anything
|
|
||||||
root ALL=(ALL:ALL) ALL
|
|
||||||
|
|
||||||
# Spark user can run anything without password
|
|
||||||
spark ALL=(ALL:ALL) NOPASSWD: ALL
|
|
||||||
|
|
||||||
# Include sudoers.d directory
|
|
||||||
@includedir /etc/sudoers.d
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod 0440 "$ROOTFS_DIR/etc/sudoers"
|
|
||||||
|
|
||||||
# /etc/network/interfaces - Wired network configuration
|
|
||||||
cat > "$ROOTFS_DIR/etc/network/interfaces" << 'EOF'
|
|
||||||
# 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
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# /etc/profile
|
|
||||||
cat > "$ROOTFS_DIR/etc/profile" << 'EOF'
|
|
||||||
# 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 ""
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create .profile for root (busybox uses .profile instead of .bashrc)
|
|
||||||
cat > "$ROOTFS_DIR/root/.profile" << 'EOF'
|
|
||||||
# SparkOS Root Shell Configuration
|
|
||||||
|
|
||||||
# Set prompt
|
|
||||||
PS1='SparkOS:\w# '
|
|
||||||
|
|
||||||
# Aliases
|
|
||||||
alias ll='ls -lah'
|
|
||||||
alias ..='cd ..'
|
|
||||||
|
|
||||||
# Environment
|
|
||||||
export EDITOR=vi
|
|
||||||
export PAGER=less
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create .profile for spark user
|
|
||||||
cat > "$ROOTFS_DIR/home/spark/.profile" << 'EOF'
|
|
||||||
# SparkOS User Shell Configuration
|
|
||||||
|
|
||||||
# Set prompt
|
|
||||||
PS1='SparkOS:\w\$ '
|
|
||||||
|
|
||||||
# Aliases
|
|
||||||
alias ll='ls -lah'
|
|
||||||
alias ..='cd ..'
|
|
||||||
|
|
||||||
# Environment
|
|
||||||
export EDITOR=vi
|
|
||||||
export PAGER=less
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create clone-sparkos.sh script for spark user
|
|
||||||
cat > "$ROOTFS_DIR/home/spark/clone-sparkos.sh" << 'EOF'
|
|
||||||
#!/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
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$ROOTFS_DIR/home/spark/clone-sparkos.sh"
|
|
||||||
|
|
||||||
# Create a simple help script
|
|
||||||
cat > "$ROOTFS_DIR/bin/sparkos-help" << 'EOF'
|
|
||||||
#!/bin/sh
|
|
||||||
cat << 'HELP'
|
|
||||||
SparkOS - Minimal Linux Distribution
|
|
||||||
====================================
|
|
||||||
|
|
||||||
Default Packages:
|
|
||||||
- Kernel (Linux)
|
|
||||||
- Init system (custom)
|
|
||||||
- Busybox (shell and utilities)
|
|
||||||
- Git (for installing spark CLI)
|
|
||||||
- Sudo (privilege elevation)
|
|
||||||
|
|
||||||
Default User:
|
|
||||||
Username: spark
|
|
||||||
Home: /home/spark
|
|
||||||
Privileges: Full sudo access (no password required)
|
|
||||||
|
|
||||||
To run commands as root: sudo <command>
|
|
||||||
To become root: sudo -i
|
|
||||||
|
|
||||||
Available commands:
|
|
||||||
ls, cd, pwd - Navigate filesystem
|
|
||||||
cat, less - View files
|
|
||||||
mkdir, rm, cp - File operations
|
|
||||||
mount, umount - Mount filesystems
|
|
||||||
ip, ifconfig - Network configuration
|
|
||||||
ping, wget - Network testing
|
|
||||||
git - Version control
|
|
||||||
sudo - Run commands as root
|
|
||||||
poweroff, reboot - System control
|
|
||||||
help - Show this help
|
|
||||||
|
|
||||||
Network:
|
|
||||||
Wired networking (eth0) configured via DHCP
|
|
||||||
DNS: 8.8.8.8, 1.1.1.1 (Google and Cloudflare)
|
|
||||||
To check network: ping 8.8.8.8
|
|
||||||
To test DNS: ping google.com
|
|
||||||
|
|
||||||
Next Steps:
|
|
||||||
1. Install spark CLI: ~/clone-sparkos.sh
|
|
||||||
2. Use spark CLI to configure WiFi and system
|
|
||||||
3. Install additional packages as needed
|
|
||||||
|
|
||||||
For more information: https://github.com/johndoe6345789/SparkOS
|
|
||||||
HELP
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$ROOTFS_DIR/bin/sparkos-help"
|
|
||||||
ln -sf sparkos-help "$ROOTFS_DIR/bin/help"
|
|
||||||
|
|
||||||
# Create network initialization script
|
|
||||||
cat > "$ROOTFS_DIR/sbin/init-network" << 'EOF'
|
|
||||||
#!/bin/sh
|
|
||||||
# SparkOS Network Initialization
|
|
||||||
# Brings up wired networking for system bootstrap
|
|
||||||
|
|
||||||
echo "Initializing network..."
|
|
||||||
|
|
||||||
# Bring up loopback
|
|
||||||
ip link set lo up 2>/dev/null || ifconfig lo up 2>/dev/null
|
|
||||||
|
|
||||||
# Bring up primary wired interface with DHCP
|
|
||||||
# Try eth0 first, then other common interface names
|
|
||||||
for iface in eth0 enp0s3 enp0s8 ens33; do
|
|
||||||
if ip link show "$iface" >/dev/null 2>&1; then
|
|
||||||
echo "Bringing up $iface..."
|
|
||||||
|
|
||||||
# Bring up the interface
|
|
||||||
if ip link set "$iface" up 2>/dev/null || ifconfig "$iface" up 2>/dev/null; then
|
|
||||||
# Try to get IP via DHCP using busybox udhcpc
|
|
||||||
if command -v udhcpc >/dev/null 2>&1; then
|
|
||||||
# Run udhcpc in background, it will daemonize itself
|
|
||||||
udhcpc -i "$iface" -b -t 5 2>/dev/null
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Warning: Failed to bring up $iface"
|
|
||||||
fi
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Network initialization complete"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$ROOTFS_DIR/sbin/init-network"
|
|
||||||
|
|
||||||
# Create README
|
# Create README
|
||||||
cat > "$ROOTFS_DIR/README.txt" << 'EOF'
|
cat > "$ROOTFS_DIR/README.txt" << 'EOF'
|
||||||
SparkOS Root Filesystem
|
SparkOS Root Filesystem
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
This is the root filesystem for SparkOS, a minimal Linux distribution.
|
This is the root filesystem for SparkOS, a GUI-only Linux distribution.
|
||||||
|
|
||||||
Minimal System Packages:
|
SparkOS Philosophy:
|
||||||
- Linux Kernel (with networking support)
|
- GUI-Only: No CLI tools, no shell, no Unix utilities
|
||||||
- SparkOS Init System (custom)
|
- Network-First: Networking integrated into Qt6 GUI
|
||||||
- Busybox (shell, utilities, networking)
|
- Direct Kernel Interface: Qt6 communicates directly with Linux kernel
|
||||||
- Git (for installing spark CLI)
|
- No Unix Baggage: No users, groups, passwords, or authentication
|
||||||
- Sudo (privilege elevation)
|
|
||||||
|
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:
|
Directory Structure:
|
||||||
/bin, /sbin - Essential binaries
|
/sbin - Init binary only
|
||||||
/etc - Configuration files
|
/etc - Minimal configuration files
|
||||||
/proc, /sys, /dev - Kernel interfaces
|
/proc, /sys, /dev - Kernel interfaces
|
||||||
/tmp - Temporary files
|
/tmp - Temporary files
|
||||||
/usr - User programs
|
/usr - Qt6 GUI application and libraries
|
||||||
/var - Variable data
|
/var - Variable data (overlay mount)
|
||||||
/root - Root home directory
|
/root - Root home directory
|
||||||
/home/spark - Default user home directory
|
|
||||||
|
|
||||||
Default User:
|
|
||||||
Username: spark (UID 1000)
|
|
||||||
Home: /home/spark
|
|
||||||
Sudo: Full access without password
|
|
||||||
Scripts: ~/clone-sparkos.sh for installing spark CLI
|
|
||||||
|
|
||||||
Network Configuration:
|
Network Configuration:
|
||||||
/etc/network/interfaces - Wired network (DHCP)
|
- Managed entirely through Qt6 GUI
|
||||||
/etc/resolv.conf - DNS configuration (8.8.8.8, 1.1.1.1)
|
- Init brings up interfaces via direct ioctl calls
|
||||||
/sbin/init-network - Network initialization script
|
- DHCP and network management handled by Qt6 NetworkManager
|
||||||
|
- /etc/resolv.conf provides fallback DNS servers
|
||||||
|
|
||||||
Bootstrap Process:
|
Boot Process:
|
||||||
1. System boots as 'spark' user with wired networking (DHCP)
|
1. Linux kernel loads
|
||||||
2. Run ~/clone-sparkos.sh to install spark CLI
|
2. Init (PID 1) mounts filesystems
|
||||||
3. Use spark CLI to configure WiFi and system
|
3. Init brings up network interfaces
|
||||||
4. Install additional packages via spark CLI
|
4. Init spawns Qt6 GUI application
|
||||||
5. Use 'sudo' for any root-level operations
|
5. All user interaction through GUI
|
||||||
|
|
||||||
Note: This is a minimal system. You'll need to populate /bin and /usr/bin
|
Note: This is a minimal, GUI-only system.
|
||||||
with actual binaries (busybox, git, sudo) from a proper Linux system
|
No shell, no CLI tools, no busybox.
|
||||||
or by cross-compiling.
|
All functionality is provided through the Qt6 GUI application.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Root filesystem structure created at: $ROOTFS_DIR"
|
echo "Root filesystem structure created at: $ROOTFS_DIR"
|
||||||
echo ""
|
echo ""
|
||||||
echo "User configuration:"
|
echo "SparkOS Configuration:"
|
||||||
echo " - Default user: spark (UID 1000)"
|
echo " - Architecture: GUI-only, no CLI"
|
||||||
echo " - Home directory: /home/spark"
|
echo " - Init: Self-contained, no external dependencies"
|
||||||
echo " - Sudo access: Enabled (no password required)"
|
echo " - Network: Direct C implementation via ioctl"
|
||||||
echo " - Clone script: /home/spark/clone-sparkos.sh"
|
echo " - User Experience: Pure Qt6 GUI"
|
||||||
echo ""
|
|
||||||
echo "Network configuration:"
|
|
||||||
echo " - Wired networking (DHCP) configured for eth0"
|
|
||||||
echo " - DNS: 8.8.8.8, 1.1.1.1, 8.8.4.4, 1.0.0.1"
|
|
||||||
echo " - Network init script: /sbin/init-network"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo " 1. Build init: make init"
|
echo " 1. Build init: make init"
|
||||||
echo " 2. Install init: make install"
|
echo " 2. Install init: make install"
|
||||||
echo " 3. Copy busybox, git, and sudo binaries to rootfs/bin/"
|
echo " 3. Build Qt6 GUI: make gui"
|
||||||
echo " 4. Create busybox symlinks"
|
echo " 4. Create bootable image: sudo make image"
|
||||||
echo " 5. Create bootable image: sudo make image"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Minimum required binaries:"
|
echo "Philosophy:"
|
||||||
echo " - busybox (provides shell, networking, utilities)"
|
echo " No busybox, no shell, no CLI tools"
|
||||||
echo " - git (for installing spark CLI)"
|
echo " Everything is GUI-driven through Qt6"
|
||||||
echo " - sudo (for privilege elevation)"
|
|
||||||
echo ""
|
|
||||||
echo "Note: Busybox should be compiled with networking support"
|
|
||||||
echo " (CONFIG_UDHCPC, CONFIG_IFCONFIG, CONFIG_IP, CONFIG_PING, CONFIG_WGET)"
|
|
||||||
|
|||||||
@@ -3,48 +3,6 @@ echo "SparkOS Docker Test Environment"
|
|||||||
echo "================================"
|
echo "================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Verify BusyBox is present and working
|
|
||||||
echo "Verifying BusyBox..."
|
|
||||||
echo "-------------------"
|
|
||||||
if command -v busybox >/dev/null 2>&1; then
|
|
||||||
echo "✓ BusyBox is installed"
|
|
||||||
echo ""
|
|
||||||
echo "BusyBox version:"
|
|
||||||
# Store version for reuse in summary
|
|
||||||
BUSYBOX_VERSION=$(busybox | head -n 1)
|
|
||||||
echo "$BUSYBOX_VERSION"
|
|
||||||
echo ""
|
|
||||||
echo "BusyBox location:"
|
|
||||||
which busybox
|
|
||||||
ls -lh "$(which busybox)"
|
|
||||||
echo ""
|
|
||||||
echo "Shell (/bin/sh) is BusyBox:"
|
|
||||||
ls -lh /bin/sh
|
|
||||||
if [ -L /bin/sh ]; then
|
|
||||||
echo " → /bin/sh is a symlink to: \"$(readlink /bin/sh)\""
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo "Available BusyBox applets (sample):"
|
|
||||||
# Store applet list once to avoid redundant executions
|
|
||||||
APPLET_LIST=$(busybox --list)
|
|
||||||
echo "$APPLET_LIST" | head -n 20
|
|
||||||
echo " ... and $(echo "$APPLET_LIST" | wc -l) total applets"
|
|
||||||
echo ""
|
|
||||||
echo "Networking applets (required for SparkOS):"
|
|
||||||
for cmd in udhcpc ip ifconfig ping wget; do
|
|
||||||
if echo "$APPLET_LIST" | grep -q "^${cmd}$"; then
|
|
||||||
echo " ✓ $cmd"
|
|
||||||
else
|
|
||||||
echo " ✗ $cmd (NOT FOUND)"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "✗ BusyBox not found!"
|
|
||||||
echo " SparkOS requires BusyBox for shell and utilities"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Verifying SparkOS init binary..."
|
echo "Verifying SparkOS init binary..."
|
||||||
echo "--------------------------------"
|
echo "--------------------------------"
|
||||||
if [ -f /sparkos/rootfs/sbin/init ]; then
|
if [ -f /sparkos/rootfs/sbin/init ]; then
|
||||||
@@ -75,10 +33,13 @@ echo "✓ SparkOS is ready for testing!"
|
|||||||
echo "================================"
|
echo "================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Summary:"
|
echo "Summary:"
|
||||||
echo " - BusyBox: $BUSYBOX_VERSION"
|
echo " - Init: Custom SparkOS init system (no external dependencies)"
|
||||||
echo " - Init: Custom SparkOS init system"
|
echo " - Architecture: GUI-only, no CLI/shell"
|
||||||
echo " - Shell: BusyBox sh (/bin/sh)"
|
echo " - Network: Direct C implementation via ioctl"
|
||||||
echo " - Networking: BusyBox udhcpc, ip, ping, wget"
|
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 ""
|
||||||
echo "To test the init system:"
|
echo "To test the init system:"
|
||||||
echo " docker run --rm <image> /sparkos/rootfs/sbin/init --help"
|
echo " docker run --rm <image> /sparkos/rootfs/sbin/init"
|
||||||
178
src/init.c
178
src/init.c
@@ -1,6 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* SparkOS Init - Minimal init system for SparkOS
|
* SparkOS Init - Minimal init system for SparkOS
|
||||||
* This is the first process that runs after the kernel boots
|
* 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 <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -8,9 +13,16 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <linux/if.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
static void signal_handler(int sig) {
|
static void signal_handler(int sig) {
|
||||||
if (sig == SIGCHLD) {
|
if (sig == SIGCHLD) {
|
||||||
@@ -33,7 +45,7 @@ static void spawn_gui() {
|
|||||||
char *argv[] = {"/usr/bin/sparkos-gui", NULL};
|
char *argv[] = {"/usr/bin/sparkos-gui", NULL};
|
||||||
char *envp[] = {
|
char *envp[] = {
|
||||||
"HOME=/root",
|
"HOME=/root",
|
||||||
"PATH=/bin:/sbin:/usr/bin:/usr/sbin",
|
"PATH=/usr/bin:/usr/sbin",
|
||||||
"QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0",
|
"QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0",
|
||||||
"QT_QPA_FB_FORCE_FULLSCREEN=1",
|
"QT_QPA_FB_FORCE_FULLSCREEN=1",
|
||||||
"QT_QPA_FONTDIR=/usr/share/fonts",
|
"QT_QPA_FONTDIR=/usr/share/fonts",
|
||||||
@@ -51,39 +63,122 @@ static void spawn_gui() {
|
|||||||
waitpid(pid, &status, 0);
|
waitpid(pid, &status, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spawn_shell() {
|
/*
|
||||||
pid_t pid = fork();
|
* 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;
|
||||||
|
|
||||||
if (pid < 0) {
|
// Create socket for ioctl operations
|
||||||
perror("fork failed");
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
return;
|
if (sock < 0) {
|
||||||
|
perror("socket creation failed");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid == 0) {
|
// Prepare interface request structure
|
||||||
// Child process - exec shell as root (fallback only)
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
|
||||||
char *argv[] = {"/bin/sh", "-l", NULL};
|
|
||||||
char *envp[] = {
|
// Get current flags
|
||||||
"HOME=/root",
|
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
|
||||||
"PATH=/bin:/sbin:/usr/bin:/usr/sbin",
|
close(sock);
|
||||||
"TERM=linux",
|
return -1; // Interface doesn't exist
|
||||||
"PS1=SparkOS# ",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
execve("/bin/sh", argv, envp);
|
|
||||||
|
|
||||||
perror("failed to exec shell");
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent process - wait for shell to exit
|
// Bring interface up
|
||||||
int status;
|
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
||||||
waitpid(pid, &status, 0);
|
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[256];
|
||||||
|
char *p = NULL;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s", path);
|
||||||
|
len = strlen(tmp);
|
||||||
|
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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
printf("SparkOS Init System Starting...\n");
|
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
|
// Make sure we're PID 1
|
||||||
if (getpid() != 1) {
|
if (getpid() != 1) {
|
||||||
@@ -94,18 +189,18 @@ int main(int argc, char *argv[]) {
|
|||||||
// Set up signal handlers
|
// Set up signal handlers
|
||||||
signal(SIGCHLD, signal_handler);
|
signal(SIGCHLD, signal_handler);
|
||||||
|
|
||||||
// Mount essential filesystems
|
// Mount essential filesystems using direct system calls
|
||||||
printf("Mounting essential filesystems...\n");
|
printf("Mounting essential filesystems...\n");
|
||||||
if (system("mount -t proc proc /proc 2>/dev/null") != 0) {
|
if (mount_fs("proc", "/proc", "proc", 0) != 0) {
|
||||||
fprintf(stderr, "Warning: Failed to mount /proc\n");
|
fprintf(stderr, "Warning: Failed to mount /proc\n");
|
||||||
}
|
}
|
||||||
if (system("mount -t sysfs sys /sys 2>/dev/null") != 0) {
|
if (mount_fs("sysfs", "/sys", "sysfs", 0) != 0) {
|
||||||
fprintf(stderr, "Warning: Failed to mount /sys\n");
|
fprintf(stderr, "Warning: Failed to mount /sys\n");
|
||||||
}
|
}
|
||||||
if (system("mount -t devtmpfs dev /dev 2>/dev/null") != 0) {
|
if (mount_fs("devtmpfs", "/dev", "devtmpfs", 0) != 0) {
|
||||||
fprintf(stderr, "Warning: Failed to mount /dev\n");
|
fprintf(stderr, "Warning: Failed to mount /dev\n");
|
||||||
}
|
}
|
||||||
if (system("mount -t tmpfs tmpfs /tmp 2>/dev/null") != 0) {
|
if (mount_fs("tmpfs", "/tmp", "tmpfs", 0) != 0) {
|
||||||
fprintf(stderr, "Warning: Failed to mount /tmp\n");
|
fprintf(stderr, "Warning: Failed to mount /tmp\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,37 +208,38 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("Setting up overlay filesystem for writable layer...\n");
|
printf("Setting up overlay filesystem for writable layer...\n");
|
||||||
|
|
||||||
// Create overlay directories in tmpfs
|
// Create overlay directories in tmpfs
|
||||||
if (system("mkdir -p /tmp/overlay/var-upper /tmp/overlay/var-work 2>/dev/null") != 0) {
|
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");
|
fprintf(stderr, "Warning: Failed to create overlay directories for /var\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount overlay on /var for logs and runtime data
|
// Mount overlay on /var for logs and runtime data
|
||||||
if (system("mount -t overlay overlay -o lowerdir=/var,upperdir=/tmp/overlay/var-upper,workdir=/tmp/overlay/var-work /var 2>/dev/null") != 0) {
|
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");
|
fprintf(stderr, "Warning: Failed to mount overlay on /var - system may be read-only\n");
|
||||||
} else {
|
} else {
|
||||||
printf("Overlay filesystem mounted on /var (base OS is immutable)\n");
|
printf("Overlay filesystem mounted on /var (base OS is immutable)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount tmpfs on /run for runtime data
|
// Mount tmpfs on /run for runtime data
|
||||||
if (system("mkdir -p /run 2>/dev/null") == 0) {
|
if (mkdir_p("/run") == 0) {
|
||||||
if (system("mount -t tmpfs tmpfs /run 2>/dev/null") != 0) {
|
if (mount_fs("tmpfs", "/run", "tmpfs", 0) != 0) {
|
||||||
fprintf(stderr, "Warning: Failed to mount /run\n");
|
fprintf(stderr, "Warning: Failed to mount /run\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize network (wired only for bootstrap)
|
// Initialize network interfaces
|
||||||
printf("Initializing wired network...\n");
|
init_network();
|
||||||
if (system("/sbin/init-network 2>/dev/null") != 0) {
|
|
||||||
fprintf(stderr, "Warning: Network initialization failed - check network interface availability\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Starting Qt6 GUI application...\n");
|
printf("\nStarting Qt6 GUI application...\n");
|
||||||
printf("Welcome to SparkOS!\n");
|
printf("Welcome to SparkOS!\n");
|
||||||
printf("===================\n");
|
printf("===================\n");
|
||||||
printf("Base OS: Read-only (immutable)\n");
|
printf("Base OS: Read-only (immutable)\n");
|
||||||
printf("Writable: /tmp, /var (overlay), /run\n");
|
printf("Writable: /tmp, /var (overlay), /run\n");
|
||||||
printf("Mode: Qt6 GUI (Network-First Architecture)\n");
|
printf("Mode: Qt6 GUI (Network-First Architecture)\n");
|
||||||
printf("No Users/Authentication - Direct Boot to GUI\n\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
|
// Main loop - keep respawning GUI application
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user