mirror of
https://github.com/johndoe6345789/SparkOS.git
synced 2026-04-24 13:34:56 +00:00
262 lines
7.4 KiB
C
262 lines
7.4 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.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 <errno.h>
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
#include <limits.h>
|
|
|
|
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;
|
|
}
|