stdio.go

  1package sys
  2
  3import (
  4	"io"
  5	"os"
  6
  7	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
  8	"github.com/tetratelabs/wazero/internal/fsapi"
  9	"github.com/tetratelabs/wazero/internal/sysfs"
 10	"github.com/tetratelabs/wazero/sys"
 11)
 12
 13// StdinFile is a fs.ModeDevice file for use implementing FdStdin.
 14// This is safer than reading from os.DevNull as it can never overrun
 15// operating system file descriptors.
 16type StdinFile struct {
 17	noopStdinFile
 18	io.Reader
 19}
 20
 21// Read implements the same method as documented on sys.File
 22func (f *StdinFile) Read(buf []byte) (int, experimentalsys.Errno) {
 23	n, err := f.Reader.Read(buf)
 24	return n, experimentalsys.UnwrapOSError(err)
 25}
 26
 27type writerFile struct {
 28	noopStdoutFile
 29
 30	w io.Writer
 31}
 32
 33// Write implements the same method as documented on sys.File
 34func (f *writerFile) Write(buf []byte) (int, experimentalsys.Errno) {
 35	n, err := f.w.Write(buf)
 36	return n, experimentalsys.UnwrapOSError(err)
 37}
 38
 39// noopStdinFile is a fs.ModeDevice file for use implementing FdStdin. This is
 40// safer than reading from os.DevNull as it can never overrun operating system
 41// file descriptors.
 42type noopStdinFile struct {
 43	noopStdioFile
 44}
 45
 46// Read implements the same method as documented on sys.File
 47func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) {
 48	return 0, 0 // Always EOF
 49}
 50
 51// Poll implements the same method as documented on fsapi.File
 52func (noopStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
 53	if flag != fsapi.POLLIN {
 54		return false, experimentalsys.ENOTSUP
 55	}
 56	return true, 0 // always ready to read nothing
 57}
 58
 59// noopStdoutFile is a fs.ModeDevice file for use implementing FdStdout and
 60// FdStderr.
 61type noopStdoutFile struct {
 62	noopStdioFile
 63}
 64
 65// Write implements the same method as documented on sys.File
 66func (noopStdoutFile) Write(buf []byte) (int, experimentalsys.Errno) {
 67	return len(buf), 0 // same as io.Discard
 68}
 69
 70type noopStdioFile struct {
 71	experimentalsys.UnimplementedFile
 72}
 73
 74// Stat implements the same method as documented on sys.File
 75func (noopStdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
 76	return sys.Stat_t{Mode: modeDevice, Nlink: 1}, 0
 77}
 78
 79// IsDir implements the same method as documented on sys.File
 80func (noopStdioFile) IsDir() (bool, experimentalsys.Errno) {
 81	return false, 0
 82}
 83
 84// Close implements the same method as documented on sys.File
 85func (noopStdioFile) Close() (errno experimentalsys.Errno) { return }
 86
 87// IsNonblock implements the same method as documented on fsapi.File
 88func (noopStdioFile) IsNonblock() bool {
 89	return false
 90}
 91
 92// SetNonblock implements the same method as documented on fsapi.File
 93func (noopStdioFile) SetNonblock(bool) experimentalsys.Errno {
 94	return experimentalsys.ENOSYS
 95}
 96
 97// Poll implements the same method as documented on fsapi.File
 98func (noopStdioFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) {
 99	return false, experimentalsys.ENOSYS
100}
101
102func stdinFileEntry(r io.Reader) (*FileEntry, error) {
103	if r == nil {
104		return &FileEntry{Name: "stdin", IsPreopen: true, File: &noopStdinFile{}}, nil
105	} else if f, ok := r.(*os.File); ok {
106		if f, err := sysfs.NewStdioFile(true, f); err != nil {
107			return nil, err
108		} else {
109			return &FileEntry{Name: "stdin", IsPreopen: true, File: f}, nil
110		}
111	} else {
112		return &FileEntry{Name: "stdin", IsPreopen: true, File: &StdinFile{Reader: r}}, nil
113	}
114}
115
116func stdioWriterFileEntry(name string, w io.Writer) (*FileEntry, error) {
117	if w == nil {
118		return &FileEntry{Name: name, IsPreopen: true, File: &noopStdoutFile{}}, nil
119	} else if f, ok := w.(*os.File); ok {
120		if f, err := sysfs.NewStdioFile(false, f); err != nil {
121			return nil, err
122		} else {
123			return &FileEntry{Name: name, IsPreopen: true, File: f}, nil
124		}
125	} else {
126		return &FileEntry{Name: name, IsPreopen: true, File: &writerFile{w: w}}, nil
127	}
128}