Compare commits
2 Commits
main
...
new-layout
Author | SHA1 | Date | |
---|---|---|---|
4087c7510e | |||
280b0c90af |
@ -11,13 +11,11 @@ jobs:
|
||||
- name: Download dependencies
|
||||
run: |
|
||||
apt update
|
||||
apt install -y cmake ninja-build nasm genext2fs qemu-system build-essential wget git clang-format
|
||||
apt install -y cmake ninja-build nasm genext2fs qemu-system build-essential wget git
|
||||
- name: Set up the toolchain
|
||||
run: |
|
||||
wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet
|
||||
tar xf ci-toolchain-arm64.tar.gz
|
||||
rm ci-toolchain-arm64.tar.gz
|
||||
- name: Check formatting
|
||||
run: tools/check-formatting.sh
|
||||
- name: Build and run tests
|
||||
run: tools/run-tests.sh
|
||||
|
@ -5,8 +5,8 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING true)
|
||||
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.7.0)
|
||||
set(LUNA_RELEASE_NAME "Pulsar")
|
||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.6.0)
|
||||
set(LUNA_RELEASE_NAME "Andromeda")
|
||||
|
||||
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
||||
@ -45,10 +45,12 @@ endif()
|
||||
|
||||
add_subdirectory(libluna)
|
||||
add_subdirectory(libos)
|
||||
add_subdirectory(gui)
|
||||
add_subdirectory(libui)
|
||||
add_subdirectory(libc)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(apps)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(shell)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(wind)
|
||||
add_subdirectory(terminal)
|
||||
add_subdirectory(editor)
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2022-2025, apio.
|
||||
Copyright (c) 2022-2024, apio.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
18
README.md
18
README.md
@ -20,30 +20,26 @@ A simple POSIX-based operating system for 64-bit computers, written in C++.
|
||||
## Screenshot
|
||||
![Screenshot as of 0.6.0](docs/screenshots/screenshot-0.6.0.png)
|
||||
|
||||
## System requirements and dependencies
|
||||
|
||||
Read [docs/dependencies.md](docs/dependencies.md) for the full information. In short, all modern Unixes should work, provided the dependencies are available.
|
||||
|
||||
## Setup
|
||||
|
||||
To build and run Luna, you will need to build a [cross-compiler](https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler) and cross-binutils for `x86_64-luna`.
|
||||
|
||||
There is a script provided for this. Run `tools/setup.sh` to build the toolchain.
|
||||
For this, you should start by installing the [required dependencies](https://wiki.osdev.org/GCC_Cross_Compiler#Installing_Dependencies).
|
||||
|
||||
Then, run `tools/setup.sh` to build the toolchain.
|
||||
|
||||
Please beware that building GCC and Binutils can take some time, depending on your machine.
|
||||
|
||||
## Running
|
||||
|
||||
To run Luna in a virtual machine, you should have [QEMU](https://www.qemu.org/) installed.
|
||||
|
||||
Additionally, the build process needs some extra dependencies to run: `cmake`, `ninja`, `nasm`, `fakeroot` and `genext2fs`.
|
||||
|
||||
`tools/run.sh` is the script you should use in most cases. It will build changed files, install, make an ISO image, and run Luna in QEMU.
|
||||
|
||||
If you have no toolchain set up, `run.sh` will build it automatically, which means that you don't necessarily have to run `setup.sh` manually since `run.sh` does it for you.
|
||||
|
||||
## Login UI
|
||||
|
||||
For development convenience, the system automatically starts a GUI session as the default user, without prompting for a password.
|
||||
|
||||
Despite this, Luna does have a login window built-in. If you'd like to try this feature out or start a GUI session as a different user, you'll need to edit [base/etc/loginui.conf](base/etc/loginui.conf) and change the line that says `Autologin=true` to `Autologin=false`.
|
||||
|
||||
## Prebuilt images
|
||||
|
||||
Prebuilt ISO images for every release version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna/releases).
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <luna/RefString.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@ -23,6 +23,16 @@ struct Tile
|
||||
class GameWidget final : public ui::Widget
|
||||
{
|
||||
public:
|
||||
GameWidget(ui::Window* window, ui::Widget* parent) : ui::Widget(window, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void show_tree(int indent) override
|
||||
{
|
||||
os::println("%*s- 2048 GameWidget (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
|
||||
m_rect.height);
|
||||
}
|
||||
|
||||
static constexpr int MARGIN = 5;
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override
|
||||
@ -325,7 +335,7 @@ class GameWidget final : public ui::Widget
|
||||
|
||||
canvas.fill(colors[tile.color]);
|
||||
|
||||
auto fmt = TRY(RefString::format("%d"_sv, tile.number));
|
||||
auto fmt = TRY(String::format("%d"_sv, tile.number));
|
||||
|
||||
auto font = ui::Font::default_bold_font();
|
||||
auto rect = ui::align({ 0, 0, canvas.width, canvas.height },
|
||||
@ -356,9 +366,8 @@ Result<int> luna_main(int, char**)
|
||||
window->set_background(ui::BLACK);
|
||||
window->set_title("2048");
|
||||
|
||||
GameWidget game;
|
||||
window->set_main_widget(game);
|
||||
game.reset();
|
||||
auto* game = TRY(window->create_main_widget<GameWidget>());
|
||||
game->reset();
|
||||
|
||||
return app.run();
|
||||
}
|
54
apps/CMakeLists.txt
Normal file
54
apps/CMakeLists.txt
Normal file
@ -0,0 +1,54 @@
|
||||
function(luna_app SOURCE_FILE APP_NAME)
|
||||
add_executable(${APP_NAME} ${SOURCE_FILE})
|
||||
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
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_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)
|
||||
luna_app(cat.cpp cat)
|
||||
luna_app(date.cpp date)
|
||||
luna_app(edit.cpp edit)
|
||||
luna_app(ls.cpp ls)
|
||||
luna_app(chown.cpp chown)
|
||||
luna_app(chmod.cpp chmod)
|
||||
luna_app(mkdir.cpp mkdir)
|
||||
luna_app(rm.cpp rm)
|
||||
luna_app(stat.cpp stat)
|
||||
luna_app(uname.cpp uname)
|
||||
luna_app(base64.cpp base64)
|
||||
luna_app(login.cpp login)
|
||||
luna_app(mount.cpp mount)
|
||||
luna_app(umount.cpp umount)
|
||||
luna_app(ps.cpp ps)
|
||||
luna_app(time.cpp time)
|
||||
luna_app(ln.cpp ln)
|
||||
luna_app(mktemp.cpp mktemp)
|
||||
luna_app(sysfuzz.cpp sysfuzz)
|
||||
luna_app(cp.cpp cp)
|
||||
luna_app(kill.cpp kill)
|
||||
luna_app(gol.cpp gol)
|
||||
target_link_libraries(gol PUBLIC ui)
|
||||
luna_app(touch.cpp touch)
|
||||
luna_app(free.cpp free)
|
||||
luna_app(about.cpp about)
|
||||
target_link_libraries(about PUBLIC ui)
|
||||
luna_app(taskbar.cpp taskbar)
|
||||
target_link_libraries(taskbar PUBLIC ui)
|
||||
luna_app(2048.cpp 2048)
|
||||
target_link_libraries(2048 PUBLIC ui)
|
||||
luna_app(clock.cpp clock)
|
||||
target_link_libraries(clock PUBLIC ui)
|
||||
luna_app(startui.cpp startui)
|
||||
luna_app(launch.cpp launch)
|
||||
luna_app(run.cpp run)
|
46
apps/about.cpp
Normal file
46
apps/about.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <luna/String.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/Label.h>
|
||||
#include <ui/Layout.h>
|
||||
|
||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_title("About");
|
||||
window->set_background(BACKGROUND_COLOR);
|
||||
|
||||
utsname info;
|
||||
uname(&info);
|
||||
|
||||
auto* main_layout = TRY(window->create_main_widget<ui::VerticalLayout>());
|
||||
main_layout->set_layout_settings(ui::Margins { 0, 0, 20, 40 });
|
||||
|
||||
auto* title = TRY(main_layout->add_child_widget<ui::Label>());
|
||||
title->set_text("About Luna");
|
||||
title->set_font(ui::Font::default_bold_font());
|
||||
|
||||
auto* version_info = TRY(main_layout->add_child_widget<ui::VerticalLayout>());
|
||||
version_info->set_layout_settings(ui::Margins { 0, 0, 10, 10 });
|
||||
|
||||
auto* os_release = TRY(version_info->add_child_widget<ui::Label>());
|
||||
String os_release_text = TRY(String::format("OS release: %s"_sv, info.release));
|
||||
os_release->set_text(os_release_text.view());
|
||||
|
||||
auto* kernel_version = TRY(version_info->add_child_widget<ui::Label>());
|
||||
String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version));
|
||||
kernel_version->set_text(kernel_version_text.view());
|
||||
|
||||
auto* license = TRY(version_info->add_child_widget<ui::Label>());
|
||||
license->set_text("Licensed under the BSD-2-Clause license.");
|
||||
|
||||
return app.run();
|
||||
}
|
@ -29,12 +29,12 @@ Result<int> luna_main(int, char**)
|
||||
window->set_title("Clock");
|
||||
window->set_background(ui::GRAY);
|
||||
|
||||
g_label = TRY(make<ui::Label>("00:00:00"));
|
||||
g_label = TRY(window->create_main_widget<ui::Label>());
|
||||
|
||||
g_label->set_text("00:00:00");
|
||||
g_label->set_font(ui::Font::default_bold_font());
|
||||
g_label->set_color(ui::BLACK);
|
||||
|
||||
window->set_main_widget(*g_label);
|
||||
|
||||
update_time();
|
||||
|
||||
auto timer = TRY(os::Timer::create_repeating(1000, update_time));
|
@ -6,10 +6,8 @@
|
||||
#include <luna/Utf8.h>
|
||||
#include <luna/Vector.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/Config.h>
|
||||
#include <os/Directory.h>
|
||||
#include <os/File.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
#include <pwd.h>
|
||||
@ -40,7 +38,6 @@ struct Service
|
||||
Option<uid_t> user {};
|
||||
Option<gid_t> group {};
|
||||
bool wait { false };
|
||||
bool wait_notify { false };
|
||||
Option<pid_t> pid {};
|
||||
};
|
||||
|
||||
@ -135,15 +132,10 @@ static Result<void> try_start_service(Service& service)
|
||||
new_stdin->set_close_on_exec();
|
||||
}
|
||||
|
||||
os::IPC::Notifier notifier;
|
||||
if (service.wait_notify) { notifier = os::IPC::Notifier::create(); }
|
||||
|
||||
pid_t pid = TRY(os::Process::fork());
|
||||
if (pid == 0)
|
||||
{
|
||||
if (service.wait_notify) { notifier.hook(); }
|
||||
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||
if (service.wait_notify) { notifier.unhook(); }
|
||||
if (rc.has_error())
|
||||
{
|
||||
do_errlog("[child %d] failed to start service %s due to error: %s\n", getpid(), service.name.chars(),
|
||||
@ -165,17 +157,7 @@ static Result<void> try_start_service(Service& service)
|
||||
do_log("[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (service.wait_notify)
|
||||
{
|
||||
bool success = notifier.wait(1000);
|
||||
if (!success)
|
||||
{
|
||||
do_log("[init] service %s with pid %d failed to start successfully\n", service.name.chars(), pid);
|
||||
}
|
||||
}
|
||||
service.pid = pid;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -193,49 +175,118 @@ static Result<void> load_service(const os::Path& path)
|
||||
{
|
||||
do_log("[init] reading service file: %s\n", path.name().chars());
|
||||
|
||||
auto file = TRY(os::ConfigFile::open(path));
|
||||
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||
|
||||
Service service;
|
||||
|
||||
auto name = file->read_string("Name");
|
||||
if (!name.has_value())
|
||||
while (true)
|
||||
{
|
||||
auto line = TRY(file->read_line());
|
||||
if (line.is_empty()) break;
|
||||
|
||||
line.trim("\n");
|
||||
if (line.is_empty()) continue;
|
||||
|
||||
auto parts = TRY(line.split_once('='));
|
||||
if (parts.size() < 2 || parts[0].is_empty() || parts[1].is_empty())
|
||||
{
|
||||
do_log("[init] file contains invalid line, aborting: '%s'\n", line.chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Name")
|
||||
{
|
||||
service.name = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Description")
|
||||
{
|
||||
// We let users specify this in the config file, but init doesn't actually use it.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Command")
|
||||
{
|
||||
service.command = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Restart")
|
||||
{
|
||||
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
|
||||
{
|
||||
service.restart = true;
|
||||
continue;
|
||||
}
|
||||
service.restart = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Environment")
|
||||
{
|
||||
service.environment = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "StandardOutput")
|
||||
{
|
||||
service.standard_output = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "StandardError")
|
||||
{
|
||||
service.standard_error = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "StandardInput")
|
||||
{
|
||||
service.standard_input = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "WorkingDirectory")
|
||||
{
|
||||
service.working_directory = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_is_system && parts[0].view() == "User")
|
||||
{
|
||||
auto* pw = getpwnam(parts[1].chars());
|
||||
if (!pw) continue;
|
||||
service.user = pw->pw_uid;
|
||||
service.group = pw->pw_gid;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Wait")
|
||||
{
|
||||
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
|
||||
{
|
||||
service.wait = true;
|
||||
continue;
|
||||
}
|
||||
service.wait = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
do_log("[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||
}
|
||||
|
||||
if (service.name.is_empty())
|
||||
{
|
||||
do_log("[init] service file is missing 'Name' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
service.name = TRY(String::from_string_view(name.value()));
|
||||
|
||||
auto command = file->read_string("Command");
|
||||
if (!command.has_value())
|
||||
if (service.command.is_empty())
|
||||
{
|
||||
do_log("[init] service file is missing 'Command' entry, aborting!\n");
|
||||
return {};
|
||||
}
|
||||
service.command = TRY(String::from_string_view(command.value()));
|
||||
|
||||
service.restart = file->read_boolean_or("Restart", false);
|
||||
service.environment = TRY(String::from_string_view(file->read_string_or("Environment", {})));
|
||||
service.standard_output = TRY(String::from_string_view(file->read_string_or("StandardOutput", {})));
|
||||
service.standard_error = TRY(String::from_string_view(file->read_string_or("StandardError", {})));
|
||||
service.standard_input = TRY(String::from_string_view(file->read_string_or("StandardInput", {})));
|
||||
service.working_directory = TRY(String::from_string_view(file->read_string_or("WorkingDirectory", {})));
|
||||
|
||||
if (g_is_system)
|
||||
{
|
||||
auto user = file->read_string("User");
|
||||
if (user.has_value())
|
||||
{
|
||||
auto* pw = getpwnam(user.value().chars());
|
||||
if (pw)
|
||||
{
|
||||
service.user = pw->pw_uid;
|
||||
service.group = pw->pw_gid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service.wait = file->read_boolean_or("Wait", false);
|
||||
service.wait_notify = file->read_boolean_or("WaitUntilReady", false);
|
||||
|
||||
do_log("[init] loaded service %s into memory\n", service.name.chars());
|
||||
|
||||
@ -324,7 +375,6 @@ static void wait_for_child(int)
|
||||
auto rc = os::Process::wait(os::Process::ANY_CHILD, &status);
|
||||
if (rc.has_error())
|
||||
{
|
||||
if (rc.error() == ECHILD) return;
|
||||
do_log("[init] waitpid error %s", rc.error_string());
|
||||
return;
|
||||
}
|
||||
@ -427,6 +477,8 @@ Result<int> luna_main(int argc, char** argv)
|
||||
"change the default service path (/etc/init or /etc/user)");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
signal(SIGCHLD, wait_for_child);
|
||||
|
||||
if (user) return user_init(service_path);
|
||||
return sysinit(service_path);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file execd.cpp
|
||||
* @file launch.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Background process that handles detached launching of apps.
|
||||
*
|
||||
@ -10,7 +10,6 @@
|
||||
#include <errno.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
@ -42,24 +41,24 @@ void handle_ipc_message(os::IPC::ClientConnection& client, u8 id, void*)
|
||||
switch (id)
|
||||
{
|
||||
case os::Launcher::LAUNCH_DETACHED_ID: handle_launch_detached_message(client); break;
|
||||
default: os::eprintln("execd: Invalid IPC message from client!"); return;
|
||||
default: os::eprintln("launch: Invalid IPC message from client!"); return;
|
||||
}
|
||||
}
|
||||
|
||||
void sigchld_handler(int)
|
||||
{
|
||||
os::Process::wait(os::Process::ANY_CHILD, nullptr, WNOHANG);
|
||||
os::Process::wait(os::Process::ANY_CHILD, nullptr);
|
||||
}
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
TRY(os::Security::pledge("stdio wpath cpath unix proc exec", NULL));
|
||||
|
||||
StringView socket_path = "/tmp/execd.sock";
|
||||
StringView socket_path = "/tmp/launch.sock";
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Background process that handles detached launching of apps."_sv);
|
||||
parser.add_system_program_info("execd"_sv);
|
||||
parser.add_system_program_info("launch"_sv);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
@ -67,9 +66,6 @@ Result<int> luna_main(int argc, char** argv)
|
||||
auto server = TRY(os::LocalServer::create(socket_path, false));
|
||||
TRY(server->listen(20));
|
||||
|
||||
// We're ready now.
|
||||
os::IPC::notify_parent();
|
||||
|
||||
Vector<OwnedPtr<os::IPC::ClientConnection>> clients;
|
||||
Vector<struct pollfd> fds;
|
||||
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
|
||||
@ -87,7 +83,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
if (fds[0].revents & POLLIN)
|
||||
{
|
||||
auto client = TRY(server->accept());
|
||||
os::println("execd: New client connected!");
|
||||
os::println("launch: New client connected!");
|
||||
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
|
||||
|
||||
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
||||
@ -99,7 +95,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
if (fds[i + 1].revents & POLLIN) clients[i]->check_for_messages();
|
||||
if (fds[i + 1].revents & POLLHUP)
|
||||
{
|
||||
os::println("execd: Client %zu disconnected", i);
|
||||
os::println("launch: Client %zu disconnected", i);
|
||||
fds.remove_at(i + 1);
|
||||
auto client = clients.remove_at(i);
|
||||
client->disconnect();
|
@ -22,7 +22,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
parser.add_positional_argument(program, "program", true);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
OwnedPtr<os::IPC::Client> launcher_client = TRY(os::IPC::Client::connect("/tmp/execd.sock", false));
|
||||
OwnedPtr<os::IPC::Client> launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
||||
|
||||
os::println("Requesting to start program '%s'...", program.chars());
|
||||
|
@ -11,16 +11,26 @@
|
||||
#include <grp.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Main.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Timeout should be provided in milliseconds.
|
||||
Result<void> wait_until_file_created(StringView path, int timeout)
|
||||
{
|
||||
struct stat st;
|
||||
while (stat(path.chars(), &st) < 0)
|
||||
{
|
||||
if (timeout <= 0) return err(ETIMEDOUT);
|
||||
usleep(100000); // FIXME: Implement something like inotify or use signals to avoid polling.
|
||||
timeout -= 100;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Vector<gid_t>> read_supplementary_groups(const char* name)
|
||||
{
|
||||
Vector<gid_t> extra_groups;
|
||||
@ -60,6 +70,12 @@ Result<void> spawn_process_as_user(Slice<StringView> arguments, uid_t user, gid_
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringView username;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
@ -68,14 +84,6 @@ Result<int> luna_main(int argc, char** argv)
|
||||
parser.add_value_argument(username, 'u', "user", "the user to start the UI session as");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (username.is_empty())
|
||||
{
|
||||
os::eprintln("error: startui needs a --user parameter to run.");
|
||||
@ -97,24 +105,9 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
setsid();
|
||||
|
||||
// First of all, start the display server, in case we haven't been started by loginui.
|
||||
// FIXME: What if we're started after a wind process has previously run but exited, so we think there's a wind
|
||||
// process when there isn't.
|
||||
if (!os::FileSystem::exists("/tmp/wind.sock"))
|
||||
{
|
||||
// We need to wait for this one, since its sockets are required later.
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
StringView wind_command[] = { "/usr/bin/wind" };
|
||||
os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1));
|
||||
},
|
||||
1000);
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("startui: failed to start wind, timed out");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// First of all, start the display server.
|
||||
StringView wind_command[] = { "/usr/bin/wind" };
|
||||
TRY(os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1)));
|
||||
|
||||
clearenv();
|
||||
chdir(pw->pw_dir);
|
||||
@ -123,23 +116,18 @@ Result<int> luna_main(int argc, char** argv)
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
setenv("SHELL", pw->pw_shell, 1);
|
||||
|
||||
// We also need to wait for this one, since taskbar requires execd.sock.
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
(void)os::FileSystem::remove("/tmp/execd.sock");
|
||||
StringView execd_command[] = { "/usr/bin/execd" };
|
||||
spawn_process_as_user(Slice<StringView>(execd_command, 1), pw->pw_uid, pw->pw_gid, groups.slice());
|
||||
},
|
||||
1000);
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("startui: failed to start execd, timed out");
|
||||
return 1;
|
||||
}
|
||||
// Next, start the required UI components.
|
||||
StringView launch_command[] = { "/usr/bin/launch" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(launch_command, 1), pw->pw_uid, pw->pw_gid, groups.slice()));
|
||||
|
||||
TRY(wait_until_file_created("/tmp/wsys.sock", 10000));
|
||||
TRY(wait_until_file_created("/tmp/launch.sock", 10000));
|
||||
|
||||
StringView taskbar_command[] = { "/usr/bin/taskbar" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(taskbar_command, 1), pw->pw_uid, pw->pw_gid, system_groups.slice()));
|
||||
|
||||
TRY(wait_until_file_created("/tmp/wind.sock", 10000));
|
||||
|
||||
// Finally, start init in user mode to manage all configured optional services.
|
||||
StringView init_command[] = { "/usr/bin/init", "--user" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(init_command, 2), pw->pw_uid, pw->pw_gid, groups.slice()));
|
@ -1,7 +1,6 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <luna/SHA.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
@ -14,14 +13,6 @@
|
||||
static struct termios orig;
|
||||
static int fd = -1;
|
||||
|
||||
Result<String> hash_password(const char* pw)
|
||||
{
|
||||
SHA256 sha;
|
||||
sha.append((const u8*)pw, strlen(pw));
|
||||
auto digest = TRY(sha.digest());
|
||||
return digest.to_string();
|
||||
}
|
||||
|
||||
void restore_terminal()
|
||||
{
|
||||
tcsetattr(fd, TCSANOW, &orig);
|
||||
@ -185,9 +176,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
char* pass = getpass();
|
||||
if (!pass) return 1;
|
||||
|
||||
auto result = hash_password(pass).release_value();
|
||||
|
||||
if (strcmp(result.chars(), passwd))
|
||||
if (strcmp(pass, passwd))
|
||||
{
|
||||
fprintf(stderr, "%s: wrong password!\n", argv[0]);
|
||||
return 1;
|
@ -1,6 +1,5 @@
|
||||
#include <luna/Sort.h>
|
||||
#include <luna/StringBuilder.h>
|
||||
#include <os/Config.h>
|
||||
#include <os/Directory.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
@ -19,31 +18,31 @@ static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
|
||||
|
||||
static OwnedPtr<os::IPC::Client> launcher_client;
|
||||
|
||||
void sigquit_handler(int)
|
||||
void sigchld_handler(int)
|
||||
{
|
||||
wait(nullptr);
|
||||
}
|
||||
|
||||
void sighup_handler(int)
|
||||
{
|
||||
// Reload the taskbar by exec-ing the executable, resetting everything.
|
||||
StringView args[] = { "/usr/bin/taskbar" };
|
||||
os::Process::exec(args[0], { args, 1 });
|
||||
}
|
||||
|
||||
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, StringView path, StringView icon)
|
||||
Result<void> create_widget_group_for_app(ui::HorizontalLayout* layout, StringView path, StringView icon)
|
||||
{
|
||||
auto* button = TRY(make<ui::Button>(ui::Rect { 0, 0, 50, 50 }));
|
||||
layout.add_widget(*button);
|
||||
auto* button = TRY(layout->add_child_widget<ui::Button>());
|
||||
|
||||
auto* container = TRY(
|
||||
make<ui::Container>(ui::Rect { 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center));
|
||||
button->set_widget(*container);
|
||||
auto* container = TRY(button->create_child_widget<ui::Container>());
|
||||
button->set_action([=] {
|
||||
os::Launcher::LaunchDetachedRequest request;
|
||||
SET_IPC_STRING(request.command, path.chars());
|
||||
launcher_client->send_async(request);
|
||||
});
|
||||
|
||||
auto image = TRY(ui::ImageWidget::load(icon));
|
||||
container->set_widget(*image);
|
||||
|
||||
image.leak();
|
||||
auto image = TRY(container->create_child_widget<ui::ImageWidget>());
|
||||
image->load(icon);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -62,31 +61,64 @@ static Result<void> load_application_file(const os::Path& path)
|
||||
{
|
||||
os::println("[taskbar] reading app file: %s", path.name().chars());
|
||||
|
||||
auto file = TRY(os::ConfigFile::open(path));
|
||||
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||
|
||||
ApplicationFile app_file;
|
||||
|
||||
app_file.name = TRY(String::from_string_view(file->read_string_or("Name", "")));
|
||||
if (app_file.name.is_empty())
|
||||
while (true)
|
||||
{
|
||||
os::println("[taskbar] app file is missing 'Name' entry, aborting!");
|
||||
return {};
|
||||
auto line = TRY(file->read_line());
|
||||
if (line.is_empty()) break;
|
||||
|
||||
line.trim("\n");
|
||||
if (line.is_empty()) continue;
|
||||
|
||||
auto parts = TRY(line.split_once('='));
|
||||
if (parts.size() < 2 || parts[0].is_empty() || parts[1].is_empty())
|
||||
{
|
||||
os::println("[taskbar] file contains invalid line, aborting: '%s'", line.chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Name")
|
||||
{
|
||||
app_file.name = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Description")
|
||||
{
|
||||
// We let users specify this in the config file, but taskbar doesn't actually use it.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Command")
|
||||
{
|
||||
app_file.command = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "Icon")
|
||||
{
|
||||
app_file.icon = move(parts[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
os::println("[taskbar] skipping unknown entry name %s", parts[0].chars());
|
||||
}
|
||||
|
||||
app_file.command = TRY(String::from_string_view(file->read_string_or("Command", "")));
|
||||
if (app_file.command.is_empty())
|
||||
{
|
||||
os::println("[taskbar] app file is missing 'Command' entry, aborting!");
|
||||
return {};
|
||||
}
|
||||
|
||||
app_file.icon = TRY(String::from_string_view(file->read_string_or("Icon", "")));
|
||||
if (app_file.icon.is_empty())
|
||||
{
|
||||
os::println("[taskbar] app file is missing 'Icon' entry, aborting!");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (app_file.command.is_empty())
|
||||
{
|
||||
os::println("[taskbar] app file is missing 'Command' entry, aborting!");
|
||||
return {};
|
||||
}
|
||||
|
||||
os::println("[taskbar] loaded app %s into memory", app_file.name.chars());
|
||||
|
||||
TRY(s_app_files.try_append(move(app_file)));
|
||||
@ -112,11 +144,11 @@ Result<int> luna_main(int, char**)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init("/tmp/wsys.sock"));
|
||||
app.pledge(ui::Pledge::ExtendedLayers);
|
||||
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGQUIT, sigquit_handler));
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGHUP, sighup_handler));
|
||||
|
||||
launcher_client = TRY(os::IPC::Client::connect("/tmp/execd.sock", false));
|
||||
launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
||||
|
||||
ui::Rect screen = app.screen_rect();
|
||||
|
||||
@ -126,11 +158,10 @@ Result<int> luna_main(int, char**)
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_background(TASKBAR_COLOR);
|
||||
window->set_layer(ui::Layer::Background);
|
||||
app.pledge(0);
|
||||
window->set_special_attributes(ui::UNFOCUSEABLE);
|
||||
|
||||
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
||||
window->set_main_widget(layout);
|
||||
auto* layout = TRY(window->create_main_widget<ui::HorizontalLayout>());
|
||||
layout->set_layout_settings(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
||||
|
||||
load_app_files_from_path("/usr/share/applications/");
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name=login
|
||||
Description=Start a graphical user session.
|
||||
Command=/usr/bin/loginui
|
||||
Command=/usr/bin/startui --user=selene
|
||||
StandardOutput=/dev/uart0
|
||||
StandardError=/dev/uart0
|
||||
Restart=true
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Configuration file for loginui.
|
||||
# If this parameter is set to "true", loginui automatically spawns a UI session as the below user instead of prompting for a username and password.
|
||||
Autologin=true
|
||||
# The user to create a session for if "Autologin" is set to true (see above). If the username is invalid, loginui will behave as if "Autologin" was set to false.
|
||||
AutologinUser=selene
|
@ -1,3 +1,3 @@
|
||||
root:ce5ca673d13b36118d54a7cf13aeb0ca012383bf771e713421b4d1fd841f539a:0:0:99999:7:::
|
||||
root:toor:0:0:99999:7:::
|
||||
wind:!:0:0:99999:7:::
|
||||
selene:9e78b43ea00edcac8299e0cc8df7f6f913078171335f733a21d5d911b6999132:0:0:99999:7:::
|
||||
selene:moon:0:0:99999:7:::
|
||||
|
@ -1,26 +1,14 @@
|
||||
Welcome to the Luna operating system!
|
||||
You are running on the default user account, selene.
|
||||
|
||||
If you are familiar with Unix-style operating systems (like Linux or *BSD),
|
||||
you should be able to use the Luna terminal without much problems.
|
||||
If you are familiar with Unix-style operating systems (like Linux or *BSD), you should be able to use the Luna terminal without much problems.
|
||||
|
||||
Following the traditional Unix filesystem structure,
|
||||
programs are installed in /usr/bin (/bin is a symlink to /usr/bin).
|
||||
The command `ls /bin` will show all commands available on
|
||||
your current Luna installation.
|
||||
Following the traditional Unix filesystem structure, programs are installed in /usr/bin (/bin is a symlink to /usr/bin). The command `ls /bin` will show all commands available on your current Luna installation.
|
||||
|
||||
Currently, because of driver limitations,
|
||||
the root file system is mounted read-only.
|
||||
Your home folder is writable, but volatile; it is
|
||||
created and populated on boot,
|
||||
and its contents will vanish after a reboot.
|
||||
Currently, because of driver limitations, the root file system is mounted read-only. Your home folder is writable, but volatile; it is created and populated on boot, and its contents will vanish after a reboot.
|
||||
|
||||
The system is booted using the 'init' program.
|
||||
You can read its configuration files in the /etc/init directory to
|
||||
learn more about the boot process.
|
||||
The system is booted using the 'init' program. You can read its configuration files in the /etc/init directory to learn more about the boot process.
|
||||
|
||||
Luna is free software, released under the BSD-2-Clause license.
|
||||
The license is included in the LICENSE file in your home directory.
|
||||
Luna is free software, released under the BSD-2-Clause license. The license is included in the LICENSE file in your home directory.
|
||||
|
||||
View the source code and read more about Luna at
|
||||
https://git.cloudapio.eu/apio/Luna.
|
||||
View the source code and read more about Luna at https://git.cloudapio.eu/apio/Luna.
|
||||
|
3
base/etc/user/00-terminal
Normal file
3
base/etc/user/00-terminal
Normal file
@ -0,0 +1,3 @@
|
||||
Name=terminal
|
||||
Description=Start the terminal.
|
||||
Command=/usr/bin/terminal
|
@ -1,3 +0,0 @@
|
||||
Name=welcome
|
||||
Description=Show a welcome message for the user.
|
||||
Command=/usr/bin/editor welcome
|
@ -1,3 +0,0 @@
|
||||
Name=editor
|
||||
Icon=/usr/share/icons/32x32/app-editor.tga
|
||||
Command=/usr/bin/editor
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
@ -54,15 +54,15 @@ As soon as the scheduler switches to the `[kinit]` thread, it will never return
|
||||
|
||||
`[kinit]` spawns two more kernel threads, `[reap]` and `[oom]`. While `[kinit]` exits before PID 1 is started, `[reap]` and `[oom]` are present throughout the lifetime of a Luna system, and can be seen in the output of `ps`. Let's take a look at what they do.
|
||||
|
||||
- `[reap]`: To understand what this thread does, we must take a look at what happens when processes exit on Luna.
|
||||
- `[reap]`: To understand what this thread does, we must take a look at what happens when threads exit on Luna.
|
||||
|
||||
_(Relevant files: [kernel/src/main.cpp](../kernel/src/main.cpp#L23), [kernel/src/thread/Scheduler.cpp](../kernel/src/thread/Scheduler.cpp#L231), [kernel/src/thread/Thread.cpp](../kernel/src/thread/Thread.cpp#L126), [kernel/src/sys/waitpid.cpp](../kernel/src/sys/waitpid.cpp#L84))_
|
||||
_(Relevant files: [kernel/src/main.cpp](../kernel/src/main.cpp#L23), [kernel/src/thread/Scheduler.cpp](../kernel/src/thread/Scheduler.cpp#L205), [kernel/src/thread/Thread.cpp](../kernel/src/thread/Thread.cpp#L105), [kernel/src/sys/waitpid.cpp](../kernel/src/sys/waitpid.cpp#L84))_
|
||||
|
||||
When a process calls the `_exit()` syscall, all its threads' states are set to "Dying". This tells the scheduler to avoid switching to them, and the process's parent is notified, by sending SIGCHLD and (optionally) unblocking a blocked `waitpid()` call. The process remains visible to the rest of the system, and if its parent does not wait for it, it will stay there as a "zombie process". Meanwhile, the `[reap]` thread runs and collects all the resources from each thread. The process object is still alive (in a "zombie" state), but its threads have been cleaned up.
|
||||
When a thread calls the `_exit()` syscall, its state is set to "Exited". This tells the scheduler to avoid switching to it, and the thread's parent is notified, either by sending SIGCHLD or unblocking a blocked `waitpid()` call. The thread remains visible to the rest of the system, and if its parent does not wait for it, it will stay there as a "zombie thread".
|
||||
|
||||
When the process's parent waits for it, it is marked for reaping (by setting its thread count to -1 (PROCESS_SHOULD_REAP)), and the `[reap]` thread runs.
|
||||
When the thread's parent waits for it, its state is instead set to "Dying", and the `[reap]` thread runs. (Kernel threads skip all the "parent notifying" shenanigans and go straight to the "Dying" state when calling `kernel_exit()`).
|
||||
|
||||
The `[reap]` thread then "reaps" all the dead processes' resources. It frees up their memory, file descriptors, and other resources. After reaping, the process is deleted, and no trace of it is left.
|
||||
The `[reap]` thread then "reaps" all the "Dying" threads' resources. It frees up the threads' memory, file descriptors, and unmaps the memory used for the kernel stack corresponding to that thread. After reaping, the thread is deleted, and no trace of it is left.
|
||||
|
||||
- `[oom]`: This thread handles Out-Of-Memory (OOM) situations. Whenever the kernel has 1/4 or 1/8 of the available physical memory left (thresholds may be tweaked in the future), or it has run out, it runs this thread.
|
||||
|
||||
@ -90,7 +90,7 @@ After the kernel stage of the boot process, the system looks like this:
|
||||
```
|
||||
|
||||
## Stage 2: preinit
|
||||
_Relevant files: [system/preinit.cpp](../system/preinit.cpp)_
|
||||
_Relevant files: [apps/preinit.cpp](../apps/preinit.cpp)_
|
||||
|
||||
Luna's userspace init process is split into two programs: `/bin/preinit`, which resides on the initial ramdisk, and `/usr/bin/init`, which resides on the root partition.
|
||||
|
||||
@ -133,7 +133,7 @@ After the preinit stage of the boot process, the system looks like this:
|
||||
```
|
||||
|
||||
## Stage 3: init
|
||||
_Relevant files: [system/init.cpp](../system/init.cpp#L406)_
|
||||
_Relevant files: [apps/init.cpp](../apps/init.cpp#L406)_
|
||||
|
||||
`/usr/bin/init` is the actual init system. It is in charge of starting user-defined services.
|
||||
|
||||
@ -149,7 +149,7 @@ Currently, there are two service files defined by default in `/etc/init`:
|
||||
|
||||
`00-home`: This service sets up a `tmpfs` on `/home/selene`, so that the home directory is writable.
|
||||
|
||||
`99-login`: This service starts a graphical session, by calling `/usr/bin/loginui`. This service will be restarted if necessary.
|
||||
`99-login`: This service starts a graphical session, by calling `/usr/bin/startui`. This service will be restarted if necessary.
|
||||
|
||||
### File system and process layout
|
||||
|
||||
@ -172,60 +172,24 @@ After the init stage of the boot process, the system looks like this:
|
||||
[x86_64-io] - PID 3
|
||||
[reap] - PID 4
|
||||
[oom] - PID 5
|
||||
/usr/bin/loginui - PID 13
|
||||
/usr/bin/startui - PID 13
|
||||
```
|
||||
|
||||
_Note: loginui is PID 13 because the `00-home` service is a shell script, which starts a few subprocesses. Since Luna does not allow for PID reuse right now, loginui ends up with PID 13._
|
||||
_Note: startui is PID 13 because the `00-home` service is a shell script, which starts a few subprocesses. Since Luna does not allow for PID reuse right now, startui ends up with PID 13._
|
||||
|
||||
## Stage 4: loginui
|
||||
_Relevant files: [gui/loginui.cpp](../gui/loginui.cpp), [gui/wind/main.cpp](../gui/wind/main.cpp)_
|
||||
|
||||
`/usr/bin/loginui`'s job is quite simple: it prompts the user to log in with their password, after which a graphical session is started.
|
||||
|
||||
_Note: On development builds, Autologin=true is added to /etc/loginui.conf which disables password prompting and executes startui directly._
|
||||
|
||||
First, loginui starts the display server, `/usr/bin/wind`, so that it can use its capabilities to show a graphical login prompt. It is started with permissions `root:root`, and later drops privileges to `wind:wind`.
|
||||
|
||||
After that, loginui prompts for a username and password, checks it against the hashed password stored in `/etc/shadow`, and finally executes `/usr/bin/startui` which does the actual heavy work of starting all the services needed for a UI session.
|
||||
|
||||
### File system and process layout
|
||||
|
||||
After the loginui stage of the boot process, the system looks like this:
|
||||
|
||||
#### File system
|
||||
```
|
||||
/ - ext2 root partition
|
||||
/dev - device file system
|
||||
/dev/shm - POSIX shared memory file system
|
||||
/dev/pts - POSIX pseudoterminal file system
|
||||
/tmp - system temporary file directory
|
||||
/usr, /etc, /home... - other directories contained in the root partition
|
||||
/home/selene - temporary home directory
|
||||
```
|
||||
|
||||
#### Processes
|
||||
```
|
||||
/usr/bin/init - PID 1
|
||||
[x86_64-io] - PID 3
|
||||
[reap] - PID 4
|
||||
[oom] - PID 5
|
||||
/usr/bin/startui - PID 13
|
||||
/usr/bin/wind - PID 14
|
||||
```
|
||||
|
||||
## Stage 5: startui
|
||||
_Relevant files: [system/startui.cpp](../system/startui.cpp), [gui/wind/main.cpp](../gui/wind/main.cpp)_
|
||||
## Stage 4: startui
|
||||
_Relevant files: [apps/startui.cpp](../apps/startui.cpp), [wind/main.cpp](../wind/main.cpp)_
|
||||
|
||||
`/usr/bin/startui` starts a graphical user session.
|
||||
|
||||
A Luna graphical user session includes the following components:
|
||||
|
||||
- The display server itself, `/usr/bin/wind`. If not already started by loginui, `startui` makes sure it's running.
|
||||
- The execution server (`/usr/bin/execd`), which starts processes and keeps them alive on behalf of other processes. It is started with the standard permissions `selene:selene`.
|
||||
- The taskbar, `/usr/bin/taskbar`. It is started with the standard permissions `selene:selene`, plus an extra group `wsys` to be able to connect to a special display server socket (`/tmp/wsys.sock`, as opposed to the standard `/tmp/wind.sock`). This grants it the ability to use advanced wind features, such as placing the taskbar window behind all other windows.
|
||||
- The display server itself, `/usr/bin/wind`. It is started with permissions `root:root`, and later drops privileges to `wind:wind`.
|
||||
- The launch server (`/usr/bih/launch`), which starts processes and keeps them alive on behalf of other processes. It is started with the standard permissions `selene:selene`.
|
||||
- The taskbar, `/usr/bin/taskbar`. It is started with the standard permissions `selene:selene`, plus an extra group `wsys` to be able to connect to a special display server socket.
|
||||
- The init process corresponding to that session (`/usr/bin/init --user`). This process does the same thing as `init` above (manages services), but runs with user privileges and reads configuration files from `/etc/user` instead (in the future this will be changed to a user-specific directory).
|
||||
|
||||
Currently, `init --user` only does one thing: it opens up a text editor with a welcome message on startup. It can be configured to do whatever the user desires to do on startup, by placing the appropriate configuration files in `/etc/user`.
|
||||
Currently, `init --user` only does one thing: it opens up a terminal window on startup. It can be configured to do whatever the user desires to do on startup, by placing the appropriate configuration files in `/etc/user`.
|
||||
|
||||
### File system and process layout
|
||||
|
||||
@ -250,7 +214,8 @@ After the startui stage of the boot process, the system is fully started up and
|
||||
[oom] - PID 5
|
||||
/usr/bin/startui - PID 13
|
||||
/usr/bin/wind - PID 14
|
||||
/usr/bin/execd - PID 15
|
||||
/usr/bin/launch - PID 15
|
||||
/usr/bin/taskbar - PID 16
|
||||
/usr/bin/init --user - PID 17
|
||||
/usr/bin/editor welcome - PID 18
|
||||
/usr/bin/terminal - PID 18
|
||||
/bin/sh - PID 19
|
||||
|
@ -1,35 +0,0 @@
|
||||
# Dependencies required to build and run Luna
|
||||
|
||||
## System requirements
|
||||
|
||||
Any modern UNIX-like system that supports all the tools listed below should work (Hopefully, that will include Luna itself in the future!).
|
||||
|
||||
I personally build and run Luna on an amd64 Fedora Linux 40 machine. CI runs on arm64 Ubuntu 22.04. Any other configurations are untested. Windows is not supported, although you can try using WSL if you really want to.
|
||||
|
||||
## Building a cross-compiler toolchain
|
||||
For this, you should start by installing the [required dependencies](https://wiki.osdev.org/GCC_Cross_Compiler#Installing_Dependencies) for any OSdev cross-compiler build.
|
||||
|
||||
Also make sure you have the perl module `File::Compare` installed, it is required to build autoconf. On Fedora you can install it using the package manager by running `# dnf install perl-File-Compare`. If your distro doesn't have it, you might have to install it via `cpan`.
|
||||
|
||||
## Building the actual system
|
||||
The build process needs some extra dependencies to run: `cmake`, `ninja`, `nasm`, `fakeroot` and `genext2fs`. On some distributions the `ninja` package is called `ninja-build` instead.
|
||||
|
||||
If you want to use `make` instead of `ninja`, create a file called `env-local.sh` in the project root and add the line `USE_MAKE=1`. In this case, ninja does not need to be installed.
|
||||
|
||||
## Running the built image in a virtual machine
|
||||
The script provided by the project to run the system, `tools/run.sh`, assumes that QEMU is installed and uses that to run the image. Therefore, make sure your system has `qemu-system-x86_64` in the PATH. If it doesn't, install it using the method appropriate for your system, usually installing `qemu` or `qemu-system` from the package manager.
|
||||
|
||||
That being said, there's no requirement to use QEMU. If you want to use a different virtualization program, such as Oracle VirtualBox or VMWare, just use `tools/build-iso.sh` instead of `run.sh` and use the built `Luna.iso` in those programs.
|
||||
|
||||
## Formatting/linting
|
||||
|
||||
Please make sure you have `clang-format` installed. Additionally, if your editor does not support format-on-save or you do not have it configured, please run `tools/run-clang-format.sh` before committing, to make sure all code follows the same style conventions.
|
||||
|
||||
## Source dependencies
|
||||
TLDR: Luna does not depend on any third-party library.
|
||||
|
||||
Every part of Luna is written from scratch and depends only on its own libraries and programs, with two small exceptions (included here for crediting and licensing purposes, but there is no need to download and build them separately):
|
||||
|
||||
The bootloader, BOOTBOOT. It is available at [gitlab.com/bztsrc/bootboot](https://gitlab.com/bztsrc/bootboot), under the MIT license. It is automatically pulled and built from source by `tools/setup.sh`.
|
||||
|
||||
[libc/src/strtod.cpp](../libc/src/strtod.cpp). Written by Yasuhiro Matsumoto, adapted from https://gist.github.com/mattn/1890186 and available under a public domain license.
|
@ -10,14 +10,13 @@
|
||||
#include "EditorWidget.h"
|
||||
#include <ctype.h>
|
||||
#include <luna/PathParser.h>
|
||||
#include <luna/RefString.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Dialog.h>
|
||||
|
||||
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
||||
EditorWidget::EditorWidget(ui::Window* window, ui::Widget* parent)
|
||||
: ui::TextInput(window, parent), m_font(ui::Font::default_font())
|
||||
{
|
||||
recalculate_lines();
|
||||
}
|
||||
@ -29,8 +28,7 @@ Result<void> EditorWidget::load_file(const os::Path& path)
|
||||
|
||||
if (!rc.has_error() && !S_ISREG(st.st_mode))
|
||||
{
|
||||
auto message = TRY(RefString::format("%s is not a regular file", path.name().chars()));
|
||||
ui::Dialog::show_message("Error", message.view());
|
||||
os::eprintln("editor: not loading %s as it is not a regular file", path.name().chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -44,9 +42,9 @@ Result<void> EditorWidget::load_file(const os::Path& path)
|
||||
|
||||
m_cursor = m_data.size();
|
||||
|
||||
m_path = TRY(String::from_string_view(path.name()));
|
||||
m_path = path;
|
||||
|
||||
auto basename = TRY(PathParser::basename(m_path.view()));
|
||||
auto basename = TRY(PathParser::basename(m_path.name()));
|
||||
|
||||
String title = TRY(String::format("Text Editor - %s"_sv, basename.chars()));
|
||||
window()->set_title(title.view());
|
||||
@ -129,38 +127,15 @@ Result<ui::EventResult> EditorWidget::handle_key_event(const ui::KeyEventRequest
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::save_file_as()
|
||||
{
|
||||
ui::Dialog::show_input_dialog(
|
||||
"Save file as...", "Please enter the path to save this file to:", [this](StringView path) {
|
||||
m_path = String::from_string_view(path).release_value();
|
||||
auto rc = save_file();
|
||||
if (rc.has_error())
|
||||
{
|
||||
os::eprintln("Failed to save file %s: %s", m_path.chars(), rc.error_string());
|
||||
ui::Dialog::show_message("Error", "Failed to save file");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto basename = PathParser::basename(m_path.view()).release_value();
|
||||
|
||||
String title = String::format("Text Editor - %s"_sv, basename.chars()).release_value();
|
||||
window()->set_title(title.view());
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::save_file()
|
||||
{
|
||||
if (m_path.is_empty())
|
||||
if (m_path.is_empty_path())
|
||||
{
|
||||
TRY(save_file_as());
|
||||
return {};
|
||||
os::eprintln("editor: no file to save buffer to!");
|
||||
return err(ENOENT);
|
||||
}
|
||||
|
||||
auto file = TRY(os::File::open_or_create(m_path.view(), os::File::WriteOnly));
|
||||
auto file = TRY(os::File::open(m_path, os::File::WriteOnly));
|
||||
return file->write(m_data);
|
||||
}
|
||||
|
@ -16,20 +16,24 @@
|
||||
class EditorWidget : public ui::TextInput
|
||||
{
|
||||
public:
|
||||
EditorWidget(SharedPtr<ui::Font> font);
|
||||
EditorWidget(ui::Window* window, ui::Widget* parent);
|
||||
|
||||
Result<void> load_file(const os::Path& path);
|
||||
|
||||
Result<void> save_file();
|
||||
Result<void> save_file_as();
|
||||
|
||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override;
|
||||
|
||||
os::Path path()
|
||||
os::Path& path()
|
||||
{
|
||||
return m_path.view();
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void set_font(SharedPtr<ui::Font> font)
|
||||
{
|
||||
m_font = font;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -42,7 +46,7 @@ class EditorWidget : public ui::TextInput
|
||||
};
|
||||
Vector<Line> m_lines;
|
||||
|
||||
String m_path;
|
||||
os::Path m_path { AT_FDCWD };
|
||||
|
||||
Result<void> recalculate_lines();
|
||||
void recalculate_cursor_position();
|
@ -11,7 +11,6 @@
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Dialog.h>
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
@ -31,23 +30,15 @@ Result<int> luna_main(int argc, char** argv)
|
||||
window->set_title("Text Editor");
|
||||
app.set_main_window(window);
|
||||
|
||||
auto* editor = TRY(make<EditorWidget>(ui::Font::default_font()));
|
||||
window->set_main_widget(*editor);
|
||||
if (!path.is_empty()) editor->load_file(path);
|
||||
auto* editor = TRY(window->create_main_widget<EditorWidget>());
|
||||
if (!path.is_empty()) TRY(editor->load_file(path));
|
||||
|
||||
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) {
|
||||
auto result = editor->save_file();
|
||||
if (result.has_error())
|
||||
{
|
||||
os::eprintln("Failed to save file %s: %s", editor->path().name().chars(), result.error_string());
|
||||
ui::Dialog::show_message("Error", "Failed to save file");
|
||||
}
|
||||
if (result.has_error()) os::eprintln("editor: failed to save file: %s", result.error_string());
|
||||
else
|
||||
os::println("editor: buffer saved to %s successfully", editor->path().name().chars());
|
||||
}));
|
||||
|
||||
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl | ui::Mod_Shift }, true,
|
||||
[&](ui::Shortcut) { editor->save_file_as(); }));
|
||||
|
||||
window->draw();
|
||||
|
||||
return app.run();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
function(luna_service SOURCE_FILE APP_NAME)
|
||||
add_executable(${APP_NAME} ${SOURCE_FILE})
|
||||
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
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_BASE}/usr/bin)
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(libui)
|
||||
add_subdirectory(wind)
|
||||
add_subdirectory(apps)
|
||||
|
||||
luna_service(execd.cpp execd)
|
||||
luna_service(run.cpp run)
|
||||
luna_service(loginui.cpp loginui)
|
||||
target_link_libraries(loginui PRIVATE ui)
|
@ -1,17 +0,0 @@
|
||||
function(luna_app SOURCE_FILE APP_NAME)
|
||||
add_executable(${APP_NAME} ${SOURCE_FILE})
|
||||
target_compile_options(${APP_NAME} PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
add_dependencies(${APP_NAME} libc)
|
||||
target_include_directories(${APP_NAME} PRIVATE ${LUNA_BASE}/usr/include)
|
||||
target_link_libraries(${APP_NAME} PRIVATE os ui)
|
||||
install(TARGETS ${APP_NAME} DESTINATION ${LUNA_BASE}/usr/bin)
|
||||
endfunction()
|
||||
|
||||
luna_app(about.cpp about)
|
||||
luna_app(taskbar.cpp taskbar)
|
||||
luna_app(2048.cpp 2048)
|
||||
luna_app(clock.cpp clock)
|
||||
luna_app(gol.cpp gol)
|
||||
|
||||
add_subdirectory(editor)
|
||||
add_subdirectory(terminal)
|
@ -1,47 +0,0 @@
|
||||
#include <luna/String.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/Label.h>
|
||||
#include <ui/Layout.h>
|
||||
|
||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_title("About");
|
||||
window->set_background(BACKGROUND_COLOR);
|
||||
|
||||
utsname info;
|
||||
uname(&info);
|
||||
|
||||
ui::VerticalLayout main_layout;
|
||||
window->set_main_widget(main_layout);
|
||||
|
||||
ui::Label title("About Luna");
|
||||
title.set_font(ui::Font::default_bold_font());
|
||||
|
||||
main_layout.add_widget(title);
|
||||
|
||||
ui::VerticalLayout version_info;
|
||||
main_layout.add_widget(version_info);
|
||||
|
||||
ui::Label license("Licensed under the BSD-2-Clause license.");
|
||||
main_layout.add_widget(license);
|
||||
|
||||
String os_release_text = TRY(String::format("OS release: %s"_sv, info.release));
|
||||
ui::Label os_release(os_release_text.view());
|
||||
version_info.add_widget(os_release);
|
||||
|
||||
String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version));
|
||||
ui::Label kernel_version(kernel_version_text.view());
|
||||
version_info.add_widget(kernel_version);
|
||||
|
||||
return app.run();
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* @file Button.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief A clickable component that triggers an action when pressed.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <luna/Action.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class Button : public Widget
|
||||
{
|
||||
public:
|
||||
Button(Rect rect);
|
||||
|
||||
void set_widget(Widget& widget);
|
||||
void set_action(Action&& action);
|
||||
|
||||
Result<EventResult> handle_mouse_move(Point position) override;
|
||||
Result<EventResult> handle_mouse_leave() override;
|
||||
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
Result<void> draw(Canvas& canvas) override;
|
||||
|
||||
private:
|
||||
bool m_hovered { false };
|
||||
bool m_clicked { false };
|
||||
Widget* m_child;
|
||||
Action m_action;
|
||||
};
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* @file Container.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief A container widget to pad and align objects inside it.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <ui/Alignment.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class Container : public Widget
|
||||
{
|
||||
public:
|
||||
Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign);
|
||||
|
||||
void set_widget(Widget& widget);
|
||||
|
||||
Result<EventResult> handle_mouse_move(Point position) override;
|
||||
Result<EventResult> handle_mouse_leave() override;
|
||||
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
Result<void> draw(Canvas& canvas) override;
|
||||
|
||||
private:
|
||||
Widget* m_widget;
|
||||
VerticalAlignment m_valign;
|
||||
HorizontalAlignment m_halign;
|
||||
};
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* @file Window.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI window dialogs.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <luna/Action.h>
|
||||
#include <ui/Window.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
namespace Dialog
|
||||
{
|
||||
Result<void> show_message(StringView title, StringView message);
|
||||
|
||||
Result<void> show_input_dialog(StringView title, StringView message, Function<StringView> callback);
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/**
|
||||
* @file Layout.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Layout widgets to organize content.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <luna/Vector.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
enum class AdjustHeight
|
||||
{
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
|
||||
enum class AdjustWidth
|
||||
{
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
|
||||
struct Margins
|
||||
{
|
||||
int left;
|
||||
int right;
|
||||
int top;
|
||||
int bottom;
|
||||
};
|
||||
|
||||
class HorizontalLayout final : public Widget
|
||||
{
|
||||
public:
|
||||
HorizontalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes,
|
||||
AdjustWidth adjust_width = AdjustWidth::Yes);
|
||||
|
||||
Result<EventResult> handle_mouse_move(Point position) override;
|
||||
Result<EventResult> handle_mouse_leave() override;
|
||||
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(Canvas& canvas) override;
|
||||
|
||||
Result<void> add_widget(Widget& widget);
|
||||
|
||||
private:
|
||||
Vector<Widget*> m_widgets;
|
||||
Margins m_margins;
|
||||
AdjustHeight m_adjust_height;
|
||||
AdjustWidth m_adjust_width;
|
||||
int m_used_width { 0 };
|
||||
};
|
||||
|
||||
class VerticalLayout final : public Widget
|
||||
{
|
||||
public:
|
||||
VerticalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes,
|
||||
AdjustWidth adjust_width = AdjustWidth::Yes);
|
||||
|
||||
Result<EventResult> handle_mouse_move(Point position) override;
|
||||
Result<EventResult> handle_mouse_leave() override;
|
||||
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(Canvas& canvas) override;
|
||||
|
||||
Result<void> add_widget(Widget& widget);
|
||||
|
||||
private:
|
||||
Vector<Widget*> m_widgets;
|
||||
Margins m_margins;
|
||||
AdjustHeight m_adjust_height;
|
||||
AdjustWidth m_adjust_width;
|
||||
int m_used_height { 0 };
|
||||
};
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* @file Dialog.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI window dialogs.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/Alloc.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Dialog.h>
|
||||
#include <ui/InputField.h>
|
||||
#include <ui/Label.h>
|
||||
#include <ui/Layout.h>
|
||||
|
||||
namespace ui::Dialog
|
||||
{
|
||||
Result<void> show_message(StringView title, StringView message)
|
||||
{
|
||||
auto rect = ui::App::the().main_window()->canvas().rect();
|
||||
int text_length = (int)message.length() * ui::Font::default_font()->width();
|
||||
int text_height = ui::Font::default_font()->height();
|
||||
|
||||
ui::Rect dialog_rect = { 0, 0, text_length + 20, text_height + 20 };
|
||||
|
||||
auto* dialog = TRY(ui::Window::create(
|
||||
ui::align(rect, dialog_rect, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)));
|
||||
|
||||
dialog->set_background(ui::GRAY);
|
||||
dialog->set_title(title);
|
||||
|
||||
ui::Label* text = TRY(make<ui::Label>(message));
|
||||
text->set_color(ui::BLACK);
|
||||
dialog->set_main_widget(*text);
|
||||
|
||||
dialog->on_close([text] { delete text; });
|
||||
|
||||
dialog->draw();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> show_input_dialog(StringView title, StringView message, Function<StringView> callback)
|
||||
{
|
||||
auto rect = ui::App::the().main_window()->canvas().rect();
|
||||
int text_length = (int)message.length() * ui::Font::default_font()->width();
|
||||
int text_height = ui::Font::default_font()->height();
|
||||
|
||||
ui::Rect dialog_rect = { 0, 0, max(text_length + 20, 300), text_height * 2 + 30 };
|
||||
|
||||
auto* dialog = TRY(ui::Window::create(
|
||||
ui::align(rect, dialog_rect, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)));
|
||||
|
||||
dialog->set_background(ui::GRAY);
|
||||
dialog->set_title(title);
|
||||
|
||||
ui::VerticalLayout* layout = TRY(make<ui::VerticalLayout>());
|
||||
dialog->set_main_widget(*layout);
|
||||
|
||||
ui::Label* text = TRY(make<ui::Label>((message)));
|
||||
text->set_color(ui::BLACK);
|
||||
layout->add_widget(*text);
|
||||
|
||||
ui::InputField* input = TRY(make<ui::InputField>(ui::Font::default_font()));
|
||||
input->on_submit([dialog, callback](StringView s) {
|
||||
callback(s);
|
||||
dialog->close();
|
||||
});
|
||||
layout->add_widget(*input);
|
||||
|
||||
dialog->on_close([layout, text, input] {
|
||||
delete text;
|
||||
delete input;
|
||||
delete layout;
|
||||
});
|
||||
|
||||
dialog->draw();
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
186
gui/loginui.cpp
186
gui/loginui.cpp
@ -1,186 +0,0 @@
|
||||
/**
|
||||
* @file loginui.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Graphical login prompt.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/RefString.h>
|
||||
#include <luna/SHA.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/Config.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/InputField.h>
|
||||
#include <ui/Label.h>
|
||||
#include <ui/Layout.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum Stage
|
||||
{
|
||||
UsernameInput,
|
||||
PasswordInput,
|
||||
};
|
||||
|
||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||
|
||||
Result<String> hash_password(StringView& view)
|
||||
{
|
||||
SHA256 sha;
|
||||
sha.append((const u8*)view.chars(), view.length());
|
||||
auto digest = TRY(sha.digest());
|
||||
return digest.to_string();
|
||||
}
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Login prompt for a graphical UI session.");
|
||||
parser.add_system_program_info("loginui"_sv);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TRY(os::Security::pledge("stdio rpath wpath unix proc exec id", nullptr));
|
||||
|
||||
setsid();
|
||||
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
StringView wind_command[] = { "/usr/bin/wind" };
|
||||
os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1));
|
||||
},
|
||||
1000);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("loginui: failed to start wind, timed out");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto config = TRY(os::ConfigFile::open("/etc/loginui.conf"));
|
||||
|
||||
if (config->read_boolean_or("Autologin", false))
|
||||
{
|
||||
StringView username = config->read_string_or("AutologinUser", "");
|
||||
|
||||
if (!username.is_empty())
|
||||
{
|
||||
auto flag = RefString::format("--user=%s"_sv, username.chars()).release_value();
|
||||
|
||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_title("Log in");
|
||||
window->set_background(BACKGROUND_COLOR);
|
||||
|
||||
ui::VerticalLayout main_layout;
|
||||
window->set_main_widget(main_layout);
|
||||
|
||||
ui::Label label("Username:");
|
||||
main_layout.add_widget(label);
|
||||
|
||||
ui::InputField input(ui::Font::default_font());
|
||||
main_layout.add_widget(input);
|
||||
|
||||
ui::Label error("");
|
||||
error.set_font(ui::Font::default_bold_font());
|
||||
error.set_color(ui::RED);
|
||||
main_layout.add_widget(error);
|
||||
|
||||
Stage stage = Stage::UsernameInput;
|
||||
struct passwd* pw;
|
||||
|
||||
input.on_submit([&](StringView data) {
|
||||
error.set_text("");
|
||||
if (stage == Stage::UsernameInput)
|
||||
{
|
||||
struct passwd* entry = getpwnam(data.chars());
|
||||
if (!entry)
|
||||
{
|
||||
error.set_text("User not found.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
pw = entry;
|
||||
stage = Stage::PasswordInput;
|
||||
label.set_text("Password:");
|
||||
|
||||
RefString title = RefString::format("Log in: %s"_sv, data.chars()).release_value();
|
||||
window->set_title(title.view());
|
||||
|
||||
input.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* passwd = pw->pw_passwd;
|
||||
|
||||
// If the user's password entry is 'x', read their password from the shadow file instead.
|
||||
if (!strcmp(pw->pw_passwd, "x"))
|
||||
{
|
||||
struct spwd* sp = getspnam(pw->pw_name);
|
||||
|
||||
if (!sp)
|
||||
{
|
||||
error.set_text("User not found in shadow file.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
endspent();
|
||||
|
||||
passwd = sp->sp_pwdp;
|
||||
}
|
||||
|
||||
if (!strcmp(passwd, "!"))
|
||||
{
|
||||
error.set_text("User's password is disabled.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = hash_password(data).release_value();
|
||||
|
||||
if (strcmp(result.chars(), passwd))
|
||||
{
|
||||
error.set_text("Incorrect password.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto flag = RefString::format("--user=%s"_sv, pw->pw_name).release_value();
|
||||
|
||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||
unreachable();
|
||||
}
|
||||
});
|
||||
|
||||
return app.run();
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include "Client.h"
|
||||
#include <os/File.h>
|
||||
|
||||
Client::Client(OwnedPtr<os::IPC::ClientConnection>&& client, i16 _pledges)
|
||||
: conn(move(client)), windows(), pledges(_pledges)
|
||||
{
|
||||
conn->set_message_handler(wind::handle_ipc_message, this);
|
||||
}
|
||||
|
||||
bool Client::update_pledges(i16 _pledges)
|
||||
{
|
||||
if (_pledges < 0)
|
||||
{
|
||||
os::eprintln("wind: Client trying to set an invalid pledge, disconnecting!");
|
||||
should_be_disconnected = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pledges < 0)
|
||||
{
|
||||
pledges = _pledges;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_pledges & ~pledges)
|
||||
{
|
||||
os::eprintln("wind: Client trying to add pledges, disconnecting!");
|
||||
should_be_disconnected = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
pledges = _pledges;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::check_pledge(i16 pledge)
|
||||
{
|
||||
check(pledge > 0);
|
||||
|
||||
if (pledges < 0)
|
||||
{
|
||||
os::eprintln("wind: Client trying to use pledge-protected functions before pledging anything, disconnecting!");
|
||||
should_be_disconnected = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((pledges & pledge) == pledge) return true;
|
||||
|
||||
os::eprintln("wind: Client trying to use a function they haven't pledged, disconnecting!");
|
||||
should_be_disconnected = true;
|
||||
return false;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include "IPC.h"
|
||||
#include "Window.h"
|
||||
#include <os/IPC.h>
|
||||
|
||||
constexpr i16 HAS_NOT_YET_PLEDGED = -1;
|
||||
constexpr i16 EMPTY_PLEDGE = 0;
|
||||
|
||||
struct Client
|
||||
{
|
||||
OwnedPtr<os::IPC::ClientConnection> conn;
|
||||
Vector<Window*> windows;
|
||||
const bool privileged { false };
|
||||
bool should_be_disconnected { false };
|
||||
i16 pledges = 0;
|
||||
|
||||
bool update_pledges(i16 pledges);
|
||||
bool check_pledge(i16 pledge);
|
||||
|
||||
Client(OwnedPtr<os::IPC::ClientConnection>&& client, i16 pledges);
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
#include "Layer.h"
|
||||
#include "Client.h"
|
||||
#include "Window.h"
|
||||
|
||||
Layer l_background;
|
||||
Layer l_global;
|
||||
Layer l_global_top;
|
||||
Layer l_system;
|
||||
Layer l_lock;
|
||||
|
||||
constexpr int NUM_LAYERS = 5;
|
||||
|
||||
static Layer* const layers_front_to_back[NUM_LAYERS] = { &l_lock, &l_system, &l_global_top, &l_global, &l_background };
|
||||
static Layer* const layers_back_to_front[NUM_LAYERS] = { &l_background, &l_global, &l_global_top, &l_system, &l_lock };
|
||||
|
||||
Window* Layer::focused_window()
|
||||
{
|
||||
for (int i = 0; i < NUM_LAYERS; i++)
|
||||
{
|
||||
Layer* l = layers_front_to_back[i];
|
||||
if (l->windows.last().has_value()) return l->windows.last().value();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Layer::draw_all_windows(ui::Canvas& canvas)
|
||||
{
|
||||
for (int i = 0; i < NUM_LAYERS; i++)
|
||||
{
|
||||
Layer* l = layers_back_to_front[i];
|
||||
for (Window* w : l->windows) { w->draw(canvas); }
|
||||
}
|
||||
}
|
||||
|
||||
Window* Layer::propagate_mouse_event(ui::Point position, u8 buttons)
|
||||
{
|
||||
for (int i = 0; i < NUM_LAYERS; i++)
|
||||
{
|
||||
Layer* l = layers_front_to_back[i];
|
||||
for (Window* window = l->windows.last().value_or(nullptr); window;
|
||||
window = l->windows.previous(window).value_or(nullptr))
|
||||
{
|
||||
if (window->surface.contains(position))
|
||||
{
|
||||
ui::MouseEventRequest request;
|
||||
request.window = window->id;
|
||||
request.position = window->surface.relative(position);
|
||||
request.buttons = buttons;
|
||||
window->client->conn->send_async(request);
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Window* Layer::propagate_drag_event(ui::Point position)
|
||||
{
|
||||
for (int i = 0; i < NUM_LAYERS; i++)
|
||||
{
|
||||
Layer* l = layers_front_to_back[i];
|
||||
for (Window* window = l->windows.last().value_or(nullptr); window;
|
||||
window = l->windows.previous(window).value_or(nullptr))
|
||||
{
|
||||
if (window->surface.contains(position))
|
||||
{
|
||||
window->focus();
|
||||
|
||||
if (window->surface.absolute(window->titlebar).contains(position)) return window;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include "Window.h"
|
||||
#include <luna/LinkedList.h>
|
||||
#include <ui/Canvas.h>
|
||||
|
||||
struct Layer
|
||||
{
|
||||
LinkedList<Window> windows;
|
||||
|
||||
static Window* focused_window();
|
||||
static void draw_all_windows(ui::Canvas& canvas);
|
||||
static Window* propagate_mouse_event(ui::Point position, u8 buttons);
|
||||
static Window* propagate_drag_event(ui::Point position);
|
||||
};
|
||||
|
||||
extern Layer l_background;
|
||||
extern Layer l_global;
|
||||
extern Layer l_global_top;
|
||||
extern Layer l_system;
|
||||
extern Layer l_lock;
|
@ -19,6 +19,3 @@
|
||||
# Uncomment the line below to make the kernel also calculate stack traces for userspace addresses on program crashes.
|
||||
# This can aid in debugging, but makes the kernel more unstable as stack tracing will access arbitrary userspace memory.
|
||||
# target_compile_definitions(moon PRIVATE MOON_ENABLE_USERSPACE_STACK_TRACES)
|
||||
|
||||
# Uncomment the line below to enable all kernel debug messages, and console logging.
|
||||
# include(debug.cmake)
|
||||
|
@ -12,4 +12,4 @@ target_compile_definitions(moon PRIVATE DEVICE_REGISTRY_DEBUG)
|
||||
target_compile_definitions(moon PRIVATE FORK_DEBUG)
|
||||
target_compile_definitions(moon PRIVATE MOUNT_DEBUG)
|
||||
target_compile_definitions(moon PRIVATE CACHE_DEBUG)
|
||||
#target_compile_options(moon PRIVATE -fsanitize=undefined)
|
||||
target_compile_options(moon PRIVATE -fsanitize=undefined)
|
||||
|
@ -77,7 +77,8 @@ static void log_text_console(LogLevel level, const char* format, va_list origin)
|
||||
TextConsole::set_foreground(WHITE);
|
||||
|
||||
// NOTE: Same as above.
|
||||
auto rc = cstyle_format(format, [](char c, void*) -> Result<void> { return TextConsole::putchar(c); }, nullptr, ap);
|
||||
auto rc = cstyle_format(
|
||||
format, [](char c, void*) -> Result<void> { return TextConsole::putchar(c); }, nullptr, ap);
|
||||
|
||||
if (rc.has_error()) { TextConsole::wprint(L"Invalid UTF-8 in log message"); }
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "Pledge.h"
|
||||
#include "Log.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
static const char* promise_names[] = {
|
||||
#define __enumerate(promise) #promise,
|
||||
@ -9,34 +8,30 @@ static const char* promise_names[] = {
|
||||
#undef __enumerate
|
||||
};
|
||||
|
||||
Result<void> check_pledge(Process* process, Promise promise)
|
||||
Result<void> check_pledge(Thread* thread, Promise promise)
|
||||
{
|
||||
// Thread has not called pledge().
|
||||
if (process->promises < 0) return {};
|
||||
if (thread->promises < 0) return {};
|
||||
int mask = (1 << (int)promise);
|
||||
if ((process->promises & mask) != mask)
|
||||
if ((thread->promises & mask) != mask)
|
||||
{
|
||||
kerrorln("Pledge violation in process %d! Has not pledged %s", process->id, promise_names[(int)promise]);
|
||||
if (process->promises & (1 << (int)Promise::p_error)) return err(ENOSYS);
|
||||
kerrorln("Pledge violation in thread %d! Has not pledged %s", thread->id, promise_names[(int)promise]);
|
||||
if (thread->promises & (1 << (int)Promise::p_error)) return err(ENOSYS);
|
||||
|
||||
Scheduler::for_each_thread(process, [](Thread* thread) {
|
||||
// Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the
|
||||
// default (dump core). We could just kill the thread here and be done, but that discards anything on the
|
||||
// current stack, which means that some destructors might not be called. Instead, leave the job to the next
|
||||
// call of Thread::process_pending_signals().
|
||||
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
||||
// Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the default
|
||||
// (dump core). We could just kill the thread here and be done, but that discards anything on the current stack,
|
||||
// which means that some destructors might not be called. Instead, leave the job to the next call of
|
||||
// Thread::process_pending_signals().
|
||||
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
||||
|
||||
// Unblock SIGABRT.
|
||||
thread->signal_mask.set(SIGABRT - 1, false);
|
||||
// Unblock SIGABRT.
|
||||
thread->signal_mask.set(SIGABRT - 1, false);
|
||||
|
||||
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting
|
||||
// the thread's pending signals.
|
||||
thread->pending_signals.clear();
|
||||
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the
|
||||
// thread's pending signals.
|
||||
thread->pending_signals.clear();
|
||||
|
||||
thread->send_signal(SIGABRT);
|
||||
|
||||
return true;
|
||||
});
|
||||
thread->send_signal(SIGABRT);
|
||||
|
||||
// This should never arrive to userspace, unless we're init and have ignored SIGABRT.
|
||||
return err(ENOSYS);
|
||||
|
@ -14,6 +14,6 @@ enum class Promise
|
||||
num_promises,
|
||||
};
|
||||
|
||||
Result<void> check_pledge(Process* process, Promise promise);
|
||||
Result<void> check_pledge(Thread* thread, Promise promise);
|
||||
|
||||
Result<int> parse_promises(u64 pledge);
|
||||
|
@ -91,9 +91,6 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
||||
auto* current = Scheduler::current();
|
||||
if (current->check_stack_on_exception(regs->rsp)) return;
|
||||
|
||||
auto space = current->process->address_space.lock();
|
||||
(*space)->debug_log();
|
||||
|
||||
current->send_signal(signo);
|
||||
current->process_pending_signals(regs);
|
||||
return;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
static inline constexpr u32 make_pci_address(const PCI::Device::Address& address, u32 field)
|
||||
{
|
||||
return 0x80000000 | (address.bus << 16) | (address.slot << 11) | (address.function << 8) | ((field) & 0xFC);
|
||||
return 0x80000000 | (address.bus << 16) | (address.slot << 11) | (address.function << 8) | ((field)&0xFC);
|
||||
}
|
||||
|
||||
namespace PCI
|
||||
|
@ -15,7 +15,7 @@ void Thread::set_ip(u64 ip)
|
||||
regs.rip = ip;
|
||||
}
|
||||
|
||||
u64 Thread::ip() const
|
||||
u64 Thread::ip()
|
||||
{
|
||||
return regs.rip;
|
||||
}
|
||||
@ -25,7 +25,7 @@ void Thread::set_sp(u64 sp)
|
||||
regs.rsp = sp;
|
||||
}
|
||||
|
||||
u64 Thread::sp() const
|
||||
u64 Thread::sp()
|
||||
{
|
||||
return regs.rsp;
|
||||
}
|
||||
@ -71,14 +71,31 @@ void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
||||
}
|
||||
|
||||
// FIXME: Move this function to a common location (also used in ThreadImage)
|
||||
Result<u64> Thread::push_mem_on_stack(const u8* mem, usize size)
|
||||
{
|
||||
return MemoryManager::push_mem_on_stack(mem, size, stack, regs.rsp);
|
||||
if ((regs.rsp - size) < stack.bottom()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_write((void*)(regs.rsp - size), size)) return err(EFAULT);
|
||||
|
||||
regs.rsp -= size;
|
||||
|
||||
memcpy((void*)regs.rsp, mem, size);
|
||||
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
Result<u64> Thread::pop_mem_from_stack(u8* mem, usize size)
|
||||
{
|
||||
return MemoryManager::pop_mem_from_stack(mem, size, stack, regs.rsp);
|
||||
if ((regs.rsp + size) > stack.top()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_read((void*)regs.rsp, size)) return err(EFAULT);
|
||||
|
||||
memcpy(mem, (void*)regs.rsp, size);
|
||||
|
||||
regs.rsp += size;
|
||||
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
bool Thread::deliver_signal(int signo, Registers* current_regs)
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Clock.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <endian.h>
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/Buffer.h>
|
||||
#include <luna/CType.h>
|
||||
@ -518,8 +517,9 @@ namespace ATA
|
||||
|
||||
m_is_lba48 = true;
|
||||
|
||||
u32 last_lba = be32toh(reply.last_lba);
|
||||
u32 sector_size = be32toh(reply.sector_size);
|
||||
// FIXME: This assumes the host machine is little-endian.
|
||||
u32 last_lba = __builtin_bswap32(reply.last_lba);
|
||||
u32 sector_size = __builtin_bswap32(reply.sector_size);
|
||||
|
||||
m_block_count = last_lba + 1;
|
||||
m_block_size = sector_size;
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "fs/devices/BlockDevice.h"
|
||||
#include "fs/devices/DeviceRegistry.h"
|
||||
#include "lib/Mutex.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/Atomic.h>
|
||||
#include <luna/SharedPtr.h>
|
||||
#include <luna/StaticString.h>
|
||||
|
@ -17,7 +17,7 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
|
||||
{
|
||||
u8 buf[256];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
|
||||
usize nread = TRY(m_inode->read(buf, 2, 255));
|
||||
if (!nread) return err(ENOEXEC);
|
||||
for (usize i = 0; i < nread; i++)
|
||||
@ -35,10 +35,11 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
|
||||
if (!m_interpreter_cmdline.size()) return err(ENOEXEC);
|
||||
|
||||
auto& interpreter_path = m_interpreter_cmdline[0];
|
||||
auto* current = Process::current();
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current, current->current_directory, true));
|
||||
if (!VFS::can_execute(interpreter, current)) return err(EACCES);
|
||||
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, ¤t->extra_groups,
|
||||
current->current_directory, true));
|
||||
if (!VFS::can_execute(interpreter, current->auth, ¤t->extra_groups)) return err(EACCES);
|
||||
|
||||
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
||||
u64 entry = TRY(loader->load(space));
|
||||
|
@ -20,7 +20,7 @@ void InitRD::initialize()
|
||||
|
||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
||||
{
|
||||
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, nullptr);
|
||||
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {}, nullptr);
|
||||
if (rc.has_error())
|
||||
{
|
||||
if (rc.error() == EEXIST) return {};
|
||||
@ -37,7 +37,8 @@ Result<void> InitRD::populate_vfs()
|
||||
{
|
||||
if (entry.type == TarStream::EntryType::RegularFile)
|
||||
{
|
||||
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, nullptr));
|
||||
auto file =
|
||||
TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}, nullptr));
|
||||
file->write(entry.data(), 0, entry.size);
|
||||
}
|
||||
else if (entry.type == TarStream::EntryType::Directory)
|
||||
|
@ -8,7 +8,7 @@ Result<void> Pipe::create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& w
|
||||
auto writer = TRY(make_shared<PipeWriter>());
|
||||
auto reader = TRY(make_shared<PipeReader>());
|
||||
|
||||
auto auth = Process::current()->credentials();
|
||||
auto auth = Scheduler::current()->auth;
|
||||
|
||||
pipe->m_writer = writer.ptr();
|
||||
pipe->m_reader = reader.ptr();
|
||||
|
@ -33,7 +33,7 @@ void StorageCache::clear()
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
StorageCache::StorageCache(BlockDevice* device) : m_device(device)
|
||||
StorageCache::StorageCache()
|
||||
{
|
||||
g_storage_caches.append(this);
|
||||
}
|
||||
|
@ -4,15 +4,12 @@
|
||||
#include <luna/HashMap.h>
|
||||
#include <luna/LinkedList.h>
|
||||
|
||||
class BlockDevice;
|
||||
|
||||
class StorageCache : public LinkedListNode<StorageCache>
|
||||
{
|
||||
public:
|
||||
struct CacheEntry
|
||||
{
|
||||
Buffer buffer {};
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
void lock()
|
||||
@ -31,11 +28,10 @@ class StorageCache : public LinkedListNode<StorageCache>
|
||||
|
||||
static void clear_caches();
|
||||
|
||||
StorageCache(BlockDevice* device);
|
||||
StorageCache();
|
||||
~StorageCache();
|
||||
|
||||
private:
|
||||
HashMap<u64, CacheEntry> m_cache_entries;
|
||||
BlockDevice* m_device;
|
||||
Mutex m_mutex;
|
||||
};
|
||||
|
@ -17,8 +17,9 @@ namespace VFS
|
||||
|
||||
static constexpr int MAX_SYMLINKS = 8;
|
||||
|
||||
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Process* process, SharedPtr<Inode> current_inode,
|
||||
bool follow_last_symlink, int& symlinks_followed)
|
||||
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<Inode> current_inode, bool follow_last_symlink,
|
||||
int& symlinks_followed)
|
||||
{
|
||||
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
||||
|
||||
@ -31,7 +32,7 @@ namespace VFS
|
||||
const char* section;
|
||||
while (parser.next().try_set_value(section))
|
||||
{
|
||||
if (!can_execute(current_inode, process)) return err(EACCES);
|
||||
if (!can_execute(current_inode, auth, extra_groups)) return err(EACCES);
|
||||
current_inode = TRY(current_inode->find(section));
|
||||
|
||||
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
|
||||
@ -45,7 +46,8 @@ namespace VFS
|
||||
symlink_root = parent_inode;
|
||||
|
||||
symlinks_followed++;
|
||||
current_inode = TRY(resolve_path_impl(link.chars(), process, symlink_root, true, symlinks_followed));
|
||||
current_inode =
|
||||
TRY(resolve_path_impl(link.chars(), auth, extra_groups, symlink_root, true, symlinks_followed));
|
||||
symlinks_followed--;
|
||||
}
|
||||
|
||||
@ -55,8 +57,8 @@ namespace VFS
|
||||
return current_inode;
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory,
|
||||
bool follow_last_symlink)
|
||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory, bool follow_last_symlink)
|
||||
{
|
||||
SharedPtr<Inode> current_inode;
|
||||
|
||||
@ -66,17 +68,17 @@ namespace VFS
|
||||
|
||||
int symlinks_followed = 0;
|
||||
|
||||
return resolve_path_impl(path, process, current_inode, follow_last_symlink, symlinks_followed);
|
||||
return resolve_path_impl(path, auth, extra_groups, current_inode, follow_last_symlink, symlinks_followed);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Process* process,
|
||||
SharedPtr<Inode> working_directory)
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
||||
|
||||
if (!can_write(parent_inode, process)) return err(EACCES);
|
||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(PathParser::basename(path));
|
||||
|
||||
@ -85,14 +87,14 @@ namespace VFS
|
||||
return parent_inode->create_subdirectory(child_name.chars(), mode);
|
||||
}
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Process* process,
|
||||
SharedPtr<Inode> working_directory)
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
||||
|
||||
if (!can_write(parent_inode, process)) return err(EACCES);
|
||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(PathParser::basename(path));
|
||||
|
||||
@ -133,81 +135,6 @@ namespace VFS
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Check all three permissions even if the UID or GID match.
|
||||
bool can_execute(SharedPtr<Inode> inode, Process* process)
|
||||
{
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
Credentials auth { 0 };
|
||||
if (process) auth = process->credentials();
|
||||
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
|
||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
|
||||
|
||||
if (process)
|
||||
{
|
||||
auto groups = process->extra_groups.lock();
|
||||
for (gid_t group : *groups)
|
||||
{
|
||||
if (metadata.gid == group) return metadata.mode & S_IXGRP;
|
||||
}
|
||||
}
|
||||
|
||||
return metadata.mode & S_IXOTH;
|
||||
}
|
||||
|
||||
// FIXME: Check all three permissions even if the UID or GID match.
|
||||
bool can_write(SharedPtr<Inode> inode, Process* process)
|
||||
{
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
Credentials auth { 0 };
|
||||
if (process) auth = process->credentials();
|
||||
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
|
||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
|
||||
|
||||
if (process)
|
||||
{
|
||||
auto groups = process->extra_groups.lock();
|
||||
for (gid_t group : *groups)
|
||||
{
|
||||
if (metadata.gid == group) return metadata.mode & S_IWGRP;
|
||||
}
|
||||
}
|
||||
|
||||
return metadata.mode & S_IWOTH;
|
||||
}
|
||||
|
||||
// FIXME: Check all three permissions even if the UID or GID match.
|
||||
bool can_read(SharedPtr<Inode> inode, Process* process)
|
||||
{
|
||||
const auto& metadata = inode->metadata();
|
||||
|
||||
Credentials auth { 0 };
|
||||
if (process) auth = process->credentials();
|
||||
|
||||
if (auth.euid == 0) return true;
|
||||
|
||||
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
|
||||
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
|
||||
|
||||
if (process)
|
||||
{
|
||||
auto groups = process->extra_groups.lock();
|
||||
for (gid_t group : *groups)
|
||||
{
|
||||
if (metadata.gid == group) return metadata.mode & S_IRGRP;
|
||||
}
|
||||
}
|
||||
|
||||
return metadata.mode & S_IROTH;
|
||||
}
|
||||
|
||||
// FIXME: Check all three permissions even if the UID or GID match.
|
||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
||||
{
|
||||
@ -305,7 +232,8 @@ namespace VFS
|
||||
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
||||
auto new_root_path = TRY(PathParser::basename(new_root));
|
||||
|
||||
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), nullptr, working_directory));
|
||||
auto new_root_parent_inode =
|
||||
TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, nullptr, working_directory));
|
||||
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
||||
|
||||
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
@ -317,7 +245,7 @@ namespace VFS
|
||||
|
||||
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), nullptr, working_directory));
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, nullptr, working_directory));
|
||||
|
||||
auto inode = TRY(parent_inode->find(child.chars()));
|
||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
@ -337,8 +265,8 @@ namespace VFS
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Process* process,
|
||||
SharedPtr<VFS::Inode> working_directory)
|
||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups, SharedPtr<VFS::Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
auto child = TRY(PathParser::basename(path));
|
||||
@ -347,7 +275,7 @@ namespace VFS
|
||||
kdbgln("vfs: Mounting filesystem on target %s", path);
|
||||
#endif
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
||||
|
||||
auto inode = TRY(parent_inode->find(child.chars()));
|
||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
@ -362,7 +290,8 @@ namespace VFS
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> umount(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory)
|
||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
auto child = TRY(PathParser::basename(path));
|
||||
@ -371,7 +300,7 @@ namespace VFS
|
||||
|
||||
kinfoln("vfs: Unmounting filesystem on target %s", path);
|
||||
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
||||
|
||||
auto inode = TRY(parent_inode->find(child.chars()));
|
||||
if (!inode->is_mountpoint()) return err(EINVAL);
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <luna/StringView.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct Process;
|
||||
struct Credentials;
|
||||
|
||||
namespace VFS
|
||||
@ -320,21 +319,20 @@ namespace VFS
|
||||
virtual ~DeviceInode() = default;
|
||||
};
|
||||
|
||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Process* process,
|
||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory = {},
|
||||
bool follow_last_symlink = true);
|
||||
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Process* process,
|
||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory = {});
|
||||
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Process* process,
|
||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory = {});
|
||||
|
||||
Result<void> validate_filename(StringView name);
|
||||
|
||||
bool can_execute(SharedPtr<Inode> inode, Process* process);
|
||||
bool can_read(SharedPtr<Inode> inode, Process* process);
|
||||
bool can_write(SharedPtr<Inode> inode, Process* process);
|
||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||
@ -348,8 +346,9 @@ namespace VFS
|
||||
|
||||
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
||||
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
|
||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Process* process,
|
||||
SharedPtr<Inode> working_directory = {});
|
||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory = {});
|
||||
|
||||
Result<void> umount(const char* path, Process* process, SharedPtr<Inode> working_directory = {});
|
||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<Inode> working_directory = {});
|
||||
}
|
||||
|
@ -2,8 +2,7 @@
|
||||
#include "arch/MMU.h"
|
||||
#include <luna/Common.h>
|
||||
|
||||
BlockDevice::BlockDevice(u64 block_size, u64 num_blocks)
|
||||
: m_cache(this), m_block_size(block_size), m_num_blocks(num_blocks)
|
||||
BlockDevice::BlockDevice(u64 block_size, u64 num_blocks) : m_cache(), m_block_size(block_size), m_num_blocks(num_blocks)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
#include "memory/SharedMemory.h"
|
||||
#include "video/Framebuffer.h"
|
||||
#include <bits/ioctl-defs.h>
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/CString.h>
|
||||
#include <luna/Alignment.h>
|
||||
|
||||
Result<void> FramebufferDevice::create()
|
||||
{
|
||||
|
@ -31,9 +31,8 @@ Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
|
||||
|
||||
slave->m_master = master.ptr();
|
||||
slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 2);
|
||||
auto credentials = Process::current()->credentials();
|
||||
slave->m_metadata.uid = credentials.euid;
|
||||
slave->m_metadata.gid = credentials.egid;
|
||||
slave->m_metadata.uid = Scheduler::current()->auth.euid;
|
||||
slave->m_metadata.gid = Scheduler::current()->auth.egid;
|
||||
slave->m_metadata.mode = 0620;
|
||||
slave->m_metadata.initialize_times();
|
||||
|
||||
@ -47,7 +46,7 @@ Result<void> MasterPTY::handle_background_process_group(bool can_succeed, int si
|
||||
auto foreground_pgrp = m_foreground_process_group.value();
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
if (current->process->pgid == foreground_pgrp) return {};
|
||||
if (current->pgid == foreground_pgrp) return {};
|
||||
|
||||
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||
{
|
||||
@ -113,8 +112,8 @@ Result<void> MasterPTY::handle_input(u8 key)
|
||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
||||
|
||||
if (m_foreground_process_group.has_value())
|
||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Process* p) {
|
||||
p->send_signal(SIGINT);
|
||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
||||
thread->send_signal(SIGINT);
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -126,8 +125,8 @@ Result<void> MasterPTY::handle_input(u8 key)
|
||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
||||
|
||||
if (m_foreground_process_group.has_value())
|
||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Process* p) {
|
||||
p->send_signal(SIGQUIT);
|
||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
||||
thread->send_signal(SIGQUIT);
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -203,7 +202,7 @@ Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
|
||||
|
||||
Result<u64> MasterPTY::ioctl(int request, void* arg)
|
||||
{
|
||||
auto* current = Process::current();
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_tty));
|
||||
|
||||
switch (request)
|
||||
|
@ -47,7 +47,7 @@ bool SlavePTY::will_block_if_read() const
|
||||
|
||||
Result<u64> SlavePTY::ioctl(int request, void* arg)
|
||||
{
|
||||
auto* current = Process::current();
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_tty));
|
||||
|
||||
if (!m_master) return err(EIO);
|
||||
@ -69,9 +69,9 @@ Result<u64> SlavePTY::ioctl(int request, void* arg)
|
||||
|
||||
bool pgid_exists = false;
|
||||
pid_t sid;
|
||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Process* p) {
|
||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* thread) {
|
||||
pgid_exists = true;
|
||||
sid = p->sid.load(); // should be the same for all threads in the process group
|
||||
sid = thread->sid; // should be the same for all threads in the process group
|
||||
return false;
|
||||
});
|
||||
if (!pgid_exists) return err(EPERM);
|
||||
@ -95,13 +95,13 @@ Result<u64> SlavePTY::ioctl(int request, void* arg)
|
||||
if (this->m_master->m_session.has_value()) return err(EPERM);
|
||||
if (!current->is_session_leader()) return err(EPERM);
|
||||
|
||||
Scheduler::for_each_in_session(current->sid, [this](Process* p) {
|
||||
p->controlling_terminal = this;
|
||||
Scheduler::for_each_in_session(current->sid, [this](Thread* thread) {
|
||||
thread->controlling_terminal = this;
|
||||
return true;
|
||||
});
|
||||
|
||||
m_master->m_session = current->sid.load();
|
||||
m_master->m_foreground_process_group = current->pgid.load();
|
||||
m_master->m_session = current->sid;
|
||||
m_master->m_foreground_process_group = current->pgid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ TTYLink::TTYLink()
|
||||
|
||||
Result<SharedPtr<VFS::Inode>> TTYLink::open()
|
||||
{
|
||||
if (!Process::current()->controlling_terminal) return err(ENXIO);
|
||||
if (!Scheduler::current()->controlling_terminal) return err(ENXIO);
|
||||
|
||||
return Process::current()->controlling_terminal;
|
||||
return Scheduler::current()->controlling_terminal;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
void Mutex::lock()
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
const pid_t desired = current->tid;
|
||||
const pid_t desired = current->id;
|
||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
||||
|
||||
while (true)
|
||||
@ -19,7 +19,7 @@ void Mutex::lock()
|
||||
{
|
||||
if (expected == desired)
|
||||
{
|
||||
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->tid);
|
||||
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->id);
|
||||
fail("Mutex deadlock detected");
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ void Mutex::lock()
|
||||
void Mutex::unlock()
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
pid_t expected = current->tid;
|
||||
pid_t expected = current->id;
|
||||
check(expected > 0); // Why the hell would the idle thread be touching a mutex?
|
||||
|
||||
m_spinlock.lock();
|
||||
@ -48,7 +48,7 @@ void Mutex::unlock()
|
||||
if (!m_thread.compare_exchange_strong(expected, 0))
|
||||
{
|
||||
kerrorln("KMutex::unlock() called on a lock already locked by another thread (%d, current is %d)", expected,
|
||||
current->tid);
|
||||
current->id);
|
||||
fail("Mutex unlock by different thread");
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ void Mutex::unlock()
|
||||
bool Mutex::try_lock()
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
const pid_t desired = current->tid;
|
||||
const pid_t desired = current->id;
|
||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
||||
|
||||
// Make sure only one thread is touching the mutex at the same time.
|
||||
@ -83,7 +83,7 @@ bool Mutex::try_lock()
|
||||
{
|
||||
kwarnln("Deadlock avoided! KMutex::try_lock() failed because it was already locked by the same thread "
|
||||
"(%d), this is not supposed to happen",
|
||||
current->tid);
|
||||
current->id);
|
||||
CPU::print_stack_trace();
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
#include <luna/Action.h>
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/CircularQueue.h>
|
||||
#include <luna/Spinlock.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class Thread;
|
||||
|
||||
class Mutex
|
||||
{
|
||||
@ -19,84 +16,6 @@ class Mutex
|
||||
Atomic<pid_t> m_thread;
|
||||
};
|
||||
|
||||
template <typename T> class MutexLocked
|
||||
{
|
||||
struct MutexLockedGuard
|
||||
{
|
||||
MutexLockedGuard(MutexLocked& value_ref) : m_value_ref(&value_ref)
|
||||
{
|
||||
}
|
||||
|
||||
MutexLockedGuard(const MutexLockedGuard& other) = delete;
|
||||
MutexLockedGuard(MutexLockedGuard&& other)
|
||||
{
|
||||
m_value_ref = other.m_value_ref;
|
||||
other.m_value_ref = nullptr;
|
||||
}
|
||||
|
||||
~MutexLockedGuard()
|
||||
{
|
||||
if (m_value_ref) m_value_ref->m_lock.unlock();
|
||||
}
|
||||
|
||||
T& ref()
|
||||
{
|
||||
expect(m_value_ref, "MutexLockedGuard::ref() called on a moved MutexLockedGuard");
|
||||
return m_value_ref->m_value;
|
||||
}
|
||||
|
||||
void set(const T& other)
|
||||
{
|
||||
ref() = other;
|
||||
}
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
return &ref();
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
return ref();
|
||||
}
|
||||
|
||||
private:
|
||||
MutexLocked* m_value_ref;
|
||||
};
|
||||
|
||||
public:
|
||||
MutexLocked() : m_value()
|
||||
{
|
||||
}
|
||||
|
||||
MutexLocked(T value) : m_value(move(value))
|
||||
{
|
||||
}
|
||||
|
||||
MutexLockedGuard lock()
|
||||
{
|
||||
m_lock.lock();
|
||||
return { *this };
|
||||
}
|
||||
|
||||
Option<MutexLockedGuard> try_lock()
|
||||
{
|
||||
if (m_lock.try_lock()) { return { *this }; }
|
||||
return {};
|
||||
}
|
||||
|
||||
void with_lock(Function<T&> callback)
|
||||
{
|
||||
m_lock.lock();
|
||||
callback(m_value);
|
||||
m_lock.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
T m_value;
|
||||
Mutex m_lock;
|
||||
};
|
||||
|
||||
class ScopedMutexLock
|
||||
{
|
||||
public:
|
||||
|
@ -26,11 +26,9 @@ void reap_thread()
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
auto dying_threads = Scheduler::check_for_dying_threads();
|
||||
auto dead_processes = Scheduler::check_for_dead_processes();
|
||||
CPU::enable_interrupts();
|
||||
|
||||
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
||||
dead_processes.consume([](Process* p) { Scheduler::reap_process(p); });
|
||||
|
||||
kernel_wait_for_event();
|
||||
}
|
||||
@ -67,8 +65,8 @@ void oom_thread()
|
||||
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
||||
mark_critical(FSRegistry::init(), "Failed to register initial file systems");
|
||||
|
||||
auto init =
|
||||
mark_critical(VFS::resolve_path("/bin/preinit", nullptr, nullptr), "Can't find init in the initial ramfs!");
|
||||
auto init = mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}, nullptr),
|
||||
"Can't find init in the initial ramfs!");
|
||||
auto init_thread = mark_critical(Scheduler::create_init_process(init, "/bin/preinit"),
|
||||
"Failed to create PID 1 process for init");
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "memory/AddressSpace.h"
|
||||
#include "Log.h"
|
||||
#include "arch/MMU.h"
|
||||
#include "memory/Heap.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
@ -386,13 +385,3 @@ void VMRegion::sync_shared()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddressSpace::debug_log()
|
||||
{
|
||||
m_regions.for_each([this](VMRegion* region) {
|
||||
kdbgln("VMRegion start: %p, end: %p, count: %zu, used: %s, persistent: %s, flags: %d, prot: %d, shmid: %lu, "
|
||||
"offset: %ld",
|
||||
(void*)region->start, (void*)region->end, region->count, region->used ? "true" : "false",
|
||||
region->persistent ? "true" : "false", region->flags, region->prot, region->shmid, region->offset);
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class VMRegion : LinkedListNode<VMRegion>
|
||||
bool persistent { false };
|
||||
int flags { 0 };
|
||||
int prot { 0 };
|
||||
u64 shmid { 0 };
|
||||
u64 shmid;
|
||||
off_t offset { 0 };
|
||||
|
||||
void cleanup_shared();
|
||||
@ -52,8 +52,6 @@ class AddressSpace
|
||||
|
||||
static Result<OwnedPtr<AddressSpace>> try_create();
|
||||
|
||||
void debug_log();
|
||||
|
||||
Result<OwnedPtr<AddressSpace>> clone();
|
||||
|
||||
PageDirectory* page_directory() const
|
||||
|
@ -641,30 +641,4 @@ namespace MemoryManager
|
||||
{
|
||||
return free_mem + used_mem + reserved_mem;
|
||||
}
|
||||
|
||||
Result<u64> push_mem_on_stack(const u8* mem, usize size, const Stack& stack, u64& sp)
|
||||
{
|
||||
if ((sp - size) < stack.bottom()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_write((void*)(sp - size), size)) return err(EFAULT);
|
||||
|
||||
sp -= size;
|
||||
|
||||
memcpy((void*)sp, mem, size);
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
Result<u64> pop_mem_from_stack(u8* mem, usize size, const Stack& stack, u64& sp)
|
||||
{
|
||||
if ((sp + size) > stack.top()) return err(E2BIG);
|
||||
|
||||
if (!MemoryManager::validate_user_read((void*)sp, size)) return err(EFAULT);
|
||||
|
||||
memcpy(mem, (void*)sp, size);
|
||||
|
||||
sp += size;
|
||||
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "arch/MMU.h"
|
||||
#include <luna/Result.h>
|
||||
#include <luna/Stack.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/Types.h>
|
||||
|
||||
@ -89,7 +88,4 @@ namespace MemoryManager
|
||||
usize used();
|
||||
usize reserved();
|
||||
usize total();
|
||||
|
||||
Result<u64> push_mem_on_stack(const u8* mem, usize size, const Stack& stack, u64& sp);
|
||||
Result<u64> pop_mem_from_stack(u8* mem, usize size, const Stack& stack, u64& sp);
|
||||
}
|
||||
|
@ -56,14 +56,14 @@ Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
|
||||
return m_data.dequeue_data(buf, length);
|
||||
}
|
||||
|
||||
static Result<void> bind_socket_to_fs(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory,
|
||||
SharedPtr<UnixSocket> socket)
|
||||
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
||||
SharedPtr<VFS::Inode> working_directory, SharedPtr<UnixSocket> socket)
|
||||
{
|
||||
auto parent_path = TRY(PathParser::dirname(path));
|
||||
|
||||
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), process, working_directory));
|
||||
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
||||
|
||||
if (!VFS::can_write(parent_inode, process)) return err(EACCES);
|
||||
if (!VFS::can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
||||
|
||||
auto child_name = TRY(PathParser::basename(path));
|
||||
|
||||
@ -89,14 +89,14 @@ Result<void> UnixSocket::bind(struct sockaddr* addr, socklen_t addrlen)
|
||||
String path = TRY(String::from_string_view(
|
||||
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
||||
|
||||
auto* current = Process::current();
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
m_metadata.mode = 0777 & ~current->umask;
|
||||
auto auth = current->credentials();
|
||||
m_metadata.uid = auth.euid;
|
||||
m_metadata.gid = auth.egid;
|
||||
m_metadata.uid = current->auth.euid;
|
||||
m_metadata.gid = current->auth.egid;
|
||||
|
||||
auto rc = bind_socket_to_fs(path.chars(), current, current->current_directory, SharedPtr<Socket> { this });
|
||||
auto rc = bind_socket_to_fs(path.chars(), current->auth, ¤t->extra_groups, current->current_directory,
|
||||
SharedPtr<Socket> { this });
|
||||
if (rc.has_error())
|
||||
{
|
||||
if (rc.error() == EEXIST) return err(EADDRINUSE);
|
||||
@ -126,13 +126,13 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
||||
String path = TRY(String::from_string_view(
|
||||
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
||||
|
||||
auto* current = Process::current();
|
||||
auto* thread = Scheduler::current();
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||
auto inode =
|
||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
||||
if (inode->type() != VFS::InodeType::Socket)
|
||||
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
||||
if (!VFS::can_write(inode, current)) return err(EACCES);
|
||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
||||
|
||||
auto socket = (SharedPtr<UnixSocket>)inode;
|
||||
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
||||
@ -144,14 +144,14 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
||||
|
||||
while (1)
|
||||
{
|
||||
m_blocked_thread = thread;
|
||||
m_blocked_thread = current;
|
||||
kernel_wait_for_event();
|
||||
m_blocked_thread = nullptr;
|
||||
if (thread->interrupted)
|
||||
if (current->interrupted)
|
||||
{
|
||||
if (thread->will_ignore_pending_signal())
|
||||
if (current->will_ignore_pending_signal())
|
||||
{
|
||||
thread->process_pending_signals(regs);
|
||||
current->process_pending_signals(regs);
|
||||
continue;
|
||||
}
|
||||
return err(EINTR);
|
||||
|
@ -8,16 +8,16 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
||||
{
|
||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||
|
||||
Process* current = Process::current();
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
TRY(check_pledge(current, Promise::p_rpath));
|
||||
|
||||
if (PathParser::is_absolute(path.view()))
|
||||
{
|
||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current));
|
||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups));
|
||||
|
||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
||||
|
||||
inode->add_handle();
|
||||
if (current->current_directory) current->current_directory->remove_handle();
|
||||
@ -29,10 +29,11 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
||||
}
|
||||
else
|
||||
{
|
||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||
SharedPtr<VFS::Inode> inode =
|
||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
||||
|
||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
||||
|
||||
auto old_wdir = current->current_directory_path.view();
|
||||
|
||||
@ -53,7 +54,7 @@ Result<u64> sys_getcwd(Registers*, SyscallArgs args)
|
||||
u8* buf = (u8*)args[0];
|
||||
usize size = (usize)args[1];
|
||||
|
||||
Process* current = Process::current();
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
StringView cwd = current->current_directory_path.view();
|
||||
if (cwd.is_empty()) cwd = "/"_sv;
|
||||
|
@ -11,7 +11,7 @@ Result<u64> sys_clock_gettime(Registers*, SyscallArgs args)
|
||||
clockid_t id = (clockid_t)args[0];
|
||||
struct timespec* ts = (struct timespec*)args[1];
|
||||
|
||||
auto* current = Process::current();
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
TRY(check_pledge(current, Promise::p_stdio));
|
||||
|
||||
|
@ -64,14 +64,14 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
|
||||
return err(E2BIG);
|
||||
|
||||
auto current = Process::current();
|
||||
auto thread = Scheduler::current();
|
||||
auto current = Scheduler::current();
|
||||
|
||||
TRY(check_pledge(current, Promise::p_exec));
|
||||
|
||||
auto inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||
auto inode =
|
||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
||||
|
||||
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
||||
|
||||
#ifdef EXEC_DEBUG
|
||||
kdbgln("exec: attempting to replace current image with %s", path.chars());
|
||||
@ -88,7 +88,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
kdbgln("exec: created loader for binary format %s", loader->format().chars());
|
||||
#endif
|
||||
|
||||
auto guard = make_scope_guard([thread] { MMU::switch_page_directory(thread->self_directory()); });
|
||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
||||
|
||||
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
||||
|
||||
@ -108,15 +108,6 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
|
||||
guard.deactivate();
|
||||
|
||||
// Terminate all other threads.
|
||||
Scheduler::for_each_thread(current, [thread](Thread* t) {
|
||||
if (t != thread) t->quit();
|
||||
return true;
|
||||
});
|
||||
Scheduler::signal_reap_thread();
|
||||
|
||||
current->thread_count = 1;
|
||||
|
||||
current->real_timer.disarm();
|
||||
current->virtual_timer.disarm();
|
||||
current->profiling_timer.disarm();
|
||||
@ -129,39 +120,32 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < FD_MAX; i++)
|
||||
{
|
||||
auto table = current->fd_table.lock();
|
||||
for (int i = 0; i < FD_MAX; i++)
|
||||
{
|
||||
auto& descriptor = (*table)[i];
|
||||
if (!descriptor.has_value()) continue;
|
||||
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
||||
}
|
||||
auto& descriptor = current->fd_table[i];
|
||||
if (!descriptor.has_value()) continue;
|
||||
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
||||
}
|
||||
|
||||
{
|
||||
auto auth = current->auth.lock();
|
||||
if (is_setuid) (*auth).euid = (*auth).suid = inode->metadata().uid;
|
||||
if (is_setgid) (*auth).egid = (*auth).sgid = inode->metadata().gid;
|
||||
}
|
||||
if (is_setuid) current->auth.euid = current->auth.suid = inode->metadata().uid;
|
||||
if (is_setgid) current->auth.egid = current->auth.sgid = inode->metadata().gid;
|
||||
|
||||
current->cmdline = cmdline.chars();
|
||||
thread->cmdline = cmdline.chars();
|
||||
|
||||
image->apply(thread);
|
||||
image->apply(current);
|
||||
|
||||
MMU::switch_page_directory(thread->self_directory());
|
||||
MMU::switch_page_directory(current->self_directory());
|
||||
|
||||
thread->set_arguments(user_argc, user_argv, user_envc, user_envp);
|
||||
current->set_arguments(user_argc, user_argv, user_envc, user_envp);
|
||||
|
||||
current->promises = current->execpromises;
|
||||
current->execpromises = -1;
|
||||
|
||||
memcpy(regs, &thread->regs, sizeof(*regs));
|
||||
memcpy(regs, ¤t->regs, sizeof(*regs));
|
||||
|
||||
for (int i = 0; i < NSIG; i++)
|
||||
{
|
||||
thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||
current->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||
}
|
||||
|
||||
current->has_called_exec = true;
|
||||
@ -173,78 +157,57 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
|
||||
Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
{
|
||||
auto current = Process::current();
|
||||
auto current_thread = Scheduler::current();
|
||||
auto current = Scheduler::current();
|
||||
|
||||
TRY(check_pledge(current, Promise::p_proc));
|
||||
|
||||
Vector<gid_t> extra_groups = TRY(current->copy_groups());
|
||||
Credentials auth = current->credentials();
|
||||
auto extra_groups = TRY(current->extra_groups.shallow_copy());
|
||||
|
||||
auto guard = make_scope_guard([current_thread] { MMU::switch_page_directory(current_thread->self_directory()); });
|
||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
||||
|
||||
memcpy(¤t_thread->regs, regs, sizeof(*regs));
|
||||
memcpy(¤t->regs, regs, sizeof(*regs));
|
||||
|
||||
auto current_directory_path = TRY(current->current_directory_path.clone());
|
||||
|
||||
auto image = TRY(ThreadImage::clone_from_thread(current_thread));
|
||||
auto image = TRY(ThreadImage::clone_from_thread(current));
|
||||
|
||||
auto thread = TRY(new_thread());
|
||||
auto process = TRY(make<Process>());
|
||||
|
||||
Option<FileDescriptor> fds[FD_MAX];
|
||||
{
|
||||
auto table = current->fd_table.lock();
|
||||
for (int i = 0; i < FD_MAX; i++) { fds[i] = (*table)[i]; }
|
||||
}
|
||||
|
||||
thread->state = ThreadState::Runnable;
|
||||
thread->is_kernel = false;
|
||||
thread->fp_data.save();
|
||||
thread->cmdline = current_thread->cmdline;
|
||||
thread->process = process;
|
||||
thread->cmdline = current->cmdline;
|
||||
thread->auth = current->auth;
|
||||
thread->current_directory = current->current_directory;
|
||||
thread->current_directory_path = move(current_directory_path);
|
||||
thread->umask = current->umask;
|
||||
thread->parent = current;
|
||||
thread->promises = current->promises;
|
||||
thread->execpromises = current->execpromises;
|
||||
thread->controlling_terminal = current->controlling_terminal;
|
||||
thread->pgid = current->pgid;
|
||||
thread->sid = current->sid;
|
||||
thread->extra_groups = move(extra_groups);
|
||||
|
||||
process->thread_count = 1;
|
||||
process->id = thread->tid;
|
||||
process->current_directory = current->current_directory;
|
||||
process->current_directory_path = move(current_directory_path);
|
||||
process->umask = current->umask;
|
||||
process->parent = current;
|
||||
process->promises = current->promises;
|
||||
process->execpromises = current->execpromises;
|
||||
process->controlling_terminal = current->controlling_terminal;
|
||||
process->pgid = current->pgid;
|
||||
process->sid = current->sid;
|
||||
process->extra_groups = move(extra_groups);
|
||||
process->cmdline = current->cmdline;
|
||||
thread->virtual_clock.set_resolution(1'000'000);
|
||||
thread->profiling_clock.set_resolution(1'000'000);
|
||||
|
||||
process->virtual_clock.set_resolution(1'000'000);
|
||||
process->profiling_clock.set_resolution(1'000'000);
|
||||
|
||||
{
|
||||
auto credentials = process->auth.lock();
|
||||
*credentials = auth;
|
||||
}
|
||||
|
||||
{
|
||||
auto table = process->fd_table.lock();
|
||||
for (int i = 0; i < FD_MAX; i++) { (*table)[i] = fds[i]; }
|
||||
}
|
||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
||||
|
||||
image->apply(thread);
|
||||
|
||||
memcpy(&thread->regs, regs, sizeof(*regs));
|
||||
|
||||
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current_thread->signal_handlers[i];
|
||||
thread->signal_mask = current_thread->signal_mask;
|
||||
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current->signal_handlers[i];
|
||||
thread->signal_mask = current->signal_mask;
|
||||
|
||||
thread->set_return(0);
|
||||
|
||||
Scheduler::add_thread(thread);
|
||||
Scheduler::add_process(process);
|
||||
|
||||
#ifdef FORK_DEBUG
|
||||
kdbgln("fork: thread %d forked into child %d", current->id, process->id);
|
||||
kdbgln("fork: thread %d forked into child %d", current->id, thread->id);
|
||||
#endif
|
||||
|
||||
return process->id;
|
||||
return thread->id;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user