mirror of https://gogs.blitter.com/RLabs/xs
				
				
				
			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
					
				| 
						 | 
				
			
			@ -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,11 +117,16 @@ 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.
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,17 +151,31 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexsh.Conn, chaf
 | 
			
		|||
		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
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
	//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 != nil {
 | 
			
		||||
		log.Printf("Command finished with error: %v", err)
 | 
			
		||||
		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 New Issue