Enable mount/umount, add rc/init programs
This commit is contained in:
@@ -5,7 +5,6 @@ KERNEL_HDRS?=kernel-hdr
|
||||
CC=x86_64-elf-yggdrasil-gcc
|
||||
|
||||
DIRS=$(STAGE)
|
||||
STAGE_BIN=$(STAGE)/init
|
||||
|
||||
all: mkdirs $(O)/initrd.img
|
||||
|
||||
@@ -15,7 +14,7 @@ clean:
|
||||
make -C $(dir) clean; \
|
||||
)
|
||||
|
||||
$(O)/initrd.img: mkstage-etc $(STAGE_BIN) mkstage-progs
|
||||
$(O)/initrd.img: mkstage-etc mkstage-progs
|
||||
cd $(STAGE) && tar czf $(abspath $@) *
|
||||
|
||||
mkdirs:
|
||||
@@ -33,6 +32,3 @@ mkstage-progs:
|
||||
$(foreach dir,$(wildcard progs/*), \
|
||||
CC=$(CC) DESTDIR=$(abspath $(STAGE)) make -C $(dir) install || exit 1; \
|
||||
)
|
||||
|
||||
$(STAGE)/init: init.c
|
||||
$(CC) -o $@ $(usr_CFLAGS) $(usr_LDFLAGS) init.c
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Runlevels: 1
|
||||
|
||||
init:1:wait:/sbin/rc default
|
||||
|
||||
l0:1:once:/bin/login
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo Mounting /dev
|
||||
mount -t devfs /dev
|
||||
|
||||
echo Mounting /sys
|
||||
mount -t sysfs /sys
|
||||
@@ -1,75 +0,0 @@
|
||||
#include <ygg/netctl.h>
|
||||
// TODO:
|
||||
//#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
// TODO
|
||||
int mount(const char *source, const char *target,
|
||||
const char *fs, unsigned long flags,
|
||||
void *data);
|
||||
|
||||
// The only thing init does now
|
||||
static int start_login(void) {
|
||||
const char *login = "/bin/login";
|
||||
const char *const argp[] = {
|
||||
login, NULL
|
||||
};
|
||||
|
||||
int pid = fork();
|
||||
int res;
|
||||
|
||||
switch (pid) {
|
||||
case 0:
|
||||
exit(execve(login, (char *const *) argp, environ));
|
||||
case -1:
|
||||
fprintf(stderr, "Init program failed\n");
|
||||
return -1;
|
||||
default:
|
||||
waitpid(pid, &res, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int start_acpid(void) {
|
||||
const char *acpid = "/sbin/acpid";
|
||||
const char *const argp[] = {
|
||||
acpid, NULL
|
||||
};
|
||||
|
||||
int pid = fork();
|
||||
int res;
|
||||
|
||||
switch (pid) {
|
||||
case 0:
|
||||
exit(execve(acpid, (char *const *) argp, environ));
|
||||
case -1:
|
||||
fprintf(stderr, "acpid failed to start\n");
|
||||
return -1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (getpid() != 1) {
|
||||
printf("Won't work if PID is not 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mount(NULL, "/dev", "devfs", 0, NULL);
|
||||
mount(NULL, "/sys", "sysfs", 0, NULL);
|
||||
//mount("/dev/sda", "/mnt", NULL, 0, NULL);
|
||||
|
||||
uint32_t inaddr = 0x0A000001;
|
||||
netctl("eth0", NETCTL_SET_INADDR, &inaddr);
|
||||
|
||||
// Start acpid
|
||||
start_acpid();
|
||||
|
||||
return start_login();
|
||||
}
|
||||
@@ -23,6 +23,8 @@ BINS=$(O)/bin/ls \
|
||||
$(O)/bin/mkfifo \
|
||||
$(O)/bin/sleep \
|
||||
$(O)/bin/ucat \
|
||||
$(O)/sbin/mount \
|
||||
$(O)/sbin/umount \
|
||||
$(O)/sbin/lspci \
|
||||
$(O)/sbin/insmod \
|
||||
$(O)/sbin/reboot \
|
||||
|
||||
@@ -9,6 +9,10 @@ int main(int argc, char **argv) {
|
||||
fclose(stdin);
|
||||
|
||||
FILE *fp = fopen("/dev/acpi", "rb");
|
||||
if (!fp) {
|
||||
perror("/dev/acpi");
|
||||
return -1;
|
||||
}
|
||||
int ch;
|
||||
|
||||
// TODO: fix fopen for tty
|
||||
@@ -19,11 +23,6 @@ int main(int argc, char **argv) {
|
||||
|
||||
fclose(stderr);
|
||||
|
||||
if (!fp) {
|
||||
perror("/dev/acpi");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((ch = fgetc(fp)) > 0) {
|
||||
switch (ch) {
|
||||
case ACEV_POWER_BUTTON:
|
||||
|
||||
+8
-5
@@ -10,8 +10,9 @@ LDFLAGS?=-lgcc
|
||||
O=build
|
||||
|
||||
rc_OBJS=$(O)/rc.o
|
||||
init_OBJS=$(O)/init.o
|
||||
|
||||
all: mkdirs $(O)/rc
|
||||
all: mkdirs $(O)/init $(O)/rc
|
||||
|
||||
mkdirs:
|
||||
mkdir -p $(O)
|
||||
@@ -19,14 +20,16 @@ mkdirs:
|
||||
clean:
|
||||
rm -rf $(O)
|
||||
|
||||
install: mkdirs $(O)/rc
|
||||
install -m0755 $(O)/rc $(DESTDIR)/init
|
||||
# TODO: symlinks in tarfs
|
||||
#ln -sf /bin/rc $(DESTDIR)/init
|
||||
install: mkdirs $(O)/init $(O)/rc
|
||||
install -m0755 $(O)/init $(DESTDIR)/init
|
||||
install -m0755 $(O)/rc $(DESTDIR)/sbin/rc
|
||||
|
||||
$(O)/rc: $(rc_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(rc_OBJS)
|
||||
|
||||
$(O)/init: $(init_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(init_OBJS)
|
||||
|
||||
$(O)/%.o: src/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
//#include <wait.h>
|
||||
|
||||
#define INITTAB "/etc/inittab"
|
||||
// Default runlevel
|
||||
#define RUNLEVEL_1 (1 << 0)
|
||||
|
||||
extern void ygg_debug_trace(const char *fmt, ...);
|
||||
|
||||
enum rc_action {
|
||||
RC_ONCE,
|
||||
RC_BOOT,
|
||||
RC_WAIT
|
||||
};
|
||||
|
||||
struct rc_rule {
|
||||
char id[4];
|
||||
uint32_t runlevels;
|
||||
enum rc_action action;
|
||||
|
||||
char process[256];
|
||||
char *argv[16];
|
||||
int argc;
|
||||
|
||||
struct rc_rule *prev, *next;
|
||||
};
|
||||
|
||||
static void sig_handler(int signum) {
|
||||
if (signum == SIGTERM) {
|
||||
// TODO: reap children
|
||||
fprintf(stderr, "init received SIGTERM\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_inittab_error(int line, const char *msg) {
|
||||
ygg_debug_trace("rc: error on line %d\n", line);
|
||||
ygg_debug_trace("rc: %s\n", msg);
|
||||
}
|
||||
|
||||
static int load_inittab(struct rc_rule **_head) {
|
||||
struct rc_rule *head = NULL, *tail = NULL;
|
||||
FILE *fp;
|
||||
char buf[1024];
|
||||
char *r;
|
||||
char *e;
|
||||
|
||||
// Parse and execute rules from inittab
|
||||
fp = fopen(INITTAB, "r");
|
||||
if (!fp) {
|
||||
perror(INITTAB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
// Terminate with either a newline or a comment
|
||||
if (buf[0] && (r = strpbrk(buf, "\n\r#")) != NULL) {
|
||||
*r = 0;
|
||||
}
|
||||
|
||||
// Skip leading whitespace
|
||||
r = buf;
|
||||
while (isspace(*r)) {
|
||||
++r;
|
||||
}
|
||||
|
||||
if (!*r) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the rule
|
||||
struct rc_rule *rule = malloc(sizeof(struct rc_rule));
|
||||
rule->runlevels = 0;
|
||||
|
||||
// {id}:...
|
||||
e = strchr(r, ':');
|
||||
if (!e) {
|
||||
fprintf(stderr, "Syntax error: `%s`\n", r);
|
||||
goto error;
|
||||
}
|
||||
if (e - r > 4 || e == r) {
|
||||
fprintf(stderr, "Rule ID must be 1-4 symbols: `%s`\n", r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
strncpy(rule->id, r, e - r);
|
||||
rule->id[e - r] = 0;
|
||||
|
||||
// ...:{runlevels}:...
|
||||
|
||||
r = e + 1;
|
||||
while (1) {
|
||||
if (*r == ':') {
|
||||
++r;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!*r || !isdigit(*r)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (*r) {
|
||||
case '1':
|
||||
rule->runlevels |= RUNLEVEL_1;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
++r;
|
||||
}
|
||||
|
||||
// ...:{action}:...
|
||||
e = strchr(r, ':');
|
||||
if (!e) {
|
||||
rc_inittab_error(0, r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
if (!strcmp(r, "once")) {
|
||||
rule->action = RC_ONCE;
|
||||
} else if (!strcmp(r, "wait")) {
|
||||
rule->action = RC_WAIT;
|
||||
} else {
|
||||
rc_inittab_error(0, r);
|
||||
goto error;
|
||||
}
|
||||
r = e + 1;
|
||||
|
||||
// ...:{process}
|
||||
strcpy(rule->process, r);
|
||||
|
||||
// Split "process" string
|
||||
r = rule->process;
|
||||
while (1) {
|
||||
e = strchr(r, ' ');
|
||||
if (!e) {
|
||||
if (*r) {
|
||||
rule->argv[rule->argc++] = r;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
*e = 0;
|
||||
rule->argv[rule->argc++] = r;
|
||||
}
|
||||
|
||||
r = e + 1;
|
||||
while (isspace(*r)) {
|
||||
++r;
|
||||
}
|
||||
if (!*r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rule->argv[rule->argc] = NULL;
|
||||
|
||||
if (tail) {
|
||||
tail->next = rule;
|
||||
} else {
|
||||
head = rule;
|
||||
}
|
||||
rule->next = NULL;
|
||||
rule->prev = tail;
|
||||
tail = rule;
|
||||
}
|
||||
|
||||
*_head = head;
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
error:
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rc_start(const struct rc_rule *rule) {
|
||||
int pid;
|
||||
ygg_debug_trace("rc: start `%s`\n", rule->argv[0]);
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
perror("fork()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
exit(execve(rule->argv[0], rule->argv, environ));
|
||||
} else {
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_enter_runlevel(const struct rc_rule *head, int runlevel) {
|
||||
// Run "once" and "wait" rules
|
||||
int n_failed = 0;
|
||||
for (const struct rc_rule *r = head; r; r = r->next) {
|
||||
if ((r->runlevels & runlevel) && (r->action == RC_ONCE || r->action == RC_WAIT)) {
|
||||
int pid = rc_start(r);
|
||||
if (pid < 0) {
|
||||
ygg_debug_trace("rc: rule start failed: fork() failed\n");
|
||||
++n_failed;
|
||||
} else {
|
||||
if (r->action == RC_WAIT) {
|
||||
int status;
|
||||
int r = waitpid(pid, &status, 0);
|
||||
if (r < 0) {
|
||||
ygg_debug_trace("rc: rule waitpid() failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return n_failed;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int runlevel = RUNLEVEL_1;
|
||||
signal(SIGTERM, sig_handler);
|
||||
|
||||
struct rc_rule *head;
|
||||
if (load_inittab(&head) != 0) {
|
||||
ygg_debug_trace("rc: failed to load inittab\n");
|
||||
return -1;
|
||||
}
|
||||
ygg_debug_trace("rc: inittab loaded\n");
|
||||
|
||||
// Run all "boot" rules
|
||||
ygg_debug_trace("rc: running `boot` ruleset\n");
|
||||
for (struct rc_rule *r = head; r; r = r->next) {
|
||||
if (r->action == RC_BOOT) {
|
||||
rc_start(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Enter target runlevel
|
||||
rc_enter_runlevel(head, runlevel);
|
||||
|
||||
while (1) {
|
||||
usleep(1000000);
|
||||
}
|
||||
}
|
||||
+56
-192
@@ -1,213 +1,77 @@
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
//#include <wait.h>
|
||||
#include <wait.h>
|
||||
|
||||
#define INITTAB "/etc/inittab"
|
||||
// Default runlevel
|
||||
#define RUNLEVEL_1 (1 << 0)
|
||||
#define DIR_DEFAULT "/etc/rc.d"
|
||||
|
||||
enum rc_action {
|
||||
RC_ONCE,
|
||||
RC_BOOT
|
||||
};
|
||||
static int rc_script_exec(const char *path, const char *arg) {
|
||||
int pid = fork();
|
||||
|
||||
struct rc_rule {
|
||||
char id[4];
|
||||
uint32_t runlevels;
|
||||
enum rc_action action;
|
||||
|
||||
char process[256];
|
||||
char *argv[16];
|
||||
int argc;
|
||||
|
||||
struct rc_rule *prev, *next;
|
||||
};
|
||||
|
||||
static void sig_handler(int signum) {
|
||||
if (signum == SIGTERM) {
|
||||
// TODO: reap children
|
||||
fprintf(stderr, "init received SIGTERM\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int load_inittab(struct rc_rule **_head) {
|
||||
struct rc_rule *head = NULL, *tail = NULL;
|
||||
FILE *fp;
|
||||
char buf[1024];
|
||||
char *r;
|
||||
char *e;
|
||||
|
||||
// Parse and execute rules from inittab
|
||||
fp = fopen(INITTAB, "r");
|
||||
if (!fp) {
|
||||
perror(INITTAB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
// Terminate with either a newline or a comment
|
||||
if (buf[0] && (r = strpbrk(buf, "\n\r#")) != NULL) {
|
||||
*r = 0;
|
||||
}
|
||||
|
||||
// Skip leading whitespace
|
||||
r = buf;
|
||||
while (isspace(*r)) {
|
||||
++r;
|
||||
}
|
||||
|
||||
if (!*r) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the rule
|
||||
struct rc_rule *rule = malloc(sizeof(struct rc_rule));
|
||||
rule->runlevels = 0;
|
||||
|
||||
// {id}:...
|
||||
e = strchr(r, ':');
|
||||
if (!e) {
|
||||
fprintf(stderr, "Syntax error: `%s`\n", r);
|
||||
goto error;
|
||||
}
|
||||
if (e - r >= 4 || e == r) {
|
||||
fprintf(stderr, "Rule ID must be 1-4 symbols: `%s`\n", r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
strncpy(rule->id, r, e - r);
|
||||
rule->id[e - r] = 0;
|
||||
|
||||
// ...:{runlevels}:...
|
||||
|
||||
r = e + 1;
|
||||
while (1) {
|
||||
if (*r == ':') {
|
||||
++r;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!*r || !isdigit(*r)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (*r) {
|
||||
case '1':
|
||||
rule->runlevels |= RUNLEVEL_1;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
++r;
|
||||
}
|
||||
|
||||
// ...:{action}:...
|
||||
e = strchr(r, ':');
|
||||
if (!e) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
if (!strcmp(r, "once")) {
|
||||
rule->action = RC_ONCE;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
r = e + 1;
|
||||
|
||||
// ...:{process}
|
||||
strcpy(rule->process, r);
|
||||
|
||||
// Split "process" string
|
||||
r = rule->process;
|
||||
while (1) {
|
||||
e = strchr(r, ' ');
|
||||
if (!e) {
|
||||
if (*r) {
|
||||
rule->argv[rule->argc++] = r;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
*e = 0;
|
||||
rule->argv[rule->argc++] = r;
|
||||
}
|
||||
|
||||
r = e + 1;
|
||||
while (isspace(*r)) {
|
||||
++r;
|
||||
}
|
||||
if (!*r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rule->argv[rule->argc] = NULL;
|
||||
|
||||
if (tail) {
|
||||
tail->next = rule;
|
||||
} else {
|
||||
head = rule;
|
||||
}
|
||||
rule->next = NULL;
|
||||
rule->prev = tail;
|
||||
tail = rule;
|
||||
}
|
||||
|
||||
*_head = head;
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
error:
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rc_start(const struct rc_rule *rule) {
|
||||
int pid;
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
perror("fork()");
|
||||
if (pid < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
exit(execve(rule->argv[0], rule->argv, environ));
|
||||
const char *const argv[] = {
|
||||
path, arg, NULL
|
||||
};
|
||||
|
||||
exit(execve(argv[0], (char *const *) argv, environ));
|
||||
} else {
|
||||
// TODO: "wait" action
|
||||
return 0;
|
||||
int res, t;
|
||||
|
||||
t = waitpid(pid, &res, 0);
|
||||
if (t < 0) {
|
||||
perror("waitpid()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_load_dir(const char *path, const char *action) {
|
||||
DIR *dir = opendir(path);
|
||||
if (!dir) {
|
||||
perror(path);
|
||||
return -1;
|
||||
}
|
||||
struct dirent *ent;
|
||||
char script[256];
|
||||
|
||||
// TODO: readdir doesn't guarantee ordering
|
||||
while ((ent = readdir(dir))) {
|
||||
if (ent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(script, sizeof(script), "%s/%s", path, ent->d_name);
|
||||
|
||||
rc_script_exec(script, "start");
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(const char *name) {
|
||||
fprintf(stderr, "usage: %s default|shutdown\n", name ? name : "/sbin/rc");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int runlevel = RUNLEVEL_1;
|
||||
signal(SIGTERM, sig_handler);
|
||||
|
||||
struct rc_rule *head;
|
||||
if (load_inittab(&head) != 0) {
|
||||
if (argc != 2) {
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Run all "boot" rules
|
||||
for (struct rc_rule *r = head; r; r = r->next) {
|
||||
if (r->action == RC_BOOT) {
|
||||
rc_start(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Run all "once" rules
|
||||
for (struct rc_rule *r = head; r; r = r->next) {
|
||||
if ((r->runlevels & runlevel) && r->action == RC_ONCE) {
|
||||
rc_start(r);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
usleep(1000000);
|
||||
if (!strcmp(argv[1], "default")) {
|
||||
return rc_load_dir(DIR_DEFAULT, "start");
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user