Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.

```toml
[dependencies]
openkal = "0.21.0"
openkal = "0.22.0"

[target.'cfg(os = "linux")'.dependencies]
openkal-linux = "0.22.0"
openkal-linux = "0.23.0"
```

## Why it does not use a C library
Expand Down Expand Up @@ -102,6 +102,14 @@ A program that carries a runtime of its own answers from that runtime instead
(`pthread_getattr_np`), because in that arrangement the runtime is what owns the
stacks and is what a program above openkal would ask for itself.

## What 0.23 adds (openkal 0.22)

**The null device at a position.** `KAL_SPAWN_NULL_IN`, `KAL_SPAWN_NULL_OUT` and
`KAL_SPAWN_NULL_ERR` place `/dev/null` at the started program's standard input,
output or error; `KAL_PROCESS_PROP_NULL_STREAMS` is claimed. The device is opened
in the started image before it is replaced, so the caller holds no descriptor for
it. A flag beside a stream at the same position is `kal_err_invalid`.

## What 0.22 adds (openkal 0.21)

**Instructions from a file.** `kal_exec_map_part` maps the file's pages over part
Expand Down
4 changes: 2 additions & 2 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-linux"
version = "0.22.0"
version = "0.23.0"
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
license = "Apache-2.0"

Expand Down Expand Up @@ -65,7 +65,7 @@ provides-interfaces = [
]

[dependencies]
openkal = "0.21.0"
openkal = "0.22.0"

