Skip to content
This repository was archived by the owner on Jul 21, 2026. It is now read-only.
Open
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
212 changes: 212 additions & 0 deletions Formula/getconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
require (Tap.fetch("automattic", "kandelo-homebrew").path/"Kandelo/formula_support/kandelo_formula_support").to_s

class Getconf < Formula
include KandeloFormulaSupport

desc "Query POSIX system and pathname configuration for Kandelo"
homepage "https://man.openbsd.org/getconf.1"
url "https://raw.githubusercontent.com/openbsd/src/d7259957e8a5d4370d76bfccd4a30d5d1fe80f38/usr.bin/getconf/getconf.c"
version "1.23"
sha256 "e1c8be153cc3cfefa1a24bcaf62fe74d4d78eeadba660f320b09931e29d95c65"
license "BSD-4-Clause"

depends_on "binaryen" => :build
depends_on "wabt" => :build

skip_clean "bin/getconf"

resource "manpage" do
url "https://raw.githubusercontent.com/openbsd/src/d7259957e8a5d4370d76bfccd4a30d5d1fe80f38/usr.bin/getconf/getconf.1"
sha256 "0acea5eed79da7b0dd04ad39a4e7b811a92da5311a8f321e8bb04339ed8ad328"
end

def install
kandelo_require_arch!("wasm32")
artifact = buildpath/"getconf.wasm"
compat = buildpath/"kandelo-openbsd-compat.h"
compat.write <<~HEADER
#ifndef KANDELO_OPENBSD_GETCONF_COMPAT_H
#define KANDELO_OPENBSD_GETCONF_COMPAT_H

#ifndef __dead
#define __dead __attribute__((__noreturn__))
#endif

static inline int pledge(const char *promises, const char *execpromises) {
(void)promises;
(void)execpromises;
return 0;
}

static inline int unveil(const char *path, const char *permissions) {
(void)path;
(void)permissions;
return 0;
}

#endif
HEADER

kandelo_wasm_build do |root|
stable_source = "/usr/src/openbsd-getconf-#{version}"
prefix_maps = {
buildpath.to_s => stable_source,
root.to_s => "/usr/src/kandelo",
"/nix/store" => "/usr/src/toolchain",
}.flat_map do |from, to|
[
"-ffile-prefix-map=#{from}=#{to}",
"-fdebug-prefix-map=#{from}=#{to}",
"-fmacro-prefix-map=#{from}=#{to}",
]
end

# pledge(2) and unveil(2) are OpenBSD host-sandbox boundaries. Kandelo's
# process remains confined by its VFS and host runtime; every reported
# value still comes from the target's sysconf, pathconf, or confstr API.
system kandelo_cc,
"-std=c17", "-O2", "-gline-tables-only", "-D_POSIX_C_SOURCE=200809L",
"-include", compat.basename,
"-fdebug-compilation-dir=#{stable_source}", *prefix_maps,
buildpath/"getconf.c", "-o", artifact
kandelo_validate_wasm_artifact(
artifact,
fork: :forbidden,
forbidden_paths: [buildpath.to_s],
)
end

kandelo_install_bin(buildpath, artifact.basename, "getconf")
resource("manpage").stage { man1.install "getconf.1" }
end

test do
assert_path_exists man1/"getconf.1"

node = lambda do |argv, **options|
kandelo_run_wasm(bin/"getconf", argv, argv0: "/usr/local/bin/getconf", **options)
end
chromium = lambda do |argv, **options|
kandelo_run_browser_wasm(bin/"getconf", argv, argv0: "getconf", **options)
end

{
["PAGESIZE"] => "65536\n",
["OPEN_MAX"] => "1024\n",
["NPROCESSORS_ONLN"] => "1\n",
["PATH"] => "/bin:/usr/bin\n",
["_POSIX_VERSION"] => "200809\n",
["_POSIX_V7_ILP32_OFFBIG"] => "1\n",
["_POSIX_V7_LP64_OFF64"] => "undefined\n",
}.each do |argv, expected|
assert_equal expected, node.call(argv)
assert_equal expected, chromium.call(argv)
end

