Skip to content

Commit bb0a34c

Browse files
0.15.0 --- the atoms of openkal 0.17, the stop word given back, and SIGPIPE no longer fatal (#30)
Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 513b28a commit bb0a34c

13 files changed

Lines changed: 581 additions & 18 deletions

‎README.md‎

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

66
```toml
77
[dependencies]
8-
openkal = "0.16.0"
8+
openkal = "0.17.0"
99

1010
[target.'cfg(os = "macos")'.dependencies]
11-
openkal-macos = "0.14.1"
11+
openkal-macos = "0.15.0"
1212
```
1313

1414
Its purpose is as much to test the specification as to be used. A specification

‎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-macos"
4-
version = "0.14.1"
4+
version = "0.15.0"
55
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."
66
license = "Apache-2.0"
77

@@ -57,7 +57,7 @@ provides-interfaces = [
5757
]
5858

5959
[dependencies]
60-
openkal = "0.16.0"
60+
openkal = "0.17.0"
6161

6262
[build]
6363
# The flags are attached to this package's own sources rather than to the whole

‎src/endpoint.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ inline int from_kernel(const ksockaddr_storage& in, kal_endpoint& out) {
106106
return kal_ok;
107107
}
108108

109+
// A connection made at a name (0.17) has no address and port to state; the
110+
// kernel reports the local family, or an unnamed one for an accepted end.
111+
if (family == 1 /* AF_UNIX */ || family == 0) return kal_err_not_supported;
112+
109113
return kal_err_invalid;
110114
}
111115

