From be6a105ec070d26c9af6c67798bb81bcd06ad17c Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Wed, 1 Jan 2025 23:07:01 -0700 Subject: lib9p: Document plan9port's openfd() --- lib9p/idl/2003-9P2000.p9p.9p.wip | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lib9p/idl/2003-9P2000.p9p.9p.wip diff --git a/lib9p/idl/2003-9P2000.p9p.9p.wip b/lib9p/idl/2003-9P2000.p9p.9p.wip new file mode 100644 index 0000000..c42584f --- /dev/null +++ b/lib9p/idl/2003-9P2000.p9p.9p.wip @@ -0,0 +1,48 @@ +# lib9p/idl/2003-9P2000.p9p.9p - Definitions of plan9port extension messages +# +# Copyright (C) 2025 Luke T. Shumaker +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Plan 9 from User Space (a.k.a. plan9port)'s lib9pclient:fsopenfd(3) +# and 9pserve(4) proxy server (which takes the place of the Plan 9 +# kernel) add a special-purpose `openfd` command to 9P2000. +# +# https://9fans.github.io/plan9port/man/man9/openfd.htlm +# https://github.com/9fans/plan9port/blob/master/man/man9/openfd.9p +# https://github.com/9fans/plan9port/commit/32f69c36e0eec1227934bbd34854bfebd88686f2 +# https://github.com/9fans/plan9port/pull/692 + +# BUG: There is no version-string for this extension; plan9port still +# calls it vanilla "9P2000". + +from ./2002-9P2000.9p import * + +# On Plan 9 the usual 9P client is the kernel, not normal userspace +# programs. The kernel multiplexes multiple userspace processes onto +# a single 9P connection; the userspace programs make 9P calls by +# making syscalls. plan9port emulates this by having mock syscalls +# that make 9P client calls over an AF_UNIX socket to a local +# `9pserve` daemon that does the multiplexing (you add an "fs" prefix; +# e.g. you replace syscall:`open()` with lib9pclient:`fsopen()`). +# +# "Unfortunately", programs in plan9port must deal both with 9P files +# and native "Unix" files; and need to turn an 9P FID into a native +# file descriptor. To do this, the `9pserve` program and lib9pclient +# add an extension call to 9P2000: Topenfd/Ropenfd/fsopenfd(). +# +# An AF_UNIX socket has the ability to send a file descriptor over it +# via an out-of-band "socket control message" ("CMSG" or "SCM"). +# +# Topenfd asks 9pserve to create a socketpair() file descriptor that +# 9pserve will pump to/from a FID, and then send that pipe file +# descriptor over a control-message to the client program. +# +# When replying, the server sends not just an in-band Ropenfd message, +# but also an out-of-band control-message with a socketpair() file +# descriptor. A successful call results in the FID being clunked. +msg Topenfd = "size[4,val=end-&size] typ[1,val=98] tag[tag] fid[fid] mode[o]" +msg Ropenfd = "size[4,val=end-&size] typ[1,val=98] tag[tag] qid[qid] iounit[4] unixfd[4]" +# BUG: The "unixfd" field nominally indicates the the file descriptor +# of the pipe, but really 9pserve doesn't know which FD it will end up +# on the client process, and lib9pclient ignores the value here and +# overwrites it with the file descriptor indicated from the CMSG -- cgit v1.2.3-2-g168b From 1dcc029b2d25dc2d337de00262dfedecb7aa7fd1 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 9 Jan 2025 20:21:43 -0700 Subject: fixup! Build the full matrix of CMAKE_BUILD_TYPE --- GNUmakefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 86008a0..571a30a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -74,12 +74,12 @@ $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build)): build/% .PHONY: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build)) check: build - $(MAKE) -j1 -k $(foreach t,$(build-types),$(foreach p,$(platforms),build/$p-$t/check)) + $(MAKE) -j1 -k $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check)) .PHONY: check -$(foreach t,$(build-types),$(foreach p,$(platforms),build/$p-$t/check)): build/%/check: build/%/Makefile +$(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/check)): build/%/check: build/%/Makefile CTEST_OUTPUT_ON_FAILURE=1 $(MAKE) -C $( Date: Thu, 9 Jan 2025 23:29:30 -0700 Subject: mv cmd/srv9p lib9p/tests/test_server # and supporting changes --- .editorconfig | 4 +- CMakeLists.txt | 3 +- GNUmakefile | 4 +- cmd/srv9p/CMakeLists.txt | 42 -------- cmd/srv9p/config/config.h | 60 ------------ cmd/srv9p/main.c | 129 ------------------------- cmd/srv9p/static/Documentation/x | 1 - cmd/srv9p/static/README.md | 1 - lib9p/CMakeLists.txt | 6 +- lib9p/tests/test_server/CMakeLists.txt | 42 ++++++++ lib9p/tests/test_server/config/config.h | 60 ++++++++++++ lib9p/tests/test_server/main.c | 129 +++++++++++++++++++++++++ lib9p/tests/test_server/static/Documentation/x | 1 + lib9p/tests/test_server/static/README.md | 1 + 14 files changed, 243 insertions(+), 240 deletions(-) delete mode 100644 cmd/srv9p/CMakeLists.txt delete mode 100644 cmd/srv9p/config/config.h delete mode 100644 cmd/srv9p/main.c delete mode 100644 cmd/srv9p/static/Documentation/x delete mode 100644 cmd/srv9p/static/README.md create mode 100644 lib9p/tests/test_server/CMakeLists.txt create mode 100644 lib9p/tests/test_server/config/config.h create mode 100644 lib9p/tests/test_server/main.c create mode 100644 lib9p/tests/test_server/static/Documentation/x create mode 100644 lib9p/tests/test_server/static/README.md diff --git a/.editorconfig b/.editorconfig index b95a6ff..88ea6cb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ # .editorconfig - How files in sbc-harness should be formatted # -# Copyright (C) 2024 Luke T. Shumaker +# Copyright (C) 2024-2025 Luke T. Shumaker # SPDX-License-Identifier: AGPL-3.0-or-later root = true @@ -30,7 +30,7 @@ _mode = markdown [*.9p{,.wip}] _mode = 9p -[{cmd/srv9p/static.h.gen,build-aux/embed-sources.h.gen}] +[{lib9p/tests/test_server/static.h.gen,build-aux/embed-sources.h.gen}] _mode = sh [{build-aux/linux-errno.txt.gen,libusb/include/libusb/tusb_helpers.h.gen}] diff --git a/CMakeLists.txt b/CMakeLists.txt index e4f4d91..0c2a9e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ # CMakeLists.txt - Main per-platform build script for sbc-harness project # -# Copyright (C) 2024 Luke T. Shumaker +# Copyright (C) 2024-2025 Luke T. Shumaker # SPDX-License-Identifier: AGPL-3.0-or-later cmake_minimum_required(VERSION 3.30) @@ -138,4 +138,3 @@ add_subdirectory(lib9p) add_subdirectory(lib9p_util) add_subdirectory(cmd/sbc_harness) -add_subdirectory(cmd/srv9p) diff --git a/GNUmakefile b/GNUmakefile index 571a30a..ab28ede 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -60,7 +60,7 @@ generate-clean: # `build` and `check` ########################################################## -platforms := $(shell sed -nE 's/if *\(PICO_PLATFORM STREQUAL "(.*)"\)/\1/p' cmd/*/CMakeLists.txt) +platforms := rp2040 host # $(shell sed -nE 's/if *\(PICO_PLATFORM STREQUAL "(.*)"\)/\1/p' cmd/*/CMakeLists.txt) build_types = Debug Release RelWithDebInfo MinSizeRel build: $(foreach t,$(build_types),$(foreach p,$(platforms),build/$p-$t/build)) @@ -120,7 +120,7 @@ lint/unknown: lint/%: @printf "%s: cannot lint unknown file type\n" $(sources_$*) >&2 lint/all: lint/%: $(eval export sources_$*) - @find $$(printf '%s\n' $${sources_$*} | grep -vE '^cmd/[^/]+/static/') \ + @find $$(printf '%s\n' $${sources_$*} | grep -vE '^lib9p/tests/[^/]+/static/') \ -maxdepth 0 -type f | \ { r=0; while read -r filename; do \ if ! grep -E -q 'Copyright \(C\) 202[4-9]((-|, )202[5-9])* Luke T. Shumaker' $$filename; then \ diff --git a/cmd/srv9p/CMakeLists.txt b/cmd/srv9p/CMakeLists.txt deleted file mode 100644 index 0d8e320..0000000 --- a/cmd/srv9p/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# cmd/srv9p/CMakeLists.txt - Build script for srv9p test/dev executable -# -# Copyright (C) 2024 Luke T. Shumaker -# SPDX-License-Identifier: AGPL-3.0-or-later - -if (PICO_PLATFORM STREQUAL "host") - -# Compile ###################################################################### - -add_library(srv9p_objs OBJECT - main.c -) -target_include_directories(srv9p_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) -target_include_directories(srv9p_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(srv9p_objs - libcr - libcr_ipc - libmisc - lib9p - lib9p_util -) - -# Analyze the stack ############################################################ - -add_stack_analysis(srv9p_stack.c srv9p_objs) - -# Link ######################################################################### - -add_executable(srv9p) -target_sources(srv9p PRIVATE - srv9p_stack.c - "$" -) - -# Embed ######################################################################## - -target_embed_sources(srv9p_objs srv9p static.h - static/README.md - static/Documentation/x -) - -endif() diff --git a/cmd/srv9p/config/config.h b/cmd/srv9p/config/config.h deleted file mode 100644 index 4f8384e..0000000 --- a/cmd/srv9p/config/config.h +++ /dev/null @@ -1,60 +0,0 @@ -/* config.h - Compile-time configuration for srv9p - * - * Copyright (C) 2024 Luke T. Shumaker - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _CONFIG_H_ -#define _CONFIG_H_ - -#define CONFIG_SRV9P_NUM_CONNS 8 - -/* 9P *************************************************************************/ - -#define CONFIG_9P_PORT 564 -/** - * This max-msg-size is sized so that a Twrite message can return - * 8KiB of data. - * - * This is the same as the default in Plan 9 4e's lib9p; it has the - * comment that "24" is "ample room for Twrite/Rread header - * (iounit)". In fact, the Twrite header is only 23 bytes - * ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and the - * Rread header is even shorter at 11 bytes ("size[4] Rread[1] - * tag[2] count[4]"), so "24" appears to be the size of the Twrite - * header rounded up to a nice round number. - * - * In older versions of 9P ("9P1"), the max message size was - * defined as part of the protocol specification rather than - * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to - * (8*1024)+160 in 2e and 3e. - */ -#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) -/** - * Maximum host-data-structure size. A message may be larger in - * unmarshaled-host-structures than marshaled-net-bytes due to (1) - * struct padding, (2) nul-terminator byes for strings. - */ -#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 -#define CONFIG_9P_MAX_FIDS 16 -#define CONFIG_9P_MAX_REQS 2 -#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ -#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ -#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ - -/* COROUTINE ******************************************************************/ - -#define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (32*1024) -#define CONFIG_COROUTINE_NAME_LEN 16 -#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ -#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ -#define CONFIG_COROUTINE_DEBUG 0 /* bool */ -#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ -#define CONFIG_COROUTINE_GDB 1 /* bool */ -#define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\ - 1 /* usb_keyboard */ +\ - CONFIG_SRV9P_NUM_CONNS /* accept+read */ +\ - (CONFIG_9P_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ ) - -#endif /* _CONFIG_H_ */ diff --git a/cmd/srv9p/main.c b/cmd/srv9p/main.c deleted file mode 100644 index b5cb122..0000000 --- a/cmd/srv9p/main.c +++ /dev/null @@ -1,129 +0,0 @@ -/* srv9p/main.c - Main entry point for test 9P server - * - * Copyright (C) 2024 Luke T. Shumaker - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "static.h" - -/* configuration **************************************************************/ - -#include "config.h" - -#ifndef CONFIG_SRV9P_NUM_CONNS - #error config.h must define CONFIG_SRV9P_NUM_CONNS -#endif - -/* implementation *************************************************************/ - -/* file tree ******************************************************************/ - -#define FILE_COMMON(NAME) { \ - .vtable = &util9p_static_file_vtable, \ - \ - .u_name = "root", .u_num = 0, /* owner user */ \ - .g_name = "root", .g_num = 0, /* owner group */ \ - .m_name = "root", .m_num = 0, /* last-modified-by user */ \ - \ - .pathnum = __COUNTER__, \ - .name = NAME, \ - .perm = 0444, \ - .atime = 1728337905, \ - .mtime = 1728337904, \ - } - -#define DIR_COMMON(NAME) { \ - .vtable = &util9p_static_dir_vtable, \ - \ - .u_name = "root", .u_num = 0, /* owner user */ \ - .g_name = "root", .g_num = 0, /* owner group */ \ - .m_name = "root", .m_num = 0, /* last-modified-by user */ \ - \ - .pathnum = __COUNTER__, \ - .name = NAME, \ - .perm = 0555, \ - .atime = 1728337905, \ - .mtime = 1728337904, \ - } - -#define STATIC_FILE(STRNAME, SYMNAME) \ - &((static struct util9p_static_file){ \ - ._util9p_static_common = FILE_COMMON(STRNAME), \ - .data_start = _binary_static_##SYMNAME##_start, \ - .data_end = _binary_static_##SYMNAME##_end, \ - }) - -static struct util9p_static_dir root = { - ._util9p_static_common = DIR_COMMON(""), - .members = { - &((static struct util9p_static_dir){ - ._util9p_static_common = DIR_COMMON("Documentation"), - .members = { - STATIC_FILE("x", Documentation_x), - NULL - }, - }), - STATIC_FILE("README.md", README_md), - NULL, - }, -}; - -static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), char *LM_UNUSED(treename)) { - return &root; -} - -/* main ***********************************************************************/ - -static COROUTINE read_cr(void *_srv) { - struct lib9p_srv *srv = _srv; - assert(srv); - - cr_begin(); - - struct hostnet_tcp_listener listener; - hostnet_tcp_listener_init(&listener, 9000); - - lib9p_srv_read_cr(srv, &listener); - - cr_end(); -} - -const char *hexdig = "0123456789abcdef"; - -struct lib9p_srv srv = { - .rootdir = get_root, -}; - -static COROUTINE init_cr(void *) { - cr_begin(); - - for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) { - char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; - if (!coroutine_add(name, read_cr, &srv)) - error(1, 0, "coroutine_add(read_cr, &srv)"); - sleep_for_s(1); - } - for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) { - char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; - if (!coroutine_add(name, lib9p_srv_write_cr, &srv)) - error(1, 0, "coroutine_add(lib9p_srv_write_cr, &srv)"); - sleep_for_s(1); - } - - cr_exit(); -} - -int main() { - coroutine_add("init", init_cr, NULL); - coroutine_main(); -} diff --git a/cmd/srv9p/static/Documentation/x b/cmd/srv9p/static/Documentation/x deleted file mode 100644 index 257cc56..0000000 --- a/cmd/srv9p/static/Documentation/x +++ /dev/null @@ -1 +0,0 @@ -foo diff --git a/cmd/srv9p/static/README.md b/cmd/srv9p/static/README.md deleted file mode 100644 index af5626b..0000000 --- a/cmd/srv9p/static/README.md +++ /dev/null @@ -1 +0,0 @@ -Hello, world! diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 488cff9..86fde39 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -1,6 +1,6 @@ # lib9p/CMakeLists.txt - TODO # -# Copyright (C) 2024 Luke T. Shumaker +# Copyright (C) 2024-2025 Luke T. Shumaker # SPDX-License-Identifier: AGPL-3.0-or-later add_library(lib9p INTERFACE) @@ -15,3 +15,7 @@ target_link_libraries(lib9p INTERFACE libmisc libhw ) + +if (ENABLE_TESTS) + add_subdirectory(tests/test_server) +endif() diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt new file mode 100644 index 0000000..a107b75 --- /dev/null +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -0,0 +1,42 @@ +# lib9p/tests/test_server/CMakeLists.txt - Build script for test_server executable +# +# Copyright (C) 2024-2025 Luke T. Shumaker +# SPDX-License-Identifier: AGPL-3.0-or-later + +if (PICO_PLATFORM STREQUAL "host") + +# Compile ###################################################################### + +add_library(test_server_objs OBJECT + main.c +) +target_include_directories(test_server_objs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/config) +target_include_directories(test_server_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(test_server_objs + libcr + libcr_ipc + libmisc + lib9p + lib9p_util +) + +# Analyze the stack ############################################################ + +add_stack_analysis(test_server_stack.c test_server_objs) + +# Link ######################################################################### + +add_executable(test_server) +target_sources(test_server PRIVATE + test_server_stack.c + "$" +) + +# Embed ######################################################################## + +target_embed_sources(test_server_objs test_server static.h + static/README.md + static/Documentation/x +) + +endif() diff --git a/lib9p/tests/test_server/config/config.h b/lib9p/tests/test_server/config/config.h new file mode 100644 index 0000000..4f8384e --- /dev/null +++ b/lib9p/tests/test_server/config/config.h @@ -0,0 +1,60 @@ +/* config.h - Compile-time configuration for srv9p + * + * Copyright (C) 2024 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define CONFIG_SRV9P_NUM_CONNS 8 + +/* 9P *************************************************************************/ + +#define CONFIG_9P_PORT 564 +/** + * This max-msg-size is sized so that a Twrite message can return + * 8KiB of data. + * + * This is the same as the default in Plan 9 4e's lib9p; it has the + * comment that "24" is "ample room for Twrite/Rread header + * (iounit)". In fact, the Twrite header is only 23 bytes + * ("size[4] Twrite[1] tag[2] fid[4] offset[8] count[4]") and the + * Rread header is even shorter at 11 bytes ("size[4] Rread[1] + * tag[2] count[4]"), so "24" appears to be the size of the Twrite + * header rounded up to a nice round number. + * + * In older versions of 9P ("9P1"), the max message size was + * defined as part of the protocol specification rather than + * negotiated. In Plan 9 1e it was (8*1024)+128, and was bumped to + * (8*1024)+160 in 2e and 3e. + */ +#define CONFIG_9P_MAX_MSG_SIZE ((4*1024)+24) +/** + * Maximum host-data-structure size. A message may be larger in + * unmarshaled-host-structures than marshaled-net-bytes due to (1) + * struct padding, (2) nul-terminator byes for strings. + */ +#define CONFIG_9P_MAX_HOSTMSG_SIZE CONFIG_9P_MAX_MSG_SIZE+16 +#define CONFIG_9P_MAX_FIDS 16 +#define CONFIG_9P_MAX_REQS 2 +#define CONFIG_9P_MAX_ERR_SIZE 128 /* 128 is what Plan 9 4e uses */ +#define CONFIG_9P_ENABLE_9P2000 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_u 1 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_e 0 /* bool */ + +/* COROUTINE ******************************************************************/ + +#define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (32*1024) +#define CONFIG_COROUTINE_NAME_LEN 16 +#define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */ +#define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */ +#define CONFIG_COROUTINE_DEBUG 0 /* bool */ +#define CONFIG_COROUTINE_VALGRIND 1 /* bool */ +#define CONFIG_COROUTINE_GDB 1 /* bool */ +#define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\ + 1 /* usb_keyboard */ +\ + CONFIG_SRV9P_NUM_CONNS /* accept+read */ +\ + (CONFIG_9P_MAX_REQS*CONFIG_SRV9P_NUM_CONNS) /* work+write */ ) + +#endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c new file mode 100644 index 0000000..adeba38 --- /dev/null +++ b/lib9p/tests/test_server/main.c @@ -0,0 +1,129 @@ +/* lib9p/tests/test_server/main.c - Main entry point for test 9P server + * + * Copyright (C) 2024-2025 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "static.h" + +/* configuration **************************************************************/ + +#include "config.h" + +#ifndef CONFIG_SRV9P_NUM_CONNS + #error config.h must define CONFIG_SRV9P_NUM_CONNS +#endif + +/* implementation *************************************************************/ + +/* file tree ******************************************************************/ + +#define FILE_COMMON(NAME) { \ + .vtable = &util9p_static_file_vtable, \ + \ + .u_name = "root", .u_num = 0, /* owner user */ \ + .g_name = "root", .g_num = 0, /* owner group */ \ + .m_name = "root", .m_num = 0, /* last-modified-by user */ \ + \ + .pathnum = __COUNTER__, \ + .name = NAME, \ + .perm = 0444, \ + .atime = 1728337905, \ + .mtime = 1728337904, \ + } + +#define DIR_COMMON(NAME) { \ + .vtable = &util9p_static_dir_vtable, \ + \ + .u_name = "root", .u_num = 0, /* owner user */ \ + .g_name = "root", .g_num = 0, /* owner group */ \ + .m_name = "root", .m_num = 0, /* last-modified-by user */ \ + \ + .pathnum = __COUNTER__, \ + .name = NAME, \ + .perm = 0555, \ + .atime = 1728337905, \ + .mtime = 1728337904, \ + } + +#define STATIC_FILE(STRNAME, SYMNAME) \ + &((static struct util9p_static_file){ \ + ._util9p_static_common = FILE_COMMON(STRNAME), \ + .data_start = _binary_static_##SYMNAME##_start, \ + .data_end = _binary_static_##SYMNAME##_end, \ + }) + +static struct util9p_static_dir root = { + ._util9p_static_common = DIR_COMMON(""), + .members = { + &((static struct util9p_static_dir){ + ._util9p_static_common = DIR_COMMON("Documentation"), + .members = { + STATIC_FILE("x", Documentation_x), + NULL + }, + }), + STATIC_FILE("README.md", README_md), + NULL, + }, +}; + +static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), char *LM_UNUSED(treename)) { + return &root; +} + +/* main ***********************************************************************/ + +static COROUTINE read_cr(void *_srv) { + struct lib9p_srv *srv = _srv; + assert(srv); + + cr_begin(); + + struct hostnet_tcp_listener listener; + hostnet_tcp_listener_init(&listener, 9000); + + lib9p_srv_read_cr(srv, &listener); + + cr_end(); +} + +const char *hexdig = "0123456789abcdef"; + +struct lib9p_srv srv = { + .rootdir = get_root, +}; + +static COROUTINE init_cr(void *) { + cr_begin(); + + for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) { + char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, read_cr, &srv)) + error(1, 0, "coroutine_add(read_cr, &srv)"); + sleep_for_s(1); + } + for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) { + char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, lib9p_srv_write_cr, &srv)) + error(1, 0, "coroutine_add(lib9p_srv_write_cr, &srv)"); + sleep_for_s(1); + } + + cr_exit(); +} + +int main() { + coroutine_add("init", init_cr, NULL); + coroutine_main(); +} diff --git a/lib9p/tests/test_server/static/Documentation/x b/lib9p/tests/test_server/static/Documentation/x new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/lib9p/tests/test_server/static/Documentation/x @@ -0,0 +1 @@ +foo diff --git a/lib9p/tests/test_server/static/README.md b/lib9p/tests/test_server/static/README.md new file mode 100644 index 0000000..af5626b --- /dev/null +++ b/lib9p/tests/test_server/static/README.md @@ -0,0 +1 @@ +Hello, world! -- cgit v1.2.3-2-g168b From d29cb3f3deda2ae55fbccfdaae3b2481410a0894 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Thu, 9 Jan 2025 23:29:30 -0700 Subject: Add basic smoke tests for the 9p server --- .editorconfig | 2 +- lib9p/CMakeLists.txt | 4 ++++ lib9p/tests/runtest | 45 +++++++++++++++++++++++++++++++++++++++ lib9p/tests/test_server/main.c | 4 ++-- libcr/coroutine.c | 23 +++++++++++++++----- libcr_ipc/include/libcr_ipc/rpc.h | 6 ++++-- libhw/host_net.c | 4 +++- 7 files changed, 77 insertions(+), 11 deletions(-) create mode 100755 lib9p/tests/runtest diff --git a/.editorconfig b/.editorconfig index 88ea6cb..8fb3a6f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -33,7 +33,7 @@ _mode = 9p [{lib9p/tests/test_server/static.h.gen,build-aux/embed-sources.h.gen}] _mode = sh -[{build-aux/linux-errno.txt.gen,libusb/include/libusb/tusb_helpers.h.gen}] +[{build-aux/linux-errno.txt.gen,libusb/include/libusb/tusb_helpers.h.gen,lib9p/tests/runtest}] _mode = bash [{lib9p/idl.gen,lib9p/include/lib9p/linux-errno.h.gen,build-aux/stack.c.gen}] diff --git a/lib9p/CMakeLists.txt b/lib9p/CMakeLists.txt index 86fde39..2f5fc70 100644 --- a/lib9p/CMakeLists.txt +++ b/lib9p/CMakeLists.txt @@ -18,4 +18,8 @@ target_link_libraries(lib9p INTERFACE if (ENABLE_TESTS) add_subdirectory(tests/test_server) + add_test( + NAME "lib9p/runtest" + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/runtest" + ) endif() diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest new file mode 100755 index 0000000..29d2089 --- /dev/null +++ b/lib9p/tests/runtest @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# lib9p/tests/runtest - TODO +# +# Copyright (C) 2025 Luke T. Shumaker +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -euE -o pipefail +set -x + +valgrind --error-exitcode=2 ./tests/test_server/test_server & +server_pid=$! +# shellcheck disable=SC2064 +trap "kill $server_pid || true; wait $server_pid || true" EXIT +server_addr='localhost:9000' + +client=(9p -a "$server_addr") + +expect_lines() ( + { set +x; } &>/dev/null + printf >&2 '+ diff -u expected.txt actual.txt\n' + diff -u <(printf '%s\n' "$@") <(printf '%s\n' "$out") +) + +while [[ -d /proc/$server_pid && "$(readlink /proc/$server_pid/fd/4 2>/dev/null)" != socket:* ]]; do sleep 0.1; done + +out=$("${client[@]}" ls -l '') +expect_lines \ + 'd-r-xr-xr-x M 0 root root 0 Oct 7 15:51 Documentation' \ + '--r--r--r-- M 0 root root 14 Oct 7 15:51 README.md' + +out=$("${client[@]}" ls -l 'Documentation/') +expect_lines \ + '--r--r--r-- M 0 root root 4 Oct 7 15:51 x' + +out=$("${client[@]}" read 'README.md') +expect_lines \ + 'Hello, world!' + +out=$("${client[@]}" read 'Documentation/x') +expect_lines \ + 'foo' + +out=$("${client[@]}" stat 'Documentation/x') +expect_lines \ + "'x' 'root' 'root' 'root' q (0000000000000009 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index adeba38..a6e4eeb 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -107,17 +107,17 @@ struct lib9p_srv srv = { static COROUTINE init_cr(void *) { cr_begin(); + sleep_for_ms(1); + for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) { char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; if (!coroutine_add(name, read_cr, &srv)) error(1, 0, "coroutine_add(read_cr, &srv)"); - sleep_for_s(1); } for (int i = 0; i < 2*CONFIG_SRV9P_NUM_CONNS; i++) { char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; if (!coroutine_add(name, lib9p_srv_write_cr, &srv)) error(1, 0, "coroutine_add(lib9p_srv_write_cr, &srv)"); - sleep_for_s(1); } cr_exit(); diff --git a/libcr/coroutine.c b/libcr/coroutine.c index 129f4da..73460b7 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -1,6 +1,6 @@ /* libcr/coroutine.c - Simple embeddable coroutine implementation * - * Copyright (C) 2024 Luke T. Shumaker + * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -158,9 +158,19 @@ static_assert(CONFIG_COROUTINE_NUM > 1); #define _CR_SIG_GDB SIGWINCH #endif +#if CONFIG_COROUTINE_VALGRIND + /* Hack around a bug in Valgrind where it runs the + * sigsuspend(tmp_sigmask)-triggered handler function with the + * original mask, not the `tmp_mask`. */ + static bool _cr_plat_in_sigsuspend = false; +#endif + bool cr_plat_is_in_intrhandler(void) { +#if CONFIG_COROUTINE_VALGRIND + if (_cr_plat_in_sigsuspend) + return true; +#endif sigset_t cur_mask; - sigfillset(&cur_mask); sigprocmask(0, NULL, &cur_mask); if (sigismember(&cur_mask, _CR_SIG_SENTINEL)) /* Interrupts are disabled, so we cannot be in @@ -184,10 +194,13 @@ static_assert(CONFIG_COROUTINE_NUM > 1); assert(!_cr_plat_are_interrupts_enabled()); sigset_t set; sigemptyset(&set); +#if CONFIG_COROUTINE_VALGRIND + _cr_plat_in_sigsuspend = true; +#endif sigsuspend(&set); - - sigfillset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); +#if CONFIG_COROUTINE_VALGRIND + _cr_plat_in_sigsuspend = false; +#endif } bool _cr_plat_save_and_disable_interrupts(void) { assert(!cr_plat_is_in_intrhandler()); diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h index 0d6d25e..80eee74 100644 --- a/libcr_ipc/include/libcr_ipc/rpc.h +++ b/libcr_ipc/include/libcr_ipc/rpc.h @@ -1,6 +1,6 @@ /* libcr_ipc/rpc.h - Simple request/response system for libcr * - * Copyright (C) 2024 Luke T. Shumaker + * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -120,8 +120,10 @@ .cid = cr_getcid(), \ }; \ _cr_ipc_sll_push_to_rear(&ch->waiting_reqs, &self); \ - if (ch->waiting_resps.front) \ + if (ch->waiting_resps.front) { \ cr_unpause(_cr_ipc_sll_node_cast(struct _##NAME##_waiting_resp, ch->waiting_resps.front)->cid); \ + _cr_ipc_sll_pop_from_front(&ch->waiting_resps); \ + } \ cr_pause_and_yield(); \ return resp; \ } \ diff --git a/libhw/host_net.c b/libhw/host_net.c index 917e764..d5a5ad5 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -1,6 +1,6 @@ /* libhw/host_net.c - implementation for hosted glibc * - * Copyright (C) 2024 Luke T. Shumaker + * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -66,9 +66,11 @@ static void hostnet_init(void) { static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) { pthread_t thread; + bool saved = cr_save_and_disable_interrupts(); if (pthread_create(&thread, NULL, fn, args)) return true; cr_pause_and_yield(); + cr_restore_interrupts(saved); if (pthread_join(thread, NULL)) return true; return false; -- cgit v1.2.3-2-g168b