-
Notifications
You must be signed in to change notification settings - Fork 0
/
io.go
192 lines (163 loc) · 4.47 KB
/
io.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package main
import (
"io"
"os"
"os/exec"
)
func wrapStdin(proc *exec.Cmd, stdin io.Reader, done chan bool) {
logger.Println("Wrapping stdin")
pipe, err := proc.StdinPipe()
fatal_if(err)
go inLoop(pipe, stdin, done)
}
// for protocol v2.0
func wrapStdin2(proc *exec.Cmd, stdin io.Reader, done chan bool) {
logger.Println("Wrapping stdin")
pipe, err := proc.StdinPipe()
fatal_if(err)
go inLoop2(pipe, proc, stdin, done)
}
func wrapStdout(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) {
logger.Printf("Wrapping stdout with %v\n", opt)
pipe, err := proc.StdoutPipe()
fatal_if(err)
go outLoop(pipe, outstream, opt, done)
}
func wrapStderr(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) {
logger.Printf("Wrapping stderr with %v\n", opt)
pipe, err := proc.StderrPipe()
fatal_if(err)
go outLoop(pipe, outstream, opt, done)
}
///
func inLoop(pipe io.WriteCloser, stdin io.Reader, done chan bool) {
buf := make([]byte, 2)
logger.Println("Entering stdin loop")
done <- true
for {
bytes_read, read_err := io.ReadFull(stdin, buf)
if read_err == io.EOF && bytes_read == 0 {
break
}
fatal_if(read_err)
length := read16_be(buf)
logger.Printf("in: packet length = %v\n", length)
if length == 0 {
// this is how Porcelain signals EOF from Elixir
break
}
bytes_written, write_err := io.CopyN(pipe, stdin, int64(length))
logger.Printf("in: copied %v bytes\n", bytes_written)
fatal_if(write_err)
}
pipe.Close()
done <- true
}
func inLoop2(pipe io.WriteCloser, proc *exec.Cmd, stdin io.Reader, done chan bool) {
buf := make([]byte, 3)
logger.Println("Entering stdin loop")
done <- true
loop: for {
bytes_read, read_err := io.ReadFull(stdin, buf[:2])
if read_err == io.EOF && bytes_read == 0 {
break
}
fatal_if(read_err)
length := read16_be(buf[:2])
logger.Printf("in: packet length = %v\n", length)
if length == 0 {
// this is how Porcelain signals EOF from Elixir
break
}
_, read_err = io.ReadFull(stdin, buf[2:])
fatal_if(read_err)
data_type := buf[2]
switch data_type {
case 0: // input data
bytes_written, write_err := io.CopyN(pipe, stdin, int64(length)-1)
logger.Printf("in: copied %v bytes\n", bytes_written)
fatal_if(write_err)
case 1: // signal
bytes_read, read_err = io.ReadFull(stdin, buf[2:])
fatal_if(read_err)
sig := buf[2]
switch sig {
case 128:
sig_err := proc.Process.Signal(os.Interrupt)
fatal_if(sig_err)
case 129:
sig_err := proc.Process.Signal(os.Kill)
fatal_if(sig_err)
default:
sig_err := proc.Process.Signal(makeSignal(sig))
fatal_if(sig_err)
}
break loop
default:
logger.Panicf("unhandled input marker: '%v'\n", buf[2])
}
}
pipe.Close()
done <- true
}
///
// Maximum buffer size for protocol 1.0 is 2 + 2^16-1 - 1
//
// * 2 is the packet length
// * 2^16-1 is the maximum amount of data that can be encoded in a
// 2-byte-length packet
// * 1 byte is used for framing, so it has to be included in the total length
//
var outBuf [1<<16]byte
func outLoop(pipe io.ReadCloser, outstream io.Writer, char byte, done chan bool) {
buf := outBuf
buf[2] = char
logger.Printf("Entering out loop with %v\n", char)
done <- true
for {
bytes_read, read_err := pipe.Read(buf[3:])
logger.Printf("out: read bytes: %v\n", bytes_read)
if bytes_read > 0 {
write16_be(buf[:2], bytes_read+1)
bytes_written, write_err := outstream.Write(buf[:2+bytes_read+1])
logger.Printf("out: written bytes: %v\n", bytes_written)
fatal_if(write_err)
}
if read_err == io.EOF || bytes_read == 0 {
// From io.Reader docs:
//
// Implementations of Read are discouraged from returning a zero
// byte count with a nil error, and callers should treat that
// situation as a no-op.
//
// In this case it appears that 0 bytes may sometimes be returned
// indefinitely. Therefore we close the pipe.
if read_err == io.EOF {
logger.Println("Encountered EOF when reading from stdout")
} else {
logger.Println("Read 0 bytes with no error")
}
break
}
if read_err != nil {
switch read_err.(type) {
case *os.PathError:
// known error
break
default:
fatal(read_err)
}
}
}
pipe.Close()
done <- true
}
// Unpack the 2-byte integer stored in big endian order
func read16_be(data []byte) uint16 {
return uint16(data[0]) << 8 | uint16(data[1])
}
// Pack a 2-byte integer in big endian order
func write16_be(data []byte, num int) {
data[0] = byte(num >> 8)
data[1] = byte(num)
}