libf 2 rokov pred
rodič
commit
cc8f5c5feb
5 zmenil súbory, kde vykonal 681 pridanie a 0 odobranie
  1. 25 0
      go.mod
  2. 35 0
      go.sum
  3. 373 0
      msh/client.go
  4. 153 0
      msh/config.go
  5. 95 0
      msh/main.go

+ 25 - 0
go.mod

@@ -3,8 +3,33 @@ module test
 go 1.19
 
 require (
+	git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10 // indirect
 	git.wecise.com/wecise/odbserver v0.0.0-20221017135833-5936b510aa0f // indirect
 	github.com/alphadose/haxmap v1.0.0 // indirect
+	github.com/atrox/homedir v1.0.0 // indirect
+	github.com/coreos/go-semver v0.3.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
 	github.com/cornelk/hashmap v1.0.8 // indirect
+	github.com/fatih/color v1.9.0 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/kevinburke/ssh_config v1.2.0 // indirect
+	github.com/mattn/go-colorable v0.1.7 // indirect
+	github.com/mattn/go-isatty v0.0.12 // indirect
 	github.com/orcaman/concurrent-map v1.0.0 // indirect
+	go.etcd.io/etcd/api/v3 v3.5.1 // indirect
+	go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
+	go.etcd.io/etcd/client/v3 v3.5.1 // indirect
+	go.uber.org/atomic v1.7.0 // indirect
+	go.uber.org/multierr v1.6.0 // indirect
+	go.uber.org/zap v1.17.0 // indirect
+	golang.org/x/crypto v0.1.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79 // indirect
+	google.golang.org/grpc v1.39.0 // indirect
+	google.golang.org/protobuf v1.27.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 )

+ 35 - 0
go.sum

@@ -5,6 +5,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
 git.wecise.com/wecise/common v0.0.0-20220208112539-631931c6a1b3/go.mod h1:Klk84GDRk1E8HdWNg1vdut3I1PIpzhMCDY8l8U5lfwE=
 git.wecise.com/wecise/common v0.0.0-20220922070633-840e60243e5a/go.mod h1:GbtDqXBvApsvqEkkQmo/yjKPgtalRB+h8b4Yu8j9EGc=
+git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10 h1:IGD+Yh12p2kMhORpom+mNzaxex2cmwH5L3akDw37RaQ=
+git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10/go.mod h1:iZzB7N61v5oSQUruMRlqAVDCLDWKvaquiHwaznqg9PM=
 git.wecise.com/wecise/odb-go v0.0.0-20211124085409-0d3e38018145/go.mod h1:RUaugeb49Q7CKD1szlFvoVmw657wrVGnt0QMMZrhMTg=
 git.wecise.com/wecise/odb-go v0.0.0-20220825070427-e443cba6de6d/go.mod h1:RUaugeb49Q7CKD1szlFvoVmw657wrVGnt0QMMZrhMTg=
 git.wecise.com/wecise/odbserver v0.0.0-20221017135833-5936b510aa0f h1:C5nR+6yqciHqL1ZHdvRut2Gxp+/HfO79jX2lWkU9di0=
@@ -51,6 +53,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
 github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
 github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/atrox/homedir v1.0.0 h1:99Vwk+XECZTDLaAPeMj7vF9JMNcVarWddqPeyDzJT5E=
+github.com/atrox/homedir v1.0.0/go.mod h1:ZKVEIDNKscX8qV1TyrwLP+ayjv3XQO7wbVmc5EW00A8=
 github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -92,7 +96,9 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cornelk/hashmap v1.0.2-0.20200117061707-33d9f787ba8d/go.mod h1:SI48x/mQnWtjsJuM7GgmODo4o5O8FhGJgClCRmtOtIQ=
 github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
@@ -132,6 +138,7 @@ github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htX
 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
 github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+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/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@@ -170,6 +177,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
 github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -189,6 +197,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -260,6 +269,8 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
 github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
 github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+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/kipriz/wgwaiter v0.0.0-20190606091156-ffbaffa3dbd2/go.mod h1:4WKQur/VzBqYF2706xitDOiNbdAGYK/Ws9G9z7v3oO8=
 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=
