Compare commits

...

7 Commits

Author SHA1 Message Date
Jeff Carr 7e68be7419 oops. had the binary
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-10 17:03:25 -07:00
Jeff Carr 78c7f261f4 fix the example import path
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-10 17:02:29 -07:00
Jeff Carr 59c9bf88ca fix stderr (stdout was wrong too)
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-10 02:46:27 +00:00
Jeff Carr 954470c34c correct stdin handling. stderr might still be broken
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-10 01:54:36 +00:00
Jeff Carr 8a5c9922e4 move to smarter log() and quiet spew()
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-10 00:24:25 +00:00
Jeff Carr f1d7a816be add total byte counts to track accurate flushing of buffers
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-09 23:48:53 +00:00
Jeff Carr 026c59bc5c allow stdout redirection
Signed-off-by: Jeff Carr <jcarr@wit.com>
2019-05-09 22:20:26 +00:00
4 changed files with 166 additions and 51 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
example1/example1

3
example1/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
go build
./example1

44
example1/main.go Normal file
View File

@ -0,0 +1,44 @@
package main
/*
import "log"
import "reflect"
*/
import "os"
// import "github.com/davecgh/go-spew/spew"
import "git.wit.com/wit/shell"
func main() {
shell.Run("ls /tmp")
shell.Run("ping -c 3 localhost")
// slow down the polling to every 2 seconds
shell.SetDelayInMsec(2000)
shell.Run("ping -c 4 localhost")
// capture ping output into a file
fout, _ := os.Create("/tmp/example1.ping.stdout")
ferr, _ := os.Create("/tmp/example1.ping.stderr")
shell.SetStdout(fout)
shell.SetStderr(ferr)
shell.Run("ping -c 5 localhost")
// turn out process exit debugging
shell.SpewOn()
fout, _ = os.Create("/tmp/example1.fail.stdout")
ferr, _ = os.Create("/tmp/example1.fail.stderr")
shell.SetStdout(fout)
shell.SetStderr(ferr)
// TODO: this might not be working
// check error handling
shell.Run("ls /tmpthisisnothere")
}

169
shell.go
View File

