~ecs/fsh

d668d0c1390f7f3623f14081b45c39d2f1bd7a48 — Eyal Sawady 4 months ago ecd558e master
Remove any way to interact with the external OS

Also remove external stuff from the TODO.
3 files changed, 0 insertions(+), 135 deletions(-)

M TODO
D commands/external.go
M kernel/state.go
M TODO => TODO +0 -17
@@ 1,13 1,5 @@
- External commands
	- Allow OS commands to interface with state somehow?
		- Serializer/deserializer for state and pipe on fd 3/4?
- External (filesystem-resident) commands
	- Shell scripts
	- JITed Go scripts using go/parser, go/token, more?
		- AST is parser.ParseFile(token.NewFileSet(), path, reader, 0)
	- ELF executables?
		- JIT it into a Go function?
		- How to communicate state?
- Make commands external
	- Do more security checks in kernel/ (eg. SSH, FS ownership/permissions)
- More commands


@@ 15,17 7,8 @@
	- mv
	- ln
	- exec
	- ! exec (can be used to replace the shell, eg. `! exec ./fsh` to reset)
	- ping
	- fsh/sh (subshell)
- Make interface for internal and external files homogeneous
	- Instead of special "external" commands
		- eg. `!load $external` is replaced with `cp !$external .`
	- Convert OS directory into kernel.Filesystem
		- List files/directories at ReadDir()
		- Track that files are OS files
		- On Open(), call os.OpenFile() (or something)
	- Dealt with on the level of openPath/resolveDir
- Command syntax:
	- Conditionals (`||`, `&&`)
	- Sequential operations (`;`, `&`)

D commands/external.go => commands/external.go +0 -114
@@ 1,114 0,0 @@
package commands

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"git.sr.ht/~ecs/fsh/kernel"
)

func init() {
	kernel.Register("!load", external{})
	kernel.Register("!", external{})
}

type external struct{}

func (external) Execute(state *kernel.State, args []string, _io kernel.IO) error {
	switch args[0] {
	case "!load":
		return load(state, args, _io)
	case "!":
		return externalCmd(state, args, _io)
	}
	fmt.Fprintln(_io.Stderr, "external command "+args[0]+" not found")
	return kernel.ErrorCode(-1)
}

func load(state *kernel.State, args []string, _io kernel.IO) error {
	if len(args) < 2 {
		fmt.Fprintln(_io.Stderr,
			"usage: !load <external-file> [<external-file>...]")
		return kernel.ErrorCode(1)
	}
	for _, arg := range args[1:] {
		stat, err := os.Stat(arg)
		if err != nil {
			return err
		}
		src, err := os.Open(arg)
		if err != nil {
			return err
		}
		if stat.IsDir() {
			fileNames, err := src.Readdirnames(0)
			if err != nil {
				return err
			}
			if _, err = state.ResolveDir(strings.Split(arg, "/")); err != nil {
				err = mkdir(state, []string{"mkdir", arg}, _io)
				if err != nil {
					return err
				}
			}
			for _, fileName := range fileNames {
				err = load(state, []string{"load", arg + "/" + fileName}, _io)
				if err != nil {
					return err
				}
			}
		} else {
			err = touch(state, []string{"touch", arg}, _io)
			if err != nil {
				return err
			}
			dst, err := state.OpenPath(arg, 0)
			if err != nil {
				return err
			}
			io.Copy(dst, src)
		}
	}

	return nil
}

func externalCmd(state *kernel.State, args []string, _io kernel.IO) error {
	if len(args) < 2 {
		fmt.Fprintln(_io.Stderr, "usage: ! <external-cmd> [<arg>...]")
		return kernel.ErrorCode(1)
	}

	if args[1] == "cd" {
		if len(args) != 3 {
			fmt.Fprintln(_io.Stderr, "usage: ! cd <dir>")
			return kernel.ErrorCode(1)
		}
		os.Chdir(args[2])
		return kernel.ErrorCode(0)
	}

	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdin = _io.Stdin
	cmd.Stdout = _io.Stdout
	cmd.Stderr = _io.Stderr
	err := cmd.Run()
	if err == nil {
		return kernel.ErrorCode(0)
	}
	if e, ok := err.(*exec.ExitError); ok {
		if e.Success() {
			return kernel.ErrorCode(0)
		}
		if ws, ok := e.Sys().(syscall.WaitStatus); ok {
			return kernel.ErrorCode(ws.ExitStatus())
		}
		return kernel.ErrorCode(1)
	}
	fmt.Fprintln(_io.Stderr, err)
	return kernel.ErrorCode(1)
}

M kernel/state.go => kernel/state.go +0 -4
@@ 157,10 157,6 @@ func (state State) ResolveDir(path []string) (*Filesystem, error) {

// OpenPath is like open(2)
func (state State) OpenPath(path string, opts int) (io.ReadWriteSeeker, error) {
	if path[0] == '!' {
		// OS file
		return os.OpenFile(path[1:], opts, 0666)
	}
	wd, err := state.ResolveDir(Dirname(path))
	if err != nil {
		return nil, fmt.Errorf("open: %w", err)