@@ -286,10 +297,12 @@ github.com/lukechampine/fastxor v0.0.0-20200124170337-07dbf569dfe7/go.mod h1:iR9
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mattheath/goflake v0.0.0-20150406224507-362f1883c5c2/go.mod h1:q89lqEssq0JkS+ZcqTtDpkNp8VoNbGFlNi5Uge5rM8k=
 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.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -316,6 +329,7 @@ github.com/nats-io/jwt/v2 v2.1.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgv
 github.com/nats-io/nats-server/v2 v2.3.0/go.mod h1:7v4HvHI2Zu4n1775982gHbvBNXywHeaTj1WGo0S+uFI=
 github.com/nats-io/nats.go v1.11.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nats.go v1.13.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
+github.com/nats-io/nats.go v1.17.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
 github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
@@ -455,13 +469,19 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd/api/v3 v3.5.1 h1:v28cktvBq+7vGyJXF8G+rWJmj+1XUmMtqcLnH8hDocM=
 go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.1 h1:XIQcHCFSG53bJETYeRJtIxdLv2EWRGxcfzR8lSnTH4E=
 go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v3 v3.5.1 h1:oImGuV5LGKjCqXdjkMHCyWa5OO1gYKCnC/1sgdfj1Uk=
 go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -480,6 +500,8 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -536,6 +558,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -592,14 +616,20 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -640,6 +670,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79 h1:s1jFTXJryg4a1mew7xv03VZD8N9XjxFhk1o4Js4WvPQ=
 google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -649,6 +680,7 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
 google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -661,6 +693,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -685,6 +718,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200602174320-3e3e88ca92fa/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 373 - 0
msh/client.go

