Skip to content

Commit e146e78

Browse files
0.23.0 --- openkal 0.22: /dev/null opened in the started image (#47)
Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent feafa3d commit e146e78

4 files changed

Lines changed: 117 additions & 8 deletions

File tree

‎README.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.
55

66
```toml
77
[dependencies]
8-
openkal = "0.21.0"
8+
openkal = "0.22.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.22.0"
11+
openkal-linux = "0.23.0"
1212
```
1313

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

105+
## What 0.23 adds (openkal 0.22)
106+
107+
**The null device at a position.** `KAL_SPAWN_NULL_IN`, `KAL_SPAWN_NULL_OUT` and
108+
`KAL_SPAWN_NULL_ERR` place `/dev/null` at the started program's standard input,
109+
output or error; `KAL_PROCESS_PROP_NULL_STREAMS` is claimed. The device is opened
110+
in the started image before it is replaced, so the caller holds no descriptor for
111+
it. A flag beside a stream at the same position is `kal_err_invalid`.
112+
105113
## What 0.22 adds (openkal 0.21)
106114

107115
**Instructions from a file.** `kal_exec_map_part` maps the file's pages over part

‎mcpp.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.22.0"
4+
version = "0.23.0"
55
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."
66
license = "Apache-2.0"
77

@@ -65,7 +65,7 @@ provides-interfaces = [
6565
]
6666

6767
[dependencies]
68-
openkal = "0.21.0"
68+
openkal = "0.22.0"
6969

7070
# The package contributes definitions and no modules. The interface it
7171
# implements is declared by the specification package, which this package

‎src/process.cpp‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,9 +533,25 @@ int start_image(void* arg) {
533533
}
534534
return r;
535535
};
536-
const okl_long sin = in != 0 ? lift(in) : 0;
537-
const okl_long sou = ou != 0 ? lift(ou) : 0;
538-
const okl_long ser = er != 0 ? lift(er) : 0;
536+
// THE NULL DEVICE (openkal 0.22), opened here, in the started image, so
537+
// that the caller holds no descriptor for it; it is placed as any other
538+
// source is. Its descriptor is marked to close, so the replacement keeps
539+
// only the positions it was placed at.
540+
const kal_uintptr nulls = how->flags & (KAL_SPAWN_NULL_IN | KAL_SPAWN_NULL_OUT | KAL_SPAWN_NULL_ERR);
541+
okl_long device = 0;
542+
if (nulls != 0) {
543+
const okl_long fd = okl::sys(okl::nr_openat, okl::at_fdcwd, reinterpret_cast<okl_long>("/dev/null"),
544+
okl::o_rdwr | okl::o_cloexec, 0);
545+
if (okl::failed(fd)) {
546+
report.say(fd);
547+
okl::sys(okl::nr_exit_group, 127);
548+
for (;;) { }
549+
}
550+
device = lift(fd);
551+
}
552+
const okl_long sin = (how->flags & KAL_SPAWN_NULL_IN) ? device : in != 0 ? lift(in) : 0;
553+
const okl_long sou = (how->flags & KAL_SPAWN_NULL_OUT) ? device : ou != 0 ? lift(ou) : 0;
554+
const okl_long ser = (how->flags & KAL_SPAWN_NULL_ERR) ? device : er != 0 ? lift(er) : 0;
539555
for (kal_uintptr i = 0; i < how->grant_count; ++i) granted[i] = lift(granted[i]);
540556
const okl_long base = lift(b);
541557
const okl_long work = lift(w);
@@ -734,7 +750,15 @@ int kal_process_spawn(const kal_spawn* how,
734750
// bound lifetime and received a program without one has been given a program
735751
// that outlives it --- which is the failure the flag exists to remove --- so an
736752
// unclaimed position is an error and not a thing to proceed without.
737-
if (how->flags & ~KAL_SPAWN_BOUND_LIFETIME) return kal_err_not_supported;
753+
constexpr kal_uintptr nulls = KAL_SPAWN_NULL_IN | KAL_SPAWN_NULL_OUT | KAL_SPAWN_NULL_ERR;
754+
if (how->flags & ~(KAL_SPAWN_BOUND_LIFETIME | nulls)) return kal_err_not_supported;
755+
// THE NULL DEVICE BESIDE A STREAM AT ONE POSITION is two answers to one
756+
// question (openkal 0.22, clause 7.15).
757+
if (streams != nullptr
758+
&& (((how->flags & KAL_SPAWN_NULL_IN) && streams->in.h != 0)
759+
|| ((how->flags & KAL_SPAWN_NULL_OUT) && streams->out.h != 0)
760+
|| ((how->flags & KAL_SPAWN_NULL_ERR) && streams->err.h != 0)))
761+
return kal_err_invalid;
738762

739763
okl::terminated p(path, path_len);
740764
if (!p.ok) return kal_err_invalid;
@@ -1358,6 +1382,7 @@ kal_uintptr kal_process_props(void) {
13581382
| KAL_PROCESS_PROP_BOUND_LIFETIME
13591383
| KAL_PROCESS_PROP_JOB | KAL_PROCESS_PROP_JOB_USAGE
13601384
| KAL_PROCESS_PROP_JOB_LIMIT_SUPERVISED
1385+
| KAL_PROCESS_PROP_NULL_STREAMS
13611386
| (open_id_available() || stat_state(0) != 0 ? KAL_PROCESS_PROP_OPEN_ID : 0u)
13621387
// AGREES WITH `kal_process_stop_requested', because the header
13631388
// defines null there as the absence this position reports. Read and

‎tests/openkal_0_22.cpp‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// What openkal-linux 0.23 changes beside openkal 0.22, as this kernel does it.
2+
//
3+
// THE NULL DEVICE AT A POSITION IS THE SYSTEM'S OWN: a started program finds a
4+
// character device at standard input and standard error, reads the end of input
5+
// there and has every write accepted (openkal 0.22, clause 7.15). The device is
6+
// opened in the started image, so the caller holds no descriptor for it
7+
// afterwards.
8+
#include <cstdio>
9+
#include <cstring>
10+
#include <fcntl.h>
11+
#include <sys/stat.h>
12+
#include <unistd.h>
13+
import openkal.types;
14+
import openkal.env;
15+
import openkal.fs;
16+
import openkal.process;
17+
18+
namespace {
19+
int failures = 0;
20+
void check(bool held, const char* what) {
21+
if (!held) { std::printf("FAIL: %s\n", what); ++failures; }
22+
}
23+
24+
kal_dir root() {
25+
for (kal_uintptr i = 0; i < kal_fs_preopen_count(); ++i) {
26+
kal_dir d{}; char n[8]; kal_uintptr l = 0;
27+
if (kal_fs_preopen(i, &d, n, sizeof n, &l) == kal_ok && l == 1 && n[0] == '/') return d;
28+
}
29+
return kal_dir{};
30+
}
31+
32+
int descriptors() {
33+
int n = 0;
34+
for (int fd = 0; fd < 1024; ++fd) n += ::fcntl(fd, F_GETFD) != -1;
35+
return n;
36+
}
37+
38+
char g_self[4096];
39+
} // namespace
40+
41+
int main(int argc, char** argv) {
42+
if (argc > 1 && std::strcmp(argv[1], "--null") == 0) {
43+
struct stat in{}, err{};
44+
if (::fstat(0, &in) != 0 || !S_ISCHR(in.st_mode)) return 41;
45+
if (::fstat(2, &err) != 0 || !S_ISCHR(err.st_mode)) return 42;
46+
if (in.st_rdev != err.st_rdev) return 43;
47+
char b[8];
48+
if (::read(0, b, sizeof b) != 0) return 44;
49+
if (::write(2, "discarded", 9) != 9) return 45;
50+
return 0;
51+
}
52+
const kal_intptr n = kal_env_image(g_self, sizeof g_self - 1);
53+
if (n <= 0 || g_self[0] != '/') { std::printf("FAIL: the program's own image is named\n"); return 1; }
54+
g_self[n] = 0;
55+
56+
const kal_dir r = root();
57+
const char* args[] = { g_self, "--null" };
58+
const kal_uintptr lens[] = { std::strlen(g_self), 6 };
59+
const kal_spawn how{ r, r, nullptr, nullptr, 0, (kal::process::null_in_flag | kal::process::null_err_flag).bits };
60+
const int before = descriptors();
61+
kal_process p{};
62+
const int e = kal_process_spawn(&how, g_self + 1, std::strlen(g_self + 1), args, lens, 2,
63+
nullptr, nullptr, 0, nullptr, &p);
64+
check(e == kal_ok, "a program is started with the null device at two positions");
65+
check(descriptors() == before, "and the caller holds no descriptor for the device");
66+
int status = -1, terminated = 0;
67+
if (e == kal_ok) {
68+
kal_process_wait(p, &status, &terminated);
69+
kal_process_close(p);
70+
}
71+
check(terminated == 0 && status == 0,
72+
"the started program finds one character device there, reads its end and writes to it");
73+
if (status > 0) std::printf(" the started program reported %d\n", status);
74+
if (failures == 0) std::printf("openkal_0_22: ok\n");
75+
return failures == 0 ? 0 : 1;
76+
}

0 commit comments

Comments
 (0)