diff options
134 files changed, 3292 insertions, 402 deletions
diff --git a/cmake/wnix_program.cmake b/cmake/wnix_program.cmake new file mode 100644 index 0000000..65efc50 --- /dev/null +++ b/cmake/wnix_program.cmake @@ -0,0 +1,19 @@ +function(wnix_program) + add_compile_options(-g) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-Og) + endif() + + add_subdirectory(../libc ${CMAKE_CURRENT_BINARY_DIR}/libc) + target_link_libraries(${PROJECT_NAME} PRIVATE c) + # TODO: Debugging symbols could still be there, but nwc still has issues. + add_custom_target(${PROJECT_NAME}_strip ALL + ${CMAKE_STRIP} ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} + ) + add_dependencies(${PROJECT_NAME}_strip ${PROJECT_NAME}) + install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +endfunction() diff --git a/programs/initd/initd.c b/programs/initd/initd.c index 5e200b3..3ca3f6e 100644 --- a/programs/initd/initd.c +++ b/programs/initd/initd.c @@ -16,9 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#if 1 #include <sys/mount.h> -#endif #include <sys/stat.h> #include <errno.h> #include <stddef.h> @@ -27,17 +25,14 @@ int main(int argc, char *argv[]) { + puts("Starting second-stage bootloader from WebAssembly!"); + #if 0 - puts("hi from wasm!"); -#endif -#if 1 - if (mkdir("/home", 0755)) - return errno; -#endif -#if 1 - if (mount("/dev/mc0", "/home", NULL, 0, NULL)) + if (mkdir("/home", 0755) + || mkdir("/home/wnix", 0755) + || mount("/dev/mc0", "/home/wnix", "ps1mcfs", 0, NULL)) return errno; #endif - return 1; + return EXIT_SUCCESS; } diff --git a/programs/wnix/CMakeLists.txt b/programs/libc/CMakeLists.txt index a118bd0..a0c6d97 100644 --- a/programs/wnix/CMakeLists.txt +++ b/programs/libc/CMakeLists.txt @@ -14,13 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -add_library(wnix - mkdir.c - mount.c - sbrk.c - start.c -) set(LIBC_FREESTANDING ON) -add_subdirectory(../../src/libc ${CMAKE_CURRENT_BINARY_DIR}/libc) +set(libc_dir ${CMAKE_CURRENT_LIST_DIR}/../../src/libc) +add_subdirectory(${libc_dir} ${CMAKE_CURRENT_BINARY_DIR}/libc) +add_subdirectory(fcntl) +add_subdirectory(sys) +add_subdirectory(unistd) target_compile_definitions(c PRIVATE PRINTF_DISABLE_SUPPORT_FLOAT) -target_link_libraries(wnix PUBLIC c) +target_sources(c PRIVATE + malloc_init.c + start.c +) diff --git a/programs/libc/fcntl/CMakeLists.txt b/programs/libc/fcntl/CMakeLists.txt new file mode 100644 index 0000000..c0ca1d1 --- /dev/null +++ b/programs/libc/fcntl/CMakeLists.txt @@ -0,0 +1,19 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(c PRIVATE + open.c +) diff --git a/programs/libc/fcntl/open.c b/programs/libc/fcntl/open.c new file mode 100644 index 0000000..71511a7 --- /dev/null +++ b/programs/libc/fcntl/open.c @@ -0,0 +1,41 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <fcntl.h> +#include <sys/types.h> +#include <errno.h> +#include <stdarg.h> + +int open(const char *const path, const int flags, ...) +{ + int __wnix_open(const char *, int, mode_t, int *) __attribute__(( + __import_module__("wnix"), + __import_name__("open") + )); + + mode_t mode = 0; + va_list ap; + + va_start(ap, flags); + + if (flags & O_CREAT) + mode = va_arg(ap, mode_t); + + va_end(ap); + return __wnix_open(path, flags, mode, &errno); +} diff --git a/programs/libc/malloc_init.c b/programs/libc/malloc_init.c new file mode 100644 index 0000000..969f523 --- /dev/null +++ b/programs/libc/malloc_init.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <libc/malloc.h> +#include <tinyalloc.h> +#include <stdint.h> + +void __malloc_init(void) +{ + /* README.md states 16 is a "good default value". */ + if (!ta_init(64, 16, sizeof (intmax_t))) + abort(); +} diff --git a/programs/wnix/start.c b/programs/libc/start.c index 06dc4d5..5e56239 100644 --- a/programs/wnix/start.c +++ b/programs/libc/start.c @@ -19,10 +19,13 @@ #include <errno.h> #include <stddef.h> #include <stdlib.h> +#include <stdio.h> void _start(void) { - int main(int, char *[]); + /* clang mangles main if argc/argv are present: + * https://reviews.llvm.org/D127888 */ + int __main_argc_argv(int, char *[]); int __wnix_argc(void) __attribute__(( __import_module__("wnix"), __import_name__("argc") @@ -32,6 +35,11 @@ void _start(void) __import_name__("exit") )); + if (!(__stdin = fopen("/dev/stdin", "rb")) + || !(__stdout = fopen("/dev/stdout", "wb")) + || !(__stderr = fopen("/dev/stderr", "wb"))) + __wnix_exit(EXIT_FAILURE); + const int argc = __wnix_argc(); char **argv = malloc((argc + 1) * sizeof *argv); @@ -60,5 +68,5 @@ void _start(void) } argv[argc] = NULL; - __wnix_exit(main(argc, argv)); + __wnix_exit(__main_argc_argv(argc, argv)); } diff --git a/programs/libc/sys/CMakeLists.txt b/programs/libc/sys/CMakeLists.txt new file mode 100644 index 0000000..ea57403 --- /dev/null +++ b/programs/libc/sys/CMakeLists.txt @@ -0,0 +1,18 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_subdirectory(mount) +add_subdirectory(stat) diff --git a/programs/libc/sys/mount/CMakeLists.txt b/programs/libc/sys/mount/CMakeLists.txt new file mode 100644 index 0000000..222e506 --- /dev/null +++ b/programs/libc/sys/mount/CMakeLists.txt @@ -0,0 +1,19 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(c PRIVATE + mount.c +) diff --git a/programs/wnix/mount.c b/programs/libc/sys/mount/mount.c index 2512f73..2512f73 100644 --- a/programs/wnix/mount.c +++ b/programs/libc/sys/mount/mount.c diff --git a/programs/libc/sys/stat/CMakeLists.txt b/programs/libc/sys/stat/CMakeLists.txt new file mode 100644 index 0000000..60a8d6c --- /dev/null +++ b/programs/libc/sys/stat/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(c PRIVATE + mkdir.c + umask.c +) diff --git a/programs/wnix/mkdir.c b/programs/libc/sys/stat/mkdir.c index 5547616..5547616 100644 --- a/programs/wnix/mkdir.c +++ b/programs/libc/sys/stat/mkdir.c diff --git a/programs/libc/sys/stat/umask.c b/programs/libc/sys/stat/umask.c new file mode 100644 index 0000000..f42a6e5 --- /dev/null +++ b/programs/libc/sys/stat/umask.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <sys/stat.h> + +mode_t umask(const mode_t mask) +{ + int __wnix_umask(mode_t) __attribute__(( + __import_module__("wnix"), + __import_name__("umask") + )); + + return __wnix_umask(mask); +} diff --git a/programs/libc/unistd/CMakeLists.txt b/programs/libc/unistd/CMakeLists.txt new file mode 100644 index 0000000..e3481bc --- /dev/null +++ b/programs/libc/unistd/CMakeLists.txt @@ -0,0 +1,21 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(c PRIVATE + close.c + sbrk.c + write.c +) diff --git a/programs/libc/unistd/close.c b/programs/libc/unistd/close.c new file mode 100644 index 0000000..94ca6fc --- /dev/null +++ b/programs/libc/unistd/close.c @@ -0,0 +1,30 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <errno.h> + +int close(const int fd) +{ + int __wnix_close(int, int *) __attribute__(( + __import_module__("wnix"), + __import_name__("close") + )); + + return __wnix_close(fd, &errno); +} diff --git a/programs/wnix/sbrk.c b/programs/libc/unistd/sbrk.c index 7e32118..7e32118 100644 --- a/programs/wnix/sbrk.c +++ b/programs/libc/unistd/sbrk.c diff --git a/programs/libc/unistd/write.c b/programs/libc/unistd/write.c new file mode 100644 index 0000000..9151e61 --- /dev/null +++ b/programs/libc/unistd/write.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <stddef.h> + +ssize_t write(const int fd, const void *const p, const size_t sz) +{ + int __wnix_write(int, const void *, size_t, int *) __attribute__(( + __import_module__("wnix"), + __import_name__("write") + )); + + return __wnix_write(fd, p, sz, &errno); +} diff --git a/programs/wasm-clang-toolchain.cmake b/programs/wasm-clang-toolchain.cmake index a6890f8..746392c 100644 --- a/programs/wasm-clang-toolchain.cmake +++ b/programs/wasm-clang-toolchain.cmake @@ -23,6 +23,7 @@ set(CMAKE_C_COMPILER_WORKS 1) set(CMAKE_C_FLAGS " \ ${CMAKE_C_FLAGS} \ --target=wasm32-unknown-unknown-wasm \ + -mcpu=mvp \ -fno-exceptions \ -nostdinc \ -nostdlib \ diff --git a/programs/yes/CMakeLists.txt b/programs/yes/CMakeLists.txt index 59abf8f..cebd1c4 100644 --- a/programs/yes/CMakeLists.txt +++ b/programs/yes/CMakeLists.txt @@ -18,14 +18,5 @@ cmake_minimum_required(VERSION 3.13) include(GNUInstallDirs) project(yes C) add_executable(${PROJECT_NAME} yes.c) -add_subdirectory(../wnix ${CMAKE_CURRENT_BINARY_DIR}/wnix) -target_link_libraries(${PROJECT_NAME} PRIVATE wnix) -# TODO: Debugging symbols could still be there, but nwc still has issues. -add_custom_target(${PROJECT_NAME}_strip ALL - ${CMAKE_STRIP} ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} -) -add_dependencies(${PROJECT_NAME}_strip ${PROJECT_NAME}) -install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +include(wnix_program) +wnix_program() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fb2cb4..d61582f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,7 +21,7 @@ add_subdirectory(wip) add_subdirectory(dynstr) # Avoid C11 since it is not supported by the i386-mingw32 toolchain. -set(cflags ${cflags} -Wall -pedantic) +set(cflags ${cflags} -Wall) set(components aio diff --git a/src/aio/src/mkdir.c b/src/aio/src/mkdir.c index 40b09b6..44cc43d 100644 --- a/src/aio/src/mkdir.c +++ b/src/aio/src/mkdir.c @@ -121,10 +121,10 @@ struct aio *aio_mkdir(const struct fs_mkdir *const m, .args = mk }; - if (d) - mk->done = *d; - else if (inode_search(&s, &aio->r)) + if (inode_search(&s, &aio->r)) goto failure; + else if (d) + mk->done = *d; return aio; diff --git a/src/bin/include/bin.h b/src/bin/include/bin.h index a5528c5..34558f9 100644 --- a/src/bin/include/bin.h +++ b/src/bin/include/bin.h @@ -20,8 +20,10 @@ #define BIN_H #include <sys/types.h> +#include <fs/fs.h> -int bin_exec(const char *path, const char *const *argv, uid_t uid, gid_t gid); +int bin_exec(const char *path, const char *const *argv, + const struct fs_stdstreams *ss, pid_t parent, uid_t uid, gid_t gid); int bin_update(void); #endif diff --git a/src/bin/private_include/bin/proc.h b/src/bin/private_include/bin/proc.h index cf0c04e..92f1566 100644 --- a/src/bin/private_include/bin/proc.h +++ b/src/bin/private_include/bin/proc.h @@ -43,6 +43,8 @@ int bin_proc_s_pop(void *dst, size_t n, void *user); int bin_proc_s_read(size_t offset, void *dst, size_t n, void *user); int bin_proc_s_write(size_t offset, const void *dst, size_t n, void *user); size_t bin_proc_s_ptr(void *user); +struct bin_proc_fd *bin_proc_fd_find(const struct bin_proc *p, int fd); +int bin_proc_fd_alloc(struct bin_proc *p, const struct fs_fd *fd, int flags); extern const struct nw_inst_cfg bin_proc_cfg; diff --git a/src/bin/private_include/bin/types.h b/src/bin/private_include/bin/types.h index 2a498ed..6f231ae 100644 --- a/src/bin/private_include/bin/types.h +++ b/src/bin/private_include/bin/types.h @@ -28,7 +28,7 @@ #include <sys/types.h> #include <stdbool.h> -enum {BIN_IMPORTS = 9}; +enum {BIN_IMPORTS = 10}; struct bin_dbg { @@ -54,18 +54,25 @@ struct bin_global size_t sz; }; +struct bin_proc_fd +{ + int fd, flags; + struct fs_fd fs_fd; +}; + struct bin_proc { const struct bin_mod *mod; struct nw_inst instance; - union nw_value args[7]; + union nw_value args[6]; struct bin_dbg dbg; struct caio *caio; char *path, **argv; size_t argc; - pid_t pid; + pid_t pid, parent; uid_t uid; gid_t gid; + mode_t umask; char header[sizeof "asm"]; size_t i; long retval; @@ -73,7 +80,8 @@ struct bin_proc struct bin_global global; void *import; struct page *linear; - int *fds; + struct fs_stdstreams ss; + struct bin_proc_fd *fds; size_t n_fds; struct bin_proc *prev, *next; }; diff --git a/src/bin/private_include/bin/wasi/errno.h b/src/bin/private_include/bin/wasi/errno.h deleted file mode 100644 index 442e877..0000000 --- a/src/bin/private_include/bin/wasi/errno.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * wnix, a Unix-like operating system for WebAssembly applications. - * Copyright (C) 2025 Xavier Del Campo Romero - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef WASI_ERRNO_H -#define WASI_ERRNO_H - -enum wasi_errno -{ - WASI_SUCCESS, - WASI_2BIG, - WASI_ACCES, - WASI_ADDRINUSE, - WASI_ADDRNOTAVAIL, - WASI_AFNOSUPPORT, - WASI_AGAIN, - WASI_ALREADY, - WASI_BADF, - WASI_BADMSG, - WASI_BUSY, - WASI_CANCELED, - WASI_CHILD, - WASI_CONNABORTED, - WASI_CONNREFUSED, - WASI_CONNRESET, - WASI_DEADLK, - WASI_DESTADDRREQ, - WASI_DOM, - WASI_DQUOT, - WASI_EXIST, - WASI_FAULT, - WASI_FBIG, - WASI_HOSTUNREACH, - WASI_IDRM, - WASI_ILSEQ, - WASI_INPROGRESS, - WASI_INTR, - WASI_INVAL, - WASI_IO, - WASI_ISCONN, - WASI_ISDIR, - WASI_LOOP, - WASI_MFILE, - WASI_MLINK, - WASI_MSGSIZE, - WASI_MULTIHOP, - WASI_NAMETOOLONG, - WASI_NETDOWN, - WASI_NETRESET, - WASI_NETUNREACH, - WASI_NFILE, - WASI_NOBUFS, - WASI_NODEV, - WASI_NOENT, - WASI_NOEXEC, - WASI_NOLCK, - WASI_NOLINK, - WASI_NOMEM, - WASI_NOMSG, - WASI_NOPROTOOPT, - WASI_NOSPC, - WASI_NOSYS, - WASI_NOTCONN, - WASI_NOTDIR, - WASI_NOTEMPTY, - WASI_NOTRECOVERABLE, - WASI_NOTSOCK, - WASI_NOTSUP, - WASI_NOTTY, - WASI_NXIO, - WASI_OVERFLOW, - WASI_OWNERDEAD, - WASI_PERM, - WASI_PIPE, - WASI_PROTO, - WASI_PROTONOSUPPORT, - WASI_PROTOTYPE, - WASI_RANGE, - WASI_ROFS, - WASI_SPIPE, - WASI_SRCH, - WASI_STALE, - WASI_TIMEDOUT, - WASI_TXTBSY, - WASI_XDEV, - WASI_NOTCAPABLE -}; - -int wasi_errno(int error, enum wasi_errno *out); - -#endif diff --git a/src/bin/private_include/bin/wnix.h b/src/bin/private_include/bin/wnix.h index 07e7131..f3844c5 100644 --- a/src/bin/private_include/bin/wnix.h +++ b/src/bin/private_include/bin/wnix.h @@ -33,5 +33,13 @@ enum nw_state bin_wnix_mkdir(const union nw_value *params, union nw_value *ret, void *user, struct nw_next *next); enum nw_state bin_wnix_mount(const union nw_value *params, union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_write(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_umask(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_open(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); +enum nw_state bin_wnix_close(const union nw_value *params, + union nw_value *ret, void *user, struct nw_next *next); #endif diff --git a/src/bin/private_include/bin/wnix/routines.h b/src/bin/private_include/bin/wnix/routines.h index aa488fb..3ba5118 100644 --- a/src/bin/private_include/bin/wnix/routines.h +++ b/src/bin/private_include/bin/wnix/routines.h @@ -23,7 +23,22 @@ #include <bin/types.h> #include <nanowasm/nw.h> -int bin_wnix_set_errno(struct bin_proc *p, int error, unsigned long addr, - struct nw_next *next); +struct bin_readstr +{ + unsigned long addr; + struct bin_proc *p; + char **str; + int (*error)(struct bin_proc *, int, struct nw_next *); +}; + +struct bin_set_errno +{ + int error; + unsigned long addr; + struct bin_proc *p; +}; + +int bin_wnix_readstr(const struct bin_readstr *cfg, struct nw_next *next); +int bin_wnix_set_errno(const struct bin_set_errno *cfg, struct nw_next *next); #endif diff --git a/src/bin/src/exec.c b/src/bin/src/exec.c index 83c0e5f..1796af9 100644 --- a/src/bin/src/exec.c +++ b/src/bin/src/exec.c @@ -30,6 +30,7 @@ #include <nanowasm/nw.h> #include <nanowasm/dbg.h> #include <fcntl.h> +#include <sys/stat.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> @@ -318,7 +319,8 @@ failure: } int bin_exec(const char *const path, const char *const *const argv, - const uid_t uid, const gid_t gid) + const struct fs_stdstreams *const ss, const pid_t parent, const uid_t uid, + const gid_t gid) { const pid_t pid = bin_pid(); char *pathdup = NULL; @@ -353,9 +355,12 @@ int bin_exec(const char *const path, const char *const *const argv, { .path = pathdup, .dbg.aio = serial, + .parent = parent, .pid = pid, .uid = uid, - .gid = gid + .gid = gid, + .ss = *ss, + .umask = S_IWGRP | S_IWOTH }; if (copy_args(p, path, argv) diff --git a/src/bin/src/mod/cfg.c b/src/bin/src/mod/cfg.c index 8c78a32..1be26ca 100644 --- a/src/bin/src/mod/cfg.c +++ b/src/bin/src/mod/cfg.c @@ -88,6 +88,50 @@ static const struct nw_import imports[BIN_IMPORTS] = .fn = bin_wnix_mount, .signature = "i(iiiiii)" } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "write", + .u.function = + { + .fn = bin_wnix_write, + .signature = "i(iiii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "umask", + .u.function = + { + .fn = bin_wnix_umask, + .signature = "i(i)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "open", + .u.function = + { + .fn = bin_wnix_open, + .signature = "i(iiii)" + } + }, + + { + .kind = NW_KIND_FUNCTION, + .module = "wnix", + .field = "close", + .u.function = + { + .fn = bin_wnix_close, + .signature = "i(ii)" + } } }; diff --git a/src/bin/src/proc/CMakeLists.txt b/src/bin/src/proc/CMakeLists.txt index 47df502..e9a1d1c 100644 --- a/src/bin/src/proc/CMakeLists.txt +++ b/src/bin/src/proc/CMakeLists.txt @@ -17,6 +17,8 @@ target_sources(bin PRIVATE cfg.c eof.c + fd_alloc.c + fd_find.c free.c init.c lock.c diff --git a/src/bin/src/proc/fd_alloc.c b/src/bin/src/proc/fd_alloc.c new file mode 100644 index 0000000..3db5e17 --- /dev/null +++ b/src/bin/src/proc/fd_alloc.c @@ -0,0 +1,54 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin/proc.h> +#include <bin/types.h> +#include <fs/fs.h> +#include <stddef.h> +#include <stdlib.h> + +int bin_proc_fd_alloc(struct bin_proc *const p, const struct fs_fd *const fs_fd, + const int flags) +{ + int fd = 0; + + for (size_t i = 0; i < p->n_fds;) + if (fd == p->fds[i].fd) + { + fd++; + i = 0; + } + else + i++; + + const size_t n = p->n_fds + 1; + struct bin_proc_fd *const fds = realloc(p->fds, n * sizeof *fds); + + if (!fds) + return -1; + + fds[p->n_fds++] = (const struct bin_proc_fd) + { + .fd = fd, + .flags = flags, + .fs_fd = *fs_fd + }; + + p->fds = fds; + return fd; +} diff --git a/src/bin/src/proc/fd_find.c b/src/bin/src/proc/fd_find.c new file mode 100644 index 0000000..b729e26 --- /dev/null +++ b/src/bin/src/proc/fd_find.c @@ -0,0 +1,38 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin/proc.h> +#include <bin/types.h> +#include <fcntl.h> +#include <stddef.h> + +struct bin_proc_fd *bin_proc_fd_find(const struct bin_proc *const p, const int fd) +{ + if (fd < 0) + return NULL; + + for (size_t i = 0; i < p->n_fds; i++) + { + struct bin_proc_fd *const pfd = &p->fds[i]; + + if (pfd->fd == fd && pfd->flags & O_WRONLY) + return pfd; + } + + return NULL; +} diff --git a/src/bin/src/proc/free.c b/src/bin/src/proc/free.c index a5bdea1..6876749 100644 --- a/src/bin/src/proc/free.c +++ b/src/bin/src/proc/free.c @@ -30,6 +30,9 @@ void bin_proc_free(struct bin_proc *const p) struct bin_dbg *const d = &p->dbg; + for (char **arg = p->argv; *arg; arg++) + free(*arg); + dynstr_free(&d->dstr); free(d->bkpt); aio_free(d->aio); diff --git a/src/bin/src/proc/pc.c b/src/bin/src/proc/pc.c index 28b2f44..29bdd7f 100644 --- a/src/bin/src/proc/pc.c +++ b/src/bin/src/proc/pc.c @@ -643,6 +643,7 @@ enum nw_state bin_proc_pc(const long offset, struct nw_next *const next, return NW_AGAIN; } else + { for (size_t i = 0; i < d->n_bkpt; i++) if (offset == d->bkpt[i]) { @@ -653,5 +654,23 @@ enum nw_state bin_proc_pc(const long offset, struct nw_next *const next, return NW_AGAIN; } + const struct fs_read r = + { + .fd = &d->fd, + .buf = &d->b, + .n = sizeof d->b + }; + + const int n = aio_read_nb(&r); + + if (n < 0) + { + kprintf("%s: aio_read_nb failed\n", __func__); + return NW_FATAL; + } + else if (n && d->b == -1) + d->running = false; + } + return NW_OK; } diff --git a/src/bin/src/update.c b/src/bin/src/update.c index 3f03ae7..f5ce1c6 100644 --- a/src/bin/src/update.c +++ b/src/bin/src/update.c @@ -35,10 +35,10 @@ static int load(void) switch (nw_load(&m->mod, &m->mod_out)) { - case STATE_AGAIN: + case NW_AGAIN: break; - case STATE_FATAL: + case NW_FATAL: /* TODO: call nw_loadexc. */ kprintf("Module %s failed to load\n", m->path); @@ -48,7 +48,7 @@ static int load(void) bin_mod_free(m); break; - case STATE_OK: + case NW_OK: kprintf("Module %s loaded successfully\n", m->path); if (bin_start(m)) diff --git a/src/bin/src/wnix/CMakeLists.txt b/src/bin/src/wnix/CMakeLists.txt index b4868f2..5a92246 100644 --- a/src/bin/src/wnix/CMakeLists.txt +++ b/src/bin/src/wnix/CMakeLists.txt @@ -18,8 +18,13 @@ target_sources(bin PRIVATE argc.c argcopy.c arglen.c + close.c exit.c mkdir.c mount.c - set_errno.c + open.c + umask.c + write.c ) + +add_subdirectory(routines) diff --git a/src/bin/src/wnix/argcopy.c b/src/bin/src/wnix/argcopy.c index cedb26e..f712016 100644 --- a/src/bin/src/wnix/argcopy.c +++ b/src/bin/src/wnix/argcopy.c @@ -21,7 +21,6 @@ #include <bin/wnix/routines.h> #include <bin/proc.h> #include <bin/types.h> -#include <endian.h> #include <nanowasm/nw.h> #include <nanowasm/linear.h> #include <errno.h> @@ -66,7 +65,14 @@ static int set_errno(struct bin_proc *const p, const int error, .user = p }; - return bin_wnix_set_errno(p, error, c->errno_addr, next); + const struct bin_set_errno cfg = + { + .addr = c->errno_addr, + .error = error, + .p = p + }; + + return bin_wnix_set_errno(&cfg, next); } static enum nw_state read_arg(void *const user, struct nw_next *const next) @@ -112,6 +118,8 @@ static enum nw_state check(void *const user, struct nw_next *const next) else *next = (const struct nw_next){.fn = read_arg, .user = user}; + return NW_AGAIN; + failure: free_argcopy(c); return NW_FATAL; @@ -137,7 +145,7 @@ enum nw_state bin_wnix_argcopy(const union nw_value *const params, struct bin_proc *const p = user; - *next = (const struct nw_next){.fn = check, .user = user}; p->import = c; + *next = (const struct nw_next){.fn = check, .user = user}; return NW_AGAIN; } diff --git a/src/bin/src/wnix/close.c b/src/bin/src/wnix/close.c new file mode 100644 index 0000000..7e1754f --- /dev/null +++ b/src/bin/src/wnix/close.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <nanowasm/nw.h> + +enum nw_state bin_wnix_close(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + /* TODO */ + ret->i32 = -1; + return NW_OK; +} diff --git a/src/bin/src/wnix/mkdir.c b/src/bin/src/wnix/mkdir.c index 30a274a..8b26a2c 100644 --- a/src/bin/src/wnix/mkdir.c +++ b/src/bin/src/wnix/mkdir.c @@ -21,18 +21,215 @@ #include <bin/wnix/routines.h> #include <bin/proc.h> #include <bin/types.h> -#include <endian.h> +#include <aio.h> +#include <fs/fs.h> +#include <loop.h> #include <nanowasm/nw.h> #include <nanowasm/linear.h> #include <errno.h> #include <stdlib.h> -#include <string.h> + +struct mkdir +{ + char *path; + unsigned long path_addr, errno_addr, mode; + union nw_value *ret; + struct aio *aio; + int error; +}; + +static void free_mkdir(struct mkdir *const m) +{ + if (!m) + return; + + aio_free(m->aio); + free(m->path); + free(m); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + + m->ret->i32 = -1; + free_mkdir(m); + return NW_OK; +} + +static int set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct mkdir *const m = p->import; + + if (m->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = m->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_mkdir(m); + return -1; + } + } + else + return fail(p, next); + + return 0; +} + +static enum nw_state check_mkdir(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + enum nw_state ret = NW_OK; + + if (m->error) + { + if (set_errno(p, m->error, next)) + ret = NW_FATAL; + else + return NW_AGAIN; + } + + free_mkdir(m); + return ret; +} + +static int mkdir_done(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct mkdir *const m = p->import; + + m->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(m->aio)) + { + free_mkdir(m); + return -1; + } + + aio_free(m->aio); + m->aio = NULL; + return 0; +} + +static enum nw_state do_mkdir(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + const struct aio_done d = + { + .f = mkdir_done, + .args = p + }; + + const struct fs_mkdir mk = + { + .mode = m->mode, + .path = m->path, + .uid = p->uid, + .gid = p->gid + }; + + struct aio *const aio = aio_mkdir(&mk, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_mkdir(m); + return NW_FATAL; + } + + m->aio = aio; + *next = (const struct nw_next){.fn = check_mkdir, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_path(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + const struct bin_readstr cfg = + { + .error = set_errno, + .addr = m->path_addr, + .str = &m->path, + .p = p + }; + + *next = (const struct nw_next){.fn = do_mkdir, .user = user}; + + if (bin_wnix_readstr(&cfg, next)) + { + free_mkdir(m); + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct mkdir *const m = p->import; + + if (!m->path_addr || !m->errno_addr) + { + if (m->errno_addr) + { + if (set_errno(p, EINVAL, next)) + goto failure; + } + else + { + m->ret->i32 = -1; + return NW_OK; + } + } + else + *next = (const struct nw_next){.fn = read_path, .user = user}; + + return NW_AGAIN; + +failure: + free_mkdir(m); + return NW_FATAL; +} enum nw_state bin_wnix_mkdir(const union nw_value *const params, union nw_value *const ret, void *const user, struct nw_next *const next) { - /* TODO */ - ret->i32 = -1; - return NW_OK; + enum {PATH, MODE, ERRNO}; + struct mkdir *const m = malloc(sizeof *m); + + if (!m) + return NW_FATAL; + + *m = (const struct mkdir) + { + .path_addr = params[PATH].i32, + .mode = params[MODE].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = m; + return NW_AGAIN; } diff --git a/src/bin/src/wnix/mount.c b/src/bin/src/wnix/mount.c index 562f7d9..f3acea6 100644 --- a/src/bin/src/wnix/mount.c +++ b/src/bin/src/wnix/mount.c @@ -78,7 +78,14 @@ static int set_errno(struct bin_proc *const p, const int error, .user = p }; - return bin_wnix_set_errno(p, error, m->errno_addr, next); + const struct bin_set_errno cfg = + { + .addr = m->errno_addr, + .error = error, + .p = p + }; + + return bin_wnix_set_errno(&cfg, next); } static enum nw_state mounted(void *const user, struct nw_next *const next) @@ -160,55 +167,23 @@ failure: return NW_FATAL; } -static enum nw_state read_str(struct bin_proc *const p, - struct mountstr *const ms) +static enum nw_state read_string(struct bin_proc *const p, + struct mountstr *const ms, struct nw_next *const next) { - char b; struct mount *const m = p->import; - struct nw_sm_io io = {.buf = &b, .n = sizeof b}; - const enum nw_state n = nw_linear_load(&p->instance, &io, ms->addr + m->i); - - if (n) - return n; - - char *const s = realloc(ms->str, m->i + 1); - if (!s) - return NW_FATAL; - - s[m->i++] = b; - ms->str = s; - return b ? NW_AGAIN : NW_OK; -} - -static enum nw_state check_read_str(struct bin_proc *const p, - struct mountstr *const ms, const struct nw_next *const innext, - struct nw_next *const outnext) -{ - struct mount *const m = p->import; - enum {PATHMAX = 128}; - - if (m->i >= PATHMAX) + const struct bin_readstr cfg = { - free_mount(m); - /* TODO: assign errno. */ - m->ret->i32 = -1; - return NW_OK; - } + .error = set_errno, + .addr = ms->addr, + .str = &ms->str, + .p = p + }; - switch (read_str(p, ms)) + if (bin_wnix_readstr(&cfg, next)) { - case NW_FATAL: - free_mount(m); - return NW_FATAL; - - case NW_OK: - m->i = 0; - *outnext = *innext; - break; - - case NW_AGAIN: - break; + free_mount(m); + return NW_FATAL; } return NW_AGAIN; @@ -218,27 +193,27 @@ static enum nw_state read_type(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = do_mount, .user = p}; - return check_read_str(p, &m->type, &rnext, next); + *next = (const struct nw_next){.fn = do_mount, .user = user}; + return read_string(p, &m->type, next); } static enum nw_state read_tgt(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = read_type, .user = p}; - return check_read_str(p, &m->tgt, &rnext, next); + *next = (const struct nw_next){.fn = read_type, .user = user}; + return read_string(p, &m->tgt, next); } static enum nw_state read_src(void *const user, struct nw_next *const next) { struct bin_proc *const p = user; struct mount *const m = p->import; - const struct nw_next rnext = {.fn = read_tgt, .user = p}; - return check_read_str(p, &m->src, &rnext, next); + *next = (const struct nw_next){.fn = read_tgt, .user = user}; + return read_string(p, &m->src, next); } static enum nw_state check(void *const user, struct nw_next *const next) diff --git a/src/bin/src/wnix/open.c b/src/bin/src/wnix/open.c new file mode 100644 index 0000000..fdab80a --- /dev/null +++ b/src/bin/src/wnix/open.c @@ -0,0 +1,222 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/wnix/routines.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <aio.h> +#include <loop.h> +#include <nanowasm/nw.h> +#include <errno.h> +#include <stdlib.h> + +struct open +{ + char *path; + struct fs_fd fd; + unsigned long path_addr, errno_addr; + long flags, mode; + union nw_value *ret; + struct aio *aio; + int error; +}; + +static void free_open(struct open *const op) +{ + if (!op) + return; + + aio_free(op->aio); + free(op->path); + free(op); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + + op->ret->i32 = -1; + free_open(op); + return NW_OK; +} + +static int set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct open *const op = p->import; + + if (op->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = op->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_open(op); + return -1; + } + } + else + return fail(p, next); + + return 0; +} + +static enum nw_state check_open(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + int fd; + + if (op->error) + return set_errno(p, op->error, next); + else if ((fd = bin_proc_fd_alloc(p, &op->fd, op->flags)) < 0) + return set_errno(p, errno, next); + + op->ret->i32 = fd; + free_open(op); + return NW_OK; +} + +static int opened(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct open *const op = p->import; + + op->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(op->aio)) + { + free_open(op); + return -1; + } + + aio_free(op->aio); + op->aio = NULL; + return 0; +} + +static enum nw_state do_open(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + const struct aio_done d = + { + .f = opened, + .args = p + }; + + const struct fs_open fo = + { + .flags = op->flags, + .mode = op->mode, + .path = op->path, + .fd = &op->fd, + .uid = p->uid, + .gid = p->gid, + .ss = p->ss + }; + + struct aio *const aio = aio_open(&fo, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_open(op); + return NW_FATAL; + } + + op->aio = aio; + *next = (const struct nw_next){.fn = check_open, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_path(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + const struct bin_readstr cfg = + { + .error = set_errno, + .addr = op->path_addr, + .str = &op->path, + .p = p + }; + + *next = (const struct nw_next){.fn = do_open, .user = user}; + + if (bin_wnix_readstr(&cfg, next)) + { + free_open(op); + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct open *const op = p->import; + + if (!op->path_addr || !op->errno_addr) + return set_errno(p, EINVAL, next); + else + *next = (const struct nw_next){.fn = read_path, .user = user}; + + return NW_AGAIN; +} + +enum nw_state bin_wnix_open(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + enum {PATH, FLAGS, MODE, ERRNO}; + struct open *const op = malloc(sizeof *op); + + if (!op) + return NW_FATAL; + + *op = (const struct open) + { + .path_addr = params[PATH].i32, + .flags = params[FLAGS].i32, + .mode = params[MODE].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = op; + return NW_AGAIN; +} diff --git a/src/bin/src/wnix/routines/CMakeLists.txt b/src/bin/src/wnix/routines/CMakeLists.txt new file mode 100644 index 0000000..784acf4 --- /dev/null +++ b/src/bin/src/wnix/routines/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(bin PRIVATE + readstr.c + set_errno.c +) diff --git a/src/bin/src/wnix/routines/readstr.c b/src/bin/src/wnix/routines/readstr.c new file mode 100644 index 0000000..06f6fde --- /dev/null +++ b/src/bin/src/wnix/routines/readstr.c @@ -0,0 +1,129 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/types.h> +#include <bin/wnix/routines.h> +#include <endian.h> +#include <nanowasm/nw.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct readstr +{ + char *s; + size_t i; + struct bin_readstr cfg; + struct nw_next next; +}; + +static void free_readstr(struct readstr *const rs) +{ + if (!rs) + return ; + + free(rs->s); + free(rs); +} + +static enum nw_state read_str(struct readstr *const rs) +{ + char b; + struct nw_sm_io io = {.buf = &b, .n = sizeof b}; + const struct bin_readstr *const cfg = &rs->cfg; + struct bin_proc *const p = cfg->p; + const enum nw_state n = nw_linear_load(&p->instance, &io, cfg->addr + rs->i); + + if (n) + return n; + + char *const s = realloc(rs->s, rs->i + 1); + + if (!s) + return NW_FATAL; + + s[rs->i++] = b; + rs->s = s; + return b ? NW_AGAIN : NW_OK; +} + +static enum nw_state check_read_str(void *const user, + struct nw_next *const next) +{ + struct readstr *const rs = user; + const struct bin_readstr *const cfg = &rs->cfg; + struct bin_proc *const p = cfg->p; + enum nw_state ret = NW_AGAIN; + enum {PATHMAX = 128}; + + if (rs->i >= PATHMAX) + { + if (rs->cfg.error(p, ENAMETOOLONG, next)) + ret = NW_FATAL; + } + else + { + switch (read_str(rs)) + { + case NW_FATAL: + if (rs->cfg.error(p, errno, next)) + ret = NW_FATAL; + + break; + + case NW_OK: + { + char *const s = strdup(rs->s); + + if (!s) + ret = NW_FATAL; + else + { + *cfg->str = s; + *next = rs->next; + } + } + break; + + case NW_AGAIN: + return NW_AGAIN; + } + } + + free_readstr(rs); + return ret; +} + +int bin_wnix_readstr(const struct bin_readstr *const cfg, + struct nw_next *const next) +{ + struct readstr *const rs = malloc(sizeof *rs); + + if (!rs) + return -1; + + *rs = (const struct readstr) + { + .cfg = *cfg, + .next = *next + }; + + *next = (const struct nw_next){.fn = check_read_str, .user = rs}; + return 0; +} diff --git a/src/bin/src/wnix/set_errno.c b/src/bin/src/wnix/routines/set_errno.c index cc04420..0db98af 100644 --- a/src/bin/src/wnix/set_errno.c +++ b/src/bin/src/wnix/routines/set_errno.c @@ -25,30 +25,38 @@ struct set_errno { - struct bin_proc *p; - struct nw_next next; - struct nw_sm_io io; struct endian_le32 error; - unsigned long addr; + struct nw_sm_io io; + struct bin_set_errno cfg; + struct nw_next next; }; -static enum nw_state store_errno(void *const user, - struct nw_next *const next) +static enum nw_state store_errno(void *const user, struct nw_next *const next) { struct set_errno *const e = user; - struct bin_proc *const p = e->p; - const enum nw_state n = nw_linear_store(&p->instance, &e->io, e->addr); + const struct bin_set_errno *const cfg = &e->cfg; + struct bin_proc *const p = cfg->p; + const enum nw_state n = nw_linear_store(&p->instance, &e->io, cfg->addr); + + switch (n) + { + case NW_FATAL: + free(e); + /* Fall through.*/ + case NW_AGAIN: + return n; - if (n) - return n; + case NW_OK: + *next = e->next; + free(e); + break; + } - *next = e->next; - free(e); return NW_AGAIN; } -int bin_wnix_set_errno(struct bin_proc *const p, const int error, - const unsigned long addr, struct nw_next *const next) +int bin_wnix_set_errno(const struct bin_set_errno *const cfg, + struct nw_next *const next) { struct set_errno *const e = malloc(sizeof *e); @@ -57,10 +65,9 @@ int bin_wnix_set_errno(struct bin_proc *const p, const int error, *e = (const struct set_errno) { - .error = endian_to_le32(error), + .error = endian_to_le32(cfg->error), + .cfg = *cfg, .next = *next, - .addr = addr, - .p = p, .io = { .buf = &e->error, diff --git a/src/bin/src/wnix/umask.c b/src/bin/src/wnix/umask.c new file mode 100644 index 0000000..f4fd701 --- /dev/null +++ b/src/bin/src/wnix/umask.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/types.h> +#include <nanowasm/nw.h> + +enum nw_state bin_wnix_umask(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + struct bin_proc *const p = user; + const mode_t mask = params->i32, oldmask = p->umask; + + p->umask = mask; + ret->i32 = oldmask; + return NW_OK; +} diff --git a/src/bin/src/wnix/write.c b/src/bin/src/wnix/write.c new file mode 100644 index 0000000..3a4b8d5 --- /dev/null +++ b/src/bin/src/wnix/write.c @@ -0,0 +1,251 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <bin.h> +#include <bin/wnix.h> +#include <bin/proc.h> +#include <bin/routines.h> +#include <bin/types.h> +#include <bin/wnix/routines.h> +#include <aio.h> +#include <fs/fs.h> +#include <loop.h> +#include <nanowasm/nw.h> +#include <nanowasm/linear.h> +#include <errno.h> +#include <stdlib.h> + +struct write +{ + long fd; + int error; + unsigned long addr, count, errno_addr; + char buf[64]; + size_t i; + union nw_value *ret; + struct aio *aio; + struct bin_proc_fd *pfd; + struct nw_sm_io io; +}; + +static enum nw_state read_chunk(void *, struct nw_next *); + +static void free_write(struct write *const w) +{ + if (!w) + return; + + aio_free(w->aio); + free(w); +} + +static enum nw_state fail(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + w->ret->i32 = -1; + free_write(w); + return NW_OK; +} + +static enum nw_state set_errno(struct bin_proc *const p, const int error, + struct nw_next *const next) +{ + struct write *const w = p->import; + + if (w->errno_addr) + { + const struct bin_set_errno cfg = + { + .addr = w->errno_addr, + .error = error, + .p = p + }; + + *next = (const struct nw_next) + { + .fn = fail, + .user = p + }; + + if (bin_wnix_set_errno(&cfg, next)) + { + free_write(w); + return NW_FATAL; + } + } + else + return fail(p, next); + + return NW_AGAIN; +} + +static void setup_buf(struct bin_proc *const p, struct nw_next *const next) +{ + struct write *const w = p->import; + const size_t rem = w->count - w->i, + n = rem > sizeof w->buf ? sizeof w->buf : rem; + + w->io = (const struct nw_sm_io) + { + .buf = w->buf, + .n = n + }; + + *next = (const struct nw_next){.fn = read_chunk, .user = p}; +} + +static enum nw_state check_write(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + if (w->error) + { + if (set_errno(p, w->error, next)) + { + free_write(w); + return NW_FATAL; + } + } + else if ((w->i += w->io.n) >= w->count) + { + w->ret->i32 = w->count; + free_write(w); + return NW_OK; + } + else + setup_buf(p, next); + + return NW_AGAIN; +} + +static int write_done(const enum state state, void *const args) +{ + struct bin_proc *const p = args; + struct write *const w = p->import; + + w->error = state ? errno : 0; + + if (bin_proc_unlock(p) || loop_rm_aio(w->aio)) + { + free_write(w); + return -1; + } + + aio_free(w->aio); + w->aio = NULL; + return 0; +} + +static enum nw_state do_write(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + const struct aio_done d = + { + .f = write_done, + .args = user + }; + + const struct fs_write fw = + { + .fd = &w->pfd->fs_fd, + .buf = w->buf, + .n = w->io.n + }; + + struct aio *const aio = aio_write(&fw, &d); + + if (!aio || bin_proc_lock(p) || loop_add_aio(aio)) + { + aio_free(aio); + free_write(w); + return NW_FATAL; + } + + w->aio = aio; + *next = (const struct nw_next){.fn = check_write, .user = user}; + return NW_AGAIN; +} + +static enum nw_state read_chunk(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + const unsigned long offset = w->addr + w->i; + const enum nw_state n = nw_linear_load(&p->instance, &w->io, offset); + + switch (n) + { + case NW_OK: + *next = (const struct nw_next){.fn = do_write, .user = user}; + break; + + case NW_FATAL: + free_write(w); + return n; + + case NW_AGAIN: + break; + } + + return NW_AGAIN; +} + +static enum nw_state check(void *const user, struct nw_next *const next) +{ + struct bin_proc *const p = user; + struct write *const w = p->import; + + if (!(w->pfd = bin_proc_fd_find(p, w->fd))) + return set_errno(p, EBADF, next); + else if (!w->addr || !w->errno_addr) + return set_errno(p, EINVAL, next); + else + setup_buf(p, next); + + return NW_AGAIN; +} + +enum nw_state bin_wnix_write(const union nw_value *const params, + union nw_value *const ret, void *const user, + struct nw_next *const next) +{ + enum {FD, ADDR, COUNT, ERRNO}; + struct write *const w = malloc(sizeof *w); + + if (!w) + return NW_FATAL; + + *w = (const struct write) + { + .fd = params[FD].i32, + .addr = params[ADDR].i32, + .count = params[COUNT].i32, + .errno_addr = params[ERRNO].i32, + .ret = ret + }; + + struct bin_proc *const p = user; + + *next = (const struct nw_next){.fn = check, .user = user}; + p->import = w; + return NW_AGAIN; +} diff --git a/src/drv/CMakeLists.txt b/src/drv/CMakeLists.txt index 3306d93..aa21753 100644 --- a/src/drv/CMakeLists.txt +++ b/src/drv/CMakeLists.txt @@ -16,10 +16,20 @@ add_library(drv) add_subdirectory(event) -add_subdirectory(tty) +add_subdirectory(null) add_subdirectory(src) +add_subdirectory(stderr) +add_subdirectory(stdin) +add_subdirectory(stdout) +add_subdirectory(tty) target_include_directories(drv PUBLIC include PRIVATE private_include) -target_link_libraries(drv PUBLIC drv_event PRIVATE drv_tty) +target_link_libraries(drv PUBLIC drv_event PRIVATE + drv_null + drv_stderr + drv_stdin + drv_stdout + drv_tty +) if(PS1_BUILD) add_subdirectory(ps1) diff --git a/src/drv/event/include/drv/event.h b/src/drv/event/include/drv/event.h index 5707242..acd3659 100644 --- a/src/drv/event/include/drv/event.h +++ b/src/drv/event/include/drv/event.h @@ -23,6 +23,8 @@ #include <stdbool.h> #include <stddef.h> +struct drv_port; + struct drv_event_done { int (*f)(int error, void *args); @@ -31,12 +33,12 @@ struct drv_event_done struct drv_event_ops { - int (*read)(void *buf, size_t n, off_t offset, - const struct drv_event_done *done, void *args); - int (*read_nb)(void *buf, size_t n, void *args); - int (*write)(const void *buf, size_t n, const struct drv_event_done *done, - void *args); - void *args; + int (*read)(struct drv_port *p, void *buf, size_t n, off_t offset, + const struct drv_event_done *done); + int (*read_nb)(struct drv_port *p, void *buf, size_t n); + int (*write)(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); + struct drv_port *p; }; struct drv_event diff --git a/src/drv/null/CMakeLists.txt b/src/drv/null/CMakeLists.txt new file mode 100644 index 0000000..ab09fc2 --- /dev/null +++ b/src/drv/null/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(drv_null) +add_subdirectory(src) +target_include_directories(drv_null PUBLIC include PRIVATE private_include) +target_link_libraries(drv_null PUBLIC c PRIVATE drv_event) diff --git a/src/drv/null/include/drv/null.h b/src/drv/null/include/drv/null.h new file mode 100644 index 0000000..52ff26f --- /dev/null +++ b/src/drv/null/include/drv/null.h @@ -0,0 +1,28 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_NULL_H +#define DRV_NULL_H + +#include <drv/event.h> + +struct drv_port *drv_null_init(const struct drv_event *ev); +int drv_null_update(struct drv_port *p); +void drv_null_free(struct drv_port *p); + +#endif diff --git a/src/drv/null/private_include/drv/null/ops.h b/src/drv/null/private_include/drv/null/ops.h new file mode 100644 index 0000000..cf9d8f2 --- /dev/null +++ b/src/drv/null/private_include/drv/null/ops.h @@ -0,0 +1,31 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_NULL_OPS_H +#define DRV_NULL_OPS_H + +#include <drv/event.h> +#include <drv/null/types.h> +#include <stddef.h> + +int drv_null_read(struct drv_port *p, void *buf, size_t n, off_t offset, + const struct drv_event_done *done); +int drv_null_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); + +#endif diff --git a/src/drv/null/private_include/drv/null/types.h b/src/drv/null/private_include/drv/null/types.h new file mode 100644 index 0000000..1c9266d --- /dev/null +++ b/src/drv/null/private_include/drv/null/types.h @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_NULL_TYPES_H +#define DRV_NULL_TYPES_H + +#include <drv/event.h> +#include <stdbool.h> +#include <stddef.h> + +struct drv_port +{ + bool init; + struct drv_event ev; +}; + +#endif diff --git a/src/drv/null/src/CMakeLists.txt b/src/drv/null/src/CMakeLists.txt new file mode 100644 index 0000000..899cada --- /dev/null +++ b/src/drv/null/src/CMakeLists.txt @@ -0,0 +1,23 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(drv_null PRIVATE + free.c + init.c + read.c + update.c + write.c +) diff --git a/src/drv/null/src/free.c b/src/drv/null/src/free.c new file mode 100644 index 0000000..1251580 --- /dev/null +++ b/src/drv/null/src/free.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/null.h> +#include <drv/null/ops.h> +#include <drv/null/types.h> +#include <stddef.h> +#include <stdlib.h> + +void drv_null_free(struct drv_port *const p) +{ + if (!p) + return; + + free(p); +} diff --git a/src/drv/null/src/init.c b/src/drv/null/src/init.c new file mode 100644 index 0000000..4ed6c39 --- /dev/null +++ b/src/drv/null/src/init.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/null.h> +#include <drv/null/ops.h> +#include <drv/null/types.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_port *drv_null_init(const struct drv_event *const ev) +{ + struct drv_port *ret = NULL; + + if (!(ret = malloc(sizeof *ret))) + return NULL; + + *ret = (const struct drv_port){.ev = *ev}; + return ret; +} diff --git a/src/drv/null/src/read.c b/src/drv/null/src/read.c new file mode 100644 index 0000000..7dd9820 --- /dev/null +++ b/src/drv/null/src/read.c @@ -0,0 +1,30 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/null.h> +#include <drv/null/types.h> +#include <drv/null/ops.h> +#include <stddef.h> + +int drv_null_read(struct drv_port *const p, void *const buf, const size_t n, + const off_t offset, const struct drv_event_done *const d) +{ + /* TODO: how to set EOF? */ + return 0; +} diff --git a/src/drv/null/src/update.c b/src/drv/null/src/update.c new file mode 100644 index 0000000..cc0b456 --- /dev/null +++ b/src/drv/null/src/update.c @@ -0,0 +1,48 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/null.h> +#include <drv/null/ops.h> +#include <drv/null/types.h> +#include <stdbool.h> + +static int init(struct drv_port *const p) +{ + if (p->init) + return 0; + + const struct drv_event *const ev = &p->ev; + const struct drv_event_ops ops = + { + .read = drv_null_read, + .write = drv_null_write, + .p = p + }; + + if (ev->status("null", &ops, true, 0666, ev->args)) + return -1; + + p->init = true; + return 0; +} + +int drv_null_update(struct drv_port *const p) +{ + return init(p); +} diff --git a/src/drv/null/src/write.c b/src/drv/null/src/write.c new file mode 100644 index 0000000..9ca8b53 --- /dev/null +++ b/src/drv/null/src/write.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/null.h> +#include <drv/null/types.h> +#include <drv/null/ops.h> +#include <stddef.h> + +int drv_null_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const d) +{ + return n; +} diff --git a/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h b/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h index 0b756de..9870445 100644 --- a/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h +++ b/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h @@ -27,10 +27,10 @@ int drv_ps1_cd_send(const struct cmd *cmd); int drv_ps1_cd_getstat(void); -int drv_ps1_cd_read(void *buf, size_t n, off_t offset, - const struct drv_event_done *done, void *args); -int drv_ps1_cd_write(const void *buf, size_t n, - const struct drv_event_done *done, void *args); +int drv_ps1_cd_read(struct drv_port *p, void *buf, size_t n, off_t offset, + const struct drv_event_done *done); +int drv_ps1_cd_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); int drv_ps1_cd_next(void); struct CdAsyncSeekL drv_ps1_cd_toseekl(unsigned i); diff --git a/src/drv/ps1/cd/src/read.c b/src/drv/ps1/cd/src/read.c index d21cbae..f8db3a0 100644 --- a/src/drv/ps1/cd/src/read.c +++ b/src/drv/ps1/cd/src/read.c @@ -208,8 +208,8 @@ static int start(void) return cached_read(p); } -int drv_ps1_cd_read(void *const buf, const size_t n, const off_t offset, - const struct drv_event_done *const done, void *const args) +int drv_ps1_cd_read(struct drv_port *const pt, void *const buf, const size_t n, + const off_t offset, const struct drv_event_done *const done) { struct cd_prv *const p = &drv_ps1_cd_prv; struct cd_req *const r = malloc(sizeof *r); diff --git a/src/drv/ps1/cd/src/write.c b/src/drv/ps1/cd/src/write.c index ba47993..a314862 100644 --- a/src/drv/ps1/cd/src/write.c +++ b/src/drv/ps1/cd/src/write.c @@ -22,8 +22,8 @@ #include <drv/event.h> #include <errno.h> -int drv_ps1_cd_write(const void *const buf, const size_t n, - const struct drv_event_done *const done, void *const args) +int drv_ps1_cd_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const done) { /* TODO: write event callback returning EROFS */ errno = EROFS; diff --git a/src/drv/ps1/sio/private_include/drv/ps1/sio/ops.h b/src/drv/ps1/sio/private_include/drv/ps1/sio/ops.h index a481fa9..a7e19dd 100644 --- a/src/drv/ps1/sio/private_include/drv/ps1/sio/ops.h +++ b/src/drv/ps1/sio/private_include/drv/ps1/sio/ops.h @@ -22,10 +22,10 @@ #include <drv/event.h> #include <sys/types.h> -int drv_ps1_sio_read(void *buf, size_t n, off_t offset, - const struct drv_event_done *done, void *args); -int drv_ps1_sio_read_nb(void *buf, size_t n, void *args); -int drv_ps1_sio_write(const void *buf, size_t n, - const struct drv_event_done *done, void *args); +int drv_ps1_sio_read(struct drv_port *p, void *buf, size_t n, off_t offset, + const struct drv_event_done *done); +int drv_ps1_sio_read_nb(struct drv_port *p, void *buf, size_t n); +int drv_ps1_sio_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); #endif diff --git a/src/drv/ps1/sio/src/read.c b/src/drv/ps1/sio/src/read.c index e13ad29..63bd29f 100644 --- a/src/drv/ps1/sio/src/read.c +++ b/src/drv/ps1/sio/src/read.c @@ -94,8 +94,8 @@ static int load(void) return 0; } -int drv_ps1_sio_read(void *const buf, const size_t n, const off_t offset, - const struct drv_event_done *const done, void *const args) +int drv_ps1_sio_read(struct drv_port *const p, void *const buf, const size_t n, + const off_t offset, const struct drv_event_done *const done) { struct drv_ps1_sio *const s = &drv_ps1_sio; struct sio_fifo *const f = &s->rx; diff --git a/src/drv/ps1/sio/src/read_nb.c b/src/drv/ps1/sio/src/read_nb.c index 755c5cf..9b9c3bf 100644 --- a/src/drv/ps1/sio/src/read_nb.c +++ b/src/drv/ps1/sio/src/read_nb.c @@ -55,7 +55,8 @@ static int read_fifo(char *buf, size_t n) return ret; } -int drv_ps1_sio_read_nb(void *const buf, const size_t n, void *const args) +int drv_ps1_sio_read_nb(struct drv_port *const p, void *const buf, + const size_t n) { return read_fifo(buf, n); } diff --git a/src/drv/ps1/sio/src/write.c b/src/drv/ps1/sio/src/write.c index 942456f..4f5a702 100644 --- a/src/drv/ps1/sio/src/write.c +++ b/src/drv/ps1/sio/src/write.c @@ -86,8 +86,8 @@ static int store(void) return 0; } -int drv_ps1_sio_write(const void *const buf, const size_t n, - const struct drv_event_done *const done, void *const args) +int drv_ps1_sio_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const done) { struct drv_ps1_sio *const s = &drv_ps1_sio; struct sio_fifo *const f = &s->tx; diff --git a/src/drv/src/tree.c b/src/drv/src/tree.c index d52df3d..087c38b 100644 --- a/src/drv/src/tree.c +++ b/src/drv/src/tree.c @@ -18,6 +18,10 @@ #include <drv/drv.h> #include <drv/tree.h> +#include <drv/null.h> +#include <drv/stderr.h> +#include <drv/stdin.h> +#include <drv/stdout.h> #include <drv/tty.h> #include <drv/port.h> #include <stddef.h> @@ -27,7 +31,11 @@ struct drv_port *(*const drv_tree_init[])(const struct drv_event *) = #if DRV_PS1 drv_ps1_init, #endif - drv_tty_init + drv_tty_init, + drv_null_init, + drv_stderr_init, + drv_stdin_init, + drv_stdout_init }; enum {N = sizeof drv_tree_init / sizeof *drv_tree_init}; @@ -37,7 +45,11 @@ int (*const drv_tree_update[N])(struct drv_port *) = #if DRV_PS1 drv_ps1_update, #endif - drv_tty_update + drv_tty_update, + drv_null_update, + drv_stderr_update, + drv_stdin_update, + drv_stdout_update }; void (*const drv_tree_free[N])(struct drv_port *) = @@ -45,7 +57,11 @@ void (*const drv_tree_free[N])(struct drv_port *) = #if DRV_PS1 drv_ps1_free, #endif - drv_tty_free + drv_tty_free, + drv_null_free, + drv_stderr_free, + drv_stdin_free, + drv_stdout_free }; const size_t drv_tree_n = N; diff --git a/src/drv/stderr/CMakeLists.txt b/src/drv/stderr/CMakeLists.txt new file mode 100644 index 0000000..cb9a07a --- /dev/null +++ b/src/drv/stderr/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(drv_stderr) +add_subdirectory(src) +target_include_directories(drv_stderr PUBLIC include PRIVATE private_include) +target_link_libraries(drv_stderr PUBLIC c PRIVATE drv_event) diff --git a/src/drv/stderr/include/drv/stderr.h b/src/drv/stderr/include/drv/stderr.h new file mode 100644 index 0000000..eceff6a --- /dev/null +++ b/src/drv/stderr/include/drv/stderr.h @@ -0,0 +1,28 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDERR_H +#define DRV_STDERR_H + +#include <drv/event.h> + +struct drv_port *drv_stderr_init(const struct drv_event *ev); +int drv_stderr_update(struct drv_port *p); +void drv_stderr_free(struct drv_port *p); + +#endif diff --git a/src/drv/stderr/private_include/drv/stderr/ops.h b/src/drv/stderr/private_include/drv/stderr/ops.h new file mode 100644 index 0000000..a265b15 --- /dev/null +++ b/src/drv/stderr/private_include/drv/stderr/ops.h @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDERR_OPS_H +#define DRV_STDERR_OPS_H + +#include <drv/event.h> +#include <drv/stderr/types.h> +#include <stddef.h> + +int drv_stderr_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); + +#endif diff --git a/src/drv/stderr/private_include/drv/stderr/types.h b/src/drv/stderr/private_include/drv/stderr/types.h new file mode 100644 index 0000000..196568c --- /dev/null +++ b/src/drv/stderr/private_include/drv/stderr/types.h @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDERR_TYPES_H +#define DRV_STDERR_TYPES_H + +#include <drv/event.h> +#include <stdbool.h> +#include <stddef.h> + +struct drv_port +{ + bool init; + struct drv_event ev; +}; + +#endif diff --git a/src/drv/stderr/src/CMakeLists.txt b/src/drv/stderr/src/CMakeLists.txt new file mode 100644 index 0000000..fd685c8 --- /dev/null +++ b/src/drv/stderr/src/CMakeLists.txt @@ -0,0 +1,22 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(drv_stderr PRIVATE + free.c + init.c + update.c + write.c +) diff --git a/src/drv/stderr/src/free.c b/src/drv/stderr/src/free.c new file mode 100644 index 0000000..1793540 --- /dev/null +++ b/src/drv/stderr/src/free.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stderr.h> +#include <drv/stderr/ops.h> +#include <drv/stderr/types.h> +#include <stddef.h> +#include <stdlib.h> + +void drv_stderr_free(struct drv_port *const p) +{ + if (!p) + return; + + free(p); +} diff --git a/src/drv/stderr/src/init.c b/src/drv/stderr/src/init.c new file mode 100644 index 0000000..95797d7 --- /dev/null +++ b/src/drv/stderr/src/init.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stderr.h> +#include <drv/stderr/ops.h> +#include <drv/stderr/types.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_port *drv_stderr_init(const struct drv_event *const ev) +{ + struct drv_port *ret = NULL; + + if (!(ret = malloc(sizeof *ret))) + return NULL; + + *ret = (const struct drv_port){.ev = *ev}; + return ret; +} diff --git a/src/drv/stderr/src/update.c b/src/drv/stderr/src/update.c new file mode 100644 index 0000000..24e0cf4 --- /dev/null +++ b/src/drv/stderr/src/update.c @@ -0,0 +1,47 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stderr.h> +#include <drv/stderr/ops.h> +#include <drv/stderr/types.h> +#include <stdbool.h> + +static int init(struct drv_port *const p) +{ + if (p->init) + return 0; + + const struct drv_event *const ev = &p->ev; + const struct drv_event_ops ops = + { + .write = drv_stderr_write, + .p = p + }; + + if (ev->status("stderr", &ops, true, 0666, ev->args)) + return -1; + + p->init = true; + return 0; +} + +int drv_stderr_update(struct drv_port *const p) +{ + return init(p); +} diff --git a/src/drv/stderr/src/write.c b/src/drv/stderr/src/write.c new file mode 100644 index 0000000..2f5ccf8 --- /dev/null +++ b/src/drv/stderr/src/write.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stderr.h> +#include <drv/stderr/types.h> +#include <drv/stderr/ops.h> +#include <stddef.h> + +int drv_stderr_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const d) +{ + return n; +} diff --git a/src/drv/stdin/CMakeLists.txt b/src/drv/stdin/CMakeLists.txt new file mode 100644 index 0000000..8c788a0 --- /dev/null +++ b/src/drv/stdin/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(drv_stdin) +add_subdirectory(src) +target_include_directories(drv_stdin PUBLIC include PRIVATE private_include) +target_link_libraries(drv_stdin PUBLIC c PRIVATE drv_event) diff --git a/src/drv/stdin/include/drv/stdin.h b/src/drv/stdin/include/drv/stdin.h new file mode 100644 index 0000000..70ec64a --- /dev/null +++ b/src/drv/stdin/include/drv/stdin.h @@ -0,0 +1,28 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDIN_H +#define DRV_STDIN_H + +#include <drv/event.h> + +struct drv_port *drv_stdin_init(const struct drv_event *ev); +int drv_stdin_update(struct drv_port *p); +void drv_stdin_free(struct drv_port *p); + +#endif diff --git a/src/drv/stdin/private_include/drv/stdin/ops.h b/src/drv/stdin/private_include/drv/stdin/ops.h new file mode 100644 index 0000000..0514172 --- /dev/null +++ b/src/drv/stdin/private_include/drv/stdin/ops.h @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDIN_OPS_H +#define DRV_STDIN_OPS_H + +#include <drv/event.h> +#include <drv/stdin/types.h> +#include <stddef.h> + +int drv_stdin_read(struct drv_port *p, void *buf, size_t n, off_t offset, + const struct drv_event_done *done); + +#endif diff --git a/src/drv/stdin/private_include/drv/stdin/types.h b/src/drv/stdin/private_include/drv/stdin/types.h new file mode 100644 index 0000000..62d1c41 --- /dev/null +++ b/src/drv/stdin/private_include/drv/stdin/types.h @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDIN_TYPES_H +#define DRV_STDIN_TYPES_H + +#include <drv/event.h> +#include <stdbool.h> +#include <stddef.h> + +struct drv_port +{ + bool init; + struct drv_event ev; +}; + +#endif diff --git a/src/drv/stdin/src/CMakeLists.txt b/src/drv/stdin/src/CMakeLists.txt new file mode 100644 index 0000000..0da5cc6 --- /dev/null +++ b/src/drv/stdin/src/CMakeLists.txt @@ -0,0 +1,22 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(drv_stdin PRIVATE + free.c + init.c + read.c + update.c +) diff --git a/src/drv/stdin/src/free.c b/src/drv/stdin/src/free.c new file mode 100644 index 0000000..0f7d7af --- /dev/null +++ b/src/drv/stdin/src/free.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdin.h> +#include <drv/stdin/ops.h> +#include <drv/stdin/types.h> +#include <stddef.h> +#include <stdlib.h> + +void drv_stdin_free(struct drv_port *const p) +{ + if (!p) + return; + + free(p); +} diff --git a/src/drv/stdin/src/init.c b/src/drv/stdin/src/init.c new file mode 100644 index 0000000..c7a2e04 --- /dev/null +++ b/src/drv/stdin/src/init.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdin.h> +#include <drv/stdin/ops.h> +#include <drv/stdin/types.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_port *drv_stdin_init(const struct drv_event *const ev) +{ + struct drv_port *ret = NULL; + + if (!(ret = malloc(sizeof *ret))) + return NULL; + + *ret = (const struct drv_port){.ev = *ev}; + return ret; +} diff --git a/src/drv/stdin/src/read.c b/src/drv/stdin/src/read.c new file mode 100644 index 0000000..a26f7df --- /dev/null +++ b/src/drv/stdin/src/read.c @@ -0,0 +1,30 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdin.h> +#include <drv/stdin/types.h> +#include <drv/stdin/ops.h> +#include <stddef.h> + +int drv_stdin_read(struct drv_port *const p, void *const buf, const size_t n, + const off_t offset, const struct drv_event_done *const d) +{ + /* TODO: how to set EOF? */ + return 0; +} diff --git a/src/drv/stdin/src/update.c b/src/drv/stdin/src/update.c new file mode 100644 index 0000000..cbfebca --- /dev/null +++ b/src/drv/stdin/src/update.c @@ -0,0 +1,47 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdin.h> +#include <drv/stdin/ops.h> +#include <drv/stdin/types.h> +#include <stdbool.h> + +static int init(struct drv_port *const p) +{ + if (p->init) + return 0; + + const struct drv_event *const ev = &p->ev; + const struct drv_event_ops ops = + { + .read = drv_stdin_read, + .p = p + }; + + if (ev->status("stdin", &ops, true, 0666, ev->args)) + return -1; + + p->init = true; + return 0; +} + +int drv_stdin_update(struct drv_port *const p) +{ + return init(p); +} diff --git a/src/drv/stdout/CMakeLists.txt b/src/drv/stdout/CMakeLists.txt new file mode 100644 index 0000000..58f430a --- /dev/null +++ b/src/drv/stdout/CMakeLists.txt @@ -0,0 +1,20 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(drv_stdout) +add_subdirectory(src) +target_include_directories(drv_stdout PUBLIC include PRIVATE private_include) +target_link_libraries(drv_stdout PUBLIC c PRIVATE drv_event) diff --git a/src/drv/stdout/include/drv/stdout.h b/src/drv/stdout/include/drv/stdout.h new file mode 100644 index 0000000..0fac80c --- /dev/null +++ b/src/drv/stdout/include/drv/stdout.h @@ -0,0 +1,28 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDOUT_H +#define DRV_STDOUT_H + +#include <drv/event.h> + +struct drv_port *drv_stdout_init(const struct drv_event *ev); +int drv_stdout_update(struct drv_port *p); +void drv_stdout_free(struct drv_port *p); + +#endif diff --git a/src/drv/stdout/private_include/drv/stdout/ops.h b/src/drv/stdout/private_include/drv/stdout/ops.h new file mode 100644 index 0000000..af848e3 --- /dev/null +++ b/src/drv/stdout/private_include/drv/stdout/ops.h @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDOUT_OPS_H +#define DRV_STDOUT_OPS_H + +#include <drv/event.h> +#include <drv/stdout/types.h> +#include <stddef.h> + +int drv_stdout_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); + +#endif diff --git a/src/drv/stdout/private_include/drv/stdout/types.h b/src/drv/stdout/private_include/drv/stdout/types.h new file mode 100644 index 0000000..a39ce64 --- /dev/null +++ b/src/drv/stdout/private_include/drv/stdout/types.h @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_STDOUT_TYPES_H +#define DRV_STDOUT_TYPES_H + +#include <drv/event.h> +#include <stdbool.h> +#include <stddef.h> + +struct drv_port +{ + bool init; + struct drv_event ev; +}; + +#endif diff --git a/src/drv/stdout/src/CMakeLists.txt b/src/drv/stdout/src/CMakeLists.txt new file mode 100644 index 0000000..490886d --- /dev/null +++ b/src/drv/stdout/src/CMakeLists.txt @@ -0,0 +1,22 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(drv_stdout PRIVATE + free.c + init.c + update.c + write.c +) diff --git a/src/drv/stdout/src/free.c b/src/drv/stdout/src/free.c new file mode 100644 index 0000000..4b7d733 --- /dev/null +++ b/src/drv/stdout/src/free.c @@ -0,0 +1,32 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdout.h> +#include <drv/stdout/ops.h> +#include <drv/stdout/types.h> +#include <stddef.h> +#include <stdlib.h> + +void drv_stdout_free(struct drv_port *const p) +{ + if (!p) + return; + + free(p); +} diff --git a/src/drv/stdout/src/init.c b/src/drv/stdout/src/init.c new file mode 100644 index 0000000..86f566a --- /dev/null +++ b/src/drv/stdout/src/init.c @@ -0,0 +1,35 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdout.h> +#include <drv/stdout/ops.h> +#include <drv/stdout/types.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_port *drv_stdout_init(const struct drv_event *const ev) +{ + struct drv_port *ret = NULL; + + if (!(ret = malloc(sizeof *ret))) + return NULL; + + *ret = (const struct drv_port){.ev = *ev}; + return ret; +} diff --git a/src/drv/stdout/src/update.c b/src/drv/stdout/src/update.c new file mode 100644 index 0000000..8b47a52 --- /dev/null +++ b/src/drv/stdout/src/update.c @@ -0,0 +1,47 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdout.h> +#include <drv/stdout/ops.h> +#include <drv/stdout/types.h> +#include <stdbool.h> + +static int init(struct drv_port *const p) +{ + if (p->init) + return 0; + + const struct drv_event *const ev = &p->ev; + const struct drv_event_ops ops = + { + .write = drv_stdout_write, + .p = p + }; + + if (ev->status("stdout", &ops, true, 0666, ev->args)) + return -1; + + p->init = true; + return 0; +} + +int drv_stdout_update(struct drv_port *const p) +{ + return init(p); +} diff --git a/src/drv/stdout/src/write.c b/src/drv/stdout/src/write.c new file mode 100644 index 0000000..06d5980 --- /dev/null +++ b/src/drv/stdout/src/write.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/event.h> +#include <drv/stdout.h> +#include <drv/stdout/types.h> +#include <drv/stdout/ops.h> +#include <stddef.h> + +int drv_stdout_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const d) +{ + return n; +} diff --git a/src/drv/tty/private_include/drv/tty/ops.h b/src/drv/tty/private_include/drv/tty/ops.h index b50b00a..ceab56a 100644 --- a/src/drv/tty/private_include/drv/tty/ops.h +++ b/src/drv/tty/private_include/drv/tty/ops.h @@ -23,8 +23,8 @@ #include <drv/tty/types.h> #include <stddef.h> -int drv_tty_write(const void *buf, size_t n, const struct drv_event_done *done, - void *args); +int drv_tty_write(struct drv_port *p, const void *buf, size_t n, + const struct drv_event_done *done); int drv_tty_setdim(struct drv_port *p, unsigned x, unsigned y); #endif diff --git a/src/drv/tty/src/update.backup.c b/src/drv/tty/src/update.backup.c index d072977..07e2a09 100644 --- a/src/drv/tty/src/update.backup.c +++ b/src/drv/tty/src/update.backup.c @@ -34,7 +34,7 @@ static int init(struct drv_port *const p) const struct drv_event_ops ops = { .write = drv_tty_write, - .args = p + .p = p }; if (ev->status("tty", &ops, true, 0666, ev->args)) diff --git a/src/drv/tty/src/update.c b/src/drv/tty/src/update.c index 7c01678..b5644d0 100644 --- a/src/drv/tty/src/update.c +++ b/src/drv/tty/src/update.c @@ -34,7 +34,7 @@ static int init(struct drv_port *const p) const struct drv_event_ops ops = { .write = drv_tty_write, - .args = p + .p = p }; if (ev->status("tty", &ops, true, 0666, ev->args)) diff --git a/src/drv/tty/src/write.c b/src/drv/tty/src/write.c index d4a988d..c7b90ed 100644 --- a/src/drv/tty/src/write.c +++ b/src/drv/tty/src/write.c @@ -23,11 +23,9 @@ #include <errno.h> #include <stddef.h> -int drv_tty_write(const void *const buf, const size_t n, - const struct drv_event_done *const d, void *const args) +int drv_tty_write(struct drv_port *const p, const void *const buf, + const size_t n, const struct drv_event_done *const d) { - struct drv_port *const p = args; - for (const char *s = buf; s - (const char *)buf < n; s++) { struct row *const r = p->last_row; diff --git a/src/fs/devfs/src/open.c b/src/fs/devfs/src/open.c index 229ff49..fb822a6 100644 --- a/src/fs/devfs/src/open.c +++ b/src/fs/devfs/src/open.c @@ -21,7 +21,9 @@ #include <devfs/ops.h> #include <fs/fs.h> #include <state.h> +#include <unistd.h> #include <stdlib.h> +#include <string.h> struct open { @@ -44,14 +46,27 @@ int devfs_open(const struct fs_open *const o, const struct fs_mp *const mp, if (!op) return -1; - - *op = (const struct open){.pr = r, .r = *r}; - *o->fd = (const struct fs_fd) + else { - .inode = *inode, - .mp = *mp - }; + const struct fs_stdstreams *const ss = &o->ss; + const char *const name = inode->memi->name; + struct fs_fd *const fd = o->fd; + if (!strcmp(name, "stderr")) + *fd = ss->streams[STDERR_FILENO]; + else if (!strcmp(name, "stdin")) + *fd = ss->streams[STDIN_FILENO]; + else if (!strcmp(name, "stdout")) + *fd = ss->streams[STDOUT_FILENO]; + else + *fd = (const struct fs_fd) + { + .inode = *inode, + .mp = *mp + }; + } + + *op = (const struct open){.pr = r, .r = *r}; *r = (const struct fs_ret){.f = done, .args = op}; return 0; } diff --git a/src/fs/devfs/src/read.c b/src/fs/devfs/src/read.c index d7214ff..ec3c841 100644 --- a/src/fs/devfs/src/read.c +++ b/src/fs/devfs/src/read.c @@ -86,7 +86,7 @@ int devfs_read(const struct fs_read *const fr, struct fs_ret *const r) const struct drv_event_ops *const ops = &o->ops; - if (ops->read(fr->buf, fr->n, fd->start + fd->offset, &d, ops->args)) + if (ops->read(ops->p, fr->buf, fr->n, fd->start + fd->offset, &d)) goto failure; *r = (const struct fs_ret){.f = wait, .args = re}; diff --git a/src/fs/devfs/src/read_nb.c b/src/fs/devfs/src/read_nb.c index 75b6c25..7b79bf8 100644 --- a/src/fs/devfs/src/read_nb.c +++ b/src/fs/devfs/src/read_nb.c @@ -37,5 +37,5 @@ int devfs_read_nb(const struct fs_read *const fr) const struct drv_event_ops *const ops = &o->ops; - return ops->read_nb(fr->buf, fr->n, ops->args); + return ops->read_nb(ops->p, fr->buf, fr->n); } diff --git a/src/fs/devfs/src/write.c b/src/fs/devfs/src/write.c index fbb0ac8..dd4568f 100644 --- a/src/fs/devfs/src/write.c +++ b/src/fs/devfs/src/write.c @@ -83,7 +83,7 @@ int devfs_write(const struct fs_write *const fw, struct fs_ret *const r) const struct drv_event_ops *const ops = &o->ops; - if (ops->write(fw->buf, fw->n, &d, ops->args)) + if (ops->write(ops->p, fw->buf, fw->n, &d)) goto failure; *r = (const struct fs_ret){.f = wait, .args = w}; diff --git a/src/fs/include/fs/fs.h b/src/fs/include/fs/fs.h index 60c1788..66bfe84 100644 --- a/src/fs/include/fs/fs.h +++ b/src/fs/include/fs/fs.h @@ -30,6 +30,23 @@ struct fs_mountpoint; struct fs_mp_prv; struct fs_fd_prv; +struct fs_mp +{ + const char *src, *tgt; + const struct fs *fs; + struct fs_mp_prv *prv; +}; + +struct fs_fd +{ + int error; + off_t start, offset, size; + struct fs_fd_prv *prv; + struct fs_mp mp; + const struct fs_mp *tgt_mp; + union inode_result inode; +}; + struct fs_stat { const char *path; @@ -62,6 +79,11 @@ struct fs_umount gid_t gid; }; +struct fs_stdstreams +{ + struct fs_fd streams[3]; +}; + struct fs_open { const char *path; @@ -70,6 +92,7 @@ struct fs_open mode_t mode; uid_t uid; gid_t gid; + struct fs_stdstreams ss; }; struct fs_read @@ -114,23 +137,6 @@ struct fs struct inode_ops iops; }; -struct fs_mp -{ - const char *src, *tgt; - const struct fs *fs; - struct fs_mp_prv *prv; -}; - -struct fs_fd -{ - int error; - off_t start, offset, size; - struct fs_fd_prv *prv; - struct fs_mp mp; - const struct fs_mp *tgt_mp; - union inode_result inode, tgt_inode; -}; - typedef int (*fs_update_fn)(struct fs_mp_prv *); int fs_register(const struct fs *fs); diff --git a/src/fs/include/fs/inode.h b/src/fs/include/fs/inode.h index a7759f9..9447ab2 100644 --- a/src/fs/include/fs/inode.h +++ b/src/fs/include/fs/inode.h @@ -20,6 +20,7 @@ #define FS_INODE_H #include <state.h> +#include <sys/stat.h> #include <sys/types.h> #include <time.h> diff --git a/src/fs/iso9660/src/eof.c b/src/fs/iso9660/src/eof.c index 1c36b63..d53461b 100644 --- a/src/fs/iso9660/src/eof.c +++ b/src/fs/iso9660/src/eof.c @@ -21,10 +21,10 @@ #include <iso9660/types.h> #include <fs/inode.h> +/* TODO: eof seems redundant because of caio_eof */ int iso9660_eof(const struct fs_fd *const fd) { - const union inode_result *const i = &fd->tgt_inode; - const off_t sz = i->cachei.size; + const off_t sz = fd->size; if (!sz) return 1; diff --git a/src/fs/iso9660/src/open.c b/src/fs/iso9660/src/open.c index 9818719..979ea91 100644 --- a/src/fs/iso9660/src/open.c +++ b/src/fs/iso9660/src/open.c @@ -31,6 +31,7 @@ struct open struct fs_fd *fd; struct fs_ret *pr, r; struct fs_mp mp; + struct fs_stdstreams ss; }; static int search_done(const enum state state, const char *const relpath, @@ -57,7 +58,6 @@ static int search_done(const enum state state, const char *const relpath, *fd = (const struct fs_fd) { .start = mpinode->prv->offset, - .tgt_inode.cachei = op->inode, .size = mpinode->size, .tgt_mp = &op->mp, .inode = *inode, @@ -93,7 +93,8 @@ int iso9660_open(const struct fs_open *const o, const struct fs_mp *const mp, .inode = *i, .mp = *mp, .pr = r, - .r = *r + .r = *r, + .ss = o->ss }; if (inode_search(&s, r)) diff --git a/src/fs/ramfs/src/search.c b/src/fs/ramfs/src/search.c index 24e2a98..076603d 100644 --- a/src/fs/ramfs/src/search.c +++ b/src/fs/ramfs/src/search.c @@ -20,6 +20,7 @@ #include <ramfs/types.h> #include <fs/fs.h> #include <state.h> +#include <errno.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -76,7 +77,10 @@ static int find_node(const char *const path, struct inode **const out, } if (!found) + { + errno = ENOENT; goto end; + } } ret = 0; diff --git a/src/init/private_include/init/types.h b/src/init/private_include/init/types.h index 7f6114d..9bbb5bd 100644 --- a/src/init/private_include/init/types.h +++ b/src/init/private_include/init/types.h @@ -25,6 +25,7 @@ struct init { + size_t path_i; struct aio *aio; struct stat sb; unsigned retries; diff --git a/src/init/ps1/private_include/init/ps1/types.h b/src/init/ps1/private_include/init/ps1/types.h index 66702d1..33c2281 100644 --- a/src/init/ps1/private_include/init/ps1/types.h +++ b/src/init/ps1/private_include/init/ps1/types.h @@ -20,16 +20,19 @@ #define INIT_PS1_TYPES_H #include <aio.h> +#include <fs/fs.h> #include <sys/stat.h> #include <time.h> struct init_ps1 { + size_t stream_i; struct aio *aio; struct stat *sb; unsigned retries; const char *path; struct timespec last_retry; + struct fs_stdstreams ss; int (*next)(struct init_ps1 *); int (*success)(struct init_ps1 *); int (*retry)(struct init_ps1 *); diff --git a/src/init/ps1/src/boot.c b/src/init/ps1/src/boot.c index 062078c..2b5e600 100644 --- a/src/init/ps1/src/boot.c +++ b/src/init/ps1/src/boot.c @@ -20,93 +20,112 @@ #include <init/ps1/types.h> #include <aio.h> #include <bin.h> +#include <fs/fs.h> #include <iso9660.h> #include <kprintf.h> #include <loop.h> #include <net.h> +#include <state.h> #include <fcntl.h> #include <sys/stat.h> +#include <errno.h> #include <stdlib.h> -#include <state.h> +#include <string.h> #include <time.h> -struct boot -{ - struct aio *aio; - struct init_ps1 *p; -}; - enum {RETRIES = 5}; static const char serial[] = "/dev/ttyS0"; +static const struct stream +{ + const char *path; + int flags; +} streams[] = +{ + {.path = "/dev/null", .flags = O_RDONLY}, + {.path = "/dev/tty", .flags = O_WRONLY}, + {.path = "/dev/tty", .flags = O_WRONLY} +}; static int retry_cd(struct init_ps1 *); +static int open_stream(struct init_ps1 *p); -static int wait_retry(struct init_ps1 *const p) +static int exec(struct init_ps1 *const p) { - struct timespec ts; - const time_t last = p->last_retry.tv_sec, timeout = 5; + const char *const args[] = {NULL}; - if (clock_gettime(CLOCK_REALTIME, &ts)) - return -1; - else if (ts.tv_sec - last >= timeout) - p->next = p->retry; + kprintf("Opening second-stage initd\n"); + p->next = NULL; - return 0; + /* TODO: create symbolic from /mnt/cdrom/bin to /bin */ + return bin_exec("/mnt/cdrom/bin/initd", args, &p->ss, 0, 0, 0); } -static int stat_done(const enum state s, void *const args) +static int stream_opened(const enum state state, void *const args) { - int ret = 0; struct init_ps1 *const p = args; + const struct stream *const s = &streams[p->stream_i]; - if (s) - { - struct timespec ts; + if (loop_rm_aio(p->aio)) + return -1; - if (!--p->retries || clock_gettime(CLOCK_REALTIME, &ts)) - { - ret = -1; - goto end; - } + aio_free(p->aio); - p->last_retry = ts; - p->next = wait_retry; - } + if (state) + kprintf("Failed to open %s: %s\n", s->path, strerror(errno)); else { - p->next = p->success; - kprintf("Successfully initialized %s (took %u attempts)\n", - p->path, RETRIES - p->retries + 1); + kprintf("Successfully opened %s\n", s->path); + + if (++p->stream_i >= sizeof streams / sizeof *streams) + p->next = exec; + else + p->next = open_stream; } -end: - loop_rm_aio(p->aio); - aio_free(p->aio); - free(p->sb); - return ret; + return 0; +} + +static int open_stream(struct init_ps1 *const p) +{ + const struct stream *const s = &streams[p->stream_i]; + const struct fs_open o = + { + .fd = &p->ss.streams[p->stream_i], + .path = s->path, + .flags = s->flags + }; + + const struct aio_done d = + { + .f = stream_opened, + .args = p + }; + + struct aio *const aio = aio_open(&o, &d); + + if (!aio || loop_add_aio(aio)) + return -1; + + p->aio = aio; + p->next = NULL; + return 0; } static int mount_done(const enum state state, void *const args) { - struct boot *const b = args; + struct init_ps1 *const p = args; - if (loop_rm_aio(b->aio)) + if (loop_rm_aio(p->aio)) return -1; - aio_free(b->aio); - free(b); + aio_free(p->aio); if (state) kprintf("Failed to mount ISO9660 filesystem\n"); else { - const char *const args[] = {NULL}; - kprintf("ISO9660 filesystem mounted successfully\n"); - kprintf("Opening second-stage initd\n"); - - /* TODO: create symbolic from /mnt/cdrom/bin to /bin */ - return bin_exec("/mnt/cdrom/bin/initd", args, 0, 0); + p->next = open_stream; } return 0; @@ -115,11 +134,6 @@ static int mount_done(const enum state state, void *const args) static int mount_cd(struct init_ps1 *const p) { struct aio *aio = NULL; - struct boot *const b = malloc(sizeof *b); - - if (!b) - goto failure; - const struct aio_mount fm = { .mount = @@ -134,7 +148,7 @@ static int mount_cd(struct init_ps1 *const p) const struct aio_done d = { - .args = b, + .args = p, .f = mount_done }; @@ -142,7 +156,7 @@ static int mount_cd(struct init_ps1 *const p) goto failure; p->next = NULL; - *b = (const struct boot){.aio = aio, .p = p}; + p->aio = aio; kprintf("Mounting ISO9660 filesystem from %s to %s\n", fm.mount.src, fm.mount.tgt); return 0; @@ -150,10 +164,54 @@ static int mount_cd(struct init_ps1 *const p) failure: kprintf("Failed to mount ISO9660 filesystem\n"); aio_free(aio); - free(b); return -1; } +static int wait_retry(struct init_ps1 *const p) +{ + struct timespec ts; + const time_t last = p->last_retry.tv_sec, timeout = 5; + + if (clock_gettime(CLOCK_REALTIME, &ts)) + return -1; + else if (ts.tv_sec - last >= timeout) + p->next = p->retry; + + return 0; +} + +static int stat_done(const enum state s, void *const args) +{ + int ret = 0; + struct init_ps1 *const p = args; + + if (s) + { + struct timespec ts; + + if (!--p->retries || clock_gettime(CLOCK_REALTIME, &ts)) + { + ret = -1; + goto end; + } + + p->last_retry = ts; + p->next = wait_retry; + } + else + { + p->next = p->success; + kprintf("Successfully initialized %s (took %u attempts)\n", + p->path, RETRIES - p->retries + 1); + } + +end: + loop_rm_aio(p->aio); + aio_free(p->aio); + free(p->sb); + return ret; +} + static int retry_cd(struct init_ps1 *const p) { static const char path[] = "/dev/cd0"; diff --git a/src/init/src/boot.c b/src/init/src/boot.c index 30d9a9e..4c3e6ad 100644 --- a/src/init/src/boot.c +++ b/src/init/src/boot.c @@ -27,6 +27,14 @@ static int retry(struct init *); enum {RETRIES = 5}; +static const char *const paths[] = +{ + "/dev/tty", + "/dev/null", + "/dev/stdout", + "/dev/stdin", + "/dev/stderr" +}; static int run(struct init *const init) { @@ -35,13 +43,26 @@ static int run(struct init *const init) static int port(struct init *const init) { - kprintf("tty initilization successful (took %u attempts)\n", - RETRIES - init->retries + 1); + const size_t path_i = init->path_i + 1; - if (init_port_boot()) - return -1; + kprintf("%s initilization successful (took %u attempts)\n", + paths[init->path_i], RETRIES - init->retries + 1); + + if (path_i >= sizeof paths / sizeof *paths) + { + if (init_port_boot()) + return -1; + + init->next = run; + } + else + init_vars = (const struct init) + { + .retries = RETRIES, + .next = retry, + .path_i = path_i + }; - init->next = run; return 0; } @@ -88,7 +109,8 @@ end: static int retry(struct init *const init) { struct aio *aio = NULL; - const struct fs_stat s = {.path = "/dev/tty", .sb = &init->sb}; + const size_t path_i = init->path_i; + const struct fs_stat s = {.path = paths[init->path_i], .sb = &init->sb}; const struct aio_done d = {.f = stat_done, .args = init}; const unsigned retries = init->retries; @@ -99,6 +121,7 @@ static int retry(struct init *const init) { .aio = aio, .retries = retries, + .path_i = path_i, .last_retry = {.tv_sec = (time_t)-1} }; diff --git a/src/libc/include/fcntl.h b/src/libc/include/fcntl.h index 282ced8..1edf2fc 100644 --- a/src/libc/include/fcntl.h +++ b/src/libc/include/fcntl.h @@ -19,11 +19,12 @@ #ifndef _FCNTL_H #define _FCNTL_H -enum -{ - O_RDONLY = 1, - O_WRONLY = 1 << 1, - O_RDWR = O_RDONLY | O_WRONLY -}; +#define O_RDONLY 1 +#define O_WRONLY (1 << 1) +#define O_CREAT (1 << 2) +#define O_APPEND (1 << 3) +#define O_RDWR (O_RDONLY | O_WRONLY) + +int open(const char *__path, int __flags, ...); #endif diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h index b9c1a38..efa60dd 100644 --- a/src/libc/include/stdio.h +++ b/src/libc/include/stdio.h @@ -19,8 +19,23 @@ #ifndef _STDIO_H #define _STDIO_H -enum {EOF = -1}; +#include <stddef.h> +#define BUFSIZ 128 +#define FOPEN_MAX 8 +#define FILENAME_MAX 128 +#define EOF -1 +#define stdin __stdin +#define stdout __stdout +#define stderr __stderr + +typedef struct __file FILE; +extern FILE *__stdin, *__stdout, *__stderr; + +int fputc(int __c, FILE *__f); +FILE *fopen(const char *__path, const char *__mode); +size_t fwrite(const void *__p, size_t __sz, size_t __nmemb, FILE *__f); +int putc(int __c, FILE *__f); int puts(const char *__s); int putchar(int __c); diff --git a/src/libc/include/stdlib.h b/src/libc/include/stdlib.h index 2704fca..b6e092e 100644 --- a/src/libc/include/stdlib.h +++ b/src/libc/include/stdlib.h @@ -57,10 +57,10 @@ div_t div(int __numerator, int __denominator); #define RESET "\x1b[0m" #define malloc(__n) ({void *__p; \ + int Printf(const char *, ...); \ Printf(GREEN "-> %s:%d (%s)", __FILE__, __LINE__, __func__); \ __p = __malloc(__n); \ Printf(", p=%p" RESET "\n", __p); \ - int Printf(const char *, ...); \ size_t ta_num_free(); \ size_t ta_num_used(); \ size_t ta_num_fresh(); \ @@ -71,13 +71,13 @@ div_t div(int __numerator, int __denominator); __p;}) #define realloc(__p, __n) ({void *__np; \ + int Printf(const char *, ...); \ + size_t ta_num_free(); \ + size_t ta_num_used(); \ + size_t ta_num_fresh(); \ Printf(YELLOW "-> %s:%d (%s)", __FILE__, __LINE__, __func__); \ __np = __realloc(__p, __n); \ Printf(", np=%p" RESET "\n", __np); \ - int Printf(const char *, ...); \ - size_t ta_num_free(); \ -size_t ta_num_used(); \ -size_t ta_num_fresh(); \ Printf("%s: free=%lu, used=%lu, fresh=%lu\n", __func__, \ (unsigned long)ta_num_free(), \ (unsigned long)ta_num_used(), \ @@ -85,13 +85,13 @@ size_t ta_num_fresh(); \ __np;}) #define calloc(__n, __sz) ({void *__p; \ + int Printf(const char *, ...); \ + size_t ta_num_free(); \ + size_t ta_num_used(); \ + size_t ta_num_fresh(); \ Printf(YELLOW "-> %s:%d (%s)", __FILE__, __LINE__, __func__); \ __p = __calloc(__n, __sz); \ Printf(", p=%p" RESET "\n", __p); \ - int Printf(const char *, ...); \ - size_t ta_num_free(); \ -size_t ta_num_used(); \ -size_t ta_num_fresh(); \ Printf("%s: free=%lu, used=%lu, fresh=%lu\n", __func__, \ (unsigned long)ta_num_free(), \ (unsigned long)ta_num_used(), \ @@ -100,18 +100,18 @@ size_t ta_num_fresh(); \ #define free(__p) ({ \ int Printf(const char *, ...); \ + size_t ta_num_free(); \ + size_t ta_num_used(); \ + size_t ta_num_fresh(); \ Printf(RED "<- %s:%d (%s), p=%p" RESET "\n", \ __FILE__, __LINE__, __func__, __p); \ - size_t ta_num_free(); \ - size_t ta_num_used(); \ - size_t ta_num_fresh(); \ - __free(__p);\ - Printf("%s: free=%lu, used=%lu, fresh=%lu\n", \ - __func__, \ - (unsigned long)ta_num_free(), \ - (unsigned long)ta_num_used(), \ - (unsigned long)ta_num_fresh()); \ - }) + __free(__p);\ + Printf("%s: free=%lu, used=%lu, fresh=%lu\n", \ + __func__, \ + (unsigned long)ta_num_free(), \ + (unsigned long)ta_num_used(), \ + (unsigned long)ta_num_fresh()); \ + }) #endif #endif diff --git a/src/libc/include/sys/stat.h b/src/libc/include/sys/stat.h index 08fcf9a..b5ef13d 100644 --- a/src/libc/include/sys/stat.h +++ b/src/libc/include/sys/stat.h @@ -22,6 +22,35 @@ #include <sys/types.h> #include <time.h> +#define S_IRWXU 0700 +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRWXG 070 +#define S_IRGRP 040 +#define S_IWGRP 020 +#define S_IXGRP 010 +#define S_IRWXO 07 +#define S_IROTH 04 +#define S_IWOTH 02 +#define S_IXOTH 1 + +#define S_IFBLK ((mode_t)1) +#define S_IFCHR ((mode_t)(1 << 1)) +#define S_IFIFO ((mode_t)(1 << 2)) +#define S_IFREG ((mode_t)(1 << 3)) +#define S_IFDIR ((mode_t)(1 << 4)) +#define S_IFLNK ((mode_t)(1 << 5)) +#define S_IFSOCK ((mode_t)(1 << 6)) + +#define S_ISBLK(__m) ((__m) & S_IFBLK) +#define S_ISCHR(__m) ((__m) & S_IFCHR) +#define S_ISDIR(__m) ((__m) & S_IFDIR) +#define S_ISFIFO(__m) ((__m) & S_IFIFO) +#define S_ISREG(__m) ((__m) & S_IFREG) +#define S_ISLNK(__m) ((__m) & S_IFLNK) +#define S_ISSOCK(__m) ((__m) & S_IFSOCK) + struct stat { dev_t st_dev; @@ -37,6 +66,7 @@ struct stat blkcnt_t st_blocks; }; -int mkdir(const char *pathname, mode_t flags); +int mkdir(const char *__pathname, mode_t __flags); +mode_t umask(mode_t __mask); #endif diff --git a/src/libc/include/sys/types.h b/src/libc/include/sys/types.h index 86ca604..fc231f1 100644 --- a/src/libc/include/sys/types.h +++ b/src/libc/include/sys/types.h @@ -22,5 +22,6 @@ typedef unsigned dev_t, mode_t, nlink_t, uid_t, gid_t, blksize_t, blkcnt_t; typedef unsigned long ino_t, off_t; typedef int pid_t; +typedef long ssize_t; #endif diff --git a/src/libc/include/unistd.h b/src/libc/include/unistd.h index ec4fc13..dcf0336 100644 --- a/src/libc/include/unistd.h +++ b/src/libc/include/unistd.h @@ -19,8 +19,20 @@ #ifndef _UNISTD_H #define _UNISTD_H +#include <stddef.h> #include <stdint.h> +#include <sys/types.h> -void *sbrk(intptr_t increment); +enum +{ + STDIN_FILENO, + STDOUT_FILENO, + STDERR_FILENO +}; + +void *sbrk(intptr_t __increment); +ssize_t read(int __fd, void *__buf, size_t __n); +ssize_t write(int __fd, const void *__buf, size_t __n); +int close(int __fd); #endif diff --git a/src/libc/private_include/libc/file.h b/src/libc/private_include/libc/file.h index dabeb89..8cb3fa0 100644 --- a/src/libc/private_include/libc/file.h +++ b/src/libc/private_include/libc/file.h @@ -1,4 +1,33 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + #ifndef LIBC_FILE_H #define LIBC_FILE_H +#include <stdio.h> + +struct __file +{ + int fd, eof, error; + char buf[BUFSIZ]; + size_t i; +}; + +extern struct __file *__files[FOPEN_MAX]; + #endif diff --git a/src/libc/src/CMakeLists.txt b/src/libc/src/CMakeLists.txt index 11e7a3c..cf88cdc 100644 --- a/src/libc/src/CMakeLists.txt +++ b/src/libc/src/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. target_sources(c PRIVATE + _putchar.c errno.c ta_init.c ) @@ -25,6 +26,7 @@ add_subdirectory(stdlib) add_subdirectory(string) if(NOT LIBC_FREESTANDING) + target_sources(c PRIVATE malloc_init.c) add_subdirectory(time) add_subdirectory(unistd) endif() diff --git a/src/libc/src/_putchar.c b/src/libc/src/_putchar.c new file mode 100644 index 0000000..df797c6 --- /dev/null +++ b/src/libc/src/_putchar.c @@ -0,0 +1,24 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +void _putchar(const char c) +{ + putchar(c); +} diff --git a/src/libc/src/malloc_init.c b/src/libc/src/malloc_init.c new file mode 100644 index 0000000..1ec87e8 --- /dev/null +++ b/src/libc/src/malloc_init.c @@ -0,0 +1,29 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <libc/malloc.h> +#include <tinyalloc.h> +#include <stdint.h> + +void __malloc_init(void) +{ + /* README.md states 16 is a "good default value". */ + if (!ta_init(300, 16, sizeof (intmax_t))) + abort(); +} diff --git a/src/libc/src/ps1/CMakeLists.txt b/src/libc/src/ps1/CMakeLists.txt index 1bab87a..25d64a4 100644 --- a/src/libc/src/ps1/CMakeLists.txt +++ b/src/libc/src/ps1/CMakeLists.txt @@ -15,7 +15,6 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. target_sources(c PRIVATE - putchar.c setjmp.c ) diff --git a/src/libc/src/stdio/CMakeLists.txt b/src/libc/src/stdio/CMakeLists.txt index ca590a1..a4a60d9 100644 --- a/src/libc/src/stdio/CMakeLists.txt +++ b/src/libc/src/stdio/CMakeLists.txt @@ -15,5 +15,11 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. target_sources(c PRIVATE + fopen.c + fputc.c + fwrite.c + putchar.c + putc.c puts.c + streams.c ) diff --git a/src/libc/src/stdio/fopen.c b/src/libc/src/stdio/fopen.c new file mode 100644 index 0000000..4c47f5a --- /dev/null +++ b/src/libc/src/stdio/fopen.c @@ -0,0 +1,95 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <libc/file.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> + +static int find_free(void) +{ + for (size_t i = 0; i < sizeof __files / sizeof *__files; i++) + if (!__files[i]) + return i; + + return -1; +} + +static int convert(const char *mode) +{ + int ret = 0; + + if (!mode) + return -1; + else if (*mode == 'r') + ret |= O_RDONLY; + else if (*mode == 'w') + ret |= O_WRONLY | O_CREAT; + else if (*mode == 'a') + ret |= O_WRONLY | O_CREAT | O_APPEND; + + if (*++mode == '+') + ret |= O_RDWR; + + if (!ret) + return -1; + + return ret; +} + +FILE *fopen(const char *const path, const char *const mode) +{ + FILE *ret = NULL; + int i, flags, fd = -1; + mode_t mask; + + if (!path || (flags = convert(mode)) < 0) + { + errno = EINVAL; + goto failure; + } + else if ((i = find_free()) < 0) + { + errno = EMFILE; + goto failure; + } + + mask = umask(0); + umask(mask); + + if ((fd = open(path, flags, 0666 & ~mask)) < 0) + goto failure; + else if (!(ret = malloc(sizeof *ret))) + goto failure; + + *ret = (const struct __file){.fd = fd}; + __files[i] = ret; + return ret; + +failure: + + if (fd >= 0) + close(fd); + + free(ret); + return NULL; +} diff --git a/src/libc/src/stdio/fputc.c b/src/libc/src/stdio/fputc.c new file mode 100644 index 0000000..10fe4c6 --- /dev/null +++ b/src/libc/src/stdio/fputc.c @@ -0,0 +1,40 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <stdio.h> +#include <libc/file.h> + +int fputc(const int ic, FILE *const f) +{ + const unsigned char c = ic; + + f->buf[f->i++] = c; + + if (c == '\n' || f->i >= sizeof f->buf) + { + const size_t n = fwrite(f->buf, f->i, 1, f); + + f->i = 0; + + if (!n) + return EOF; + } + + return c; +} diff --git a/src/libc/src/stdio/fwrite.c b/src/libc/src/stdio/fwrite.c new file mode 100644 index 0000000..96a4daf --- /dev/null +++ b/src/libc/src/stdio/fwrite.c @@ -0,0 +1,50 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <stdio.h> +#include <libc/file.h> +#include <unistd.h> + +size_t fwrite(const void *const p, const size_t sz, const size_t nmemb, + FILE *const f) +{ + size_t ret = 0; + const char *b = p; + + for (size_t i = 0; i < nmemb; i++, ret++) + { + size_t rem = sz; + + while (rem) + { + const ssize_t n = write(f->fd, b, rem); + + if (n < 0) + { + f->error = 1; + return 0; + } + + b += n; + rem -= n; + } + } + + return ret; +} diff --git a/src/libc/src/stdio/putc.c b/src/libc/src/stdio/putc.c new file mode 100644 index 0000000..b8d015d --- /dev/null +++ b/src/libc/src/stdio/putc.c @@ -0,0 +1,25 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <stdio.h> + +int putc(const int c, FILE *const f) +{ + return fputc(c, f); +} diff --git a/src/libc/src/ps1/putchar.c b/src/libc/src/stdio/putchar.c index 81c98ec..290ad97 100644 --- a/src/libc/src/ps1/putchar.c +++ b/src/libc/src/stdio/putchar.c @@ -20,10 +20,5 @@ int putchar(const int c) { - return EOF; -} - -void _putchar(const char c) -{ - putchar(c); + return putc(c, stdout); } diff --git a/src/libc/src/stdio/streams.c b/src/libc/src/stdio/streams.c new file mode 100644 index 0000000..d88caa1 --- /dev/null +++ b/src/libc/src/stdio/streams.c @@ -0,0 +1,24 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <libc/file.h> +#include <unistd.h> + +struct __file *__stdin, *__stdout, *__stderr; +struct __file *__files[FOPEN_MAX]; diff --git a/src/libc/src/stdlib/malloc.c b/src/libc/src/stdlib/malloc.c index 786f314..9025491 100644 --- a/src/libc/src/stdlib/malloc.c +++ b/src/libc/src/stdlib/malloc.c @@ -20,16 +20,12 @@ #include <libc/malloc.h> #include <tinyalloc.h> #include <errno.h> -#include <stdint.h> void *__malloc(const size_t n) { if (!__malloc_ta_init) { - /* README.md states 16 is a "good default value". */ - if (!ta_init(300, 16, sizeof (intmax_t))) - abort(); - + __malloc_init(); __malloc_ta_init = 1; } diff --git a/src/libc/src/unistd/sbrk.c b/src/libc/src/unistd/sbrk.c index b41c192..5d193da 100644 --- a/src/libc/src/unistd/sbrk.c +++ b/src/libc/src/unistd/sbrk.c @@ -25,9 +25,6 @@ static char *base = __bss_end; void *sbrk(const intptr_t increment) { - - - if (base + increment >= __ram_end) return NULL; diff --git a/src/libc/tinyalloc b/src/libc/tinyalloc -Subproject fd8128d9b84e362abdbd05fafe252421edee8ab +Subproject e32da5559962c6131e1f933a245526d699d45cf diff --git a/src/loop/src/run.c b/src/loop/src/run.c index 6c23365..3ce97b7 100644 --- a/src/loop/src/run.c +++ b/src/loop/src/run.c @@ -31,8 +31,6 @@ #include <drv/ps1/bios.h> int loop_run(void) { - static unsigned iterations; - if (fs_update() || aio_poll(loop_aio_head, 0) < 0 || init_run() @@ -41,6 +39,9 @@ int loop_run(void) || gfx_update()) return -1; +#if 0 + static unsigned iterations; + iterations++; static struct timespec ts; @@ -59,6 +60,6 @@ int loop_run(void) ts = now; iterations = 0; } - +#endif return 0; } diff --git a/src/nanowasm b/src/nanowasm -Subproject 1e3a4e9ff4d9963aa77dc6135f351f90d927e9e +Subproject 4e4964c65700636e7411ea2d83b7f25ca4ad137 diff --git a/tools/nwc b/tools/nwc -Subproject 15d2bb51e092d6dc34fa5fb5c1eb9b64c7c597b +Subproject 690cfd848c81550aa96d1643eaf57ef28d7841f |
