Compare commits
No commits in common. "bbe1eca71119a3c816b0964aba27746234de8e55" and "fd402083d7d65092059002c44e70e22bb9e54e23" have entirely different histories.
bbe1eca711
...
fd402083d7
25
.drone.yml
Normal file
25
.drone.yml
Normal file
@ -0,0 +1,25 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: test
|
||||
|
||||
platform:
|
||||
arch: arm64
|
||||
os: linux
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
image: ubuntu
|
||||
commands:
|
||||
- apt update
|
||||
- apt install build-essential cmake ninja-build wget nasm genext2fs qemu-system git -y
|
||||
- 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
|
||||
- tools/run-tests.sh
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
@ -1,21 +0,0 @@
|
||||
name: Build and test
|
||||
run-name: ${{ gitea.actor }} is testing and running the code
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v3
|
||||
- name: Download dependencies
|
||||
run: |
|
||||
apt update
|
||||
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: Build and run tests
|
||||
run: tools/run-tests.sh
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,7 +10,6 @@ base/usr/*
|
||||
base/usr/share/*
|
||||
!base/usr/share/fonts
|
||||
!base/usr/share/icons
|
||||
!base/usr/share/applications
|
||||
base/etc/skel/LICENSE
|
||||
.fakeroot
|
||||
kernel/config.cmake
|
||||
|
@ -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,11 @@ 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)
|
||||
|
32
README.md
32
README.md
@ -1,18 +1,18 @@
|
||||
# Luna
|
||||
A simple POSIX-based operating system for 64-bit computers, written in C++.
|
||||
A simple POSIX-based operating system for personal computers, written in C++. [![Build Status](https://drone.cloudapio.eu/api/badges/apio/Luna/status.svg)](https://drone.cloudapio.eu/apio/Luna)
|
||||
|
||||
## Another UNIX clone?
|
||||
[Yes, another UNIX clone](https://wiki.osdev.org/User:Sortie/Yes_Another_Unix_Clone).
|
||||
|
||||
## Features
|
||||
- Lightweight 64-bit [kernel](kernel/). Compatible with the x86_64 architecture.
|
||||
- Basic threads/processes, using a simple round-robin [scheduler](kernel/src/thread/).
|
||||
- x86_64-compatible lightweight [kernel](kernel/).
|
||||
- Simple round-robin [scheduler](kernel/src/thread/).
|
||||
- Read-only [ext2](kernel/src/fs/ext2/) filesystem.
|
||||
- Can [load ELF executables](kernel/src/binfmt/ELF.cpp), [shebang scripts](kernel/src/binfmt/Script.cpp) or [arbitrary binary formats](kernel/src/binfmt/BinaryFormat.h) (registered through kernel modules, which are not supported yet =D).
|
||||
- Can [load ELF programs](kernel/src/binfmt/ELF.cpp), [shebang scripts](kernel/src/binfmt/Script.cpp) or [arbitrary binary formats](kernel/src/binfmt/BinaryFormat.h) (registered through kernel modules, which are not supported yet =D).
|
||||
- [C Library](libc/), aiming for POSIX compatibility, with many features such as local domain sockets, signals, and shared memory.
|
||||
- Support for [several third-party programs](ports/), including the [GNU binutils](ports/binutils/PACKAGE) suite of utilities and the [GCC](ports/gcc/PACKAGE) compiler.
|
||||
- Designed to be [portable](kernel/src/arch), so that additional architectures can be added in the future with relatively low effort.
|
||||
- Everything text-related is designed around [UTF-8](libluna/include/luna/Utf8.h).
|
||||
- Support for [several third-party programs](ports/), including the [GNU binutils](ports/binutils/PACKAGE) suite of utilities.
|
||||
- Designed to be [portable](kernel/src/arch), no need to be restricted to x86_64.
|
||||
- Everything is designed around [UTF-8](libluna/include/luna/Utf8.h).
|
||||
- Environment-agnostic [utility library](libluna/), which can be used in both kernel and userspace.
|
||||
- An extensive set of [standard Unix utilities](apps/), from [ls](apps/ls.cpp) to [uname](apps/uname.cpp) to [base64](apps/base64.cpp). Written in modern C++ and very small amounts of code, using Luna's practical [OS library](libos/).
|
||||
- A simple and efficient [windowing system](wind/), providing a lightweight GUI environment (still in development, not many GUI apps exist).
|
||||
@ -34,21 +34,21 @@ Please beware that building GCC and Binutils can take some time, depending on yo
|
||||
|
||||
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`.
|
||||
Additionally, the build process needs some extra dependencies to run: `cmake`, `ninja`, `nasm` 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/init/99-login](base/etc/init/99-login) and change the line that says `Command=/usr/bin/loginui --autologin=selene` to `Command=/usr/bin/loginui`.
|
||||
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` since `run.sh` does it for you.
|
||||
|
||||
## Prebuilt images
|
||||
|
||||
Prebuilt ISO images for every release version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna/releases).
|
||||
Prebuilt ISO images (numbered) for every version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna/releases).
|
||||
|
||||
These images are built manually whenever a new release is created, and thus don't reflect the latest changes on the `main` branch.
|
||||
|
||||
Every hour, this server pulls the latest commits on `main` and builds an hourly ISO image. The ten most recent ones can be found in the [hourly](https://pub.cloudapio.eu/luna/hourly) directory, and [Luna-latest.iso](https://pub.cloudapio.eu/luna/Luna-latest.iso) should always be symlinked to the newest one.
|
||||
|
||||
These images do reflect the latest changes on the `main` branch, but are obviously less stable. Additionally, an hourly image will be skipped if building the latest commit of the project fails.
|
||||
|
||||
## Is there third-party software I can use on Luna?
|
||||
|
||||
|
@ -50,9 +50,9 @@ class GameWidget final : public ui::Widget
|
||||
|
||||
bool should_add_tile = false;
|
||||
|
||||
switch (request.code)
|
||||
switch (request.key)
|
||||
{
|
||||
case moon::K_UpArrow: {
|
||||
case 'w': {
|
||||
bool changed;
|
||||
changed = move_up();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -61,7 +61,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case moon::K_LeftArrow: {
|
||||
case 'a': {
|
||||
bool changed;
|
||||
changed = move_left();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -70,7 +70,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case moon::K_DownArrow: {
|
||||
case 's': {
|
||||
bool changed;
|
||||
changed = move_down();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -79,7 +79,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case moon::K_RightArrow: {
|
||||
case 'd': {
|
||||
bool changed;
|
||||
changed = move_right();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -88,7 +88,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case moon::K_Home: {
|
||||
case 'r': {
|
||||
reset();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
@ -343,12 +343,12 @@ class GameWidget final : public ui::Widget
|
||||
}
|
||||
};
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 400 }));
|
||||
app.set_main_window(window);
|
51
apps/CMakeLists.txt
Normal file
51
apps/CMakeLists.txt
Normal file
@ -0,0 +1,51 @@
|
||||
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)
|
@ -7,10 +7,10 @@
|
||||
|
||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
||||
app.set_main_window(window);
|
@ -18,10 +18,10 @@ void update_time()
|
||||
ui::App::the().main_window()->draw();
|
||||
}
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 500, 400, 100, 50 }));
|
||||
app.set_main_window(window);
|
@ -81,11 +81,6 @@ Result<void> copy_tree(StringView source, StringView destination, bool verbose,
|
||||
{
|
||||
path = TRY(String::from_string_view(destination));
|
||||
if (!os::FileSystem::exists(path.view(), false)) TRY(os::FileSystem::create_directory(path.view(), 0755));
|
||||
else
|
||||
{
|
||||
os::eprintln("cp: cannot overwrite non-directory '%s' with directory '%s'", path.chars(), source.chars());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
auto dir = TRY(os::Directory::open(source));
|
@ -108,10 +108,10 @@ static void update()
|
||||
draw_cells();
|
||||
}
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
g_window = TRY(ui::Window::create(ui::Rect { 200, 200, 600, 400 }));
|
||||
g_window->set_title("Game of Life");
|
@ -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 {};
|
||||
};
|
||||
|
||||
@ -51,8 +48,6 @@ static void do_log(const char* format, ...)
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
if (!g_is_system) fprintf(g_init_log, "(user) ");
|
||||
|
||||
if (g_init_log) vfprintf(g_init_log, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
@ -63,8 +58,6 @@ static void do_errlog(const char* format, ...)
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
fprintf(stderr, "(user) ");
|
||||
|
||||
vfprintf(stderr, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
@ -135,13 +128,9 @@ 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 (rc.has_error())
|
||||
{
|
||||
@ -164,17 +153,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 {};
|
||||
}
|
||||
@ -192,49 +171,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());
|
||||
|
@ -212,15 +212,14 @@ Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
if (colors)
|
||||
{
|
||||
os::println("%s %lu %4s %4s %10lu %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
|
||||
os::println("%s %u %4s %4s %10lu %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
|
||||
formatted_mode, st.st_nlink, owner.chars(), group.chars(), st.st_size,
|
||||
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
}
|
||||
else
|
||||
{
|
||||
os::println("%s %lu %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(),
|
||||
group.chars(), st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ",
|
||||
link.chars());
|
||||
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -228,13 +227,13 @@ Result<int> luna_main(int argc, char** argv)
|
||||
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
|
||||
if (colors)
|
||||
{
|
||||
os::println("%s %lu %4s %4s %6s %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
|
||||
os::println("%s %u %4s %4s %6s %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
|
||||
formatted_mode, st.st_nlink, owner.chars(), group.chars(), size.chars(),
|
||||
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
}
|
||||
else
|
||||
{
|
||||
os::println("%s %lu %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
|
||||
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
os::println("%s", str.chars());
|
||||
os::println("%s"_sv, str.chars());
|
||||
|
||||
return 0;
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
#include <grp.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -149,25 +148,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
|
||||
const char* passwd = entry->pw_passwd;
|
||||
|
||||
// If the user's password entry is 'x', read their password from the shadow file instead.
|
||||
if (!strcmp(entry->pw_passwd, "x"))
|
||||
{
|
||||
struct spwd* sp = getspnam(name.chars());
|
||||
|
||||
if (!sp)
|
||||
{
|
||||
fprintf(stderr, "%s: user %s not found in shadow file!\n", argv[0], name.chars());
|
||||
return 1;
|
||||
}
|
||||
|
||||
endspent();
|
||||
|
||||
passwd = sp->sp_pwdp;
|
||||
}
|
||||
|
||||
if (!strcmp(passwd, "!"))
|
||||
if (!strcmp(entry->pw_passwd, "!"))
|
||||
{
|
||||
fprintf(stderr, "%s: %s's password is disabled!\n", argv[0], entry->pw_name);
|
||||
return 1;
|
||||
@ -176,7 +157,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
char* pass = getpass();
|
||||
if (!pass) return 1;
|
||||
|
||||
if (strcmp(pass, passwd))
|
||||
if (strcmp(pass, entry->pw_passwd))
|
||||
{
|
||||
fprintf(stderr, "%s: wrong password!\n", argv[0]);
|
||||
return 1;
|
70
apps/taskbar.cpp
Normal file
70
apps/taskbar.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include <os/File.h>
|
||||
#include <os/Process.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/Container.h>
|
||||
#include <ui/Image.h>
|
||||
#include <ui/Layout.h>
|
||||
|
||||
static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
|
||||
|
||||
void sigchld_handler(int)
|
||||
{
|
||||
wait(nullptr);
|
||||
}
|
||||
|
||||
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, Slice<StringView> args, StringView icon)
|
||||
{
|
||||
auto* button = new (std::nothrow) ui::Button({ 0, 0, 50, 50 });
|
||||
if (!button) return err(ENOMEM);
|
||||
layout.add_widget(*button);
|
||||
|
||||
auto* container = new (std::nothrow)
|
||||
ui::Container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
|
||||
if (!container) return err(ENOMEM);
|
||||
button->set_widget(*container);
|
||||
button->set_action([=] { os::Process::spawn(args[0], args, false); });
|
||||
|
||||
auto image = TRY(ui::ImageWidget::load(icon));
|
||||
container->set_widget(*image);
|
||||
|
||||
image.leak();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
||||
|
||||
ui::Rect screen = app.screen_rect();
|
||||
|
||||
ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 };
|
||||
|
||||
auto window = TRY(ui::Window::create(bar, ui::WindowType::System));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_background(TASKBAR_COLOR);
|
||||
|
||||
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
||||
window->set_main_widget(layout);
|
||||
|
||||
StringView terminal_command[] = { "/usr/bin/terminal" };
|
||||
TRY(create_widget_group_for_app(layout, { terminal_command, 1 }, "/usr/share/icons/32x32/app-terminal.tga"));
|
||||
|
||||
StringView about_command[] = { "/usr/bin/about" };
|
||||
TRY(create_widget_group_for_app(layout, { about_command, 1 }, "/usr/share/icons/32x32/app-about.tga"));
|
||||
|
||||
StringView gol_command[] = { "/usr/bin/gol" };
|
||||
TRY(create_widget_group_for_app(layout, { gol_command, 1 }, "/usr/share/icons/32x32/app-gol.tga"));
|
||||
|
||||
StringView clock_command[] = { "/usr/bin/clock" };
|
||||
TRY(create_widget_group_for_app(layout, { clock_command, 1 }, "/usr/share/icons/32x32/app-clock.tga"));
|
||||
|
||||
return app.run();
|
||||
}
|
@ -33,7 +33,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
auto cmdline = TRY(String::join(command, " "));
|
||||
|
||||
os::println("%s %ld.%.2lds user %ld.%.2lds system", cmdline.chars(), usage.ru_utime.tv_sec,
|
||||
os::println("%s %d.%.2ds user %d.%.2ds system"_sv, cmdline.chars(), usage.ru_utime.tv_sec,
|
||||
usage.ru_utime.tv_usec / 10000, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 10000);
|
||||
|
||||
return 0;
|
@ -1,5 +1,4 @@
|
||||
root:!:0:
|
||||
users:!:1:selene
|
||||
wind:!:2:selene
|
||||
wsys:!:3:
|
||||
selene:!:1000:
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name=login
|
||||
Description=Start a graphical user session.
|
||||
Command=/usr/bin/loginui --autologin=selene
|
||||
Description=Start the display server.
|
||||
Command=/usr/bin/wind --user=selene
|
||||
StandardOutput=/dev/uart0
|
||||
StandardError=/dev/uart0
|
||||
Restart=true
|
||||
|
@ -1,3 +1,3 @@
|
||||
root:x:0:0:Administrator:/:/usr/bin/sh
|
||||
wind:x:2:2:Window Manager:/:/usr/bin/init
|
||||
selene:x:1000:1000:User:/home/selene:/usr/bin/sh
|
||||
root:toor:0:0:Administrator:/:/usr/bin/sh
|
||||
wind:!:2:2:Window Manager:/:/usr/bin/init
|
||||
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
|
||||
|
@ -1,3 +0,0 @@
|
||||
root:toor:0:0:99999:7:::
|
||||
wind:!:0:0:99999:7:::
|
||||
selene:moon:0:0:99999:7:::
|
@ -2,9 +2,5 @@
|
||||
# Create and populate a volatile home directory.
|
||||
mount -t tmpfs tmpfs /home/selene
|
||||
chown selene:selene /home/selene
|
||||
|
||||
cp /etc/skel/welcome /home/selene/
|
||||
cp /etc/skel/LICENSE /home/selene/
|
||||
|
||||
chown selene:selene /home/selene/welcome
|
||||
chown selene:selene /home/selene/LICENSE
|
||||
|
5
base/etc/user/00-taskbar
Normal file
5
base/etc/user/00-taskbar
Normal file
@ -0,0 +1,5 @@
|
||||
Name=taskbar
|
||||
Description=Start the taskbar.
|
||||
Command=/usr/bin/taskbar
|
||||
WorkingDirectory=/home/selene
|
||||
Restart=true
|
@ -1,3 +1,4 @@
|
||||
Name=terminal
|
||||
Description=Start the terminal.
|
||||
WorkingDirectory=/home/selene
|
||||
Command=/usr/bin/terminal
|
@ -1,3 +0,0 @@
|
||||
Name=terminal
|
||||
Icon=/usr/share/icons/32x32/app-terminal.tga
|
||||
Command=/usr/bin/terminal
|
@ -1,3 +0,0 @@
|
||||
Name=about
|
||||
Icon=/usr/share/icons/32x32/app-about.tga
|
||||
Command=/usr/bin/about
|
@ -1,3 +0,0 @@
|
||||
Name=gol
|
||||
Icon=/usr/share/icons/32x32/app-gol.tga
|
||||
Command=/usr/bin/gol
|
@ -1,3 +0,0 @@
|
||||
Name=clock
|
||||
Icon=/usr/share/icons/32x32/app-clock.tga
|
||||
Command=/usr/bin/clock
|
@ -1,3 +0,0 @@
|
||||
Name=2048
|
||||
Icon=/usr/share/icons/32x32/app-2048.tga
|
||||
Command=/usr/bin/2048
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
@ -1,221 +0,0 @@
|
||||
# The Luna boot process
|
||||
|
||||
## Stage 0: The Bootloader
|
||||
Luna uses the [BOOTBOOT](https://gitlab.com/bztsrc/bootboot) bootloader. _(For more information, read the [bootloader specification](https://gitlab.com/bztsrc/bootboot/-/blob/master/bootboot_spec_1st_ed.pdf).)_
|
||||
|
||||
This bootloader reads the initial ramdisk, which contains the following files:
|
||||
```
|
||||
/sys/config - copy of the configuration file for the bootloader
|
||||
/boot/moon - the kernel itself
|
||||
/bin/preinit - the first user program run in the boot process, before the root filesystem is mounted
|
||||
```
|
||||
|
||||
The bootloader loads the kernel in 64-bit mode into the higher half at address `0xffffffffffe02000`, with an appropriate stack already set up.
|
||||
|
||||
The first 16Gb of memory are identity-mapped at page 0.
|
||||
|
||||
It places a few other things into known addresses:
|
||||
```
|
||||
0xfffffffffc000000 - initial framebuffer
|
||||
0xffffffffffe00000 - bootloader information passed to the kernel
|
||||
0xffffffffffe01000 - kernel command line
|
||||
```
|
||||
|
||||
From here, the kernel takes over.
|
||||
|
||||
## Stage 1: The Kernel
|
||||
_Relevant files: [kernel/src/main.cpp](../kernel/src/main.cpp), [kernel/src/arch/x86_64/CPU.cpp](../kernel/src/arch/x86_64/CPU.cpp#L285)_
|
||||
|
||||
The kernel begins execution in the `_start()` function. This function initializes basic kernel functionality, such as time-keeping, memory management, graphics, and finally threading.
|
||||
|
||||
Once threading is set up and the scheduler is started, the kernel starts up a new kernel thread titled `[kinit]` to finish starting up other subsystems that assume they're running in a thread.
|
||||
|
||||
Before switching to `[kinit]`, `_start` does one more thing, it calls the `CPU::platform_finish_init()` function which is platform-specific. On x86_64, this function does the following things:
|
||||
|
||||
- Creates a new kernel thread: `[x86_64-io]`, which handles keyboard and mouse interrupts asynchronously
|
||||
- Starts receiving external interrupts
|
||||
- Initializes the mouse
|
||||
|
||||
As soon as the scheduler switches to the `[kinit]` thread, it will never return to `_start` (since it has no thread associated to it).
|
||||
|
||||
**IMPORTANT**: Although the `[kinit]` thread is the first thread to be started in the system, it has PID 2, not 1. The reason for this is that PID 1 is reserved for the userspace init process.
|
||||
|
||||
`[kinit]` does the following things, in order:
|
||||
|
||||
- Loads kernel debug symbols from the initial ramdisk
|
||||
- Creates the virtual file system and mounts the initial ramdisk on /
|
||||
- Initializes virtual device files such as `/dev/null` (the internal kernel representation of them, `/dev` is not mounted yet)
|
||||
- Loads `/bin/preinit` from the initial ramdisk as PID 1
|
||||
- Creates two more kernel threads, `[reap]` and `[oom]`
|
||||
- Scans for ATA hard disks and reads their partition tables
|
||||
- Finally, it sets PID 1's state to "Running" so that the scheduler can switch to it, and exits
|
||||
|
||||
### Kernel threads
|
||||
|
||||
`[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 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#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 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 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 "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.
|
||||
|
||||
The OOM thread then goes through all the disk caches and purges them all, hoping to reclaim as much memory as possible.
|
||||
|
||||
### File system and process layout
|
||||
|
||||
After the kernel stage of the boot process, the system looks like this:
|
||||
|
||||
#### File system
|
||||
```
|
||||
/ - initial ramdisk
|
||||
/sys/config - copy of the configuration file for the bootloader
|
||||
/boot/moon - the kernel itself
|
||||
/bin/preinit - the first user program run in the boot process
|
||||
```
|
||||
|
||||
#### Processes
|
||||
```
|
||||
/bin/preinit - PID 1
|
||||
[kinit] - PID 2 (Exited, soon to be reaped)
|
||||
[x86_64-io] - PID 3
|
||||
[reap] - PID 4
|
||||
[oom] - PID 5
|
||||
```
|
||||
|
||||
## Stage 2: preinit
|
||||
_Relevant files: [system/preinit.cpp](../system/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.
|
||||
|
||||
`/bin/preinit`'s job is to set up the file system in a "minimal known good" state for the actual `init` to run.
|
||||
|
||||
The "minimal known good" state includes:
|
||||
|
||||
- The ext2 root partition, which includes all the binaries in /usr
|
||||
- The /dev file system
|
||||
|
||||
`preinit` does the following things, in order:
|
||||
|
||||
- Mounts `/dev` to get access to disk device files
|
||||
- Mounts the root partition (`/dev/cd0p2`) on `/osroot`
|
||||
- Unmounts `/dev`
|
||||
- Uses the `pivot_root` system call to change the root file system to the one that was in `/osroot`, and mounts the old one on `/mnt` (previously `/osroot/mnt`)
|
||||
- Unmounts the initial ramdisk on `/mnt`
|
||||
- Mounts the `/dev` file system again on the new root partition
|
||||
- Executes `/usr/bin/init`
|
||||
|
||||
For now, much of `preinit`'s functionality is hard-coded, but as Luna supports more devices, it will become responsible for loading device drivers, discovering the root partition, and more...
|
||||
|
||||
### File system and process layout
|
||||
|
||||
After the preinit stage of the boot process, the system looks like this:
|
||||
|
||||
#### File system
|
||||
```
|
||||
/ - ext2 root partition
|
||||
/dev - device file system
|
||||
/usr, /etc, /home... - other directories contained in the root partition
|
||||
```
|
||||
|
||||
#### Processes
|
||||
```
|
||||
/usr/bin/init - PID 1
|
||||
[x86_64-io] - PID 3
|
||||
[reap] - PID 4
|
||||
[oom] - PID 5
|
||||
```
|
||||
|
||||
## Stage 3: init
|
||||
_Relevant files: [system/init.cpp](../system/init.cpp#L406)_
|
||||
|
||||
`/usr/bin/init` is the actual init system. It is in charge of starting user-defined services.
|
||||
|
||||
It does the following things:
|
||||
|
||||
- Mounts `/tmp`, `/dev/shm` and `/dev/pts`
|
||||
- Sets the system hostname by reading `/etc/hostname`
|
||||
- Reads configuration files from `/etc/init`
|
||||
- Starts services defined in `/etc/init`
|
||||
- Enters the init loop, waiting for child processes and restarting them if needed
|
||||
|
||||
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/startui`. This service will be restarted if necessary.
|
||||
|
||||
### File system and process layout
|
||||
|
||||
After the init 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
|
||||
```
|
||||
|
||||
_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: startui
|
||||
_Relevant files: [system/startui.cpp](../system/startui.cpp), [gui/wind/main.cpp](../gui/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`. 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 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
|
||||
|
||||
After the startui stage of the boot process, the system is fully started up and 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
|
||||
/usr/bin/launch - PID 15
|
||||
/usr/bin/taskbar - PID 16
|
||||
/usr/bin/init --user - PID 17
|
||||
/usr/bin/terminal - PID 18
|
||||
/bin/sh - PID 19
|
@ -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(launch.cpp launch)
|
||||
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,12 +0,0 @@
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
EditorWidget.h
|
||||
EditorWidget.cpp
|
||||
)
|
||||
|
||||
add_executable(editor ${SOURCES})
|
||||
target_compile_options(editor PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
add_dependencies(editor libc)
|
||||
target_include_directories(editor PRIVATE ${LUNA_BASE}/usr/include ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(editor PRIVATE os ui)
|
||||
install(TARGETS editor DESTINATION ${LUNA_BASE}/usr/bin)
|
@ -1,233 +0,0 @@
|
||||
/**
|
||||
* @file EditorWidget.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Multiline text editing widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorWidget.h"
|
||||
#include <ctype.h>
|
||||
#include <luna/PathParser.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <ui/App.h>
|
||||
|
||||
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
||||
{
|
||||
recalculate_lines();
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::load_file(const os::Path& path)
|
||||
{
|
||||
struct stat st;
|
||||
auto rc = os::FileSystem::stat(path, st, true);
|
||||
|
||||
if (!rc.has_error() && !S_ISREG(st.st_mode))
|
||||
{
|
||||
os::eprintln("editor: not loading %s as it is not a regular file", path.name().chars());
|
||||
return {};
|
||||
}
|
||||
|
||||
os::eprintln("Loading file: %s", path.name().chars());
|
||||
|
||||
auto file = TRY(os::File::open_or_create(path, os::File::ReadOnly));
|
||||
|
||||
m_data = TRY(file->read_all());
|
||||
|
||||
os::eprintln("Read %zu bytes.", m_data.size());
|
||||
|
||||
m_cursor = m_data.size();
|
||||
|
||||
m_path = path;
|
||||
|
||||
auto basename = TRY(PathParser::basename(m_path.name()));
|
||||
|
||||
String title = TRY(String::format("Text Editor - %s"_sv, basename.chars()));
|
||||
window()->set_title(title.view());
|
||||
|
||||
TRY(recalculate_lines());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<ui::EventResult> EditorWidget::handle_key_event(const ui::KeyEventRequest& request)
|
||||
{
|
||||
// Avoid handling "key released" events
|
||||
if (!request.pressed) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (request.code == moon::K_UpArrow)
|
||||
{
|
||||
if (m_cursor_position.y > 0) m_cursor_position.y--;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_index();
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_DownArrow)
|
||||
{
|
||||
if (m_cursor_position.y + 1 < (int)m_lines.size()) m_cursor_position.y++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_index();
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_LeftArrow)
|
||||
{
|
||||
if (m_cursor > 0) m_cursor--;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_position();
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_RightArrow)
|
||||
{
|
||||
if (m_cursor < m_data.size()) m_cursor++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_position();
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_Backspace)
|
||||
{
|
||||
if (m_cursor == 0) return ui::EventResult::DidNotHandle;
|
||||
m_cursor--;
|
||||
|
||||
delete_current_character();
|
||||
|
||||
TRY(recalculate_lines());
|
||||
|
||||
update_cursor();
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.letter != '\n' && iscntrl(request.letter)) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (m_cursor == m_data.size()) TRY(m_data.append_data((const u8*)&request.letter, 1));
|
||||
else
|
||||
TRY(insert_character(request.letter));
|
||||
|
||||
m_cursor++;
|
||||
TRY(recalculate_lines());
|
||||
|
||||
update_cursor();
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::save_file()
|
||||
{
|
||||
if (m_path.is_empty_path())
|
||||
{
|
||||
os::eprintln("editor: no file to save buffer to!");
|
||||
return err(ENOENT);
|
||||
}
|
||||
|
||||
auto file = TRY(os::File::open(m_path, os::File::WriteOnly));
|
||||
return file->write(m_data);
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::draw(ui::Canvas& canvas)
|
||||
{
|
||||
int visible_lines = canvas.height / m_font->height();
|
||||
int visible_columns = canvas.width / m_font->width();
|
||||
|
||||
if ((usize)visible_lines > m_lines.size()) visible_lines = static_cast<int>(m_lines.size());
|
||||
|
||||
for (int i = 0; i < visible_lines; i++)
|
||||
{
|
||||
auto line = m_lines[i];
|
||||
if (line.begin == line.end) continue;
|
||||
|
||||
auto slice = TRY(m_data.slice(line.begin, line.end - line.begin));
|
||||
auto string = TRY(
|
||||
String::from_string_view(StringView::from_fixed_size_cstring((const char*)slice, line.end - line.begin)));
|
||||
|
||||
Utf8StringDecoder decoder(string.chars());
|
||||
wchar_t buf[4096];
|
||||
decoder.decode(buf, sizeof(buf)).release_value();
|
||||
|
||||
int characters_to_render = (int)wcslen(buf);
|
||||
|
||||
for (int j = 0; j < visible_columns && j < characters_to_render; j++)
|
||||
{
|
||||
auto subcanvas =
|
||||
canvas.subcanvas({ j * m_font->width(), i * m_font->height(), m_font->width(), m_font->height() });
|
||||
m_font->render(buf[j], ui::WHITE, subcanvas);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the cursor
|
||||
if (m_cursor_position.x < visible_columns && m_cursor_position.y < visible_lines && m_cursor_activated)
|
||||
{
|
||||
canvas
|
||||
.subcanvas(
|
||||
{ m_cursor_position.x * m_font->width(), m_cursor_position.y * m_font->height(), 1, m_font->height() })
|
||||
.fill(ui::WHITE);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::recalculate_lines()
|
||||
{
|
||||
m_lines.clear();
|
||||
|
||||
Line l;
|
||||
l.begin = 0;
|
||||
for (usize i = 0; i < m_data.size(); i++)
|
||||
{
|
||||
if (m_data.data()[i] == '\n')
|
||||
{
|
||||
l.end = i;
|
||||
TRY(m_lines.try_append(l));
|
||||
l.begin = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
l.end = m_data.size();
|
||||
TRY(m_lines.try_append(l));
|
||||
|
||||
recalculate_cursor_position();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void EditorWidget::recalculate_cursor_position()
|
||||
{
|
||||
if (m_cursor == 0) m_cursor_position = { 0, 0 };
|
||||
|
||||
for (int i = 0; i < (int)m_lines.size(); i++)
|
||||
{
|
||||
auto line = m_lines[i];
|
||||
if (m_cursor >= line.begin && m_cursor <= line.end)
|
||||
{
|
||||
m_cursor_position.x = (int)(m_cursor - line.begin);
|
||||
m_cursor_position.y = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
void EditorWidget::recalculate_cursor_index()
|
||||
{
|
||||
m_cursor = m_lines[m_cursor_position.y].begin + m_cursor_position.x;
|
||||
if (m_cursor > m_lines[m_cursor_position.y].end)
|
||||
{
|
||||
m_cursor = m_lines[m_cursor_position.y].end;
|
||||
recalculate_cursor_position();
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @file EditorWidget.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Multiline text editing widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/String.h>
|
||||
#include <os/Timer.h>
|
||||
#include <ui/Font.h>
|
||||
#include <ui/TextInput.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
class EditorWidget : public ui::TextInput
|
||||
{
|
||||
public:
|
||||
EditorWidget(SharedPtr<ui::Font> font);
|
||||
|
||||
Result<void> load_file(const os::Path& path);
|
||||
|
||||
Result<void> save_file();
|
||||
|
||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override;
|
||||
|
||||
os::Path& path()
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
SharedPtr<ui::Font> m_font;
|
||||
|
||||
struct Line
|
||||
{
|
||||
usize begin;
|
||||
usize end;
|
||||
};
|
||||
Vector<Line> m_lines;
|
||||
|
||||
os::Path m_path { AT_FDCWD };
|
||||
|
||||
Result<void> recalculate_lines();
|
||||
void recalculate_cursor_position();
|
||||
void recalculate_cursor_index();
|
||||
};
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* @file main.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Graphical text editor.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorWidget.h"
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <ui/App.h>
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView path;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("A graphical text editor"_sv);
|
||||
parser.add_system_program_info("editor"_sv);
|
||||
parser.add_positional_argument(path, "path", false);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 200, 300, 600, 600 }));
|
||||
window->set_background(ui::Color::from_rgb(40, 40, 40));
|
||||
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()) 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("editor: failed to save file: %s", result.error_string());
|
||||
else
|
||||
os::println("editor: buffer saved to %s successfully", editor->path().name().chars());
|
||||
}));
|
||||
|
||||
window->draw();
|
||||
|
||||
return app.run();
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
#include <luna/Sort.h>
|
||||
#include <luna/StringBuilder.h>
|
||||
#include <os/Config.h>
|
||||
#include <os/Directory.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/ipc/Launcher.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/Container.h>
|
||||
#include <ui/Image.h>
|
||||
#include <ui/Layout.h>
|
||||
|
||||
static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
|
||||
|
||||
static OwnedPtr<os::IPC::Client> launcher_client;
|
||||
|
||||
void sigchld_handler(int)
|
||||
{
|
||||
wait(nullptr);
|
||||
}
|
||||
|
||||
void sigquit_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)
|
||||
{
|
||||
auto* button = TRY(make<ui::Button>(ui::Rect { 0, 0, 50, 50 }));
|
||||
layout.add_widget(*button);
|
||||
|
||||
auto* container = TRY(
|
||||
make<ui::Container>(ui::Rect { 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center));
|
||||
button->set_widget(*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();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
struct ApplicationFile
|
||||
{
|
||||
String name;
|
||||
String command;
|
||||
String icon;
|
||||
};
|
||||
|
||||
Vector<ApplicationFile> s_app_files;
|
||||
|
||||
// Pretty much copied from init.cpp.
|
||||
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));
|
||||
|
||||
ApplicationFile app_file;
|
||||
|
||||
app_file.name = TRY(String::from_string_view(file->read_string_or("Name", "")));
|
||||
if (app_file.name.is_empty())
|
||||
{
|
||||
os::println("[taskbar] app file is missing 'Name' entry, aborting!");
|
||||
return {};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
os::println("[taskbar] loaded app %s into memory", app_file.name.chars());
|
||||
|
||||
TRY(s_app_files.try_append(move(app_file)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> load_app_files_from_path(StringView path)
|
||||
{
|
||||
os::println("[taskbar] loading app files from %s", path.chars());
|
||||
|
||||
auto dir = TRY(os::Directory::open(path));
|
||||
|
||||
auto services = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
|
||||
sort(services.begin(), services.end(), String::compare);
|
||||
|
||||
for (const auto& entry : services) TRY(load_application_file({ dir->fd(), entry.view() }));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<int> luna_main(int, char**)
|
||||
{
|
||||
ui::App app;
|
||||
TRY(app.init("/tmp/wsys.sock"));
|
||||
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGQUIT, sigquit_handler));
|
||||
|
||||
launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
||||
|
||||
ui::Rect screen = app.screen_rect();
|
||||
|
||||
ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 };
|
||||
|
||||
auto window = TRY(ui::Window::create(bar, ui::WindowType::System));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_background(TASKBAR_COLOR);
|
||||
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);
|
||||
|
||||
load_app_files_from_path("/usr/share/applications/");
|
||||
|
||||
auto home = TRY(os::FileSystem::home_directory());
|
||||
|
||||
StringBuilder sb;
|
||||
TRY(sb.add(home.view()));
|
||||
TRY(sb.add("/.applications/"_sv));
|
||||
auto local_app_file_dir = TRY(sb.string());
|
||||
|
||||
load_app_files_from_path(local_app_file_dir.view());
|
||||
|
||||
for (const auto& app_file : s_app_files)
|
||||
{
|
||||
create_widget_group_for_app(layout, app_file.command.view(), app_file.icon.view());
|
||||
}
|
||||
|
||||
return app.run();
|
||||
}
|
109
gui/launch.cpp
109
gui/launch.cpp
@ -1,109 +0,0 @@
|
||||
/**
|
||||
* @file launch.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Background process that handles detached launching of apps.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <os/ipc/Launcher.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Result<void> handle_launch_detached_message(os::IPC::ClientConnection& client)
|
||||
{
|
||||
os::Launcher::LaunchDetachedRequest request;
|
||||
if (!TRY(client.read_message(request))) return {};
|
||||
|
||||
auto path = COPY_IPC_STRING(request.command);
|
||||
|
||||
StringView args[] = { path.view() };
|
||||
|
||||
os::Process::spawn(args[0], { args, 1 }, request.search_in_path);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
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("launch: Invalid IPC message from client!"); return;
|
||||
}
|
||||
}
|
||||
|
||||
void sigchld_handler(int)
|
||||
{
|
||||
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/launch.sock";
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Background process that handles detached launching of apps."_sv);
|
||||
parser.add_system_program_info("launch"_sv);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
|
||||
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 }));
|
||||
|
||||
TRY(os::Security::pledge("stdio unix proc exec", NULL));
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (auto& pfd : fds) { pfd.revents = 0; }
|
||||
|
||||
int rc = poll(fds.data(), fds.size(), 1000);
|
||||
if (!rc) continue;
|
||||
if (rc < 0 && errno != EINTR) { os::println("poll: error: %s", strerror(errno)); }
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
{
|
||||
auto client = TRY(server->accept());
|
||||
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)));
|
||||
connection->set_message_handler(handle_ipc_message, nullptr);
|
||||
TRY(clients.try_append(move(connection)));
|
||||
}
|
||||
for (usize i = 0; i < clients.size(); i++)
|
||||
{
|
||||
if (fds[i + 1].revents & POLLIN) clients[i]->check_for_messages();
|
||||
if (fds[i + 1].revents & POLLHUP)
|
||||
{
|
||||
os::println("launch: Client %zu disconnected", i);
|
||||
fds.remove_at(i + 1);
|
||||
auto client = clients.remove_at(i);
|
||||
client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @file InputField.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Single line text input widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <os/Action.h>
|
||||
#include <ui/Font.h>
|
||||
#include <ui/TextInput.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class InputField : public ui::TextInput
|
||||
{
|
||||
public:
|
||||
InputField(SharedPtr<ui::Font> font);
|
||||
|
||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override;
|
||||
|
||||
void clear();
|
||||
|
||||
StringView data();
|
||||
|
||||
void on_submit(os::Function<StringView>&& action)
|
||||
{
|
||||
m_on_submit_action = move(action);
|
||||
m_has_on_submit_action = true;
|
||||
}
|
||||
|
||||
private:
|
||||
SharedPtr<ui::Font> m_font;
|
||||
|
||||
os::Function<StringView> m_on_submit_action;
|
||||
bool m_has_on_submit_action { false };
|
||||
};
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @file TextInput.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Base class for text inputs.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <luna/Buffer.h>
|
||||
#include <os/Timer.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class TextInput : public Widget
|
||||
{
|
||||
public:
|
||||
TextInput();
|
||||
|
||||
virtual Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) = 0;
|
||||
|
||||
virtual Result<void> draw(ui::Canvas& canvas) = 0;
|
||||
|
||||
protected:
|
||||
Buffer m_data;
|
||||
|
||||
usize m_cursor { 0 };
|
||||
ui::Point m_cursor_position { 0, 0 };
|
||||
|
||||
OwnedPtr<os::Timer> m_cursor_timer;
|
||||
bool m_cursor_activated = true;
|
||||
|
||||
void tick_cursor();
|
||||
void update_cursor();
|
||||
|
||||
Result<void> delete_current_character();
|
||||
Result<void> insert_character(char c);
|
||||
};
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
/**
|
||||
* @file InputField.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Single line text input widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/StringView.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <ui/InputField.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
InputField::InputField(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
||||
{
|
||||
u8 zero = 0;
|
||||
m_data.append_data(&zero, 1);
|
||||
}
|
||||
|
||||
Result<ui::EventResult> InputField::handle_key_event(const ui::KeyEventRequest& request)
|
||||
{
|
||||
// Avoid handling "key released" events
|
||||
if (!request.pressed) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (request.code == moon::K_LeftArrow)
|
||||
{
|
||||
if (m_cursor > 0) m_cursor--;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_RightArrow)
|
||||
{
|
||||
if (m_cursor < (m_data.size() - 1)) m_cursor++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
update_cursor();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.code == moon::K_Backspace)
|
||||
{
|
||||
if (m_cursor == 0) return ui::EventResult::DidNotHandle;
|
||||
m_cursor--;
|
||||
|
||||
delete_current_character();
|
||||
|
||||
update_cursor();
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.letter == '\n')
|
||||
{
|
||||
if (m_has_on_submit_action)
|
||||
{
|
||||
m_on_submit_action(data());
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
return ui::EventResult::DidNotHandle;
|
||||
}
|
||||
|
||||
if (iscntrl(request.letter)) return ui::EventResult::DidNotHandle;
|
||||
|
||||
TRY(insert_character(request.letter));
|
||||
|
||||
m_cursor++;
|
||||
|
||||
update_cursor();
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
void InputField::clear()
|
||||
{
|
||||
m_data.try_resize(0);
|
||||
u8 zero = 0;
|
||||
m_data.append_data(&zero, 1);
|
||||
m_cursor = 0;
|
||||
}
|
||||
|
||||
Result<void> InputField::draw(ui::Canvas& canvas)
|
||||
{
|
||||
int visible_characters = canvas.width / m_font->width();
|
||||
|
||||
auto string = data();
|
||||
|
||||
Utf8StringDecoder decoder(string.chars());
|
||||
wchar_t buf[4096];
|
||||
decoder.decode(buf, sizeof(buf)).release_value();
|
||||
|
||||
int characters_to_render = (int)wcslen(buf);
|
||||
|
||||
for (int j = 0; j < visible_characters && j < characters_to_render; j++)
|
||||
{
|
||||
auto subcanvas = canvas.subcanvas({ j * m_font->width(), 0, m_font->width(), m_font->height() });
|
||||
m_font->render(buf[j], ui::WHITE, subcanvas);
|
||||
}
|
||||
|
||||
// Draw the cursor
|
||||
if ((int)m_cursor < visible_characters && m_cursor_activated)
|
||||
{
|
||||
canvas.subcanvas({ (int)m_cursor * m_font->width(), 0, 1, m_font->height() }).fill(ui::WHITE);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView InputField::data()
|
||||
{
|
||||
if (!m_data.size()) return StringView {};
|
||||
return StringView { (const char*)m_data.data(), m_data.size() };
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* @file TextInput.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Base class for text inputs.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ui/App.h>
|
||||
#include <ui/TextInput.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
TextInput::TextInput() : Widget()
|
||||
{
|
||||
m_cursor_timer = os::Timer::create_repeating(500, [this]() { this->tick_cursor(); }).release_value();
|
||||
}
|
||||
|
||||
void TextInput::update_cursor()
|
||||
{
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
}
|
||||
|
||||
Result<void> TextInput::delete_current_character()
|
||||
{
|
||||
usize size = m_data.size() - m_cursor;
|
||||
u8* slice = TRY(m_data.slice(m_cursor, size));
|
||||
memmove(slice, slice + 1, size - 1);
|
||||
TRY(m_data.try_resize(m_data.size() - 1));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> TextInput::insert_character(char c)
|
||||
{
|
||||
usize size = m_data.size() - m_cursor;
|
||||
u8* slice = TRY(m_data.slice(m_cursor, size + 1));
|
||||
memmove(slice + 1, slice, size);
|
||||
*slice = (u8)c;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void TextInput::tick_cursor()
|
||||
{
|
||||
m_cursor_activated = !m_cursor_activated;
|
||||
|
||||
window()->draw();
|
||||
}
|
||||
}
|
171
gui/loginui.cpp
171
gui/loginui.cpp
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* @file loginui.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Graphical login prompt.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/String.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.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<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView username;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Login prompt for a graphical UI session.");
|
||||
parser.add_system_program_info("loginui"_sv);
|
||||
// FIXME: Make this a config option instead of a switch.
|
||||
// Also, calling "loginui --autologin=user" is functionally identical to calling "startui --user=user", the only
|
||||
// difference is that it makes the init config easier to change (only adding or removing the autologin flag, instead
|
||||
// of changing the program to use)
|
||||
parser.add_value_argument(username, ' ', "autologin", "login as a specific user without prompting");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!username.is_empty())
|
||||
{
|
||||
auto flag = String::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:");
|
||||
|
||||
String title = String::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;
|
||||
}
|
||||
|
||||
if (strcmp(data.chars(), passwd))
|
||||
{
|
||||
error.set_text("Incorrect password.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto flag = String::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();
|
||||
}
|
35
gui/run.cpp
35
gui/run.cpp
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* @file run.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Tiny command-line utility to start a detached program in the current GUI session.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/LocalClient.h>
|
||||
#include <os/ipc/Launcher.h>
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView program;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Start a detached program in the current GUI session."_sv);
|
||||
parser.add_system_program_info("run"_sv);
|
||||
parser.add_positional_argument(program, "program", true);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
OwnedPtr<os::IPC::Client> launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
||||
|
||||
os::println("Requesting to start program '%s'...", program.chars());
|
||||
|
||||
os::Launcher::LaunchDetachedRequest request;
|
||||
SET_IPC_STRING(request.command, program.chars());
|
||||
request.search_in_path = true;
|
||||
launcher_client->send_async(request);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "IPC.h"
|
||||
#include "Window.h"
|
||||
#include <os/IPC.h>
|
||||
|
||||
struct Client
|
||||
{
|
||||
OwnedPtr<os::IPC::ClientConnection> conn;
|
||||
Vector<Window*> windows;
|
||||
const bool privileged { false };
|
||||
bool should_be_disconnected { false };
|
||||
|
||||
Client(OwnedPtr<os::IPC::ClientConnection>&& client, bool priv)
|
||||
#ifdef CLIENT_IMPLEMENTATION
|
||||
: conn(move(client)), windows(), privileged(priv)
|
||||
{
|
||||
conn->set_message_handler(wind::handle_ipc_message, this);
|
||||
}
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
};
|
201
gui/wind/IPC.cpp
201
gui/wind/IPC.cpp
@ -1,201 +0,0 @@
|
||||
#include "IPC.h"
|
||||
#include "Mouse.h"
|
||||
#include "Screen.h"
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/String.h>
|
||||
#include <os/File.h>
|
||||
#include <os/SharedMemory.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
|
||||
#define TRY_OR_IPC_ERROR(expr) \
|
||||
({ \
|
||||
auto _expr_rc = (expr); \
|
||||
if (!_expr_rc.has_value()) \
|
||||
{ \
|
||||
client.conn->send_error(_expr_rc.error()); \
|
||||
return {}; \
|
||||
} \
|
||||
_expr_rc.release_value(); \
|
||||
})
|
||||
|
||||
#define CHECK_WINDOW_ID(request, context) \
|
||||
do { \
|
||||
if ((usize)request.window >= client.windows.size() || !client.windows[request.window]) \
|
||||
{ \
|
||||
os::eprintln("wind: Window id is invalid! (%s)", context); \
|
||||
return {}; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static Result<void> handle_create_window_message(Client& client)
|
||||
{
|
||||
ui::CreateWindowRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
request.rect = request.rect.normalized();
|
||||
|
||||
auto name = TRY_OR_IPC_ERROR(String::from_cstring("Window"));
|
||||
|
||||
auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn->fd(), time(NULL)));
|
||||
|
||||
auto* window = new (std::nothrow) Window(request.rect, move(name));
|
||||
if (!window)
|
||||
{
|
||||
client.conn->send_error(ENOMEM);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto guard = make_scope_guard([window] {
|
||||
g_windows.remove(window);
|
||||
delete window;
|
||||
});
|
||||
|
||||
window->pixels = (u32*)TRY_OR_IPC_ERROR(
|
||||
os::SharedMemory::create(shm_path.view(), window->surface.height * window->surface.width * 4));
|
||||
|
||||
TRY_OR_IPC_ERROR(client.windows.try_append(window));
|
||||
int id = static_cast<int>(client.windows.size() - 1);
|
||||
|
||||
// No more fallible operations, this operation is guaranteed to succeed now.
|
||||
guard.deactivate();
|
||||
|
||||
window->client = &client;
|
||||
window->id = id;
|
||||
|
||||
ui::CreateWindowResponse response;
|
||||
response.window = id;
|
||||
SET_IPC_STRING(response.shm_path, shm_path.chars());
|
||||
window->shm_path = move(shm_path);
|
||||
client.conn->send_async(response);
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_remove_shm_message(Client& client)
|
||||
{
|
||||
ui::RemoveSharedMemoryRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
CHECK_WINDOW_ID(request, "RemoveShm");
|
||||
|
||||
shm_unlink(client.windows[request.window]->shm_path.chars());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_set_window_title_message(Client& client)
|
||||
{
|
||||
ui::SetWindowTitleRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
auto name = COPY_IPC_STRING(request.title);
|
||||
|
||||
os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window);
|
||||
|
||||
CHECK_WINDOW_ID(request, "SetWindowTitle");
|
||||
|
||||
client.windows[request.window]->name = move(name);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_invalidate_message(Client& client)
|
||||
{
|
||||
ui::InvalidateRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
CHECK_WINDOW_ID(request, "Invalidate");
|
||||
|
||||
client.windows[request.window]->dirty = true;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_close_window_message(Client& client)
|
||||
{
|
||||
ui::CloseWindowRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
CHECK_WINDOW_ID(request, "CloseWindow");
|
||||
|
||||
auto* window = client.windows[request.window];
|
||||
client.windows[request.window] = nullptr;
|
||||
g_windows.remove(window);
|
||||
Mouse::the().window_did_close(window);
|
||||
delete window;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_get_screen_rect_message(Client& client)
|
||||
{
|
||||
ui::GetScreenRectRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {}; // Kinda pointless, but required.
|
||||
|
||||
ui::GetScreenRectResponse response;
|
||||
response.rect = Screen::the().canvas().rect();
|
||||
client.conn->send_async(response);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_set_titlebar_height_message(Client& client)
|
||||
{
|
||||
ui::SetTitlebarHeightRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
if (request.height < 0) request.height = 0;
|
||||
|
||||
CHECK_WINDOW_ID(request, "SetTitlebarHeight");
|
||||
|
||||
auto* window = client.windows[request.window];
|
||||
|
||||
if (request.height > window->surface.height)
|
||||
{
|
||||
os::eprintln("wind: SetTitlebarHeight: titlebar height bigger than window!");
|
||||
return {};
|
||||
}
|
||||
|
||||
window->titlebar = ui::Rect { 0, 0, window->surface.width, request.height };
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_set_special_window_attributes_message(Client& client)
|
||||
{
|
||||
ui::SetSpecialWindowAttributesRequest request;
|
||||
if (!TRY(client.conn->read_message(request))) return {};
|
||||
|
||||
if (!client.privileged)
|
||||
{
|
||||
os::eprintln(
|
||||
"wind: Unprivileged client trying to call privileged request (SetSpecialWindowAttributes), disconnecting!");
|
||||
client.should_be_disconnected = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
CHECK_WINDOW_ID(request, "SetSpecialWindowAttributes");
|
||||
|
||||
client.windows[request.window]->attributes = request.attributes;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace wind
|
||||
{
|
||||
void handle_ipc_message(os::IPC::ClientConnection&, u8 id, void* c)
|
||||
{
|
||||
Client& client = *(Client*)c;
|
||||
switch (id)
|
||||
{
|
||||
case ui::CREATE_WINDOW_ID: handle_create_window_message(client); break;
|
||||
case ui::REMOVE_SHM_ID: handle_remove_shm_message(client); break;
|
||||
case ui::SET_WINDOW_TITLE_ID: handle_set_window_title_message(client); break;
|
||||
case ui::INVALIDATE_ID: handle_invalidate_message(client); break;
|
||||
case ui::CLOSE_WINDOW_ID: handle_close_window_message(client); break;
|
||||
case ui::GET_SCREEN_RECT_ID: handle_get_screen_rect_message(client); break;
|
||||
case ui::SET_TITLEBAR_HEIGHT_ID: handle_set_titlebar_height_message(client); break;
|
||||
case ui::SET_SPECIAL_WINDOW_ATTRIBUTES_ID: handle_set_special_window_attributes_message(client); break;
|
||||
default: os::eprintln("wind: Invalid IPC message from client!"); return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
#include "Client.h"
|
||||
#include <ui/ipc/Server.h>
|
||||
|
||||
namespace wind
|
||||
{
|
||||
void handle_ipc_message(os::IPC::ClientConnection& connection, u8 id, void* c);
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
#include <luna/Format.h>
|
||||
#include <luna/SourceLocation.h>
|
||||
#include <luna/Spinlock.h>
|
||||
#include <luna/StringBuilder.h>
|
||||
|
||||
static bool g_debug_enabled = true;
|
||||
static bool g_serial_enabled = true;
|
||||
@ -175,23 +174,3 @@ static bool g_check_already_failed = false;
|
||||
}
|
||||
CPU::efficient_halt();
|
||||
}
|
||||
|
||||
Result<void> hexdump(void* data, usize size)
|
||||
{
|
||||
StringBuilder sb;
|
||||
|
||||
u8* ptr = (u8*)data;
|
||||
|
||||
while (size)
|
||||
{
|
||||
TRY(sb.format("%#2x ", *ptr));
|
||||
ptr++;
|
||||
size--;
|
||||
}
|
||||
|
||||
auto message = TRY(sb.string());
|
||||
|
||||
kdbgln("hexdump: %s", message.chars());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -26,8 +26,6 @@ void set_text_console_initialized();
|
||||
#define kwarnln(...) log(LogLevel::Warn, __VA_ARGS__)
|
||||
#define kerrorln(...) log(LogLevel::Error, __VA_ARGS__)
|
||||
|
||||
Result<void> hexdump(void* data, usize size);
|
||||
|
||||
[[noreturn]] extern void __critical_error_handler(SourceLocation location, const char* expr, const char* failmsg,
|
||||
const char* errmsg);
|
||||
|
||||
|
@ -117,30 +117,6 @@ namespace moon
|
||||
K_CH46, // .
|
||||
K_CH47, // /
|
||||
K_CH48, // Space
|
||||
// Multimedia keys
|
||||
K_MediaPrev,
|
||||
K_MediaNext,
|
||||
K_MediaMute,
|
||||
K_MediaCalc,
|
||||
K_MediaPlay,
|
||||
K_MediaStop,
|
||||
K_MediaVolDown,
|
||||
K_MediaVolUp,
|
||||
// WWW keys
|
||||
K_WWWHome,
|
||||
K_WWWSearch,
|
||||
K_WWWFavorites,
|
||||
K_WWWRefresh,
|
||||
K_WWWStop,
|
||||
K_WWWForward,
|
||||
K_WWWBack,
|
||||
K_WWWMyComputer,
|
||||
K_WWWEmail,
|
||||
K_WWWSelect,
|
||||
// Power keys
|
||||
K_ACPIPower,
|
||||
K_ACPISleep,
|
||||
K_ACPIWake,
|
||||
// Unknown key
|
||||
K_Unknown,
|
||||
};
|
||||
|
@ -1,16 +1,24 @@
|
||||
#pragma once
|
||||
#include "api/Keyboard.h"
|
||||
#include <luna/Option.h>
|
||||
#include <luna/Vector.h>
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
struct TTYKeyboardState
|
||||
{
|
||||
bool ignore_next { false };
|
||||
bool left_shift { false };
|
||||
bool right_shift { false };
|
||||
bool left_control { false };
|
||||
bool capslock { false };
|
||||
};
|
||||
|
||||
struct KeyboardState
|
||||
{
|
||||
Vector<u8> key_state;
|
||||
bool parsing_ext { false };
|
||||
bool parsing_pause { false };
|
||||
bool ignore_next { false };
|
||||
};
|
||||
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state);
|
||||
|
||||
Option<moon::KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state);
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ namespace MMU
|
||||
NoExecute = 4,
|
||||
WriteThrough = 8,
|
||||
CacheDisable = 16,
|
||||
Global = 32,
|
||||
};
|
||||
|
||||
enum class UseHugePages
|
||||
|
@ -25,13 +25,6 @@ enable_nx:
|
||||
wrmsr
|
||||
ret
|
||||
|
||||
global enable_global_pages
|
||||
enable_global_pages:
|
||||
mov rax, cr4
|
||||
or ax, 1 << 7
|
||||
mov cr4, rax
|
||||
ret
|
||||
|
||||
global load_gdt
|
||||
load_gdt:
|
||||
cli
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
extern "C" void enable_sse();
|
||||
extern "C" void enable_write_protect();
|
||||
extern "C" void enable_global_pages();
|
||||
extern "C" void enable_nx();
|
||||
|
||||
extern void setup_gdt();
|
||||
@ -76,6 +75,16 @@ void decode_page_fault_error_code(u64 code)
|
||||
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
|
||||
}
|
||||
|
||||
static void check_stack(Thread* current, Registers* regs)
|
||||
{
|
||||
if (regs->rsp < current->stack.bottom() || regs->rsp >= current->stack.top())
|
||||
kerrorln("Abnormal stack (RSP outside the normal range, %.16lx-%.16lx)", current->stack.bottom(),
|
||||
current->stack.top());
|
||||
|
||||
if (regs->rsp >= (current->stack.bottom() - ARCH_PAGE_SIZE) && regs->rsp < current->stack.bottom())
|
||||
kerrorln("Likely stack overflow (CPU exception inside guard page)");
|
||||
}
|
||||
|
||||
void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
||||
{
|
||||
if (err) kerrorln("Caught CPU exception: %s", err);
|
||||
@ -89,7 +98,7 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
||||
if (!is_in_kernel(regs))
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
if (current->check_stack_on_exception(regs->rsp)) return;
|
||||
check_stack(current, regs);
|
||||
|
||||
current->send_signal(signo);
|
||||
current->process_pending_signals(regs);
|
||||
@ -269,7 +278,6 @@ namespace CPU
|
||||
void platform_init()
|
||||
{
|
||||
enable_sse();
|
||||
enable_global_pages();
|
||||
// enable_write_protect();
|
||||
if (test_nx()) enable_nx();
|
||||
else
|
||||
|
@ -13,9 +13,104 @@ static bool is_key_released(u8& scancode)
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr static u8 print_screen_pressed[] = { 0xe0, 0x2a, 0xe0, 0x37 };
|
||||
constexpr static u8 print_screen_released[] = { 0xe0, 0xb7, 0xe0, 0xaa };
|
||||
constexpr static u8 pause[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5 };
|
||||
constexpr u8 EXTENDED_KEY_CODE = 0xe0;
|
||||
constexpr u8 LEFT_SHIFT = 0x2a;
|
||||
constexpr u8 RIGHT_SHIFT = 0x36;
|
||||
constexpr u8 CAPS_LOCK = 0x3a;
|
||||
|
||||
constexpr u8 LEFT_CONTROL = 0x1D;
|
||||
constexpr u8 LEFT_ALT = 0x38;
|
||||
constexpr u8 F11 = 0x57;
|
||||
constexpr u8 F12 = 0x58;
|
||||
|
||||
static bool should_ignore_key(u8 scancode)
|
||||
{
|
||||
return (scancode > 0x3A && scancode < 0x47) || scancode == F11 || scancode == F12;
|
||||
}
|
||||
|
||||
constexpr char key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\t', // tab
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
'\0', // left ctrl
|
||||
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
|
||||
'\0', // left shift
|
||||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
|
||||
'\0', // right shift
|
||||
'*', // keypad *
|
||||
'\0', // left alt
|
||||
' ',
|
||||
'\0', // caps lock
|
||||
'\0', // f1
|
||||
'\0', // f2
|
||||
'\0', // f3
|
||||
'\0', // f4
|
||||
'\0', // f5
|
||||
'\0', // f6
|
||||
'\0', // f7
|
||||
'\0', // f8
|
||||
'\0', // f9
|
||||
'\0', // f10
|
||||
'\0', // num lock
|
||||
'\0', // scroll lock
|
||||
'7', // keypad 7
|
||||
'8', // keypad 8
|
||||
'9', // keypad 9
|
||||
'-', // keypad -
|
||||
'4', // keypad 4
|
||||
'5', // keypad 5
|
||||
'6', // keypad 6
|
||||
'+', // keypad +
|
||||
'1', // keypad 1
|
||||
'2', // keypad 2
|
||||
'3', // keypad 3
|
||||
'0', // keypad 0
|
||||
'.', // keypad .
|
||||
};
|
||||
|
||||
constexpr char shifted_key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||
'\t', // tab
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||
'\0', // left ctrl
|
||||
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
|
||||
'\0', // left shift
|
||||
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
|
||||
'\0', // right shift
|
||||
'*', // keypad *
|
||||
'\0', // left alt
|
||||
' ',
|
||||
'\0', // caps lock
|
||||
'\0', // f1
|
||||
'\0', // f2
|
||||
'\0', // f3
|
||||
'\0', // f4
|
||||
'\0', // f5
|
||||
'\0', // f6
|
||||
'\0', // f7
|
||||
'\0', // f8
|
||||
'\0', // f9
|
||||
'\0', // f10
|
||||
'\0', // num lock
|
||||
'\0', // scroll lock
|
||||
'7', // keypad 7
|
||||
'8', // keypad 8
|
||||
'9', // keypad 9
|
||||
'-', // keypad -
|
||||
'4', // keypad 4
|
||||
'5', // keypad 5
|
||||
'6', // keypad 6
|
||||
'+', // keypad +
|
||||
'1', // keypad 1
|
||||
'2', // keypad 2
|
||||
'3', // keypad 3
|
||||
'0', // keypad 0
|
||||
'.', // keypad .
|
||||
};
|
||||
|
||||
using namespace moon;
|
||||
|
||||
@ -81,113 +176,95 @@ constexpr KeyCode keycode_table[] = {
|
||||
K_F11, K_F12,
|
||||
};
|
||||
|
||||
constexpr KeyCode extended_keycode_table[] = {
|
||||
K_MediaPrev, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_MediaNext, K_Unknown, K_Unknown, K_KeypadEnter, K_RightControl,
|
||||
K_Unknown, K_Unknown, K_MediaMute, K_MediaCalc, K_MediaPlay, K_Unknown, K_MediaStop,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_MediaVolDown, K_Unknown, K_MediaVolUp, K_Unknown, K_WWWHome,
|
||||
K_Unknown, K_Unknown, K_KeypadDiv, K_Unknown, K_Unknown, K_RightAlt, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Home,
|
||||
K_UpArrow, K_PageUp, K_Unknown, K_LeftArrow, K_Unknown, K_RightArrow, K_Unknown,
|
||||
K_End, K_DownArrow, K_PageDown, K_Insert, K_Delete, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Super, K_Super,
|
||||
K_Menu, K_ACPIPower, K_ACPISleep, K_Unknown, K_Unknown, K_Unknown, K_ACPIWake,
|
||||
K_Unknown, K_WWWSearch, K_WWWFavorites, K_WWWRefresh, K_WWWStop, K_WWWForward, K_WWWBack,
|
||||
K_WWWMyComputer, K_WWWEmail, K_WWWSelect
|
||||
};
|
||||
static bool is_shifted(const Keyboard::TTYKeyboardState& state)
|
||||
{
|
||||
if (state.capslock) return !(state.left_shift || state.right_shift);
|
||||
return state.left_shift || state.right_shift;
|
||||
}
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state)
|
||||
{
|
||||
if (state.ignore_next)
|
||||
{
|
||||
state.ignore_next = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Support extended scancodes.
|
||||
if (scancode == EXTENDED_KEY_CODE)
|
||||
{
|
||||
state.ignore_next = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
if (scancode == LEFT_SHIFT)
|
||||
{
|
||||
state.left_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == RIGHT_SHIFT)
|
||||
{
|
||||
state.right_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == CAPS_LOCK)
|
||||
{
|
||||
if (!released) state.capslock = !state.capslock;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == LEFT_CONTROL)
|
||||
{
|
||||
if (released) state.left_control = false;
|
||||
else
|
||||
state.left_control = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (should_ignore_key(scancode)) return {};
|
||||
|
||||
if (released) return {};
|
||||
|
||||
if (state.left_control)
|
||||
{
|
||||
char key;
|
||||
if (is_shifted(state)) key = shifted_key_table[scancode];
|
||||
else
|
||||
key = key_table[scancode];
|
||||
if (_islower(key)) key = (char)_toupper(key);
|
||||
if (_isupper(key)) return key - 0x40;
|
||||
if (key == '@') return key - 0x40;
|
||||
if (key > 'Z' && key < '`') return key - 0x40;
|
||||
if (key == '?') return 0x7f;
|
||||
}
|
||||
|
||||
if (is_shifted(state)) return shifted_key_table[scancode];
|
||||
return key_table[scancode];
|
||||
}
|
||||
|
||||
Option<KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state)
|
||||
{
|
||||
if (state.parsing_pause)
|
||||
if (state.ignore_next)
|
||||
{
|
||||
if (state.key_state.size() < 6) state.key_state.try_append(scancode).release_value();
|
||||
|
||||
if (state.key_state.size() == 6)
|
||||
{
|
||||
state.parsing_pause = state.parsing_ext = false;
|
||||
if (!memcmp(state.key_state.data(), pause, 6))
|
||||
{
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Pause, false };
|
||||
}
|
||||
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Unknown, false };
|
||||
}
|
||||
}
|
||||
|
||||
if (state.parsing_ext)
|
||||
{
|
||||
if (scancode == 0xe0 && state.key_state.size() == 2)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
state.ignore_next = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (state.key_state.size() == 3)
|
||||
// FIXME: Support extended scancodes.
|
||||
if (scancode == EXTENDED_KEY_CODE)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
if (!memcmp(state.key_state.data(), print_screen_pressed, 4))
|
||||
{
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_PrtScr, false };
|
||||
}
|
||||
|
||||
if (!memcmp(state.key_state.data(), print_screen_released, 4))
|
||||
{
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_PrtScr, true };
|
||||
}
|
||||
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Unknown, false };
|
||||
}
|
||||
|
||||
if (scancode == 0x2a || scancode == 0xb7)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
state.ignore_next = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
KeyCode key = KeyCode::K_Unknown;
|
||||
|
||||
if (scancode <= 0x6d) key = extended_keycode_table[scancode - 0x10];
|
||||
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
|
||||
return KeyboardPacket { key, released };
|
||||
}
|
||||
|
||||
if (scancode == 0xe0)
|
||||
{
|
||||
state.parsing_ext = true;
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == 0xe1)
|
||||
{
|
||||
state.parsing_pause = true;
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
KeyCode key = KeyCode::K_Unknown;
|
||||
|
||||
if (scancode <= 0x58) key = keycode_table[scancode];
|
||||
|
||||
return KeyboardPacket { key, released };
|
||||
return KeyboardPacket { keycode_table[scancode], released };
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,6 @@ namespace MMU
|
||||
if (entry.no_execute) result |= Flags::NoExecute;
|
||||
if (entry.write_through) result |= Flags::WriteThrough;
|
||||
if (entry.cache_disabled) result |= Flags::CacheDisable;
|
||||
if (entry.global) result |= Flags::Global;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -182,7 +181,6 @@ namespace MMU
|
||||
entry.write_through = has_flag(flags, Flags::WriteThrough);
|
||||
entry.cache_disabled = has_flag(flags, Flags::CacheDisable);
|
||||
entry.no_execute = has_flag(flags, Flags::NoExecute);
|
||||
entry.global = has_flag(flags, Flags::Global);
|
||||
entry.set_address(phys);
|
||||
}
|
||||
|
||||
@ -251,7 +249,6 @@ namespace MMU
|
||||
l1.write_through = has_flag(flags, Flags::WriteThrough);
|
||||
l1.cache_disabled = has_flag(flags, Flags::CacheDisable);
|
||||
l1.no_execute = has_flag(flags, Flags::NoExecute);
|
||||
l1.global = has_flag(flags, Flags::Global);
|
||||
flush_page(virt);
|
||||
return {};
|
||||
}
|
||||
@ -294,7 +291,7 @@ namespace MMU
|
||||
|
||||
check(physical_memory_size % ARCH_HUGE_PAGE_SIZE == 0);
|
||||
MemoryManager::map_huge_frames_at(physical_memory_base, 0, physical_memory_size / ARCH_HUGE_PAGE_SIZE,
|
||||
MMU::ReadWrite | MMU::NoExecute | MMU::Global);
|
||||
MMU::ReadWrite | MMU::NoExecute);
|
||||
|
||||
g_physical_mapping_base = physical_memory_base;
|
||||
|
||||
|
@ -17,7 +17,7 @@ struct [[gnu::packed]] PageTableEntry
|
||||
bool accessed : 1;
|
||||
bool ignore0 : 1;
|
||||
bool larger_pages : 1;
|
||||
bool global : 1;
|
||||
bool ignore1 : 1;
|
||||
u8 available : 3;
|
||||
u64 address : 48;
|
||||
u8 available2 : 3;
|
||||
|
@ -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>
|
||||
@ -158,27 +157,13 @@ namespace ATA
|
||||
|
||||
void Channel::select(u8 drive)
|
||||
{
|
||||
if (drive == m_current_drive) return;
|
||||
|
||||
u8 value = (u8)(drive << 4) | 0xa0;
|
||||
if (value == m_current_select_value) return;
|
||||
|
||||
write_register(Register::DriveSelect, value);
|
||||
|
||||
delay_400ns();
|
||||
|
||||
m_current_select_value = value;
|
||||
m_current_drive = drive;
|
||||
}
|
||||
|
||||
void Channel::select(u8 base, u8 drive)
|
||||
{
|
||||
u8 value = (u8)(drive << 4) | base;
|
||||
if (value == m_current_select_value) return;
|
||||
|
||||
write_register(Register::DriveSelect, value);
|
||||
|
||||
delay_400ns();
|
||||
|
||||
m_current_select_value = value;
|
||||
m_current_drive = drive;
|
||||
}
|
||||
|
||||
@ -518,8 +503,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;
|
||||
@ -557,61 +543,10 @@ namespace ATA
|
||||
return true;
|
||||
}
|
||||
|
||||
void Drive::select_lba(u64 lba, usize count)
|
||||
{
|
||||
if (m_is_lba48)
|
||||
{
|
||||
m_channel->write_register(Register::SectorCount, (u8)((count >> 8) & 0xff));
|
||||
m_channel->write_register(Register::LBALow, (u8)((lba >> 24) & 0xff));
|
||||
m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 32) & 0xff));
|
||||
m_channel->write_register(Register::LBAHigh, (u8)((lba >> 40) & 0xff));
|
||||
}
|
||||
m_channel->write_register(Register::SectorCount, (u8)(count & 0xff));
|
||||
m_channel->write_register(Register::LBALow, (u8)(lba & 0xff));
|
||||
m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 8) & 0xff));
|
||||
m_channel->write_register(Register::LBAHigh, (u8)((lba >> 16) & 0xff));
|
||||
}
|
||||
|
||||
Result<void> Drive::read_pio_bytes(void* out, usize size)
|
||||
Result<void> Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size)
|
||||
{
|
||||
u8* ptr = (u8*)out;
|
||||
|
||||
usize i = 0;
|
||||
|
||||
while (i < size)
|
||||
{
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
usize byte_count;
|
||||
|
||||
if (m_is_atapi)
|
||||
{
|
||||
byte_count =
|
||||
m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle);
|
||||
}
|
||||
else
|
||||
byte_count = min(512ul, size - i);
|
||||
|
||||
usize word_count = byte_count / 2;
|
||||
|
||||
while (word_count--)
|
||||
{
|
||||
u16 value = m_channel->read_data();
|
||||
ptr[0] = (u8)(value & 0xff);
|
||||
ptr[1] = (u8)(value >> 8);
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
i += byte_count;
|
||||
|
||||
m_channel->delay_400ns();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size)
|
||||
{
|
||||
m_channel->select(m_drive_index);
|
||||
|
||||
// We use PIO here.
|
||||
@ -624,11 +559,30 @@ namespace ATA
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
usize i = 0;
|
||||
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
for (int j = 0; j < 6; j++) m_channel->write_data(packet->command_words[j]);
|
||||
|
||||
TRY(read_pio_bytes(out, response_size));
|
||||
while (i < response_size)
|
||||
{
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
usize byte_count =
|
||||
m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle);
|
||||
usize word_count = byte_count / 2;
|
||||
|
||||
while (word_count--)
|
||||
{
|
||||
u16 value = m_channel->read_data();
|
||||
ptr[0] = (u8)(value & 0xff);
|
||||
ptr[1] = (u8)(value >> 8);
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
i += byte_count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -750,26 +704,6 @@ namespace ATA
|
||||
return send_packet_atapi_pio(&read_packet, out, (u16)size);
|
||||
}
|
||||
|
||||
Result<void> Drive::ata_read_pio(u64 lba, void* out, usize size)
|
||||
{
|
||||
check(lba < m_block_count);
|
||||
check(size <= ARCH_PAGE_SIZE);
|
||||
|
||||
usize count = ceil_div(size, m_block_size);
|
||||
|
||||
m_channel->select(m_is_lba48 ? 0x40 : (0xe0 | ((lba >> 24) & 0xf)), m_drive_index);
|
||||
|
||||
select_lba(lba, count);
|
||||
|
||||
m_channel->write_register(Register::Command, m_is_lba48 ? CMD_ReadSectorsExt : CMD_ReadSectors);
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
TRY(read_pio_bytes(out, size));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::read_lba(u64 lba, void* out, usize nblocks)
|
||||
{
|
||||
const usize blocks_per_page = ARCH_PAGE_SIZE / m_block_size;
|
||||
@ -785,16 +719,7 @@ namespace ATA
|
||||
return atapi_read_pio(lba, out, nblocks * m_block_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nblocks > blocks_per_page)
|
||||
{
|
||||
TRY(ata_read_pio(lba, out, ARCH_PAGE_SIZE));
|
||||
lba += blocks_per_page;
|
||||
nblocks -= blocks_per_page;
|
||||
out = offset_ptr(out, ARCH_PAGE_SIZE);
|
||||
}
|
||||
return ata_read_pio(lba, out, nblocks * m_block_size);
|
||||
}
|
||||
todo();
|
||||
}
|
||||
|
||||
void Drive::irq_handler()
|
||||
|
@ -54,8 +54,6 @@ namespace ATA
|
||||
|
||||
enum CommandRegister : u8
|
||||
{
|
||||
CMD_ReadSectors = 0x20,
|
||||
CMD_ReadSectorsExt = 0x24,
|
||||
CMD_Identify = 0xec,
|
||||
CMD_Packet = 0xa0,
|
||||
CMD_Identify_Packet = 0xa1
|
||||
@ -182,11 +180,6 @@ namespace ATA
|
||||
|
||||
Result<void> atapi_read_pio(u64 lba, void* out, usize size);
|
||||
|
||||
Result<void> ata_read_pio(u64 lba, void* out, usize size);
|
||||
|
||||
void select_lba(u64 lba, usize count);
|
||||
Result<void> read_pio_bytes(void* out, usize size);
|
||||
|
||||
Channel* m_channel;
|
||||
|
||||
u8 m_drive_index;
|
||||
@ -257,7 +250,6 @@ namespace ATA
|
||||
}
|
||||
|
||||
void select(u8 drive);
|
||||
void select(u8 base, u8 drive);
|
||||
|
||||
bool initialize();
|
||||
|
||||
@ -278,7 +270,6 @@ namespace ATA
|
||||
bool m_irq_called { false };
|
||||
|
||||
u8 m_current_drive = (u8)-1;
|
||||
u8 m_current_select_value = 0xff;
|
||||
|
||||
Option<Drive> m_drives[2];
|
||||
};
|
||||
|
@ -16,8 +16,6 @@ Result<bool> ScriptLoader::sniff()
|
||||
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++)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -105,44 +105,6 @@ Result<u64> AddressSpace::alloc_region(usize count, int prot, int flags, off_t o
|
||||
region->shmid = shmid;
|
||||
};
|
||||
|
||||
for (auto* region = m_regions.expect_first(); region; region = m_regions.next(region).value_or(nullptr))
|
||||
{
|
||||
if (!region->used)
|
||||
{
|
||||
if (region->count < count) continue;
|
||||
if (region->count == count)
|
||||
{
|
||||
update_region(region);
|
||||
u64 address = region->start;
|
||||
try_merge_region_with_neighbors(region);
|
||||
return address;
|
||||
}
|
||||
|
||||
u64 boundary = region->end - (count * ARCH_PAGE_SIZE);
|
||||
|
||||
auto* new_region = TRY(split_region(region, boundary));
|
||||
update_region(new_region);
|
||||
try_merge_region_with_neighbors(new_region);
|
||||
|
||||
return boundary;
|
||||
}
|
||||
}
|
||||
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<u64> AddressSpace::alloc_region_near_end(usize count, int prot, int flags, off_t offset, u64 shmid,
|
||||
bool persistent)
|
||||
{
|
||||
auto update_region = [=](VMRegion* region) {
|
||||
region->used = true;
|
||||
region->persistent = persistent;
|
||||
region->prot = prot;
|
||||
region->flags = flags;
|
||||
region->offset = offset;
|
||||
region->shmid = shmid;
|
||||
};
|
||||
|
||||
for (auto* region = m_regions.expect_last(); region; region = m_regions.previous(region).value_or(nullptr))
|
||||
{
|
||||
if (!region->used)
|
||||
@ -169,42 +131,6 @@ Result<u64> AddressSpace::alloc_region_near_end(usize count, int prot, int flags
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<u64> AddressSpace::grow_region(u64 address, usize count, bool backwards)
|
||||
{
|
||||
if (address >= VM_END) return err(EINVAL);
|
||||
|
||||
for (auto* region : m_regions)
|
||||
{
|
||||
if (region->start != address) continue;
|
||||
|
||||
auto* neighbor =
|
||||
backwards ? m_regions.previous(region).value_or(nullptr) : m_regions.next(region).value_or(nullptr);
|
||||
|
||||
if (neighbor->persistent || neighbor->used) return err(ENOMEM);
|
||||
|
||||
if (neighbor->count < count) return err(ENOMEM);
|
||||
|
||||
neighbor->count -= count;
|
||||
if (backwards) neighbor->end -= count * ARCH_PAGE_SIZE;
|
||||
else
|
||||
neighbor->start += count * ARCH_PAGE_SIZE;
|
||||
if (neighbor->count == 0)
|
||||
{
|
||||
m_regions.remove(neighbor);
|
||||
delete neighbor;
|
||||
}
|
||||
|
||||
region->count += count;
|
||||
if (backwards) region->start -= count * ARCH_PAGE_SIZE;
|
||||
else
|
||||
region->end += count * ARCH_PAGE_SIZE;
|
||||
|
||||
return region->start;
|
||||
}
|
||||
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<bool> AddressSpace::set_region(u64 address, usize count, bool used, int prot, int flags, off_t offset, u64 shmid,
|
||||
bool persistent)
|
||||
{
|
||||
|
@ -32,11 +32,6 @@ class AddressSpace
|
||||
|
||||
Result<u64> alloc_region(usize count, int prot, int flags, off_t offset, u64 shmid = 0, bool persistent = false);
|
||||
|
||||
Result<u64> alloc_region_near_end(usize count, int prot, int flags, off_t offset, u64 shmid = 0,
|
||||
bool persistent = false);
|
||||
|
||||
Result<u64> grow_region(u64 address, usize count, bool backwards);
|
||||
|
||||
Result<bool> test_and_alloc_region(u64 address, usize count, int prot, int flags, off_t offset, u64 shmid = 0,
|
||||
bool persistent = false)
|
||||
{
|
||||
|
@ -44,11 +44,11 @@ namespace MemoryManager
|
||||
{
|
||||
const usize rodata_size = (usize)(end_of_kernel_rodata - start_of_kernel_rodata);
|
||||
const usize rodata_pages = ceil_div(rodata_size, ARCH_PAGE_SIZE);
|
||||
TRY(remap((u64)start_of_kernel_rodata, rodata_pages, MMU::NoExecute | MMU::Global));
|
||||
TRY(remap((u64)start_of_kernel_rodata, rodata_pages, MMU::NoExecute));
|
||||
|
||||
const usize data_size = (usize)(end_of_kernel_data - start_of_kernel_data);
|
||||
const usize data_pages = ceil_div(data_size, ARCH_PAGE_SIZE);
|
||||
TRY(remap((u64)start_of_kernel_data, data_pages, MMU::NoExecute | MMU::ReadWrite | MMU::Global));
|
||||
TRY(remap((u64)start_of_kernel_data, data_pages, MMU::NoExecute | MMU::ReadWrite));
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -380,7 +380,7 @@ namespace MemoryManager
|
||||
while (pages_mapped < count)
|
||||
{
|
||||
const u64 frame = TRY(alloc_frame());
|
||||
TRY(MMU::map(virt, frame, flags | MMU::Global, MMU::UseHugePages::No));
|
||||
TRY(MMU::map(virt, frame, flags, MMU::UseHugePages::No));
|
||||
virt += ARCH_PAGE_SIZE;
|
||||
pages_mapped++;
|
||||
}
|
||||
@ -405,7 +405,7 @@ namespace MemoryManager
|
||||
|
||||
while (pages_mapped < count)
|
||||
{
|
||||
TRY(MMU::map(virt, phys, flags | MMU::Global, MMU::UseHugePages::No));
|
||||
TRY(MMU::map(virt, phys, flags, MMU::UseHugePages::No));
|
||||
virt += ARCH_PAGE_SIZE;
|
||||
phys += ARCH_PAGE_SIZE;
|
||||
pages_mapped++;
|
||||
|
@ -159,9 +159,6 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
||||
break;
|
||||
}
|
||||
|
||||
// This means that the connection was established, but closed before this thread could run again.
|
||||
if (m_state == Reset) return err(ECONNABORTED);
|
||||
|
||||
check(m_state == Connected);
|
||||
check(m_peer);
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <bits/open-flags.h>
|
||||
#include <bits/signal.h>
|
||||
#include <bits/waitpid.h>
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/Alloc.h>
|
||||
#include <luna/Atomic.h>
|
||||
#include <luna/PathParser.h>
|
||||
@ -104,14 +103,14 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
|
||||
[[noreturn]] void Thread::exit_and_signal_parent(int _status)
|
||||
{
|
||||
check(!is_kernel);
|
||||
|
||||
#ifndef MOON_ENABLE_TESTING_FEATURES
|
||||
if (this->id == 1) fail("the init process exited");
|
||||
#else
|
||||
if (this->id == 1) CPU::magic_exit(_status);
|
||||
#endif
|
||||
|
||||
if (is_kernel) state = ThreadState::Dying;
|
||||
else
|
||||
{
|
||||
Scheduler::for_each_child(this, [](Thread* child) {
|
||||
child->parent = Scheduler::init_thread();
|
||||
return true;
|
||||
@ -150,7 +149,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
||||
}
|
||||
|
||||
state = ThreadState::Exited;
|
||||
|
||||
}
|
||||
status = _status;
|
||||
kernel_yield();
|
||||
unreachable();
|
||||
@ -266,66 +265,6 @@ void Thread::send_signal(int signo)
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr usize MAX_STACK_SIZE = 8 * 1024 * 1024; // 8 MB
|
||||
|
||||
bool Thread::check_stack_on_exception(u64 stack_pointer)
|
||||
{
|
||||
if (stack_pointer < stack.bottom() || stack_pointer >= stack.top())
|
||||
kwarnln("Abnormal stack (Stack pointer outside the normal range, %.16lx-%.16lx)", stack.bottom(), stack.top());
|
||||
|
||||
// Check whether the stack pointer is within 8 pages of the bottom of the stack
|
||||
u64 threshold = stack.bottom() - (ARCH_PAGE_SIZE * 8);
|
||||
|
||||
if (stack_pointer >= threshold && stack_pointer < stack.bottom())
|
||||
{
|
||||
kwarnln("Likely stack overflow (CPU exception a few pages below the stack)");
|
||||
|
||||
// Try to grow the stack
|
||||
usize stack_space_remaining = MAX_STACK_SIZE - stack.bytes();
|
||||
if (!stack_space_remaining)
|
||||
{
|
||||
kwarnln("Failed to grow stack: this thread already used up all its stack space");
|
||||
return false;
|
||||
}
|
||||
|
||||
usize exceeded_bytes = align_up<ARCH_PAGE_SIZE>(stack.bottom() - stack_pointer);
|
||||
if (exceeded_bytes > stack_space_remaining)
|
||||
{
|
||||
kwarnln("Failed to grow stack: this thread needs more space than the one it has remaining (%zu bytes out "
|
||||
"of %zu remaining)",
|
||||
exceeded_bytes, stack_space_remaining);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we can, we'll add 2 more pages of buffer space, otherwise we use whatever we can.
|
||||
usize bytes_to_grow = min(stack_space_remaining, exceeded_bytes + 2 * ARCH_PAGE_SIZE);
|
||||
auto maybe_base = address_space->grow_region(stack.bottom(), bytes_to_grow / ARCH_PAGE_SIZE, true);
|
||||
if (maybe_base.has_error())
|
||||
{
|
||||
kwarnln("Failed to grow stack: could not allocate virtual memory space (%s)", maybe_base.error_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 base = maybe_base.release_value();
|
||||
auto result = MemoryManager::alloc_at_zeroed(base, bytes_to_grow / ARCH_PAGE_SIZE,
|
||||
MMU::ReadWrite | MMU::NoExecute | MMU::User);
|
||||
if (result.has_error())
|
||||
{
|
||||
address_space->free_region(base, bytes_to_grow / ARCH_PAGE_SIZE);
|
||||
kwarnln("Failed to grow stack: could not allocate physical pages (%s)", result.error_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
kinfoln("Stack expanded from %lx (%zu bytes) to %lx (%zu bytes)", stack.bottom(), stack.bytes(), base,
|
||||
stack.bytes() + bytes_to_grow);
|
||||
|
||||
stack = Stack { base, stack.bytes() + bytes_to_grow };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Thread::stop()
|
||||
{
|
||||
state = ThreadState::Stopped;
|
||||
|
@ -169,8 +169,6 @@ struct Thread : public LinkedListNode<Thread>
|
||||
Result<u64> push_mem_on_stack(const u8* mem, usize size);
|
||||
Result<u64> pop_mem_from_stack(u8* mem, usize size);
|
||||
|
||||
bool check_stack_on_exception(u64 stack_pointer);
|
||||
|
||||
void stop();
|
||||
void resume();
|
||||
|
||||
|
@ -7,15 +7,23 @@
|
||||
|
||||
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
|
||||
static constexpr usize DEFAULT_USER_STACK_SIZE = DEFAULT_USER_STACK_PAGES * ARCH_PAGE_SIZE;
|
||||
static constexpr u64 THREAD_STACK_BASE = 0x10000;
|
||||
|
||||
static Result<void> create_user_stack(Stack& user_stack, AddressSpace* space)
|
||||
{
|
||||
auto base = TRY(space->alloc_region_near_end(DEFAULT_USER_STACK_PAGES, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0, true));
|
||||
if (!TRY(space->test_and_alloc_region(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0, true)))
|
||||
return err(ENOMEM);
|
||||
|
||||
TRY(MemoryManager::alloc_at_zeroed(base, DEFAULT_USER_STACK_PAGES, MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
||||
// Stack overflow guard page, remains unmapped.
|
||||
if (!TRY(space->test_and_alloc_region(THREAD_STACK_BASE - ARCH_PAGE_SIZE, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
0, 0, true)))
|
||||
return err(ENOMEM);
|
||||
|
||||
user_stack = { base, DEFAULT_USER_STACK_SIZE };
|
||||
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
|
||||
MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
||||
|
||||
user_stack = { THREAD_STACK_BASE, DEFAULT_USER_STACK_SIZE };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ set(SOURCES
|
||||
src/math.cpp
|
||||
src/pty.cpp
|
||||
src/utmp.cpp
|
||||
src/shadow.cpp
|
||||
src/sys/stat.cpp
|
||||
src/sys/mman.cpp
|
||||
src/sys/wait.cpp
|
||||
|
@ -1,47 +0,0 @@
|
||||
/* endian.h: Functions to convert between different byte orderings. */
|
||||
|
||||
#ifndef _ENDIAN_H
|
||||
#define _ENDIAN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
|
||||
#define BIG_ENDIAN __ORDER_BIG_ENDIAN__
|
||||
|
||||
#define BYTE_ORDER __BYTE_ORDER__
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define be64toh(x) __builtin_bswap64(x)
|
||||
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htobe64(x) __builtin_bswap64(x)
|
||||
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define htole64(x) (x)
|
||||
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#define le64toh(x) (x)
|
||||
#else
|
||||
#define be16toh(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define be64toh(x) (x)
|
||||
|
||||
#define htobe16(x) (x)
|
||||
#define htobe32(x) (x)
|
||||
#define htobe64(x) (x)
|
||||
|
||||
#define htole16(x) __builtin_bswap16(x)
|
||||
#define htole32(x) __builtin_bswap32(x)
|
||||
#define htole64(x) __builtin_bswap64(x)
|
||||
|
||||
#define le16toh(x) __builtin_bswap16(x)
|
||||
#define le32toh(x) __builtin_bswap32(x)
|
||||
#define le64toh(x) __builtin_bswap64(x)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,48 +0,0 @@
|
||||
/* shadow.h: Password file parsing. */
|
||||
|
||||
#ifndef _SHADOW_H
|
||||
#define _SHADOW_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct spwd
|
||||
{
|
||||
char* sp_namp;
|
||||
char* sp_pwdp;
|
||||
long sp_lstchg;
|
||||
long sp_min;
|
||||
long sp_max;
|
||||
long sp_warn;
|
||||
long sp_inact;
|
||||
long sp_expire;
|
||||
unsigned long sp_flag;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Read the next entry from the shadow file. */
|
||||
struct spwd* getspent();
|
||||
|
||||
/* Find the entry with a matching username in the shadow file. */
|
||||
struct spwd* getspnam(const char* name);
|
||||
|
||||
/* Read the next entry from the shadow file (reentrant version). */
|
||||
int getspent_r(struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp);
|
||||
|
||||
/* Read the next entry from a shadow file (reentrant version). */
|
||||
int fgetspent_r(FILE* stream, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp);
|
||||
|
||||
/* Rewind the shadow file. */
|
||||
void setspent(void);
|
||||
|
||||
/* End shadow file parsing. */
|
||||
void endspent(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -56,7 +56,7 @@ extern "C"
|
||||
#endif
|
||||
|
||||
/* Flush a stream's buffers. */
|
||||
int fflush(FILE* stream);
|
||||
int fflush(FILE*);
|
||||
|
||||
/* Open a file and bind a stream to it. */
|
||||
FILE* fopen(const char* path, const char* mode);
|
||||
@ -67,7 +67,7 @@ extern "C"
|
||||
/* Change the underlying file and mode of a stream. */
|
||||
FILE* freopen(const char* path, const char* mode, FILE* stream);
|
||||
|
||||
/* Close a file and free up its stream. */
|
||||
/* Close a file and frees up its stream. */
|
||||
int fclose(FILE* stream);
|
||||
|
||||
/* Return the file descriptor associated with a stream. */
|
||||
|
@ -98,13 +98,8 @@ extern "C"
|
||||
* endptr if nonnull. */
|
||||
unsigned long strtoul(const char* str, char** endptr, int base);
|
||||
|
||||
/* Parse an integer of the specified base from a string, storing the first non-number character in endptr if
|
||||
* nonnull. */
|
||||
long long strtoll(const char* str, char** endptr, int base);
|
||||
|
||||
/* Parse an unsigned integer of the specified base from a string, storing the first non-number character in
|
||||
* endptr if nonnull. */
|
||||
unsigned long long strtoull(const char* str, char** endptr, int base);
|
||||
#define strtoll strtol
|
||||
#define strtoull strtoul
|
||||
|
||||
/* Return the next pseudorandom number. */
|
||||
int rand();
|
||||
|
@ -22,7 +22,6 @@
|
||||
#define _POSIX_SYNCHRONIZED_IO 200112L
|
||||
#define _POSIX_SAVED_IDS 200112L
|
||||
#define _POSIX_VDISABLE (-2)
|
||||
#define _POSIX_VERSION 200112L
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
|
@ -1,116 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <shadow.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct spwd spwd;
|
||||
static FILE* f { nullptr };
|
||||
static char s_buf[4096];
|
||||
|
||||
extern char* _strtok_once(char* str, const char* delim);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct spwd* getspent()
|
||||
{
|
||||
struct spwd* result;
|
||||
|
||||
if (getspent_r(&spwd, s_buf, sizeof(s_buf), &result) < 0) return nullptr;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int getspent_r(struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp)
|
||||
{
|
||||
*spbufp = nullptr;
|
||||
|
||||
if (!f)
|
||||
{
|
||||
f = fopen("/etc/shadow", "r");
|
||||
if (!f) return -1;
|
||||
fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
return fgetspent_r(f, spbuf, buf, buflen, spbufp);
|
||||
}
|
||||
|
||||
int fgetspent_r(FILE* stream, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp)
|
||||
{
|
||||
*spbufp = nullptr;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char* rc = fgets(buf, buflen, stream);
|
||||
if (!rc) return -1;
|
||||
|
||||
char* name = _strtok_once(rc, ":\n");
|
||||
if (!name) continue;
|
||||
|
||||
char* passwd = _strtok_once(nullptr, ":\n");
|
||||
if (!passwd) continue;
|
||||
|
||||
char* lstchg = _strtok_once(nullptr, ":\n");
|
||||
if (!lstchg) continue;
|
||||
|
||||
char* min = _strtok_once(nullptr, ":\n");
|
||||
if (!min) continue;
|
||||
|
||||
char* max = _strtok_once(nullptr, ":\n");
|
||||
if (!max) continue;
|
||||
|
||||
char* warn = _strtok_once(nullptr, ":\n");
|
||||
if (!warn) continue;
|
||||
|
||||
char* inact = _strtok_once(nullptr, ":\n");
|
||||
if (!inact) continue;
|
||||
|
||||
char* expire = _strtok_once(nullptr, ":\n");
|
||||
if (!expire) continue;
|
||||
|
||||
char* flag = _strtok_once(nullptr, ":\n");
|
||||
if (!flag) continue;
|
||||
|
||||
spbuf->sp_namp = name;
|
||||
spbuf->sp_pwdp = passwd;
|
||||
spbuf->sp_lstchg = (long)strtol(lstchg, nullptr, 10);
|
||||
spbuf->sp_min = (long)strtol(min, nullptr, 10);
|
||||
spbuf->sp_max = (long)strtol(max, nullptr, 10);
|
||||
spbuf->sp_warn = (long)strtol(warn, nullptr, 10);
|
||||
spbuf->sp_inact = (long)strtol(inact, nullptr, 10);
|
||||
spbuf->sp_expire = (long)strtol(expire, nullptr, 10);
|
||||
spbuf->sp_flag = (unsigned long)strtoul(flag, nullptr, 10);
|
||||
|
||||
*spbufp = spbuf;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct spwd* getspnam(const char* name)
|
||||
{
|
||||
setspent();
|
||||
|
||||
struct spwd* entry;
|
||||
|
||||
while ((entry = getspent()))
|
||||
{
|
||||
if (!strcmp(entry->sp_namp, name)) { return entry; }
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void setspent()
|
||||
{
|
||||
if (f) rewind(f);
|
||||
}
|
||||
|
||||
void endspent()
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
fclose(f);
|
||||
f = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
@ -273,21 +273,14 @@ extern "C"
|
||||
|
||||
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
||||
|
||||
fflush(stream);
|
||||
|
||||
if (!path)
|
||||
{
|
||||
// FIXME: No mode changes are permitted.
|
||||
errno = EBADF;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer);
|
||||
|
||||
close(stream->_fd);
|
||||
|
||||
s_open_files[stream->_fd] = nullptr;
|
||||
|
||||
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer);
|
||||
|
||||
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
||||
|
||||
int fd = open(path, flags, 0666);
|
||||
if (fd < 0) { return nullptr; }
|
||||
|
||||
@ -800,13 +793,11 @@ extern "C"
|
||||
{
|
||||
close(pfds[0]);
|
||||
dup2(pfds[1], STDOUT_FILENO);
|
||||
close(pfds[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
close(pfds[1]);
|
||||
dup2(pfds[0], STDIN_FILENO);
|
||||
close(pfds[0]);
|
||||
}
|
||||
|
||||
execl("/bin/sh", "sh", "-c", command, nullptr);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user