From c56e042db9c09af65f2d1fb69e3d6c307737aba5 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 6 Nov 2026 05:18:51 +0900 Subject: [PATCH 1/4] feat: observe declared units through native identity --- src/process.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++- src/unit_reference.h | 29 ++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/unit_reference.h diff --git a/src/process.cpp b/src/process.cpp index afd085e..ad6ab6f 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -1386,7 +1386,7 @@ void kal_process_close(kal_process h) { // because `kal_process_spawn' above refuses every flag; STOP_REQUESTED is // claimed only where the trampoline it needs has been entered by a running // program, which is the architecture continuous integration executes. -kal_uintptr kal_process_props(void) { return +kal_uintptr kal_process_props(void) { return KAL_PROCESS_PROP_JOB_OBSERVE | KAL_PROCESS_PROP_TERMINATE | KAL_PROCESS_PROP_STREAM_PASSING | KAL_PROCESS_PROP_EXIT_STATUS | KAL_PROCESS_PROP_CHANNEL | KAL_PROCESS_PROP_GRANT_DIR @@ -1407,3 +1407,95 @@ kal_uintptr kal_process_props(void) { return ; } } + +// Observable units (openkal 0.24). The unit stays the kernel's own group; +// identity validation does not create a registry or infer quiet from a leader. +#include "unit_reference.h" +namespace { +struct unit_identity { + kal_u64 group = 0, birth = 0, previous = 0, previous_birth = 0; + char boot[64]{}, space[64]{}; +}; +bool unit_birth(okm_long pid, kal_u64& value) { + bsdinfo b{}; if (!bsd(pid == 0 ? okm::sys(okm::nr_getpid) : pid,b)) return false; + value = b.start_sec * 1000000ull + b.start_usec; return value != 0; +} +bool unit_origin(char* boot, char*) { + static const char name[] = "kern.bootsessionuuid"; + int lookup[2]{0,3}, oid[8]{}; okm_uptr oid_size = sizeof oid; + auto rc = okm::sys(okm::nr_sysctl,reinterpret_cast(lookup),2, + reinterpret_cast(oid),reinterpret_cast(&oid_size), + reinterpret_cast(name),sizeof name - 1); + if (okm::failed(rc) || oid_size < sizeof(int)) return false; + okm_uptr size = 63; + rc = okm::sys(okm::nr_sysctl,reinterpret_cast(oid),oid_size / sizeof(int), + reinterpret_cast(boot),reinterpret_cast(&size),0,0); + if (okm::failed(rc) || size == 0 || size > 63) return false; + boot[size] = 0; return true; +} +bool unit_parse(const char* p, kal_uintptr n, unit_identity& u) { + if (!p || n == 0 || n > 256) return false; + unit_reference::reader r{p,n}; char kind[8]{}; + return r.token(kind, sizeof kind) && unit_reference::same(kind,"M1") + && r.number(u.group) && u.group > 0 && u.group <= 0x7fffffffu + && r.number(u.birth) && u.birth != 0 + && r.number(u.previous) && u.previous > 0 && u.previous <= 0x7fffffffu + && r.number(u.previous_birth) && u.previous_birth != 0 + && r.token(u.boot,sizeof u.boot) && r.at == n; +} +bool unit_same_origin(const unit_identity& u) { + char boot[64]{}, space[64]{}; + return unit_origin(boot,space) && unit_reference::same(u.boot,boot); +} +} +extern "C" { +int kal_process_job_create(kal_job* out, char* reference, kal_uintptr capacity, kal_uintptr* used) { + if (!out || !reference || !used) return kal_err_invalid; + if (capacity < 256) return kal_err_no_space; + unit_identity u; + u.group = static_cast(okm::sys(okm::nr_getpid)); + bsdinfo self{}; if (!bsd(static_cast(u.group),self)) return kal_err_not_supported; + u.previous = self.pgid; + if (u.previous == u.group || !unit_origin(u.boot,u.space) + || !unit_birth(0,u.birth) || !unit_birth(static_cast(u.previous),u.previous_birth) + || !u.birth || !u.previous_birth) return kal_err_not_supported; + unit_reference::writer w; w.text("M1;"); w.number(u.group); w.next(); w.number(u.birth); w.next(); + w.number(u.previous); w.next(); w.number(u.previous_birth); w.next(); w.text(u.boot); + if (!w.valid) return kal_err_no_space; + kal_job job{}; const int rc = kal_process_job_enter(&job); + if (rc != kal_ok) return rc; + *out = job; return unit_reference::copy(w,reference,capacity,used); +} +int kal_process_job_open(const char* reference, kal_uintptr length, kal_job* out) { + if (!out) return kal_err_invalid; + unit_identity u; if (!unit_parse(reference,length,u)) return kal_err_invalid; + if (!unit_same_origin(u)) return kal_err_not_supported; + out->h = static_cast(u.group); return kal_ok; +} +int kal_process_job_observe(kal_job job, const char* reference, kal_uintptr length, kal_u32* state) { + if (!state) return kal_err_invalid; *state = KAL_JOB_UNKNOWN; + unit_identity u; if (!unit_parse(reference,length,u) || job.h != u.group) return kal_err_invalid; + if (!unit_same_origin(u)) return kal_ok; + const auto status = okm::sys(okm::nr_kill,-static_cast(u.group),0); + if (status == -3 /* ESRCH */) { *state = KAL_JOB_EMPTY; return kal_ok; } + if (okm::failed(status)) return kal_ok; + kal_u64 birth = 0; + if (unit_birth(static_cast(u.group),birth) && birth != u.birth) return kal_ok; + *state = KAL_JOB_OCCUPIED; return kal_ok; +} +int kal_process_job_retire(kal_job job, const char* reference, kal_uintptr length, kal_u32* state) { + if (!state) return kal_err_invalid; *state = KAL_JOB_UNKNOWN; + unit_identity u; if (!unit_parse(reference,length,u) || job.h != u.group) return kal_err_invalid; + kal_u64 birth = 0; + if (static_cast(okm::sys(okm::nr_getpid)) != u.group || !unit_same_origin(u) + || !unit_birth(0,birth) || birth != u.birth) return kal_err_permission; + bsdinfo self{}; if (!bsd(static_cast(u.group),self)) return kal_ok; + const auto current = self.pgid; + if (current == static_cast(u.group)) { + if (!unit_birth(static_cast(u.previous),birth) || birth != u.previous_birth) return kal_ok; + const auto rc = okm::sys(nr_setpgid,0,static_cast(u.previous)); + if (okm::failed(rc)) return kal_ok; + } else if (current != static_cast(u.previous)) return kal_ok; + return kal_process_job_observe(job,reference,length,state); +} +} diff --git a/src/unit_reference.h b/src/unit_reference.h new file mode 100644 index 0000000..b6fadac --- /dev/null +++ b/src/unit_reference.h @@ -0,0 +1,29 @@ +// Counted, opaque unit references. This codec stores no process registry. +#pragma once +#include +namespace unit_reference { +struct writer { + char bytes[256]{}; kal_uintptr size = 0; bool valid = true; + void text(const char* p) { for (; *p; ++p) { if (size == sizeof bytes) { valid = false; return; } bytes[size++] = *p; } } + void number(kal_u64 n) { char b[24]; unsigned used = 0; do { b[used++] = char('0' + n % 10); n /= 10; } while (n); while (used) { char c[2]{b[--used],0}; text(c); } } + void next() { text(";"); } +}; +struct reader { + const char* bytes; kal_uintptr size, at = 0; + bool token(char* p, kal_uintptr capacity) { + kal_uintptr n = 0; + while (at < size && bytes[at] != ';') { if (n + 1 >= capacity || bytes[at] < 33 || bytes[at] > 126) return false; p[n++] = bytes[at++]; } + p[n] = 0; if (at < size) ++at; return n != 0; + } + bool number(kal_u64& n) { + n = 0; const auto begin = at; + while (at < size && bytes[at] != ';') { const auto c = bytes[at++]; if (c < '0' || c > '9' || n > (~kal_u64{0} - kal_u64(c - '0')) / 10) return false; n = n * 10 + kal_u64(c - '0'); } + if (at == begin) return false; if (at < size) ++at; return true; + } +}; +inline bool same(const char* a, const char* b) { for (; *a && *a == *b; ++a, ++b) {} return *a == *b; } +inline int copy(const writer& w, char* out, kal_uintptr capacity, kal_uintptr* used) { + if (!w.valid || capacity < w.size) return kal_err_no_space; + for (kal_uintptr i = 0; i < w.size; ++i) out[i] = w.bytes[i]; *used = w.size; return kal_ok; +} +} From 0ad90826b6097c806bc7db7b0c3579acd231b3b7 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 6 Nov 2026 06:10:48 +0900 Subject: [PATCH 2/4] fix: retire units against a verified live continuation --- src/process.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index ad6ab6f..aff53de 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -1413,7 +1413,7 @@ kal_uintptr kal_process_props(void) { return KAL_PROCESS_PROP_JOB_OBSERVE | #include "unit_reference.h" namespace { struct unit_identity { - kal_u64 group = 0, birth = 0, previous = 0, previous_birth = 0; + kal_u64 group = 0, birth = 0; char boot[64]{}, space[64]{}; }; bool unit_birth(okm_long pid, kal_u64& value) { @@ -1436,11 +1436,9 @@ bool unit_origin(char* boot, char*) { bool unit_parse(const char* p, kal_uintptr n, unit_identity& u) { if (!p || n == 0 || n > 256) return false; unit_reference::reader r{p,n}; char kind[8]{}; - return r.token(kind, sizeof kind) && unit_reference::same(kind,"M1") + return r.token(kind, sizeof kind) && unit_reference::same(kind,"M2") && r.number(u.group) && u.group > 0 && u.group <= 0x7fffffffu && r.number(u.birth) && u.birth != 0 - && r.number(u.previous) && u.previous > 0 && u.previous <= 0x7fffffffu - && r.number(u.previous_birth) && u.previous_birth != 0 && r.token(u.boot,sizeof u.boot) && r.at == n; } bool unit_same_origin(const unit_identity& u) { @@ -1454,13 +1452,9 @@ int kal_process_job_create(kal_job* out, char* reference, kal_uintptr capacity, if (capacity < 256) return kal_err_no_space; unit_identity u; u.group = static_cast(okm::sys(okm::nr_getpid)); - bsdinfo self{}; if (!bsd(static_cast(u.group),self)) return kal_err_not_supported; - u.previous = self.pgid; - if (u.previous == u.group || !unit_origin(u.boot,u.space) - || !unit_birth(0,u.birth) || !unit_birth(static_cast(u.previous),u.previous_birth) - || !u.birth || !u.previous_birth) return kal_err_not_supported; - unit_reference::writer w; w.text("M1;"); w.number(u.group); w.next(); w.number(u.birth); w.next(); - w.number(u.previous); w.next(); w.number(u.previous_birth); w.next(); w.text(u.boot); + if (!unit_origin(u.boot,u.space)||!unit_birth(0,u.birth)||!u.birth) return kal_err_not_supported; + unit_reference::writer w; w.text("M2;"); w.number(u.group); w.next(); w.number(u.birth); w.next(); + w.text(u.boot); if (!w.valid) return kal_err_no_space; kal_job job{}; const int rc = kal_process_job_enter(&job); if (rc != kal_ok) return rc; @@ -1483,19 +1477,29 @@ int kal_process_job_observe(kal_job job, const char* reference, kal_uintptr leng if (unit_birth(static_cast(u.group),birth) && birth != u.birth) return kal_ok; *state = KAL_JOB_OCCUPIED; return kal_ok; } -int kal_process_job_retire(kal_job job, const char* reference, kal_uintptr length, kal_u32* state) { +int kal_process_job_retire(kal_job job, const char* reference, kal_uintptr length, + const kal_program_identity* continuation, kal_u32* state) { if (!state) return kal_err_invalid; *state = KAL_JOB_UNKNOWN; unit_identity u; if (!unit_parse(reference,length,u) || job.h != u.group) return kal_err_invalid; kal_u64 birth = 0; if (static_cast(okm::sys(okm::nr_getpid)) != u.group || !unit_same_origin(u) || !unit_birth(0,birth) || birth != u.birth) return kal_err_permission; - bsdinfo self{}; if (!bsd(static_cast(u.group),self)) return kal_ok; - const auto current = self.pgid; - if (current == static_cast(u.group)) { - if (!unit_birth(static_cast(u.previous),birth) || birth != u.previous_birth) return kal_ok; - const auto rc = okm::sys(nr_setpgid,0,static_cast(u.previous)); - if (okm::failed(rc)) return kal_ok; - } else if (current != static_cast(u.previous)) return kal_ok; + if (!continuation || !continuation->id || continuation->id > 0x7fffffffu || !continuation->started_ns) + return kal_ok; + const auto anchor = static_cast(continuation->id); + bsdinfo destination{},self{}; + if (!bsd(anchor,destination) || destination.status == 5 + || destination.start_sec * 1000000000ull + destination.start_usec * 1000ull != continuation->started_ns + || !bsd(static_cast(u.group),self)) return kal_ok; + if (destination.pgid == 0 || destination.pgid == u.group) return kal_ok; + const auto destination_group = destination.pgid; + if (self.pgid == u.group) { + if (okm::failed(okm::sys(nr_setpgid,0,static_cast(destination.pgid)))) return kal_ok; + } else if (self.pgid != destination.pgid) return kal_ok; + if (!bsd(anchor,destination) || destination.status == 5 + || destination.start_sec * 1000000000ull + destination.start_usec * 1000ull != continuation->started_ns + || destination.pgid != destination_group + || !bsd(static_cast(u.group),self) || self.pgid != destination_group) return kal_ok; return kal_process_job_observe(job,reference,length,state); } } From e1534d674c980ed839903bf4be880efee1a55e3a Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 6 Nov 2026 07:41:05 +0900 Subject: [PATCH 3/4] chore: release observable process units with OpenKal 0.24 --- .github/workflows/ci.yml | 4 ++-- README.md | 11 +++++++++-- mcpp.toml | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 679a678..e40171c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ on: default: "" env: MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF }} - MCPP_VERSION: 2026.8.27.1 - XLINGS_VERSION: v2026.8.17.2 + MCPP_VERSION: 2026.11.5.1 + XLINGS_VERSION: v2026.10.26.1 XLINGS_NON_INTERACTIVE: '1' jobs: diff --git a/README.md b/README.md index 88e6ebc..f1406af 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ written on the kernel's own calls. ```toml [dependencies] -openkal = "0.23.0" +openkal = "0.24.0" [target.'cfg(os = "macos")'.dependencies] -openkal-macos = "0.21.3" +openkal-macos = "0.22.0" ``` Its purpose is as much to test the specification as to be used. A specification @@ -16,6 +16,13 @@ satisfied only by the system it was written against has not been shown to be portable, however many programs that system hosts; a second implementation on a different system is what turns the claim into an observation. +## Observable process units + +The process interface provides the OpenKal 0.24 declared-unit operations. +References retain the original process identity, birth and boot session. +Retirement checks the live continuation before and after membership migration; +ambiguity reports `unknown`. No process registry is installed. + ## Interfaces provided Every interface of `SURFACE.txt` except `openkal.prof` and `openkal.debug`, which are a diff --git a/mcpp.toml b/mcpp.toml index 83ecb43..95b533d 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-macos" -version = "0.21.3" +version = "0.22.0" description = "An implementation of openkal for macOS, written on the kernel's own calls. Its purpose is as much to test the specification as to be used." license = "Apache-2.0" @@ -61,7 +61,7 @@ provides-interfaces = [ ] [dependencies] -openkal = "0.23.0" +openkal = "0.24.0" [build] # The flags are attached to this package's own sources rather than to the whole From 5cad63b22b61df2af38d632ff1590f8cbb527130 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 6 Nov 2026 07:50:16 +0900 Subject: [PATCH 4/4] test: inspect backend objects independently of build helpers --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e40171c..ae54213 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -306,7 +306,7 @@ jobs: # this package does not use, and it is not this package's to answer # for. They are told apart by name, because the tool places both in # one directory. - objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*' | tr '\n' ' ')" + objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*' ! -path '*/.build-mcpp/*' | tr '\n' ' ')" test -n "$objs" || { echo "no objects were found" >&2; exit 1; } bash .spec/tools/check-surface.sh --complete .spec/SURFACE.txt $objs @@ -338,7 +338,7 @@ jobs: for profile in dev release; do echo "--- profile $profile" rm -rf target && mcpp build --features standalone --profile "$profile" - objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*')" + objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*' ! -path '*/.build-mcpp/*')" test -n "$objs" || { echo "no objects were found; the check would pass vacuously" >&2; exit 1; } # The permitted set, and why each entry is in it. @@ -436,7 +436,7 @@ jobs: run: | printf 'extern "C" int puts(const char*);\nextern "C" void okm_probe(void) { puts("x"); }\n' > src/probe.cpp rm -rf target && mcpp build --features standalone - objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*')" + objs="$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'mcpp_*' ! -path '*/.build-mcpp/*')" if ! nm -u $objs | sed 's/^ *//' | grep -qx '_puts'; then echo "the probe did not produce the reference it was written to produce" >&2 rm -f src/probe.cpp; exit 1