v7_flags = {}
%w[CFLAGS LDFLAGS LIBS].each do |kind|
variable = "POSIX_V7_ILP32_OFFBIG_#{kind}"
argv = ["-v", "POSIX_V7_ILP32_OFFBIG", variable]
v7_flags[kind] = node.call(argv).chomp
assert_equal v7_flags[kind] + "\n", chromium.call(argv)
end

workspace = testpath/"workspace"
workspace.mkpath
sample = workspace/"sample.txt"
sample.write "sample\n"
node_mount = { "/work" => workspace }
browser_files = { "/work/sample.txt" => sample }

assert_equal "255\n", node.call(
["NAME_MAX", "/work/sample.txt"], writable_host_directories: node_mount
)
assert_equal "255\n", chromium.call(
["NAME_MAX", "/work/sample.txt"], guest_files: browser_files
)
assert_equal "4096\n", node.call(
["PATH_MAX", "/work"], writable_host_directories: node_mount
)
assert_equal "4096\n", chromium.call(
["PATH_MAX", "/work"], guest_files: browser_files
)

unknown_expected = "/usr/local/bin/getconf: NOT_A_VARIABLE: unknown variable\n"
assert_equal unknown_expected, node.call(
["NOT_A_VARIABLE"], merge_stderr: true, expected_status: 1
)
assert_equal unknown_expected, chromium.call(
["NOT_A_VARIABLE"], merge_stderr: true, expected_status: 1
)

missing_expected = "/usr/local/bin/getconf: /work/missing: No such file or directory\n"
assert_equal missing_expected, node.call(
["NAME_MAX", "/work/missing"],
merge_stderr: true, expected_status: 1, writable_host_directories: node_mount,
)
assert_equal missing_expected, chromium.call(
["NAME_MAX", "/work/missing"],
guest_files: browser_files, merge_stderr: true, expected_status: 1,
)

unsupported_env = [
"-v", "POSIX_V7_LP64_OFF64", "POSIX_V7_LP64_OFF64_CFLAGS"
]
unsupported_expected = "/usr/local/bin/getconf: POSIX_V7_LP64_OFF64: unknown specification\n"
assert_equal unsupported_expected, node.call(
unsupported_env, merge_stderr: true, expected_status: 1
)
assert_equal unsupported_expected, chromium.call(
unsupported_env, merge_stderr: true, expected_status: 1
)

kandelo_activate_sdk!
kandelo_activate_sysroot!
smoke_c = testpath/"v7-flags-smoke.c"
smoke_wasm = testpath/"v7-flags-smoke.wasm"
smoke_c.write <<~C
#include <stdio.h>
int main(void) {
puts("v7-flags-ok");
return 0;
}
C
system kandelo_cc,
*Shellwords.split(v7_flags.fetch("CFLAGS")), smoke_c,
*Shellwords.split(v7_flags.fetch("LDFLAGS")),
*Shellwords.split(v7_flags.fetch("LIBS")), "-o", smoke_wasm
assert_equal "v7-flags-ok\n", kandelo_run_wasm(smoke_wasm, [])
assert_equal "v7-flags-ok\n", kandelo_run_browser_wasm(smoke_wasm, [])

interleave_c = testpath/"interleaved-output.c"
interleave_wasm = testpath/"interleaved-output.wasm"
interleave_c.write <<~C
#include <stddef.h>
#include <unistd.h>

static int write_all(int fd, const char *data, size_t length) {
while (length > 0) {
ssize_t written = write(fd, data, length);
if (written <= 0) return -1;
data += written;
length -= (size_t)written;
}
return 0;
}

int main(void) {
if (write_all(STDOUT_FILENO, "stdout-1\\n", 9) != 0) return 2;
if (write_all(STDERR_FILENO, "stderr-1\\n", 9) != 0) return 2;
if (write_all(STDOUT_FILENO, "stdout-2\\n", 9) != 0) return 2;
if (write_all(STDERR_FILENO, "stderr-2\\n", 9) != 0) return 2;
return 0;
}
C
system kandelo_cc, interleave_c, "-o", interleave_wasm
interleaved = "stdout-1\nstderr-1\nstdout-2\nstderr-2\n"
assert_equal interleaved, kandelo_run_wasm(interleave_wasm, [], merge_stderr: true)
assert_equal interleaved, kandelo_run_browser_wasm(
interleave_wasm, [], argv0: "interleaved-output", merge_stderr: true
)
end
end
27 changes: 24 additions & 3 deletions Kandelo/formula_support/browser-smoke-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface BrowserSmokeResult {
exitCode: number;
stdout: string;
stderr: string;
mergedOutput: string;
}