‎src/env.cpp‎

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace okm {
1010
int g_argc = 0;
1111
char** g_argv = nullptr;
1212
char** g_envp = nullptr;
13+
// Whether SIGPIPE was ignored HERE, at the default this program was started
14+
// with: a program it starts is given the default back (process.cpp).
15+
bool g_pipe_ignored_here = false;
1316

1417
void record(int argc, char** argv, char** envp) {
1518
g_argc = argc; g_argv = argv; g_envp = envp;
@@ -41,14 +44,33 @@ namespace {
4144
// mistake that shows up as a program dying in a way nobody can trace --- which is
4245
// the defect this note is about, arrived at from the other side.
4346
//
44-
// ⇒ It is left until it can be MEASURED on this system. This repository already
45-
// refuses to claim a facility it has not exercised, and a signal disposition
46-
// installed by guesswork is exactly that. The consequence meanwhile is stated:
47-
// a program above this implementation that writes to a stream whose far end has
48-
// gone is ended by SIGPIPE rather than told, and no layer between it and here
49-
// can name what happened.
47+
// ⇒ MEASURED SINCE (0.15): the stop word installs a handler through this
48+
// kernel's `sigaction' with this implementation's own trampoline, and the
49+
// conformance suite raises the signal and returns from it. Ignoring needs no
50+
// trampoline at all --- the kernel never enters one for an ignored signal ---
51+
// so the shape is the measured one and the trampoline field is left empty.
52+
//
53+
// ONLY WHERE NOTHING ELSE HAS CHOSEN. A program whose own runtime installed a
54+
// handler for SIGPIPE keeps it; a disposition still at its default is the one
55+
// that ends the program without telling it, and that is the one replaced.
56+
//
57+
// AND IT IS THIS PROGRAM'S, NOT PASSED ON. An ignored disposition survives
58+
// `execve', so a program started from here would begin with SIGPIPE ignored and
59+
// a shell it runs could not undo it. `kal_process_spawn' gives the default back
60+
// to the program it starts when the ignore was made here (process.cpp).
61+
void ignore_sigpipe() {
62+
struct { void (*handler)(int); void* tramp; unsigned int mask; int flags; } act{};
63+
struct { void (*handler)(int); unsigned int mask; int flags; } old{};
64+
if (okm::failed(okm::sys(46 /* sigaction */, 13 /* SIGPIPE */, 0, reinterpret_cast<okm_long>(&old))))
65+
return;
66+
if (old.handler != nullptr) return; // not SIG_DFL
67+
act.handler = reinterpret_cast<void (*)(int)>(1); // SIG_IGN
68+
if (!okm::failed(okm::sys(46 /* sigaction */, 13, reinterpret_cast<okm_long>(&act), 0)))
69+
okm::g_pipe_ignored_here = true;
70+
}
5071
[[gnu::constructor(101)]] void capture(int argc, char** argv, char** envp) {
5172
if (okm::g_argv == nullptr) okm::record(argc, argv, envp);
73+
ignore_sigpipe();
5274
}
5375
} // namespace
5476

@@ -108,4 +130,42 @@ kal_intptr kal_env_var_at(kal_uintptr index, char* out, kal_uintptr cap) {
108130
return -kal_err_not_found;
109131
}
110132

133+
// THE IMAGE THE KERNEL STARTED (0.17): what `proc_pidpath' answers, the
134+
// kernel's record of the file it mapped (`proc_info', PROC_PIDPATHINFO);
135+
// where that is refused, `executable_path=' from the vector this kernel passes
136+
// after the environment, which is the name the kernel was given.
137+
// A program built for its environment was loaded by nothing the interface
138+
// names (clause 12: only an image has a loader to name).
139+
kal_intptr kal_env_loader(char* out, kal_uintptr cap) {
140+
(void)out; (void)cap;
141+
return -kal_err_not_found;
142+
}
143+
144+
kal_intptr kal_env_image(char* out, kal_uintptr cap) {
145+
char buf[4096];
146+
const okm_long n = okm::sys(okm::nr_proc_info, 2 /* PROC_INFO_CALL_PIDINFO */,
147+
okm::sys(okm::nr_getpid), 11 /* PROC_PIDPATHINFO */, 0,
148+
reinterpret_cast<okm_long>(buf), sizeof buf);
149+
if (!okm::failed(n) && n > 0) {
150+
kal_uintptr len = 0; while (len < sizeof buf && buf[len] != '\0') ++len;
151+
if (len > 0) return give(buf, len, out, cap);
152+
}
153+
if (okm::g_envp != nullptr) {
154+
char** a = okm::g_envp;
155+
while (*a) ++a;
156+
for (++a; *a; ++a) {
157+
const char key[] = "executable_path=";
158+
kal_uintptr i = 0;
159+
while (key[i] != '\0' && (*a)[i] == key[i]) ++i;
160+
if (key[i] == '\0') return give(*a + i, okm::length(*a + i), out, cap);
161+
}
162+
}
163+
return -kal_err_not_found;
164+
}
165+
166+
// For conventions only, as the declaration restricts it.
167+
kal_intptr kal_env_system(char* out, kal_uintptr cap) {
168+
return give("macos", 5, out, cap);
169+
}
170+
111171
}

‎src/exec.cpp‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,22 @@ int kal_exec_publish(void* p, kal_uintptr size) {
169169
return kal_ok;
170170
}
171171

172+
// PART OF A REGION (0.17): a run of whole pages made executable, the rest left
173+
// writable. A part that is not whole pages is refused rather than rounded.
174+
int kal_exec_publish_part(void* region, kal_uintptr offset, kal_uintptr size) {
175+
const okm_uptr g = granularity();
176+
if (region == nullptr || size == 0 || (offset & (g - 1)) != 0 || (size & (g - 1)) != 0
177+
|| (reinterpret_cast<okm_uptr>(region) & (g - 1)) != 0)
178+
return kal_err_invalid;
179+
const okm_long r = okm::sys(okm::nr_mprotect,
180+
reinterpret_cast<okm_long>(static_cast<char*>(region) + offset),
181+
static_cast<okm_long>(size), okm::prot_read | okm::prot_exec);
182+
return okm::failed(r) ? okm::translate(r) : kal_ok;
183+
}
184+
185+
// The page this kernel protects in, which is also what it reserves in.
186+
kal_uintptr kal_exec_granularity(void) { return granularity(); }
187+
172188
void kal_exec_free(void* p, kal_uintptr size) {
173189
if (p == nullptr || size == 0) return;
174190
const okm_uptr bytes = round_up(static_cast<okm_uptr>(size), granularity());

‎src/fs.cpp‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ kal_uintptr volume_props(int fd) {
146146
const kal_uintptr conservative =
147147
KAL_FS_PROP_MODIFIED_TIME | KAL_FS_PROP_ATOMIC_RENAME
148148
| KAL_FS_PROP_LOCKS | KAL_FS_PROP_CAPACITY;
149+
// A NAME HELD OPEN IS REMOVED AT ONCE (0.17, clause 7.14) on the formats
150+
// whose names this kernel keeps: `unlink' drops the entry and the file
151+
// lives on until its last descriptor closes. A network volume's client
152+
// renames a held file to a hidden name instead, so the directory that held
153+
// it is not empty until the close, and the position is not claimed there.
154+
const kal_uintptr local = conservative | KAL_FS_PROP_REMOVE_OPEN;
149155
if (fd < 0) return 0;
150156

151157
okm::kstatfs64 sf{};
@@ -162,9 +168,13 @@ kal_uintptr volume_props(int fd) {
162168

163169
// Nodes whose content is another name, on a format that stores a mode.
164170
// This is the ordinary volume of this system.
171+
//
172+
// Both implement `renameatx_np' with RENAME_EXCL (0.17), and both store a
173+
// socket bound to a name.
165174
if (named("apfs") || named("hfs"))
166-
return conservative | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
167-
| KAL_FS_PROP_EXECUTABLE;
175+
return local | KAL_FS_PROP_LINKS | KAL_FS_PROP_MAKE_LINKS
176+
| KAL_FS_PROP_EXECUTABLE
177+
| KAL_FS_PROP_RENAME_NO_REPLACE | KAL_FS_PROP_LOCAL_ENDPOINT;
168178

169179
// The automounter's own namespace: links, and no mode of its own.
170180
if (named("autofs"))
@@ -179,7 +189,7 @@ kal_uintptr volume_props(int fd) {
179189
// The FAT family stores neither a case distinction nor a node that names
180190
// another, nor a mode. `symlinkat' on such a volume reports a refusal, and
181191
// this is where a caller learns it before it tries.
182-
if (named("msdos") || named("exfat")) return conservative;
192+
if (named("msdos") || named("exfat")) return local;
183193

184194
return conservative;
185195
}
@@ -659,6 +669,25 @@ int kal_fs_rename(kal_dir from, const char* a, kal_uintptr alen,
659669
return okm::failed(r) ? okm::translate(r) : kal_ok;
660670
}
661671

672+
// A RENAME THAT DOES NOT REPLACE (0.17): `renameatx_np' with RENAME_EXCL, the
673+
// kernel's own test-and-rename. A format that does not implement the flag
674+
// answers ENOTSUP, which is a volume that cannot.
675+
int kal_fs_rename_flags(kal_dir from, const char* a, kal_uintptr alen,
676+
kal_dir to, const char* b, kal_uintptr blen, kal_uintptr flags) {
677+
if ((flags & ~static_cast<kal_uintptr>(KAL_RENAME_NO_REPLACE)) != 0) return kal_err_invalid;
678+
if (flags == 0) return kal_fs_rename(from, a, alen, to, b, blen);
679+
const int f = okm::unpack(from.h), t2 = okm::unpack(to.h);
680+
if (f < 0 || t2 < 0 || !okm::acceptable(a, alen) || !okm::acceptable(b, blen))
681+
return kal_err_invalid;
682+
if ((volume_props(t2) & KAL_FS_PROP_RENAME_NO_REPLACE) == 0) return kal_err_not_supported;
683+
okm::terminated ta(a, alen), tb(b, blen);
684+
if (!ta.ok || !tb.ok) return kal_err_invalid;
685+
const okm_long r = okm::sys(okm::nr_renameatx_np, f, reinterpret_cast<okm_long>(ta.buf),
686+
t2, reinterpret_cast<okm_long>(tb.buf), 0x4 /* RENAME_EXCL */);
687+
if (r == -45 /* ENOTSUP */) return kal_err_not_supported;
688+
return okm::failed(r) ? okm::translate(r) : kal_ok;
689+
}
690+
662691
int kal_fs_list_begin(kal_dir d, kal_uintptr* iter) {
663692
const int fd = okm::unpack(d.h);
664693
if (fd < 0 || iter == nullptr) return kal_err_invalid;
@@ -720,6 +749,15 @@ int kal_fs_list_next(kal_dir d, kal_uintptr* iter,
720749
// the position reports it in advance, which no operation could.
721750
kal_uintptr kal_fs_props(kal_dir d) { return volume_props(okm::unpack(d.h)); }
722751

752+
} // extern "C" (reopened below)
753+
754+
namespace okm {
755+
// For openkal.net's rendezvous at a name.
756+
kal_uintptr volume_props_of(int fd) { return volume_props(fd); }
757+
} // namespace okm
758+
759+
extern "C" {
760+
723761
// Nodes whose content is another name.
724762
int kal_fs_link_create(kal_dir base, const char* name, kal_uintptr len,
725763
const char* target, kal_uintptr target_len,

‎src/net.cpp‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,64 @@ int report_address(okm_long call, int fd, kal_endpoint* out) {
4646

4747
} // namespace
4848

49+
namespace okm { kal_uintptr volume_props_of(int fd); } // fs.cpp
50+
51+
namespace {
52+
// A RENDEZVOUS AT A NAME (0.17). This kernel binds a local socket to a path and
53+
// has no call that binds beneath a descriptor; it does have a working
54+
// directory per THREAD (`__pthread_fchdir'), so the name is bound relative to
55+
// the directory the caller holds by entering it on this thread alone and
56+
// leaving it again --- no other context's names change meaning meanwhile. The
57+
// address holds 104 bytes.
58+
int at_name(kal_dir base, const char* name, kal_uintptr len, bool listen, kal_uintptr& out) {
59+
const int dir = okm::unpack(base.h);
60+
if (dir < 0 || len == 0 || len > 103 || !okm::acceptable(name, len)) return kal_err_invalid;
61+
for (kal_uintptr i = 0; i < len; ++i) if (name[i] == '/') return kal_err_invalid;
62+
if ((okm::volume_props_of(dir) & KAL_FS_PROP_LOCAL_ENDPOINT) == 0) return kal_err_not_supported;
63+
unsigned char addr[106] = {};
64+
addr[0] = static_cast<unsigned char>(2 + len + 1); // sun_len
65+
addr[1] = 1; // AF_UNIX
66+
for (kal_uintptr i = 0; i < len; ++i) addr[2 + i] = static_cast<unsigned char>(name[i]);
67+
const okm_long fd = okm::sys(okm::nr_socket, 1 /* AF_UNIX */, okm::sock_stream, 0);
68+
if (okm::failed(fd)) return okm::translate(fd);
69+
close_on_exec(fd);
70+
if (const okm_long r = okm::sys(okm::nr_pthread_fchdir, dir); okm::failed(r)) {
71+
okm::sys(okm::nr_close, fd);
72+
return okm::translate(r);
73+
}
74+
okm_long r = 0;
75+
if (listen) {
76+
r = okm::sys(okm::nr_bind, fd, reinterpret_cast<okm_long>(addr), addr[0]);
77+
} else {
78+
do r = okm::sys(okm::nr_connect, fd, reinterpret_cast<okm_long>(addr), addr[0]);
79+
while (okm::interrupted(r));
80+
}
81+
okm::sys(okm::nr_pthread_fchdir, -1); // back to the process's directory
82+
if (!okm::failed(r) && listen) r = okm::sys(okm::nr_listen, fd, 128);
83+
if (okm::failed(r)) {
84+
okm::sys(okm::nr_close, fd);
85+
if (listen && r == -48 /* EADDRINUSE */) return kal_err_exists;
86+
if (!listen && (r == -2 /* ENOENT */ || r == -61 /* ECONNREFUSED */)) return kal_err_not_found;
87+
return okm::translate(r);
88+
}
89+
out = okm::pack(static_cast<int>(fd));
90+
if (out == 0) { okm::sys(okm::nr_close, fd); return kal_err_no_memory; }
91+
return kal_ok;
92+
}
93+
} // namespace
94+
4995
extern "C" {
5096

97+
int kal_net_listen_at(kal_dir base, const char* name, kal_uintptr len, kal_net_listener* out) {
98+
if (out == nullptr) return kal_err_invalid;
99+
return at_name(base, name, len, true, out->h);
100+
}
101+
102+
int kal_net_connect_at(kal_dir base, const char* name, kal_uintptr len, kal_net_conn* out) {
103+
if (out == nullptr) return kal_err_invalid;
104+
return at_name(base, name, len, false, out->h);
105+
}
106+
51107
int kal_net_connect(const kal_endpoint* to, kal_net_conn* out) {
52108
if (to == nullptr || out == nullptr) return kal_err_invalid;
53109
const okm_long family = okm::family_of(*to);

0 commit comments

Comments
 (0)