Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e1a0d0e13 | |||
773cd576d1 | |||
498c371547 | |||
b0be170b41 | |||
5cb4b8b1fe | |||
d9713723c9 | |||
c0cf952113 | |||
22766a6724 | |||
abdaad5ea4 | |||
f116afd59d | |||
1b80111938 | |||
f91800f5e1 | |||
6dcdc43dc2 | |||
00382421b2 | |||
5d5c85a022 | |||
48ee803e58 | |||
984200ca9a | |||
ac260d0397 | |||
fb3333a086 | |||
ccef3e2069 | |||
ad3cea7e78 | |||
865a913502 | |||
499bf6dd19 | |||
94e7dde8af | |||
f38c9e68c1 | |||
3b8aabce0f | |||
5f56e4b63a | |||
24b886b0d1 | |||
d8e4489079 | |||
2868fd8122 | |||
56a2b607b5 | |||
ec6ceb4c8d | |||
d05d6fad0b | |||
42afef5ccb | |||
853a6d7b38 | |||
8e30e0e19d | |||
dc766e1da7 | |||
6fc49a0be5 | |||
7761a8a41f | |||
0ca6c5f814 | |||
3032415bc0 | |||
7b2977a036 | |||
9e65131452 | |||
d908ccea6b | |||
e3613d1653 | |||
53f8a583dc | |||
c21fc2a297 | |||
fd26f40938 | |||
fd2fe16538 | |||
38fcd8e3e1 | |||
05bf792dbd | |||
b95cfac3ec | |||
17a31e5ea9 | |||
1f0286c9c7 | |||
ffd1c73b0f | |||
12ab71ee40 | |||
4cf39c14a1 |
@ -11,11 +11,13 @@ jobs:
|
|||||||
- name: Download dependencies
|
- name: Download dependencies
|
||||||
run: |
|
run: |
|
||||||
apt update
|
apt update
|
||||||
apt install -y cmake ninja-build nasm genext2fs qemu-system build-essential wget git
|
apt install -y cmake ninja-build nasm genext2fs qemu-system build-essential wget git clang-format
|
||||||
- name: Set up the toolchain
|
- name: Set up the toolchain
|
||||||
run: |
|
run: |
|
||||||
wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet
|
wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet
|
||||||
tar xf ci-toolchain-arm64.tar.gz
|
tar xf ci-toolchain-arm64.tar.gz
|
||||||
rm ci-toolchain-arm64.tar.gz
|
rm ci-toolchain-arm64.tar.gz
|
||||||
|
- name: Check formatting
|
||||||
|
run: tools/check-formatting.sh
|
||||||
- name: Build and run tests
|
- name: Build and run tests
|
||||||
run: tools/run-tests.sh
|
run: tools/run-tests.sh
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
BSD 2-Clause License
|
BSD 2-Clause License
|
||||||
|
|
||||||
Copyright (c) 2022-2024, apio.
|
Copyright (c) 2022-2025, apio.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
14
README.md
14
README.md
@ -20,22 +20,20 @@ A simple POSIX-based operating system for 64-bit computers, written in C++.
|
|||||||
## Screenshot
|
## Screenshot
|
||||||
![Screenshot as of 0.6.0](docs/screenshots/screenshot-0.6.0.png)
|
![Screenshot as of 0.6.0](docs/screenshots/screenshot-0.6.0.png)
|
||||||
|
|
||||||
|
## System requirements and dependencies
|
||||||
|
|
||||||
|
Read [docs/dependencies.md](docs/dependencies.md) for the full information. In short, all modern Unixes should work, provided the dependencies are available.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
To build and run Luna, you will need to build a [cross-compiler](https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler) and cross-binutils for `x86_64-luna`.
|
To build and run Luna, you will need to build a [cross-compiler](https://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler) and cross-binutils for `x86_64-luna`.
|
||||||
|
|
||||||
For this, you should start by installing the [required dependencies](https://wiki.osdev.org/GCC_Cross_Compiler#Installing_Dependencies).
|
There is a script provided for this. Run `tools/setup.sh` to build the toolchain.
|
||||||
|
|
||||||
Then, run `tools/setup.sh` to build the toolchain.
|
|
||||||
|
|
||||||
Please beware that building GCC and Binutils can take some time, depending on your machine.
|
Please beware that building GCC and Binutils can take some time, depending on your machine.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
To run Luna in a virtual machine, you should have [QEMU](https://www.qemu.org/) installed.
|
|
||||||
|
|
||||||
Additionally, the build process needs some extra dependencies to run: `cmake`, `ninja`, `nasm`, `fakeroot` and `genext2fs`.
|
|
||||||
|
|
||||||
`tools/run.sh` is the script you should use in most cases. It will build changed files, install, make an ISO image, and run Luna in QEMU.
|
`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.
|
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.
|
||||||
@ -44,7 +42,7 @@ If you have no toolchain set up, `run.sh` will build it automatically, which mea
|
|||||||
|
|
||||||
For development convenience, the system automatically starts a GUI session as the default user, without prompting for a password.
|
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`.
|
Despite this, Luna does have a login window built-in. If you'd like to try this feature out or start a GUI session as a different user, you'll need to edit [base/etc/loginui.conf](base/etc/loginui.conf) and change the line that says `Autologin=true` to `Autologin=false`.
|
||||||
|
|
||||||
## Prebuilt images
|
## Prebuilt images
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Name=login
|
Name=login
|
||||||
Description=Start a graphical user session.
|
Description=Start a graphical user session.
|
||||||
Command=/usr/bin/loginui --autologin=selene
|
Command=/usr/bin/loginui
|
||||||
StandardOutput=/dev/uart0
|
StandardOutput=/dev/uart0
|
||||||
StandardError=/dev/uart0
|
StandardError=/dev/uart0
|
||||||
Restart=true
|
Restart=true
|
||||||
|
5
base/etc/loginui.conf
Normal file
5
base/etc/loginui.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Configuration file for loginui.
|
||||||
|
# If this parameter is set to "true", loginui automatically spawns a UI session as the below user instead of prompting for a username and password.
|
||||||
|
Autologin=true
|
||||||
|
# The user to create a session for if "Autologin" is set to true (see above). If the username is invalid, loginui will behave as if "Autologin" was set to false.
|
||||||
|
AutologinUser=selene
|
@ -1,3 +1,3 @@
|
|||||||
root:toor:0:0:99999:7:::
|
root:ce5ca673d13b36118d54a7cf13aeb0ca012383bf771e713421b4d1fd841f539a:0:0:99999:7:::
|
||||||
wind:!:0:0:99999:7:::
|
wind:!:0:0:99999:7:::
|
||||||
selene:moon:0:0:99999:7:::
|
selene:9e78b43ea00edcac8299e0cc8df7f6f913078171335f733a21d5d911b6999132:0:0:99999:7:::
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
Welcome to the Luna operating system!
|
Welcome to the Luna operating system!
|
||||||
You are running on the default user account, selene.
|
You are running on the default user account, selene.
|
||||||
|
|
||||||
If you are familiar with Unix-style operating systems (like Linux or *BSD), you should be able to use the Luna terminal without much problems.
|
If you are familiar with Unix-style operating systems (like Linux or *BSD),
|
||||||
|
you should be able to use the Luna terminal without much problems.
|
||||||
|
|
||||||
Following the traditional Unix filesystem structure, programs are installed in /usr/bin (/bin is a symlink to /usr/bin). The command `ls /bin` will show all commands available on your current Luna installation.
|
Following the traditional Unix filesystem structure,
|
||||||
|
programs are installed in /usr/bin (/bin is a symlink to /usr/bin).
|
||||||
|
The command `ls /bin` will show all commands available on
|
||||||
|
your current Luna installation.
|
||||||
|
|
||||||
Currently, because of driver limitations, the root file system is mounted read-only. Your home folder is writable, but volatile; it is created and populated on boot, and its contents will vanish after a reboot.
|
Currently, because of driver limitations,
|
||||||
|
the root file system is mounted read-only.
|
||||||
|
Your home folder is writable, but volatile; it is
|
||||||
|
created and populated on boot,
|
||||||
|
and its contents will vanish after a reboot.
|
||||||
|
|
||||||
The system is booted using the 'init' program. You can read its configuration files in the /etc/init directory to learn more about the boot process.
|
The system is booted using the 'init' program.
|
||||||
|
You can read its configuration files in the /etc/init directory to
|
||||||
|
learn more about the boot process.
|
||||||
|
|
||||||
Luna is free software, released under the BSD-2-Clause license. The license is included in the LICENSE file in your home directory.
|
Luna is free software, released under the BSD-2-Clause license.
|
||||||
|
The license is included in the LICENSE file in your home directory.
|
||||||
|
|
||||||
View the source code and read more about Luna at https://git.cloudapio.eu/apio/Luna.
|
View the source code and read more about Luna at
|
||||||
|
https://git.cloudapio.eu/apio/Luna.
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
Name=terminal
|
|
||||||
Description=Start the terminal.
|
|
||||||
Command=/usr/bin/terminal
|
|
3
base/etc/user/00-welcome
Normal file
3
base/etc/user/00-welcome
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Name=welcome
|
||||||
|
Description=Show a welcome message for the user.
|
||||||
|
Command=/usr/bin/editor welcome
|
3
base/usr/share/applications/05-editor
Normal file
3
base/usr/share/applications/05-editor
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Name=editor
|
||||||
|
Icon=/usr/share/icons/32x32/app-editor.tga
|
||||||
|
Command=/usr/bin/editor
|
BIN
base/usr/share/icons/32x32/app-editor.tga
Normal file
BIN
base/usr/share/icons/32x32/app-editor.tga
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
@ -54,15 +54,15 @@ As soon as the scheduler switches to the `[kinit]` thread, it will never return
|
|||||||
|
|
||||||
`[kinit]` spawns two more kernel threads, `[reap]` and `[oom]`. While `[kinit]` exits before PID 1 is started, `[reap]` and `[oom]` are present throughout the lifetime of a Luna system, and can be seen in the output of `ps`. Let's take a look at what they do.
|
`[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.
|
- `[reap]`: To understand what this thread does, we must take a look at what happens when processes 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))_
|
_(Relevant files: [kernel/src/main.cpp](../kernel/src/main.cpp#L23), [kernel/src/thread/Scheduler.cpp](../kernel/src/thread/Scheduler.cpp#L231), [kernel/src/thread/Thread.cpp](../kernel/src/thread/Thread.cpp#L126), [kernel/src/sys/waitpid.cpp](../kernel/src/sys/waitpid.cpp#L84))_
|
||||||
|
|
||||||
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 a process calls the `_exit()` syscall, all its threads' states are set to "Dying". This tells the scheduler to avoid switching to them, and the process's parent is notified, by sending SIGCHLD and (optionally) unblocking a blocked `waitpid()` call. The process remains visible to the rest of the system, and if its parent does not wait for it, it will stay there as a "zombie process". Meanwhile, the `[reap]` thread runs and collects all the resources from each thread. The process object is still alive (in a "zombie" state), but its threads have been cleaned up.
|
||||||
|
|
||||||
When 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()`).
|
When the process's parent waits for it, it is marked for reaping (by setting its thread count to -1 (PROCESS_SHOULD_REAP)), and the `[reap]` thread runs.
|
||||||
|
|
||||||
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.
|
The `[reap]` thread then "reaps" all the dead processes' resources. It frees up their memory, file descriptors, and other resources. After reaping, the process is deleted, and no trace of it is left.
|
||||||
|
|
||||||
- `[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.
|
- `[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.
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ Currently, there are two service files defined by default in `/etc/init`:
|
|||||||
|
|
||||||
`00-home`: This service sets up a `tmpfs` on `/home/selene`, so that the home directory is writable.
|
`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.
|
`99-login`: This service starts a graphical session, by calling `/usr/bin/loginui`. This service will be restarted if necessary.
|
||||||
|
|
||||||
### File system and process layout
|
### File system and process layout
|
||||||
|
|
||||||
@ -172,24 +172,60 @@ After the init stage of the boot process, the system looks like this:
|
|||||||
[x86_64-io] - PID 3
|
[x86_64-io] - PID 3
|
||||||
[reap] - PID 4
|
[reap] - PID 4
|
||||||
[oom] - PID 5
|
[oom] - PID 5
|
||||||
/usr/bin/startui - PID 13
|
/usr/bin/loginui - 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._
|
_Note: loginui is PID 13 because the `00-home` service is a shell script, which starts a few subprocesses. Since Luna does not allow for PID reuse right now, loginui ends up with PID 13._
|
||||||
|
|
||||||
## Stage 4: startui
|
## Stage 4: loginui
|
||||||
|
_Relevant files: [gui/loginui.cpp](../gui/loginui.cpp), [gui/wind/main.cpp](../gui/wind/main.cpp)_
|
||||||
|
|
||||||
|
`/usr/bin/loginui`'s job is quite simple: it prompts the user to log in with their password, after which a graphical session is started.
|
||||||
|
|
||||||
|
_Note: On development builds, Autologin=true is added to /etc/loginui.conf which disables password prompting and executes startui directly._
|
||||||
|
|
||||||
|
First, loginui starts the display server, `/usr/bin/wind`, so that it can use its capabilities to show a graphical login prompt. It is started with permissions `root:root`, and later drops privileges to `wind:wind`.
|
||||||
|
|
||||||
|
After that, loginui prompts for a username and password, checks it against the hashed password stored in `/etc/shadow`, and finally executes `/usr/bin/startui` which does the actual heavy work of starting all the services needed for a UI session.
|
||||||
|
|
||||||
|
### File system and process layout
|
||||||
|
|
||||||
|
After the loginui stage of the boot process, the system looks like this:
|
||||||
|
|
||||||
|
#### File system
|
||||||
|
```
|
||||||
|
/ - ext2 root partition
|
||||||
|
/dev - device file system
|
||||||
|
/dev/shm - POSIX shared memory file system
|
||||||
|
/dev/pts - POSIX pseudoterminal file system
|
||||||
|
/tmp - system temporary file directory
|
||||||
|
/usr, /etc, /home... - other directories contained in the root partition
|
||||||
|
/home/selene - temporary home directory
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Processes
|
||||||
|
```
|
||||||
|
/usr/bin/init - PID 1
|
||||||
|
[x86_64-io] - PID 3
|
||||||
|
[reap] - PID 4
|
||||||
|
[oom] - PID 5
|
||||||
|
/usr/bin/startui - PID 13
|
||||||
|
/usr/bin/wind - PID 14
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stage 5: startui
|
||||||
_Relevant files: [system/startui.cpp](../system/startui.cpp), [gui/wind/main.cpp](../gui/wind/main.cpp)_
|
_Relevant files: [system/startui.cpp](../system/startui.cpp), [gui/wind/main.cpp](../gui/wind/main.cpp)_
|
||||||
|
|
||||||
`/usr/bin/startui` starts a graphical user session.
|
`/usr/bin/startui` starts a graphical user session.
|
||||||
|
|
||||||
A Luna graphical user session includes the following components:
|
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 display server itself, `/usr/bin/wind`. If not already started by loginui, `startui` makes sure it's running.
|
||||||
- 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 execution server (`/usr/bin/execd`), which starts processes and keeps them alive on behalf of other processes. It is started with the standard permissions `selene:selene`.
|
||||||
- The taskbar, `/usr/bin/taskbar`. It is started with the standard permissions `selene:selene`, plus an extra group `wsys` to be able to connect to a special display server socket.
|
- The taskbar, `/usr/bin/taskbar`. It is started with the standard permissions `selene:selene`, plus an extra group `wsys` to be able to connect to a special display server socket (`/tmp/wsys.sock`, as opposed to the standard `/tmp/wind.sock`). This grants it the ability to use advanced wind features, such as placing the taskbar window behind all other windows.
|
||||||
- The 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).
|
- 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`.
|
Currently, `init --user` only does one thing: it opens up a text editor with a welcome message on startup. It can be configured to do whatever the user desires to do on startup, by placing the appropriate configuration files in `/etc/user`.
|
||||||
|
|
||||||
### File system and process layout
|
### File system and process layout
|
||||||
|
|
||||||
@ -214,8 +250,7 @@ After the startui stage of the boot process, the system is fully started up and
|
|||||||
[oom] - PID 5
|
[oom] - PID 5
|
||||||
/usr/bin/startui - PID 13
|
/usr/bin/startui - PID 13
|
||||||
/usr/bin/wind - PID 14
|
/usr/bin/wind - PID 14
|
||||||
/usr/bin/launch - PID 15
|
/usr/bin/execd - PID 15
|
||||||
/usr/bin/taskbar - PID 16
|
/usr/bin/taskbar - PID 16
|
||||||
/usr/bin/init --user - PID 17
|
/usr/bin/init --user - PID 17
|
||||||
/usr/bin/terminal - PID 18
|
/usr/bin/editor welcome - PID 18
|
||||||
/bin/sh - PID 19
|
|
||||||
|
35
docs/dependencies.md
Normal file
35
docs/dependencies.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Dependencies required to build and run Luna
|
||||||
|
|
||||||
|
## System requirements
|
||||||
|
|
||||||
|
Any modern UNIX-like system that supports all the tools listed below should work (Hopefully, that will include Luna itself in the future!).
|
||||||
|
|
||||||
|
I personally build and run Luna on an amd64 Fedora Linux 40 machine. CI runs on arm64 Ubuntu 22.04. Any other configurations are untested. Windows is not supported, although you can try using WSL if you really want to.
|
||||||
|
|
||||||
|
## Building a cross-compiler toolchain
|
||||||
|
For this, you should start by installing the [required dependencies](https://wiki.osdev.org/GCC_Cross_Compiler#Installing_Dependencies) for any OSdev cross-compiler build.
|
||||||
|
|
||||||
|
Also make sure you have the perl module `File::Compare` installed, it is required to build autoconf. On Fedora you can install it using the package manager by running `# dnf install perl-File-Compare`. If your distro doesn't have it, you might have to install it via `cpan`.
|
||||||
|
|
||||||
|
## Building the actual system
|
||||||
|
The build process needs some extra dependencies to run: `cmake`, `ninja`, `nasm`, `fakeroot` and `genext2fs`. On some distributions the `ninja` package is called `ninja-build` instead.
|
||||||
|
|
||||||
|
If you want to use `make` instead of `ninja`, create a file called `env-local.sh` in the project root and add the line `USE_MAKE=1`. In this case, ninja does not need to be installed.
|
||||||
|
|
||||||
|
## Running the built image in a virtual machine
|
||||||
|
The script provided by the project to run the system, `tools/run.sh`, assumes that QEMU is installed and uses that to run the image. Therefore, make sure your system has `qemu-system-x86_64` in the PATH. If it doesn't, install it using the method appropriate for your system, usually installing `qemu` or `qemu-system` from the package manager.
|
||||||
|
|
||||||
|
That being said, there's no requirement to use QEMU. If you want to use a different virtualization program, such as Oracle VirtualBox or VMWare, just use `tools/build-iso.sh` instead of `run.sh` and use the built `Luna.iso` in those programs.
|
||||||
|
|
||||||
|
## Formatting/linting
|
||||||
|
|
||||||
|
Please make sure you have `clang-format` installed. Additionally, if your editor does not support format-on-save or you do not have it configured, please run `tools/run-clang-format.sh` before committing, to make sure all code follows the same style conventions.
|
||||||
|
|
||||||
|
## Source dependencies
|
||||||
|
TLDR: Luna does not depend on any third-party library.
|
||||||
|
|
||||||
|
Every part of Luna is written from scratch and depends only on its own libraries and programs, with two small exceptions (included here for crediting and licensing purposes, but there is no need to download and build them separately):
|
||||||
|
|
||||||
|
The bootloader, BOOTBOOT. It is available at [gitlab.com/bztsrc/bootboot](https://gitlab.com/bztsrc/bootboot), under the MIT license. It is automatically pulled and built from source by `tools/setup.sh`.
|
||||||
|
|
||||||
|
[libc/src/strtod.cpp](../libc/src/strtod.cpp). Written by Yasuhiro Matsumoto, adapted from https://gist.github.com/mattn/1890186 and available under a public domain license.
|
@ -11,7 +11,7 @@ add_subdirectory(libui)
|
|||||||
add_subdirectory(wind)
|
add_subdirectory(wind)
|
||||||
add_subdirectory(apps)
|
add_subdirectory(apps)
|
||||||
|
|
||||||
luna_service(launch.cpp launch)
|
luna_service(execd.cpp execd)
|
||||||
luna_service(run.cpp run)
|
luna_service(run.cpp run)
|
||||||
luna_service(loginui.cpp loginui)
|
luna_service(loginui.cpp loginui)
|
||||||
target_link_libraries(loginui PRIVATE ui)
|
target_link_libraries(loginui PRIVATE ui)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <luna/String.h>
|
#include <luna/RefString.h>
|
||||||
#include <luna/Utf8.h>
|
#include <luna/Utf8.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -325,7 +325,7 @@ class GameWidget final : public ui::Widget
|
|||||||
|
|
||||||
canvas.fill(colors[tile.color]);
|
canvas.fill(colors[tile.color]);
|
||||||
|
|
||||||
auto fmt = TRY(String::format("%d"_sv, tile.number));
|
auto fmt = TRY(RefString::format("%d"_sv, tile.number));
|
||||||
|
|
||||||
auto font = ui::Font::default_bold_font();
|
auto font = ui::Font::default_bold_font();
|
||||||
auto rect = ui::align({ 0, 0, canvas.width, canvas.height },
|
auto rect = ui::align({ 0, 0, canvas.width, canvas.height },
|
||||||
|
@ -10,10 +10,12 @@
|
|||||||
#include "EditorWidget.h"
|
#include "EditorWidget.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
|
#include <luna/RefString.h>
|
||||||
#include <luna/Utf8.h>
|
#include <luna/Utf8.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
#include <ui/App.h>
|
#include <ui/App.h>
|
||||||
|
#include <ui/Dialog.h>
|
||||||
|
|
||||||
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
||||||
{
|
{
|
||||||
@ -27,7 +29,8 @@ Result<void> EditorWidget::load_file(const os::Path& path)
|
|||||||
|
|
||||||
if (!rc.has_error() && !S_ISREG(st.st_mode))
|
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());
|
auto message = TRY(RefString::format("%s is not a regular file", path.name().chars()));
|
||||||
|
ui::Dialog::show_message("Error", message.view());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,9 +44,9 @@ Result<void> EditorWidget::load_file(const os::Path& path)
|
|||||||
|
|
||||||
m_cursor = m_data.size();
|
m_cursor = m_data.size();
|
||||||
|
|
||||||
m_path = path;
|
m_path = TRY(String::from_string_view(path.name()));
|
||||||
|
|
||||||
auto basename = TRY(PathParser::basename(m_path.name()));
|
auto basename = TRY(PathParser::basename(m_path.view()));
|
||||||
|
|
||||||
String title = TRY(String::format("Text Editor - %s"_sv, basename.chars()));
|
String title = TRY(String::format("Text Editor - %s"_sv, basename.chars()));
|
||||||
window()->set_title(title.view());
|
window()->set_title(title.view());
|
||||||
@ -126,15 +129,38 @@ Result<ui::EventResult> EditorWidget::handle_key_event(const ui::KeyEventRequest
|
|||||||
return ui::EventResult::DidHandle;
|
return ui::EventResult::DidHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> EditorWidget::save_file()
|
Result<void> EditorWidget::save_file_as()
|
||||||
{
|
{
|
||||||
if (m_path.is_empty_path())
|
ui::Dialog::show_input_dialog(
|
||||||
|
"Save file as...", "Please enter the path to save this file to:", [this](StringView path) {
|
||||||
|
m_path = String::from_string_view(path).release_value();
|
||||||
|
auto rc = save_file();
|
||||||
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
os::eprintln("editor: no file to save buffer to!");
|
os::eprintln("Failed to save file %s: %s", m_path.chars(), rc.error_string());
|
||||||
return err(ENOENT);
|
ui::Dialog::show_message("Error", "Failed to save file");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto basename = PathParser::basename(m_path.view()).release_value();
|
||||||
|
|
||||||
|
String title = String::format("Text Editor - %s"_sv, basename.chars()).release_value();
|
||||||
|
window()->set_title(title.view());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file = TRY(os::File::open(m_path, os::File::WriteOnly));
|
Result<void> EditorWidget::save_file()
|
||||||
|
{
|
||||||
|
if (m_path.is_empty())
|
||||||
|
{
|
||||||
|
TRY(save_file_as());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file = TRY(os::File::open_or_create(m_path.view(), os::File::WriteOnly));
|
||||||
return file->write(m_data);
|
return file->write(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,14 +21,15 @@ class EditorWidget : public ui::TextInput
|
|||||||
Result<void> load_file(const os::Path& path);
|
Result<void> load_file(const os::Path& path);
|
||||||
|
|
||||||
Result<void> save_file();
|
Result<void> save_file();
|
||||||
|
Result<void> save_file_as();
|
||||||
|
|
||||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||||
|
|
||||||
Result<void> draw(ui::Canvas& canvas) override;
|
Result<void> draw(ui::Canvas& canvas) override;
|
||||||
|
|
||||||
os::Path& path()
|
os::Path path()
|
||||||
{
|
{
|
||||||
return m_path;
|
return m_path.view();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -41,7 +42,7 @@ class EditorWidget : public ui::TextInput
|
|||||||
};
|
};
|
||||||
Vector<Line> m_lines;
|
Vector<Line> m_lines;
|
||||||
|
|
||||||
os::Path m_path { AT_FDCWD };
|
String m_path;
|
||||||
|
|
||||||
Result<void> recalculate_lines();
|
Result<void> recalculate_lines();
|
||||||
void recalculate_cursor_position();
|
void recalculate_cursor_position();
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <ui/App.h>
|
#include <ui/App.h>
|
||||||
|
#include <ui/Dialog.h>
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@ -32,15 +33,20 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
auto* editor = TRY(make<EditorWidget>(ui::Font::default_font()));
|
auto* editor = TRY(make<EditorWidget>(ui::Font::default_font()));
|
||||||
window->set_main_widget(*editor);
|
window->set_main_widget(*editor);
|
||||||
if (!path.is_empty()) TRY(editor->load_file(path));
|
if (!path.is_empty()) editor->load_file(path);
|
||||||
|
|
||||||
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) {
|
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) {
|
||||||
auto result = editor->save_file();
|
auto result = editor->save_file();
|
||||||
if (result.has_error()) os::eprintln("editor: failed to save file: %s", result.error_string());
|
if (result.has_error())
|
||||||
else
|
{
|
||||||
os::println("editor: buffer saved to %s successfully", editor->path().name().chars());
|
os::eprintln("Failed to save file %s: %s", editor->path().name().chars(), result.error_string());
|
||||||
|
ui::Dialog::show_message("Error", "Failed to save file");
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl | ui::Mod_Shift }, true,
|
||||||
|
[&](ui::Shortcut) { editor->save_file_as(); }));
|
||||||
|
|
||||||
window->draw();
|
window->draw();
|
||||||
|
|
||||||
return app.run();
|
return app.run();
|
||||||
|
@ -19,11 +19,6 @@ static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
|
|||||||
|
|
||||||
static OwnedPtr<os::IPC::Client> launcher_client;
|
static OwnedPtr<os::IPC::Client> launcher_client;
|
||||||
|
|
||||||
void sigchld_handler(int)
|
|
||||||
{
|
|
||||||
wait(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sigquit_handler(int)
|
void sigquit_handler(int)
|
||||||
{
|
{
|
||||||
// Reload the taskbar by exec-ing the executable, resetting everything.
|
// Reload the taskbar by exec-ing the executable, resetting everything.
|
||||||
@ -117,11 +112,11 @@ Result<int> luna_main(int, char**)
|
|||||||
{
|
{
|
||||||
ui::App app;
|
ui::App app;
|
||||||
TRY(app.init("/tmp/wsys.sock"));
|
TRY(app.init("/tmp/wsys.sock"));
|
||||||
|
app.pledge(ui::Pledge::ExtendedLayers);
|
||||||
|
|
||||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
|
||||||
TRY(os::EventLoop::the().register_signal_handler(SIGQUIT, sigquit_handler));
|
TRY(os::EventLoop::the().register_signal_handler(SIGQUIT, sigquit_handler));
|
||||||
|
|
||||||
launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
launcher_client = TRY(os::IPC::Client::connect("/tmp/execd.sock", false));
|
||||||
|
|
||||||
ui::Rect screen = app.screen_rect();
|
ui::Rect screen = app.screen_rect();
|
||||||
|
|
||||||
@ -131,7 +126,8 @@ Result<int> luna_main(int, char**)
|
|||||||
app.set_main_window(window);
|
app.set_main_window(window);
|
||||||
|
|
||||||
window->set_background(TASKBAR_COLOR);
|
window->set_background(TASKBAR_COLOR);
|
||||||
window->set_special_attributes(ui::UNFOCUSEABLE);
|
window->set_layer(ui::Layer::Background);
|
||||||
|
app.pledge(0);
|
||||||
|
|
||||||
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
||||||
window->set_main_widget(layout);
|
window->set_main_widget(layout);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @file launch.cpp
|
* @file execd.cpp
|
||||||
* @author apio (cloudapio.eu)
|
* @author apio (cloudapio.eu)
|
||||||
* @brief Background process that handles detached launching of apps.
|
* @brief Background process that handles detached launching of apps.
|
||||||
*
|
*
|
||||||
@ -42,24 +42,24 @@ void handle_ipc_message(os::IPC::ClientConnection& client, u8 id, void*)
|
|||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case os::Launcher::LAUNCH_DETACHED_ID: handle_launch_detached_message(client); break;
|
case os::Launcher::LAUNCH_DETACHED_ID: handle_launch_detached_message(client); break;
|
||||||
default: os::eprintln("launch: Invalid IPC message from client!"); return;
|
default: os::eprintln("execd: Invalid IPC message from client!"); return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sigchld_handler(int)
|
void sigchld_handler(int)
|
||||||
{
|
{
|
||||||
os::Process::wait(os::Process::ANY_CHILD, nullptr);
|
os::Process::wait(os::Process::ANY_CHILD, nullptr, WNOHANG);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
TRY(os::Security::pledge("stdio wpath cpath unix proc exec", NULL));
|
TRY(os::Security::pledge("stdio wpath cpath unix proc exec", NULL));
|
||||||
|
|
||||||
StringView socket_path = "/tmp/launch.sock";
|
StringView socket_path = "/tmp/execd.sock";
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Background process that handles detached launching of apps."_sv);
|
parser.add_description("Background process that handles detached launching of apps."_sv);
|
||||||
parser.add_system_program_info("launch"_sv);
|
parser.add_system_program_info("execd"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
signal(SIGCHLD, sigchld_handler);
|
signal(SIGCHLD, sigchld_handler);
|
||||||
@ -87,7 +87,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
if (fds[0].revents & POLLIN)
|
if (fds[0].revents & POLLIN)
|
||||||
{
|
{
|
||||||
auto client = TRY(server->accept());
|
auto client = TRY(server->accept());
|
||||||
os::println("launch: New client connected!");
|
os::println("execd: New client connected!");
|
||||||
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
|
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
|
||||||
|
|
||||||
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
||||||
@ -99,7 +99,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
if (fds[i + 1].revents & POLLIN) clients[i]->check_for_messages();
|
if (fds[i + 1].revents & POLLIN) clients[i]->check_for_messages();
|
||||||
if (fds[i + 1].revents & POLLHUP)
|
if (fds[i + 1].revents & POLLHUP)
|
||||||
{
|
{
|
||||||
os::println("launch: Client %zu disconnected", i);
|
os::println("execd: Client %zu disconnected", i);
|
||||||
fds.remove_at(i + 1);
|
fds.remove_at(i + 1);
|
||||||
auto client = clients.remove_at(i);
|
auto client = clients.remove_at(i);
|
||||||
client->disconnect();
|
client->disconnect();
|
@ -19,6 +19,7 @@ set(SOURCES
|
|||||||
src/Label.cpp
|
src/Label.cpp
|
||||||
src/InputField.cpp
|
src/InputField.cpp
|
||||||
src/TextInput.cpp
|
src/TextInput.cpp
|
||||||
|
src/Dialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(ui ${SOURCES})
|
add_library(ui ${SOURCES})
|
||||||
|
@ -49,6 +49,8 @@ namespace ui
|
|||||||
return m_main_window;
|
return m_main_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pledge(i16 pledges);
|
||||||
|
|
||||||
Result<void> register_window(OwnedPtr<Window>&& window, Badge<Window>);
|
Result<void> register_window(OwnedPtr<Window>&& window, Badge<Window>);
|
||||||
void unregister_window(Window* window, Badge<Window>);
|
void unregister_window(Window* window, Badge<Window>);
|
||||||
|
|
||||||
@ -61,6 +63,7 @@ namespace ui
|
|||||||
HashMap<int, OwnedPtr<Window>> m_windows;
|
HashMap<int, OwnedPtr<Window>> m_windows;
|
||||||
bool m_should_close { false };
|
bool m_should_close { false };
|
||||||
os::EventLoop m_loop;
|
os::EventLoop m_loop;
|
||||||
|
Vector<int> m_window_clear_queue;
|
||||||
|
|
||||||
bool process_events();
|
bool process_events();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <os/Action.h>
|
#include <luna/Action.h>
|
||||||
#include <ui/Widget.h>
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
@ -19,7 +19,7 @@ namespace ui
|
|||||||
Button(Rect rect);
|
Button(Rect rect);
|
||||||
|
|
||||||
void set_widget(Widget& widget);
|
void set_widget(Widget& widget);
|
||||||
void set_action(os::Action&& action);
|
void set_action(Action&& action);
|
||||||
|
|
||||||
Result<EventResult> handle_mouse_move(Point position) override;
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
Result<EventResult> handle_mouse_leave() override;
|
Result<EventResult> handle_mouse_leave() override;
|
||||||
@ -32,6 +32,6 @@ namespace ui
|
|||||||
bool m_hovered { false };
|
bool m_hovered { false };
|
||||||
bool m_clicked { false };
|
bool m_clicked { false };
|
||||||
Widget* m_child;
|
Widget* m_child;
|
||||||
os::Action m_action;
|
Action m_action;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
22
gui/libui/include/ui/Dialog.h
Normal file
22
gui/libui/include/ui/Dialog.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @file Window.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UI window dialogs.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Action.h>
|
||||||
|
#include <ui/Window.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
namespace Dialog
|
||||||
|
{
|
||||||
|
Result<void> show_message(StringView title, StringView message);
|
||||||
|
|
||||||
|
Result<void> show_input_dialog(StringView title, StringView message, Function<StringView> callback);
|
||||||
|
}
|
||||||
|
}
|
@ -8,13 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <os/Action.h>
|
#include <luna/Action.h>
|
||||||
#include <ui/Font.h>
|
#include <ui/Font.h>
|
||||||
#include <ui/TextInput.h>
|
#include <ui/TextInput.h>
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
class InputField : public ui::TextInput
|
class InputField final : public ui::TextInput
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputField(SharedPtr<ui::Font> font);
|
InputField(SharedPtr<ui::Font> font);
|
||||||
@ -27,7 +27,7 @@ namespace ui
|
|||||||
|
|
||||||
StringView data();
|
StringView data();
|
||||||
|
|
||||||
void on_submit(os::Function<StringView>&& action)
|
void on_submit(Function<StringView>&& action)
|
||||||
{
|
{
|
||||||
m_on_submit_action = move(action);
|
m_on_submit_action = move(action);
|
||||||
m_has_on_submit_action = true;
|
m_has_on_submit_action = true;
|
||||||
@ -36,7 +36,7 @@ namespace ui
|
|||||||
private:
|
private:
|
||||||
SharedPtr<ui::Font> m_font;
|
SharedPtr<ui::Font> m_font;
|
||||||
|
|
||||||
os::Function<StringView> m_on_submit_action;
|
Function<StringView> m_on_submit_action;
|
||||||
bool m_has_on_submit_action { false };
|
bool m_has_on_submit_action { false };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ namespace ui
|
|||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void set_special_attributes(WindowAttributes attributes);
|
void set_layer(Layer layer);
|
||||||
|
|
||||||
Result<void> draw();
|
Result<void> draw();
|
||||||
Result<ui::EventResult> handle_mouse_leave();
|
Result<ui::EventResult> handle_mouse_leave();
|
||||||
@ -73,13 +73,19 @@ namespace ui
|
|||||||
Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons);
|
Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons);
|
||||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request);
|
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request);
|
||||||
|
|
||||||
Result<void> add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, os::Function<ui::Shortcut>&& action);
|
Result<void> add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, Function<ui::Shortcut>&& action);
|
||||||
|
|
||||||
int id() const
|
int id() const
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_close(Action&& action)
|
||||||
|
{
|
||||||
|
m_on_close_action = move(action);
|
||||||
|
m_has_on_close_action = true;
|
||||||
|
}
|
||||||
|
|
||||||
~Window();
|
~Window();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -93,10 +99,13 @@ namespace ui
|
|||||||
Option<int> m_old_mouse_buttons;
|
Option<int> m_old_mouse_buttons;
|
||||||
bool m_decorated { false };
|
bool m_decorated { false };
|
||||||
|
|
||||||
|
Action m_on_close_action;
|
||||||
|
bool m_has_on_close_action { false };
|
||||||
|
|
||||||
struct ShortcutAction
|
struct ShortcutAction
|
||||||
{
|
{
|
||||||
bool intercept;
|
bool intercept;
|
||||||
os::Function<Shortcut> action;
|
Function<Shortcut> action;
|
||||||
};
|
};
|
||||||
|
|
||||||
HashMap<Shortcut, ShortcutAction> m_shortcuts;
|
HashMap<Shortcut, ShortcutAction> m_shortcuts;
|
||||||
|
@ -25,7 +25,8 @@ namespace ui
|
|||||||
CLOSE_WINDOW_ID,
|
CLOSE_WINDOW_ID,
|
||||||
GET_SCREEN_RECT_ID,
|
GET_SCREEN_RECT_ID,
|
||||||
SET_TITLEBAR_HEIGHT_ID,
|
SET_TITLEBAR_HEIGHT_ID,
|
||||||
SET_SPECIAL_WINDOW_ATTRIBUTES_ID,
|
SET_WINDOW_LAYER_ID,
|
||||||
|
UPDATE_PLEDGE_REQUEST_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreateWindowRequest
|
struct CreateWindowRequest
|
||||||
@ -81,16 +82,32 @@ namespace ui
|
|||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WindowAttributes : u8
|
enum Layer : u8
|
||||||
{
|
{
|
||||||
UNFOCUSEABLE = 1,
|
Background,
|
||||||
|
Global,
|
||||||
|
GlobalTop,
|
||||||
|
System,
|
||||||
|
Lock
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SetSpecialWindowAttributesRequest
|
struct SetWindowLayer
|
||||||
{
|
{
|
||||||
static constexpr u8 ID = SET_SPECIAL_WINDOW_ATTRIBUTES_ID;
|
static constexpr u8 ID = SET_WINDOW_LAYER_ID;
|
||||||
|
|
||||||
int window;
|
int window;
|
||||||
WindowAttributes attributes;
|
Layer layer;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Pledge : i16
|
||||||
|
{
|
||||||
|
ExtendedLayers = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpdatePledgeRequest
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = UPDATE_PLEDGE_REQUEST_ID;
|
||||||
|
|
||||||
|
i16 pledges;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace ui
|
|||||||
void App::unregister_window(Window* window, Badge<Window>)
|
void App::unregister_window(Window* window, Badge<Window>)
|
||||||
{
|
{
|
||||||
int id = window->id();
|
int id = window->id();
|
||||||
check(m_windows.try_remove(id));
|
m_window_clear_queue.try_append(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window* App::find_window(int id)
|
Window* App::find_window(int id)
|
||||||
@ -124,6 +124,22 @@ namespace ui
|
|||||||
{
|
{
|
||||||
check(m_main_window);
|
check(m_main_window);
|
||||||
m_client->check_for_messages().release_value();
|
m_client->check_for_messages().release_value();
|
||||||
|
for (int id : m_window_clear_queue)
|
||||||
|
{
|
||||||
|
check(m_windows.try_remove(id));
|
||||||
|
|
||||||
|
ui::CloseWindowRequest request;
|
||||||
|
request.window = id;
|
||||||
|
client().send_async(request);
|
||||||
|
}
|
||||||
|
m_window_clear_queue.clear_data();
|
||||||
return !m_should_close;
|
return !m_should_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::pledge(i16 pledges)
|
||||||
|
{
|
||||||
|
ui::UpdatePledgeRequest request;
|
||||||
|
request.pledges = pledges;
|
||||||
|
client().send_async(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace ui
|
|||||||
widget.set_parent(this);
|
widget.set_parent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::set_action(os::Action&& action)
|
void Button::set_action(Action&& action)
|
||||||
{
|
{
|
||||||
m_action = move(action);
|
m_action = move(action);
|
||||||
}
|
}
|
||||||
|
82
gui/libui/src/Dialog.cpp
Normal file
82
gui/libui/src/Dialog.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* @file Dialog.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UI window dialogs.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <luna/Alloc.h>
|
||||||
|
#include <ui/App.h>
|
||||||
|
#include <ui/Dialog.h>
|
||||||
|
#include <ui/InputField.h>
|
||||||
|
#include <ui/Label.h>
|
||||||
|
#include <ui/Layout.h>
|
||||||
|
|
||||||
|
namespace ui::Dialog
|
||||||
|
{
|
||||||
|
Result<void> show_message(StringView title, StringView message)
|
||||||
|
{
|
||||||
|
auto rect = ui::App::the().main_window()->canvas().rect();
|
||||||
|
int text_length = (int)message.length() * ui::Font::default_font()->width();
|
||||||
|
int text_height = ui::Font::default_font()->height();
|
||||||
|
|
||||||
|
ui::Rect dialog_rect = { 0, 0, text_length + 20, text_height + 20 };
|
||||||
|
|
||||||
|
auto* dialog = TRY(ui::Window::create(
|
||||||
|
ui::align(rect, dialog_rect, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)));
|
||||||
|
|
||||||
|
dialog->set_background(ui::GRAY);
|
||||||
|
dialog->set_title(title);
|
||||||
|
|
||||||
|
ui::Label* text = TRY(make<ui::Label>(message));
|
||||||
|
text->set_color(ui::BLACK);
|
||||||
|
dialog->set_main_widget(*text);
|
||||||
|
|
||||||
|
dialog->on_close([text] { delete text; });
|
||||||
|
|
||||||
|
dialog->draw();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> show_input_dialog(StringView title, StringView message, Function<StringView> callback)
|
||||||
|
{
|
||||||
|
auto rect = ui::App::the().main_window()->canvas().rect();
|
||||||
|
int text_length = (int)message.length() * ui::Font::default_font()->width();
|
||||||
|
int text_height = ui::Font::default_font()->height();
|
||||||
|
|
||||||
|
ui::Rect dialog_rect = { 0, 0, max(text_length + 20, 300), text_height * 2 + 30 };
|
||||||
|
|
||||||
|
auto* dialog = TRY(ui::Window::create(
|
||||||
|
ui::align(rect, dialog_rect, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)));
|
||||||
|
|
||||||
|
dialog->set_background(ui::GRAY);
|
||||||
|
dialog->set_title(title);
|
||||||
|
|
||||||
|
ui::VerticalLayout* layout = TRY(make<ui::VerticalLayout>());
|
||||||
|
dialog->set_main_widget(*layout);
|
||||||
|
|
||||||
|
ui::Label* text = TRY(make<ui::Label>((message)));
|
||||||
|
text->set_color(ui::BLACK);
|
||||||
|
layout->add_widget(*text);
|
||||||
|
|
||||||
|
ui::InputField* input = TRY(make<ui::InputField>(ui::Font::default_font()));
|
||||||
|
input->on_submit([dialog, callback](StringView s) {
|
||||||
|
callback(s);
|
||||||
|
dialog->close();
|
||||||
|
});
|
||||||
|
layout->add_widget(*input);
|
||||||
|
|
||||||
|
dialog->on_close([layout, text, input] {
|
||||||
|
delete text;
|
||||||
|
delete input;
|
||||||
|
delete layout;
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog->draw();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
@ -115,7 +115,7 @@ namespace ui
|
|||||||
|
|
||||||
StringView InputField::data()
|
StringView InputField::data()
|
||||||
{
|
{
|
||||||
if (!m_data.size()) return StringView {};
|
if (m_data.size() < 2) return StringView {};
|
||||||
return StringView { (const char*)m_data.data(), m_data.size() };
|
return StringView { (const char*)m_data.data(), m_data.size() - 1 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ namespace ui
|
|||||||
Window::~Window()
|
Window::~Window()
|
||||||
{
|
{
|
||||||
if (m_canvas.ptr) munmap(m_canvas.ptr, ((usize)m_canvas.width) * ((usize)m_canvas.height) * 4);
|
if (m_canvas.ptr) munmap(m_canvas.ptr, ((usize)m_canvas.width) * ((usize)m_canvas.height) * 4);
|
||||||
|
|
||||||
|
if (m_has_on_close_action) m_on_close_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::set_title(StringView title)
|
void Window::set_title(StringView title)
|
||||||
@ -108,20 +110,16 @@ namespace ui
|
|||||||
{
|
{
|
||||||
App& app = App::the();
|
App& app = App::the();
|
||||||
|
|
||||||
ui::CloseWindowRequest request;
|
|
||||||
request.window = m_id;
|
|
||||||
app.client().send_async(request);
|
|
||||||
|
|
||||||
if (this == app.main_window()) app.set_should_close(true);
|
if (this == app.main_window()) app.set_should_close(true);
|
||||||
|
|
||||||
app.unregister_window(this, {});
|
app.unregister_window(this, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::set_special_attributes(WindowAttributes attributes)
|
void Window::set_layer(Layer layer)
|
||||||
{
|
{
|
||||||
ui::SetSpecialWindowAttributesRequest request;
|
ui::SetWindowLayer request;
|
||||||
request.window = m_id;
|
request.window = m_id;
|
||||||
request.attributes = attributes;
|
request.layer = layer;
|
||||||
App::the().client().send_async(request);
|
App::the().client().send_async(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +224,7 @@ namespace ui
|
|||||||
return m_main_widget->handle_key_event(request);
|
return m_main_widget->handle_key_event(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> Window::add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept,
|
Result<void> Window::add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, Function<ui::Shortcut>&& action)
|
||||||
os::Function<ui::Shortcut>&& action)
|
|
||||||
{
|
{
|
||||||
TRY(m_shortcuts.try_set(shortcut, { intercept, move(action) }));
|
TRY(m_shortcuts.try_set(shortcut, { intercept, move(action) }));
|
||||||
|
|
||||||
|
@ -7,12 +7,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <luna/String.h>
|
#include <luna/RefString.h>
|
||||||
|
#include <luna/SHA.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/Config.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
#include <os/IPC.h>
|
#include <os/IPC.h>
|
||||||
#include <os/Process.h>
|
#include <os/Process.h>
|
||||||
|
#include <os/Security.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <shadow.h>
|
#include <shadow.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -31,18 +34,19 @@ enum Stage
|
|||||||
|
|
||||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||||
|
|
||||||
|
Result<String> hash_password(StringView& view)
|
||||||
|
{
|
||||||
|
SHA256 sha;
|
||||||
|
sha.append((const u8*)view.chars(), view.length());
|
||||||
|
auto digest = TRY(sha.digest());
|
||||||
|
return digest.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
StringView username;
|
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("Login prompt for a graphical UI session.");
|
parser.add_description("Login prompt for a graphical UI session.");
|
||||||
parser.add_system_program_info("loginui"_sv);
|
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);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
if (geteuid() != 0)
|
if (geteuid() != 0)
|
||||||
@ -51,6 +55,8 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRY(os::Security::pledge("stdio rpath wpath unix proc exec id", nullptr));
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
bool success = os::IPC::Notifier::run_and_wait(
|
bool success = os::IPC::Notifier::run_and_wait(
|
||||||
@ -66,14 +72,21 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto config = TRY(os::ConfigFile::open("/etc/loginui.conf"));
|
||||||
|
|
||||||
|
if (config->read_boolean_or("Autologin", false))
|
||||||
|
{
|
||||||
|
StringView username = config->read_string_or("AutologinUser", "");
|
||||||
|
|
||||||
if (!username.is_empty())
|
if (!username.is_empty())
|
||||||
{
|
{
|
||||||
auto flag = String::format("--user=%s"_sv, username.chars()).release_value();
|
auto flag = RefString::format("--user=%s"_sv, username.chars()).release_value();
|
||||||
|
|
||||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ui::App app;
|
ui::App app;
|
||||||
TRY(app.init());
|
TRY(app.init());
|
||||||
@ -117,7 +130,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
stage = Stage::PasswordInput;
|
stage = Stage::PasswordInput;
|
||||||
label.set_text("Password:");
|
label.set_text("Password:");
|
||||||
|
|
||||||
String title = String::format("Log in: %s"_sv, data.chars()).release_value();
|
RefString title = RefString::format("Log in: %s"_sv, data.chars()).release_value();
|
||||||
window->set_title(title.view());
|
window->set_title(title.view());
|
||||||
|
|
||||||
input.clear();
|
input.clear();
|
||||||
@ -152,14 +165,16 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(data.chars(), passwd))
|
auto result = hash_password(data).release_value();
|
||||||
|
|
||||||
|
if (strcmp(result.chars(), passwd))
|
||||||
{
|
{
|
||||||
error.set_text("Incorrect password.");
|
error.set_text("Incorrect password.");
|
||||||
input.clear();
|
input.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto flag = String::format("--user=%s"_sv, pw->pw_name).release_value();
|
auto flag = RefString::format("--user=%s"_sv, pw->pw_name).release_value();
|
||||||
|
|
||||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||||
|
@ -22,7 +22,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
parser.add_positional_argument(program, "program", true);
|
parser.add_positional_argument(program, "program", true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
OwnedPtr<os::IPC::Client> launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
OwnedPtr<os::IPC::Client> launcher_client = TRY(os::IPC::Client::connect("/tmp/execd.sock", false));
|
||||||
|
|
||||||
os::println("Requesting to start program '%s'...", program.chars());
|
os::println("Requesting to start program '%s'...", program.chars());
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ set(SOURCES
|
|||||||
Keyboard.cpp
|
Keyboard.cpp
|
||||||
Keyboard.h
|
Keyboard.h
|
||||||
Client.h
|
Client.h
|
||||||
|
Client.cpp
|
||||||
|
Layer.cpp
|
||||||
|
Layer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(wind ${SOURCES})
|
add_executable(wind ${SOURCES})
|
||||||
|
52
gui/wind/Client.cpp
Normal file
52
gui/wind/Client.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "Client.h"
|
||||||
|
#include <os/File.h>
|
||||||
|
|
||||||
|
Client::Client(OwnedPtr<os::IPC::ClientConnection>&& client, i16 _pledges)
|
||||||
|
: conn(move(client)), windows(), pledges(_pledges)
|
||||||
|
{
|
||||||
|
conn->set_message_handler(wind::handle_ipc_message, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::update_pledges(i16 _pledges)
|
||||||
|
{
|
||||||
|
if (_pledges < 0)
|
||||||
|
{
|
||||||
|
os::eprintln("wind: Client trying to set an invalid pledge, disconnecting!");
|
||||||
|
should_be_disconnected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pledges < 0)
|
||||||
|
{
|
||||||
|
pledges = _pledges;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pledges & ~pledges)
|
||||||
|
{
|
||||||
|
os::eprintln("wind: Client trying to add pledges, disconnecting!");
|
||||||
|
should_be_disconnected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pledges = _pledges;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::check_pledge(i16 pledge)
|
||||||
|
{
|
||||||
|
check(pledge > 0);
|
||||||
|
|
||||||
|
if (pledges < 0)
|
||||||
|
{
|
||||||
|
os::eprintln("wind: Client trying to use pledge-protected functions before pledging anything, disconnecting!");
|
||||||
|
should_be_disconnected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pledges & pledge) == pledge) return true;
|
||||||
|
|
||||||
|
os::eprintln("wind: Client trying to use a function they haven't pledged, disconnecting!");
|
||||||
|
should_be_disconnected = true;
|
||||||
|
return false;
|
||||||
|
}
|
@ -3,20 +3,19 @@
|
|||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include <os/IPC.h>
|
#include <os/IPC.h>
|
||||||
|
|
||||||
|
constexpr i16 HAS_NOT_YET_PLEDGED = -1;
|
||||||
|
constexpr i16 EMPTY_PLEDGE = 0;
|
||||||
|
|
||||||
struct Client
|
struct Client
|
||||||
{
|
{
|
||||||
OwnedPtr<os::IPC::ClientConnection> conn;
|
OwnedPtr<os::IPC::ClientConnection> conn;
|
||||||
Vector<Window*> windows;
|
Vector<Window*> windows;
|
||||||
const bool privileged { false };
|
const bool privileged { false };
|
||||||
bool should_be_disconnected { false };
|
bool should_be_disconnected { false };
|
||||||
|
i16 pledges = 0;
|
||||||
|
|
||||||
Client(OwnedPtr<os::IPC::ClientConnection>&& client, bool priv)
|
bool update_pledges(i16 pledges);
|
||||||
#ifdef CLIENT_IMPLEMENTATION
|
bool check_pledge(i16 pledge);
|
||||||
: conn(move(client)), windows(), privileged(priv)
|
|
||||||
{
|
Client(OwnedPtr<os::IPC::ClientConnection>&& client, i16 pledges);
|
||||||
conn->set_message_handler(wind::handle_ipc_message, this);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#include "IPC.h"
|
#include "IPC.h"
|
||||||
|
#include "Layer.h"
|
||||||
#include "Mouse.h"
|
#include "Mouse.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/String.h>
|
#include <luna/RefString.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/SharedMemory.h>
|
#include <os/SharedMemory.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
@ -35,9 +36,9 @@ static Result<void> handle_create_window_message(Client& client)
|
|||||||
|
|
||||||
request.rect = request.rect.normalized();
|
request.rect = request.rect.normalized();
|
||||||
|
|
||||||
auto name = TRY_OR_IPC_ERROR(String::from_cstring("Window"));
|
auto name = TRY_OR_IPC_ERROR(RefString::from_cstring("Window"));
|
||||||
|
|
||||||
auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn->fd(), time(NULL)));
|
auto shm_path = TRY_OR_IPC_ERROR(RefString::format("/wind-shm-%d-%lu"_sv, client.conn->fd(), time(NULL)));
|
||||||
|
|
||||||
auto* window = new (std::nothrow) Window(request.rect, move(name));
|
auto* window = new (std::nothrow) Window(request.rect, move(name));
|
||||||
if (!window)
|
if (!window)
|
||||||
@ -47,7 +48,7 @@ static Result<void> handle_create_window_message(Client& client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto guard = make_scope_guard([window] {
|
auto guard = make_scope_guard([window] {
|
||||||
g_windows.remove(window);
|
window->layer->windows.remove(window);
|
||||||
delete window;
|
delete window;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ static Result<void> handle_set_window_title_message(Client& client)
|
|||||||
ui::SetWindowTitleRequest request;
|
ui::SetWindowTitleRequest request;
|
||||||
if (!TRY(client.conn->read_message(request))) return {};
|
if (!TRY(client.conn->read_message(request))) return {};
|
||||||
|
|
||||||
auto name = COPY_IPC_STRING(request.title);
|
auto name = TRY(RefString::from_string(COPY_IPC_STRING(request.title)));
|
||||||
|
|
||||||
os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window);
|
os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window);
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ static Result<void> handle_close_window_message(Client& client)
|
|||||||
|
|
||||||
auto* window = client.windows[request.window];
|
auto* window = client.windows[request.window];
|
||||||
client.windows[request.window] = nullptr;
|
client.windows[request.window] = nullptr;
|
||||||
g_windows.remove(window);
|
window->layer->windows.remove(window);
|
||||||
Mouse::the().window_did_close(window);
|
Mouse::the().window_did_close(window);
|
||||||
delete window;
|
delete window;
|
||||||
|
|
||||||
@ -160,22 +161,48 @@ static Result<void> handle_set_titlebar_height_message(Client& client)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> handle_set_special_window_attributes_message(Client& client)
|
static Result<void> handle_set_window_layer_message(Client& client)
|
||||||
{
|
{
|
||||||
ui::SetSpecialWindowAttributesRequest request;
|
ui::SetWindowLayer request;
|
||||||
if (!TRY(client.conn->read_message(request))) return {};
|
if (!TRY(client.conn->read_message(request))) return {};
|
||||||
|
|
||||||
if (!client.privileged)
|
if (request.layer != ui::Layer::Global && request.layer != ui::Layer::GlobalTop)
|
||||||
{
|
{
|
||||||
os::eprintln(
|
if (!client.check_pledge(ui::Pledge::ExtendedLayers)) return {};
|
||||||
"wind: Unprivileged client trying to call privileged request (SetSpecialWindowAttributes), disconnecting!");
|
}
|
||||||
|
|
||||||
|
CHECK_WINDOW_ID(request, "SetWindowLayer");
|
||||||
|
|
||||||
|
auto* window = client.windows[request.window];
|
||||||
|
|
||||||
|
window->layer->windows.remove(window);
|
||||||
|
|
||||||
|
switch (request.layer)
|
||||||
|
{
|
||||||
|
case ui::Layer::Background: window->layer = &l_background; break;
|
||||||
|
case ui::Layer::Global: window->layer = &l_global; break;
|
||||||
|
case ui::Layer::GlobalTop: window->layer = &l_global_top; break;
|
||||||
|
case ui::Layer::System: window->layer = &l_system; break;
|
||||||
|
case ui::Layer::Lock: window->layer = &l_lock; break;
|
||||||
|
default: {
|
||||||
|
window->layer->windows.append(window);
|
||||||
|
os::eprintln("wind: Client trying to set window layer to an invalid layer, disconnecting!");
|
||||||
client.should_be_disconnected = true;
|
client.should_be_disconnected = true;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_WINDOW_ID(request, "SetSpecialWindowAttributes");
|
window->layer->windows.append(window);
|
||||||
|
|
||||||
client.windows[request.window]->attributes = request.attributes;
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result<void> handle_update_pledge_request_message(Client& client)
|
||||||
|
{
|
||||||
|
ui::UpdatePledgeRequest request;
|
||||||
|
if (!TRY(client.conn->read_message(request))) return {};
|
||||||
|
|
||||||
|
client.update_pledges(request.pledges); // update_pledges does all the checking.
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -194,7 +221,8 @@ namespace wind
|
|||||||
case ui::CLOSE_WINDOW_ID: handle_close_window_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::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_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;
|
case ui::SET_WINDOW_LAYER_ID: handle_set_window_layer_message(client); break;
|
||||||
|
case ui::UPDATE_PLEDGE_REQUEST_ID: handle_update_pledge_request_message(client); break;
|
||||||
default: os::eprintln("wind: Invalid IPC message from client!"); return;
|
default: os::eprintln("wind: Invalid IPC message from client!"); return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
gui/wind/Layer.cpp
Normal file
79
gui/wind/Layer.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "Layer.h"
|
||||||
|
#include "Client.h"
|
||||||
|
#include "Window.h"
|
||||||
|
|
||||||
|
Layer l_background;
|
||||||
|
Layer l_global;
|
||||||
|
Layer l_global_top;
|
||||||
|
Layer l_system;
|
||||||
|
Layer l_lock;
|
||||||
|
|
||||||
|
constexpr int NUM_LAYERS = 5;
|
||||||
|
|
||||||
|
static Layer* const layers_front_to_back[NUM_LAYERS] = { &l_lock, &l_system, &l_global_top, &l_global, &l_background };
|
||||||
|
static Layer* const layers_back_to_front[NUM_LAYERS] = { &l_background, &l_global, &l_global_top, &l_system, &l_lock };
|
||||||
|
|
||||||
|
Window* Layer::focused_window()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_LAYERS; i++)
|
||||||
|
{
|
||||||
|
Layer* l = layers_front_to_back[i];
|
||||||
|
if (l->windows.last().has_value()) return l->windows.last().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Layer::draw_all_windows(ui::Canvas& canvas)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_LAYERS; i++)
|
||||||
|
{
|
||||||
|
Layer* l = layers_back_to_front[i];
|
||||||
|
for (Window* w : l->windows) { w->draw(canvas); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* Layer::propagate_mouse_event(ui::Point position, u8 buttons)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_LAYERS; i++)
|
||||||
|
{
|
||||||
|
Layer* l = layers_front_to_back[i];
|
||||||
|
for (Window* window = l->windows.last().value_or(nullptr); window;
|
||||||
|
window = l->windows.previous(window).value_or(nullptr))
|
||||||
|
{
|
||||||
|
if (window->surface.contains(position))
|
||||||
|
{
|
||||||
|
ui::MouseEventRequest request;
|
||||||
|
request.window = window->id;
|
||||||
|
request.position = window->surface.relative(position);
|
||||||
|
request.buttons = buttons;
|
||||||
|
window->client->conn->send_async(request);
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* Layer::propagate_drag_event(ui::Point position)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_LAYERS; i++)
|
||||||
|
{
|
||||||
|
Layer* l = layers_front_to_back[i];
|
||||||
|
for (Window* window = l->windows.last().value_or(nullptr); window;
|
||||||
|
window = l->windows.previous(window).value_or(nullptr))
|
||||||
|
{
|
||||||
|
if (window->surface.contains(position))
|
||||||
|
{
|
||||||
|
window->focus();
|
||||||
|
|
||||||
|
if (window->surface.absolute(window->titlebar).contains(position)) return window;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
20
gui/wind/Layer.h
Normal file
20
gui/wind/Layer.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Window.h"
|
||||||
|
#include <luna/LinkedList.h>
|
||||||
|
#include <ui/Canvas.h>
|
||||||
|
|
||||||
|
struct Layer
|
||||||
|
{
|
||||||
|
LinkedList<Window> windows;
|
||||||
|
|
||||||
|
static Window* focused_window();
|
||||||
|
static void draw_all_windows(ui::Canvas& canvas);
|
||||||
|
static Window* propagate_mouse_event(ui::Point position, u8 buttons);
|
||||||
|
static Window* propagate_drag_event(ui::Point position);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Layer l_background;
|
||||||
|
extern Layer l_global;
|
||||||
|
extern Layer l_global_top;
|
||||||
|
extern Layer l_system;
|
||||||
|
extern Layer l_lock;
|
@ -1,5 +1,6 @@
|
|||||||
#include "Mouse.h"
|
#include "Mouse.h"
|
||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
|
#include "Layer.h"
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/IPC.h>
|
#include <os/IPC.h>
|
||||||
#include <ui/Image.h>
|
#include <ui/Image.h>
|
||||||
@ -57,45 +58,17 @@ void Mouse::update(const moon::MousePacket& packet)
|
|||||||
|
|
||||||
else if ((packet.buttons & moon::MouseButton::Left) && !m_dragging_window)
|
else if ((packet.buttons & moon::MouseButton::Left) && !m_dragging_window)
|
||||||
{
|
{
|
||||||
// Iterate from the end of the list, since windows at the beginning are stacked at the bottom and windows at the
|
if (auto* window = Layer::propagate_drag_event(m_position))
|
||||||
// top are at the end.
|
|
||||||
for (Window* window = g_windows.last().value_or(nullptr); window;
|
|
||||||
window = g_windows.previous(window).value_or(nullptr))
|
|
||||||
{
|
|
||||||
if (window->surface.contains(m_position))
|
|
||||||
{
|
|
||||||
if (!(window->attributes & ui::UNFOCUSEABLE)) window->focus();
|
|
||||||
|
|
||||||
if (window->surface.absolute(window->titlebar).contains(m_position))
|
|
||||||
{
|
{
|
||||||
m_dragging_window = window;
|
m_dragging_window = window;
|
||||||
m_initial_drag_position = window->surface.relative(m_position);
|
m_initial_drag_position = window->surface.relative(m_position);
|
||||||
os::println("Started drag: window at (%d,%d,%d,%d) with offset (%d,%d)", window->surface.pos.x,
|
os::println("Started drag: window at (%d,%d,%d,%d) with offset (%d,%d)", window->surface.pos.x,
|
||||||
window->surface.pos.y, window->surface.width, window->surface.height,
|
window->surface.pos.y, window->surface.width, window->surface.height, m_initial_drag_position.x,
|
||||||
m_initial_drag_position.x, m_initial_drag_position.y);
|
m_initial_drag_position.y);
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Window* new_active_window = nullptr;
|
Window* new_active_window = Layer::propagate_mouse_event(m_position, packet.buttons);
|
||||||
|
|
||||||
for (Window* window = g_windows.last().value_or(nullptr); window;
|
|
||||||
window = g_windows.previous(window).value_or(nullptr))
|
|
||||||
{
|
|
||||||
if (window->surface.contains(m_position))
|
|
||||||
{
|
|
||||||
ui::MouseEventRequest request;
|
|
||||||
request.window = window->id;
|
|
||||||
request.position = window->surface.relative(m_position);
|
|
||||||
request.buttons = packet.buttons;
|
|
||||||
window->client->conn->send_async(request);
|
|
||||||
new_active_window = window;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_active_window != new_active_window)
|
if (m_active_window != new_active_window)
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "Layer.h"
|
||||||
#include <luna/Utf8.h>
|
#include <luna/Utf8.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <ui/Font.h>
|
#include <ui/Font.h>
|
||||||
#include <ui/Image.h>
|
#include <ui/Image.h>
|
||||||
|
|
||||||
LinkedList<Window> g_windows;
|
|
||||||
|
|
||||||
void Window::draw(ui::Canvas& screen)
|
void Window::draw(ui::Canvas& screen)
|
||||||
{
|
{
|
||||||
dirty = false;
|
dirty = false;
|
||||||
@ -18,15 +17,16 @@ void Window::draw(ui::Canvas& screen)
|
|||||||
void Window::focus()
|
void Window::focus()
|
||||||
{
|
{
|
||||||
// Bring the window to the front of the list.
|
// Bring the window to the front of the list.
|
||||||
g_windows.remove(this);
|
layer->windows.remove(this);
|
||||||
g_windows.append(this);
|
layer->windows.append(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::Window(ui::Rect r, String&& n) : surface(r), name(move(n))
|
Window::Window(ui::Rect r, RefString&& n) : surface(r), name(move(n))
|
||||||
{
|
{
|
||||||
auto font = ui::Font::default_font();
|
auto font = ui::Font::default_font();
|
||||||
titlebar = ui::Rect { 0, 0, 0, 0 };
|
titlebar = ui::Rect { 0, 0, 0, 0 };
|
||||||
g_windows.append(this);
|
l_global.windows.append(this);
|
||||||
|
layer = &l_global;
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::~Window()
|
Window::~Window()
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/LinkedList.h>
|
#include <luna/LinkedList.h>
|
||||||
#include <luna/String.h>
|
#include <luna/RefString.h>
|
||||||
#include <ui/Canvas.h>
|
#include <ui/Canvas.h>
|
||||||
#include <ui/Color.h>
|
#include <ui/Color.h>
|
||||||
#include <ui/Rect.h>
|
#include <ui/Rect.h>
|
||||||
#include <ui/ipc/Server.h>
|
#include <ui/ipc/Server.h>
|
||||||
|
|
||||||
struct Client;
|
struct Client;
|
||||||
|
struct Layer;
|
||||||
|
|
||||||
struct Window : public LinkedListNode<Window>
|
struct Window : public LinkedListNode<Window>
|
||||||
{
|
{
|
||||||
ui::Rect surface;
|
ui::Rect surface;
|
||||||
ui::Rect titlebar;
|
ui::Rect titlebar;
|
||||||
u32* pixels;
|
u32* pixels;
|
||||||
String name;
|
RefString name;
|
||||||
String shm_path;
|
RefString shm_path;
|
||||||
bool dirty { false };
|
bool dirty { false };
|
||||||
Client* client;
|
Client* client;
|
||||||
|
Layer* layer;
|
||||||
int id;
|
int id;
|
||||||
ui::WindowAttributes attributes { 0 };
|
|
||||||
|
|
||||||
Window(ui::Rect, String&&);
|
Window(ui::Rect, RefString&&);
|
||||||
~Window();
|
~Window();
|
||||||
|
|
||||||
void focus();
|
void focus();
|
||||||
|
|
||||||
void draw(ui::Canvas& screen);
|
void draw(ui::Canvas& screen);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern LinkedList<Window> g_windows;
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#define CLIENT_IMPLEMENTATION
|
|
||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "Keyboard.h"
|
#include "Keyboard.h"
|
||||||
|
#include "Layer.h"
|
||||||
#include "Mouse.h"
|
#include "Mouse.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
@ -23,42 +23,11 @@ static constexpr uid_t WIND_USER_ID = 2;
|
|||||||
static constexpr gid_t WIND_GROUP_ID = 2;
|
static constexpr gid_t WIND_GROUP_ID = 2;
|
||||||
static constexpr gid_t WSYS_GROUP_ID = 3;
|
static constexpr gid_t WSYS_GROUP_ID = 3;
|
||||||
|
|
||||||
static void debug(const Vector<OwnedPtr<Client>>& clients)
|
|
||||||
{
|
|
||||||
os::println("--- wind: DEBUG OUTPUT ---");
|
|
||||||
|
|
||||||
os::println("-- wind: Listing clients --");
|
|
||||||
|
|
||||||
for (const auto& client : clients)
|
|
||||||
{
|
|
||||||
os::println("Client with fd %d, owns %zu windows", client->conn->fd(), client->windows.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
os::println("-- wind: Listing windows --");
|
|
||||||
|
|
||||||
for (const auto& window : g_windows)
|
|
||||||
{
|
|
||||||
os::println("Window of client (fd %d), id %d, %sdirty (\"%s\") (%d,%d,%d,%d)", window->client->conn->fd(),
|
|
||||||
window->id, window->dirty ? "" : "not ", window->name.chars(), window->surface.pos.x,
|
|
||||||
window->surface.pos.y, window->surface.width, window->surface.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
os::println("-- wind: Listing processes --");
|
|
||||||
|
|
||||||
system("ps");
|
|
||||||
|
|
||||||
os::println("-- wind: Listing memory usage --");
|
|
||||||
|
|
||||||
system("free -h");
|
|
||||||
|
|
||||||
os::println("--- wind: END DEBUG OUTPUT ---");
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
srand((unsigned)time(NULL));
|
srand((unsigned)time(NULL));
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec tty id", NULL));
|
TRY(os::Security::pledge("stdio rpath wpath cpath unix tty id", NULL));
|
||||||
|
|
||||||
StringView socket_path = "/tmp/wind.sock";
|
StringView socket_path = "/tmp/wind.sock";
|
||||||
StringView system_socket_path = "/tmp/wsys.sock";
|
StringView system_socket_path = "/tmp/wsys.sock";
|
||||||
@ -73,8 +42,8 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
if (geteuid() != 0)
|
if (geteuid() != 0)
|
||||||
{
|
{
|
||||||
os::eprintln("error: wind must be run as root to initialize resources, run with --user=<USERNAME> to drop "
|
os::eprintln("error: wind must be run as root to initialize resources, the server will drop "
|
||||||
"privileges afterwards");
|
"privileges automatically afterwards");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,12 +97,12 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
|
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
|
||||||
TRY(fds.try_append({ .fd = system_server->fd(), .events = POLLIN, .revents = 0 }));
|
TRY(fds.try_append({ .fd = system_server->fd(), .events = POLLIN, .revents = 0 }));
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec", NULL));
|
TRY(os::Security::pledge("stdio rpath wpath cpath unix", NULL));
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
screen.canvas().fill(background);
|
screen.canvas().fill(background);
|
||||||
for (auto* window : g_windows) window->draw(screen.canvas());
|
Layer::draw_all_windows(screen.canvas());
|
||||||
mouse_pointer.draw(screen.canvas());
|
mouse_pointer.draw(screen.canvas());
|
||||||
screen.sync();
|
screen.sync();
|
||||||
|
|
||||||
@ -153,11 +122,9 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
moon::KeyboardPacket packet;
|
moon::KeyboardPacket packet;
|
||||||
TRY(keyboard->read_typed(packet));
|
TRY(keyboard->read_typed(packet));
|
||||||
if (!packet.released && packet.key == moon::K_Tab) debug(clients);
|
|
||||||
auto request = wind::Keyboard::decode_keyboard_event((moon::KeyCode)packet.key, packet.released);
|
auto request = wind::Keyboard::decode_keyboard_event((moon::KeyCode)packet.key, packet.released);
|
||||||
if (g_windows.last().has_value())
|
if (auto* window = Layer::focused_window())
|
||||||
{
|
{
|
||||||
auto* window = g_windows.last().value();
|
|
||||||
request.window = window->id;
|
request.window = window->id;
|
||||||
window->client->conn->send_async(request);
|
window->client->conn->send_async(request);
|
||||||
}
|
}
|
||||||
@ -170,7 +137,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
||||||
|
|
||||||
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), false)));
|
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), EMPTY_PLEDGE)));
|
||||||
TRY(clients.try_append(move(c)));
|
TRY(clients.try_append(move(c)));
|
||||||
}
|
}
|
||||||
if (fds[3].revents & POLLIN)
|
if (fds[3].revents & POLLIN)
|
||||||
@ -181,7 +148,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
auto connection = TRY(os::IPC::ClientConnection::adopt_connection(move(client)));
|
||||||
|
|
||||||
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), true)));
|
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(connection), HAS_NOT_YET_PLEDGED)));
|
||||||
TRY(clients.try_append(move(c)));
|
TRY(clients.try_append(move(c)));
|
||||||
}
|
}
|
||||||
for (usize i = 0; i < clients.size(); i++)
|
for (usize i = 0; i < clients.size(); i++)
|
||||||
@ -198,7 +165,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
if (window)
|
if (window)
|
||||||
{
|
{
|
||||||
g_windows.remove(window);
|
window->layer->windows.remove(window);
|
||||||
mouse_pointer.window_did_close(window);
|
mouse_pointer.window_did_close(window);
|
||||||
delete window;
|
delete window;
|
||||||
}
|
}
|
||||||
|
@ -19,3 +19,6 @@
|
|||||||
# Uncomment the line below to make the kernel also calculate stack traces for userspace addresses on program crashes.
|
# Uncomment the line below to make the kernel also calculate stack traces for userspace addresses on program crashes.
|
||||||
# This can aid in debugging, but makes the kernel more unstable as stack tracing will access arbitrary userspace memory.
|
# This can aid in debugging, but makes the kernel more unstable as stack tracing will access arbitrary userspace memory.
|
||||||
# target_compile_definitions(moon PRIVATE MOON_ENABLE_USERSPACE_STACK_TRACES)
|
# target_compile_definitions(moon PRIVATE MOON_ENABLE_USERSPACE_STACK_TRACES)
|
||||||
|
|
||||||
|
# Uncomment the line below to enable all kernel debug messages, and console logging.
|
||||||
|
# include(debug.cmake)
|
||||||
|
@ -12,4 +12,4 @@ target_compile_definitions(moon PRIVATE DEVICE_REGISTRY_DEBUG)
|
|||||||
target_compile_definitions(moon PRIVATE FORK_DEBUG)
|
target_compile_definitions(moon PRIVATE FORK_DEBUG)
|
||||||
target_compile_definitions(moon PRIVATE MOUNT_DEBUG)
|
target_compile_definitions(moon PRIVATE MOUNT_DEBUG)
|
||||||
target_compile_definitions(moon PRIVATE CACHE_DEBUG)
|
target_compile_definitions(moon PRIVATE CACHE_DEBUG)
|
||||||
target_compile_options(moon PRIVATE -fsanitize=undefined)
|
#target_compile_options(moon PRIVATE -fsanitize=undefined)
|
||||||
|
@ -77,8 +77,7 @@ static void log_text_console(LogLevel level, const char* format, va_list origin)
|
|||||||
TextConsole::set_foreground(WHITE);
|
TextConsole::set_foreground(WHITE);
|
||||||
|
|
||||||
// NOTE: Same as above.
|
// NOTE: Same as above.
|
||||||
auto rc = cstyle_format(
|
auto rc = cstyle_format(format, [](char c, void*) -> Result<void> { return TextConsole::putchar(c); }, nullptr, ap);
|
||||||
format, [](char c, void*) -> Result<void> { return TextConsole::putchar(c); }, nullptr, ap);
|
|
||||||
|
|
||||||
if (rc.has_error()) { TextConsole::wprint(L"Invalid UTF-8 in log message"); }
|
if (rc.has_error()) { TextConsole::wprint(L"Invalid UTF-8 in log message"); }
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "Pledge.h"
|
#include "Pledge.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
|
||||||
static const char* promise_names[] = {
|
static const char* promise_names[] = {
|
||||||
#define __enumerate(promise) #promise,
|
#define __enumerate(promise) #promise,
|
||||||
@ -8,31 +9,35 @@ static const char* promise_names[] = {
|
|||||||
#undef __enumerate
|
#undef __enumerate
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<void> check_pledge(Thread* thread, Promise promise)
|
Result<void> check_pledge(Process* process, Promise promise)
|
||||||
{
|
{
|
||||||
// Thread has not called pledge().
|
// Thread has not called pledge().
|
||||||
if (thread->promises < 0) return {};
|
if (process->promises < 0) return {};
|
||||||
int mask = (1 << (int)promise);
|
int mask = (1 << (int)promise);
|
||||||
if ((thread->promises & mask) != mask)
|
if ((process->promises & mask) != mask)
|
||||||
{
|
{
|
||||||
kerrorln("Pledge violation in thread %d! Has not pledged %s", thread->id, promise_names[(int)promise]);
|
kerrorln("Pledge violation in process %d! Has not pledged %s", process->id, promise_names[(int)promise]);
|
||||||
if (thread->promises & (1 << (int)Promise::p_error)) return err(ENOSYS);
|
if (process->promises & (1 << (int)Promise::p_error)) return err(ENOSYS);
|
||||||
|
|
||||||
// Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the default
|
Scheduler::for_each_thread(process, [](Thread* thread) {
|
||||||
// (dump core). We could just kill the thread here and be done, but that discards anything on the current stack,
|
// Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the
|
||||||
// which means that some destructors might not be called. Instead, leave the job to the next call of
|
// default (dump core). We could just kill the thread here and be done, but that discards anything on the
|
||||||
// Thread::process_pending_signals().
|
// current stack, which means that some destructors might not be called. Instead, leave the job to the next
|
||||||
|
// call of Thread::process_pending_signals().
|
||||||
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
||||||
|
|
||||||
// Unblock SIGABRT.
|
// Unblock SIGABRT.
|
||||||
thread->signal_mask.set(SIGABRT - 1, false);
|
thread->signal_mask.set(SIGABRT - 1, false);
|
||||||
|
|
||||||
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the
|
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting
|
||||||
// thread's pending signals.
|
// the thread's pending signals.
|
||||||
thread->pending_signals.clear();
|
thread->pending_signals.clear();
|
||||||
|
|
||||||
thread->send_signal(SIGABRT);
|
thread->send_signal(SIGABRT);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
// This should never arrive to userspace, unless we're init and have ignored SIGABRT.
|
// This should never arrive to userspace, unless we're init and have ignored SIGABRT.
|
||||||
return err(ENOSYS);
|
return err(ENOSYS);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,6 @@ enum class Promise
|
|||||||
num_promises,
|
num_promises,
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<void> check_pledge(Thread* thread, Promise promise);
|
Result<void> check_pledge(Process* process, Promise promise);
|
||||||
|
|
||||||
Result<int> parse_promises(u64 pledge);
|
Result<int> parse_promises(u64 pledge);
|
||||||
|
@ -91,6 +91,9 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs)
|
|||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
if (current->check_stack_on_exception(regs->rsp)) return;
|
if (current->check_stack_on_exception(regs->rsp)) return;
|
||||||
|
|
||||||
|
auto space = current->process->address_space.lock();
|
||||||
|
(*space)->debug_log();
|
||||||
|
|
||||||
current->send_signal(signo);
|
current->send_signal(signo);
|
||||||
current->process_pending_signals(regs);
|
current->process_pending_signals(regs);
|
||||||
return;
|
return;
|
||||||
|
@ -15,7 +15,7 @@ void Thread::set_ip(u64 ip)
|
|||||||
regs.rip = ip;
|
regs.rip = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Thread::ip()
|
u64 Thread::ip() const
|
||||||
{
|
{
|
||||||
return regs.rip;
|
return regs.rip;
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ void Thread::set_sp(u64 sp)
|
|||||||
regs.rsp = sp;
|
regs.rsp = sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Thread::sp()
|
u64 Thread::sp() const
|
||||||
{
|
{
|
||||||
return regs.rsp;
|
return regs.rsp;
|
||||||
}
|
}
|
||||||
@ -71,31 +71,14 @@ void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
|||||||
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Move this function to a common location (also used in ThreadImage)
|
|
||||||
Result<u64> Thread::push_mem_on_stack(const u8* mem, usize size)
|
Result<u64> Thread::push_mem_on_stack(const u8* mem, usize size)
|
||||||
{
|
{
|
||||||
if ((regs.rsp - size) < stack.bottom()) return err(E2BIG);
|
return MemoryManager::push_mem_on_stack(mem, size, stack, regs.rsp);
|
||||||
|
|
||||||
if (!MemoryManager::validate_user_write((void*)(regs.rsp - size), size)) return err(EFAULT);
|
|
||||||
|
|
||||||
regs.rsp -= size;
|
|
||||||
|
|
||||||
memcpy((void*)regs.rsp, mem, size);
|
|
||||||
|
|
||||||
return regs.rsp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> Thread::pop_mem_from_stack(u8* mem, usize size)
|
Result<u64> Thread::pop_mem_from_stack(u8* mem, usize size)
|
||||||
{
|
{
|
||||||
if ((regs.rsp + size) > stack.top()) return err(E2BIG);
|
return MemoryManager::pop_mem_from_stack(mem, size, stack, regs.rsp);
|
||||||
|
|
||||||
if (!MemoryManager::validate_user_read((void*)regs.rsp, size)) return err(EFAULT);
|
|
||||||
|
|
||||||
memcpy(mem, (void*)regs.rsp, size);
|
|
||||||
|
|
||||||
regs.rsp += size;
|
|
||||||
|
|
||||||
return regs.rsp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::deliver_signal(int signo, Registers* current_regs)
|
bool Thread::deliver_signal(int signo, Registers* current_regs)
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "fs/devices/BlockDevice.h"
|
#include "fs/devices/BlockDevice.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "lib/Mutex.h"
|
#include "lib/Mutex.h"
|
||||||
|
#include "thread/Thread.h"
|
||||||
#include <luna/Atomic.h>
|
#include <luna/Atomic.h>
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
#include <luna/StaticString.h>
|
#include <luna/StaticString.h>
|
||||||
|
@ -35,11 +35,10 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
|
|||||||
if (!m_interpreter_cmdline.size()) return err(ENOEXEC);
|
if (!m_interpreter_cmdline.size()) return err(ENOEXEC);
|
||||||
|
|
||||||
auto& interpreter_path = m_interpreter_cmdline[0];
|
auto& interpreter_path = m_interpreter_cmdline[0];
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, ¤t->extra_groups,
|
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current, current->current_directory, true));
|
||||||
current->current_directory, true));
|
if (!VFS::can_execute(interpreter, current)) return err(EACCES);
|
||||||
if (!VFS::can_execute(interpreter, current->auth, ¤t->extra_groups)) return err(EACCES);
|
|
||||||
|
|
||||||
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
|
||||||
u64 entry = TRY(loader->load(space));
|
u64 entry = TRY(loader->load(space));
|
||||||
|
@ -20,7 +20,7 @@ void InitRD::initialize()
|
|||||||
|
|
||||||
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
|
||||||
{
|
{
|
||||||
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {}, nullptr);
|
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, nullptr);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
if (rc.error() == EEXIST) return {};
|
if (rc.error() == EEXIST) return {};
|
||||||
@ -37,8 +37,7 @@ Result<void> InitRD::populate_vfs()
|
|||||||
{
|
{
|
||||||
if (entry.type == TarStream::EntryType::RegularFile)
|
if (entry.type == TarStream::EntryType::RegularFile)
|
||||||
{
|
{
|
||||||
auto file =
|
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, nullptr));
|
||||||
TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}, nullptr));
|
|
||||||
file->write(entry.data(), 0, entry.size);
|
file->write(entry.data(), 0, entry.size);
|
||||||
}
|
}
|
||||||
else if (entry.type == TarStream::EntryType::Directory)
|
else if (entry.type == TarStream::EntryType::Directory)
|
||||||
|
@ -8,7 +8,7 @@ Result<void> Pipe::create(SharedPtr<VFS::Inode>& rpipe, SharedPtr<VFS::Inode>& w
|
|||||||
auto writer = TRY(make_shared<PipeWriter>());
|
auto writer = TRY(make_shared<PipeWriter>());
|
||||||
auto reader = TRY(make_shared<PipeReader>());
|
auto reader = TRY(make_shared<PipeReader>());
|
||||||
|
|
||||||
auto auth = Scheduler::current()->auth;
|
auto auth = Process::current()->credentials();
|
||||||
|
|
||||||
pipe->m_writer = writer.ptr();
|
pipe->m_writer = writer.ptr();
|
||||||
pipe->m_reader = reader.ptr();
|
pipe->m_reader = reader.ptr();
|
||||||
|
@ -17,9 +17,8 @@ namespace VFS
|
|||||||
|
|
||||||
static constexpr int MAX_SYMLINKS = 8;
|
static constexpr int MAX_SYMLINKS = 8;
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Process* process, SharedPtr<Inode> current_inode,
|
||||||
SharedPtr<Inode> current_inode, bool follow_last_symlink,
|
bool follow_last_symlink, int& symlinks_followed)
|
||||||
int& symlinks_followed)
|
|
||||||
{
|
{
|
||||||
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ namespace VFS
|
|||||||
const char* section;
|
const char* section;
|
||||||
while (parser.next().try_set_value(section))
|
while (parser.next().try_set_value(section))
|
||||||
{
|
{
|
||||||
if (!can_execute(current_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_execute(current_inode, process)) return err(EACCES);
|
||||||
current_inode = TRY(current_inode->find(section));
|
current_inode = TRY(current_inode->find(section));
|
||||||
|
|
||||||
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
|
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
|
||||||
@ -46,8 +45,7 @@ namespace VFS
|
|||||||
symlink_root = parent_inode;
|
symlink_root = parent_inode;
|
||||||
|
|
||||||
symlinks_followed++;
|
symlinks_followed++;
|
||||||
current_inode =
|
current_inode = TRY(resolve_path_impl(link.chars(), process, symlink_root, true, symlinks_followed));
|
||||||
TRY(resolve_path_impl(link.chars(), auth, extra_groups, symlink_root, true, symlinks_followed));
|
|
||||||
symlinks_followed--;
|
symlinks_followed--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +55,8 @@ namespace VFS
|
|||||||
return current_inode;
|
return current_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory,
|
||||||
SharedPtr<VFS::Inode> working_directory, bool follow_last_symlink)
|
bool follow_last_symlink)
|
||||||
{
|
{
|
||||||
SharedPtr<Inode> current_inode;
|
SharedPtr<Inode> current_inode;
|
||||||
|
|
||||||
@ -68,17 +66,17 @@ namespace VFS
|
|||||||
|
|
||||||
int symlinks_followed = 0;
|
int symlinks_followed = 0;
|
||||||
|
|
||||||
return resolve_path_impl(path, auth, extra_groups, current_inode, follow_last_symlink, symlinks_followed);
|
return resolve_path_impl(path, process, current_inode, follow_last_symlink, symlinks_followed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Process* process,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_write(parent_inode, process)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -87,14 +85,14 @@ namespace VFS
|
|||||||
return parent_inode->create_subdirectory(child_name.chars(), mode);
|
return parent_inode->create_subdirectory(child_name.chars(), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Process* process,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
|
SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!can_write(parent_inode, process)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -135,6 +133,81 @@ namespace VFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Check all three permissions even if the UID or GID match.
|
||||||
|
bool can_execute(SharedPtr<Inode> inode, Process* process)
|
||||||
|
{
|
||||||
|
const auto& metadata = inode->metadata();
|
||||||
|
|
||||||
|
Credentials auth { 0 };
|
||||||
|
if (process) auth = process->credentials();
|
||||||
|
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
|
||||||
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
|
||||||
|
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
auto groups = process->extra_groups.lock();
|
||||||
|
for (gid_t group : *groups)
|
||||||
|
{
|
||||||
|
if (metadata.gid == group) return metadata.mode & S_IXGRP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.mode & S_IXOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Check all three permissions even if the UID or GID match.
|
||||||
|
bool can_write(SharedPtr<Inode> inode, Process* process)
|
||||||
|
{
|
||||||
|
const auto& metadata = inode->metadata();
|
||||||
|
|
||||||
|
Credentials auth { 0 };
|
||||||
|
if (process) auth = process->credentials();
|
||||||
|
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
|
||||||
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
|
||||||
|
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
auto groups = process->extra_groups.lock();
|
||||||
|
for (gid_t group : *groups)
|
||||||
|
{
|
||||||
|
if (metadata.gid == group) return metadata.mode & S_IWGRP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.mode & S_IWOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Check all three permissions even if the UID or GID match.
|
||||||
|
bool can_read(SharedPtr<Inode> inode, Process* process)
|
||||||
|
{
|
||||||
|
const auto& metadata = inode->metadata();
|
||||||
|
|
||||||
|
Credentials auth { 0 };
|
||||||
|
if (process) auth = process->credentials();
|
||||||
|
|
||||||
|
if (auth.euid == 0) return true;
|
||||||
|
|
||||||
|
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
|
||||||
|
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
|
||||||
|
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
auto groups = process->extra_groups.lock();
|
||||||
|
for (gid_t group : *groups)
|
||||||
|
{
|
||||||
|
if (metadata.gid == group) return metadata.mode & S_IRGRP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.mode & S_IROTH;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Check all three permissions even if the UID or GID match.
|
// FIXME: Check all three permissions even if the UID or GID match.
|
||||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
|
||||||
{
|
{
|
||||||
@ -232,8 +305,7 @@ namespace VFS
|
|||||||
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
||||||
auto new_root_path = TRY(PathParser::basename(new_root));
|
auto new_root_path = TRY(PathParser::basename(new_root));
|
||||||
|
|
||||||
auto new_root_parent_inode =
|
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), nullptr, working_directory));
|
||||||
TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, nullptr, working_directory));
|
|
||||||
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
||||||
|
|
||||||
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -245,7 +317,7 @@ namespace VFS
|
|||||||
|
|
||||||
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, nullptr, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), nullptr, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -265,8 +337,8 @@ namespace VFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Process* process,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<VFS::Inode> working_directory)
|
SharedPtr<VFS::Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto child = TRY(PathParser::basename(path));
|
||||||
@ -275,7 +347,7 @@ namespace VFS
|
|||||||
kdbgln("vfs: Mounting filesystem on target %s", path);
|
kdbgln("vfs: Mounting filesystem on target %s", path);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
@ -290,8 +362,7 @@ namespace VFS
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<void> umount(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory)
|
||||||
SharedPtr<VFS::Inode> working_directory)
|
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto child = TRY(PathParser::basename(path));
|
||||||
@ -300,7 +371,7 @@ namespace VFS
|
|||||||
|
|
||||||
kinfoln("vfs: Unmounting filesystem on target %s", path);
|
kinfoln("vfs: Unmounting filesystem on target %s", path);
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), process, working_directory));
|
||||||
|
|
||||||
auto inode = TRY(parent_inode->find(child.chars()));
|
auto inode = TRY(parent_inode->find(child.chars()));
|
||||||
if (!inode->is_mountpoint()) return err(EINVAL);
|
if (!inode->is_mountpoint()) return err(EINVAL);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <luna/StringView.h>
|
#include <luna/StringView.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
struct Process;
|
||||||
struct Credentials;
|
struct Credentials;
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
@ -319,20 +320,21 @@ namespace VFS
|
|||||||
virtual ~DeviceInode() = default;
|
virtual ~DeviceInode() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
Result<SharedPtr<Inode>> resolve_path(const char* path, Process* process,
|
||||||
SharedPtr<VFS::Inode> working_directory = {},
|
SharedPtr<VFS::Inode> working_directory = {},
|
||||||
bool follow_last_symlink = true);
|
bool follow_last_symlink = true);
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Process* process,
|
||||||
const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<VFS::Inode> working_directory = {});
|
SharedPtr<VFS::Inode> working_directory = {});
|
||||||
|
|
||||||
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
|
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Process* process,
|
||||||
const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<VFS::Inode> working_directory = {});
|
SharedPtr<VFS::Inode> working_directory = {});
|
||||||
|
|
||||||
Result<void> validate_filename(StringView name);
|
Result<void> validate_filename(StringView name);
|
||||||
|
|
||||||
|
bool can_execute(SharedPtr<Inode> inode, Process* process);
|
||||||
|
bool can_read(SharedPtr<Inode> inode, Process* process);
|
||||||
|
bool can_write(SharedPtr<Inode> inode, Process* process);
|
||||||
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||||
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||||
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
|
||||||
@ -346,9 +348,8 @@ namespace VFS
|
|||||||
|
|
||||||
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
|
||||||
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
|
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
|
||||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Process* process,
|
||||||
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory = {});
|
|
||||||
|
|
||||||
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
|
||||||
SharedPtr<Inode> working_directory = {});
|
SharedPtr<Inode> working_directory = {});
|
||||||
|
|
||||||
|
Result<void> umount(const char* path, Process* process, SharedPtr<Inode> working_directory = {});
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
#include "memory/SharedMemory.h"
|
#include "memory/SharedMemory.h"
|
||||||
#include "video/Framebuffer.h"
|
#include "video/Framebuffer.h"
|
||||||
#include <bits/ioctl-defs.h>
|
#include <bits/ioctl-defs.h>
|
||||||
#include <luna/CString.h>
|
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
|
#include <luna/CString.h>
|
||||||
|
|
||||||
Result<void> FramebufferDevice::create()
|
Result<void> FramebufferDevice::create()
|
||||||
{
|
{
|
||||||
|
@ -31,8 +31,9 @@ Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
|
|||||||
|
|
||||||
slave->m_master = master.ptr();
|
slave->m_master = master.ptr();
|
||||||
slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 2);
|
slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 2);
|
||||||
slave->m_metadata.uid = Scheduler::current()->auth.euid;
|
auto credentials = Process::current()->credentials();
|
||||||
slave->m_metadata.gid = Scheduler::current()->auth.egid;
|
slave->m_metadata.uid = credentials.euid;
|
||||||
|
slave->m_metadata.gid = credentials.egid;
|
||||||
slave->m_metadata.mode = 0620;
|
slave->m_metadata.mode = 0620;
|
||||||
slave->m_metadata.initialize_times();
|
slave->m_metadata.initialize_times();
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ Result<void> MasterPTY::handle_background_process_group(bool can_succeed, int si
|
|||||||
auto foreground_pgrp = m_foreground_process_group.value();
|
auto foreground_pgrp = m_foreground_process_group.value();
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
if (current->pgid == foreground_pgrp) return {};
|
if (current->process->pgid == foreground_pgrp) return {};
|
||||||
|
|
||||||
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||||
{
|
{
|
||||||
@ -112,8 +113,8 @@ Result<void> MasterPTY::handle_input(u8 key)
|
|||||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
||||||
|
|
||||||
if (m_foreground_process_group.has_value())
|
if (m_foreground_process_group.has_value())
|
||||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Process* p) {
|
||||||
thread->send_signal(SIGINT);
|
p->send_signal(SIGINT);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -125,8 +126,8 @@ Result<void> MasterPTY::handle_input(u8 key)
|
|||||||
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear();
|
||||||
|
|
||||||
if (m_foreground_process_group.has_value())
|
if (m_foreground_process_group.has_value())
|
||||||
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) {
|
Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Process* p) {
|
||||||
thread->send_signal(SIGQUIT);
|
p->send_signal(SIGQUIT);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
|
|||||||
|
|
||||||
Result<u64> MasterPTY::ioctl(int request, void* arg)
|
Result<u64> MasterPTY::ioctl(int request, void* arg)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_tty));
|
TRY(check_pledge(current, Promise::p_tty));
|
||||||
|
|
||||||
switch (request)
|
switch (request)
|
||||||
|
@ -47,7 +47,7 @@ bool SlavePTY::will_block_if_read() const
|
|||||||
|
|
||||||
Result<u64> SlavePTY::ioctl(int request, void* arg)
|
Result<u64> SlavePTY::ioctl(int request, void* arg)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_tty));
|
TRY(check_pledge(current, Promise::p_tty));
|
||||||
|
|
||||||
if (!m_master) return err(EIO);
|
if (!m_master) return err(EIO);
|
||||||
@ -69,9 +69,9 @@ Result<u64> SlavePTY::ioctl(int request, void* arg)
|
|||||||
|
|
||||||
bool pgid_exists = false;
|
bool pgid_exists = false;
|
||||||
pid_t sid;
|
pid_t sid;
|
||||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* thread) {
|
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Process* p) {
|
||||||
pgid_exists = true;
|
pgid_exists = true;
|
||||||
sid = thread->sid; // should be the same for all threads in the process group
|
sid = p->sid.load(); // should be the same for all threads in the process group
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (!pgid_exists) return err(EPERM);
|
if (!pgid_exists) return err(EPERM);
|
||||||
@ -95,13 +95,13 @@ Result<u64> SlavePTY::ioctl(int request, void* arg)
|
|||||||
if (this->m_master->m_session.has_value()) return err(EPERM);
|
if (this->m_master->m_session.has_value()) return err(EPERM);
|
||||||
if (!current->is_session_leader()) return err(EPERM);
|
if (!current->is_session_leader()) return err(EPERM);
|
||||||
|
|
||||||
Scheduler::for_each_in_session(current->sid, [this](Thread* thread) {
|
Scheduler::for_each_in_session(current->sid, [this](Process* p) {
|
||||||
thread->controlling_terminal = this;
|
p->controlling_terminal = this;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
m_master->m_session = current->sid;
|
m_master->m_session = current->sid.load();
|
||||||
m_master->m_foreground_process_group = current->pgid;
|
m_master->m_foreground_process_group = current->pgid.load();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ TTYLink::TTYLink()
|
|||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> TTYLink::open()
|
Result<SharedPtr<VFS::Inode>> TTYLink::open()
|
||||||
{
|
{
|
||||||
if (!Scheduler::current()->controlling_terminal) return err(ENXIO);
|
if (!Process::current()->controlling_terminal) return err(ENXIO);
|
||||||
|
|
||||||
return Scheduler::current()->controlling_terminal;
|
return Process::current()->controlling_terminal;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
void Mutex::lock()
|
void Mutex::lock()
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
const pid_t desired = current->id;
|
const pid_t desired = current->tid;
|
||||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@ -19,7 +19,7 @@ void Mutex::lock()
|
|||||||
{
|
{
|
||||||
if (expected == desired)
|
if (expected == desired)
|
||||||
{
|
{
|
||||||
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->id);
|
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->tid);
|
||||||
fail("Mutex deadlock detected");
|
fail("Mutex deadlock detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ void Mutex::lock()
|
|||||||
void Mutex::unlock()
|
void Mutex::unlock()
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
pid_t expected = current->id;
|
pid_t expected = current->tid;
|
||||||
check(expected > 0); // Why the hell would the idle thread be touching a mutex?
|
check(expected > 0); // Why the hell would the idle thread be touching a mutex?
|
||||||
|
|
||||||
m_spinlock.lock();
|
m_spinlock.lock();
|
||||||
@ -48,7 +48,7 @@ void Mutex::unlock()
|
|||||||
if (!m_thread.compare_exchange_strong(expected, 0))
|
if (!m_thread.compare_exchange_strong(expected, 0))
|
||||||
{
|
{
|
||||||
kerrorln("KMutex::unlock() called on a lock already locked by another thread (%d, current is %d)", expected,
|
kerrorln("KMutex::unlock() called on a lock already locked by another thread (%d, current is %d)", expected,
|
||||||
current->id);
|
current->tid);
|
||||||
fail("Mutex unlock by different thread");
|
fail("Mutex unlock by different thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ void Mutex::unlock()
|
|||||||
bool Mutex::try_lock()
|
bool Mutex::try_lock()
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
const pid_t desired = current->id;
|
const pid_t desired = current->tid;
|
||||||
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
|
||||||
|
|
||||||
// Make sure only one thread is touching the mutex at the same time.
|
// Make sure only one thread is touching the mutex at the same time.
|
||||||
@ -83,7 +83,7 @@ bool Mutex::try_lock()
|
|||||||
{
|
{
|
||||||
kwarnln("Deadlock avoided! KMutex::try_lock() failed because it was already locked by the same thread "
|
kwarnln("Deadlock avoided! KMutex::try_lock() failed because it was already locked by the same thread "
|
||||||
"(%d), this is not supposed to happen",
|
"(%d), this is not supposed to happen",
|
||||||
current->id);
|
current->tid);
|
||||||
CPU::print_stack_trace();
|
CPU::print_stack_trace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "thread/Thread.h"
|
#include <luna/Action.h>
|
||||||
#include <luna/CircularQueue.h>
|
#include <luna/CircularQueue.h>
|
||||||
#include <luna/Spinlock.h>
|
#include <luna/Spinlock.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
class Thread;
|
||||||
|
|
||||||
class Mutex
|
class Mutex
|
||||||
{
|
{
|
||||||
@ -16,6 +19,84 @@ class Mutex
|
|||||||
Atomic<pid_t> m_thread;
|
Atomic<pid_t> m_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T> class MutexLocked
|
||||||
|
{
|
||||||
|
struct MutexLockedGuard
|
||||||
|
{
|
||||||
|
MutexLockedGuard(MutexLocked& value_ref) : m_value_ref(&value_ref)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLockedGuard(const MutexLockedGuard& other) = delete;
|
||||||
|
MutexLockedGuard(MutexLockedGuard&& other)
|
||||||
|
{
|
||||||
|
m_value_ref = other.m_value_ref;
|
||||||
|
other.m_value_ref = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MutexLockedGuard()
|
||||||
|
{
|
||||||
|
if (m_value_ref) m_value_ref->m_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
T& ref()
|
||||||
|
{
|
||||||
|
expect(m_value_ref, "MutexLockedGuard::ref() called on a moved MutexLockedGuard");
|
||||||
|
return m_value_ref->m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const T& other)
|
||||||
|
{
|
||||||
|
ref() = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
return &ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*()
|
||||||
|
{
|
||||||
|
return ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MutexLocked* m_value_ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
MutexLocked() : m_value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLocked(T value) : m_value(move(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLockedGuard lock()
|
||||||
|
{
|
||||||
|
m_lock.lock();
|
||||||
|
return { *this };
|
||||||
|
}
|
||||||
|
|
||||||
|
Option<MutexLockedGuard> try_lock()
|
||||||
|
{
|
||||||
|
if (m_lock.try_lock()) { return { *this }; }
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void with_lock(Function<T&> callback)
|
||||||
|
{
|
||||||
|
m_lock.lock();
|
||||||
|
callback(m_value);
|
||||||
|
m_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_value;
|
||||||
|
Mutex m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
class ScopedMutexLock
|
class ScopedMutexLock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -26,9 +26,11 @@ void reap_thread()
|
|||||||
{
|
{
|
||||||
CPU::disable_interrupts();
|
CPU::disable_interrupts();
|
||||||
auto dying_threads = Scheduler::check_for_dying_threads();
|
auto dying_threads = Scheduler::check_for_dying_threads();
|
||||||
|
auto dead_processes = Scheduler::check_for_dead_processes();
|
||||||
CPU::enable_interrupts();
|
CPU::enable_interrupts();
|
||||||
|
|
||||||
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
||||||
|
dead_processes.consume([](Process* p) { Scheduler::reap_process(p); });
|
||||||
|
|
||||||
kernel_wait_for_event();
|
kernel_wait_for_event();
|
||||||
}
|
}
|
||||||
@ -65,8 +67,8 @@ void oom_thread()
|
|||||||
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
|
||||||
mark_critical(FSRegistry::init(), "Failed to register initial file systems");
|
mark_critical(FSRegistry::init(), "Failed to register initial file systems");
|
||||||
|
|
||||||
auto init = mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}, nullptr),
|
auto init =
|
||||||
"Can't find init in the initial ramfs!");
|
mark_critical(VFS::resolve_path("/bin/preinit", nullptr, nullptr), "Can't find init in the initial ramfs!");
|
||||||
auto init_thread = mark_critical(Scheduler::create_init_process(init, "/bin/preinit"),
|
auto init_thread = mark_critical(Scheduler::create_init_process(init, "/bin/preinit"),
|
||||||
"Failed to create PID 1 process for init");
|
"Failed to create PID 1 process for init");
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "memory/AddressSpace.h"
|
#include "memory/AddressSpace.h"
|
||||||
|
#include "Log.h"
|
||||||
#include "arch/MMU.h"
|
#include "arch/MMU.h"
|
||||||
#include "memory/Heap.h"
|
#include "memory/Heap.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
@ -385,3 +386,13 @@ void VMRegion::sync_shared()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddressSpace::debug_log()
|
||||||
|
{
|
||||||
|
m_regions.for_each([this](VMRegion* region) {
|
||||||
|
kdbgln("VMRegion start: %p, end: %p, count: %zu, used: %s, persistent: %s, flags: %d, prot: %d, shmid: %lu, "
|
||||||
|
"offset: %ld",
|
||||||
|
(void*)region->start, (void*)region->end, region->count, region->used ? "true" : "false",
|
||||||
|
region->persistent ? "true" : "false", region->flags, region->prot, region->shmid, region->offset);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@ class VMRegion : LinkedListNode<VMRegion>
|
|||||||
bool persistent { false };
|
bool persistent { false };
|
||||||
int flags { 0 };
|
int flags { 0 };
|
||||||
int prot { 0 };
|
int prot { 0 };
|
||||||
u64 shmid;
|
u64 shmid { 0 };
|
||||||
off_t offset { 0 };
|
off_t offset { 0 };
|
||||||
|
|
||||||
void cleanup_shared();
|
void cleanup_shared();
|
||||||
@ -52,6 +52,8 @@ class AddressSpace
|
|||||||
|
|
||||||
static Result<OwnedPtr<AddressSpace>> try_create();
|
static Result<OwnedPtr<AddressSpace>> try_create();
|
||||||
|
|
||||||
|
void debug_log();
|
||||||
|
|
||||||
Result<OwnedPtr<AddressSpace>> clone();
|
Result<OwnedPtr<AddressSpace>> clone();
|
||||||
|
|
||||||
PageDirectory* page_directory() const
|
PageDirectory* page_directory() const
|
||||||
|
@ -641,4 +641,30 @@ namespace MemoryManager
|
|||||||
{
|
{
|
||||||
return free_mem + used_mem + reserved_mem;
|
return free_mem + used_mem + reserved_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<u64> push_mem_on_stack(const u8* mem, usize size, const Stack& stack, u64& sp)
|
||||||
|
{
|
||||||
|
if ((sp - size) < stack.bottom()) return err(E2BIG);
|
||||||
|
|
||||||
|
if (!MemoryManager::validate_user_write((void*)(sp - size), size)) return err(EFAULT);
|
||||||
|
|
||||||
|
sp -= size;
|
||||||
|
|
||||||
|
memcpy((void*)sp, mem, size);
|
||||||
|
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<u64> pop_mem_from_stack(u8* mem, usize size, const Stack& stack, u64& sp)
|
||||||
|
{
|
||||||
|
if ((sp + size) > stack.top()) return err(E2BIG);
|
||||||
|
|
||||||
|
if (!MemoryManager::validate_user_read((void*)sp, size)) return err(EFAULT);
|
||||||
|
|
||||||
|
memcpy(mem, (void*)sp, size);
|
||||||
|
|
||||||
|
sp += size;
|
||||||
|
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "arch/MMU.h"
|
#include "arch/MMU.h"
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
#include <luna/Stack.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
@ -88,4 +89,7 @@ namespace MemoryManager
|
|||||||
usize used();
|
usize used();
|
||||||
usize reserved();
|
usize reserved();
|
||||||
usize total();
|
usize total();
|
||||||
|
|
||||||
|
Result<u64> push_mem_on_stack(const u8* mem, usize size, const Stack& stack, u64& sp);
|
||||||
|
Result<u64> pop_mem_from_stack(u8* mem, usize size, const Stack& stack, u64& sp);
|
||||||
}
|
}
|
||||||
|
@ -56,14 +56,14 @@ Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
|
|||||||
return m_data.dequeue_data(buf, length);
|
return m_data.dequeue_data(buf, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
|
static Result<void> bind_socket_to_fs(const char* path, Process* process, SharedPtr<VFS::Inode> working_directory,
|
||||||
SharedPtr<VFS::Inode> working_directory, SharedPtr<UnixSocket> socket)
|
SharedPtr<UnixSocket> socket)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parent_path = TRY(PathParser::dirname(path));
|
||||||
|
|
||||||
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
|
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), process, working_directory));
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, auth, extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, process)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(PathParser::basename(path));
|
||||||
|
|
||||||
@ -89,14 +89,14 @@ Result<void> UnixSocket::bind(struct sockaddr* addr, socklen_t addrlen)
|
|||||||
String path = TRY(String::from_string_view(
|
String path = TRY(String::from_string_view(
|
||||||
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
m_metadata.mode = 0777 & ~current->umask;
|
m_metadata.mode = 0777 & ~current->umask;
|
||||||
m_metadata.uid = current->auth.euid;
|
auto auth = current->credentials();
|
||||||
m_metadata.gid = current->auth.egid;
|
m_metadata.uid = auth.euid;
|
||||||
|
m_metadata.gid = auth.egid;
|
||||||
|
|
||||||
auto rc = bind_socket_to_fs(path.chars(), current->auth, ¤t->extra_groups, current->current_directory,
|
auto rc = bind_socket_to_fs(path.chars(), current, current->current_directory, SharedPtr<Socket> { this });
|
||||||
SharedPtr<Socket> { this });
|
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
{
|
{
|
||||||
if (rc.error() == EEXIST) return err(EADDRINUSE);
|
if (rc.error() == EEXIST) return err(EADDRINUSE);
|
||||||
@ -126,13 +126,13 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
|||||||
String path = TRY(String::from_string_view(
|
String path = TRY(String::from_string_view(
|
||||||
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
auto* thread = Scheduler::current();
|
||||||
|
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
if (inode->type() != VFS::InodeType::Socket)
|
if (inode->type() != VFS::InodeType::Socket)
|
||||||
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
auto socket = (SharedPtr<UnixSocket>)inode;
|
auto socket = (SharedPtr<UnixSocket>)inode;
|
||||||
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
||||||
@ -144,14 +144,14 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
m_blocked_thread = current;
|
m_blocked_thread = thread;
|
||||||
kernel_wait_for_event();
|
kernel_wait_for_event();
|
||||||
m_blocked_thread = nullptr;
|
m_blocked_thread = nullptr;
|
||||||
if (current->interrupted)
|
if (thread->interrupted)
|
||||||
{
|
{
|
||||||
if (current->will_ignore_pending_signal())
|
if (thread->will_ignore_pending_signal())
|
||||||
{
|
{
|
||||||
current->process_pending_signals(regs);
|
thread->process_pending_signals(regs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return err(EINTR);
|
return err(EINTR);
|
||||||
|
@ -8,16 +8,16 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_rpath));
|
TRY(check_pledge(current, Promise::p_rpath));
|
||||||
|
|
||||||
if (PathParser::is_absolute(path.view()))
|
if (PathParser::is_absolute(path.view()))
|
||||||
{
|
{
|
||||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups));
|
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current));
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
inode->add_handle();
|
inode->add_handle();
|
||||||
if (current->current_directory) current->current_directory->remove_handle();
|
if (current->current_directory) current->current_directory->remove_handle();
|
||||||
@ -29,11 +29,10 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SharedPtr<VFS::Inode> inode =
|
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
auto old_wdir = current->current_directory_path.view();
|
auto old_wdir = current->current_directory_path.view();
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ Result<u64> sys_getcwd(Registers*, SyscallArgs args)
|
|||||||
u8* buf = (u8*)args[0];
|
u8* buf = (u8*)args[0];
|
||||||
usize size = (usize)args[1];
|
usize size = (usize)args[1];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
StringView cwd = current->current_directory_path.view();
|
StringView cwd = current->current_directory_path.view();
|
||||||
if (cwd.is_empty()) cwd = "/"_sv;
|
if (cwd.is_empty()) cwd = "/"_sv;
|
||||||
|
@ -11,7 +11,7 @@ Result<u64> sys_clock_gettime(Registers*, SyscallArgs args)
|
|||||||
clockid_t id = (clockid_t)args[0];
|
clockid_t id = (clockid_t)args[0];
|
||||||
struct timespec* ts = (struct timespec*)args[1];
|
struct timespec* ts = (struct timespec*)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
|
@ -64,14 +64,14 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
|
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
|
||||||
return err(E2BIG);
|
return err(E2BIG);
|
||||||
|
|
||||||
auto current = Scheduler::current();
|
auto current = Process::current();
|
||||||
|
auto thread = Scheduler::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_exec));
|
TRY(check_pledge(current, Promise::p_exec));
|
||||||
|
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_execute(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
#ifdef EXEC_DEBUG
|
#ifdef EXEC_DEBUG
|
||||||
kdbgln("exec: attempting to replace current image with %s", path.chars());
|
kdbgln("exec: attempting to replace current image with %s", path.chars());
|
||||||
@ -88,7 +88,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
kdbgln("exec: created loader for binary format %s", loader->format().chars());
|
kdbgln("exec: created loader for binary format %s", loader->format().chars());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
auto guard = make_scope_guard([thread] { MMU::switch_page_directory(thread->self_directory()); });
|
||||||
|
|
||||||
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
auto image = TRY(ThreadImage::try_load_from_binary(loader));
|
||||||
|
|
||||||
@ -108,6 +108,15 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
|
||||||
|
// Terminate all other threads.
|
||||||
|
Scheduler::for_each_thread(current, [thread](Thread* t) {
|
||||||
|
if (t != thread) t->quit();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
Scheduler::signal_reap_thread();
|
||||||
|
|
||||||
|
current->thread_count = 1;
|
||||||
|
|
||||||
current->real_timer.disarm();
|
current->real_timer.disarm();
|
||||||
current->virtual_timer.disarm();
|
current->virtual_timer.disarm();
|
||||||
current->profiling_timer.disarm();
|
current->profiling_timer.disarm();
|
||||||
@ -120,32 +129,39 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto table = current->fd_table.lock();
|
||||||
for (int i = 0; i < FD_MAX; i++)
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
{
|
{
|
||||||
auto& descriptor = current->fd_table[i];
|
auto& descriptor = (*table)[i];
|
||||||
if (!descriptor.has_value()) continue;
|
if (!descriptor.has_value()) continue;
|
||||||
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (is_setuid) current->auth.euid = current->auth.suid = inode->metadata().uid;
|
{
|
||||||
if (is_setgid) current->auth.egid = current->auth.sgid = inode->metadata().gid;
|
auto auth = current->auth.lock();
|
||||||
|
if (is_setuid) (*auth).euid = (*auth).suid = inode->metadata().uid;
|
||||||
|
if (is_setgid) (*auth).egid = (*auth).sgid = inode->metadata().gid;
|
||||||
|
}
|
||||||
|
|
||||||
current->cmdline = cmdline.chars();
|
current->cmdline = cmdline.chars();
|
||||||
|
thread->cmdline = cmdline.chars();
|
||||||
|
|
||||||
image->apply(current);
|
image->apply(thread);
|
||||||
|
|
||||||
MMU::switch_page_directory(current->self_directory());
|
MMU::switch_page_directory(thread->self_directory());
|
||||||
|
|
||||||
current->set_arguments(user_argc, user_argv, user_envc, user_envp);
|
thread->set_arguments(user_argc, user_argv, user_envc, user_envp);
|
||||||
|
|
||||||
current->promises = current->execpromises;
|
current->promises = current->execpromises;
|
||||||
current->execpromises = -1;
|
current->execpromises = -1;
|
||||||
|
|
||||||
memcpy(regs, ¤t->regs, sizeof(*regs));
|
memcpy(regs, &thread->regs, sizeof(*regs));
|
||||||
|
|
||||||
for (int i = 0; i < NSIG; i++)
|
for (int i = 0; i < NSIG; i++)
|
||||||
{
|
{
|
||||||
current->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
current->has_called_exec = true;
|
current->has_called_exec = true;
|
||||||
@ -157,57 +173,78 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto current = Scheduler::current();
|
auto current = Process::current();
|
||||||
|
auto current_thread = Scheduler::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
|
|
||||||
auto extra_groups = TRY(current->extra_groups.shallow_copy());
|
Vector<gid_t> extra_groups = TRY(current->copy_groups());
|
||||||
|
Credentials auth = current->credentials();
|
||||||
|
|
||||||
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
|
auto guard = make_scope_guard([current_thread] { MMU::switch_page_directory(current_thread->self_directory()); });
|
||||||
|
|
||||||
memcpy(¤t->regs, regs, sizeof(*regs));
|
memcpy(¤t_thread->regs, regs, sizeof(*regs));
|
||||||
|
|
||||||
auto current_directory_path = TRY(current->current_directory_path.clone());
|
auto current_directory_path = TRY(current->current_directory_path.clone());
|
||||||
|
|
||||||
auto image = TRY(ThreadImage::clone_from_thread(current));
|
auto image = TRY(ThreadImage::clone_from_thread(current_thread));
|
||||||
|
|
||||||
auto thread = TRY(new_thread());
|
auto thread = TRY(new_thread());
|
||||||
|
auto process = TRY(make<Process>());
|
||||||
|
|
||||||
|
Option<FileDescriptor> fds[FD_MAX];
|
||||||
|
{
|
||||||
|
auto table = current->fd_table.lock();
|
||||||
|
for (int i = 0; i < FD_MAX; i++) { fds[i] = (*table)[i]; }
|
||||||
|
}
|
||||||
|
|
||||||
thread->state = ThreadState::Runnable;
|
thread->state = ThreadState::Runnable;
|
||||||
thread->is_kernel = false;
|
|
||||||
thread->fp_data.save();
|
thread->fp_data.save();
|
||||||
thread->cmdline = current->cmdline;
|
thread->cmdline = current_thread->cmdline;
|
||||||
thread->auth = current->auth;
|
thread->process = process;
|
||||||
thread->current_directory = current->current_directory;
|
|
||||||
thread->current_directory_path = move(current_directory_path);
|
|
||||||
thread->umask = current->umask;
|
|
||||||
thread->parent = current;
|
|
||||||
thread->promises = current->promises;
|
|
||||||
thread->execpromises = current->execpromises;
|
|
||||||
thread->controlling_terminal = current->controlling_terminal;
|
|
||||||
thread->pgid = current->pgid;
|
|
||||||
thread->sid = current->sid;
|
|
||||||
thread->extra_groups = move(extra_groups);
|
|
||||||
|
|
||||||
thread->virtual_clock.set_resolution(1'000'000);
|
process->thread_count = 1;
|
||||||
thread->profiling_clock.set_resolution(1'000'000);
|
process->id = thread->tid;
|
||||||
|
process->current_directory = current->current_directory;
|
||||||
|
process->current_directory_path = move(current_directory_path);
|
||||||
|
process->umask = current->umask;
|
||||||
|
process->parent = current;
|
||||||
|
process->promises = current->promises;
|
||||||
|
process->execpromises = current->execpromises;
|
||||||
|
process->controlling_terminal = current->controlling_terminal;
|
||||||
|
process->pgid = current->pgid;
|
||||||
|
process->sid = current->sid;
|
||||||
|
process->extra_groups = move(extra_groups);
|
||||||
|
process->cmdline = current->cmdline;
|
||||||
|
|
||||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
process->virtual_clock.set_resolution(1'000'000);
|
||||||
|
process->profiling_clock.set_resolution(1'000'000);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto credentials = process->auth.lock();
|
||||||
|
*credentials = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto table = process->fd_table.lock();
|
||||||
|
for (int i = 0; i < FD_MAX; i++) { (*table)[i] = fds[i]; }
|
||||||
|
}
|
||||||
|
|
||||||
image->apply(thread);
|
image->apply(thread);
|
||||||
|
|
||||||
memcpy(&thread->regs, regs, sizeof(*regs));
|
memcpy(&thread->regs, regs, sizeof(*regs));
|
||||||
|
|
||||||
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current->signal_handlers[i];
|
for (int i = 0; i < NSIG; i++) thread->signal_handlers[i] = current_thread->signal_handlers[i];
|
||||||
thread->signal_mask = current->signal_mask;
|
thread->signal_mask = current_thread->signal_mask;
|
||||||
|
|
||||||
thread->set_return(0);
|
thread->set_return(0);
|
||||||
|
|
||||||
Scheduler::add_thread(thread);
|
Scheduler::add_thread(thread);
|
||||||
|
Scheduler::add_process(process);
|
||||||
|
|
||||||
#ifdef FORK_DEBUG
|
#ifdef FORK_DEBUG
|
||||||
kdbgln("fork: thread %d forked into child %d", current->id, thread->id);
|
kdbgln("fork: thread %d forked into child %d", current->id, process->id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return thread->id;
|
return process->id;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
u8 status = (u8)args[0];
|
u8 status = (u8)args[0];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
current->exit_and_signal_parent(status);
|
current->exit(status);
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@ Result<u64> sys_read(Registers* regs, SyscallArgs args)
|
|||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current->process, Promise::p_stdio));
|
||||||
|
|
||||||
auto& descriptor = *TRY(current->resolve_fd(fd));
|
auto& descriptor = *TRY(current->process->resolve_fd(fd));
|
||||||
|
|
||||||
if (!descriptor.is_readable()) return err(EBADF);
|
if (!descriptor.is_readable()) return err(EBADF);
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ Result<u64> sys_write(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (!MemoryManager::validate_user_read(buf, size)) return err(EFAULT);
|
if (!MemoryManager::validate_user_read(buf, size)) return err(EFAULT);
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
|
|||||||
off_t offset = (long)args[1];
|
off_t offset = (long)args[1];
|
||||||
int whence = (int)args[2];
|
int whence = (int)args[2];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
|
|||||||
int fd = (int)args[0];
|
int fd = (int)args[0];
|
||||||
int cmd = (int)args[1];
|
int cmd = (int)args[1];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -135,13 +135,13 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
|
|||||||
case F_DUPFD: is_cloexec = false; [[fallthrough]];
|
case F_DUPFD: is_cloexec = false; [[fallthrough]];
|
||||||
case F_DUPFD_CLOEXEC: {
|
case F_DUPFD_CLOEXEC: {
|
||||||
int arg = (int)args[2];
|
int arg = (int)args[2];
|
||||||
int new_fd = TRY(current->allocate_fd(arg));
|
int new_fd = TRY(current->allocate_fd(arg, descriptor));
|
||||||
|
|
||||||
current->fd_table[new_fd] = descriptor;
|
auto table = current->fd_table.lock();
|
||||||
|
|
||||||
if (is_cloexec) current->fd_table[new_fd]->flags |= O_CLOEXEC;
|
if (is_cloexec) (*table)[new_fd]->flags |= O_CLOEXEC;
|
||||||
else
|
else
|
||||||
current->fd_table[new_fd]->flags &= ~O_CLOEXEC;
|
(*table)[new_fd]->flags &= ~O_CLOEXEC;
|
||||||
|
|
||||||
return (u64)new_fd;
|
return (u64)new_fd;
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ Result<u64> sys_ioctl(Registers*, SyscallArgs args)
|
|||||||
int request = (int)args[1];
|
int request = (int)args[1];
|
||||||
void* arg = (void*)args[2];
|
void* arg = (void*)args[2];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
auto& descriptor = *TRY(current->resolve_fd(fd));
|
auto& descriptor = *TRY(current->resolve_fd(fd));
|
||||||
|
|
||||||
return descriptor.inode()->ioctl(request, arg);
|
return descriptor.inode()->ioctl(request, arg);
|
||||||
@ -184,7 +184,7 @@ Result<u64> sys_isatty(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
int fd = (int)args[0];
|
int fd = (int)args[0];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
auto& descriptor = *TRY(current->resolve_fd(fd));
|
auto& descriptor = *TRY(current->resolve_fd(fd));
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ Result<u64> sys_dup2(Registers*, SyscallArgs args)
|
|||||||
int oldfd = (int)args[0];
|
int oldfd = (int)args[0];
|
||||||
int newfd = (int)args[1];
|
int newfd = (int)args[1];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -206,8 +206,10 @@ Result<u64> sys_dup2(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (newfd == oldfd) return (u64)newfd;
|
if (newfd == oldfd) return (u64)newfd;
|
||||||
|
|
||||||
current->fd_table[newfd] = descriptor;
|
auto table = current->fd_table.lock();
|
||||||
current->fd_table[newfd]->flags &= ~O_CLOEXEC;
|
|
||||||
|
(*table)[newfd] = descriptor;
|
||||||
|
(*table)[newfd]->flags &= ~O_CLOEXEC;
|
||||||
|
|
||||||
return (u64)newfd;
|
return (u64)newfd;
|
||||||
}
|
}
|
||||||
@ -216,23 +218,23 @@ Result<u64> sys_pipe(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
int* pfds = (int*)args[0];
|
int* pfds = (int*)args[0];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
int rfd = TRY(current->allocate_fd(0));
|
|
||||||
int wfd = TRY(current->allocate_fd(rfd + 1));
|
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user_typed(pfds, &rfd)) return err(EFAULT);
|
|
||||||
if (!MemoryManager::copy_to_user_typed(pfds + 1, &wfd)) return err(EFAULT);
|
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> rpipe;
|
SharedPtr<VFS::Inode> rpipe;
|
||||||
SharedPtr<VFS::Inode> wpipe;
|
SharedPtr<VFS::Inode> wpipe;
|
||||||
|
|
||||||
TRY(Pipe::create(rpipe, wpipe));
|
TRY(Pipe::create(rpipe, wpipe));
|
||||||
|
|
||||||
current->fd_table[rfd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(rpipe, O_RDONLY)), 0 };
|
auto rd = FileDescriptor { TRY(make_shared<OpenFileDescription>(rpipe, O_RDONLY)), 0 };
|
||||||
current->fd_table[wfd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(wpipe, O_WRONLY)), 0 };
|
auto wd = FileDescriptor { TRY(make_shared<OpenFileDescription>(wpipe, O_WRONLY)), 0 };
|
||||||
|
|
||||||
|
int rfd = TRY(current->allocate_fd(0, rd));
|
||||||
|
int wfd = TRY(current->allocate_fd(rfd + 1, wd));
|
||||||
|
|
||||||
|
if (!MemoryManager::copy_to_user_typed(pfds, &rfd)) return err(EFAULT);
|
||||||
|
if (!MemoryManager::copy_to_user_typed(pfds + 1, &wfd)) return err(EFAULT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -241,7 +243,7 @@ Result<u64> sys_umask(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
mode_t new_umask = (mode_t)args[0];
|
mode_t new_umask = (mode_t)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -257,12 +259,12 @@ Result<u64> sys_truncate(Registers*, SyscallArgs args)
|
|||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
size_t length = (size_t)args[1];
|
size_t length = (size_t)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_wpath));
|
TRY(check_pledge(current, Promise::p_wpath));
|
||||||
auto inode =
|
|
||||||
TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
|
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
auto inode = TRY(VFS::resolve_path(path.chars(), current, current->current_directory));
|
||||||
|
|
||||||
|
if (!VFS::can_write(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
TRY(inode->truncate(length));
|
TRY(inode->truncate(length));
|
||||||
|
|
||||||
@ -274,7 +276,7 @@ Result<u64> sys_ftruncate(Registers*, SyscallArgs args)
|
|||||||
int fd = (int)args[0];
|
int fd = (int)args[0];
|
||||||
size_t length = (size_t)args[1];
|
size_t length = (size_t)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
auto description = TRY(current->resolve_fd(fd))->description;
|
auto description = TRY(current->resolve_fd(fd))->description;
|
||||||
if (!(description->flags & O_WRONLY)) return err(EBADF);
|
if (!(description->flags & O_WRONLY)) return err(EBADF);
|
||||||
@ -291,9 +293,12 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
|
|||||||
const auto* times = (const struct timespec*)args[2];
|
const auto* times = (const struct timespec*)args[2];
|
||||||
int flags = (int)args[3];
|
int flags = (int)args[3];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_fattr));
|
TRY(check_pledge(current, Promise::p_fattr));
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
|
||||||
|
auto* process = Process::current();
|
||||||
|
auto credentials = process->credentials();
|
||||||
|
auto inode = TRY(process->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
struct timespec ktimes[2];
|
struct timespec ktimes[2];
|
||||||
ktimes[0].tv_sec = ktimes[1].tv_sec = 0;
|
ktimes[0].tv_sec = ktimes[1].tv_sec = 0;
|
||||||
@ -309,11 +314,10 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (allow_write_access)
|
if (allow_write_access)
|
||||||
{
|
{
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups) &&
|
if (!VFS::can_write(inode, current) && credentials.euid != inode->metadata().uid && credentials.euid != 0)
|
||||||
current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
|
|
||||||
return err(EACCES);
|
return err(EACCES);
|
||||||
}
|
}
|
||||||
else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
|
else if (credentials.euid != inode->metadata().uid && credentials.euid != 0)
|
||||||
return err(EPERM);
|
return err(EPERM);
|
||||||
|
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
|
@ -11,7 +11,7 @@ Result<u64> sys_getdents(Registers*, SyscallArgs args)
|
|||||||
luna_dirent* ent = (luna_dirent*)args[1];
|
luna_dirent* ent = (luna_dirent*)args[1];
|
||||||
usize count = (usize)args[2];
|
usize count = (usize)args[2];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
auto& descriptor = *TRY(current->resolve_fd(fd));
|
auto& descriptor = *TRY(current->resolve_fd(fd));
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
Result<u64> sys_getpid(Registers*, SyscallArgs)
|
Result<u64> sys_getpid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
return current->id;
|
return current->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getppid(Registers*, SyscallArgs)
|
Result<u64> sys_getppid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
auto* parent = current->parent;
|
auto* parent = current->parent;
|
||||||
return parent ? parent->id : 0;
|
return parent ? parent->id : 0;
|
||||||
@ -21,48 +21,48 @@ Result<u64> sys_getppid(Registers*, SyscallArgs)
|
|||||||
|
|
||||||
Result<u64> sys_getuid(Registers*, SyscallArgs)
|
Result<u64> sys_getuid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
return current->auth.uid;
|
return current->credentials().uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_geteuid(Registers*, SyscallArgs)
|
Result<u64> sys_geteuid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
return current->auth.euid;
|
return current->credentials().euid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getgid(Registers*, SyscallArgs)
|
Result<u64> sys_getgid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
return current->auth.gid;
|
return current->credentials().gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getegid(Registers*, SyscallArgs)
|
Result<u64> sys_getegid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
return current->auth.egid;
|
return current->credentials().egid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_setuid(Registers*, SyscallArgs args)
|
Result<u64> sys_setuid(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
u32 uid = (u32)args[0];
|
u32 uid = (u32)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
TRY(check_pledge(current, Promise::p_id));
|
||||||
Credentials& auth = current->auth;
|
auto auth = current->auth.lock();
|
||||||
|
|
||||||
if (auth.euid == 0)
|
if (auth->euid == 0)
|
||||||
{
|
{
|
||||||
auth.uid = auth.euid = auth.suid = uid;
|
auth->uid = auth->euid = auth->suid = uid;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uid != auth.uid && uid != auth.suid) return err(EPERM);
|
if (uid != auth->uid && uid != auth->suid) return err(EPERM);
|
||||||
auth.euid = uid;
|
auth->euid = uid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -71,12 +71,12 @@ Result<u64> sys_seteuid(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
u32 uid = (u32)args[0];
|
u32 uid = (u32)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
TRY(check_pledge(current, Promise::p_id));
|
||||||
Credentials& auth = current->auth;
|
auto auth = current->auth.lock();
|
||||||
|
|
||||||
if (auth.euid != 0 && uid != auth.uid && uid != auth.suid) return err(EPERM);
|
if (auth->euid != 0 && uid != auth->uid && uid != auth->suid) return err(EPERM);
|
||||||
auth.euid = uid;
|
auth->euid = uid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -85,18 +85,18 @@ Result<u64> sys_setgid(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
u32 gid = (u32)args[0];
|
u32 gid = (u32)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
TRY(check_pledge(current, Promise::p_id));
|
||||||
Credentials& auth = current->auth;
|
auto auth = current->auth.lock();
|
||||||
|
|
||||||
if (auth.euid == 0)
|
if (auth->euid == 0)
|
||||||
{
|
{
|
||||||
auth.gid = auth.egid = auth.sgid = gid;
|
auth->gid = auth->egid = auth->sgid = gid;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gid != auth.gid && gid != auth.sgid) return err(EPERM);
|
if (gid != auth->gid && gid != auth->sgid) return err(EPERM);
|
||||||
auth.egid = gid;
|
auth->egid = gid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -105,12 +105,12 @@ Result<u64> sys_setegid(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
u32 gid = (u32)args[0];
|
u32 gid = (u32)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
TRY(check_pledge(current, Promise::p_id));
|
||||||
Credentials& auth = current->auth;
|
auto auth = current->auth.lock();
|
||||||
|
|
||||||
if (auth.euid != 0 && gid != auth.gid && gid != auth.sgid) return err(EPERM);
|
if (auth->euid != 0 && gid != auth->gid && gid != auth->sgid) return err(EPERM);
|
||||||
auth.egid = gid;
|
auth->egid = gid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -120,34 +120,34 @@ Result<u64> sys_setpgid(Registers*, SyscallArgs args)
|
|||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
pid_t pgid = (pid_t)args[1];
|
pid_t pgid = (pid_t)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
if (pid == 0) pid = current->id;
|
if (pid == 0) pid = current->id;
|
||||||
if (pgid == 0) pgid = current->id;
|
if (pgid == 0) pgid = current->id;
|
||||||
|
|
||||||
if (pgid < 0) return err(EINVAL);
|
if (pgid < 0) return err(EINVAL);
|
||||||
|
|
||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* target = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
if (thread != current && thread->parent != current) return err(ESRCH);
|
if (target != current && target->parent != current) return err(ESRCH);
|
||||||
|
|
||||||
if (thread->is_session_leader() || thread->sid != current->sid) return err(EPERM);
|
if (target->is_session_leader() || target->sid != current->sid) return err(EPERM);
|
||||||
|
|
||||||
if (thread->has_called_exec) return err(EPERM);
|
if (target->has_called_exec) return err(EACCES);
|
||||||
|
|
||||||
if (pgid != current->id)
|
if (pgid != current->id)
|
||||||
{
|
{
|
||||||
bool pgid_exists = false;
|
bool pgid_exists = false;
|
||||||
pid_t sid;
|
pid_t sid;
|
||||||
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* t) {
|
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Process* p) {
|
||||||
pgid_exists = true;
|
pgid_exists = true;
|
||||||
sid = t->sid; // this should be the same for all threads in the process group
|
sid = p->sid; // this should be the same for all threads in the process group
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (!pgid_exists) return err(EPERM);
|
if (!pgid_exists) return err(EPERM);
|
||||||
if (sid != thread->sid) return err(EPERM);
|
if (sid != target->sid) return err(EPERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->pgid = (u64)pgid;
|
target->pgid = (u64)pgid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -156,20 +156,20 @@ Result<u64> sys_getpgid(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
if (pid == 0) pid = current->id;
|
if (pid == 0) pid = current->id;
|
||||||
|
|
||||||
if (pid < 0) return err(EINVAL);
|
if (pid < 0) return err(EINVAL);
|
||||||
|
|
||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* process = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
return (u64)thread->pgid;
|
return (u64)process->pgid.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_setsid(Registers*, SyscallArgs)
|
Result<u64> sys_setsid(Registers*, SyscallArgs)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
|
|
||||||
if (current->pgid == current->id) return err(EPERM);
|
if (current->pgid == current->id) return err(EPERM);
|
||||||
@ -177,21 +177,21 @@ Result<u64> sys_setsid(Registers*, SyscallArgs)
|
|||||||
current->sid = current->pgid = current->id;
|
current->sid = current->pgid = current->id;
|
||||||
current->controlling_terminal = {};
|
current->controlling_terminal = {};
|
||||||
|
|
||||||
return current->sid;
|
return current->sid.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_getsid(Registers*, SyscallArgs args)
|
Result<u64> sys_getsid(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
if (pid == 0) pid = current->id;
|
if (pid == 0) pid = current->id;
|
||||||
|
|
||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* p = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
return thread->sid;
|
return p->sid.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
||||||
@ -201,12 +201,13 @@ Result<u64> sys_fchmodat(Registers*, SyscallArgs args)
|
|||||||
mode_t mode = (mode_t)args[2];
|
mode_t mode = (mode_t)args[2];
|
||||||
int flags = (int)args[3];
|
int flags = (int)args[3];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_wpath));
|
TRY(check_pledge(current, Promise::p_wpath));
|
||||||
|
auto credentials = current->credentials();
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
if (current->auth.euid != 0 && current->auth.euid != inode->metadata().uid) return err(EPERM);
|
if (credentials.euid != 0 && credentials.euid != inode->metadata().uid) return err(EPERM);
|
||||||
|
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.mode = mode;
|
metadata.mode = mode;
|
||||||
@ -223,12 +224,13 @@ Result<u64> sys_fchownat(Registers*, SyscallArgs args)
|
|||||||
gid_t gid = (u32)args[3];
|
gid_t gid = (u32)args[3];
|
||||||
int flags = (int)args[4];
|
int flags = (int)args[4];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_chown));
|
TRY(check_pledge(current, Promise::p_chown));
|
||||||
|
auto credentials = current->credentials();
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
|
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (credentials.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
if (uid != (uid_t)-1) metadata.uid = uid;
|
if (uid != (uid_t)-1) metadata.uid = uid;
|
||||||
@ -243,20 +245,20 @@ Result<u64> sys_getgroups(Registers*, SyscallArgs args)
|
|||||||
int ngroups = (int)args[0];
|
int ngroups = (int)args[0];
|
||||||
gid_t* grouplist = (gid_t*)args[1];
|
gid_t* grouplist = (gid_t*)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
if (!ngroups) return current->extra_groups.size();
|
auto groups = current->extra_groups.lock();
|
||||||
|
|
||||||
|
if (!ngroups) return groups->size();
|
||||||
if (ngroups < 0) return err(EINVAL);
|
if (ngroups < 0) return err(EINVAL);
|
||||||
|
|
||||||
if (static_cast<usize>(ngroups) < current->extra_groups.size()) return err(EINVAL);
|
if (static_cast<usize>(ngroups) < groups->size()) return err(EINVAL);
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user(grouplist, current->extra_groups.data(),
|
if (!MemoryManager::copy_to_user(grouplist, groups->data(), groups->size() * sizeof(gid_t))) return err(EFAULT);
|
||||||
current->extra_groups.size() * sizeof(gid_t)))
|
|
||||||
return err(EFAULT);
|
|
||||||
|
|
||||||
return current->extra_groups.size();
|
return groups->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> sys_setgroups(Registers*, SyscallArgs args)
|
Result<u64> sys_setgroups(Registers*, SyscallArgs args)
|
||||||
@ -264,26 +266,27 @@ Result<u64> sys_setgroups(Registers*, SyscallArgs args)
|
|||||||
int ngroups = (int)args[0];
|
int ngroups = (int)args[0];
|
||||||
const gid_t* grouplist = (const gid_t*)args[1];
|
const gid_t* grouplist = (const gid_t*)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_id));
|
TRY(check_pledge(current, Promise::p_id));
|
||||||
|
|
||||||
Credentials& auth = current->auth;
|
auto credentials = current->credentials();
|
||||||
if (auth.euid != 0) return err(EPERM);
|
if (credentials.euid != 0) return err(EPERM);
|
||||||
|
|
||||||
|
auto groups = current->extra_groups.lock();
|
||||||
|
|
||||||
if (!ngroups)
|
if (!ngroups)
|
||||||
{
|
{
|
||||||
current->extra_groups.clear();
|
groups->clear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngroups < 0 || ngroups > 32) return err(EINVAL);
|
if (ngroups < 0 || ngroups > 32) return err(EINVAL);
|
||||||
|
|
||||||
TRY(current->extra_groups.try_reserve(ngroups));
|
TRY(groups->try_reserve(ngroups));
|
||||||
|
|
||||||
current->extra_groups.mutate([&](gid_t* list, usize) -> usize {
|
groups->mutate([&](gid_t* list, usize) -> usize {
|
||||||
if (MemoryManager::copy_from_user(grouplist, list, ngroups * sizeof(gid_t))) return ngroups;
|
if (MemoryManager::copy_from_user(grouplist, list, ngroups * sizeof(gid_t))) return ngroups;
|
||||||
return current->extra_groups.size();
|
return groups->size();
|
||||||
});
|
});
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -12,7 +12,7 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
int flags = (int)args[2];
|
int flags = (int)args[2];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_cpath));
|
TRY(check_pledge(current, Promise::p_cpath));
|
||||||
|
|
||||||
auto dirname = TRY(PathParser::dirname(path.view()));
|
auto dirname = TRY(PathParser::dirname(path.view()));
|
||||||
@ -23,13 +23,14 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
|
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
|
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
|
||||||
if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
auto auth = current->credentials();
|
||||||
|
if (!VFS::can_write(inode, current)) return err(EACCES);
|
||||||
|
|
||||||
auto child = TRY(inode->find(basename.chars()));
|
auto child = TRY(inode->find(basename.chars()));
|
||||||
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
|
||||||
|
|
||||||
if (current->auth.euid != 0 && VFS::is_sticky(inode) && current->auth.euid != inode->metadata().uid &&
|
if (auth.euid != 0 && VFS::is_sticky(inode) && auth.euid != inode->metadata().uid &&
|
||||||
current->auth.euid != child->metadata().uid)
|
auth.euid != child->metadata().uid)
|
||||||
return err(EACCES);
|
return err(EACCES);
|
||||||
|
|
||||||
TRY(inode->remove_entry(basename.chars()));
|
TRY(inode->remove_entry(basename.chars()));
|
||||||
@ -45,14 +46,14 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (target.is_empty()) return err(ENOENT);
|
if (target.is_empty()) return err(ENOENT);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_cpath));
|
TRY(check_pledge(current, Promise::p_cpath));
|
||||||
|
|
||||||
auto parent = TRY(PathParser::dirname(linkpath.view()));
|
auto parent = TRY(PathParser::dirname(linkpath.view()));
|
||||||
|
|
||||||
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
||||||
|
|
||||||
@ -60,8 +61,9 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
|
auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view()));
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
auto auth = current->credentials();
|
||||||
metadata.gid = current->auth.egid;
|
metadata.uid = auth.euid;
|
||||||
|
metadata.gid = auth.egid;
|
||||||
TRY(inode->set_metadata(metadata));
|
TRY(inode->set_metadata(metadata));
|
||||||
TRY(parent_inode->add_entry(inode, child_name.chars()));
|
TRY(parent_inode->add_entry(inode, child_name.chars()));
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ Result<u64> sys_readlinkat(Registers*, SyscallArgs args)
|
|||||||
char* buf = (char*)args[2];
|
char* buf = (char*)args[2];
|
||||||
usize bufsiz = (usize)args[3];
|
usize bufsiz = (usize)args[3];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_rpath));
|
TRY(check_pledge(current, Promise::p_rpath));
|
||||||
|
|
||||||
auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false));
|
auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false));
|
||||||
@ -101,7 +103,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
|||||||
auto newpath = TRY(MemoryManager::strdup_from_user(args[3]));
|
auto newpath = TRY(MemoryManager::strdup_from_user(args[3]));
|
||||||
int flags = (int)args[4];
|
int flags = (int)args[4];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_cpath));
|
TRY(check_pledge(current, Promise::p_cpath));
|
||||||
|
|
||||||
auto parent = TRY(PathParser::dirname(newpath.view()));
|
auto parent = TRY(PathParser::dirname(newpath.view()));
|
||||||
@ -115,7 +117,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (target->fs() != parent_inode->fs()) return err(EXDEV);
|
if (target->fs() != parent_inode->fs()) return err(EXDEV);
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(newpath.view()));
|
auto child_name = TRY(PathParser::basename(newpath.view()));
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Result<u64> sys_memstat(Registers*, SyscallArgs args)
|
Result<u64> sys_memstat(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
struct membuf buf;
|
struct membuf buf;
|
||||||
|
@ -10,14 +10,14 @@ Result<u64> sys_mkdir(Registers*, SyscallArgs args)
|
|||||||
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto path = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
mode_t mode = (mode_t)args[1];
|
mode_t mode = (mode_t)args[1];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
auto credentials = current->credentials();
|
||||||
TRY(check_pledge(current, Promise::p_cpath));
|
TRY(check_pledge(current, Promise::p_cpath));
|
||||||
|
|
||||||
auto inode = TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, ¤t->extra_groups,
|
auto inode = TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current, current->current_directory));
|
||||||
current->current_directory));
|
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
metadata.uid = credentials.euid;
|
||||||
metadata.gid = current->auth.egid;
|
metadata.gid = credentials.egid;
|
||||||
TRY(inode->set_metadata(metadata));
|
TRY(inode->set_metadata(metadata));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -20,7 +20,7 @@ Result<u64> sys_mmap(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (params.flags < 0) return err(EINVAL);
|
if (params.flags < 0) return err(EINVAL);
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
if (params.prot & PROT_EXEC) TRY(check_pledge(current, Promise::p_prot_exec));
|
if (params.prot & PROT_EXEC) TRY(check_pledge(current, Promise::p_prot_exec));
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -55,15 +55,15 @@ Result<u64> sys_mmap(Registers*, SyscallArgs args)
|
|||||||
shmem->prot |= params.prot;
|
shmem->prot |= params.prot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto space = current->address_space.lock();
|
||||||
|
|
||||||
u64 address;
|
u64 address;
|
||||||
if (!params.addr)
|
if (!params.addr) address = TRY((*space)->alloc_region(pages, params.prot, params.flags, params.offset, shmid));
|
||||||
address = TRY(current->address_space->alloc_region(pages, params.prot, params.flags, params.offset, shmid));
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// FIXME: We should be more flexible if MAP_FIXED was not specified.
|
// FIXME: We should be more flexible if MAP_FIXED was not specified.
|
||||||
address = align_down<ARCH_PAGE_SIZE>((u64)params.addr);
|
address = align_down<ARCH_PAGE_SIZE>((u64)params.addr);
|
||||||
if (!TRY(current->address_space->test_and_alloc_region(address, pages, params.prot, params.flags, params.offset,
|
if (!TRY((*space)->test_and_alloc_region(address, pages, params.prot, params.flags, params.offset, shmid)))
|
||||||
shmid)))
|
|
||||||
return err(ENOMEM);
|
return err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,10 +94,12 @@ Result<u64> sys_munmap(Registers*, SyscallArgs args)
|
|||||||
if (size == 0) return err(EINVAL);
|
if (size == 0) return err(EINVAL);
|
||||||
if (!is_aligned<ARCH_PAGE_SIZE>(address)) return err(EINVAL);
|
if (!is_aligned<ARCH_PAGE_SIZE>(address)) return err(EINVAL);
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
bool ok = TRY(current->address_space->free_region(address, ceil_div(size, ARCH_PAGE_SIZE)));
|
auto space = current->address_space.lock();
|
||||||
|
|
||||||
|
bool ok = TRY((*space)->free_region(address, ceil_div(size, ARCH_PAGE_SIZE)));
|
||||||
|
|
||||||
// POSIX says munmap should silently do nothing if the memory was not already mapped.
|
// POSIX says munmap should silently do nothing if the memory was not already mapped.
|
||||||
if (!ok) return 0;
|
if (!ok) return 0;
|
||||||
@ -119,10 +121,12 @@ Result<u64> sys_msync(Registers*, SyscallArgs args)
|
|||||||
if (!size) return 0;
|
if (!size) return 0;
|
||||||
if (!is_aligned<ARCH_PAGE_SIZE>(address)) return err(EINVAL);
|
if (!is_aligned<ARCH_PAGE_SIZE>(address)) return err(EINVAL);
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
TRY(current->address_space->sync_regions(address, ceil_div(size, ARCH_PAGE_SIZE)));
|
auto space = current->address_space.lock();
|
||||||
|
|
||||||
|
TRY((*space)->sync_regions(address, ceil_div(size, ARCH_PAGE_SIZE)));
|
||||||
|
|
||||||
return { 0 };
|
return { 0 };
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,12 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
|
|||||||
auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
|
auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
auto source = TRY(MemoryManager::strdup_from_user(args[2]));
|
auto source = TRY(MemoryManager::strdup_from_user(args[2]));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_mount));
|
TRY(check_pledge(current, Promise::p_mount));
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->credentials().euid != 0) return err(EPERM);
|
||||||
|
|
||||||
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
|
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
|
||||||
auto inode =
|
auto inode = TRY(VFS::resolve_path(source.chars(), current, current->current_directory));
|
||||||
TRY(VFS::resolve_path(source.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
|
||||||
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
|
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
|
||||||
dev_t device_id = inode->metadata().devid;
|
dev_t device_id = inode->metadata().devid;
|
||||||
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
|
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
|
||||||
@ -41,7 +40,7 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
|
|||||||
fs = TRY(factory(device));
|
fs = TRY(factory(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(VFS::mount(target.chars(), fs, current->auth, ¤t->extra_groups, current->current_directory));
|
TRY(VFS::mount(target.chars(), fs, current, current->current_directory));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -50,11 +49,11 @@ Result<u64> sys_umount(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto target = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_mount));
|
TRY(check_pledge(current, Promise::p_mount));
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->credentials().euid != 0) return err(EPERM);
|
||||||
|
|
||||||
TRY(VFS::umount(target.chars(), current->auth, ¤t->extra_groups, current->current_directory));
|
TRY(VFS::umount(target.chars(), current, current->current_directory));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -64,9 +63,9 @@ Result<u64> sys_pivot_root(Registers*, SyscallArgs args)
|
|||||||
auto new_root = TRY(MemoryManager::strdup_from_user(args[0]));
|
auto new_root = TRY(MemoryManager::strdup_from_user(args[0]));
|
||||||
auto put_old = TRY(MemoryManager::strdup_from_user(args[1]));
|
auto put_old = TRY(MemoryManager::strdup_from_user(args[1]));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_mount));
|
TRY(check_pledge(current, Promise::p_mount));
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->credentials().euid != 0) return err(EPERM);
|
||||||
|
|
||||||
TRY(VFS::pivot_root(new_root.chars(), put_old.chars(), current->current_directory));
|
TRY(VFS::pivot_root(new_root.chars(), put_old.chars(), current->current_directory));
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
int flags = (int)args[2];
|
int flags = (int)args[2];
|
||||||
mode_t mode = (mode_t)args[3];
|
mode_t mode = (mode_t)args[3];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> inode;
|
SharedPtr<VFS::Inode> inode;
|
||||||
|
|
||||||
@ -44,12 +44,12 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
{
|
{
|
||||||
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
|
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
|
||||||
{
|
{
|
||||||
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, ¤t->extra_groups,
|
auto auth = current->credentials();
|
||||||
parent_inode));
|
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current, parent_inode));
|
||||||
// FIXME: Pass these in create_file().
|
// FIXME: Pass these in create_file().
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
metadata.uid = auth.euid;
|
||||||
metadata.gid = current->auth.egid;
|
metadata.gid = auth.egid;
|
||||||
TRY(inode->set_metadata(metadata));
|
TRY(inode->set_metadata(metadata));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -59,8 +59,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
return err(EEXIST);
|
return err(EEXIST);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if ((flags & O_RDONLY) && !VFS::can_read(inode, current)) return err(EACCES);
|
||||||
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES);
|
if ((flags & O_WRONLY) && !VFS::can_write(inode, current)) return err(EACCES);
|
||||||
}
|
}
|
||||||
|
|
||||||
inode = TRY(inode->open());
|
inode = TRY(inode->open());
|
||||||
@ -72,11 +72,12 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (flags & O_TMPFILE)
|
if (flags & O_TMPFILE)
|
||||||
{
|
{
|
||||||
|
auto auth = current->credentials();
|
||||||
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);
|
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);
|
||||||
inode = TRY(inode->fs()->create_file_inode(mode & current->umask));
|
inode = TRY(inode->fs()->create_file_inode(mode & current->umask));
|
||||||
auto metadata = inode->metadata();
|
auto metadata = inode->metadata();
|
||||||
metadata.uid = current->auth.euid;
|
metadata.uid = auth.euid;
|
||||||
metadata.gid = current->auth.egid;
|
metadata.gid = auth.egid;
|
||||||
TRY(inode->set_metadata(metadata));
|
TRY(inode->set_metadata(metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,15 +92,15 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
|
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
|
||||||
|
|
||||||
int fd = TRY(current->allocate_fd(0));
|
auto descriptor =
|
||||||
|
FileDescriptor { TRY(make_shared<OpenFileDescription>(inode, flags & FLAGS_TO_KEEP)), 0, flags & O_CLOEXEC };
|
||||||
|
|
||||||
|
int fd = TRY(current->allocate_fd(0, descriptor));
|
||||||
|
|
||||||
#ifdef OPEN_DEBUG
|
#ifdef OPEN_DEBUG
|
||||||
kdbgln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
|
kdbgln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
current->fd_table[fd] =
|
|
||||||
FileDescriptor { TRY(make_shared<OpenFileDescription>(inode, flags & FLAGS_TO_KEEP)), 0, flags & O_CLOEXEC };
|
|
||||||
|
|
||||||
return (u64)fd;
|
return (u64)fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,10 +109,11 @@ Result<u64> sys_close(Registers*, SyscallArgs args)
|
|||||||
int fd = (int)args[0];
|
int fd = (int)args[0];
|
||||||
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
|
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
Option<FileDescriptor>& descriptor = current->fd_table[fd];
|
auto table = current->fd_table.lock();
|
||||||
|
Option<FileDescriptor>& descriptor = (*table)[fd];
|
||||||
|
|
||||||
if (!descriptor.has_value()) return err(EBADF);
|
if (!descriptor.has_value()) return err(EBADF);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ Result<u64> sys_pledge(Registers*, SyscallArgs args)
|
|||||||
int promises = TRY(parse_promises(args[0]));
|
int promises = TRY(parse_promises(args[0]));
|
||||||
int execpromises = TRY(parse_promises(args[1]));
|
int execpromises = TRY(parse_promises(args[1]));
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
if (promises >= 0)
|
if (promises >= 0)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,8 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);
|
if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
auto* thread = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
Vector<SharedPtr<VFS::Inode>> inodes;
|
Vector<SharedPtr<VFS::Inode>> inodes;
|
||||||
@ -78,8 +79,8 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
|
|||||||
if (!fds_with_events && (timeout > 0 || infinite))
|
if (!fds_with_events && (timeout > 0 || infinite))
|
||||||
{
|
{
|
||||||
kernel_sleep(10);
|
kernel_sleep(10);
|
||||||
timeout -= (10 - (int)current->sleep_ticks_left);
|
timeout -= (10 - (int)thread->sleep_ticks_left);
|
||||||
if (current->interrupted)
|
if (thread->interrupted)
|
||||||
{
|
{
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
free_impl(kfds);
|
free_impl(kfds);
|
||||||
|
@ -15,28 +15,29 @@ Result<u64> sys_pstat(Registers*, SyscallArgs args)
|
|||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
struct process* ps = (struct process*)args[1];
|
struct process* ps = (struct process*)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
|
|
||||||
// If pid == -1, return the PID of the last spawned thread.
|
// If pid == -1, return the PID of the last spawned thread.
|
||||||
if (pid == -1) return g_threads.expect_last()->id;
|
if (pid == -1) return g_processes.expect_last()->id;
|
||||||
|
|
||||||
auto* thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* target = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
struct process proc;
|
struct process proc;
|
||||||
proc.ps_pid = thread->id;
|
proc.ps_pid = target->id;
|
||||||
proc.ps_ppid = thread->parent ? thread->parent->id : 0;
|
proc.ps_ppid = target->parent ? target->parent->id : 0;
|
||||||
proc.ps_uid = thread->auth.uid;
|
auto auth = target->credentials();
|
||||||
proc.ps_gid = thread->auth.gid;
|
proc.ps_uid = auth.uid;
|
||||||
proc.ps_euid = thread->auth.euid;
|
proc.ps_gid = auth.gid;
|
||||||
proc.ps_egid = thread->auth.egid;
|
proc.ps_euid = auth.euid;
|
||||||
proc.ps_state = (int)thread->state;
|
proc.ps_egid = auth.egid;
|
||||||
proc.ps_flags = thread->is_kernel ? PS_FLAG_KRNL : 0;
|
proc.ps_state = 0; // FIXME: this is thread-specific now
|
||||||
set_timespec(proc.ps_time, thread->user_ticks_self + thread->kernel_ticks_self);
|
proc.ps_flags = 0; // FIXME: add flags
|
||||||
set_timespec(proc.ps_ktime, thread->kernel_ticks_self);
|
set_timespec(proc.ps_time, target->user_ticks_self + target->kernel_ticks_self);
|
||||||
set_timespec(proc.ps_utime, thread->kernel_ticks_children);
|
set_timespec(proc.ps_ktime, target->kernel_ticks_self);
|
||||||
strlcpy(proc.ps_name, thread->cmdline.chars(), sizeof(proc.ps_name));
|
set_timespec(proc.ps_utime, target->kernel_ticks_children);
|
||||||
strlcpy(proc.ps_cwd, thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars(),
|
strlcpy(proc.ps_name, target->cmdline.chars(), sizeof(proc.ps_name));
|
||||||
|
strlcpy(proc.ps_cwd, target->current_directory_path.is_empty() ? "/" : target->current_directory_path.chars(),
|
||||||
sizeof(proc.ps_cwd));
|
sizeof(proc.ps_cwd));
|
||||||
|
|
||||||
if (!MemoryManager::copy_to_user_typed(ps, &proc)) return err(EFAULT);
|
if (!MemoryManager::copy_to_user_typed(ps, &proc)) return err(EFAULT);
|
||||||
|
@ -16,7 +16,7 @@ Result<u64> sys_getrusage(Registers*, SyscallArgs args)
|
|||||||
int who = (int)args[0];
|
int who = (int)args[0];
|
||||||
struct rusage* ru = (struct rusage*)args[1];
|
struct rusage* ru = (struct rusage*)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
struct rusage kru;
|
struct rusage kru;
|
||||||
switch (who)
|
switch (who)
|
||||||
|
@ -16,7 +16,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
const struct itimerval* new_timer = (const struct itimerval*)args[1];
|
const struct itimerval* new_timer = (const struct itimerval*)args[1];
|
||||||
struct itimerval* old_timer = (struct itimerval*)args[2];
|
struct itimerval* old_timer = (struct itimerval*)args[2];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ Result<u64> sys_setitimer(Registers*, SyscallArgs args)
|
|||||||
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
|
if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT);
|
||||||
|
|
||||||
timer->signo = SIGALRM; // FIXME: Also use SIGVTALRM or SIGPROF for other timer types.
|
timer->signo = SIGALRM; // FIXME: Also use SIGVTALRM or SIGPROF for other timer types.
|
||||||
timer->thread = current;
|
timer->process = current;
|
||||||
|
|
||||||
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
|
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0)
|
||||||
{
|
{
|
||||||
@ -93,7 +93,7 @@ Result<u64> sys_timer_create(Registers*, SyscallArgs args)
|
|||||||
struct sigevent* sevp = (struct sigevent*)args[1];
|
struct sigevent* sevp = (struct sigevent*)args[1];
|
||||||
timer_t* timerid = (timer_t*)args[2];
|
timer_t* timerid = (timer_t*)args[2];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -118,7 +118,6 @@ Result<u64> sys_timer_create(Registers*, SyscallArgs args)
|
|||||||
if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL);
|
if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL);
|
||||||
|
|
||||||
int id = TRY(current->allocate_timerid());
|
int id = TRY(current->allocate_timerid());
|
||||||
current->posix_timers[id] = Timer {};
|
|
||||||
|
|
||||||
Timer* timer = current->posix_timers[id].value_ptr();
|
Timer* timer = current->posix_timers[id].value_ptr();
|
||||||
timer->signo = ksevp.sigev_signo;
|
timer->signo = ksevp.sigev_signo;
|
||||||
@ -138,7 +137,7 @@ Result<u64> sys_timer_settime(Registers*, SyscallArgs args)
|
|||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
||||||
if (flags > 0) return err(ENOTSUP);
|
if (flags > 0) return err(ENOTSUP);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -170,7 +169,7 @@ Result<u64> sys_timer_settime(Registers*, SyscallArgs args)
|
|||||||
Clock* clock = timer->designated_clock;
|
Clock* clock = timer->designated_clock;
|
||||||
check(clock);
|
check(clock);
|
||||||
|
|
||||||
timer->thread = current;
|
timer->process = current;
|
||||||
|
|
||||||
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0)
|
if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0)
|
||||||
{
|
{
|
||||||
@ -196,7 +195,7 @@ Result<u64> sys_timer_gettime(Registers*, SyscallArgs args)
|
|||||||
struct itimerspec* value = (struct itimerspec*)args[1];
|
struct itimerspec* value = (struct itimerspec*)args[1];
|
||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
@ -225,7 +224,7 @@ Result<u64> sys_timer_delete(Registers*, SyscallArgs args)
|
|||||||
timer_t timerid = (timer_t)args[0];
|
timer_t timerid = (timer_t)args[0];
|
||||||
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
|
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current, Promise::p_stdio));
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Result<u64> sys_sigreturn(Registers* regs, SyscallArgs)
|
|||||||
Result<u64> sys_sigaction(Registers*, SyscallArgs args)
|
Result<u64> sys_sigaction(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current->process, Promise::p_stdio));
|
||||||
|
|
||||||
int signo = (int)args[0];
|
int signo = (int)args[0];
|
||||||
const struct sigaction* act = (const struct sigaction*)args[1];
|
const struct sigaction* act = (const struct sigaction*)args[1];
|
||||||
@ -48,15 +48,16 @@ Result<u64> sys_sigaction(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Result<u64> sys_kill(Registers*, SyscallArgs args)
|
Result<u64> sys_kill(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_proc));
|
TRY(check_pledge(current, Promise::p_proc));
|
||||||
|
|
||||||
pid_t pid = (pid_t)args[0];
|
pid_t pid = (pid_t)args[0];
|
||||||
int signo = (int)args[1];
|
int signo = (int)args[1];
|
||||||
|
|
||||||
auto send_signal = [&](Thread* target) -> Result<void> {
|
auto send_signal = [&](Process* target) -> Result<void> {
|
||||||
if (current->auth.euid != 0 && current->auth.euid != target->auth.euid &&
|
auto this_auth = current->credentials();
|
||||||
current->auth.egid != target->auth.egid)
|
auto other_auth = target->credentials();
|
||||||
|
if (this_auth.euid != 0 && this_auth.euid != other_auth.euid && this_auth.egid != other_auth.egid)
|
||||||
return err(EPERM);
|
return err(EPERM);
|
||||||
if (target->is_kernel) return {};
|
if (target->is_kernel) return {};
|
||||||
if (signo == 0) return {};
|
if (signo == 0) return {};
|
||||||
@ -68,14 +69,14 @@ Result<u64> sys_kill(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
auto* target = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
auto* target = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
TRY(send_signal(target));
|
TRY(send_signal(target));
|
||||||
}
|
}
|
||||||
else if (pid == 0)
|
else if (pid == 0)
|
||||||
{
|
{
|
||||||
int errno = -1;
|
int errno = -1;
|
||||||
bool pgid_exists = false;
|
bool pgid_exists = false;
|
||||||
Scheduler::for_each_in_process_group(current->pgid, [&](Thread* target) {
|
Scheduler::for_each_in_process_group(current->pgid, [&](Process* target) {
|
||||||
pgid_exists = true;
|
pgid_exists = true;
|
||||||
auto rc = send_signal(target);
|
auto rc = send_signal(target);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
@ -90,17 +91,17 @@ Result<u64> sys_kill(Registers*, SyscallArgs args)
|
|||||||
}
|
}
|
||||||
else if (pid == -1)
|
else if (pid == -1)
|
||||||
{
|
{
|
||||||
for (auto* thread : g_threads)
|
for (auto* process : g_processes)
|
||||||
{
|
{
|
||||||
// We ignore permission errors here.
|
// We ignore permission errors here.
|
||||||
if (thread != current && thread->id != 1) send_signal(thread);
|
if (process != current && process->id != 1) send_signal(process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pid < -1)
|
else if (pid < -1)
|
||||||
{
|
{
|
||||||
int errno = -1;
|
int errno = -1;
|
||||||
bool pgid_exists = false;
|
bool pgid_exists = false;
|
||||||
Scheduler::for_each_in_process_group(-pid, [&](Thread* target) {
|
Scheduler::for_each_in_process_group(-pid, [&](Process* target) {
|
||||||
pgid_exists = true;
|
pgid_exists = true;
|
||||||
auto rc = send_signal(target);
|
auto rc = send_signal(target);
|
||||||
if (rc.has_error())
|
if (rc.has_error())
|
||||||
@ -120,7 +121,7 @@ Result<u64> sys_kill(Registers*, SyscallArgs args)
|
|||||||
Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
||||||
{
|
{
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current->process, Promise::p_stdio));
|
||||||
|
|
||||||
int how = (int)args[0];
|
int how = (int)args[0];
|
||||||
const sigset_t* set = (const sigset_t*)args[1];
|
const sigset_t* set = (const sigset_t*)args[1];
|
||||||
|
@ -15,14 +15,13 @@ Result<u64> sys_socket(Registers*, SyscallArgs args)
|
|||||||
if (type != SOCK_STREAM) return err(EPROTOTYPE);
|
if (type != SOCK_STREAM) return err(EPROTOTYPE);
|
||||||
if (domain != AF_UNIX) return err(EAFNOSUPPORT);
|
if (domain != AF_UNIX) return err(EAFNOSUPPORT);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_unix));
|
TRY(check_pledge(current, Promise::p_unix));
|
||||||
|
|
||||||
auto socket = TRY(make_shared<UnixSocket>());
|
auto socket = TRY(make_shared<UnixSocket>());
|
||||||
|
|
||||||
int fd = TRY(current->allocate_fd(0));
|
auto descriptor = FileDescriptor { TRY(make_shared<OpenFileDescription>(socket, O_RDWR)), 0 };
|
||||||
|
int fd = TRY(current->allocate_fd(0, descriptor));
|
||||||
current->fd_table[fd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(socket, O_RDWR)), 0 };
|
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
@ -37,7 +36,7 @@ Result<u64> sys_bind(Registers*, SyscallArgs args)
|
|||||||
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
|
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
|
||||||
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
|
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_unix));
|
TRY(check_pledge(current, Promise::p_unix));
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_fd(sockfd))->inode();
|
auto inode = TRY(current->resolve_fd(sockfd))->inode();
|
||||||
@ -61,7 +60,7 @@ Result<u64> sys_connect(Registers* regs, SyscallArgs args)
|
|||||||
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
|
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
|
||||||
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
|
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_unix));
|
TRY(check_pledge(current, Promise::p_unix));
|
||||||
|
|
||||||
auto description = TRY(current->resolve_fd(sockfd))->description;
|
auto description = TRY(current->resolve_fd(sockfd))->description;
|
||||||
@ -80,7 +79,7 @@ Result<u64> sys_listen(Registers*, SyscallArgs args)
|
|||||||
int sockfd = (int)args[0];
|
int sockfd = (int)args[0];
|
||||||
int backlog = (int)args[1];
|
int backlog = (int)args[1];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_unix));
|
TRY(check_pledge(current, Promise::p_unix));
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_fd(sockfd))->inode();
|
auto inode = TRY(current->resolve_fd(sockfd))->inode();
|
||||||
@ -108,7 +107,7 @@ Result<u64> sys_accept(Registers* regs, SyscallArgs args)
|
|||||||
if (!MemoryManager::copy_from_user_typed(addrlen, &len)) return err(EFAULT);
|
if (!MemoryManager::copy_from_user_typed(addrlen, &len)) return err(EFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_unix));
|
TRY(check_pledge(current, Promise::p_unix));
|
||||||
|
|
||||||
auto description = TRY(current->resolve_fd(sockfd))->description;
|
auto description = TRY(current->resolve_fd(sockfd))->description;
|
||||||
@ -121,8 +120,8 @@ Result<u64> sys_accept(Registers* regs, SyscallArgs args)
|
|||||||
socklen_t client_len;
|
socklen_t client_len;
|
||||||
auto new_description = TRY(socket->accept(regs, description->flags, &client, &client_len));
|
auto new_description = TRY(socket->accept(regs, description->flags, &client, &client_len));
|
||||||
|
|
||||||
int fd = TRY(current->allocate_fd(0));
|
auto descriptor = FileDescriptor { new_description, 0 };
|
||||||
current->fd_table[fd] = FileDescriptor { new_description, 0 };
|
int fd = TRY(current->allocate_fd(0, descriptor));
|
||||||
|
|
||||||
if (client_len < len) len = client_len;
|
if (client_len < len) len = client_len;
|
||||||
if (addr)
|
if (addr)
|
||||||
|
@ -33,7 +33,7 @@ Result<u64> sys_fstatat(Registers*, SyscallArgs args)
|
|||||||
stat* st = (stat*)args[2];
|
stat* st = (stat*)args[2];
|
||||||
int flags = (int)args[3];
|
int flags = (int)args[3];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_rpath));
|
TRY(check_pledge(current, Promise::p_rpath));
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW)));
|
||||||
@ -68,22 +68,24 @@ Result<u64> sys_faccessat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Credentials creds;
|
Credentials creds;
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_rpath));
|
TRY(check_pledge(current, Promise::p_rpath));
|
||||||
|
auto auth = current->credentials();
|
||||||
|
|
||||||
if (flags & AT_EACCESS) creds = current->auth;
|
if (flags & AT_EACCESS) creds = auth;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto auth = current->auth;
|
|
||||||
creds.euid = auth.uid;
|
creds.euid = auth.uid;
|
||||||
creds.egid = auth.gid;
|
creds.egid = auth.gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
|
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
|
||||||
|
|
||||||
if ((amode & R_OK) && !VFS::can_read(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
auto groups = current->extra_groups.lock();
|
||||||
if ((amode & W_OK) && !VFS::can_write(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
|
||||||
if ((amode & X_OK) && !VFS::can_execute(inode, creds, ¤t->extra_groups)) return err(EACCES);
|
if ((amode & R_OK) && !VFS::can_read(inode, creds, &groups.ref())) return err(EACCES);
|
||||||
|
if ((amode & W_OK) && !VFS::can_write(inode, creds, &groups.ref())) return err(EACCES);
|
||||||
|
if ((amode & X_OK) && !VFS::can_execute(inode, creds, &groups.ref())) return err(EACCES);
|
||||||
|
|
||||||
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
|
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
|
||||||
// otherwise.
|
// otherwise.
|
||||||
|
@ -40,9 +40,9 @@ Result<u64> sys_sethostname(Registers*, SyscallArgs args)
|
|||||||
const char* buf = (const char*)args[0];
|
const char* buf = (const char*)args[0];
|
||||||
usize length = (usize)args[1];
|
usize length = (usize)args[1];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Process* current = Process::current();
|
||||||
TRY(check_pledge(current, Promise::p_host));
|
TRY(check_pledge(current, Promise::p_host));
|
||||||
if (current->auth.euid != 0) return err(EPERM);
|
if (current->credentials().euid != 0) return err(EPERM);
|
||||||
|
|
||||||
if (length >= _UTSNAME_LENGTH) return err(EINVAL);
|
if (length >= _UTSNAME_LENGTH) return err(EINVAL);
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ Result<u64> sys_usleep(Registers*, SyscallArgs args)
|
|||||||
useconds_t us = (useconds_t)args[0];
|
useconds_t us = (useconds_t)args[0];
|
||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current->process, Promise::p_stdio));
|
||||||
|
|
||||||
// FIXME: Allow usleep() to use a more precise resolution.
|
// FIXME: Allow usleep() to use a more precise resolution.
|
||||||
if (us < 1000) return 0;
|
if (us < 1000) return 0;
|
||||||
|
|
||||||
kernel_sleep(us / 1000);
|
kernel_sleep(us / 1000);
|
||||||
|
|
||||||
return current->sleep_ticks_left;
|
return current->sleep_ticks_left.load();
|
||||||
}
|
}
|
||||||
|
@ -12,21 +12,21 @@ Result<u64> sys_waitpid(Registers* regs, SyscallArgs args)
|
|||||||
int options = (int)args[2];
|
int options = (int)args[2];
|
||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
TRY(check_pledge(current, Promise::p_stdio));
|
TRY(check_pledge(current->process, Promise::p_stdio));
|
||||||
|
|
||||||
Thread* thread;
|
Process* target;
|
||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
target = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||||
|
|
||||||
if (thread->parent && thread->parent != current) return err(ECHILD);
|
if (target->parent && target->parent != current->process) return err(ECHILD);
|
||||||
|
|
||||||
if (options & WNOHANG) return err(EAGAIN);
|
if (options & WNOHANG) return err(EAGAIN);
|
||||||
|
|
||||||
wait_for_child:
|
wait_for_child:
|
||||||
if (thread->state != ThreadState::Exited) kernel_wait(pid);
|
if (!target->dead()) kernel_wait(pid);
|
||||||
if (current->interrupted)
|
if (current->interrupted && (current->pending_signal_count() > 1 || current->pending_signal() != SIGCHLD))
|
||||||
{
|
{
|
||||||
kdbgln("signal: waitpid interrupted by signal");
|
kdbgln("signal: waitpid interrupted by signal");
|
||||||
if (current->will_ignore_pending_signal())
|
if (current->will_ignore_pending_signal())
|
||||||
@ -37,20 +37,20 @@ Result<u64> sys_waitpid(Registers* regs, SyscallArgs args)
|
|||||||
return err(EINTR);
|
return err(EINTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
check(thread->state == ThreadState::Exited);
|
check(target->dead());
|
||||||
}
|
}
|
||||||
else if (pid == -1)
|
else if (pid == -1)
|
||||||
{
|
{
|
||||||
if (!Scheduler::has_children(current)) return err(ECHILD);
|
if (!Scheduler::has_children(current->process)) return err(ECHILD);
|
||||||
|
|
||||||
auto child = Scheduler::find_exited_child(current);
|
auto child = Scheduler::find_exited_child(current->process);
|
||||||
if (!child.has_value())
|
if (!child.has_value())
|
||||||
{
|
{
|
||||||
if (options & WNOHANG) return err(EAGAIN);
|
if (options & WNOHANG) return err(EAGAIN);
|
||||||
|
|
||||||
wait_for_any_child:
|
wait_for_any_child:
|
||||||
kernel_wait(pid);
|
kernel_wait(pid);
|
||||||
if (current->interrupted)
|
if (current->interrupted && (current->pending_signal_count() > 1 || current->pending_signal() != SIGCHLD))
|
||||||
{
|
{
|
||||||
kdbgln("signal: waitpid interrupted by signal");
|
kdbgln("signal: waitpid interrupted by signal");
|
||||||
if (current->will_ignore_pending_signal())
|
if (current->will_ignore_pending_signal())
|
||||||
@ -61,26 +61,26 @@ Result<u64> sys_waitpid(Registers* regs, SyscallArgs args)
|
|||||||
return err(EINTR);
|
return err(EINTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
check(current->child_being_waited_for.value_or(-1) != -1);
|
check(current->child_being_waited_for != -1);
|
||||||
|
|
||||||
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(*current->child_being_waited_for), ESRCH));
|
target = TRY(Result<Process*>::from_option(Scheduler::find_by_pid(current->child_being_waited_for), ESRCH));
|
||||||
check(thread->state == ThreadState::Exited);
|
check(target->dead());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
thread = child.value();
|
target = child.value();
|
||||||
}
|
}
|
||||||
else // FIXME: Now that we have process groups, implement the cases where pid = 0 and pid < -1.
|
else // FIXME: Now that we have process groups, implement the cases where pid = 0 and pid < -1.
|
||||||
return err(ENOTSUP);
|
return err(ENOTSUP);
|
||||||
|
|
||||||
current->child_being_waited_for = {};
|
current->child_being_waited_for = -2;
|
||||||
|
|
||||||
int status = (int)thread->status;
|
int status = (int)target->status;
|
||||||
u64 id = thread->id;
|
u64 id = target->id;
|
||||||
|
|
||||||
current->user_ticks_children += thread->user_ticks_self + thread->user_ticks_children;
|
current->process->user_ticks_children += target->user_ticks_self + target->user_ticks_children;
|
||||||
current->kernel_ticks_children += thread->kernel_ticks_self + thread->kernel_ticks_children;
|
current->process->kernel_ticks_children += target->kernel_ticks_self + target->kernel_ticks_children;
|
||||||
|
|
||||||
thread->state = ThreadState::Dying;
|
target->thread_count = PROCESS_SHOULD_REAP;
|
||||||
Scheduler::signal_reap_thread();
|
Scheduler::signal_reap_thread();
|
||||||
|
|
||||||
if (status_ptr)
|
if (status_ptr)
|
||||||
|
@ -124,7 +124,7 @@ void Clock::tick()
|
|||||||
{
|
{
|
||||||
this->m_timer_queue.remove(t);
|
this->m_timer_queue.remove(t);
|
||||||
t->active_clock = nullptr;
|
t->active_clock = nullptr;
|
||||||
t->thread->send_signal(t->signo);
|
t->process->send_signal(t->signo);
|
||||||
if (t->restart) timers_to_be_restarted.append(t);
|
if (t->restart) timers_to_be_restarted.append(t);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
#include <luna/Stack.h>
|
#include <luna/Stack.h>
|
||||||
|
|
||||||
static Thread g_idle;
|
static Thread g_idle;
|
||||||
|
static Process g_idle_process;
|
||||||
static Thread* g_current = nullptr;
|
static Thread* g_current = nullptr;
|
||||||
static Thread* g_init = nullptr;
|
static Process* g_init = nullptr;
|
||||||
static Thread* g_reap = nullptr;
|
static Thread* g_reap = nullptr;
|
||||||
static Thread* g_oom = nullptr;
|
static Thread* g_oom = nullptr;
|
||||||
|
|
||||||
@ -22,15 +23,20 @@ namespace Scheduler
|
|||||||
{
|
{
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
g_idle.id = 0;
|
g_idle.tid = 0;
|
||||||
g_idle.init_regs_kernel();
|
g_idle.init_regs_kernel();
|
||||||
g_idle.set_ip((u64)CPU::idle_loop);
|
g_idle.set_ip((u64)CPU::idle_loop);
|
||||||
g_idle.state = ThreadState::Idle;
|
g_idle.state = ThreadState::Idle;
|
||||||
g_idle.is_kernel = true;
|
g_idle.is_kernel = true;
|
||||||
g_idle.parent = nullptr;
|
g_idle.process = &g_idle_process;
|
||||||
g_idle.cmdline = "[idle]";
|
g_idle.cmdline = "[idle]";
|
||||||
g_idle.active_directory = nullptr;
|
g_idle.active_directory = nullptr;
|
||||||
|
|
||||||
|
g_idle_process.id = 0;
|
||||||
|
g_idle_process.parent = nullptr;
|
||||||
|
g_idle_process.thread_count = 1;
|
||||||
|
g_idle_process.is_kernel = true;
|
||||||
|
|
||||||
g_idle.ticks_left = 1;
|
g_idle.ticks_left = 1;
|
||||||
|
|
||||||
// Map some stack for the idle task
|
// Map some stack for the idle task
|
||||||
@ -42,7 +48,7 @@ namespace Scheduler
|
|||||||
|
|
||||||
g_idle.stack = idle_stack;
|
g_idle.stack = idle_stack;
|
||||||
|
|
||||||
kinfoln("Created idle thread: id %d with ip %#lx and sp %#lx", g_idle.id, g_idle.ip(), g_idle.sp());
|
kinfoln("Created idle thread: id %d with ip %#lx and sp %#lx", g_idle_process.id, g_idle.ip(), g_idle.sp());
|
||||||
|
|
||||||
g_current = &g_idle;
|
g_current = &g_idle;
|
||||||
}
|
}
|
||||||
@ -57,7 +63,7 @@ namespace Scheduler
|
|||||||
return &g_idle;
|
return &g_idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread* init_thread()
|
Process* init_process()
|
||||||
{
|
{
|
||||||
return g_init;
|
return g_init;
|
||||||
}
|
}
|
||||||
@ -88,30 +94,38 @@ namespace Scheduler
|
|||||||
// If anything fails, make sure to clean up.
|
// If anything fails, make sure to clean up.
|
||||||
auto guard = make_scope_guard([&] { delete thread; });
|
auto guard = make_scope_guard([&] { delete thread; });
|
||||||
|
|
||||||
|
Process* process = TRY(make<Process>());
|
||||||
|
|
||||||
|
auto guard2 = make_scope_guard([&] { delete process; });
|
||||||
|
|
||||||
const u64 thread_stack_vm = TRY(MemoryManager::alloc_for_kernel(4, MMU::NoExecute | MMU::ReadWrite));
|
const u64 thread_stack_vm = TRY(MemoryManager::alloc_for_kernel(4, MMU::NoExecute | MMU::ReadWrite));
|
||||||
|
|
||||||
guard.deactivate();
|
guard.deactivate();
|
||||||
|
guard2.deactivate();
|
||||||
|
|
||||||
const Stack thread_stack { thread_stack_vm, ARCH_PAGE_SIZE * 4 };
|
const Stack thread_stack { thread_stack_vm, ARCH_PAGE_SIZE * 4 };
|
||||||
thread->set_sp(thread_stack.top());
|
thread->set_sp(thread_stack.top());
|
||||||
|
|
||||||
thread->stack = thread_stack;
|
thread->stack = thread_stack;
|
||||||
|
|
||||||
thread->cmdline = name;
|
thread->cmdline = name;
|
||||||
|
|
||||||
thread->is_kernel = true;
|
thread->is_kernel = true;
|
||||||
thread->active_directory = MMU::kernel_page_directory();
|
thread->active_directory = MMU::kernel_page_directory();
|
||||||
|
|
||||||
thread->virtual_clock.set_resolution(1'000'000);
|
thread->process = process;
|
||||||
thread->profiling_clock.set_resolution(1'000'000);
|
|
||||||
|
|
||||||
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
|
process->id = thread->tid;
|
||||||
|
process->parent = nullptr;
|
||||||
|
process->thread_count = 1;
|
||||||
|
process->virtual_clock.set_resolution(1'000'000);
|
||||||
|
process->profiling_clock.set_resolution(1'000'000);
|
||||||
|
process->cmdline = name;
|
||||||
|
process->is_kernel = true;
|
||||||
|
|
||||||
g_threads.append(thread);
|
g_threads.append(thread);
|
||||||
|
g_processes.append(process);
|
||||||
|
|
||||||
thread->state = ThreadState::Runnable;
|
thread->state = ThreadState::Runnable;
|
||||||
|
|
||||||
kinfoln("Created kernel thread: id %d with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp());
|
kinfoln("Created kernel thread: id %d with ip %#lx and sp %#lx", process->id, thread->ip(), thread->sp());
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
@ -149,14 +163,17 @@ namespace Scheduler
|
|||||||
check(!g_init);
|
check(!g_init);
|
||||||
|
|
||||||
Thread* const thread = TRY(make<Thread>());
|
Thread* const thread = TRY(make<Thread>());
|
||||||
|
Process* const process = TRY(make<Process>());
|
||||||
|
|
||||||
thread->state = ThreadState::None;
|
thread->state = ThreadState::None;
|
||||||
thread->is_kernel = false;
|
thread->tid = 1;
|
||||||
thread->id = 1;
|
|
||||||
thread->pgid = 1;
|
|
||||||
thread->cmdline = name;
|
thread->cmdline = name;
|
||||||
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
|
thread->process = process;
|
||||||
thread->extra_groups = {};
|
|
||||||
|
process->id = 1;
|
||||||
|
process->pgid = 1;
|
||||||
|
process->thread_count = 1;
|
||||||
|
process->cmdline = name;
|
||||||
|
|
||||||
Vector<String> args;
|
Vector<String> args;
|
||||||
auto name_string = TRY(String::from_cstring(name));
|
auto name_string = TRY(String::from_cstring(name));
|
||||||
@ -164,7 +181,10 @@ namespace Scheduler
|
|||||||
|
|
||||||
Vector<String> env;
|
Vector<String> env;
|
||||||
|
|
||||||
auto guard = make_scope_guard([&] { delete thread; });
|
auto guard = make_scope_guard([&] {
|
||||||
|
delete thread;
|
||||||
|
delete process;
|
||||||
|
});
|
||||||
|
|
||||||
// Contrary to other programs, which use BinaryFormat::create_loader(), init must be a native executable.
|
// Contrary to other programs, which use BinaryFormat::create_loader(), init must be a native executable.
|
||||||
auto loader = TRY(ELFLoader::create(inode, nullptr, 0));
|
auto loader = TRY(ELFLoader::create(inode, nullptr, 0));
|
||||||
@ -188,11 +208,12 @@ namespace Scheduler
|
|||||||
thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
kinfoln("Created userspace thread: id %d with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
|
kinfoln("Created userspace thread: id %d with ip %#.16lx and sp %#.16lx (ksp %#lx)", process->id, thread->ip(),
|
||||||
thread->sp(), thread->kernel_stack.top());
|
thread->sp(), thread->kernel_stack.top());
|
||||||
|
|
||||||
g_threads.append(thread);
|
g_threads.append(thread);
|
||||||
g_init = thread;
|
g_processes.append(process);
|
||||||
|
g_init = process;
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
@ -202,12 +223,35 @@ namespace Scheduler
|
|||||||
g_threads.append(thread);
|
g_threads.append(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_process(Process* process)
|
||||||
|
{
|
||||||
|
g_processes.append(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reap_process(Process* process)
|
||||||
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
|
// FIXME: Shouldn't all this be done when the timers' destructors are called?
|
||||||
|
process->real_timer.disarm();
|
||||||
|
process->virtual_timer.disarm();
|
||||||
|
process->profiling_timer.disarm();
|
||||||
|
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
|
||||||
|
{
|
||||||
|
if (process->posix_timers[i].has_value()) process->posix_timers[i]->disarm();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete process;
|
||||||
|
|
||||||
|
CPU::enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
void reap_thread(Thread* thread)
|
void reap_thread(Thread* thread)
|
||||||
{
|
{
|
||||||
CPU::disable_interrupts();
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
#ifdef REAP_DEBUG
|
#ifdef REAP_DEBUG
|
||||||
kdbgln("reap: reaping thread with id %d", thread->id);
|
kdbgln("reap: reaping thread with id %d", thread->tid);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (thread->is_kernel)
|
if (thread->is_kernel)
|
||||||
@ -221,15 +265,6 @@ namespace Scheduler
|
|||||||
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
|
MemoryManager::unmap_owned_and_free_vm(stack.bottom(), stack.bytes() / ARCH_PAGE_SIZE).release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Shouldn't all this be done when the timers' destructors are called?
|
|
||||||
thread->real_timer.disarm();
|
|
||||||
thread->virtual_timer.disarm();
|
|
||||||
thread->profiling_timer.disarm();
|
|
||||||
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
|
|
||||||
{
|
|
||||||
if (thread->posix_timers[i].has_value()) thread->posix_timers[i]->disarm();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete thread;
|
delete thread;
|
||||||
|
|
||||||
CPU::enable_interrupts();
|
CPU::enable_interrupts();
|
||||||
@ -307,14 +342,19 @@ namespace Scheduler
|
|||||||
{
|
{
|
||||||
CPU::disable_interrupts();
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
if (is_in_kernel(regs)) g_current->kernel_ticks_self++;
|
if (is_in_kernel(regs))
|
||||||
|
{
|
||||||
|
g_current->process->kernel_ticks_self++;
|
||||||
|
g_current->kernel_ticks_self++;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g_current->virtual_clock.tick();
|
g_current->process->virtual_clock.tick();
|
||||||
|
g_current->process->user_ticks_self++;
|
||||||
g_current->user_ticks_self++;
|
g_current->user_ticks_self++;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_current->profiling_clock.tick();
|
g_current->process->profiling_clock.tick();
|
||||||
|
|
||||||
g_current->ticks_left--;
|
g_current->ticks_left--;
|
||||||
|
|
||||||
@ -344,21 +384,46 @@ namespace Scheduler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<Thread*> find_by_pid(pid_t pid)
|
LinkedList<Process> check_for_dead_processes()
|
||||||
{
|
{
|
||||||
for (auto* const thread : g_threads)
|
LinkedList<Process> result;
|
||||||
|
|
||||||
|
g_processes.delayed_for_each([&](Process* p) {
|
||||||
|
if (p->thread_count == PROCESS_SHOULD_REAP)
|
||||||
{
|
{
|
||||||
if (thread->id == pid && thread->state != ThreadState::Dying) return thread;
|
g_processes.remove(p);
|
||||||
|
result.append(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Option<Process*> find_by_pid(pid_t pid)
|
||||||
|
{
|
||||||
|
for (auto* const process : g_processes)
|
||||||
|
{
|
||||||
|
if (process->id == pid) return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_children(Thread* thread)
|
Option<Thread*> find_by_tid(pid_t tid)
|
||||||
|
{
|
||||||
|
for (auto* const thread : g_threads)
|
||||||
|
{
|
||||||
|
if (thread->tid == tid) return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_children(Process* process)
|
||||||
{
|
{
|
||||||
bool result { false };
|
bool result { false };
|
||||||
|
|
||||||
for_each_child(thread, [&](Thread*) {
|
for_each_child(process, [&](Process*) {
|
||||||
result = true;
|
result = true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -366,12 +431,12 @@ namespace Scheduler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<Thread*> find_exited_child(Thread* thread)
|
Option<Process*> find_exited_child(Process* process)
|
||||||
{
|
{
|
||||||
Option<Thread*> result;
|
Option<Process*> result;
|
||||||
|
|
||||||
for_each_child(thread, [&](Thread* child) {
|
for_each_child(process, [&](Process* child) {
|
||||||
if (!result.has_value() && child->state == ThreadState::Exited)
|
if (!result.has_value() && child->dead())
|
||||||
{
|
{
|
||||||
result = child;
|
result = child;
|
||||||
return false;
|
return false;
|
||||||
@ -387,16 +452,24 @@ namespace Scheduler
|
|||||||
CPU::disable_interrupts();
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
kdbgln("--- BEGIN SCHEDULER DUMP ---");
|
kdbgln("--- BEGIN SCHEDULER DUMP ---");
|
||||||
kdbgln("current at %p, id = %d", g_current, g_current->id);
|
kdbgln("Current thread at %p, tid = %d", g_current, g_current->tid);
|
||||||
|
kdbgln("Current process at %p, pid = %d", g_current->process, g_current->process->id);
|
||||||
|
|
||||||
for (const auto* thread : g_threads)
|
for (const auto* thread : g_threads)
|
||||||
{
|
{
|
||||||
kdbgln("%p %c [%-20s] %4d, parent = (%-18p,%d), state = %d, ticks: (k:%04zu,u:%04zu), status = "
|
kdbgln("Thread %p (belongs to pid %4d) %c [%-20s] %4d, state = %d, ip = %p", thread, thread->process->id,
|
||||||
"%d, cwd = %s",
|
thread->is_kernel ? 'k' : 'u', thread->cmdline.chars(), thread->tid, (int)thread->state,
|
||||||
thread, thread->is_kernel ? 'k' : 'u', thread->cmdline.chars(), thread->id, thread->parent,
|
(void*)thread->ip());
|
||||||
thread->parent ? thread->parent->id : 0, (int)thread->state, thread->kernel_ticks_self,
|
}
|
||||||
thread->user_ticks_self, thread->status,
|
|
||||||
thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars());
|
for (const auto* process : g_processes)
|
||||||
|
{
|
||||||
|
kdbgln("Process %p (%zu threads) %4d, parent = (%-18p,%d), cwd = %s, ticks: (k:%04zu,u:%04zu), "
|
||||||
|
"status = %d",
|
||||||
|
process, process->thread_count.load(), process->id, process->parent,
|
||||||
|
process->parent ? process->parent->id : 0,
|
||||||
|
process->current_directory_path.is_empty() ? "/" : process->current_directory_path.chars(),
|
||||||
|
process->kernel_ticks_self.load(), process->user_ticks_self.load(), process->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
kdbgln("--- END SCHEDULER DUMP ---");
|
kdbgln("--- END SCHEDULER DUMP ---");
|
||||||
@ -428,6 +501,7 @@ void kernel_wait_for_event()
|
|||||||
[[noreturn]] void kernel_exit()
|
[[noreturn]] void kernel_exit()
|
||||||
{
|
{
|
||||||
g_current->state = ThreadState::Dying;
|
g_current->state = ThreadState::Dying;
|
||||||
|
g_current->process->thread_count = PROCESS_SHOULD_REAP;
|
||||||
Scheduler::signal_reap_thread();
|
Scheduler::signal_reap_thread();
|
||||||
kernel_yield();
|
kernel_yield();
|
||||||
unreachable();
|
unreachable();
|
||||||
|
@ -8,7 +8,7 @@ namespace Scheduler
|
|||||||
|
|
||||||
Thread* current();
|
Thread* current();
|
||||||
Thread* idle();
|
Thread* idle();
|
||||||
Thread* init_thread();
|
Process* init_process();
|
||||||
|
|
||||||
void set_reap_thread(Thread*);
|
void set_reap_thread(Thread*);
|
||||||
void signal_reap_thread();
|
void signal_reap_thread();
|
||||||
@ -23,24 +23,41 @@ namespace Scheduler
|
|||||||
Result<Thread*> create_init_process(SharedPtr<VFS::Inode> inode, const char* name);
|
Result<Thread*> create_init_process(SharedPtr<VFS::Inode> inode, const char* name);
|
||||||
|
|
||||||
void add_thread(Thread* thread);
|
void add_thread(Thread* thread);
|
||||||
|
void add_process(Process* process);
|
||||||
|
|
||||||
Thread* pick_task();
|
Thread* pick_task();
|
||||||
|
|
||||||
void reap_thread(Thread* thread);
|
void reap_thread(Thread* thread);
|
||||||
|
void reap_process(Process* thread);
|
||||||
|
|
||||||
void switch_task(Registers* regs);
|
void switch_task(Registers* regs);
|
||||||
|
|
||||||
void invoke(Registers* regs);
|
void invoke(Registers* regs);
|
||||||
|
|
||||||
LinkedList<Thread> check_for_dying_threads();
|
LinkedList<Thread> check_for_dying_threads();
|
||||||
|
LinkedList<Process> check_for_dead_processes();
|
||||||
|
|
||||||
Option<Thread*> find_by_pid(pid_t pid);
|
Option<Process*> find_by_pid(pid_t pid);
|
||||||
|
Option<Thread*> find_by_tid(pid_t tid);
|
||||||
|
|
||||||
template <typename Callback> void for_each_child(Thread* thread, Callback callback)
|
template <typename Callback> void for_each_child(Process* process, Callback callback)
|
||||||
{
|
{
|
||||||
for (Thread* current = thread; current; current = g_threads.next(current).value_or(nullptr))
|
for (Process* current = process; current; current = g_processes.next(current).value_or(nullptr))
|
||||||
{
|
{
|
||||||
if (current->parent == thread)
|
if (current->parent == process)
|
||||||
|
{
|
||||||
|
bool should_continue = callback(current);
|
||||||
|
if (!should_continue) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Callback> void for_each_thread(Process* process, Callback callback)
|
||||||
|
{
|
||||||
|
for (Thread* current = g_threads.first().value_or(nullptr); current;
|
||||||
|
current = g_threads.next(current).value_or(nullptr))
|
||||||
|
{
|
||||||
|
if (current->process == process)
|
||||||
{
|
{
|
||||||
bool should_continue = callback(current);
|
bool should_continue = callback(current);
|
||||||
if (!should_continue) return;
|
if (!should_continue) return;
|
||||||
@ -50,8 +67,8 @@ namespace Scheduler
|
|||||||
|
|
||||||
template <typename Callback> void for_each_in_process_group(pid_t group, Callback callback)
|
template <typename Callback> void for_each_in_process_group(pid_t group, Callback callback)
|
||||||
{
|
{
|
||||||
for (Thread* current = g_threads.first().value_or(nullptr); current;
|
for (Process* current = g_processes.first().value_or(nullptr); current;
|
||||||
current = g_threads.next(current).value_or(nullptr))
|
current = g_processes.next(current).value_or(nullptr))
|
||||||
{
|
{
|
||||||
if (current->pgid == group)
|
if (current->pgid == group)
|
||||||
{
|
{
|
||||||
@ -63,8 +80,8 @@ namespace Scheduler
|
|||||||
|
|
||||||
template <typename Callback> void for_each_in_session(pid_t sid, Callback callback)
|
template <typename Callback> void for_each_in_session(pid_t sid, Callback callback)
|
||||||
{
|
{
|
||||||
for (Thread* current = g_threads.first().value_or(nullptr); current;
|
for (Process* current = g_processes.first().value_or(nullptr); current;
|
||||||
current = g_threads.next(current).value_or(nullptr))
|
current = g_processes.next(current).value_or(nullptr))
|
||||||
{
|
{
|
||||||
if (current->sid == sid)
|
if (current->sid == sid)
|
||||||
{
|
{
|
||||||
@ -76,9 +93,9 @@ namespace Scheduler
|
|||||||
|
|
||||||
void dump_state();
|
void dump_state();
|
||||||
|
|
||||||
bool has_children(Thread* thread);
|
bool has_children(Process* thread);
|
||||||
|
|
||||||
Option<Thread*> find_exited_child(Thread* thread);
|
Option<Process*> find_exited_child(Process* thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void kernel_yield();
|
extern "C" void kernel_yield();
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
static Atomic<pid_t> g_next_id;
|
static Atomic<pid_t> g_next_id;
|
||||||
|
|
||||||
LinkedList<Thread> g_threads;
|
LinkedList<Thread> g_threads;
|
||||||
|
LinkedList<Process> g_processes;
|
||||||
|
|
||||||
void Thread::init()
|
void Thread::init()
|
||||||
{
|
{
|
||||||
@ -25,7 +26,7 @@ Result<Thread*> new_thread()
|
|||||||
{
|
{
|
||||||
Thread* const thread = TRY(make<Thread>());
|
Thread* const thread = TRY(make<Thread>());
|
||||||
|
|
||||||
thread->id = g_next_id++;
|
thread->tid = g_next_id++;
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
@ -35,63 +36,83 @@ pid_t next_thread_id()
|
|||||||
return g_next_id.load();
|
return g_next_id.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> Thread::allocate_fd(int min)
|
Result<int> Process::allocate_fd(int min, FileDescriptor& descriptor)
|
||||||
{
|
{
|
||||||
if (min < 0 || min >= FD_MAX) return err(EINVAL);
|
if (min < 0 || min >= FD_MAX) return err(EINVAL);
|
||||||
|
|
||||||
|
auto table = fd_table.lock();
|
||||||
for (int i = min; i < FD_MAX; i++)
|
for (int i = min; i < FD_MAX; i++)
|
||||||
{
|
{
|
||||||
// FIXME: Possible race condition if multiple threads share a FileDescriptorTable? Let's not worry about it for
|
if (!(*table)[i].has_value())
|
||||||
// now, we're still a long way away from reaching that point.
|
{
|
||||||
if (!fd_table[i].has_value()) { return i; }
|
(*table)[i] = descriptor;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err(EMFILE);
|
return err(EMFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<FileDescriptor*> Thread::resolve_fd(int fd)
|
Result<FileDescriptor*> Process::resolve_fd(int fd)
|
||||||
{
|
{
|
||||||
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
|
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
|
||||||
|
|
||||||
Option<FileDescriptor>& maybe_descriptor = fd_table[fd];
|
auto table = fd_table.lock();
|
||||||
|
|
||||||
|
Option<FileDescriptor>& maybe_descriptor = (*table)[fd];
|
||||||
|
|
||||||
if (!maybe_descriptor.has_value()) return err(EBADF);
|
if (!maybe_descriptor.has_value()) return err(EBADF);
|
||||||
|
|
||||||
return maybe_descriptor.value_ptr();
|
return maybe_descriptor.value_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> Thread::allocate_timerid()
|
Credentials Process::credentials()
|
||||||
{
|
{
|
||||||
|
auto credentials = auth.lock();
|
||||||
|
return *credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Vector<gid_t>> Process::copy_groups()
|
||||||
|
{
|
||||||
|
auto groups = extra_groups.lock();
|
||||||
|
return groups->shallow_copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<int> Process::allocate_timerid()
|
||||||
|
{
|
||||||
|
ScopedMutexLock lock(posix_timer_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
|
for (int i = 0; i < MAX_POSIX_TIMERS; i++)
|
||||||
{
|
{
|
||||||
// FIXME: Possible race condition, this should be used alongside a mutex.
|
if (!posix_timers[i].has_value())
|
||||||
if (!posix_timers[i].has_value()) { return i; }
|
{
|
||||||
|
posix_timers[i] = Timer {};
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err(EMFILE);
|
return err(EMFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Timer*> Thread::resolve_timerid(int tid)
|
Result<Timer*> Process::resolve_timerid(int _tid)
|
||||||
{
|
{
|
||||||
if (tid < 0 || tid >= MAX_POSIX_TIMERS) return err(EBADF);
|
if (_tid < 0 || _tid >= MAX_POSIX_TIMERS) return err(EBADF);
|
||||||
|
|
||||||
Option<Timer>& maybe_timer = posix_timers[tid];
|
Option<Timer>& maybe_timer = posix_timers[_tid];
|
||||||
|
|
||||||
if (!maybe_timer.has_value()) return err(EINVAL);
|
if (!maybe_timer.has_value()) return err(EINVAL);
|
||||||
|
|
||||||
return maybe_timer.value_ptr();
|
return maybe_timer.value_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
Result<SharedPtr<VFS::Inode>> Process::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
||||||
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
|
bool follow_last_symlink, SharedPtr<VFS::Inode>* parent_inode)
|
||||||
{
|
{
|
||||||
if (parent_inode) *parent_inode = this->current_directory;
|
if (parent_inode) *parent_inode = this->current_directory;
|
||||||
|
|
||||||
if (PathParser::is_absolute(path.view()))
|
if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this, {}, follow_last_symlink);
|
||||||
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, {}, follow_last_symlink);
|
|
||||||
|
|
||||||
if (dirfd == AT_FDCWD)
|
if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this, this->current_directory, follow_last_symlink);
|
||||||
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, this->current_directory,
|
|
||||||
follow_last_symlink);
|
|
||||||
|
|
||||||
auto descriptor = TRY(resolve_fd(dirfd));
|
auto descriptor = TRY(resolve_fd(dirfd));
|
||||||
|
|
||||||
@ -99,63 +120,92 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
|||||||
|
|
||||||
if (path.is_empty() && allow_empty_path) return descriptor->inode();
|
if (path.is_empty() && allow_empty_path) return descriptor->inode();
|
||||||
|
|
||||||
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, descriptor->inode(), follow_last_symlink);
|
return VFS::resolve_path(path.chars(), this, descriptor->inode(), follow_last_symlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void Thread::exit_and_signal_parent(int _status)
|
[[noreturn]] void Process::exit(int _status)
|
||||||
{
|
{
|
||||||
check(!is_kernel);
|
check(this == Process::current()); // Process::exit() should only be called by the process itself.
|
||||||
|
|
||||||
#ifndef MOON_ENABLE_TESTING_FEATURES
|
#ifndef MOON_ENABLE_TESTING_FEATURES
|
||||||
if (this->id == 1) fail("the init process exited");
|
if (id == 1) fail("the init process exited");
|
||||||
#else
|
#else
|
||||||
if (this->id == 1) CPU::magic_exit(_status);
|
if (id == 1) CPU::magic_exit(_status);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Scheduler::for_each_child(this, [](Thread* child) {
|
Scheduler::for_each_thread(this, [](Thread* thread) {
|
||||||
child->parent = Scheduler::init_thread();
|
thread->quit();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
Scheduler::signal_reap_thread();
|
||||||
|
|
||||||
|
thread_count = 0;
|
||||||
|
|
||||||
|
status = _status;
|
||||||
|
|
||||||
|
Scheduler::for_each_child(this, [](Process* child) {
|
||||||
|
child->parent = Scheduler::init_process();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (is_session_leader())
|
if (is_session_leader())
|
||||||
{
|
{
|
||||||
kinfoln("thread %d is exiting as a session leader, sending signals to session", id);
|
kinfoln("process %d is exiting as a session leader, sending signals to session", id);
|
||||||
// FIXME: Send SIGHUP only to the foreground process group if the session has a controlling terminal.
|
// FIXME: Send SIGHUP only to the foreground process group if the session has a controlling terminal.
|
||||||
Scheduler::for_each_in_session(sid, [this](Thread* thread) {
|
Scheduler::for_each_in_session(sid, [this](Process* p) {
|
||||||
if (thread == this) return true;
|
if (p == this) return true;
|
||||||
thread->sid = 0;
|
p->sid = 0;
|
||||||
thread->controlling_terminal = {};
|
p->controlling_terminal = {};
|
||||||
thread->send_signal(SIGHUP);
|
p->send_signal(SIGHUP);
|
||||||
kinfoln("reparenting and sending SIGHUP to %d", thread->id);
|
kinfoln("reparenting and sending SIGHUP to %d", p->id);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
{
|
{
|
||||||
if (parent->state == ThreadState::Waiting)
|
Scheduler::for_each_thread(parent, [&](Thread* t) {
|
||||||
|
if (t->state == ThreadState::Waiting)
|
||||||
{
|
{
|
||||||
auto child = *parent->child_being_waited_for;
|
pid_t expected = -1;
|
||||||
if (child == -1 || child == id)
|
if (t->child_being_waited_for.compare_exchange_strong(expected, id))
|
||||||
{
|
{
|
||||||
parent->child_being_waited_for = id;
|
t->wake_up();
|
||||||
parent->wake_up();
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = id;
|
||||||
|
if (t->child_being_waited_for.compare_exchange_strong(expected, id))
|
||||||
|
{
|
||||||
|
t->wake_up();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
return true;
|
||||||
{
|
});
|
||||||
while (parent->pending_signals.get(SIGCHLD - 1)) kernel_yield();
|
|
||||||
parent->send_signal(SIGCHLD);
|
parent->send_signal(SIGCHLD);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
state = ThreadState::Exited;
|
|
||||||
|
|
||||||
status = _status;
|
|
||||||
kernel_yield();
|
kernel_yield();
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::quit()
|
||||||
|
{
|
||||||
|
state = ThreadState::Dying;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::exit(bool yield)
|
||||||
|
{
|
||||||
|
quit();
|
||||||
|
|
||||||
|
process->thread_count--;
|
||||||
|
if (process->thread_count == 0) { process->exit(0); }
|
||||||
|
|
||||||
|
if (yield) kernel_yield();
|
||||||
|
}
|
||||||
|
|
||||||
enum class DefaultSignalAction
|
enum class DefaultSignalAction
|
||||||
{
|
{
|
||||||
Ignore,
|
Ignore,
|
||||||
@ -200,7 +250,7 @@ void Thread::process_pending_signals(Registers* current_regs)
|
|||||||
if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP)
|
if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP)
|
||||||
{
|
{
|
||||||
default_signal:
|
default_signal:
|
||||||
if (id == 1)
|
if (process->id == 1)
|
||||||
{
|
{
|
||||||
kwarnln("signal: init got a signal it has no handler for, ignoring");
|
kwarnln("signal: init got a signal it has no handler for, ignoring");
|
||||||
return;
|
return;
|
||||||
@ -211,9 +261,10 @@ void Thread::process_pending_signals(Registers* current_regs)
|
|||||||
{
|
{
|
||||||
case DefaultSignalAction::Ignore: return;
|
case DefaultSignalAction::Ignore: return;
|
||||||
case DefaultSignalAction::Terminate:
|
case DefaultSignalAction::Terminate:
|
||||||
kwarnln("Terminating thread %d with signal %d", id, signo);
|
kwarnln("Terminating thread %d with signal %d", tid, signo);
|
||||||
CPU::print_stack_trace_at(current_regs);
|
CPU::print_stack_trace_at(current_regs);
|
||||||
exit_and_signal_parent(signo | _SIGBIT);
|
process->exit(signo | _SIGBIT);
|
||||||
|
unreachable();
|
||||||
case DefaultSignalAction::Stop: stop();
|
case DefaultSignalAction::Stop: stop();
|
||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
@ -226,6 +277,25 @@ void Thread::process_pending_signals(Registers* current_regs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Thread::pending_signal_count()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < NSIG; i++)
|
||||||
|
{
|
||||||
|
if (pending_signals.get(i)) { result++; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Thread::pending_signal()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NSIG; i++)
|
||||||
|
{
|
||||||
|
if (pending_signals.get(i)) { return i + 1; }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Thread::will_ignore_pending_signal()
|
bool Thread::will_ignore_pending_signal()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NSIG; i++)
|
for (int i = 0; i < NSIG; i++)
|
||||||
@ -244,6 +314,14 @@ bool Thread::will_ignore_pending_signal()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Process::send_signal(int signo)
|
||||||
|
{
|
||||||
|
Scheduler::for_each_thread(this, [signo](Thread* t) {
|
||||||
|
t->send_signal(signo);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::send_signal(int signo)
|
void Thread::send_signal(int signo)
|
||||||
{
|
{
|
||||||
if (is_kernel) return;
|
if (is_kernel) return;
|
||||||
@ -297,9 +375,11 @@ bool Thread::check_stack_on_exception(u64 stack_pointer)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto address_space = process->address_space.lock();
|
||||||
|
|
||||||
// If we can, we'll add 2 more pages of buffer space, otherwise we use whatever we can.
|
// 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);
|
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);
|
auto maybe_base = (*address_space)->grow_region(stack.bottom(), bytes_to_grow / ARCH_PAGE_SIZE, true);
|
||||||
if (maybe_base.has_error())
|
if (maybe_base.has_error())
|
||||||
{
|
{
|
||||||
kwarnln("Failed to grow stack: could not allocate virtual memory space (%s)", maybe_base.error_string());
|
kwarnln("Failed to grow stack: could not allocate virtual memory space (%s)", maybe_base.error_string());
|
||||||
@ -311,7 +391,7 @@ bool Thread::check_stack_on_exception(u64 stack_pointer)
|
|||||||
MMU::ReadWrite | MMU::NoExecute | MMU::User);
|
MMU::ReadWrite | MMU::NoExecute | MMU::User);
|
||||||
if (result.has_error())
|
if (result.has_error())
|
||||||
{
|
{
|
||||||
address_space->free_region(base, bytes_to_grow / ARCH_PAGE_SIZE);
|
(*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());
|
kwarnln("Failed to grow stack: could not allocate physical pages (%s)", result.error_string());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -331,3 +411,8 @@ void Thread::stop()
|
|||||||
state = ThreadState::Stopped;
|
state = ThreadState::Stopped;
|
||||||
kernel_yield();
|
kernel_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process* Process::current()
|
||||||
|
{
|
||||||
|
return Scheduler::current()->process;
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "arch/MMU.h"
|
#include "arch/MMU.h"
|
||||||
#include "fs/OpenFileDescription.h"
|
#include "fs/OpenFileDescription.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
|
#include "lib/Mutex.h"
|
||||||
#include "memory/AddressSpace.h"
|
#include "memory/AddressSpace.h"
|
||||||
#include <bits/signal.h>
|
#include <bits/signal.h>
|
||||||
#include <luna/Bitset.h>
|
#include <luna/Bitset.h>
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr int MAX_POSIX_TIMERS = 64;
|
constexpr int MAX_POSIX_TIMERS = 64;
|
||||||
|
constexpr i64 PROCESS_SHOULD_REAP = -1;
|
||||||
|
|
||||||
class Timer;
|
class Timer;
|
||||||
|
|
||||||
@ -47,59 +48,33 @@ struct Credentials
|
|||||||
u32 sgid { 0 };
|
u32 sgid { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Thread : public LinkedListNode<Thread>
|
struct Process : public LinkedListNode<Process>
|
||||||
{
|
{
|
||||||
Registers regs;
|
Atomic<i64> thread_count;
|
||||||
|
|
||||||
pid_t id;
|
pid_t id;
|
||||||
pid_t pgid { 0 };
|
Atomic<pid_t> pgid { 0 };
|
||||||
pid_t sid { 0 };
|
Atomic<pid_t> sid { 0 };
|
||||||
|
|
||||||
Credentials auth;
|
bool has_called_exec { false };
|
||||||
Vector<gid_t> extra_groups;
|
|
||||||
|
|
||||||
u64 user_ticks_self = 0;
|
mode_t umask { 0 };
|
||||||
u64 kernel_ticks_self = 0;
|
|
||||||
u64 user_ticks_children = 0;
|
|
||||||
u64 kernel_ticks_children = 0;
|
|
||||||
|
|
||||||
u64 ticks_left;
|
|
||||||
u64 sleep_ticks_left;
|
|
||||||
|
|
||||||
int promises { -1 };
|
int promises { -1 };
|
||||||
int execpromises { -1 };
|
int execpromises { -1 };
|
||||||
|
|
||||||
Stack stack;
|
Process* parent { nullptr };
|
||||||
Stack kernel_stack;
|
|
||||||
|
|
||||||
OwnedPtr<AddressSpace> address_space;
|
MutexLocked<Credentials> auth { Credentials { 0, 0, 0, 0, 0, 0 } };
|
||||||
Option<FileDescriptor> fd_table[FD_MAX] = {};
|
|
||||||
|
|
||||||
Result<int> allocate_fd(int min);
|
MutexLocked<Vector<gid_t>> extra_groups { {} };
|
||||||
Result<FileDescriptor*> resolve_fd(int fd);
|
|
||||||
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
|
||||||
bool follow_last_symlink,
|
|
||||||
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
|
||||||
|
|
||||||
struct sigaction signal_handlers[NSIG];
|
Credentials credentials();
|
||||||
Bitset<sigset_t> signal_mask { 0 };
|
Result<Vector<gid_t>> copy_groups();
|
||||||
Bitset<sigset_t> pending_signals { 0 };
|
|
||||||
bool interrupted { false };
|
|
||||||
|
|
||||||
SharedPtr<VFS::Inode> controlling_terminal;
|
MutexLocked<OwnedPtr<AddressSpace>> address_space;
|
||||||
|
|
||||||
bool unrestricted_task { false };
|
MutexLocked<Option<FileDescriptor>[FD_MAX]> fd_table = {};
|
||||||
|
|
||||||
FPData fp_data;
|
|
||||||
|
|
||||||
ThreadState state = ThreadState::Runnable;
|
|
||||||
|
|
||||||
bool is_kernel { true };
|
|
||||||
bool has_called_exec { false };
|
|
||||||
|
|
||||||
int status { 0 };
|
|
||||||
|
|
||||||
mode_t umask { 0 };
|
|
||||||
|
|
||||||
Timer real_timer;
|
Timer real_timer;
|
||||||
Timer virtual_timer;
|
Timer virtual_timer;
|
||||||
@ -108,27 +83,104 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
Clock virtual_clock;
|
Clock virtual_clock;
|
||||||
Clock profiling_clock;
|
Clock profiling_clock;
|
||||||
|
|
||||||
|
bool is_kernel { false };
|
||||||
|
|
||||||
Option<Timer> posix_timers[MAX_POSIX_TIMERS];
|
Option<Timer> posix_timers[MAX_POSIX_TIMERS];
|
||||||
|
Mutex posix_timer_mutex;
|
||||||
|
|
||||||
|
StaticString<128> cmdline;
|
||||||
|
|
||||||
|
Atomic<u64> user_ticks_self = 0;
|
||||||
|
Atomic<u64> kernel_ticks_self = 0;
|
||||||
|
Atomic<u64> user_ticks_children = 0;
|
||||||
|
Atomic<u64> kernel_ticks_children = 0;
|
||||||
|
|
||||||
Result<int> allocate_timerid();
|
Result<int> allocate_timerid();
|
||||||
Result<Timer*> resolve_timerid(int id);
|
Result<Timer*> resolve_timerid(int id);
|
||||||
|
|
||||||
StaticString<128> cmdline;
|
Result<int> allocate_fd(int min, FileDescriptor& descriptor);
|
||||||
|
Result<FileDescriptor*> resolve_fd(int fd);
|
||||||
|
Result<SharedPtr<VFS::Inode>> resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
||||||
|
bool follow_last_symlink,
|
||||||
|
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
||||||
|
|
||||||
String current_directory_path = {};
|
String current_directory_path = {};
|
||||||
SharedPtr<VFS::Inode> current_directory = {};
|
SharedPtr<VFS::Inode> current_directory = {};
|
||||||
|
|
||||||
Thread* parent { nullptr };
|
SharedPtr<VFS::Inode> controlling_terminal;
|
||||||
Option<pid_t> child_being_waited_for = {};
|
|
||||||
|
int status { 0 };
|
||||||
|
|
||||||
|
void send_signal(int signo);
|
||||||
|
|
||||||
|
bool is_session_leader()
|
||||||
|
{
|
||||||
|
return id == sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alive()
|
||||||
|
{
|
||||||
|
return thread_count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dead()
|
||||||
|
{
|
||||||
|
return thread_count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Process* current();
|
||||||
|
|
||||||
|
[[noreturn]] void exit(int status);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Thread : public LinkedListNode<Thread>
|
||||||
|
{
|
||||||
|
Process* process;
|
||||||
|
|
||||||
|
pid_t tid;
|
||||||
|
|
||||||
|
Registers regs;
|
||||||
|
|
||||||
|
Atomic<u64> ticks_left;
|
||||||
|
Atomic<u64> sleep_ticks_left;
|
||||||
|
|
||||||
|
Atomic<u64> user_ticks_self = 0;
|
||||||
|
Atomic<u64> kernel_ticks_self = 0;
|
||||||
|
|
||||||
|
Stack stack;
|
||||||
|
Stack kernel_stack;
|
||||||
|
|
||||||
|
struct sigaction signal_handlers[NSIG];
|
||||||
|
Bitset<sigset_t> signal_mask { 0 };
|
||||||
|
Bitset<sigset_t> pending_signals { 0 };
|
||||||
|
bool interrupted { false };
|
||||||
|
|
||||||
|
Atomic<pid_t> child_being_waited_for = -2;
|
||||||
|
|
||||||
|
bool unrestricted_task { false };
|
||||||
|
|
||||||
|
FPData fp_data;
|
||||||
|
|
||||||
|
ThreadState state = ThreadState::Runnable;
|
||||||
|
|
||||||
|
bool is_kernel { false };
|
||||||
|
|
||||||
|
StaticString<128> cmdline;
|
||||||
|
|
||||||
PageDirectory* self_directory() const
|
PageDirectory* self_directory() const
|
||||||
{
|
{
|
||||||
return address_space->page_directory();
|
PageDirectory* result;
|
||||||
|
auto lambda = Function<OwnedPtr<AddressSpace>&>::wrap([&](OwnedPtr<AddressSpace>& space) {
|
||||||
|
result = space->page_directory();
|
||||||
|
}).release_value();
|
||||||
|
process->address_space.with_lock(move(lambda));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PageDirectory* active_directory { nullptr };
|
PageDirectory* active_directory { nullptr };
|
||||||
|
|
||||||
[[noreturn]] void exit_and_signal_parent(int status);
|
void quit();
|
||||||
|
void exit(bool yield = true);
|
||||||
|
|
||||||
bool is_idle()
|
bool is_idle()
|
||||||
{
|
{
|
||||||
@ -140,27 +192,24 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
state = ThreadState::Runnable;
|
state = ThreadState::Runnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_session_leader()
|
|
||||||
{
|
|
||||||
return id == sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_regs_kernel();
|
void init_regs_kernel();
|
||||||
void init_regs_user();
|
void init_regs_user();
|
||||||
|
|
||||||
void set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4);
|
void set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4);
|
||||||
|
|
||||||
void set_ip(u64 ip);
|
void set_ip(u64 ip);
|
||||||
u64 ip();
|
u64 ip() const;
|
||||||
|
|
||||||
void set_sp(u64 sp);
|
void set_sp(u64 sp);
|
||||||
u64 sp();
|
u64 sp() const;
|
||||||
|
|
||||||
void set_return(u64 ret);
|
void set_return(u64 ret);
|
||||||
u64 return_register();
|
u64 return_register();
|
||||||
|
|
||||||
void process_pending_signals(Registers* current_regs);
|
void process_pending_signals(Registers* current_regs);
|
||||||
|
|
||||||
|
int pending_signal_count();
|
||||||
|
int pending_signal();
|
||||||
bool will_ignore_pending_signal();
|
bool will_ignore_pending_signal();
|
||||||
|
|
||||||
bool deliver_signal(int signo, Registers* current_regs);
|
bool deliver_signal(int signo, Registers* current_regs);
|
||||||
@ -188,3 +237,4 @@ Result<Thread*> new_thread();
|
|||||||
pid_t next_thread_id();
|
pid_t next_thread_id();
|
||||||
|
|
||||||
extern LinkedList<Thread> g_threads;
|
extern LinkedList<Thread> g_threads;
|
||||||
|
extern LinkedList<Process> g_processes;
|
||||||
|
@ -51,7 +51,9 @@ Result<OwnedPtr<ThreadImage>> ThreadImage::clone_from_thread(Thread* parent)
|
|||||||
{
|
{
|
||||||
auto image = TRY(make_owned<ThreadImage>());
|
auto image = TRY(make_owned<ThreadImage>());
|
||||||
|
|
||||||
auto address_space = TRY(parent->address_space->clone());
|
auto space = parent->process->address_space.lock();
|
||||||
|
|
||||||
|
auto address_space = TRY((*space)->clone());
|
||||||
|
|
||||||
const u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute));
|
const u64 kernel_stack_base = TRY(MemoryManager::alloc_for_kernel(4, MMU::ReadWrite | MMU::NoExecute));
|
||||||
Stack kernel_stack { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
|
Stack kernel_stack { kernel_stack_base, 4 * ARCH_PAGE_SIZE };
|
||||||
@ -67,15 +69,7 @@ Result<OwnedPtr<ThreadImage>> ThreadImage::clone_from_thread(Thread* parent)
|
|||||||
|
|
||||||
Result<u64> ThreadImage::push_mem_on_stack(const u8* mem, usize size)
|
Result<u64> ThreadImage::push_mem_on_stack(const u8* mem, usize size)
|
||||||
{
|
{
|
||||||
if ((m_sp - size) < m_user_stack.bottom()) return err(E2BIG);
|
return MemoryManager::push_mem_on_stack(mem, size, m_user_stack, m_sp);
|
||||||
|
|
||||||
if (!MemoryManager::validate_user_write((void*)(m_sp - size), size)) return err(EFAULT);
|
|
||||||
|
|
||||||
m_sp -= size;
|
|
||||||
|
|
||||||
memcpy((void*)m_sp, mem, size);
|
|
||||||
|
|
||||||
return m_sp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<u64> ThreadImage::push_string_vector_on_stack(const Vector<String>& vec)
|
Result<u64> ThreadImage::push_string_vector_on_stack(const Vector<String>& vec)
|
||||||
@ -106,5 +100,6 @@ void ThreadImage::apply(Thread* thread)
|
|||||||
|
|
||||||
thread->active_directory = m_address_space->page_directory();
|
thread->active_directory = m_address_space->page_directory();
|
||||||
|
|
||||||
thread->address_space = move(m_address_space);
|
auto space = thread->process->address_space.lock();
|
||||||
|
*space = move(m_address_space);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <bits/signal.h>
|
#include <bits/signal.h>
|
||||||
#include <luna/LinkedList.h>
|
#include <luna/LinkedList.h>
|
||||||
|
|
||||||
struct Thread;
|
struct Process;
|
||||||
struct Clock;
|
struct Clock;
|
||||||
|
|
||||||
class Timer : public LinkedListNode<Timer>
|
class Timer : public LinkedListNode<Timer>
|
||||||
@ -10,7 +10,7 @@ class Timer : public LinkedListNode<Timer>
|
|||||||
public:
|
public:
|
||||||
u64 delta_ticks { 0 };
|
u64 delta_ticks { 0 };
|
||||||
u64 interval_ticks { 0 };
|
u64 interval_ticks { 0 };
|
||||||
Thread* thread;
|
Process* process;
|
||||||
int signo { SIGALRM };
|
int signo { SIGALRM };
|
||||||
bool restart { false };
|
bool restart { false };
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user