github.com/tetratelabs/wazero@v1.2.1/internal/sys/stdio.go (about)

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