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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ 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
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
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-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"

Expand Down Expand Up @@ -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
Expand Down
98 changes: 97 additions & 1 deletion src/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -1407,3 +1407,99 @@ 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;
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<okm_long>(lookup),2,
reinterpret_cast<okm_long>(oid),reinterpret_cast<okm_long>(&oid_size),
reinterpret_cast<okm_long>(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<okm_long>(oid),oid_size / sizeof(int),
reinterpret_cast<okm_long>(boot),reinterpret_cast<okm_long>(&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,"M2")
&& r.number(u.group) && u.group > 0 && u.group <= 0x7fffffffu
&& r.number(u.birth) && u.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<kal_u64>(okm::sys(okm::nr_getpid));
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;
*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<kal_uintptr>(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<okm_long>(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<okm_long>(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,
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<kal_u64>(okm::sys(okm::nr_getpid)) != u.group || !unit_same_origin(u)
|| !unit_birth(0,birth) || birth != u.birth) return kal_err_permission;
if (!continuation || !continuation->id || continuation->id > 0x7fffffffu || !continuation->started_ns)
return kal_ok;
const auto anchor = static_cast<okm_long>(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<okm_long>(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<okm_long>(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<okm_long>(u.group),self) || self.pgid != destination_group) return kal_ok;
return kal_process_job_observe(job,reference,length,state);
}
}
29 changes: 29 additions & 0 deletions src/unit_reference.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Counted, opaque unit references. This codec stores no process registry.
#pragma once
#include <openkal/types.h>
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;
}
}
Loading