@@ -0,0 +1,373 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net"
+	"os"
+	"os/user"
+	"path"
+	"regexp"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
+
+	"git.wecise.com/wecise/common/logger"
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/crypto/ssh/terminal"
+)
+
+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 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(err)
+		return nil
+	}
+
+	var authMethods []ssh.AuthMethod
+
+	var pemBytes []byte
+	if node.KeyPath == "" {
+		pemBytes, err = ioutil.ReadFile(path.Join(u.HomeDir, ".ssh/id_rsa"))
+	} else {
+		pemBytes, err = ioutil.ReadFile(node.KeyPath)
+	}
+	if err != nil && !os.IsNotExist(err) {
+		logger.Error(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(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 := terminal.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(err)
+			return
+		}
+		conn, err := proxyClient.Dial("tcp", net.JoinHostPort(host, port))
+		if err != nil {
+			logger.Error(err)
+			return
+		}
+		ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(host, port), c.clientConfig)
+		if err != nil {
+			logger.Error(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 = terminal.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(err)
+			return
+		}
+	}
+	defer client.Close()
+
+	logger.Warnf("connect server ssh -p %d %s@%s version: %s\n", c.node.port(), c.node.user(), host, string(client.ServerVersion()))
+
+	session, err := client.NewSession()
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+	defer session.Close()
+
+	fd := int(os.Stdin.Fd())
+	w, h, err := terminal.GetSize(fd)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	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(err)
+		return
+	}
+
+	stdinPipe, err := session.StdinPipe()
+	if err != nil {
+		logger.Error(err)
+		session.Stdin = os.Stdin
+	}
+	stdoutPipe, err := session.StdoutPipe()
+	if err != nil {
+		logger.Error(err)
+		session.Stdout = os.Stdout
+	}
+	stderrPipe, err := session.StderrPipe()
+	if err != nil {
+		logger.Error(err)
+		session.Stderr = os.Stderr
+	}
+
+	err = session.Shell()
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	cmdidx := 0
+	regxpassword := regexp.MustCompile(`.*[Pp]assword: *$`)
+	regxprompt := regexp.MustCompile(`.*[%#\$\>]\s*$`)
+	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(err)
+				return
+			}
+			if len(bs) > 0 {
+				change = true
+				s := string(bs)
+				localstdout.Write([]byte(s))
+				as += s
+			} else if change {
+				change = false
+				if regxpassword.MatchString(as) {
+					p := c.node.Commands[cmdidx].Password
+					if p == "" {
+						p = c.node.Commands[0].Password
+					}
+					// don't echo password, os.Stdout.Write([]byte(p + "\n"))
+					stdinPipe.Write([]byte(p + "\n"))
+				}
+				if regxprompt.MatchString(as) {
+					cmdidx++
+					if cmdidx >= len(c.node.Commands) {
+						localstdout.Write([]byte("exit" + "\n"))
+						stdinPipe.Write([]byte("exit" + "\n"))
+					} else {
+						localstdout.Write([]byte(c.node.Commands[cmdidx].Cmd + "\n"))
+						stdinPipe.Write([]byte(c.node.Commands[cmdidx].Cmd + "\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(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 := terminal.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()
+	logger.Warnf("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
+	}
+}

+ 153 - 0
msh/config.go

@@ -0,0 +1,153 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/user"
+	"path"
+	"regexp"
+	"strconv"
+
+	"git.wecise.com/wecise/common/logger"
+	"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
+	Output string
+}
+
+type Command struct {
+	Cmd      string `yaml:"cmd"`
+	Password string `yaml:"password"`
+	Regexp   []*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(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 := ioutil.ReadFile(path.Join(u.HomeDir, names[i]))
+		if err == nil {
+			return sshw, nil
+		}
+	}
+	// relative
+	for i := range names {
+		sshw, err := ioutil.ReadFile(names[i])
+		if err == nil {
+			return sshw, nil
+		}
+	}
+	return nil, err
+}

+ 95 - 0
msh/main.go

@@ -0,0 +1,95 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"os/user"
+	"regexp"
+	"strings"
+
+	"git.wecise.com/wecise/common/logger"
+)
+
+func init() {
+	logger.SetConsole(true)
+	logger.SetLevel(logger.INFO)
+}
+
+func uch() (u, c, h string, o bool) {
+	if len(os.Args) < 2 {
+		return
+	}
+	if regexp.MustCompile(`p=.+`).MatchString(os.Args[1]) {
+		user, e := user.Current()
+		if e != nil {
+			logger.Error(e)
+			return
+		}
+		u = user.Username
+		c = os.Args[1][2:]
+		h = "127.0.0.1"
+		o = true
+		return
+	}
+	uch := strings.SplitN(os.Args[1], ":", 2)
+	if len(uch) < 2 {
+		return
+	}
+	n := strings.LastIndex(uch[1], "@")
+	if n < 0 {
+		return
+	}
+	return uch[0], uch[1][:n], uch[1][n+1:], true
+}
+
+func main() {
+	u, c, h, o := uch()
+	if !o {
+		fmt.Println("usage:")
+		fmt.Println("  msh [user:code@host]|[p=password] [[c=]command [p=password] [[r=regexp] [o=output]]...]...")
+		return
+	}
+
+	cmds := []*Command{{Cmd: "", Password: c, Regexp: []*Regexp{{Regexp: nil, Output: ""}}}}
+	for i := 2; i < len(os.Args); i++ {
+		arg := os.Args[i]
+		kv := strings.SplitN(arg, "=", 2)
+		var cmd string
+		switch kv[0] {
+		case "cmd", "command", "c":
+			cmd = kv[1]
+		case "password", "code", "pass", "p":
+			cmds[len(cmds)-1].Password = kv[1]
+			continue
+		case "re", "r", "regex":
+			re, err := regexp.Compile(kv[1])
+			if err != nil {
+				logger.Error(err)
+			} else {
+				cmds[len(cmds)-1].Regexp = append(cmds[len(cmds)-1].Regexp, &Regexp{Regexp: re})
+			}
+			continue
+		case "out", "o", "output":
+			cmds[len(cmds)-1].Regexp[len(cmds[len(cmds)-1].Regexp)-1].Output = kv[1]
+			continue
+		default:
+			cmd = arg
+		}
+		cmds = append(cmds, &Command{Cmd: cmd, Password: c, Regexp: []*Regexp{{Regexp: nil, Output: ""}}})
+	}
+	node := &Node{
+		Name:       "x",
+		Alias:      "x",
+		Host:       h,
+		User:       u,
+		Port:       22,
+		KeyPath:    "",
+		Passphrase: "",
+		Password:   c,
+		Commands:   cmds,
+		Children:   []*Node{},
+		Jump:       []*Node{},
+	}
+	sshc := NewClient(node)
+	sshc.Login()
+}