diff --git a/Makefile b/Makefile index d4b0e1b..ed4d6bb 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/etc/inittab b/etc/inittab index 90659da..434b26d 100644 --- a/etc/inittab +++ b/etc/inittab @@ -1,3 +1,5 @@ # Runlevels: 1 +init:1:wait:/sbin/rc default + l0:1:once:/bin/login diff --git a/etc/rc.d/00-mounts b/etc/rc.d/00-mounts new file mode 100644 index 0000000..c7bd1bc --- /dev/null +++ b/etc/rc.d/00-mounts @@ -0,0 +1,7 @@ +#!/bin/sh + +echo Mounting /dev +mount -t devfs /dev + +echo Mounting /sys +mount -t sysfs /sys diff --git a/init.c b/init.c deleted file mode 100644 index f701927..0000000 --- a/init.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -// TODO: -//#include -#include -#include -#include -#include -#include - -// 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(); -} diff --git a/progs/base/Makefile b/progs/base/Makefile index 7d44fc9..31a212f 100644 --- a/progs/base/Makefile +++ b/progs/base/Makefile @@ -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 \ diff --git a/progs/base/sbin/acpid.c b/progs/base/sbin/acpid.c index ca499ce..962670e 100644 --- a/progs/base/sbin/acpid.c +++ b/progs/base/sbin/acpid.c @@ -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: diff --git a/progs/base/bin/mount.c b/progs/base/sbin/mount.c similarity index 100% rename from progs/base/bin/mount.c rename to progs/base/sbin/mount.c diff --git a/progs/base/bin/umount.c b/progs/base/sbin/umount.c similarity index 100% rename from progs/base/bin/umount.c rename to progs/base/sbin/umount.c diff --git a/progs/rc/Makefile b/progs/rc/Makefile index 01325e0..8775532 100644 --- a/progs/rc/Makefile +++ b/progs/rc/Makefile @@ -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 $@ $< diff --git a/progs/rc/src/init.c b/progs/rc/src/init.c new file mode 100644 index 0000000..658c23e --- /dev/null +++ b/progs/rc/src/init.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +//#include + +#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); + } +} diff --git a/progs/rc/src/rc.c b/progs/rc/src/rc.c index ff2b4ea..2707f6e 100644 --- a/progs/rc/src/rc.c +++ b/progs/rc/src/rc.c @@ -1,213 +1,77 @@ -#include #include -#include #include +#include #include -#include #include -//#include +#include -#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; } }