diff options
Diffstat (limited to 'lib9p/tests/test_server')
-rw-r--r-- | lib9p/tests/test_server/CMakeLists.txt | 45 | ||||
-rw-r--r-- | lib9p/tests/test_server/config/config.h | 67 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_flush.c | 131 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_flush.h | 27 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_shutdown.c | 104 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_shutdown.h | 23 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_whoami.c | 153 | ||||
-rw-r--r-- | lib9p/tests/test_server/fs_whoami.h | 20 | ||||
-rw-r--r-- | lib9p/tests/test_server/main.c | 168 | ||||
-rw-r--r-- | lib9p/tests/test_server/static/Documentation/x.txt | 7 | ||||
-rw-r--r-- | lib9p/tests/test_server/static/README.md | 7 |
11 files changed, 752 insertions, 0 deletions
diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt new file mode 100644 index 0000000..c61d344 --- /dev/null +++ b/lib9p/tests/test_server/CMakeLists.txt @@ -0,0 +1,45 @@ +# lib9p/tests/test_server/CMakeLists.txt - Build script for test_server executable +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +if (PICO_PLATFORM STREQUAL "host") + +# Compile ###################################################################### + +add_library(test_server_objs OBJECT + main.c + fs_flush.c + fs_shutdown.c + fs_whoami.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_util + libhw_cr +) + +# 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 + "$<TARGET_OBJECTS:test_server_objs>" +) + +# Embed ######################################################################## + +target_embed_sources(test_server_objs test_server static.h + static/README.md + static/Documentation/x.txt +) + +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..f49894b --- /dev/null +++ b/lib9p/tests/test_server/config/config.h @@ -0,0 +1,67 @@ +/* config.h - Compile-time configuration for lib9p/test/test_server + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define _CONFIG_9P_MAX_CONNS 8 +#define _CONFIG_9P_MAX_REQS (2*_CONFIG_9P_MAX_CONNS) + +/* 9P *************************************************************************/ + +#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 */ +#define CONFIG_9P_ENABLE_9P2000_L 0 /* bool */ +#define CONFIG_9P_ENABLE_9P2000_p9p 0 /* bool */ + +/* 9P_SRV *********************************************************************/ + +#define CONFIG_9P_SRV_DEBUG 1 /* bool */ + +/** + * 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_SRV_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) array pointers. + */ +#define CONFIG_9P_SRV_MAX_HOSTMSG_SIZE CONFIG_9P_SRV_MAX_MSG_SIZE+16 + +/* COROUTINE ******************************************************************/ + +#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (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_9P_MAX_CONNS /* accept+read */ + \ + _CONFIG_9P_MAX_REQS /* work+write */ ) + +#endif /* _CONFIG_H_ */ diff --git a/lib9p/tests/test_server/fs_flush.c b/lib9p/tests/test_server/fs_flush.c new file mode 100644 index 0000000..e6408d7 --- /dev/null +++ b/lib9p/tests/test_server/fs_flush.c @@ -0,0 +1,131 @@ +/* lib9p/tests/test_server/fs_flush.c - flush-* API endpoints + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libmisc/alloc.h> + +#define IMPLEMENTATION_FOR_LIB9P_SRV_H YES /* for ctx->flush_ch */ +#include "fs_flush.h" + +LO_IMPLEMENTATION_C(lib9p_srv_file, struct flush_file, flush_file, static); + +struct flush_fio { + struct flush_file *parent; +}; +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct flush_fio, flush_fio); +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct flush_fio, flush_fio, static); + +/* srv_file *******************************************************************/ + +static void flush_file_free(struct flush_file *self) { + assert(self); +} +static struct lib9p_qid flush_file_qid(struct flush_file *self) { + assert(self); + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} + +static struct lib9p_srv_stat flush_file_stat(struct flush_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + return (struct lib9p_srv_stat){ + .qid = flush_file_qid(self), + .mode = 0444, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = 6, + .name = lib9p_str(self->name), + .owner_uid = { .name = lib9p_str("root"), .num = 0 }, + .owner_gid = { .name = lib9p_str("root"), .num = 0 }, + .last_modifier_uid = { .name = lib9p_str("root"), .num = 0 }, + .extension = lib9p_str(NULL), + }; +} +static void flush_file_wstat(struct flush_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); +} +static void flush_file_remove(struct flush_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct flush_file, flush_file) + +static lo_interface lib9p_srv_fio flush_file_fopen(struct flush_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + + struct flush_fio *ret = heap_alloc(1, struct flush_fio); + ret->parent = self; + + return lo_box_flush_fio_as_lib9p_srv_fio(ret); +} + +/* srv_fio ********************************************************************/ + +static void flush_fio_iofree(struct flush_fio *self) { + assert(self); + free(self); +} + +static struct lib9p_qid flush_fio_qid(struct flush_fio *self) { + assert(self); + return flush_file_qid(self->parent); +} + +static uint32_t flush_fio_iounit(struct flush_fio *self) { + assert(self); + return 0; +} + +static uint32_t flush_fio_pwrite(struct flush_fio *LM_UNUSED(self), + struct lib9p_srv_ctx *LM_UNUSED(ctx), + void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count), + uint64_t LM_UNUSED(offset)) { + assert_notreached("not writable"); +} + +static void flush_fio_pread(struct flush_fio *self, struct lib9p_srv_ctx *ctx, + uint32_t byte_count, uint64_t LM_UNUSED(byte_offset), + struct iovec *ret) { + assert(self); + assert(ctx); + assert(ret); + + /* Wait for first Tflush */ + while (!lib9p_srv_flush_requested(ctx)) + cr_yield(); + + /* Wait for the specified number of Tflush (may be higher *or* + * lower than 1; lower would mean that the first Tflush needs + * to be flushed itself). */ + while (cr_chan_num_waiters(&ctx->flush_ch) != self->parent->flush_cnt) + cr_yield(); + + /* Return */ + switch (self->parent->flush_behavior) { + case FLUSH_READ: + *ret = (struct iovec){ + .iov_base = "Sloth\n", + .iov_len = 6 < byte_count ? 6 : byte_count, + }; + break; + case FLUSH_ERROR: + lib9p_srv_acknowledge_flush(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush"); + break; + case FLUSH_SILENT: + lib9p_srv_acknowledge_flush(ctx); + break; + } + cr_yield(); +} diff --git a/lib9p/tests/test_server/fs_flush.h b/lib9p/tests/test_server/fs_flush.h new file mode 100644 index 0000000..a509c4a --- /dev/null +++ b/lib9p/tests/test_server/fs_flush.h @@ -0,0 +1,27 @@ +/* lib9p/tests/test_server/fs_flush.h - flush-* API endpoints + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ +#define _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ + +#include <util9p/static.h> +#include <libhw/host_net.h> + +struct flush_file { + char *name; + uint64_t pathnum; + + unsigned int flush_cnt; + enum { + FLUSH_READ, + FLUSH_ERROR, + FLUSH_SILENT, + } flush_behavior; +}; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct flush_file, flush_file); +#define lo_box_flush_file_as_lib9p_srv_file(obj) util9p_box(flush_file, obj) + +#endif /* _LIB9P_TESTS_TEST_SERVER_FS_FLUSH_H_ */ diff --git a/lib9p/tests/test_server/fs_shutdown.c b/lib9p/tests/test_server/fs_shutdown.c new file mode 100644 index 0000000..d4ae67e --- /dev/null +++ b/lib9p/tests/test_server/fs_shutdown.c @@ -0,0 +1,104 @@ +/* lib9p/tests/test_server/fs_shutdown.c - /shutdown API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libmisc/alloc.h> + +#include "fs_shutdown.h" + +LO_IMPLEMENTATION_C(lib9p_srv_file, struct shutdown_file, shutdown_file, static); + +struct shutdown_fio { + struct shutdown_file *parent; +}; +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct shutdown_fio, shutdown_fio); +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct shutdown_fio, shutdown_fio, static); + +/* srv_file *******************************************************************/ + +static void shutdown_file_free(struct shutdown_file *self) { + assert(self); +} +static struct lib9p_qid shutdown_file_qid(struct shutdown_file *self) { + assert(self); + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE | LIB9P_QT_APPEND, + .vers = 1, + .path = self->pathnum, + }; +} + +static struct lib9p_srv_stat shutdown_file_stat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + return (struct lib9p_srv_stat){ + .qid = shutdown_file_qid(self), + .mode = 0222 | LIB9P_DM_APPEND, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = 0, + .name = lib9p_str(self->name), + .owner_uid = { .name=lib9p_str("root"), .num=0 }, + .owner_gid = { .name=lib9p_str("root"), .num=0 }, + .last_modifier_uid = { .name=lib9p_str("root"), .num=0 }, + .extension = lib9p_str(NULL), + }; +} +static void shutdown_file_wstat(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); +} +static void shutdown_file_remove(struct shutdown_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct shutdown_file, shutdown_file) + +static lo_interface lib9p_srv_fio shutdown_file_fopen(struct shutdown_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + + struct shutdown_fio *ret = heap_alloc(1, struct shutdown_fio); + ret->parent = self; + + return lo_box_shutdown_fio_as_lib9p_srv_fio(ret); +} + +/* srv_fio ********************************************************************/ + +static void shutdown_fio_iofree(struct shutdown_fio *self) { + assert(self); + free(self); +} + +static struct lib9p_qid shutdown_fio_qid(struct shutdown_fio *self) { + assert(self); + return shutdown_file_qid(self->parent); +} + +static uint32_t shutdown_fio_iounit(struct shutdown_fio *self) { + assert(self); + return 0; +} + +static uint32_t shutdown_fio_pwrite(struct shutdown_fio *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t offset) { + assert(self); + assert(ctx); + assert(buf); + assert(offset == 0); + if (byte_count == 0) + return 0; + for (size_t i = 0; i < self->parent->nlisteners; i++) + LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&self->parent->listeners[i]), close); + return byte_count; +} +static void shutdown_fio_pread(struct shutdown_fio *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx), + uint32_t LM_UNUSED(byte_count), uint64_t LM_UNUSED(byte_offset), + struct iovec *LM_UNUSED(ret)) { + assert_notreached("not readable"); +} diff --git a/lib9p/tests/test_server/fs_shutdown.h b/lib9p/tests/test_server/fs_shutdown.h new file mode 100644 index 0000000..65956db --- /dev/null +++ b/lib9p/tests/test_server/fs_shutdown.h @@ -0,0 +1,23 @@ +/* lib9p/tests/test_server/fs_shutdown.h - /shutdown API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ +#define _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ + +#include <util9p/static.h> +#include <libhw/host_net.h> + +struct shutdown_file { + char *name; + uint64_t pathnum; + + struct hostnet_tcp_listener *listeners; + size_t nlisteners; +}; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct shutdown_file, shutdown_file); +#define lo_box_shutdown_file_as_lib9p_srv_file(obj) util9p_box(shutdown_file, obj) + +#endif /* _LIB9P_TESTS_TEST_SERVER_FS_SHUTDOWN_H_ */ diff --git a/lib9p/tests/test_server/fs_whoami.c b/lib9p/tests/test_server/fs_whoami.c new file mode 100644 index 0000000..8d9752a --- /dev/null +++ b/lib9p/tests/test_server/fs_whoami.c @@ -0,0 +1,153 @@ +/* lib9p/tests/test_server/fs_whoami.c - /whoami API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <stdio.h> /* for snprintf() */ +#include <stdlib.h> /* for realloc(), free() */ + +#include <libmisc/alloc.h> + +#include "fs_whoami.h" + +LO_IMPLEMENTATION_C(lib9p_srv_file, struct whoami_file, whoami_file, static); + +struct whoami_fio { + struct whoami_file *parent; + size_t buf_len; + char *buf; +}; +LO_IMPLEMENTATION_H(lib9p_srv_fio, struct whoami_fio, whoami_fio); +LO_IMPLEMENTATION_C(lib9p_srv_fio, struct whoami_fio, whoami_fio, static); + +size_t whoami_len(struct lib9p_srv_ctx *ctx) { + assert(ctx); + assert(ctx->user); + + size_t len = 0; + uint32_t uid = ctx->user->num; + while (uid) { + len++; + uid /= 10; + } + if (!len) + len++; + len += 2; + len += ctx->user->name.len; + return len; +} + +/* srv_file *******************************************************************/ + +static void whoami_file_free(struct whoami_file *self) { + assert(self); +} +static struct lib9p_qid whoami_file_qid(struct whoami_file *self) { + assert(self); + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} + +static struct lib9p_srv_stat whoami_file_stat(struct whoami_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + + return (struct lib9p_srv_stat){ + .qid = whoami_file_qid(self), + .mode = 0444, + .atime_sec = UTIL9P_ATIME, + .mtime_sec = UTIL9P_MTIME, + .size = whoami_len(ctx), + .name = lib9p_str(self->name), + .owner_uid = { .name=lib9p_str("root"), .num=0 }, + .owner_gid = { .name=lib9p_str("root"), .num=0 }, + .last_modifier_uid = { .name=lib9p_str("root"), .num=0 }, + .extension = lib9p_str(NULL), + }; +} +static void whoami_file_wstat(struct whoami_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_srv_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot wstat API file"); +} +static void whoami_file_remove(struct whoami_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct whoami_file, whoami_file) + +static lo_interface lib9p_srv_fio whoami_file_fopen(struct whoami_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + + struct whoami_fio *ret = heap_alloc(1, struct whoami_fio); + ret->parent = self; + ret->buf_len = 0; + ret->buf = NULL; + + return lo_box_whoami_fio_as_lib9p_srv_fio(ret); +} + +/* srv_fio ********************************************************************/ + +static void whoami_fio_iofree(struct whoami_fio *self) { + assert(self); + if (self->buf) + free(self->buf); + free(self); +} + +static struct lib9p_qid whoami_fio_qid(struct whoami_fio *self) { + assert(self); + assert(self->parent); + return whoami_file_qid(self->parent); +} + +static uint32_t whoami_fio_iounit(struct whoami_fio *self) { + assert(self); + return 0; +} + +static uint32_t whoami_fio_pwrite(struct whoami_fio *LM_UNUSED(self), + struct lib9p_srv_ctx *LM_UNUSED(ctx), + void *LM_UNUSED(buf), uint32_t LM_UNUSED(byte_count), + uint64_t LM_UNUSED(offset)) { + assert_notreached("not writable"); +} +static void whoami_fio_pread(struct whoami_fio *self, struct lib9p_srv_ctx *ctx, + uint32_t byte_count, uint64_t byte_offset, + struct iovec *ret) { + assert(self); + assert(ctx); + assert(ret); + + size_t data_size = whoami_len(ctx); + if (self->buf_len < data_size+1) { + self->buf = realloc(self->buf, data_size+1); + self->buf_len = data_size+1; + } + snprintf(self->buf, self->buf_len, "%"PRIu32" %.*s\n", + ctx->user->num, ctx->user->name.len, ctx->user->name.utf8); + + if (byte_offset > (uint64_t)data_size) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_EINVAL, "offset is past end-of-file length"); + return; + } + + size_t beg_off = (size_t)byte_offset; + size_t end_off = beg_off + (size_t)byte_count; + if (end_off > data_size) + end_off = data_size; + + *ret = (struct iovec){ + .iov_base = &self->buf[beg_off], + .iov_len = end_off-beg_off, + }; +} diff --git a/lib9p/tests/test_server/fs_whoami.h b/lib9p/tests/test_server/fs_whoami.h new file mode 100644 index 0000000..0d3d311 --- /dev/null +++ b/lib9p/tests/test_server/fs_whoami.h @@ -0,0 +1,20 @@ +/* lib9p/tests/test_server/fs_whoami.h - /whoami API endpoint + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ +#define _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ + +#include <util9p/static.h> +#include <libhw/host_net.h> + +struct whoami_file { + char *name; + uint64_t pathnum; +}; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct whoami_file, whoami_file); +#define lo_box_whoami_file_as_lib9p_srv_file(obj) util9p_box(whoami_file, obj) + +#endif /* _LIB9P_TESTS_TEST_SERVER_FS_WHOAMI_H_ */ diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c new file mode 100644 index 0000000..d7819eb --- /dev/null +++ b/lib9p/tests/test_server/main.c @@ -0,0 +1,168 @@ +/* lib9p/tests/test_server/main.c - Main entry point for test 9P server + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <error.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> /* for atoi() */ + +#include <lib9p/srv.h> +#include <libcr/coroutine.h> +#include <libhw/generic/net.h> +#include <libhw/generic/alarmclock.h> +#include <libhw/host_alarmclock.h> +#include <libhw/host_net.h> +#include <libmisc/macro.h> +#include <util9p/static.h> + +#include "static.h" +#include "fs_flush.h" +#include "fs_shutdown.h" +#include "fs_whoami.h" + +/* configuration **************************************************************/ + +#include "config.h" + +#ifndef _CONFIG_9P_MAX_CONNS + #error config.h must define _CONFIG_9P_MAX_CONNS +#endif +#ifndef _CONFIG_9P_MAX_REQS + #error config.h must define _CONFIG_9P_MAX_REQS +#endif + +/* globals ********************************************************************/ + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *, struct lib9p_s); + +const char *hexdig = "0123456789abcdef"; + +struct { + uint16_t port; + struct hostnet_tcp_listener listeners[_CONFIG_9P_MAX_CONNS]; + struct lib9p_srv srv; + FILE *logstream; +} globals = { + .srv = (struct lib9p_srv){ + .rootdir = get_root, + }, +}; + +/* file tree ******************************************************************/ + +#define STATIC_FILE(N, STRNAME, SYMNAME) \ + UTIL9P_STATIC_FILE(N, STRNAME, \ + .data_start = _binary_static_##SYMNAME##_start, \ + .data_end = _binary_static_##SYMNAME##_end) +#define STATIC_DIR(N, STRNAME, ...) \ + UTIL9P_STATIC_DIR(N, STRNAME, __VA_ARGS__) + +#define API_FILE(N, STRNAME, SYMNAME, ...) \ + lo_box_##SYMNAME##_file_as_lib9p_srv_file(&((struct SYMNAME##_file){ \ + .name = STRNAME, \ + .pathnum = N \ + __VA_OPT__(,) __VA_ARGS__ \ + })) + +struct lib9p_srv_file root = + STATIC_DIR(1, "", + STATIC_DIR(2, "Documentation", + STATIC_FILE(3, "x", Documentation_x_txt), + ), + STATIC_FILE(4, "README.md", README_md), + API_FILE(5, "shutdown", shutdown, + .listeners = globals.listeners, + .nlisteners = LM_ARRAY_LEN(globals.listeners)), + API_FILE(8, "whoami", whoami), + API_FILE(9, "flush-read", flush, .flush_cnt=1, .flush_behavior=FLUSH_READ), + API_FILE(10, "flush-error", flush, .flush_cnt=1, .flush_behavior=FLUSH_ERROR), + API_FILE(11, "flush-silent", flush, .flush_cnt=1, .flush_behavior=FLUSH_SILENT), + API_FILE(12, "flush-slowsilent", flush, .flush_cnt=2, .flush_behavior=FLUSH_SILENT), + API_FILE(13, "flush-slowread", flush, .flush_cnt=0, .flush_behavior=FLUSH_READ), + ); + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { + return root; +} + +/* main ***********************************************************************/ + +static COROUTINE read_cr(void *_i) { + int i = *((int *)_i); + cr_begin(); + + hostnet_tcp_listener_init(&globals.listeners[i], globals.port); + + lib9p_srv_accept_and_read_loop(&globals.srv, lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i])); + + cr_end(); +} + +static COROUTINE write_cr(void *) { + cr_begin(); + + lib9p_srv_worker_loop(&globals.srv); + + cr_end(); +} + +static COROUTINE init_cr(void *) { + cr_begin(); + + sleep_for_ms(1); /* test that sleep works */ + + for (int i = 0; i < _CONFIG_9P_MAX_CONNS; i++) { + char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, read_cr, &i)) + error(1, 0, "coroutine_add(read_cr, &i)"); + } + for (int i = 0; i < _CONFIG_9P_MAX_REQS; i++) { + char name[] = {'w', 'r', 'i', 't', 'e', '-', hexdig[i], '\0'}; + if (!coroutine_add(name, write_cr, NULL)) + error(1, 0, "coroutine_add(write_cr, NULL)"); + } + + cr_exit(); +} + +static void log_fct(char character, void *_stream) { + FILE *stream = _stream; + putc(character, stream); + putchar(character); +} + +static void log_msg(struct lib9p_srv_ctx *ctx, enum lib9p_msg_type typ, void *hostmsg) { + /* It sucks that %v trips -Wformat and -Wformat-extra-args + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + fmt_fctprintf(log_fct, globals.logstream, + "%c %v\n", typ % 2 ? '<' : '>', + lo_box_lib9p_msg_as_fmt_formatter(&ctx->basectx, typ, hostmsg)); +#pragma GCC diagnostic pop + fflush(globals.logstream); +} + +int main(int argc, char *argv[]) { + if (argc != 3) + error(2, 0, "usage: %s PORT_NUMBER LOGFILE", argv[0]); + + globals.port = atoi(argv[1]); + globals.logstream = fopen(argv[2], "w"); + if (!globals.logstream) + error(2, errno, "fopen"); + globals.srv.msglog = log_msg; + + struct hostclock clock_monotonic = { + .clock_id = CLOCK_MONOTONIC, + }; + bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic); + coroutine_add("init", init_cr, NULL); + coroutine_main(); + fclose(globals.logstream); + return 0; +} diff --git a/lib9p/tests/test_server/static/Documentation/x.txt b/lib9p/tests/test_server/static/Documentation/x.txt new file mode 100644 index 0000000..e85ee4e --- /dev/null +++ b/lib9p/tests/test_server/static/Documentation/x.txt @@ -0,0 +1,7 @@ +<!-- + Documentation/x.txt - test static file + + Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later +--> +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..c2d88ed --- /dev/null +++ b/lib9p/tests/test_server/static/README.md @@ -0,0 +1,7 @@ +<!-- + README.md - test static file + + Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + SPDX-License-Identifier: AGPL-3.0-or-later +--> +Hello, world! |