toaruos/apps/init.c

138 lines
3.8 KiB
C
Raw Normal View History

2018-08-15 04:07:33 +03:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
2018-08-14 08:45:30 +03:00
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2018 K. Lange
*
* init - First process.
*
* `init` calls startup scripts and then waits for them to complete.
* It also waits for orphaned proceses so they can be collected.
*
* `init` itself is statically-linked, so minimizing libc dependencies
* is worthwhile as it reduces to the total size of init itself, which
* remains in memory throughout the entire lifetime of the OS.
*
* Startup scripts for init are stored in /etc/startup.d and are run
* in sorted alphabetical order. It is generally recommended that these
* startup scripts be named with numbers at the front to ensure easy
* ordering. This system of running a set of scripts on startup is
* somewhat similar to how sysvinit worked, but no claims of
* compatibility are made.
*
* Startup scripts can be any executable binary. Shell scripts are
* generally used to allow easy editing, but you could also use
* a binary (even a dynamically linked one) as a startup script.
* `init` will wait for each startup script (that is, it will wait for
* the original process it started to exit) before running the next one.
* So if you wish to run daemons, be sure to fork them off and then
* exit so that the rest of the startup process can continue.
*
* When the last startup script finishes, `init` will reboot the system.
*/
2018-08-12 10:36:56 +03:00
#include <dirent.h>
2018-02-25 08:13:54 +03:00
#include <errno.h>
2018-08-12 10:36:56 +03:00
#include <stdio.h>
#include <stdlib.h>
2018-03-04 16:50:53 +03:00
#include <string.h>
2018-08-12 10:36:56 +03:00
#include <syscall.h>
2018-08-01 21:40:43 +03:00
#include <unistd.h>
2018-08-12 10:36:56 +03:00
#include <wait.h>
#include <sys/wait.h>
#define INITD_PATH "/etc/startup.d"
2018-02-25 08:13:54 +03:00
2018-08-14 08:45:30 +03:00
/* Initialize fd 0, 1, 2 */
2018-08-01 21:40:43 +03:00
void set_console(void) {
syscall_open("/dev/null", 0, 0);
syscall_open("/dev/null", 1, 0);
syscall_open("/dev/null", 1, 0);
2018-02-25 08:13:54 +03:00
}
2018-08-14 08:45:30 +03:00
/* Run a startup script and wait for it to finish */
2018-02-25 08:13:54 +03:00
int start_options(char * args[]) {
2018-08-14 08:45:30 +03:00
/* Fork child to run script */
int cpid = syscall_fork();
2018-08-14 08:45:30 +03:00
/* Child process... */
if (!cpid) {
2018-08-14 08:45:30 +03:00
/* Pass environment from init to child */
syscall_execve(args[0], args, environ);
/* exec failed, exit this subprocess */
2018-02-25 08:13:54 +03:00
syscall_exit(0);
}
2018-08-14 08:45:30 +03:00
/* Wait for the child process to finish */
2018-08-12 10:36:56 +03:00
int pid = 0;
do {
2018-08-14 08:45:30 +03:00
/*
* Wait, ignoring kernel threads
* (which also end up as children to init)
*/
2018-08-12 10:36:56 +03:00
pid = waitpid(-1, NULL, WNOKERN);
2018-08-14 08:45:30 +03:00
2018-08-12 10:36:56 +03:00
if (pid == -1 && errno == ECHILD) {
2018-08-14 08:45:30 +03:00
/* There are no more children */
2018-08-12 10:36:56 +03:00
break;
}
2018-08-14 08:45:30 +03:00
2018-08-12 10:36:56 +03:00
if (pid == cpid) {
2018-08-14 08:45:30 +03:00
/* The child process finished */
2018-08-12 10:36:56 +03:00
break;
}
2018-08-14 08:45:30 +03:00
/* Continue while no error (or error was "interrupted") */
2018-08-12 10:36:56 +03:00
} while ((pid > 0) || (pid == -1 && errno == EINTR));
2018-08-12 10:36:56 +03:00
return cpid;
2018-02-25 08:13:54 +03:00
}
int main(int argc, char * argv[]) {
2018-08-12 10:36:56 +03:00
/* Initialize stdin/out/err */
2018-02-25 08:13:54 +03:00
set_console();
2018-03-04 16:50:53 +03:00
/* Get directory listing for /etc/startup.d */
2018-08-12 10:36:56 +03:00
int initd_dir = syscall_open(INITD_PATH, 0, 0);
if (initd_dir < 0) {
/* No init scripts; try to start getty as a fallback */
start_options((char *[]){"/bin/getty",NULL});
} else {
int count = 0, i = 0, ret = 0;
/* Figure out how many entries we have with a dry run */
do {
struct dirent ent;
ret = syscall_readdir(initd_dir, ++count, &ent);
} while (ret > 0);
/* Read each directory entry */
struct dirent entries[count];
do {
syscall_readdir(initd_dir, i, &entries[i]);
i++;
} while (i < count);
/* Sort the directory entries */
int comparator(const void * c1, const void * c2) {
const struct dirent * d1 = c1;
const struct dirent * d2 = c2;
return strcmp(d1->d_name, d2->d_name);
}
qsort(entries, count, sizeof(struct dirent), comparator);
/* Run scripts */
for (int i = 0; i < count; ++i) {
if (entries[i].d_name[0] != '.') {
char path[256];
sprintf(path, "/etc/startup.d/%s", entries[i].d_name);
2018-08-12 10:36:56 +03:00
start_options((char *[]){path, NULL});
}
2018-03-04 16:50:53 +03:00
}
}
2018-08-12 10:36:56 +03:00
2018-08-14 08:45:30 +03:00
/* Self-explanatory */
2018-08-12 10:36:56 +03:00
syscall_reboot();
return 0;
2018-02-25 08:13:54 +03:00
}