245 lines
4.2 KiB
Go
245 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// Copyright 2012 the u-root Authors. All rights reserved
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
type (
|
|
chunk struct {
|
|
id int
|
|
buf []byte
|
|
}
|
|
)
|
|
|
|
const (
|
|
echo = false
|
|
)
|
|
|
|
func comprefix(lines [][]byte) (int, int) {
|
|
var line0 []byte
|
|
len0 := 0
|
|
for _, ln := range lines {
|
|
if len(ln) > len0 {
|
|
line0 = ln
|
|
len0 = len(ln)
|
|
}
|
|
}
|
|
ndiff := 0
|
|
for i := range line0 {
|
|
nshort := 0
|
|
for _, ln := range lines {
|
|
if i >= len(ln) {
|
|
nshort++
|
|
} else if ln[i] != line0[i] {
|
|
ndiff++
|
|
}
|
|
}
|
|
if ndiff > 0 {
|
|
return i, ndiff
|
|
}
|
|
if nshort > 0 {
|
|
return i, ndiff
|
|
}
|
|
}
|
|
return len(line0), ndiff
|
|
}
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) < 2 {
|
|
os.Exit(1)
|
|
}
|
|
|
|
var conns []*net.TCPConn
|
|
for _, a := range os.Args[1:] {
|
|
port := "1522"
|
|
if !strings.Contains(a, ":") {
|
|
a = a + ":" + port
|
|
}
|
|
|
|
tcpdst, err := net.ResolveTCPAddr("tcp", a)
|
|
if err != nil {
|
|
fmt.Printf("%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
c, err := net.DialTCP("tcp", nil, tcpdst)
|
|
if err != nil {
|
|
fmt.Printf("%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
conns = append(conns, c)
|
|
}
|
|
|
|
if terminal.IsTerminal(0) {
|
|
unraw, err := terminal.MakeRaw(0)
|
|
if err != nil {
|
|
fmt.Printf("%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer terminal.Restore(0, unraw)
|
|
|
|
sigc := make(chan os.Signal, 1)
|
|
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
|
|
go func() {
|
|
for _ = range sigc {
|
|
terminal.Restore(0, unraw)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
}
|
|
|
|
go func() {
|
|
buf := make([]byte, 256)
|
|
for {
|
|
n, err := os.Stdin.Read(buf)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
fmt.Printf("%v\n", err)
|
|
}
|
|
for _, c := range conns {
|
|
c.CloseWrite() // I know, I know.. but it is handy sometimes.
|
|
}
|
|
break
|
|
}
|
|
buf = buf[0:n]
|
|
for i := range buf {
|
|
if echo {
|
|
fmt.Printf("%s", string(buf[i]))
|
|
}
|
|
if buf[i] == '\r' {
|
|
buf[i] = '\n'
|
|
if echo {
|
|
fmt.Printf("\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, c := range conns {
|
|
if _, err := c.Write(buf); err != nil {
|
|
fmt.Printf("%v\n", err)
|
|
break
|
|
}
|
|
}
|
|
|
|
}
|
|
}()
|
|
|
|
waitc := make(chan int)
|
|
chunkc := make(chan chunk)
|
|
for id, c := range conns {
|
|
go func(id int, c *net.TCPConn) {
|
|
for {
|
|
buf := make([]byte, 256)
|
|
n, err := c.Read(buf)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
fmt.Printf("%v\n", err)
|
|
}
|
|
c.Close()
|
|
waitc <- 1
|
|
break
|
|
}
|
|
if n == 0 {
|
|
continue
|
|
}
|
|
chunkc <- chunk{id, buf[0:n]}
|
|
}
|
|
}(id, c)
|
|
}
|
|
|
|
act := make([][]byte, len(conns))
|
|
|
|
go func() {
|
|
oldpfix := 0
|
|
insync := true
|
|
for {
|
|
chunk, more := <-chunkc
|
|
if !more {
|
|
break
|
|
}
|
|
id := chunk.id
|
|
for _, b := range chunk.buf {
|
|
act[id] = append(act[id], b)
|
|
if false && b == '\b' {
|
|
act[id] = append(act[id], ' ')
|
|
act[id] = append(act[id], '\b')
|
|
}
|
|
}
|
|
|
|
/* streams agree, so spit out all they agree on */
|
|
if insync {
|
|
newpfix, ndiff := comprefix(act)
|
|
if ndiff == 0 && newpfix > oldpfix {
|
|
os.Stdout.Write(act[id][oldpfix:newpfix])
|
|
for {
|
|
i := bytes.IndexByte(act[id][:newpfix], '\n')
|
|
if i == -1 {
|
|
break
|
|
}
|
|
for id := range act {
|
|
clen := copy(act[id], act[id][i+1:])
|
|
act[id] = act[id][0:clen]
|
|
}
|
|
newpfix = newpfix - (i + 1)
|
|
}
|
|
oldpfix = newpfix
|
|
}
|
|
|
|
if ndiff > 0 {
|
|
insync = false
|
|
}
|
|
}
|
|
|
|
/*
|
|
* streams disagree, first to newline completes from oldpfix on.
|
|
* the outer loop is just for transitioning from insync, where
|
|
* a leader may be several lines ahead
|
|
*/
|
|
if !insync {
|
|
for id := range act {
|
|
for {
|
|
i := bytes.IndexByte(act[id], '\n')
|
|
if i != -1 {
|
|
if oldpfix == 0 {
|
|
fmt.Fprintf(os.Stdout, "[%d]", id)
|
|
}
|
|
os.Stdout.Write(act[id][oldpfix : i+1])
|
|
clen := copy(act[id], act[id][i+1:])
|
|
act[id] = act[id][0:clen]
|
|
oldpfix = 0
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
/* try for prompt (or sheer luck) */
|
|
if _, ndiff := comprefix(act); ndiff == 0 {
|
|
insync = true
|
|
oldpfix = 0
|
|
}
|
|
}
|
|
}
|
|
waitc <- 1
|
|
}()
|
|
|
|
for _ = range conns {
|
|
<-waitc
|
|
}
|
|
close(chunkc)
|
|
<-waitc
|
|
}
|