Skip to content

Commit 3872aa4

Browse files
authored
Take up openkal 0.11's unit: a handle the caller holds, not a flag (#23)
* 0.10.0 --- one spawn, a working directory, and a job that terminate reaches ⭐ THE SAVING IS NOT ONLY IN THE HEADER. This file held THREE bodies of sixty lines that differed by four: every fix to the shared part --- the exec-report pipe among them --- had to be made three times, or be made once and be wrong twice. They are one function now, and the modifiers are positions in `kal_spawn'. Two of those positions are new work rather than rearrangement: `work' --- the directory the started program runs in, an `fchdir' in the duplicate before the replacement. `execveat' takes `base' as a dirfd, but that RESOLVES the name, and resolving a name is not entering a directory --- which is what the comment here used to claim and the code never did. ⚠️ A failed `fchdir' does NOT reach `execveat': running the right program in the wrong directory is precisely the silent wrongness this exists to remove, so it goes back through the pipe an exec failure uses. `KAL_SPAWN_OWN_JOB' --- `setpgid(0, 0)' in the duplicate, asked for there and not from the parent, whose own call races the replacement and loses. ⭐ AND `kal_process_terminate' NOW REACHES THE WHOLE JOB, WITHOUT THE HANDLE CARRYING ANY STATE TO SAY SO. A program that formed its own job has a group identifier equal to its own pid; one that did not inherited this implementation's, which is some other process. `getpgid(pid) == pid' distinguishes them exactly. ⚠️ Without this half the flag would do nothing a caller could see --- forming the job matters only because terminating then reaches what the started program itself started. Conformance against openkal 0.11: 140 held, 0 did not hold. * Take up openkal 0.11's unit: a handle the caller holds, not a flag The identity is established at the first start --- the first member forms the group and its identifier is reported --- so nothing has to be remembered and no registry appears. `kal_process_terminate' is one program again; the unit has its own operations. * Enter a unit, terminate one for good, and stop a signal openkal cannot report `kal_process_job_enter' is `setpgid(0, join)': a program joins a unit or forms one and learns its identity. It exists because `kal_spawn.job' cannot say it --- that field places a program the caller STARTS, and a copy wishing to lead a unit must say so about ITSELF before it replaces itself, which is what every shell and every runner with a timeout is written as. ⚠️⚠️ `kal_process_job_terminate' NOW USES THE SIGNAL THAT CANNOT BE DECLINED, and that is a decision. A unit exists because its members include programs the caller never held a handle to; a request any one of them may ignore does not terminate the unit, it terminates the part that agreed. Measured with a consumer's own test: a shell that traps the polite signal and loops outlived every deadline, and the caller's escalation could not help --- openkal has no vocabulary for "and this time I mean it". What it costs is stated at the call: a member gets no chance to clean up, and a caller that wants that still has `kal_process_terminate' upon a member it holds. ⭐⭐ AND SIGPIPE IS IGNORED AT STARTUP, WHICH IS THE OTHER HALF OF THE SAME IDEA. openkal defines no signals. `kal_stream_write' is required to REPORT that a stream's far end has gone --- and this kernel instead delivers SIGPIPE, whose default action ends the program. So a program that wrote to a closed stream was not told; it stopped, with a status no operation here produced. ⚠️ A C library above answers `signal(SIGPIPE, SIG_IGN)' and TRUTHFULLY reports success, because openkal has no signals and there is nothing for it to set. The program is killed anyway, four layers below the call it made to prevent exactly that. Measured: a consumer's test exited 141 having printed none of its own assertions. Ignored here because here is the only place that can; the write then fails with EPIPE, which is what the interface said would happen all along. The earlier `getpgid(pid) == pid' trick is gone with the flag it served: `kal_process_terminate' means one program again, always. * Answer the stop-request word, with the restorer this architecture needs A disposition for the two polite signals, storing a word and waking whoever waits on it --- both of which are safe to do from one. `kal_task_wait' is what a caller waits with, so the wake is the raw call rather than the library one. ⚠️ A DISPOSITION AND NOT A WAITING CONTEXT. Consuming these from a context of its own needs them blocked in EVERY context, and blocking is per-context and inherited: a program that already had contexts running would have some that still take the default action, and blocking at startup would make a program that never asks unkillable. A disposition is per PROGRAM and can be installed at any moment --- so it is armed on the first enquiry, and a program that never asks is unaffected. ⚠️ THE RESTORER IS SUPPLIED HERE ON x86_64 AND BY THE KERNEL ON aarch64. Without SA_RESTORER the return from a handler faults; the C library normally supplies those three instructions and this implementation has none beneath it. * Update this repository's own tests for the 0.11 spawn record ⚠️ Missed on the first pass because I updated src/ and grepped src/. The tests call kal_process_spawn too, and CI is what said so.
1 parent 034e87b commit 3872aa4

