mirror of https://gogs.blitter.com/RLabs/xs
WIP tarpipe construction: server-side, TODOL client-side, -r behaviour
This commit is contained in:
parent
5859131678
commit
0b9b8b8320
|
@ -107,8 +107,10 @@ func doCopyMode(conn *hkexnet.Conn, remoteDest bool, files string, recurs bool,
|
||||||
// runTarSrc(), runTarSink() ?
|
// runTarSrc(), runTarSink() ?
|
||||||
if remoteDest {
|
if remoteDest {
|
||||||
fmt.Println("local files:", files, "remote filepath:", string(rec.cmd))
|
fmt.Println("local files:", files, "remote filepath:", string(rec.cmd))
|
||||||
|
fmt.Fprintf(conn, "copyMode remoteDest TODO\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("remote filepath:", string(rec.cmd), "local files:", files)
|
fmt.Println("remote filepath:", string(rec.cmd), "local files:", files)
|
||||||
|
fmt.Fprintf(conn, "copyMode localDest TODO\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +265,7 @@ func main() {
|
||||||
copyDst = tmpPath
|
copyDst = tmpPath
|
||||||
fileArgs = string(copySrc)
|
fileArgs = string(copySrc)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(otherArgs) == 0 {
|
if len(otherArgs) == 0 {
|
||||||
log.Fatal("ERROR: Must specify dest path for copy")
|
log.Fatal("ERROR: Must specify dest path for copy")
|
||||||
} else if len(otherArgs) == 1 {
|
} else if len(otherArgs) == 1 {
|
||||||
|
|
|
@ -37,48 +37,140 @@ type cmdSpec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------- */
|
/* -------------------------------------------------------------- */
|
||||||
|
// Perform a client->server copy
|
||||||
/*
|
func runClientToServerCopyAs(who string, conn hkexnet.Conn, destPath string, chaffing bool) (err error, exitStatus int) {
|
||||||
// Run a command (via os.exec) as a specific user
|
|
||||||
//
|
|
||||||
// Uses ptys to support commands which expect a terminal.
|
|
||||||
func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
|
|
||||||
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)
|
||||||
fmt.Sscanf(u.Gid, "%d", &gid)
|
fmt.Sscanf(u.Gid, "%d", &gid)
|
||||||
fmt.Println("uid:", uid, "gid:", gid)
|
log.Println("uid:", uid, "gid:", gid)
|
||||||
|
|
||||||
args := strings.Split(cmd, " ")
|
// Need to clear server's env and set key vars of the
|
||||||
arg0 := args[0]
|
// target user. This isn't perfect (TERM doesn't seem to
|
||||||
args = args[1:]
|
// work 100%; ANSI/xterm colour isn't working even
|
||||||
c := exec.Command(arg0, args...)
|
// if we set "xterm" or "ansi" here; and line count
|
||||||
|
// reported by 'stty -a' defaults to 24 regardless
|
||||||
|
// of client shell window used to run client.
|
||||||
|
// Investigate -- rlm 2018-01-26)
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("HOME", u.HomeDir)
|
||||||
|
os.Setenv("TERM", "vt102") // TODO: server or client option?
|
||||||
|
|
||||||
|
var c *exec.Cmd
|
||||||
|
cmdName := "/bin/tar"
|
||||||
|
// NOTE the lack of quotes around --xform option's sed expression.
|
||||||
|
// When args are passed in exec() format, no quoting is required
|
||||||
|
// (as this isn't input from a shell) (right? -rlm 20180823)
|
||||||
|
cmdArgs := []string{"-xzv", "-C", destPath, `--xform=s#.*/\(.*\)#\1#`}
|
||||||
|
c = exec.Command(cmdName, cmdArgs...)
|
||||||
|
|
||||||
|
//If os.Clearenv() isn't called by server above these will be seen in the
|
||||||
|
//client's session env.
|
||||||
|
//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who}
|
||||||
|
c.Dir = u.HomeDir
|
||||||
c.SysProcAttr = &syscall.SysProcAttr{}
|
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||||
c.Stdin = conn
|
c.Stdin = conn
|
||||||
c.Stdout = conn
|
c.Stdout = conn
|
||||||
c.Stderr = conn
|
c.Stderr = conn
|
||||||
|
|
||||||
// Start the command with a pty.
|
// Start the command (no pty)
|
||||||
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
log.Printf("[%v %v]\n", cmdName, cmdArgs)
|
||||||
if err != nil {
|
err = c.Start() // returns immediately
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Make sure to close the pty at the end.
|
|
||||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
|
||||||
// Copy stdin to the pty and the pty to stdout.
|
|
||||||
go func() { _, _ = io.Copy(ptmx, conn) }()
|
|
||||||
_, _ = io.Copy(conn, ptmx)
|
|
||||||
|
|
||||||
//err = c.Run() // returns when c finishes.
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Command finished with error: %v", err)
|
log.Printf("Command finished with error: %v", err)
|
||||||
log.Printf("[%s]\n", cmd)
|
return err, 253 // !?
|
||||||
|
} else {
|
||||||
|
if chaffing {
|
||||||
|
conn.EnableChaff()
|
||||||
|
}
|
||||||
|
defer conn.DisableChaff()
|
||||||
|
defer conn.ShutdownChaff()
|
||||||
|
|
||||||
|
if err := c.Wait(); err != nil {
|
||||||
|
fmt.Println("*** c.Wait() done ***")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a server->client copy
|
||||||
|
func runServerToClientCopyAs(who string, conn hkexnet.Conn, srcPath string, chaffing bool) (err error, exitStatus int) {
|
||||||
|
u, _ := user.Lookup(who)
|
||||||
|
var uid, gid uint32
|
||||||
|
fmt.Sscanf(u.Uid, "%d", &uid)
|
||||||
|
fmt.Sscanf(u.Gid, "%d", &gid)
|
||||||
|
log.Println("uid:", uid, "gid:", gid)
|
||||||
|
|
||||||
|
// Need to clear server's env and set key vars of the
|
||||||
|
// target user. This isn't perfect (TERM doesn't seem to
|
||||||
|
// work 100%; ANSI/xterm colour isn't working even
|
||||||
|
// if we set "xterm" or "ansi" here; and line count
|
||||||
|
// reported by 'stty -a' defaults to 24 regardless
|
||||||
|
// of client shell window used to run client.
|
||||||
|
// Investigate -- rlm 2018-01-26)
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("HOME", u.HomeDir)
|
||||||
|
os.Setenv("TERM", "vt102") // TODO: server or client option?
|
||||||
|
|
||||||
|
var c *exec.Cmd
|
||||||
|
cmdName := "/bin/tar"
|
||||||
|
cmdArgs := []string{"-cz", "-f", "-", srcPath}
|
||||||
|
c = exec.Command(cmdName, cmdArgs...)
|
||||||
|
|
||||||
|
//If os.Clearenv() isn't called by server above these will be seen in the
|
||||||
|
//client's session env.
|
||||||
|
//c.Env = []string{"HOME=" + u.HomeDir, "SUDO_GID=", "SUDO_UID=", "SUDO_USER=", "SUDO_COMMAND=", "MAIL=", "LOGNAME="+who}
|
||||||
|
c.Dir = u.HomeDir
|
||||||
|
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
|
||||||
|
c.Stdin = conn
|
||||||
|
c.Stdout = conn
|
||||||
|
c.Stderr = conn
|
||||||
|
|
||||||
|
// Start the command (no pty)
|
||||||
|
log.Printf("[%v %v]\n", cmdName, cmdArgs)
|
||||||
|
err = c.Start() // returns immediately
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Command finished with error: %v", err)
|
||||||
|
return err, 253 // !?
|
||||||
|
} else {
|
||||||
|
if chaffing {
|
||||||
|
conn.EnableChaff()
|
||||||
|
}
|
||||||
|
defer conn.DisableChaff()
|
||||||
|
defer conn.ShutdownChaff()
|
||||||
|
|
||||||
|
if err := c.Wait(); err != nil {
|
||||||
|
fmt.Println("*** c.Wait() done ***")
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Run a command (via default shell) as a specific user
|
// Run a command (via default shell) as a specific user
|
||||||
//
|
//
|
||||||
|
@ -167,7 +259,6 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
|
||||||
_, e := io.Copy(conn, ptmx)
|
_, e := io.Copy(conn, ptmx)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
log.Println("** pty->stdout ended **:", e.Error())
|
log.Println("** pty->stdout ended **:", e.Error())
|
||||||
//wg.Done() //!return
|
|
||||||
}
|
}
|
||||||
// The above io.Copy() will exit when the command attached
|
// The above io.Copy() will exit when the command attached
|
||||||
// to the pty exits
|
// to the pty exits
|
||||||
|
@ -190,8 +281,6 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Wait() // Wait on pty->stdout completion to client
|
wg.Wait() // Wait on pty->stdout completion to client
|
||||||
//conn.DisableChaff()
|
|
||||||
//conn.ShutdownChaff()
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -374,11 +463,31 @@ func main() {
|
||||||
log.Printf("[Client->Server copy]\n")
|
log.Printf("[Client->Server copy]\n")
|
||||||
// TODO: call function with hc, rec.cmd, chaffEnabled etc.
|
// TODO: call function with hc, rec.cmd, chaffEnabled etc.
|
||||||
// func hooks tar cmd right-half of pipe to hc Reader
|
// func hooks tar cmd right-half of pipe to hc Reader
|
||||||
|
addr := hc.RemoteAddr()
|
||||||
|
hname := strings.Split(addr.String(), ":")[0]
|
||||||
|
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
|
||||||
|
runErr, cmdStatus := runClientToServerCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled)
|
||||||
|
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)
|
||||||
|
hc.SetStatus(uint8(cmdStatus))
|
||||||
|
}
|
||||||
} else if rec.op[0] == 'S' {
|
} else if rec.op[0] == 'S' {
|
||||||
// File copy (src) operation - server copy to client
|
// File copy (src) operation - server copy to client
|
||||||
log.Printf("[Server->Client copy]\n")
|
log.Printf("[Server->Client copy]\n")
|
||||||
// TODO: call function to copy rec.cmd (file list) to
|
// TODO: call function to copy rec.cmd (file list) to
|
||||||
// tar cmd left-half of pipeline to hc.Writer ?
|
// tar cmd left-half of pipeline to hc.Writer ?
|
||||||
|
addr := hc.RemoteAddr()
|
||||||
|
hname := strings.Split(addr.String(), ":")[0]
|
||||||
|
log.Printf("[Running copy for [%s@%s]]\n", rec.who, hname)
|
||||||
|
runErr, cmdStatus := runServerToClientCopyAs(string(rec.who), hc, string(rec.cmd), chaffEnabled)
|
||||||
|
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)
|
||||||
|
hc.SetStatus(uint8(cmdStatus))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Println("[Bad cmdSpec]")
|
log.Println("[Bad cmdSpec]")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue