/* * 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; }