5 files changed

Lines changed: 276 additions & 213 deletions

File tree

‎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.9.0"
4+
version = "0.10.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

@@ -18,7 +18,7 @@ authors = ["mcpplibs"]
1818
repo = "https://github.com/mcpplibs/openkal-linux"
1919

2020
[dependencies]
21-
openkal = "0.10.0"
21+
openkal = "0.11.0"
2222

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

‎src/env.cpp‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,38 @@ bool recover(char*** argv_out, int* argc_out, char*** envp_out) {
100100
return false;
101101
}
102102

103+
// ⚠️⚠️ A PROGRAM ABOVE openkal SHALL NOT BE ENDED BY SOMETHING openkal NEVER
104+
// TOLD IT ABOUT, AND WITHOUT THIS LINE ONE WAS.
105+
//
106+
// openkal defines no signals. `kal_stream_write' is required to REPORT that the
107+
// far end of a stream is gone --- there is a condition for it --- and this kernel
108+
// instead delivers SIGPIPE, whose default action ends the program. So a program
109+
// that wrote to a closed stream did not receive the error the interface promises;
110+
// it stopped, with a status no operation here produced and no wording anywhere in
111+
// the specification.
112+
//
113+
// ⭐ MEASURED THROUGH A CONSUMER, AND THE SHAPE IS WHY IT TOOK SO LONG TO SEE. A
114+
// C library above this one answers `signal(SIGPIPE, SIG_IGN)' --- openkal has no
115+
// signals, so the library has nothing to set and truthfully reports success. The
116+
// program is then killed anyway, four layers below the call it made to prevent
117+
// exactly that. Exit 141 in a test whose own assertions never printed.
118+
//
119+
// ⇒ Ignored HERE and not there, because here is the only place that can: the
120+
// interface has no operation a C library could use to say it. The write then
121+
// fails with EPIPE, which `kal_stream_write' translates and reports, which is
122+
// what the interface said would happen all along.
123+
//
124+
// ⚠️ NOT A POLICY CHOICE ABOUT SIGNALS IN GENERAL. This is the one signal an
125+
// ordinary openkal operation provokes; the rest are left exactly as this program
126+
// was started with.
127+
[[gnu::constructor(101)]] void quiet_the_signal_openkal_cannot_report() {
128+
// struct k_sigaction as this kernel takes it: handler, flags, restorer, mask.
129+
struct { void* handler; unsigned long flags; void* restorer; unsigned long mask; }
130+
ignore{ reinterpret_cast<void*>(1) /* SIG_IGN */, 0, nullptr, 0 };
131+
okl::sys(okl::nr_rt_sigaction, 13 /* SIGPIPE */,
132+
reinterpret_cast<okl_long>(&ignore), 0, sizeof ignore.mask);
133+
}
134+
103135
[[gnu::constructor(101)]] void capture(int argc, char** argv, char** envp) {
104136
if (okl::g_argv != nullptr) return;
105137
if (plausible(argc, argv, envp)) { okl::record(argc, argv, envp); return; }

0 commit comments

Comments
 (0)