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
|
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"strconv"
"syscall"
"github.com/google/goterm/term"
)
// Constants for IO and greeting.
const (
file = "/tmp/goscript-" // file the base filename of the logfile
bufSz = 8192 // BUFSZ size of the buffers used for IO
Welcome = "Examplescript up and running" // Welcome Welcome message printed when the application starts
)
// A version of the UNIX command "script" with no errchecking buffered IO or cool features
func main() {
// Get PTYs up
pty, _ := term.OpenPTY()
defer pty.Close()
// Save the current Stdin attributes
backupTerm, _ := term.Attr(os.Stdin)
// Copy attributes
myTerm := backupTerm
// Change the Stdin term to RAW so we get everything
myTerm.Raw()
myTerm.Set(os.Stdin)
// Set the backup attributes on our PTY slave
backupTerm.Set(pty.Slave)
// Make sure we'll get the attributes back when exiting
defer backupTerm.Set(os.Stdin)
// Get the snooping going
go Snoop(pty)
// Handle changes in termsize
sig := make(chan os.Signal, 2)
// Notify if window size changes or shell dies
signal.Notify(sig, syscall.SIGWINCH, syscall.SIGCLD)
// Start up the slaveshell
cmd := exec.Command(os.Getenv("SHELL"), "")
cmd.Stdin, cmd.Stdout, cmd.Stderr = pty.Slave, pty.Slave, pty.Slave
cmd.Args = nil
cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
Setctty: true}
cmd.Start()
// Get the initial winsize
myTerm.Winsz(os.Stdin)
myTerm.Winsz(pty.Slave)
// If the termsize changes , propagate to our PTY
for {
switch <-sig {
case syscall.SIGWINCH:
myTerm.Winsz(os.Stdin)
myTerm.Setwinsz(pty.Slave)
default:
return
}
}
}
// Snoop gets the script file up and running and kicks of the reader and writer functions
func Snoop(pty *term.PTY) {
// Just something that might be a bit uniqe
pid := os.Getpid()
pidcol, _ := term.NewColor256(strconv.Itoa(pid), strconv.Itoa(pid%256), "")
greet := fmt.Sprintln("\n", term.Green(Welcome), " pid:", pidcol,
" file:", term.Yellow(file+strconv.Itoa(pid)+"\n"))
// Our logfile
file, _ := os.Create(file + strconv.Itoa(pid))
os.Stdout.Write([]byte(greet))
go reader(pty.Master, file)
go writer(pty.Master)
}
// reader reads from master and writes to file and stdout
func reader(master *os.File, log *os.File) {
var buf = make([]byte, bufSz)
defer func() {
log.Sync()
log.Close()
}()
for {
nr, _ := master.Read(buf)
os.Stdout.Write(buf[:nr])
log.Write(buf[:nr])
}
}
// writer reads from stdin and writes to master
func writer(master *os.File) {
var buf = make([]byte, bufSz)
for {
nr, _ := os.Stdin.Read(buf)
master.Write(buf[:nr])
}
}
|