-
Notifications
You must be signed in to change notification settings - Fork 2
/
fanos.go
103 lines (86 loc) · 2.03 KB
/
fanos.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package main
import (
"bufio"
"bytes"
"context"
"errors"
"flag"
"io"
"log"
"os"
"os/exec"
"strconv"
"syscall"
)
var (
fanosShellPath = flag.String("oils_path", "/usr/local/bin/osh", "Path to Oils interpreter (https://oils-for-unix.org/)")
)
type FANOSShell struct {
cmd *exec.Cmd
socket *os.File
in, out, err *os.File
}
func NewFANOSShell() (*FANOSShell, error) {
shell := &FANOSShell{}
shell.cmd = exec.Command(*fanosShellPath, "--headless")
fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, err
}
shell.socket = os.NewFile(uintptr(fds[0]), "fanos_client")
server := os.NewFile(uintptr(fds[1]), "fanos_server")
shell.cmd.Stdin = server
shell.cmd.Stdout = server
shell.cmd.Stderr = os.Stderr
return shell, shell.cmd.Start()
}
func (s *FANOSShell) StdIO(in, out, err *os.File) error {
// Save these for the next Run
s.in, s.out, s.err = in, out, err
if s.in == nil {
s.in, _ = os.Open(os.DevNull)
}
if s.out == nil {
s.out, _ = os.Open(os.DevNull)
}
if s.err == nil {
s.err, _ = os.Open(os.DevNull)
}
return nil
}
// Run calls the FANOS EVAL method
func (s *FANOSShell) Run(ctx context.Context, r io.Reader) error {
rights := syscall.UnixRights(int(s.in.Fd()), int(s.out.Fd()), int(s.err.Fd()))
var buf bytes.Buffer
buf.WriteString("EVAL ")
_, err := io.Copy(&buf, r)
if err != nil {
return err
}
_, err = s.socket.Write([]byte(strconv.Itoa(buf.Len()) + ":"))
if err != nil {
return err
}
err = syscall.Sendmsg(int(s.socket.Fd()), buf.Bytes(), rights, nil, 0)
if err != nil {
return err
}
_, err = s.socket.Write([]byte(","))
if err != nil {
return err
}
// TODO: Actually read netstring instead of reading until ','
sockReader := bufio.NewReader(s.socket)
msg, err := sockReader.ReadString(',')
if err != nil {
return err
}
log.Println(msg)
return nil
}
func (s *FANOSShell) Dir() string {
return ""
}
func (b *FANOSShell) Complete(ctx context.Context, cmd io.Reader) ([]string, error) {
return nil, errors.New("unimplemented")
}