// Copyright 2021 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. // This file defines the standard streams of the new file system. // Current Status: Work in Progress. // See https://github.com/emscripten-core/emscripten/issues/15041. #include #include #include #include #include "special_files.h" namespace wasmfs::SpecialFiles { namespace { class StdinFile : public DataFile { void open(oflags_t) override {} void close() override {} ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { return -__WASI_ERRNO_INVAL; } ssize_t read(uint8_t* buf, size_t len, off_t offset) override { // TODO: Implement reading from stdin. abort(); }; void flush() override {} size_t getSize() override { return 0; } void setSize(size_t size) override {} public: StdinFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; } }; // A standard stream that writes: stdout or stderr. class WritingStdFile : public DataFile { protected: std::vector writeBuffer; void open(oflags_t) override {} void close() override {} ssize_t read(uint8_t* buf, size_t len, off_t offset) override { return -__WASI_ERRNO_INVAL; }; void flush() override { // Write a null to flush the output if we have content. if (!writeBuffer.empty()) { const uint8_t nothing = '\0'; write(¬hing, 1, 0); } } size_t getSize() override { return 0; } void setSize(size_t size) override {} ssize_t writeToJS(const uint8_t* buf, size_t len, void (*console_write)(const char*), std::vector& fd_write_buffer) { for (size_t j = 0; j < len; j++) { uint8_t current = buf[j]; // Flush on either a null or a newline. if (current == '\0' || current == '\n') { fd_write_buffer.push_back('\0'); // for null-terminated C strings console_write(fd_write_buffer.data()); fd_write_buffer.clear(); } else { fd_write_buffer.push_back(current); } } return len; } public: WritingStdFile() : DataFile(S_IWUGO, NullBackend, S_IFCHR) { seekable = false; } }; class StdoutFile : public WritingStdFile { ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { // Node and worker threads issue in Emscripten: // https://github.com/emscripten-core/emscripten/issues/14804. // Issue filed in Node: https://github.com/nodejs/node/issues/40961 // This is confirmed to occur when running with EXIT_RUNTIME and // PROXY_TO_PTHREAD. This results in only a single console.log statement // being outputted. The solution for now is to use out() and err() instead. return writeToJS(buf, len, &_emscripten_out, writeBuffer); } public: StdoutFile() {} }; class StderrFile : public WritingStdFile { ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { // Similar issue with Node and worker threads as emscripten_out. // TODO: May not want to proxy stderr (fd == 2) to the main thread, as // emscripten_err does. // This will not show in HTML - a console.warn in a worker is // sufficient. This would be a change from the current FS. return writeToJS(buf, len, &_emscripten_err, writeBuffer); } public: StderrFile() {} }; class RandomFile : public DataFile { void open(oflags_t) override {} void close() override {} ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { return -__WASI_ERRNO_INVAL; } ssize_t read(uint8_t* buf, size_t len, off_t offset) override { uint8_t* end = buf + len; for (; buf < end; buf += 256) { int err = getentropy(buf, std::min(end - buf, 256l)); assert(err == 0); } return len; }; void flush() override {} size_t getSize() override { return 0; } void setSize(size_t size) override {} public: RandomFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; } }; } // anonymous namespace std::shared_ptr getStdin() { static auto stdin = std::make_shared(); return stdin; } std::shared_ptr getStdout() { static auto stdout = std::make_shared(); return stdout; } std::shared_ptr getStderr() { static auto stderr = std::make_shared(); return stderr; } std::shared_ptr getRandom() { static auto random = std::make_shared(); return random; } std::shared_ptr getURandom() { static auto urandom = std::make_shared(); return urandom; } } // namespace wasmfs::SpecialFiles