Resync/master

This commit is contained in:
Russ Magee 2026-02-05 19:35:41 -07:00
commit 36f6684f4f
5 changed files with 70 additions and 8 deletions

View File

@ -3,6 +3,7 @@
# XS
![last build status](https://bacillus.blitter.com/onPush-xs-build/lastStatusIcon)
![terminal screenshot on MSYS64](https://blitter.com/~russtopia/files/xs-msys64.png)
--
XS (**X**perimental **S**hell) is a simple alternative to ssh (<5% total SLOCC) written from scratch in Go.
@ -210,6 +211,12 @@ If no leading / is specified in src-or-dest-path, it is assumed to be relative t
remote user. File operations are all performed as the remote user, so account permissions apply
as expected.
When running under MSYS2, one must set the MINGW_ROOT environment variable to assist in
determining how to convert Windows paths to UNIX-style paths. This should be the installation path
of one's MSYS2 environment (eg., _C:/msys2_). Go's stdlib, under the hood, still uses Windows
style paths (drive letters and all) to locate other executables and _xc_ uses _tar_ as part of the copy
functionality.
Local (client) to remote (server) copy:
```
$ xc fileA /some/where/fileB /some/where/else/dirC joebloggs@host-or-ip:remoteDir

27
auth.go
View File

@ -215,18 +215,39 @@ func AuthUserByToken(ctx *AuthCtx, username string, connhostname string, auth st
return
}
func GroomFsPath(path string) (ret string) {
pathRoot := os.Getenv("MINGW_ROOT")
if pathRoot != "" {
ret = path[len(pathRoot):]
ret = strings.ReplaceAll(ret, "\\", "/")
} else {
ret = path
}
//fmt.Printf("groomed fspath:%v\n", ret)
return
}
func GetTool(tool string) (ret string) {
ret = "/bin/" + tool
cmdSuffix := ""
pathRoot := os.Getenv("MINGW_ROOT")
if pathRoot != "" {
cmdSuffix = ".exe"
}
//fmt.Printf("pathRoot:%v cmdSuffix:%v\n", pathRoot, cmdSuffix)
ret = pathRoot + "/bin/" + tool + cmdSuffix
_, err := os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/bin/" + tool
ret = pathRoot + "/usr/bin/" + tool + cmdSuffix
_, err = os.Stat(ret)
if err == nil {
return ret
}
ret = "/usr/local/bin/" + tool
ret = pathRoot + "/usr/local/bin/" + tool + cmdSuffix
_, err = os.Stat(ret)
if err == nil {
return ret

View File

@ -25,8 +25,15 @@ echo "Building most recent push on branch $branch"
git checkout "$branch"
ls
go mod init
go mod tidy
#!############
#!stage "GoMod"
#!############
#!go clean -modcache
#!
#!rm -f go.{mod,sum}
#!go mod init blitter.com/go/xs
#!go mod tidy
#!echo "---"
############
stage "Build"
@ -35,16 +42,19 @@ echo "Invoking 'make clean' ..."
make clean
echo "Invoking 'make all' ..."
make all
echo "---"
############
stage "Lint"
############
make lint
echo "---"
############
stage "UnitTests"
############
go test -v .
echo "---"
############
stage "Test(Authtoken)"
@ -64,6 +74,7 @@ else
echo "client cmd performed OK."
unset tokentest
fi
echo "---"
############
stage "Test(xc S->C)"
@ -88,6 +99,7 @@ else
echo "FAILED!"
exit $stat
fi
echo "---"
############
stage "Test(xc C->S)"
@ -98,12 +110,14 @@ if [ -f ~/.config/xs/.xs_id.bak ]; then
echo "Restoring test user $USER .xs_id file ..."
mv ~/.config/xs/.xs_id.bak ~/.config/xs/.xs_id
fi
echo "---"
############
stage "Artifacts"
############
echo -n "Creating tarfile ..."
tar -cz --exclude=.git --exclude=cptest -f ${BACILLUS_ARTFDIR}/xs.tgz .
echo "---"
############
stage "Cleanup"

4
go.mod
View File

@ -1,6 +1,8 @@
module blitter.com/go/xs
go 1.25.3
go 1.24.0
toolchain go1.24.11
require (
blitter.com/go/cryptmt v1.0.3

View File

@ -294,7 +294,14 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
captureStderr = true
cmd = xs.GetTool("tar")
args = []string{"-cz", "-f", "/dev/stdout"}
//fmt.Printf("GetTool found cmd:%v\n", cmd)
/* Explicit -f /dev/stdout doesn't work in MINGW/MSYS64
* as '/dev/stdout' doesn't actually appear in the /dev/ filesystem...?
* And it appears not to actually be required as without -f stdout is
* implied. -rlm 2025-12-07
*/
//args = []string{"-cz", "-f", "/dev/stdout"}
args = []string{"-cz"}
files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables
@ -310,6 +317,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
// remote destDir.
for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec
v = xs.GroomFsPath(v)
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
args = append(args, fileTmp)
@ -322,7 +330,8 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
bandwidthInBytesPerSec := " -L " + fmt.Sprintf("%d", copyLimitBPS)
displayOpts := " -pre " //nolint:goconst,nolintlint
cmd = xs.GetTool("bash")
args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
//args = []string{"-c", xs.GetTool("tar") + " -cz -f /dev/stdout "}
args = []string{"-c", xs.GetTool("tar") + " -cz "}
files = strings.TrimSpace(files)
// Awesome fact: tar actually can take multiple -C args, and
// changes to the dest dir *as it sees each one*. This enables
@ -338,6 +347,7 @@ func buildCmdLocalToRemote(copyQuiet bool, copyLimitBPS uint, files string) (cap
// remote destDir.
for _, v := range strings.Split(files, " ") {
v, _ = filepath.Abs(v) // #nosec
v = xs.GroomFsPath(v)
dirTmp, fileTmp := path.Split(v)
if dirTmp == "" {
args[1] = args[1] + fileTmp + " "
@ -386,6 +396,8 @@ func doCopyMode(conn *xsnet.Conn, remoteDest bool, files string, copyQuiet bool,
c.Stderr = os.Stderr
}
//fmt.Printf("cmd:%v args:%v\n", cmdName, cmdArgs)
// Start the command (no pty)
err = c.Start() // returns immediately
/////////////
@ -614,6 +626,12 @@ func parseNonSwitchArgs(a []string) (user, host, path string, isDest bool, other
// Whether fancyArg is src or dst file depends on flag.Args() index;
// fancyArg as last flag.Args() element denotes dstFile
// fancyArg as not-last flag.Args() element denotes srcFile
/* rlm:2025-12-10 This breaks if srcPath is outside of MSYS2 tree, as srcPath
appears to silently be converted to an absolute winpath eg.,
/c/users/RM/... -> C:/users/RM/...
and the colon (:) in this breaks the logic below.
*/
var fancyUser, fancyHost, fancyPath string
for i, arg := range a {
if strings.Contains(arg, ":") || strings.Contains(arg, "@") {