github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/sys/stdio.go (about)

     1  package sys
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  
     7  	experimentalsys "github.com/bananabytelabs/wazero/experimental/sys"
     8  	"github.com/bananabytelabs/wazero/internal/fsapi"
     9  	"github.com/bananabytelabs/wazero/internal/sysfs"
    10  	"github.com/bananabytelabs/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.
    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  }