/* AdrOS SysV-like init (/sbin/init)
*
* Reads /etc/inittab for configuration.
* Supports runlevels 0-6 and S (single-user).
* Actions: sysinit, respawn, wait, once, ctrlaltdel, shutdown.
*
* Default behavior (no inittab):
* 1. Run /etc/init.d/rcS (if exists)
* 2. Spawn /bin/sh on /dev/console
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#define MAX_ENTRIES 32
#define LINE_MAX 256
/* Inittab entry actions */
enum action {
ACT_SYSINIT, /* Run during system initialization */
ACT_WAIT, /* Run and wait for completion */
ACT_ONCE, /* Run once when entering runlevel */
ACT_RESPAWN, /* Restart when process dies */
ACT_CTRLALTDEL, /* Run on Ctrl+Alt+Del */
ACT_SHUTDOWN, /* Run during shutdown */
};
struct inittab_entry {
char id[8];
char runlevels[16];
enum action action;
char process[128];
int pid; /* PID of running process (for respawn) */
int active;
};
static struct inittab_entry entries[MAX_ENTRIES];
static int nentries = 0;
static int current_runlevel = 3; /* Default: multi-user */
static enum action parse_action(const char* s) {
if (strcmp(s, "sysinit") == 0) return ACT_SYSINIT;
if (strcmp(s, "wait") == 0) return ACT_WAIT;
if (strcmp(s, "once") == 0) return ACT_ONCE;
if (strcmp(s, "respawn") == 0) return ACT_RESPAWN;
if (strcmp(s, "ctrlaltdel") == 0) return ACT_CTRLALTDEL;
if (strcmp(s, "shutdown") == 0) return ACT_SHUTDOWN;
return ACT_ONCE;
}
/* Parse /etc/inittab
* Format: id:runlevels:action:process
* Example:
* ::sysinit:/etc/init.d/rcS
* ::respawn:/bin/sh
* tty1:2345:respawn:/bin/sh
*/
static int parse_inittab(void) {
int fd = open("/etc/inittab", O_RDONLY);
if (fd < 0) return -1;
char buf[2048];
int total = 0;
int r;
while ((r = read(fd, buf + total, (size_t)(sizeof(buf) - (size_t)total - 1))) > 0)
total += r;
buf[total] = '\0';
close(fd);
char* p = buf;
while (*p && nentries < MAX_ENTRIES) {
/* Skip whitespace and comments */
while (*p == ' ' || *p == '\t') p++;
if (*p == '#' || *p == '\n') {
while (*p && *p != '\n') p++;
if (*p == '\n') p++;
continue;
}
if (*p == '\0') break;
struct inittab_entry* e = &entries[nentries];
memset(e, 0, sizeof(*e));
/* id */
char* start = p;
while (*p && *p != ':') p++;
int len = (int)(p - start);
if (len > 7) len = 7;
memcpy(e->id, start, (size_t)len);
e->id[len] = '\0';
if (*p == ':') p++;
/* runlevels */
start = p;
while (*p && *p != ':') p++;
len = (int)(p - start);
if (len > 15) len = 15;
memcpy(e->runlevels, start, (size_t)len);
e->runlevels[len] = '\0';
if (*p == ':') p++;
/* action */
start = p;
while (*p && *p != ':') p++;
char action_str[32];
len = (int)(p - start);
if (len > 31) len = 31;
memcpy(action_str, start, (size_t)len);
action_str[len] = '\0';
e->action = parse_action(action_str);
if (*p == ':') p++;
/* process */
start = p;
while (*p && *p != '\n') p++;
len = (int)(p - start);
if (len > 127) len = 127;
memcpy(e->process, start, (size_t)len);
e->process[len] = '\0';
if (*p == '\n') p++;
e->pid = -1;
e->active = 1;
nentries++;
}
return nentries > 0 ? 0 : -1;
}
/* Check if entry should run at current runlevel */
static int entry_matches_runlevel(const struct inittab_entry* e) {
if (e->runlevels[0] == '\0') return 1; /* Empty = all runlevels */
char rl = '0' + (char)current_runlevel;
for (const char* p = e->runlevels; *p; p++) {
if (*p == rl || *p == 'S' || *p == 's') return 1;
}
return 0;
}
/* Run a process (fork + exec) */
static int run_process(const char* cmd) {
int pid = fork();
if (pid < 0) return -1;
if (pid == 0) {
/* Child: parse command into argv */
char buf[128];
strncpy(buf, cmd, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char* argv[16];
int argc = 0;
char* p = buf;
while (*p && argc < 15) {
while (*p == ' ' || *p == '\t') p++;
if (*p == '\0') break;
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
argv[argc] = NULL;
if (argc > 0) {
execve(argv[0], argv, NULL);
/* If execve fails, try with /bin/sh -c */
char* sh_argv[] = { "/bin/sh", "-c", (char*)cmd, NULL };
execve("/bin/sh", sh_argv, NULL);
}
_exit(127);
}
return pid;
}
/* Run a process and wait for it */
static int run_and_wait(const char* cmd) {
int pid = run_process(cmd);
if (pid < 0) return -1;
int st;
waitpid(pid, &st, 0);
return st;
}
/* Run all entries matching a given action and current runlevel */
static void run_action(enum action act, int do_wait) {
for (int i = 0; i < nentries; i++) {
if (entries[i].action != act) continue;
if (!entry_matches_runlevel(&entries[i])) continue;
if (!entries[i].active) continue;
if (do_wait) {
run_and_wait(entries[i].process);
} else {
entries[i].pid = run_process(entries[i].process);
}
}
}
/* Respawn dead children */
static void check_respawn(void) {
for (int i = 0; i < nentries; i++) {
if (entries[i].action != ACT_RESPAWN) continue;
if (!entry_matches_runlevel(&entries[i])) continue;
if (!entries[i].active) continue;
if (entries[i].pid <= 0) {
entries[i].pid = run_process(entries[i].process);
} else {
/* Check if still running */
int st;
int r = waitpid(entries[i].pid, &st, 1 /* WNOHANG */);
if (r > 0) {
/* Process exited, respawn */
entries[i].pid = run_process(entries[i].process);
}
}
}
}
/* Default behavior when no inittab exists */
static void default_init(void) {
/* Run /etc/init.d/rcS if it exists */
if (access("/etc/init.d/rcS", 0) == 0) {
run_and_wait("/etc/init.d/rcS");
}
/* Spawn shell */
while (1) {
int pid = fork();
if (pid < 0) {
fprintf(stderr, "init: fork failed\n");
for (;;) { struct timespec ts = {1,0}; nanosleep(&ts, NULL); }
}
if (pid == 0) {
char* argv[] = { "/bin/sh", NULL };
execve("/bin/sh", argv, NULL);
_exit(127);
}
int st;
waitpid(pid, &st, 0);
/* Shell exited, respawn after a small delay */
struct timespec ts = {1, 0};
nanosleep(&ts, NULL);
}
}
int main(int argc, char** argv) {
(void)argc; (void)argv;
/* PID 1 should not die on signals */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = (uintptr_t)SIG_IGN;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
printf("AdrOS init starting (PID %d)\n", getpid());
/* Try to parse inittab */
if (parse_inittab() < 0) {
printf("init: no /etc/inittab, using defaults\n");
default_init();
return 0; /* unreachable */
}
printf("init: loaded %d inittab entries, runlevel %d\n",
nentries, current_runlevel);
/* Phase 1: sysinit entries */
run_action(ACT_SYSINIT, 1);
/* Phase 2: wait entries */
run_action(ACT_WAIT, 1);
/* Phase 3: once entries */
run_action(ACT_ONCE, 0);
/* Phase 4: respawn entries */
run_action(ACT_RESPAWN, 0);
/* Main loop: reap children and respawn */
while (1) {
int st;
int pid = waitpid(-1, &st, 0);
if (pid > 0) {
/* Mark dead child and respawn if needed */
for (int i = 0; i < nentries; i++) {
if (entries[i].pid == pid) {
entries[i].pid = -1;
break;
}
}
check_respawn();
} else {
/* No children or error — sleep briefly */
struct timespec ts = {1, 0};
nanosleep(&ts, NULL);
check_respawn();
}
}
return 0;
}