kernel+apps+base+tools: Use Ext2 for the root partition file system
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
init is now split into two parts: preinit, which lives in the initrd and prepares the root file system for init, and the actual /usr/bin/init, which lives in the root partition and starts services and reaps zombies. The kernel now looks for /bin/preinit instead of /bin/init as the executable for the init process. All configuration files in initrd/etc have been moved to base/etc. (The plan is to have only moon and preinit in the initrd.) Since the current Ext2 implementation is read-only (and it's on a CDROM so it would be read-only anyways), /home/selene is a tmpfs (as well as /tmp), to allow for a writable home directory. The system is slower now, but that's to expect since the Ext2 code doesn't use caching and the ATA code still uses PIO.
This commit is contained in:
parent
40413eee18
commit
16b0531d42
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,7 +4,6 @@ build/
|
||||
initrd/boot/moon
|
||||
env-local.sh
|
||||
initrd/bin/**
|
||||
initrd/tests/**
|
||||
base/
|
||||
base/usr/**
|
||||
.fakeroot
|
||||
kernel/config.cmake
|
||||
|
@ -6,9 +6,10 @@ A very basic POSIX-based operating system for personal computers, written in C++
|
||||
|
||||
## Features
|
||||
- x86_64-compatible lightweight [kernel](kernel/).
|
||||
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||
- Preemptive multitasking, with a round-robin [scheduler](kernel/src/thread/).
|
||||
- [Virtual file system](kernel/src/fs/) with a simple [tmpfs](kernel/src/fs/tmpfs/) and read-only [ext2](kernel/src/fs/ext2/) support.
|
||||
- Can [load ELF programs](kernel/src/thread/ELF.cpp) from the file system as userspace tasks.
|
||||
- Boots from an [ext2](apps/preinit.cpp) root filesystem (a bit slow for now).
|
||||
- [System call](kernel/src/sys/) interface and [C Library](libc/), aiming to be mostly POSIX-compatible.
|
||||
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
||||
- Designed around [UTF-8](libluna/include/luna/Utf8.h).
|
||||
|
@ -4,9 +4,15 @@ function(luna_app SOURCE_FILE APP_NAME)
|
||||
add_dependencies(${APP_NAME} libc)
|
||||
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
|
||||
target_link_libraries(${APP_NAME} PRIVATE os)
|
||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin)
|
||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_BASE}/usr/bin)
|
||||
endfunction()
|
||||
|
||||
add_executable(preinit preinit.cpp)
|
||||
target_compile_options(preinit PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
add_dependencies(preinit libc)
|
||||
target_include_directories(preinit PRIVATE ${LUNA_BASE}/usr/include)
|
||||
install(TARGETS preinit DESTINATION ${LUNA_ROOT}/initrd/bin)
|
||||
|
||||
luna_app(init.cpp init)
|
||||
luna_app(env.cpp env)
|
||||
luna_app(su.cpp su)
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
FILE* g_init_log;
|
||||
FILE* g_init_log = nullptr;
|
||||
|
||||
struct Service
|
||||
{
|
||||
@ -33,6 +33,16 @@ struct Service
|
||||
|
||||
Vector<Service> g_services;
|
||||
|
||||
static void do_log(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
if (g_init_log) vfprintf(g_init_log, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
|
||||
SharedPtr<os::File> input)
|
||||
{
|
||||
@ -82,23 +92,23 @@ static Result<void> try_start_service(Service& service)
|
||||
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||
if (rc.has_error())
|
||||
{
|
||||
fprintf(g_init_log, "[child %d] failed to start service %s due to error: %s\n", getpid(),
|
||||
service.name.chars(), rc.error_string());
|
||||
do_log("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
||||
rc.error_string());
|
||||
}
|
||||
fclose(g_init_log);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] created new child process %d for service %s\n", pid, service.name.chars());
|
||||
do_log("[init] created new child process %d for service %s\n", pid, service.name.chars());
|
||||
|
||||
if (service.wait)
|
||||
{
|
||||
fprintf(g_init_log, "[init] waiting for child process %d to finish\n", pid);
|
||||
do_log("[init] waiting for child process %d to finish\n", pid);
|
||||
|
||||
int status;
|
||||
TRY(os::Process::wait(pid, &status));
|
||||
|
||||
fprintf(g_init_log, "[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
||||
do_log("[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
||||
}
|
||||
else
|
||||
service.pid = pid;
|
||||
@ -111,14 +121,13 @@ static void start_service(Service& service)
|
||||
auto rc = try_start_service(service);
|
||||
if (rc.has_error())
|
||||
{
|
||||
fprintf(g_init_log, "[init] failed to start service %s due to error: %s\n", service.name.chars(),
|
||||
rc.error_string());
|
||||
do_log("[init] failed to start service %s due to error: %s\n", service.name.chars(), rc.error_string());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<void> load_service(const os::Path& path)
|
||||
{
|
||||
fprintf(g_init_log, "[init] reading service file: %s\n", path.name().chars());
|
||||
do_log("[init] reading service file: %s\n", path.name().chars());
|
||||
|
||||
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||
|
||||
@ -135,7 +144,7 @@ static Result<void> load_service(const os::Path& path)
|
||||
auto parts = TRY(line.split_once('='));
|
||||
if (parts.size() < 2 || parts[0].is_empty() || parts[1].is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] file contains invalid line, aborting: '%s'\n", line.chars());
|
||||
do_log("[init] file contains invalid line, aborting: '%s'\n", line.chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -149,7 +158,7 @@ static Result<void> load_service(const os::Path& path)
|
||||
{
|
||||
if (!service.command.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] 'Command' cannot be specified after 'Script' has already been set! (%s)\n",
|
||||
do_log("[init] 'Command' cannot be specified after 'Script' has already been set! (%s)\n",
|
||||
line.chars());
|
||||
return {};
|
||||
}
|
||||
@ -161,7 +170,7 @@ static Result<void> load_service(const os::Path& path)
|
||||
{
|
||||
if (!service.command.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] 'Script' cannot be specified after 'Command' has already been set! (%s)\n",
|
||||
do_log("[init] 'Script' cannot be specified after 'Command' has already been set! (%s)\n",
|
||||
line.chars());
|
||||
return {};
|
||||
}
|
||||
@ -215,22 +224,22 @@ static Result<void> load_service(const os::Path& path)
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||
do_log("[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||
}
|
||||
|
||||
if (service.name.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] service file is missing 'Name' entry, aborting!\n");
|
||||
do_log("[init] service file is missing 'Name' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (service.command.is_empty())
|
||||
{
|
||||
fprintf(g_init_log, "[init] service file is missing 'Command' or 'Script' entry, aborting!\n");
|
||||
do_log("[init] service file is missing 'Command' or 'Script' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
fprintf(g_init_log, "[init] loaded service %s into memory\n", service.name.chars());
|
||||
do_log("[init] loaded service %s into memory\n", service.name.chars());
|
||||
|
||||
TRY(g_services.try_append(move(service)));
|
||||
|
||||
@ -254,7 +263,7 @@ static Result<void> start_services()
|
||||
TRY(load_services());
|
||||
for (auto& service : g_services)
|
||||
{
|
||||
fprintf(g_init_log, "[init] starting service %s\n", service.name.chars());
|
||||
do_log("[init] starting service %s\n", service.name.chars());
|
||||
start_service(service);
|
||||
}
|
||||
|
||||
@ -270,16 +279,16 @@ static Result<void> set_hostname()
|
||||
|
||||
if (sethostname(hostname.chars(), hostname.length()) < 0) return {};
|
||||
|
||||
fprintf(g_init_log, "[init] successfully set system hostname to '%s'\n", hostname.chars());
|
||||
do_log("[init] successfully set system hostname to '%s'\n", hostname.chars());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void mount_devfs()
|
||||
static void mount_tmpfs()
|
||||
{
|
||||
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255);
|
||||
if (mount("/tmp", "tmpfs", "tmpfs") < 0) exit(255);
|
||||
|
||||
if (mount("/dev", "devfs", "devfs") < 0) exit(255);
|
||||
if (chmod("/tmp", 01777) < 0) exit(255);
|
||||
}
|
||||
|
||||
int main()
|
||||
@ -290,17 +299,17 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
mount_devfs();
|
||||
|
||||
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
|
||||
// can print stuff.
|
||||
stdin = fopen("/dev/console", "r");
|
||||
stdout = fopen("/dev/console", "w");
|
||||
stderr = fopen("/dev/console", "w");
|
||||
|
||||
mount_tmpfs();
|
||||
|
||||
umask(022);
|
||||
|
||||
g_init_log = fopen("/init.log", "w+");
|
||||
g_init_log = fopen("/tmp/init.log", "w+");
|
||||
fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC);
|
||||
|
||||
set_hostname();
|
||||
@ -319,12 +328,11 @@ int main()
|
||||
{
|
||||
if (service.pid.has_value() && service.pid.value() == child)
|
||||
{
|
||||
fprintf(g_init_log, "[init] service %s exited with status %d\n", service.name.chars(),
|
||||
WEXITSTATUS(status));
|
||||
do_log("[init] service %s exited with status %d\n", service.name.chars(), WEXITSTATUS(status));
|
||||
|
||||
if (service.restart)
|
||||
{
|
||||
fprintf(g_init_log, "[init] restarting service %s\n", service.name.chars());
|
||||
do_log("[init] restarting service %s\n", service.name.chars());
|
||||
|
||||
start_service(service);
|
||||
}
|
||||
|
86
apps/preinit.cpp
Normal file
86
apps/preinit.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// FIXME: Make this configurable.
|
||||
#define LUNA_ROOT_PARTITION "/dev/cd0p2"
|
||||
|
||||
static void mount_devfs()
|
||||
{
|
||||
if (mkdir("/dev", 0755) < 0 && errno != EEXIST && errno != EROFS) exit(255);
|
||||
|
||||
if (mount("/dev", "devfs", "devfs") < 0) exit(255);
|
||||
}
|
||||
|
||||
static void open_std_streams()
|
||||
{
|
||||
stdin = fopen("/dev/console", "r");
|
||||
stdout = fopen("/dev/console", "w");
|
||||
stderr = fopen("/dev/console", "w");
|
||||
}
|
||||
|
||||
static void fail(const char* message)
|
||||
{
|
||||
open_std_streams();
|
||||
fprintf(stderr, "preinit: fatal error: %s\n", message);
|
||||
exit(255);
|
||||
}
|
||||
|
||||
static void fail_errno(const char* message)
|
||||
{
|
||||
int err = errno;
|
||||
open_std_streams();
|
||||
fprintf(stderr, "preinit: fatal error: %s (%s)\n", message, strerror(err));
|
||||
exit(255);
|
||||
}
|
||||
|
||||
static void mount_rootfs()
|
||||
{
|
||||
if (mkdir("/osroot", 0755) < 0 && errno != EEXIST) exit(255);
|
||||
|
||||
if (mount("/osroot", "ext2", LUNA_ROOT_PARTITION) < 0) fail_errno("Cannot mount the root partition");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (getpid() != 1)
|
||||
{
|
||||
fprintf(stderr, "error: preinit must be run as the init process.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mount_devfs();
|
||||
mount_rootfs();
|
||||
|
||||
struct stat st;
|
||||
if (stat("/osroot/mnt", &st) < 0 || !S_ISDIR(st.st_mode)) fail("No suitable temporary mountpoint for pivot_root");
|
||||
|
||||
// Now that we have mounted the root file system, remove the /dev mount on the current ramfs root.
|
||||
umount("/dev");
|
||||
|
||||
long rc = syscall(SYS_pivot_root, "/osroot", "/osroot/mnt");
|
||||
if (rc < 0) exit(255);
|
||||
|
||||
chdir("/");
|
||||
umount("/mnt");
|
||||
|
||||
// Now, mount the /dev file system on the new root.
|
||||
mount_devfs();
|
||||
|
||||
/*setenv("PATH", "/bin:/usr/bin", 1);
|
||||
char* argv[] = { "init", nullptr };
|
||||
char* envp[] = { nullptr };
|
||||
execvpe(argv[0], argv, envp);*/
|
||||
|
||||
char* argv[] = { "/usr/bin/init", nullptr };
|
||||
execv(argv[0], argv);
|
||||
|
||||
fail_errno("Failed to execute init");
|
||||
|
||||
return 255;
|
||||
}
|
3
base/etc/init/00-motd
Normal file
3
base/etc/init/00-motd
Normal file
@ -0,0 +1,3 @@
|
||||
Name=motd
|
||||
Command=/usr/bin/cat /etc/motd
|
||||
Wait=true
|
3
base/etc/init/01-selene
Normal file
3
base/etc/init/01-selene
Normal file
@ -0,0 +1,3 @@
|
||||
Name=selene
|
||||
Script=/etc/startup/selene-home.sh
|
||||
Wait=true
|
@ -1,3 +1,3 @@
|
||||
Name=login
|
||||
Command=/bin/login
|
||||
Command=/usr/bin/login
|
||||
Restart=true
|
2
base/etc/startup/selene-home.sh
Normal file
2
base/etc/startup/selene-home.sh
Normal file
@ -0,0 +1,2 @@
|
||||
mount -t tmpfs tmpfs /home/selene
|
||||
chown selene:selene /home/selene
|
@ -1,3 +0,0 @@
|
||||
Name=tmpfs
|
||||
Script=/sbin/mount-tmpfs
|
||||
Wait=true
|
@ -1,3 +0,0 @@
|
||||
Name=motd
|
||||
Command=/bin/cat /etc/motd
|
||||
Wait=true
|
@ -1,3 +0,0 @@
|
||||
Name=ext2fs
|
||||
Script=/sbin/mount-ext2fs
|
||||
Wait=true
|
@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p /tmp
|
||||
mount -t tmpfs tmpfs /tmp
|
||||
chmod 1777 /tmp
|
@ -45,9 +45,10 @@ void reap_thread()
|
||||
mark_critical(InitRD::populate_vfs(), "Failed to load files from the initial ramdisk");
|
||||
mark_critical(DeviceRegistry::init(), "Failed to register initial devices");
|
||||
|
||||
auto init = mark_critical(VFS::resolve_path("/bin/init", Credentials {}), "Can't find init in the initial ramfs!");
|
||||
auto init =
|
||||
mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}), "Can't find init in the initial ramfs!");
|
||||
auto init_thread =
|
||||
mark_critical(Scheduler::new_userspace_thread(init, "/bin/init"), "Failed to create PID 1 process for init");
|
||||
mark_critical(Scheduler::new_userspace_thread(init, "/bin/preinit"), "Failed to create PID 1 process for init");
|
||||
|
||||
auto reap = mark_critical(Scheduler::new_kernel_thread(reap_thread, "[reap]"),
|
||||
"Failed to create the process reaper kernel thread");
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"diskguid": "00000000-0000-0000-0000-000000000000",
|
||||
"disksize": 20,
|
||||
"disksize": 32,
|
||||
"align": 1024,
|
||||
"iso9660": true,
|
||||
"config": "initrd/sys/config",
|
||||
@ -12,7 +12,7 @@
|
||||
"partitions": [
|
||||
{
|
||||
"type": "boot",
|
||||
"size": 16
|
||||
"size": 8
|
||||
},
|
||||
{
|
||||
"type": "ext2",
|
||||
|
@ -9,7 +9,7 @@ function(luna_test SOURCE_FILE APP_NAME)
|
||||
add_dependencies(${APP_NAME} libc)
|
||||
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
|
||||
target_link_libraries(${APP_NAME} PRIVATE test os)
|
||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_ROOT}/initrd/bin/tests)
|
||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_BASE}/usr/bin/tests)
|
||||
endfunction()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
|
@ -17,14 +17,17 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chown -R root:root initrd
|
||||
chown -R root:root base
|
||||
|
||||
cmake --install $LUNA_BUILD_DIR
|
||||
|
||||
chmod 400 initrd/boot/moon
|
||||
chmod a+s initrd/bin/su
|
||||
chmod a+s base/usr/bin/su
|
||||
|
||||
mkdir -p initrd/home/selene
|
||||
chown 1000:1000 initrd/home/selene
|
||||
mkdir -p base/home/selene
|
||||
|
||||
mkdir -p initrd/dev
|
||||
mkdir -p base/dev
|
||||
mkdir -p base/mnt
|
||||
mkdir -p base/tmp
|
||||
|
||||
rm -f base/bin
|
||||
ln -s usr/bin base/bin
|
||||
|
@ -7,6 +7,6 @@ cd $LUNA_ROOT
|
||||
|
||||
fakeroot -u -s $LUNA_ROOT/.fakeroot -- tools/install.sh
|
||||
|
||||
genext2fs -d base -B 4096 -b 1024 -L luna-rootfs -U -N 1024 build/ext2fs.bin
|
||||
fakeroot -u -i $LUNA_ROOT/.fakeroot -- genext2fs -d base -B 4096 -b 1024 -L luna-rootfs -N 1024 build/ext2fs.bin
|
||||
|
||||
fakeroot -u -i $LUNA_ROOT/.fakeroot mkbootimg luna.json Luna.iso
|
||||
mkbootimg luna.json Luna.iso
|
||||
|
Loading…
Reference in New Issue
Block a user