let activeKernel: BrowserKernel | null = null;
Expand All @@ -40,14 +41,21 @@ async function run(request: BrowserSmokeRequest): Promise<BrowserSmokeResult> {

const stdoutDecoder = new TextDecoder();
const stderrDecoder = new TextDecoder();
const mergedChunks: Uint8Array[] = [];
let stdout = "";
let stderr = "";
const kernel = new BrowserKernel({
kernelOwnedFs: true,
maxWorkers: 6,
maxMemoryPages: 16_384,
onStdout: (data) => { stdout += stdoutDecoder.decode(data, { stream: true }); },
onStderr: (data) => { stderr += stderrDecoder.decode(data, { stream: true }); },
onStdout: (data) => {
stdout += stdoutDecoder.decode(data, { stream: true });
mergedChunks.push(data.slice());
},
onStderr: (data) => {
stderr += stderrDecoder.decode(data, { stream: true });
mergedChunks.push(data.slice());
},
});
activeKernel = kernel;

Expand Down Expand Up @@ -89,7 +97,20 @@ async function run(request: BrowserSmokeRequest): Promise<BrowserSmokeResult> {
);
stdout += stdoutDecoder.decode();
stderr += stderrDecoder.decode();
return { exitCode, stdout, stderr };
const mergedBytes = new Uint8Array(
mergedChunks.reduce((total, chunk) => total + chunk.byteLength, 0),
);
let offset = 0;
for (const chunk of mergedChunks) {
mergedBytes.set(chunk, offset);
offset += chunk.byteLength;
}
return {
exitCode,
stdout,
stderr,
mergedOutput: new TextDecoder().decode(mergedBytes),
};
} finally {
await kernel.destroy().catch(() => {});
activeKernel = null;
Expand Down
20 changes: 13 additions & 7 deletions Kandelo/formula_support/kandelo_formula_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -740,12 +740,16 @@ def kandelo_run_kms_browser_wasm(bin_path, argv: [], min_page_flips: 2, timeout_
# runtimes whose behavior depends on argv[0]. `exec_programs:` stages
# executable Wasm programs for spawn/exec behavior, while immutable
# `guest_files:` use the same absolute-path and bounded-rootfs contract as
# Node formula tests.
# Node formula tests. `expected_status:` and `merge_stderr:` permit exact
# negative-path checks without converting a guest failure into a
# browser-runner failure.
def kandelo_run_browser_wasm(
bin_path, argv, argv0: nil, env: {}, exec_programs: {}, guest_files: {},
timeout_ms: 120_000, allow_stderr: false
timeout_ms: 120_000, allow_stderr: false, merge_stderr: false, expected_status: 0
)
root = kandelo_require_root!
valid_status = expected_status.is_a?(Integer) && expected_status.between?(0, 255)
odie "expected browser status must be an integer from 0 through 255" unless valid_status
if (node = ENV.fetch("HOMEBREW_KANDELO_NODE", nil)).to_s != ""
ENV.prepend_path "PATH", File.dirname(node)
end
Expand All @@ -757,11 +761,13 @@ def kandelo_run_browser_wasm(
odie "invalid browser guest command name: #{command_name}" if invalid_command_name

config = JSON.generate({
argv: argv.map(&:to_s),
argv0: command_name,
env: env.transform_values(&:to_s),
timeoutMs: timeout_ms,
allowStderr: allow_stderr,
argv: argv.map(&:to_s),
argv0: command_name,
env: env.transform_values(&:to_s),
timeoutMs: timeout_ms,
allowStderr: allow_stderr,
mergeStderr: merge_stderr,
expectedStatus: expected_status,
})
guest_files_manifest = testpath/"#{wasm_path.basename}.browser-guest-files.json"
File.binwrite(
Expand Down
22 changes: 20 additions & 2 deletions Kandelo/formula_support/run-browser-wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ interface RunnerConfig {
env: Record<string, string>;
timeoutMs: number;
allowStderr: boolean;
mergeStderr: boolean;
expectedStatus: number;
}

interface BrowserSmokeResult {
exitCode: number;
stdout: string;
stderr: string;
mergedOutput: string;
}

interface PageRunnerConfig extends RunnerConfig {
Expand Down Expand Up @@ -98,6 +101,16 @@ function parseConfig(text: string): RunnerConfig {
if (typeof value.allowStderr !== "boolean") {
throw new Error("formula browser allowStderr must be boolean");
}
if (typeof value.mergeStderr !== "boolean") {
throw new Error("formula browser mergeStderr must be boolean");
}
if (
!Number.isSafeInteger(value.expectedStatus) ||
(value.expectedStatus ?? -1) < 0 ||
(value.expectedStatus ?? -1) > 255
) {
throw new Error(`invalid formula browser expected status: ${String(value.expectedStatus)}`);
}
return value as RunnerConfig;
}

Expand Down Expand Up @@ -391,10 +404,15 @@ async function main(): Promise<void> {
}).__runKandeloFormulaBrowserSmoke(request),
pageConfig,
);
if (result.exitCode !== 0 || (!config.allowStderr && result.stderr.length > 0) || pageErrors.length > 0) {
const unexpectedStderr = !config.allowStderr && !config.mergeStderr && result.stderr.length > 0;
if (
result.exitCode !== config.expectedStatus ||
unexpectedStderr ||
pageErrors.length > 0
) {
throw new Error(`formula browser smoke failed: ${JSON.stringify({ ...result, pageErrors })}`);
}
process.stdout.write(result.stdout);
process.stdout.write(config.mergeStderr ? result.mergedOutput : result.stdout);
await page.evaluate(() =>
(window as unknown as { __cleanupKandeloFormulaBrowserSmoke?: () => Promise<void> })
.__cleanupKandeloFormulaBrowserSmoke?.(),
Expand Down
30 changes: 30 additions & 0 deletions Kandelo/formula_support/test/kandelo_formula_support_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,8 @@ def test_browser_execution_uses_focused_chromium_runner_and_removes_stale_host_d
assert_includes harness.command, command.to_s
assert_includes harness.command, "console.log"
assert_includes harness.command, "allowStderr"
assert_includes harness.command, "expectedStatus"
assert_includes harness.command, "mergeStderr"
assert_includes harness.command, "node"
manifest = harness.test_path/"node.browser-guest-files.json"
assert_equal({ "/opt/formula/format.dat" => guest_file.to_s }, JSON.parse(manifest.read))
Expand All @@ -1087,6 +1089,34 @@ def test_browser_execution_uses_focused_chromium_runner_and_removes_stale_host_d
end
end

def test_browser_execution_accepts_expected_nonzero_status_and_merged_stderr
Dir.mktmpdir("kandelo-formula-support") do |dir|
harness = Harness.new
harness.root_path = Pathname(dir)/"kandelo root"
harness.test_path = Pathname(dir)/"formula test"
harness.test_path.mkpath
command = Pathname(dir)/"getconf"
command.binwrite("\0asm")

output = harness.kandelo_run_browser_wasm(
command, ["NOT_A_VARIABLE"],
argv0: "getconf", expected_status: 1, merge_stderr: true
)

assert_equal "runtime-ok\n", output
assert_includes harness.command, 'expectedStatus\":1'
assert_includes harness.command, 'mergeStderr\":true'
end
end

def test_browser_execution_rejects_invalid_expected_status
error = assert_raises(RuntimeError) do
Harness.new.kandelo_run_browser_wasm("program.wasm", [], expected_status: 256)
end

assert_equal "expected browser status must be an integer from 0 through 255", error.message
end

def test_browser_execution_accepts_posix_multicall_bracket_name
Dir.mktmpdir("kandelo-formula-support") do |dir|
harness = Harness.new
Expand Down
Loading