Path: blob/main/system/lib/wasmfs/special_files.cpp
6174 views
// Copyright 2021 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45// This file defines the standard streams.67#include <emscripten/html5.h>8#include <unistd.h>9#include <vector>10#include <wasi/api.h>1112#include "special_files.h"13#include "wasmfs_internal.h"1415namespace wasmfs::SpecialFiles {1617namespace {1819// No-op reads and writes: /dev/null20class NullFile : public DataFile {21int open(oflags_t) override { return 0; }22int close() override { return 0; }2324ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {25return len;26}2728ssize_t read(uint8_t* buf, size_t len, off_t offset) override { return 0; }2930int flush() override { return 0; }31off_t getSize() override { return 0; }32int setSize(off_t size) override { return -EPERM; }3334public:35NullFile() : DataFile(S_IRUGO | S_IWUGO, NullBackend, S_IFCHR) {}36};3738class StdinFile : public DataFile {39int open(oflags_t) override { return 0; }40int close() override { return 0; }4142ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {43return -__WASI_ERRNO_INVAL;44}4546ssize_t read(uint8_t* buf, size_t len, off_t offset) override {47for (size_t i = 0; i < len; i++) {48auto c = _wasmfs_stdin_get_char();49if (c < 0) {50// No more input can be read, return what we did read.51return i;52}53buf[i] = c;54}55return len;56};5758int flush() override { return 0; }59off_t getSize() override { return 0; }60int setSize(off_t size) override { return -EPERM; }6162public:63StdinFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; }64};6566// A standard stream that writes: stdout or stderr.67class WritingStdFile : public DataFile {68protected:69std::vector<char> writeBuffer;7071int open(oflags_t) override { return 0; }72int close() override { return 0; }7374ssize_t read(uint8_t* buf, size_t len, off_t offset) override {75return -__WASI_ERRNO_INVAL;76};7778int flush() override {79// Write a null to flush the output if we have content.80if (!writeBuffer.empty()) {81const uint8_t nothing = '\0';82write(¬hing, 1, 0);83}84return 0;85}8687off_t getSize() override { return 0; }88int setSize(off_t size) override { return -EPERM; }8990ssize_t writeToJS(const uint8_t* buf,91size_t len,92void (*console_write)(const char*),93std::vector<char>& fd_write_buffer) {94for (size_t j = 0; j < len; j++) {95uint8_t current = buf[j];96// Flush on either a null or a newline.97if (current == '\0' || current == '\n') {98fd_write_buffer.push_back('\0'); // for null-terminated C strings99console_write(fd_write_buffer.data());100fd_write_buffer.clear();101} else {102fd_write_buffer.push_back(current);103}104}105return len;106}107108public:109WritingStdFile() : DataFile(S_IWUGO, NullBackend, S_IFCHR) {110seekable = false;111}112};113114class StdoutFile : public WritingStdFile {115ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {116// Node and worker threads issue in Emscripten:117// https://github.com/emscripten-core/emscripten/issues/14804.118// Issue filed in Node: https://github.com/nodejs/node/issues/40961119// This is confirmed to occur when running with EXIT_RUNTIME and120// PROXY_TO_PTHREAD. This results in only a single console.log statement121// being outputted. The solution for now is to use out() and err() instead.122return writeToJS(buf, len, &emscripten_out, writeBuffer);123}124125public:126StdoutFile() {}127};128129class StderrFile : public WritingStdFile {130ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {131// Similar issue with Node and worker threads as emscripten_out.132// TODO: May not want to proxy stderr (fd == 2) to the main thread, as133// emscripten_err does.134// This will not show in HTML - a console.warn in a worker is135// sufficient. This would be a change from the current FS.136return writeToJS(buf, len, &emscripten_err, writeBuffer);137}138139public:140StderrFile() {}141};142143class RandomFile : public DataFile {144int open(oflags_t) override { return 0; }145int close() override { return 0; }146147ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {148return -__WASI_ERRNO_INVAL;149}150151ssize_t read(uint8_t* buf, size_t len, off_t offset) override {152uint8_t* end = buf + len;153for (; buf < end; buf += 256) {154[[maybe_unused]] int err = getentropy(buf, std::min(end - buf, 256l));155assert(err == 0);156}157return len;158};159160int flush() override { return 0; }161off_t getSize() override { return 0; }162int setSize(off_t size) override { return -EPERM; }163164public:165RandomFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; }166};167168} // anonymous namespace169170std::shared_ptr<DataFile> getNull() {171static auto null = std::make_shared<NullFile>();172return null;173}174175std::shared_ptr<DataFile> getStdin() {176static auto stdin = std::make_shared<StdinFile>();177return stdin;178}179180std::shared_ptr<DataFile> getStdout() {181static auto stdout = std::make_shared<StdoutFile>();182return stdout;183}184185std::shared_ptr<DataFile> getStderr() {186static auto stderr = std::make_shared<StderrFile>();187return stderr;188}189190std::shared_ptr<DataFile> getRandom() {191static auto random = std::make_shared<RandomFile>();192return random;193}194195std::shared_ptr<DataFile> getURandom() {196static auto urandom = std::make_shared<RandomFile>();197return urandom;198}199200} // namespace wasmfs::SpecialFiles201202203