Skip to content

Commit a5d9f09

Browse files
0.18.1 --- SIGHUP is a request to end, and a #! script is given its name (#40)
Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 9780ac8 commit a5d9f09

5 files changed

Lines changed: 126 additions & 9 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ for Linux, written on the kernel's own system-call interface.
88
openkal = "0.17.0"
99

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

1414
## Why it does not use a C library

‎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.18.0"
4+
version = "0.18.1"
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

@@ -63,7 +63,7 @@ provides-interfaces = [
6363
]
6464

6565
[dependencies]
66-
openkal = "0.17.0"
66+
openkal = "0.17.1"
6767

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

‎src/process.cpp‎

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,16 @@ bool absolute_under(int fd, const char* name, okl_uptr len, char* out, okl_uptr
415415
return true;
416416
}
417417

418+
// Whether `name' reaches the file `fd' holds: the same device and inode.
419+
bool same_file(okl_long fd, const char* name) {
420+
okl::kstat held{}, named{};
421+
if (okl::failed(okl::sys(okl::nr_fstat, fd, reinterpret_cast<okl_long>(&held)))) return false;
422+
if (okl::failed(okl::sys(okl::nr_newfstatat, okl::at_fdcwd, reinterpret_cast<okl_long>(name),
423+
reinterpret_cast<okl_long>(&named), 0)))
424+
return false;
425+
return held.dev == named.dev && held.ino == named.ino;
426+
}
427+
418428
} // namespace
419429

