github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/sys/stdio.go (about) 1 package sys 2 3 import ( 4 "io" 5 "os" 6 7 experimentalsys "github.com/wasilibs/wazerox/experimental/sys" 8 "github.com/wasilibs/wazerox/internal/fsapi" 9 "github.com/wasilibs/wazerox/internal/sysfs" 10 "github.com/wasilibs/wazerox/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. 16 type StdinFile struct { 17 noopStdinFile 18 io.Reader 19 } 20 21 // Read implements the same method as documented on sys.File 22 func (f *StdinFile) Read(buf []byte) (int, experimentalsys.Errno) { 23 n, err := f.Reader.Read(buf) 24 return n, experimentalsys.UnwrapOSError(err) 25 } 26 27 type writerFile struct { 28 noopStdoutFile 29 30 w io.Writer 31 } 32 33 // Write implements the same method as documented on sys.File 34 func (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. 42 type noopStdinFile struct { 43 noopStdioFile 44 } 45 46 // Read implements the same method as documented on sys.File 47 func (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 52 func (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. 61 type noopStdoutFile struct { 62 noopStdioFile 63 } 64 65 // Write implements the same method as documented on sys.File 66 func (noopStdoutFile) Write(buf []byte) (int, experimentalsys.Errno) { 67 return len(buf), 0 // same as io.Discard 68 } 69 70 type noopStdioFile struct { 71 experimentalsys.UnimplementedFile 72 } 73 74 // Stat implements the same method as documented on sys.File 75 func (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 80 func (noopStdioFile) IsDir() (bool, experimentalsys.Errno) { 81 return false, 0 82 } 83 84 // Close implements the same method as documented on sys.File 85 func (noopStdioFile) Close() (errno experimentalsys.Errno) { return } 86 87 // IsNonblock implements the same method as documented on fsapi.File 88 func (noopStdioFile) IsNonblock() bool { 89 return false 90 } 91 92 // SetNonblock implements the same method as documented on fsapi.File 93 func (noopStdioFile) SetNonblock(bool) experimentalsys.Errno { 94 return experimentalsys.ENOSYS 95 } 96 97 // Poll implements the same method as documented on fsapi.File 98 func (noopStdioFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) { 99 return false, experimentalsys.ENOSYS 100 } 101 102 func 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 116 func 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 }