mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
Basic server-side recording of exitStatus of pty(cmd).
TODO: sending of exitStatus to client and client handling of said packet via a WritePacket() with unique existStatus op.
This commit is contained in:
parent
aa48314ee9
commit
c64797f2d9
1 changed files with 65 additions and 38 deletions
|
@ -80,7 +80,7 @@ func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
|
|||
// Run a command (via default shell) as a specific user
|
||||
//
|
||||
// Uses ptys to support commands which expect a terminal.
|
||||
func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaffing bool) (err error) {
|
||||
func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaffing bool) (err error, exitStatus int) {
|
||||
u, _ := user.Lookup(who)
|
||||
var uid, gid uint32
|
||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||
|
@ -117,46 +117,65 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaf
|
|||
// Start the command with a pty.
|
||||
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
||||
if err != nil {
|
||||
return err
|
||||
return err, 0
|
||||
}
|
||||
// Make sure to close the pty at the end.
|
||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||
|
||||
// Watch for term resizes
|
||||
go func() {
|
||||
for sz := range conn.WinCh {
|
||||
log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
|
||||
pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols})
|
||||
}
|
||||
}()
|
||||
|
||||
// Copy stdin to the pty.. (bgnd goroutine)
|
||||
go func() {
|
||||
_, e := io.Copy(ptmx, conn)
|
||||
if e != nil {
|
||||
log.Printf("** std->pty ended **\n")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if chaffing {
|
||||
conn.EnableChaff()
|
||||
}
|
||||
defer conn.DisableChaff()
|
||||
defer conn.ShutdownChaff()
|
||||
|
||||
// ..and the pty to stdout.
|
||||
_, e := io.Copy(conn, ptmx)
|
||||
if e != nil {
|
||||
log.Printf("** pty->stdout ended **\n")
|
||||
return
|
||||
}
|
||||
|
||||
//err = c.Run() // returns when c finishes.
|
||||
|
||||
log.Printf("[%s]\n", cmd)
|
||||
if err != nil {
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
} else {
|
||||
|
||||
// Watch for term resizes
|
||||
go func() {
|
||||
for sz := range conn.WinCh {
|
||||
log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
|
||||
pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols})
|
||||
}
|
||||
}()
|
||||
|
||||
// Copy stdin to the pty.. (bgnd goroutine)
|
||||
go func() {
|
||||
_, e := io.Copy(ptmx, conn)
|
||||
if e != nil {
|
||||
log.Printf("** std->pty ended **\n")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if chaffing {
|
||||
conn.EnableChaff()
|
||||
}
|
||||
defer conn.DisableChaff()
|
||||
defer conn.ShutdownChaff()
|
||||
|
||||
// ..and the pty to stdout.
|
||||
go func() {
|
||||
_, e := io.Copy(conn, ptmx)
|
||||
if e != nil {
|
||||
log.Printf("** pty->stdout ended **\n")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// The above io.Copy() will exit when the command attached
|
||||
// to the pty exits
|
||||
|
||||
if err := c.Wait(); err != nil {
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus = status.ExitStatus()
|
||||
log.Printf("Exit Status: %d", exitStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -303,11 +322,15 @@ func main() {
|
|||
hname := goutmp.GetHost(addr.String())
|
||||
|
||||
log.Printf("[Running command for [%s@%s]]\n", rec.who, hname)
|
||||
runShellAs(string(rec.who), string(rec.cmd), false, conn, chaffEnabled)
|
||||
runErr, cmdStatus := runShellAs(string(rec.who), string(rec.cmd), false, conn, chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.op[0] = 0
|
||||
log.Printf("[Command completed for [%s@%s]\n", rec.who, hname)
|
||||
if runErr != nil {
|
||||
log.Printf("[Error spawning cmd for %s@%s]\n", rec.who, hname)
|
||||
} else {
|
||||
log.Printf("[Command completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
}
|
||||
} else if rec.op[0] == 's' {
|
||||
// Interactive session
|
||||
addr := c.RemoteAddr()
|
||||
|
@ -317,11 +340,15 @@ func main() {
|
|||
utmpx := goutmp.Put_utmp(string(rec.who), hname)
|
||||
defer func() { goutmp.Unput_utmp(utmpx) }()
|
||||
goutmp.Put_lastlog_entry("hkexsh", string(rec.who), hname)
|
||||
runShellAs(string(rec.who), string(rec.cmd), true, conn, chaffEnabled)
|
||||
runErr, cmdStatus := runShellAs(string(rec.who), string(rec.cmd), true, conn, chaffEnabled)
|
||||
// Returned hopefully via an EOF or exit/logout;
|
||||
// Clear current op so user can enter next, or EOF
|
||||
rec.op[0] = 0
|
||||
log.Printf("[Exiting shell for [%s@%s]]\n", rec.who, hname)
|
||||
if runErr != nil {
|
||||
log.Printf("[Error spawning shell for %s@%s]\n", rec.who, hname)
|
||||
} else {
|
||||
log.Printf("[Shell completed for %s@%s, status %d]\n", rec.who, hname, cmdStatus)
|
||||
}
|
||||
} else {
|
||||
log.Println("[Bad cmdSpec]")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue