-
Notifications
You must be signed in to change notification settings - Fork 95
/
commands.go
147 lines (128 loc) · 2.88 KB
/
commands.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package main
import (
"fmt"
"io"
"strconv"
"strings"
)
type readLiner interface {
ReadLine() (string, error)
}
type commandContext struct {
args []string
stdin readLiner
stdout, stderr io.Writer
pty bool
user string
}
type command interface {
execute(context commandContext) (uint32, error)
}
var commands = map[string]command{
"sh": cmdShell{},
"true": cmdTrue{},
"false": cmdFalse{},
"echo": cmdEcho{},
"cat": cmdCat{},
"su": cmdSu{},
}
var shellProgram = []string{"sh"}
func executeProgram(context commandContext) (uint32, error) {
if len(context.args) == 0 {
return 0, nil
}
command := commands[context.args[0]]
if command == nil {
_, err := fmt.Fprintf(context.stderr, "%v: command not found\n", context.args[0])
return 127, err
}
return command.execute(context)
}
type cmdShell struct{}
func (cmdShell) execute(context commandContext) (uint32, error) {
var prompt string
if context.pty {
switch context.user {
case "root":
prompt = "# "
default:
prompt = "$ "
}
}
var lastStatus uint32
var line string
var err error
for {
_, err = fmt.Fprint(context.stdout, prompt)
if err != nil {
return lastStatus, err
}
line, err = context.stdin.ReadLine()
if err != nil {
return lastStatus, err
}
args := strings.Fields(line)
if len(args) == 0 {
continue
}
if args[0] == "exit" {
var err error
var status uint64 = uint64(lastStatus)
if len(args) > 1 {
status, err = strconv.ParseUint(args[1], 10, 32)
if err != nil {
status = 255
}
}
return uint32(status), nil
}
newContext := context
newContext.args = args
if lastStatus, err = executeProgram(newContext); err != nil {
return lastStatus, err
}
}
}
type cmdTrue struct{}
func (cmdTrue) execute(context commandContext) (uint32, error) {
return 0, nil
}
type cmdFalse struct{}
func (cmdFalse) execute(context commandContext) (uint32, error) {
return 1, nil
}
type cmdEcho struct{}
func (cmdEcho) execute(context commandContext) (uint32, error) {
_, err := fmt.Fprintln(context.stdout, strings.Join(context.args[1:], " "))
return 0, err
}
type cmdCat struct{}
func (cmdCat) execute(context commandContext) (uint32, error) {
if len(context.args) > 1 {
for _, file := range context.args[1:] {
if _, err := fmt.Fprintf(context.stderr, "%v: %v: No such file or directory\n", context.args[0], file); err != nil {
return 0, err
}
}
return 1, nil
}
var line string
var err error
for err == nil {
line, err = context.stdin.ReadLine()
if err == nil {
_, err = fmt.Fprintln(context.stdout, line)
}
}
return 0, err
}
type cmdSu struct{}
func (cmdSu) execute(context commandContext) (uint32, error) {
newContext := context
newContext.user = "root"
if len(context.args) > 1 {
newContext.user = context.args[1]
}
newContext.args = shellProgram
return executeProgram(newContext)
}