420430
// EVERY ENVIRONMENT ENTRY IS `NAME=value' (0.17, process.h). Refused before
@@ -643,11 +653,18 @@ int kal_process_spawn(const kal_spawn* how,
643653
// the caller had no standard input and supplied none, the lowest free
644654
// number is 0, and a script would read its own descriptor as input.
645655
//
646-
// What is given up: a script now sees `$0' as `/dev/fd/<n>' and not as
647-
// a path under its directory, so one that locates its neighbours with
648-
// `dirname "$0"' no longer finds them. That is the price of leaving a
649-
// descriptor for the file and not for the directory, the same one
650-
// `fexecve' charges. The descriptor still names the program: it can be
656+
// A SCRIPT IS GIVEN ITS NAME WHERE THE NAME REACHES THE SAME FILE
657+
// (0.18.1). Through the descriptor a script sees `$0' as `/dev/fd/<n>',
658+
// and one that locates its neighbours with `dirname "$0"' --- a wrapper
659+
// installed beside the files it runs, a test's stub provider --- finds
660+
// none of them: measured in mcpp, whose provider stub wrote beside
661+
// itself into `/dev/fd/ticks'. So a program that needs an interpreter is
662+
// started by its absolute name, as every shell and `posix_spawn' start
663+
// it, when that name is known and names the file the descriptor holds
664+
// (the same device and inode, so the name grants nothing the descriptor
665+
// did not). Only where it does not --- a base the kernel cannot name, a
666+
// name rebound since it was opened --- is the descriptor kept, with the
667+
// price stated above: the descriptor still names the program and can be
651668
// reopened through `/proc/self/fd/<n>', as `/proc/self/exe' can.
652669
//
653670
// Opened HERE, in the started image, and not where the preopens are made:
@@ -679,6 +696,10 @@ int kal_process_spawn(const kal_spawn* how,
679696
reinterpret_cast<okl_long>(envs.slots), okl::at_empty_path);
680697
};
681698
okl_long why = start();
699+
if (why == -okl::e_noent && named && same_file(exe, resolved))
700+
why = okl::sys(okl::nr_execve, reinterpret_cast<okl_long>(resolved),
701+
reinterpret_cast<okl_long>(args.slots),
702+
reinterpret_cast<okl_long>(envs.slots));
682703
if (why == -okl::e_noent) {
683704
okl::sys(okl::nr_fcntl, exe, okl::f_setfd, 0);
684705
why = start();
@@ -951,7 +972,11 @@ const kal_u32* kal_process_stop_requested(void) {
951972
// caller is told what the first found rather than arming again.
952973
int state = __atomic_load_n(&g_stop_armed, __ATOMIC_ACQUIRE);
953974
if (state == 0) {
954-
const bool ok = arm_one(15) && arm_one(2); // SIGTERM, SIGINT
975+
// SIGTERM, SIGINT and SIGHUP (0.18.1): the terminal that started a
976+
// program closing is a request to end it as much as the other two, and
977+
// a program that took the requests over and not this one was ended by
978+
// it before it could finish in order.
979+
const bool ok = arm_one(15) && arm_one(2) && arm_one(1);
955980
state = ok ? 1 : -1;
956981
__atomic_store_n(&g_stop_armed, state, __ATOMIC_RELEASE);
957982
}

‎tests/script_sees_its_name.cpp‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// A `#!' script is given its name (0.18.1).
2+
//
3+
// Started through the descriptor, a script saw `$0' as `/dev/fd/<n>', and one
4+
// that locates its neighbours with `dirname "$0"' found none of them. A
5+
// program that needs an interpreter is now started by its absolute name where
6+
// that name reaches the file the descriptor holds.
7+
//
8+
// The script writes beside itself, under the name its `$0' gives; the test
9+
// reads the file back from the directory it placed the script in.
10+
#include <cstdio>
11+
#include <cstdlib>
12+
#include <cstring>
13+
#include <fcntl.h>
14+
#include <sys/stat.h>
15+
#include <unistd.h>
16+
import openkal.types;
17+
import openkal.fs;
18+
import openkal.process;
19+
20+
namespace {
21+
kal_dir root() {
22+
for (kal_uintptr i = 0; i < kal_fs_preopen_count(); ++i) {
23+
kal_dir d{}; char n[8]; kal_uintptr l = 0;
24+
if (kal_fs_preopen(i, &d, n, sizeof n, &l) == kal_ok && l == 1 && n[0] == '/') return d;
25+
}
26+
return kal_dir{};
27+
}
28+
}
29+
30+
int main() {
31+
char dir[] = "/tmp/okl-script-XXXXXX";
32+
if (::mkdtemp(dir) == nullptr) { std::printf("FAIL: a scratch directory\n"); return 1; }
33+
char script[256], beside[256];
34+
std::snprintf(script, sizeof script, "%s/stub", dir);
35+
std::snprintf(beside, sizeof beside, "%s/beside", dir);
36+
{
37+
const int fd = ::open(script, O_WRONLY | O_CREAT | O_TRUNC, 0755);
38+
const char text[] = "#!/bin/sh\nprintf '%s' \"$0\" > \"${0%/*}/beside\"\n";
39+
const bool written = fd >= 0 && ::write(fd, text, sizeof text - 1) == static_cast<ssize_t>(sizeof text - 1);
40+
if (fd >= 0) ::close(fd);
41+
if (!written) { std::printf("FAIL: the script is written\n"); return 1; }
42+
}
43+
44+
const kal_dir r = root();
45+
const char* argv[] = { script };
46+
const kal_uintptr lens[] = { std::strlen(script) };
47+
const kal_spawn how{ r, r, nullptr, nullptr, 0, 0 };
48+
kal_process p{};
49+
const int e = kal_process_spawn(&how, script + 1, std::strlen(script + 1), argv, lens, 1,
50+
nullptr, nullptr, 0, nullptr, &p);
51+
int status = -1, terminated = 0;
52+
if (e == kal_ok) kal_process_wait(p, &status, &terminated);
53+
54+
char seen[256] = {};
55+
if (FILE* f = std::fopen(beside, "r")) { std::fgets(seen, sizeof seen, f); std::fclose(f); }
56+
::unlink(beside);
57+
::unlink(script);
58+
::rmdir(dir);
59+
60+
int failures = 0;
61+
if (e != kal_ok || terminated != 0 || status != 0) {
62+
std::printf("FAIL: the script is started (rc %d, status %d)\n", e, status);
63+
++failures;
64+
}
65+
if (std::strcmp(seen, script) != 0) {
66+
std::printf("FAIL: the script sees its own name as $0 (saw '%s')\n", seen);
67+
++failures;
68+
}
69+
if (failures == 0) std::printf("openkal-linux: a script is given its name\n");
70+
return failures == 0 ? 0 : 1;
71+
}

‎tests/stop_word_take_over.cpp‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ void check(bool held, const char* what) {
2525
}
2626

2727
int main(int argc, char** argv) {
28+
if (argc > 1 && std::strcmp(argv[1], "--hang-up") == 0) {
29+
// 0.18.1: the closing of the terminal is a request to end, taken over
30+
// with the others, and the program ends as it would have.
31+
const kal_u32* word = kal_process_stop_requested();
32+
if (word == nullptr) return 90;
33+
::kill(::getpid(), SIGHUP);
34+
for (int i = 0; i < 1000 && *word == 0; ++i) ::usleep(1000);
35+
if (*word == 0) return 91;
36+
kal_process_end_as_requested();
37+
return 92;
38+
}
2839
if (argc > 1 && std::strcmp(argv[1], "--end-as-requested") == 0) {
2940
const kal_u32* word = kal_process_stop_requested();
3041
if (word == nullptr) return 90;
@@ -58,6 +69,16 @@ int main(int argc, char** argv) {
5869
check(WIFSIGNALED(st) && WTERMSIG(st) == SIGTERM,
5970
"a program that ends as requested is reported to its starter as ended by the request");
6071

72+
const pid_t hung = ::fork();
73+
if (hung == 0) {
74+
::execl("/proc/self/exe", argv[0], "--hang-up", nullptr);
75+
::_exit(93);
76+
}
77+
st = 0;
78+
::waitpid(hung, &st, 0);
79+
check(WIFSIGNALED(st) && WTERMSIG(st) == SIGHUP,
80+
"a hang-up is taken over as a request, and the program ends as it would have");
81+
6182
if (failures == 0) std::printf("openkal-linux: the stop word is taken over and given back\n");
6283
return failures == 0 ? 0 : 1;
6384
}

0 commit comments

Comments
 (0)