# The package contributes definitions and no modules. The interface it
# implements is declared by the specification package, which this package
Expand Down
33 changes: 29 additions & 4 deletions src/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,25 @@ int start_image(void* arg) {
}
return r;
};
const okl_long sin = in != 0 ? lift(in) : 0;
const okl_long sou = ou != 0 ? lift(ou) : 0;
const okl_long ser = er != 0 ? lift(er) : 0;
// THE NULL DEVICE (openkal 0.22), opened here, in the started image, so
// that the caller holds no descriptor for it; it is placed as any other
// source is. Its descriptor is marked to close, so the replacement keeps
// only the positions it was placed at.
const kal_uintptr nulls = how->flags & (KAL_SPAWN_NULL_IN | KAL_SPAWN_NULL_OUT | KAL_SPAWN_NULL_ERR);
okl_long device = 0;
if (nulls != 0) {
const okl_long fd = okl::sys(okl::nr_openat, okl::at_fdcwd, reinterpret_cast<okl_long>("/dev/null"),
okl::o_rdwr | okl::o_cloexec, 0);
if (okl::failed(fd)) {
report.say(fd);
okl::sys(okl::nr_exit_group, 127);
for (;;) { }
}
device = lift(fd);
}
const okl_long sin = (how->flags & KAL_SPAWN_NULL_IN) ? device : in != 0 ? lift(in) : 0;
const okl_long sou = (how->flags & KAL_SPAWN_NULL_OUT) ? device : ou != 0 ? lift(ou) : 0;
const okl_long ser = (how->flags & KAL_SPAWN_NULL_ERR) ? device : er != 0 ? lift(er) : 0;
for (kal_uintptr i = 0; i < how->grant_count; ++i) granted[i] = lift(granted[i]);
const okl_long base = lift(b);
const okl_long work = lift(w);
Expand Down Expand Up @@ -734,7 +750,15 @@ int kal_process_spawn(const kal_spawn* how,
// bound lifetime and received a program without one has been given a program
// that outlives it --- which is the failure the flag exists to remove --- so an
// unclaimed position is an error and not a thing to proceed without.
if (how->flags & ~KAL_SPAWN_BOUND_LIFETIME) return kal_err_not_supported;
constexpr kal_uintptr nulls = KAL_SPAWN_NULL_IN | KAL_SPAWN_NULL_OUT | KAL_SPAWN_NULL_ERR;
if (how->flags & ~(KAL_SPAWN_BOUND_LIFETIME | nulls)) return kal_err_not_supported;
// THE NULL DEVICE BESIDE A STREAM AT ONE POSITION is two answers to one
// question (openkal 0.22, clause 7.15).
if (streams != nullptr
&& (((how->flags & KAL_SPAWN_NULL_IN) && streams->in.h != 0)
|| ((how->flags & KAL_SPAWN_NULL_OUT) && streams->out.h != 0)
|| ((how->flags & KAL_SPAWN_NULL_ERR) && streams->err.h != 0)))
return kal_err_invalid;

okl::terminated p(path, path_len);
if (!p.ok) return kal_err_invalid;
Expand Down Expand Up @@ -1358,6 +1382,7 @@ kal_uintptr kal_process_props(void) {
| KAL_PROCESS_PROP_BOUND_LIFETIME
| KAL_PROCESS_PROP_JOB | KAL_PROCESS_PROP_JOB_USAGE
| KAL_PROCESS_PROP_JOB_LIMIT_SUPERVISED
| KAL_PROCESS_PROP_NULL_STREAMS
| (open_id_available() || stat_state(0) != 0 ? KAL_PROCESS_PROP_OPEN_ID : 0u)
// AGREES WITH `kal_process_stop_requested', because the header
// defines null there as the absence this position reports. Read and
Expand Down
76 changes: 76 additions & 0 deletions tests/openkal_0_22.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// What openkal-linux 0.23 changes beside openkal 0.22, as this kernel does it.
//
// THE NULL DEVICE AT A POSITION IS THE SYSTEM'S OWN: a started program finds a
// character device at standard input and standard error, reads the end of input
// there and has every write accepted (openkal 0.22, clause 7.15). The device is
// opened in the started image, so the caller holds no descriptor for it
// afterwards.
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
import openkal.types;
import openkal.env;
import openkal.fs;
import openkal.process;

namespace {
int failures = 0;
void check(bool held, const char* what) {
if (!held) { std::printf("FAIL: %s\n", what); ++failures; }
}

kal_dir root() {
for (kal_uintptr i = 0; i < kal_fs_preopen_count(); ++i) {
kal_dir d{}; char n[8]; kal_uintptr l = 0;
if (kal_fs_preopen(i, &d, n, sizeof n, &l) == kal_ok && l == 1 && n[0] == '/') return d;
}
return kal_dir{};
}

int descriptors() {
int n = 0;
for (int fd = 0; fd < 1024; ++fd) n += ::fcntl(fd, F_GETFD) != -1;
return n;
}

char g_self[4096];
} // namespace

int main(int argc, char** argv) {
if (argc > 1 && std::strcmp(argv[1], "--null") == 0) {
struct stat in{}, err{};
if (::fstat(0, &in) != 0 || !S_ISCHR(in.st_mode)) return 41;
if (::fstat(2, &err) != 0 || !S_ISCHR(err.st_mode)) return 42;
if (in.st_rdev != err.st_rdev) return 43;
char b[8];
if (::read(0, b, sizeof b) != 0) return 44;
if (::write(2, "discarded", 9) != 9) return 45;
return 0;
}
const kal_intptr n = kal_env_image(g_self, sizeof g_self - 1);
if (n <= 0 || g_self[0] != '/') { std::printf("FAIL: the program's own image is named\n"); return 1; }
g_self[n] = 0;

const kal_dir r = root();
const char* args[] = { g_self, "--null" };
const kal_uintptr lens[] = { std::strlen(g_self), 6 };
const kal_spawn how{ r, r, nullptr, nullptr, 0, (kal::process::null_in_flag | kal::process::null_err_flag).bits };
const int before = descriptors();
kal_process p{};
const int e = kal_process_spawn(&how, g_self + 1, std::strlen(g_self + 1), args, lens, 2,
nullptr, nullptr, 0, nullptr, &p);
check(e == kal_ok, "a program is started with the null device at two positions");
check(descriptors() == before, "and the caller holds no descriptor for the device");
int status = -1, terminated = 0;
if (e == kal_ok) {
kal_process_wait(p, &status, &terminated);
kal_process_close(p);
}
check(terminated == 0 && status == 0,
"the started program finds one character device there, reads its end and writes to it");
if (status > 0) std::printf(" the started program reported %d\n", status);
if (failures == 0) std::printf("openkal_0_22: ok\n");
return failures == 0 ? 0 : 1;
}
Loading