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
|
// Run a command (via default shell) as a specific user
|
||||||
//
|
//
|
||||||
// Uses ptys to support commands which expect a terminal.
|
// 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)
|
u, _ := user.Lookup(who)
|
||||||
var uid, gid uint32
|
var uid, gid uint32
|
||||||
fmt.Sscanf(u.Uid, "%d", &uid)
|
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||||
|
@ -117,11 +117,16 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaf
|
||||||
// Start the command with a pty.
|
// Start the command with a pty.
|
||||||
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err, 0
|
||||||
}
|
}
|
||||||
// Make sure to close the pty at the end.
|
// Make sure to close the pty at the end.
|
||||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
|
||||||
|
log.Printf("[%s]\n", cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Command finished with error: %v", err)
|
||||||
|
} else {
|
||||||
|
|
||||||
// Watch for term resizes
|
// Watch for term resizes
|
||||||
go func() {
|
go func() {
|
||||||
for sz := range conn.WinCh {
|
for sz := range conn.WinCh {
|
||||||
|
@ -146,17 +151,31 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaf
|
||||||
defer conn.ShutdownChaff()
|
defer conn.ShutdownChaff()
|
||||||
|
|
||||||
// ..and the pty to stdout.
|
// ..and the pty to stdout.
|
||||||
|
go func() {
|
||||||
_, e := io.Copy(conn, ptmx)
|
_, e := io.Copy(conn, ptmx)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
log.Printf("** pty->stdout ended **\n")
|
log.Printf("** pty->stdout ended **\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
//err = c.Run() // returns when c finishes.
|
// The above io.Copy() will exit when the command attached
|
||||||
|
// to the pty exits
|
||||||
|
|
||||||
log.Printf("[%s]\n", cmd)
|
if err := c.Wait(); err != nil {
|
||||||
if err != nil {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
log.Printf("Command finished with error: %v", err)
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
@ -303,11 +322,15 @@ func main() {
|
||||||
hname := goutmp.GetHost(addr.String())
|
hname := goutmp.GetHost(addr.String())
|
||||||
|
|
||||||
log.Printf("[Running command for [%s@%s]]\n", rec.who, hname)
|
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;
|
// Returned hopefully via an EOF or exit/logout;
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
rec.op[0] = 0
|
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' {
|
} else if rec.op[0] == 's' {
|
||||||
// Interactive session
|
// Interactive session
|
||||||
addr := c.RemoteAddr()
|
addr := c.RemoteAddr()
|
||||||
|
@ -317,11 +340,15 @@ func main() {
|
||||||
utmpx := goutmp.Put_utmp(string(rec.who), hname)
|
utmpx := goutmp.Put_utmp(string(rec.who), hname)
|
||||||
defer func() { goutmp.Unput_utmp(utmpx) }()
|
defer func() { goutmp.Unput_utmp(utmpx) }()
|
||||||
goutmp.Put_lastlog_entry("hkexsh", string(rec.who), hname)
|
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;
|
// Returned hopefully via an EOF or exit/logout;
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
rec.op[0] = 0
|
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 {
|
} else {
|
||||||
log.Println("[Bad cmdSpec]")
|
log.Println("[Bad cmdSpec]")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue