libf 4 月之前
父節點
當前提交
7b6c5b8de1
共有 8 個文件被更改,包括 56 次插入1178 次删除
  1. 4 9
      go.mod
  2. 11 60
      go.sum
  3. 0 457
      ssh/exsample/client.go
  4. 0 174
      ssh/exsample/config.go
  5. 0 210
      ssh/exsample/main.go
  6. 0 132
      ssh/sftp.go
  7. 0 134
      ssh/ssh.go
  8. 41 2
      sync/sync.go

+ 4 - 9
go.mod

@@ -3,10 +3,7 @@ module zerone
 go 1.22
 
 require (
-	github.com/atrox/homedir v1.0.0
 	github.com/creack/pty v1.1.18
-	github.com/kevinburke/ssh_config v1.2.0
-	golang.org/x/crypto v0.30.0
 	golang.org/x/term v0.27.0
 	gopkg.in/yaml.v2 v2.4.0
 )
@@ -21,7 +18,6 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
-	github.com/kr/fs v0.1.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/spacemonkeygo/errors v0.0.0-20201030155909-2f5f890dbc62 // indirect
 	go.etcd.io/etcd/api/v3 v3.5.17 // indirect
@@ -42,13 +38,12 @@ require (
 )
 
 require (
-	git.wecise.com/wecise/util v0.0.0-20241213033438-5064ad8df1f5
+	git.wecise.com/wecise/util v0.0.0-20241216141312-1587ceee6f6e
 	github.com/chromedp/cdproto v0.0.0-20240214232516-ad4608604e9e
 	github.com/chromedp/chromedp v0.9.5
-	github.com/fatih/color v1.9.0 // indirect
-	github.com/mattn/go-colorable v0.1.7 // indirect
+	github.com/fatih/color v1.18.0 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/pkg/sftp v1.13.7
-	github.com/spf13/cast v1.6.0 // indirect
+	github.com/spf13/cast v1.7.0 // indirect
 	golang.org/x/sys v0.28.0 // indirect
 )

+ 11 - 60
go.sum

@@ -1,7 +1,5 @@
-git.wecise.com/wecise/util v0.0.0-20241213033438-5064ad8df1f5 h1:iwgK+2gBbRjyk2LB7JgEKpjGxUF/o2oQJwAEdVfFNFo=
-git.wecise.com/wecise/util v0.0.0-20241213033438-5064ad8df1f5/go.mod h1:3T0eAIwD7TnDT87ShyoV6HHGFoWcK/SHFUKhNe2ok+o=
-github.com/atrox/homedir v1.0.0 h1:99Vwk+XECZTDLaAPeMj7vF9JMNcVarWddqPeyDzJT5E=
-github.com/atrox/homedir v1.0.0/go.mod h1:ZKVEIDNKscX8qV1TyrwLP+ayjv3XQO7wbVmc5EW00A8=
+git.wecise.com/wecise/util v0.0.0-20241216141312-1587ceee6f6e h1:x1WYYej8y2jc0hffLmC85Le4IHjJO6awdlSJtvZywGo=
+git.wecise.com/wecise/util v0.0.0-20241216141312-1587ceee6f6e/go.mod h1:2YXWE9m5mNgAu40zpYrL3woGz6S8CoHAW/CJeWXaIko=
 github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
 github.com/chromedp/cdproto v0.0.0-20240214232516-ad4608604e9e h1:kXEolCWQZzuEFcuaTzfqXToX+e29OcvK87BcBiBBJ1c=
 github.com/chromedp/cdproto v0.0.0-20240214232516-ad4608604e9e/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
@@ -18,8 +16,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -37,12 +35,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
-github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
-github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -51,40 +45,30 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL
 github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
-github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
 github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
-github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/spacemonkeygo/errors v0.0.0-20201030155909-2f5f890dbc62 h1:X5+jSi+pL+sc2/Sp9mtrmRqDDnKkm9YVy1ik/jJDRD0=
 github.com/spacemonkeygo/errors v0.0.0-20201030155909-2f5f890dbc62/go.mod h1:7NL9UAYQnRM5iKHUCld3tf02fKb5Dft+41+VckASUy0=
-github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
-github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
+github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w=
 go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
 go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw=
@@ -100,68 +84,35 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
-golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
 golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
 golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
 golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

+ 0 - 457
ssh/exsample/client.go

@@ -1,457 +0,0 @@
-package main
-
-import (
-	"bufio"
-	"encoding/base64"
-	"fmt"
-	"io"
-	"net"
-	"os"
-	"os/user"
-	"path"
-	"regexp"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
-
-	"git.wecise.com/wecise/util/logger"
-	"git.wecise.com/wecise/util/merrs"
-	"golang.org/x/crypto/ssh"
-	"golang.org/x/term"
-)
-
-var (
-	DefaultCiphers = []string{
-		"aes128-ctr",
-		"aes192-ctr",
-		"aes256-ctr",
-		"aes128-gcm@openssh.com",
-		"chacha20-poly1305@openssh.com",
-		"arcfour256",
-		"arcfour128",
-		"arcfour",
-		"aes128-cbc",
-		"3des-cbc",
-		"blowfish-cbc",
-		"cast128-cbc",
-		"aes192-cbc",
-		"aes256-cbc",
-	}
-)
-
-var regxyesno = &Regexp{regexp.MustCompile(`.*(\(yes\/no\)\?)\s*$`)}
-var regxpassword = &Regexp{regexp.MustCompile(`.*([Pp]assword:)\s*$`)}
-var regxprompt = &Regexp{regexp.MustCompile(`.*([%#\$\>])\s*$`)}
-
-type Client interface {
-	Login()
-}
-
-type defaultClient struct {
-	clientConfig *ssh.ClientConfig
-	node         *Node
-}
-
-func genSSHConfig(node *Node) *defaultClient {
-	u, err := user.Current()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		return nil
-	}
-
-	var authMethods []ssh.AuthMethod
-
-	var pemBytes []byte
-	if node.KeyPath == "" {
-		pemBytes, err = os.ReadFile(path.Join(u.HomeDir, ".ssh/id_rsa"))
-	} else {
-		pemBytes, err = os.ReadFile(node.KeyPath)
-	}
-	if err != nil && !os.IsNotExist(err) {
-		logger.Error(merrs.NewError(err))
-	} else if len(pemBytes) > 0 {
-		var signer ssh.Signer
-		if node.Passphrase != "" {
-			signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(node.Passphrase))
-		} else {
-			signer, err = ssh.ParsePrivateKey(pemBytes)
-		}
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-		} else {
-			authMethods = append(authMethods, ssh.PublicKeys(signer))
-		}
-	}
-
-	password := node.password()
-
-	if password != nil {
-		authMethods = append(authMethods, password)
-	}
-
-	authMethods = append(authMethods, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
-		answers := make([]string, 0, len(questions))
-		for i, q := range questions {
-			fmt.Print(q)
-			if echos[i] {
-				scan := bufio.NewScanner(os.Stdin)
-				if scan.Scan() {
-					answers = append(answers, scan.Text())
-				}
-				err := scan.Err()
-				if err != nil {
-					return nil, err
-				}
-			} else {
-				b, err := term.ReadPassword(int(syscall.Stdin))
-				if err != nil {
-					return nil, err
-				}
-				fmt.Println()
-				answers = append(answers, string(b))
-			}
-		}
-		return answers, nil
-	}))
-
-	config := &ssh.ClientConfig{
-		User:            node.user(),
-		Auth:            authMethods,
-		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
-		Timeout:         time.Second * 10,
-	}
-
-	config.SetDefaults()
-	config.Ciphers = append(config.Ciphers, DefaultCiphers...)
-
-	return &defaultClient{
-		clientConfig: config,
-		node:         node,
-	}
-}
-
-func NewClient(node *Node) Client {
-	return genSSHConfig(node)
-}
-
-func (c *defaultClient) Login() {
-	host := c.node.Host
-	port := strconv.Itoa(c.node.port())
-	jNodes := c.node.Jump
-
-	var client *ssh.Client
-
-	if len(jNodes) > 0 {
-		jNode := jNodes[0]
-		jc := genSSHConfig(jNode)
-		proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(jNode.Host, strconv.Itoa(jNode.port())), jc.clientConfig)
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-			return
-		}
-		conn, err := proxyClient.Dial("tcp", net.JoinHostPort(host, port))
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-			return
-		}
-		ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(host, port), c.clientConfig)
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-			return
-		}
-		client = ssh.NewClient(ncc, chans, reqs)
-	} else {
-		client1, err := ssh.Dial("tcp", net.JoinHostPort(host, port), c.clientConfig)
-		client = client1
-		if err != nil {
-			msg := err.Error()
-			// use terminal password retry
-			if strings.Contains(msg, "no supported methods remain") && !strings.Contains(msg, "password") {
-				fmt.Printf("%s@%s's password:", c.clientConfig.User, host)
-				var b []byte
-				b, err = term.ReadPassword(int(syscall.Stdin))
-				if err == nil {
-					p := string(b)
-					if p != "" {
-						c.clientConfig.Auth = append(c.clientConfig.Auth, ssh.Password(p))
-					}
-					fmt.Println()
-					client, err = ssh.Dial("tcp", net.JoinHostPort(host, port), c.clientConfig)
-				}
-			}
-		}
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-			return
-		}
-	}
-	defer client.Close()
-
-	if strings.Index(c.node.Commands[0].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 {
-		logger.Trace("connect server ssh", fmt.Sprintf("-p %d %s@%s version: %s", c.node.port(), c.node.user(), host, string(client.ServerVersion())))
-	}
-
-	session, err := client.NewSession()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		return
-	}
-	defer session.Close()
-
-	fd := int(os.Stdin.Fd())
-	w, h, err := term.GetSize(fd)
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		w = 300
-		h = 50
-	}
-
-	modes := ssh.TerminalModes{
-		ssh.ECHO:          0,
-		ssh.TTY_OP_ISPEED: 14400,
-		ssh.TTY_OP_OSPEED: 14400,
-	}
-	err = session.RequestPty("xterm", h, w, modes)
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		return
-	}
-
-	stdinPipe, err := session.StdinPipe()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		session.Stdin = os.Stdin
-	}
-	stdoutPipe, err := session.StdoutPipe()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		session.Stdout = os.Stdout
-	}
-	stderrPipe, err := session.StderrPipe()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		session.Stderr = os.Stderr
-	}
-
-	err = session.Shell()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		return
-	}
-
-	cmdidx := 0
-	outputproc := func(stdoutbtr *BTReader, localstdout io.Writer, stdinPipe io.Writer) {
-		as := ""
-		change := false
-		for {
-			bs, err := stdoutbtr.ReadTimeout(10 * time.Millisecond)
-			if err != nil {
-				if err == io.EOF {
-					return
-				}
-				logger.Error(merrs.NewError(err))
-				return
-			}
-			if len(bs) > 0 {
-				change = true
-				s := string(bs)
-				localstdout.Write([]byte(s))
-				as += s
-			} else if change {
-				change = false
-				if cmdidx < len(c.node.Commands) {
-					if msi := c.node.Commands[cmdidx].Endregx.FindStringSubmatchIndex(as); msi != nil {
-						match := as[msi[0]:msi[1]]
-						if strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
-							logger.Trace("match end:", "'"+match+"'")
-						}
-						as = "" // 全清,开始新的命令
-						cmdidx++
-						if cmdidx >= len(c.node.Commands) {
-							continue
-						}
-						if strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
-							logger.Trace("command:", c.node.Commands[cmdidx].Cmd)
-						}
-						localstdout.Write([]byte(c.node.Commands[cmdidx].Cmd + "\n"))
-						stdinPipe.Write([]byte(c.node.Commands[cmdidx].Cmd + "\n"))
-						continue
-					}
-					for _, regex := range c.node.Commands[cmdidx].Regexps {
-						if regex == nil {
-							continue
-						}
-						if regex.Regexp == nil {
-							// like match all
-							if regex.Output != "" {
-								localstdout.Write([]byte(regex.Output))
-								stdinPipe.Write([]byte(regex.Output))
-							}
-						} else if msi := regex.Regexp.FindStringSubmatchIndex(as); msi != nil {
-							match := as[msi[0]:msi[1]]
-							if len(msi) >= 4 {
-								match = as[msi[2]:msi[3]]
-								as = as[msi[3]:] // 清除已处理完的内容
-							} else {
-								as = as[msi[1]:] // 清除已处理完的内容
-							}
-							if strings.Index(regex.Debug, "m") >= 0 || strings.Index(regex.Debug, "1") >= 0 {
-								logger.Trace("match regex:", "'"+match+"'")
-							}
-							if regex.Output != "" {
-								localstdout.Write([]byte(regex.Output))
-								stdinPipe.Write([]byte(regex.Output))
-							}
-						}
-					}
-				}
-				if msi := regxyesno.FindStringSubmatchIndex(as); msi != nil {
-					match := as[msi[0]:msi[1]]
-					if len(msi) >= 4 {
-						as = as[msi[3]:] // 清除已处理完的内容
-					} else {
-						as = as[msi[1]:] // 清除已处理完的内容
-					}
-					if strings.Index(c.node.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 ||
-						cmdidx < len(c.node.Commands) &&
-							(strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
-						logger.Trace("match yesno:", "'"+match+"'")
-					}
-					os.Stdout.Write([]byte("yes\n"))
-					stdinPipe.Write([]byte("yes\n"))
-				}
-				if msi := regxpassword.FindStringSubmatchIndex(as); msi != nil {
-					match := as[msi[0]:msi[1]]
-					if len(msi) >= 4 {
-						as = as[msi[3]:] // 清除已处理完的内容
-					} else {
-						as = as[msi[1]:] // 清除已处理完的内容
-					}
-					p := c.node.Commands[0].Password
-					if cmdidx < len(c.node.Commands) {
-						p = c.node.Commands[cmdidx].Password
-					}
-					if strings.Index(c.node.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 ||
-						cmdidx < len(c.node.Commands) &&
-							(strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
-						logger.Trace("match password:", "'"+match+"'")
-					}
-					if p != "" {
-						if p[0:1] == "=" {
-							p = p[1:]
-						} else {
-							x, e := base64.RawStdEncoding.DecodeString(p)
-							if e == nil {
-								p = string(x)
-							}
-							// else 不是Base64编码,保持原值
-						}
-						// don't echo password
-						if c.node.Commands[0].Regexps[0].Debug != "" || cmdidx < len(c.node.Commands) && c.node.Commands[cmdidx].Regexps[0].Debug != "" {
-							os.Stdout.Write([]byte(p + "\n"))
-						}
-						stdinPipe.Write([]byte(p + "\n"))
-					}
-				}
-				if len(as) > 1024 {
-					as = as[len(as)-1024:]
-				}
-			}
-		}
-	}
-	go outputproc(&BTReader{Reader: bufio.NewReader(stdoutPipe)}, os.Stdout, stdinPipe)
-	go outputproc(&BTReader{Reader: bufio.NewReader(stderrPipe)}, os.Stderr, stdinPipe)
-
-	go func() {
-		for {
-			bs := make([]byte, 1024)
-			n, err := os.Stdin.Read(bs)
-			if err != nil {
-				if err == io.EOF {
-					return
-				}
-				logger.Error(merrs.NewError(err))
-				return
-			}
-			s := string(bs[:n])
-			stdinPipe.Write([]byte(s))
-		}
-	}()
-
-	// interval get terminal size
-	// fix resize issue
-	go func() {
-		var (
-			ow = w
-			oh = h
-		)
-		for {
-			cw, ch, err := term.GetSize(fd)
-			if err != nil {
-				break
-			}
-
-			if cw != ow || ch != oh {
-				err = session.WindowChange(ch, cw)
-				if err != nil {
-					break
-				}
-				ow = cw
-				oh = ch
-			}
-			time.Sleep(time.Second)
-		}
-	}()
-
-	// send keepalive
-	go func() {
-		for {
-			time.Sleep(time.Second * 10)
-			client.SendRequest("nop", false, nil)
-		}
-	}()
-
-	session.Wait()
-	if strings.Index(c.node.Commands[0].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 {
-		logger.Trace("disconnected")
-	}
-}
-
-type BTReader struct {
-	*bufio.Reader
-	bufop int32
-	chbs  chan []byte
-}
-
-func (me *BTReader) ReadTimeout(d time.Duration) (rbs []byte, err error) {
-	if me.chbs == nil {
-		me.chbs = make(chan []byte)
-		go func() {
-			n := 0
-			bs := make([]byte, me.Size())
-			for {
-				_, err = me.ReadByte()
-				if err != nil {
-					return
-				}
-				err = me.UnreadByte()
-				if err != nil {
-					return
-				}
-				n, err = me.Read(bs[0:me.Buffered()])
-				if err != nil {
-					return
-				}
-				me.chbs <- bs[0:n]
-			}
-		}()
-	}
-	t := time.NewTimer(d)
-	select {
-	case rbs = <-me.chbs:
-		return
-	case <-t.C:
-		return
-	}
-}

+ 0 - 174
ssh/exsample/config.go

@@ -1,174 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/user"
-	"path"
-	"regexp"
-	"strconv"
-
-	"git.wecise.com/wecise/util/logger"
-	"git.wecise.com/wecise/util/merrs"
-	"github.com/atrox/homedir"
-	"github.com/kevinburke/ssh_config"
-	"golang.org/x/crypto/ssh"
-	"gopkg.in/yaml.v2"
-)
-
-type Node struct {
-	Name       string     `yaml:"name"`
-	Alias      string     `yaml:"alias"`
-	Host       string     `yaml:"host"`
-	User       string     `yaml:"user"`
-	Port       int        `yaml:"port"`
-	KeyPath    string     `yaml:"keypath"`
-	Passphrase string     `yaml:"passphrase"`
-	Password   string     `yaml:"password"`
-	Commands   []*Command `yaml:"commands"`
-	Children   []*Node    `yaml:"children"`
-	Jump       []*Node    `yaml:"jump"`
-}
-
-type Regexp struct {
-	*regexp.Regexp
-}
-
-func (r *Regexp) MarshalJSON() ([]byte, error) {
-	if r == nil || r.Regexp == nil {
-		return json.Marshal(nil)
-	}
-	return json.Marshal(r.String())
-}
-
-func (r *Regexp) MarshalYAML() (interface{}, error) {
-	if r == nil || r.Regexp == nil {
-		return nil, nil
-	}
-	return r.String(), nil
-}
-
-type Matcher struct {
-	Regexp *Regexp
-	Output string `yaml:"output"`
-	Debug  string `yaml:"debug"`
-}
-
-type Command struct {
-	Cmd      string `yaml:"cmd"`
-	Password string `yaml:"password"`
-	Regexps  []*Matcher
-	Endregx  *Regexp
-}
-
-func (n *Node) String() string {
-	return n.Name
-}
-
-func (n *Node) user() string {
-	if n.User == "" {
-		return "root"
-	}
-	return n.User
-}
-
-func (n *Node) port() int {
-	if n.Port <= 0 {
-		return 22
-	}
-	return n.Port
-}
-
-func (n *Node) password() ssh.AuthMethod {
-	if n.Password == "" {
-		return nil
-	}
-	return ssh.Password(n.Password)
-}
-
-func (n *Node) alias() string {
-	return n.Alias
-}
-
-var (
-	config []*Node
-)
-
-func GetConfig() []*Node {
-	return config
-}
-
-func LoadConfig() error {
-	b, err := LoadConfigBytes(".sshw", ".sshw.yml", ".sshw.yaml")
-	if err != nil {
-		return err
-	}
-	var c []*Node
-	err = yaml.Unmarshal(b, &c)
-	if err != nil {
-		return err
-	}
-
-	config = c
-
-	return nil
-}
-
-func LoadSshConfig() error {
-	u, err := user.Current()
-	if err != nil {
-		logger.Error(merrs.NewError(err))
-		return nil
-	}
-	f, _ := os.Open(path.Join(u.HomeDir, ".ssh/config"))
-	cfg, _ := ssh_config.Decode(f)
-	var nc []*Node
-	for _, host := range cfg.Hosts {
-		alias := fmt.Sprintf("%s", host.Patterns[0])
-		hostName, err := cfg.Get(alias, "HostName")
-		if err != nil {
-			return err
-		}
-		if hostName != "" {
-			port, _ := cfg.Get(alias, "Port")
-			if port == "" {
-				port = "22"
-			}
-			var c = new(Node)
-			c.Name = alias
-			c.Alias = alias
-			c.Host = hostName
-			c.User, _ = cfg.Get(alias, "User")
-			c.Port, _ = strconv.Atoi(port)
-			keyPath, _ := cfg.Get(alias, "IdentityFile")
-			c.KeyPath, _ = homedir.Expand(keyPath)
-			nc = append(nc, c)
-			// fmt.Println(c.Alias, c.Host, c.User, c.Port, c.KeyPath)
-		}
-	}
-	config = nc
-	return nil
-}
-
-func LoadConfigBytes(names ...string) ([]byte, error) {
-	u, err := user.Current()
-	if err != nil {
-		return nil, err
-	}
-	// homedir
-	for i := range names {
-		sshw, err := os.ReadFile(path.Join(u.HomeDir, names[i]))
-		if err == nil {
-			return sshw, nil
-		}
-	}
-	// relative
-	for i := range names {
-		sshw, err := os.ReadFile(names[i])
-		if err == nil {
-			return sshw, nil
-		}
-	}
-	return nil, err
-}

+ 0 - 210
ssh/exsample/main.go

@@ -1,210 +0,0 @@
-package main
-
-import (
-	"encoding/base64"
-	"fmt"
-	"os"
-	"os/user"
-	"regexp"
-	"strings"
-
-	"git.wecise.com/wecise/util/logger"
-	"git.wecise.com/wecise/util/merrs"
-	"gopkg.in/yaml.v2"
-)
-
-func init() {
-	logger.SetConsole(true)
-	logger.SetLevel(logger.TRACE)
-	logger.SetFormat("yyyy-MM-dd HH:mm:ss.SSS [level] msg", "\n")
-}
-
-// 通过命令行参数获取 user:password@host
-func uchi() (u, c, h string, i int) {
-	if len(os.Args) < 2 {
-		return
-	}
-	if msi := regexp.MustCompile(`^([^:]+):(.*)@([^@]*)$`).FindStringSubmatchIndex(os.Args[1]); msi != nil {
-		u = os.Args[1][msi[2]:msi[3]]
-		c = "=" + os.Args[1][msi[4]:msi[5]]
-		h = os.Args[1][msi[6]:msi[7]]
-		i = 2
-		return
-	}
-	if msi := regexp.MustCompile(`^([^:]+)@([^@]*)$`).FindStringSubmatchIndex(os.Args[1]); msi != nil {
-		u = os.Args[1][msi[2]:msi[3]]
-		h = os.Args[1][msi[6]:msi[7]]
-		i = 2
-	} else if os.Args[1][0] != '-' && strings.Index(os.Args[1], "=") < 0 {
-		h = os.Args[1]
-		i = 2
-	} else {
-		i = 1
-	}
-	if u == "" {
-		user, err := user.Current()
-		if err != nil {
-			logger.Error(merrs.NewError(err))
-			return "", "", "", 0
-		}
-		u = user.Username
-	}
-	if h == "" {
-		h = "127.0.0.1"
-	}
-	return
-}
-
-type KV struct{ Key, Val string }
-
-func parseArgs(args []string) (kvs []*KV) {
-	argk := ""
-	argv := ""
-	for _, arg := range args {
-		if argk != "" {
-			argv = arg
-		} else if regexp.MustCompile(`^\-\w+$`).MatchString(arg) {
-			argk = arg[1:]
-			continue
-		} else {
-			kv := strings.SplitN(arg, "=", 2)
-			if len(kv) == 2 {
-				argk = kv[0]
-				argv = kv[1]
-			} else {
-				argk = ""
-				argv = arg
-			}
-		}
-		kvs = append(kvs, &KV{argk, argv})
-		argk, argv = "", ""
-	}
-	if argk != "" {
-		kvs = append(kvs, &KV{argk, argv})
-	}
-	return
-}
-
-func main() {
-	u, c, h, i := uchi()
-	if i == 0 {
-		fmt.Println("usage:")
-		fmt.Println("  msh user:password@host|p=password|a=passcode [[c=]command [p=password|a=passcode] [x=cmd-end-regexp] [[r=regexp] [o=output|n=outputline]]...]...")
-		fmt.Println("  a=passcode should be base64 encoded, or use p=password")
-		fmt.Println("  debug info include: p(progress) a(argments) m(match) 1(all)")
-		return
-	}
-	kvs := parseArgs(os.Args[i:])
-	if c == "" {
-		for _, kv := range kvs {
-			if kv.Key == "a" {
-				c = kv.Val
-				if c == "" {
-					c = "="
-				}
-				break
-			}
-			if kv.Key == "p" {
-				c = "=" + kv.Val
-				break
-			}
-		}
-	}
-	p := c
-	if len(p) > 0 && p[0:1] == "=" {
-		p = p[1:]
-	} else {
-		x, e := base64.RawStdEncoding.DecodeString(p)
-		if e == nil {
-			p = string(x)
-		}
-		// else 不是Base64编码,保持原值
-	}
-	// explainArgs key, val
-	cmds := []*Command{{Cmd: "ssh " + u + ":" + p + "@" + h, Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt}}
-	for _, kv := range kvs {
-		key, val := kv.Key, kv.Val
-		switch key {
-		case "", "cmd", "command", "c":
-			cmds = append(cmds, &Command{Cmd: val, Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt})
-		case "ry":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, merrs.NewError(err))
-				return
-			} else {
-				regxyesno = &Regexp{re}
-			}
-		case "rc":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, merrs.NewError(err))
-				return
-			} else {
-				regxpassword = &Regexp{re}
-			}
-		case "rp":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, merrs.NewError(err))
-				return
-			} else {
-				regxprompt = &Regexp{re}
-			}
-		case "password", "code", "pass", "p":
-			cmds[len(cmds)-1].Password = "=" + val
-		case "passcode", "b64code", "a":
-			cmds[len(cmds)-1].Password = val
-		case "re", "r", "regex":
-			if val == "" {
-				cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: nil})
-			} else {
-				re, err := regexp.Compile(val)
-				if err != nil {
-					logger.Error("arg", i, merrs.NewError(err))
-					return
-				} else {
-					cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: &Regexp{re}})
-				}
-			}
-		case "x", "end":
-			if val == "" {
-				cmds[len(cmds)-1].Endregx = regxprompt
-			} else {
-				re, err := regexp.Compile(val)
-				if err != nil {
-					logger.Error("arg", i, merrs.NewError(err))
-					return
-				} else {
-					cmds[len(cmds)-1].Endregx = &Regexp{re}
-				}
-			}
-		case "out", "o", "output":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val
-		case "outln", "n", "outputline":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val + "\n"
-		case "debug", "d":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Debug += val
-		}
-	}
-	if strings.Index(cmds[0].Regexps[0].Debug, "a") >= 0 || strings.Index(cmds[0].Regexps[0].Debug, "1") >= 0 {
-		//bs, _ := json.MarshalIndent(cmds, "", "  ")
-		bs, _ := yaml.Marshal(cmds)
-		logger.Debug("arguments:\n" + string(bs))
-	}
-	node := &Node{
-		Name:       "x",
-		Alias:      "x",
-		Host:       h,
-		User:       u,
-		Port:       22,
-		KeyPath:    "",
-		Passphrase: "",
-		Password:   p,
-		Commands:   cmds,
-		Children:   []*Node{},
-		Jump:       []*Node{},
-	}
-	sshc := NewClient(node)
-	sshc.Login()
-}

+ 0 - 132
ssh/sftp.go

@@ -1,132 +0,0 @@
-package ssh
-
-import (
-	"fmt"
-	"os"
-	"time"
-
-	"github.com/pkg/sftp"
-)
-
-func (me *Client) List(dir string, proc func(path string, err error) error) error {
-	// open an SFTP session over an existing ssh connection.
-	client, err := sftp.NewClient(me.Client)
-	if err != nil {
-		return err
-	}
-	defer client.Close()
-
-	// walk a directory
-	w := client.Walk(dir)
-	for w.Step() {
-		err = proc(w.Path(), w.Err())
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (me *Client) Stat(fpath string) (os.FileInfo, error) {
-	// open an SFTP session over an existing ssh connection.
-	client, err := sftp.NewClient(me.Client)
-	if err != nil {
-		return nil, err
-	}
-	defer client.Close()
-	return client.Lstat(fpath)
-}
-
-func (me *Client) ReadFile(fpath string) (data []byte, err error) {
-	// open an SFTP session over an existing ssh connection.
-	client, err := sftp.NewClient(me.Client)
-	if err != nil {
-		return nil, err
-	}
-	defer client.Close()
-	f, err := client.Open(fpath)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-
-	var size int
-	if info, err := f.Stat(); err == nil {
-		size64 := info.Size()
-		if int64(int(size64)) == size64 {
-			size = int(size64)
-		}
-	}
-	size++ // one byte for final read at EOF
-
-	// If a file claims a small size, read at least 512 bytes.
-	// In particular, files in Linux's /proc claim size 0 but
-	// then do not work right if read in small pieces,
-	// so an initial read of 1 byte would not work correctly.
-	offset := len(data)
-	if size > cap(data) {
-		ndata := make([]byte, len(data), size)
-		copy(ndata, data)
-		data = ndata
-	}
-
-	for {
-		n, err := f.ReadAt(data[offset:cap(data)], int64(offset))
-		offset += n
-		if err != nil {
-			return data[:offset], err
-		}
-		if offset >= cap(data) {
-			d := append(data[:cap(data)], 0)
-			data = d[:offset]
-		}
-	}
-}
-
-func (me *Client) WriteFile(fpath string, bs []byte, mode os.FileMode, mtime time.Time) error {
-	// open an SFTP session over an existing ssh connection.
-	client, err := sftp.NewClient(me.Client)
-	if err != nil {
-		return err
-	}
-	defer client.Close()
-
-	// leave your mark
-	f, err := client.Create(fpath)
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-	w := 0
-	for w < len(bs) {
-		n, err := f.Write(bs[w:])
-		w += n
-		if err != nil {
-			return err
-		}
-	}
-
-	err = client.Chmod(fpath, mode)
-	if err != nil {
-		return err
-	}
-	err = client.Chtimes(fpath, mtime, mtime)
-	if err != nil {
-		return err
-	}
-
-	// check it's there
-	fi, err := client.Lstat(fpath)
-	if err != nil {
-		return err
-	}
-
-	if fi.Size() != int64(len(bs)) {
-		return fmt.Errorf("write file size incorrect")
-	}
-	if !fi.ModTime().Equal(mtime) {
-		return fmt.Errorf("write file time incorrect")
-	}
-
-	return nil
-}

+ 0 - 134
ssh/ssh.go

@@ -1,134 +0,0 @@
-package ssh
-
-import (
-	"bufio"
-	"fmt"
-	"os"
-	"os/user"
-	"path"
-	"syscall"
-	"time"
-
-	"git.wecise.com/wecise/util/merrs"
-	"golang.org/x/crypto/ssh"
-	"golang.org/x/term"
-)
-
-var (
-	DefaultCiphers = []string{
-		"aes128-ctr",
-		"aes192-ctr",
-		"aes256-ctr",
-		"aes128-gcm@openssh.com",
-		"chacha20-poly1305@openssh.com",
-		"arcfour256",
-		"arcfour128",
-		"arcfour",
-		"aes128-cbc",
-		"3des-cbc",
-		"blowfish-cbc",
-		"cast128-cbc",
-		"aes192-cbc",
-		"aes256-cbc",
-	}
-)
-
-type Node struct {
-	IPPort     string
-	User       string
-	Password   string
-	Passphrase string
-	Keypath    string
-	Timeout    time.Duration
-}
-
-type Client struct {
-	*ssh.Client
-}
-
-func (me *Node) Connect() (*Client, error) {
-	authMethods, err := authMethods(me.Password, me.Passphrase, me.Keypath)
-	if err != nil {
-		return nil, err
-	}
-	config := &ssh.ClientConfig{
-		User:            me.User,
-		Auth:            authMethods,
-		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
-		Timeout:         me.Timeout,
-	}
-	config.SetDefaults()
-	config.Ciphers = append(config.Ciphers, DefaultCiphers...)
-
-	client, err := ssh.Dial("tcp", me.IPPort, config)
-	if err != nil {
-		return nil, err
-	}
-
-	return &Client{
-		Client: client,
-	}, nil
-}
-
-func authMethods(password string, passphrase string, keypath string) ([]ssh.AuthMethod, error) {
-	var authMethods []ssh.AuthMethod
-
-	osuser, err := user.Current()
-	if err != nil {
-		return nil, merrs.NewError(err)
-	}
-
-	var pemBytes []byte
-	if keypath == "" {
-		pemBytes, err = os.ReadFile(path.Join(osuser.HomeDir, ".ssh/id_rsa"))
-	} else {
-		pemBytes, err = os.ReadFile(keypath)
-	}
-	if err != nil && !os.IsNotExist(err) {
-		return nil, merrs.NewError(err)
-	}
-
-	if len(pemBytes) > 0 {
-		var signer ssh.Signer
-		if passphrase != "" {
-			signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(passphrase))
-		} else {
-			signer, err = ssh.ParsePrivateKey(pemBytes)
-		}
-		if err != nil {
-			return nil, merrs.NewError(err)
-		}
-		authMethods = append(authMethods, ssh.PublicKeys(signer))
-	}
-
-	if password != "" {
-		authMethods = append(authMethods, ssh.Password(password))
-	}
-
-	authMethods = append(authMethods, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
-		answers := make([]string, 0, len(questions))
-		for i, q := range questions {
-			fmt.Print(q)
-			if echos[i] {
-				scan := bufio.NewScanner(os.Stdin)
-				if scan.Scan() {
-					answers = append(answers, scan.Text())
-				}
-				err := scan.Err()
-				if err != nil {
-					return nil, err
-				}
-			} else {
-				b, err := term.ReadPassword(int(syscall.Stdin))
-				if err != nil {
-					return nil, err
-				}
-				fmt.Println()
-				answers = append(answers, string(b))
-			}
-		}
-		return answers, nil
-	}))
-
-	return authMethods, nil
-}

+ 41 - 2
sync/sync.go

@@ -4,9 +4,11 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"os/signal"
 	"path/filepath"
 	"regexp"
 	"strings"
+	"syscall"
 	"time"
 
 	ccfg "git.wecise.com/wecise/util/cfg"
@@ -14,6 +16,15 @@ import (
 	clog "git.wecise.com/wecise/util/logger"
 )
 
+func init() {
+	exitChan := make(chan os.Signal, 1)
+	signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM)
+	go func() {
+		<-exitChan
+		os.Exit(1)
+	}()
+}
+
 var config = ccfg.MConfig()
 var logger = clog.New()
 
@@ -66,6 +77,18 @@ func main() {
 }
 
 func sync(frompath, topath string) (err error) {
+	if len(frompath) == 0 ||
+		!(len(frompath) >= 1 && frompath[:1] == "/") &&
+			!(len(frompath) >= 2 && frompath[1:2] == ":") &&
+			!regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(frompath) {
+		return fmt.Errorf("Must specify absolute path")
+	}
+	if len(topath) == 0 ||
+		!(len(topath) >= 1 && topath[:1] == "/") &&
+			!(len(topath) >= 2 && topath[1:2] == ":") &&
+			!regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(topath) {
+		return fmt.Errorf("Must specify absolute path")
+	}
 	fw, e := filewalker.NewFileWalker([]string{frompath}, `^[^\.].*`) // orderby: dirfirst, filefirst, fullpath
 	if e != nil {
 		return e
@@ -111,7 +134,7 @@ func sync(frompath, topath string) (err error) {
 			if tofi != nil && fromfi.Size() == tofi.Size() && fromfi.ModTime() == tofi.ModTime() {
 				return true
 			}
-			fmt.Println(fromfile, " ==> ", tofile)
+			fmt.Print(fromfile, " ==> ", tofile)
 			frombs, e := ReadFile(fromfile)
 			if e != nil {
 				err = fmt.Errorf("from file %v", e)
@@ -144,6 +167,8 @@ func sync(frompath, topath string) (err error) {
 }
 
 func ReadFile(name string) (data []byte, err error) {
+	fmt.Print("   -.---")
+	defer fmt.Println()
 	data = make([]byte, 0, 512)
 	for {
 		data, err = ReadFileContinue(name, data)
@@ -181,10 +206,24 @@ func ReadFileContinue(name string, data []byte) ([]byte, error) {
 		data = ndata
 	}
 
+	statuschan := make(chan int, 100)
+	go func() {
+		for range statuschan {
+			s := fmt.Sprintf("%1.3f", float64(offset)/float64(size))
+			bs := strings.Repeat("\b", len(s))
+			fmt.Printf("%s%s", bs, s)
+		}
+	}()
 	for {
-		n, err := f.ReadAt(data[offset:cap(data)], int64(offset))
+		to := offset + 1024*512
+		if to > cap(data) {
+			to = cap(data)
+		}
+		n, err := f.ReadAt(data[offset:to], int64(offset))
 		offset += n
+		statuschan <- offset
 		if err != nil {
+			close(statuschan)
 			return data[:offset], err
 		}
 		if offset >= cap(data) {