@ -1,7 +1,6 @@
package shell package shell
import "fmt" // import "log"
import "log"
import "strings" import "strings"
import "time" import "time"
import "os" import "os"
@ -10,19 +9,50 @@ import "bufio"
import "github.com/davecgh/go-spew/spew" import "github.com/davecgh/go-spew/spew"
import "github.com/svent/go-nbreader" import "github.com/svent/go-nbreader"
import log "github.com/sirupsen/logrus"
// import "github.com/wercker/journalhook"
var shellStdout *os.File
var shellStderr *os.File
var spewOn bool = false
var msecDelay int = 20 // number of milliseconds to delay between reads with no data
func Script(cmds string) int { func Script(cmds string) int {
// split on new lines (while we are at it, handle stupid windows text files // split on new lines (while we are at it, handle stupid windows text files
lines := strings.Split(strings.Replace(cmds, "\r\n", "\n", -1), "\n") lines := strings.Split(strings.Replace(cmds, "\r\n", "\n", -1), "\n")
for _, line := range lines { for _, line := range lines {
line = strings.TrimSpace(line) // this is like 'chomp' in perl line = strings.TrimSpace(line) // this is like 'chomp' in perl
fmt.Println("LINE:", line) log.Println("LINE:", line)
time.Sleep(1) time.Sleep(1)
Run(line) Run(line)
} }
return 0 return 0
} }
/*
func UseJournalctl() {
journalhook.Enable()
}
*/
func SpewOn() {
spewOn = true
}
func SetDelayInMsec(msecs int) {
msecDelay = msecs
}
func SetStdout(newout *os.File) {
shellStdout = newout
}
func SetStderr(newerr *os.File) {
shellStderr = newerr
}
func Run(cmdline string) int { func Run(cmdline string) int {
log.Println("START " + cmdline) log.Println("START " + cmdline)
@ -42,71 +72,74 @@ func Run(cmdline string) int {
} }
process := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...) process := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
stdout, _ := process.StdoutPipe() pstdout, _ := process.StdoutPipe()
stderr, _ := process.StderrPipe() pstderr, _ := process.StderrPipe()
if (spewOn) {
spew.Dump(pstdout)
}
process.Start() process.Start()
f := bufio.NewWriter(os.Stdout) if (shellStdout == nil) {
shellStdout = os.Stdout
}
newreader := bufio.NewReader(stdout) f := bufio.NewWriter(shellStdout)
newreader := bufio.NewReader(pstdout)
nbr := nbreader.NewNBReader(newreader, 1024) nbr := nbreader.NewNBReader(newreader, 1024)
newerrreader := bufio.NewReader(stderr) tmp := bufio.NewReader(pstderr)
nbrerr := nbreader.NewNBReader(newerrreader, 1024) go NonBlockingReader(tmp, shellStderr)
for { totalCount := 0
time.Sleep(2 * time.Millisecond) // only check the buffer 500 times a second
var dead bool = false
for (dead == false) {
time.Sleep(time.Duration(msecDelay) * time.Millisecond) // only check the buffer 500 times a second
// log.Println("sleep done") // log.Println("sleep done")
oneByte := make([]byte, 1024) var empty bool = false
count, err := nbr.Read(oneByte) // tight loop that reads 1K at a time until buffer is empty
for (empty == false) {
oneByte := make([]byte, 1024)
count, err := nbr.Read(oneByte)
totalCount += count
if (err != nil) { if (err != nil) {
// log.Println("Read() count = ", count, "err = ", err) log.Println("Read() count = ", count, "err = ", err)
oneByte = make([]byte, 1024) oneByte = make([]byte, 1024)
count, err = nbr.Read(oneByte) count, err = nbr.Read(oneByte)
f.Write([]byte(string(oneByte))) log.Println("STDOUT: count = ", count)
f.Flush() f.Write(oneByte[0:count])
} f.Flush()
f.Write([]byte(string(oneByte))) empty = true
f.Flush() dead = true
}
oneByte = make([]byte, 1024) // f.Write([]byte(string(oneByte)))
count, err = nbrerr.Read(oneByte) if (count == 0) {
empty = true
if (err != nil) { } else {
oneByte = make([]byte, 1024) log.Println("STDOUT: count = ", count)
count, err = nbrerr.Read(oneByte) f.Write(oneByte[0:count])
f.Write([]byte(string(oneByte))) f.Flush()
f.Flush()
log.Println("Read() count = ", count, "err = ", err)
spew.Dump(process.Process)
spew.Dump(process.ProcessState)
err := process.Wait()
if err != nil {
spew.Dump(err.(*exec.ExitError))
spew.Dump(process.ProcessState)
stuff := err.(*exec.ExitError)
log.Println("ERROR ", stuff)
log.Println("END ", cmdline)
return -1
} }
log.Println("END ", cmdline)
return 0
} else {
f.Write([]byte(string(oneByte)))
f.Flush()
} }
// spew.Dump(reflect.ValueOf(cmd.Process).Elem()) if (totalCount != 0) {
log.Println("STDOUT: totalCount = ", totalCount)
totalCount = 0
}
} }
err := process.Wait() err := process.Wait()
if err != nil { if err != nil {
spew.Dump(err.(*exec.ExitError)) if (spewOn) {
spew.Dump(process.ProcessState) spew.Dump(err.(*exec.ExitError))
spew.Dump(process.ProcessState)
}
stuff := err.(*exec.ExitError) stuff := err.(*exec.ExitError)
log.Println("ERROR ", stuff) log.Println("ERROR ", stuff)
log.Println("END ", cmdline) log.Println("END ", cmdline)
@ -122,3 +155,37 @@ func Daemon(cmdline string, timeout time.Duration) int {
time.Sleep(timeout) time.Sleep(timeout)
} }
} }
// pass in two file handles (1 read, 1 write)
func NonBlockingReader(buffReader *bufio.Reader, writeFileHandle *os.File) {
// newreader := bufio.NewReader(readFileHandle)
// create a nonblocking GO reader
nbr := nbreader.NewNBReader(buffReader, 1024)
for {
// defer buffReader.Close()
// defer writeFileHandle.Flush()
defer writeFileHandle.Close()
totalCount := 0
for {
oneByte := make([]byte, 1024)
count, err := nbr.Read(oneByte)
if (err != nil) {
log.Println("count, err =", count, err)
return
}
totalCount += count
if (count == 0) {
time.Sleep(time.Duration(msecDelay) * time.Millisecond) // without this delay this will peg the CPU
if (totalCount != 0) {
log.Println("STDERR: totalCount = ", totalCount)
totalCount = 0
}
} else {
log.Println("STDERR: count = ", count)
writeFileHandle.Write(oneByte[0:count])
}
}
}
}