blob: 5afbe5559c777b9af8f476e7eb90352066a5ed03 [file] [log] [blame] [edit]
// 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])
}
}