main.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "io/fs"
  9. "os"
  10. "os/exec"
  11. "os/signal"
  12. "os/user"
  13. "reflect"
  14. "regexp"
  15. "strings"
  16. "time"
  17. "git.wecise.com/wecise/common/matrix/logger"
  18. "git.wecise.com/wecise/common/matrix/util"
  19. "github.com/creack/pty"
  20. "golang.org/x/term"
  21. "gopkg.in/yaml.v2"
  22. )
  23. func init() {
  24. logger.SetConsole(true)
  25. logger.SetLevel(logger.TRACE)
  26. logger.SetFormat("yyyy-MM-dd HH:mm:ss.SSS [level] msg", "\n")
  27. }
  28. type KV struct{ Key, Val string }
  29. func parseArgs(args []string) (kvs []*KV) {
  30. argk := ""
  31. argv := ""
  32. for _, arg := range args {
  33. if argk != "" {
  34. argv = arg
  35. } else if regexp.MustCompile(`^\-\w+$`).MatchString(arg) {
  36. argk = arg[1:]
  37. continue
  38. } else {
  39. kv := strings.SplitN(arg, "=", 2)
  40. if len(kv) == 2 {
  41. argk = kv[0]
  42. argv = kv[1]
  43. } else {
  44. argk = ""
  45. argv = arg
  46. }
  47. }
  48. kvs = append(kvs, &KV{argk, argv})
  49. argk, argv = "", ""
  50. }
  51. if argk != "" {
  52. kvs = append(kvs, &KV{argk, argv})
  53. }
  54. return
  55. }
  56. func usage() {
  57. fmt.Println("usage:")
  58. fmt.Println(" msh p=password|a=passcode [[c=]command [p=password|a=passcode] [x=cmd-end-regexp] [[r=regexp] [o=output|n=outputline]]...]...")
  59. fmt.Println(" a=passcode should be base64 encoded, or use p=password")
  60. fmt.Println(" debug info include: p(progress) a(argments) m(match) 1(all)")
  61. }
  62. func main() {
  63. // get user
  64. u := func() string {
  65. user, err := user.Current()
  66. if err != nil {
  67. logger.Error(util.ErrorWithSourceLine(err))
  68. return ""
  69. }
  70. return user.Username
  71. }()
  72. // get passcode,取默认密码设置
  73. c := ""
  74. kvs := parseArgs(os.Args)
  75. for _, kv := range kvs {
  76. if kv.Key == "a" {
  77. c = kv.Val
  78. if c == "" {
  79. c = "="
  80. }
  81. break
  82. }
  83. if kv.Key == "p" {
  84. c = "=" + kv.Val
  85. break
  86. }
  87. }
  88. // get password
  89. p := c
  90. if p == "" {
  91. usage()
  92. return
  93. } else if p[0:1] == "=" {
  94. p = p[1:]
  95. } else {
  96. x, e := base64.RawStdEncoding.DecodeString(p)
  97. if e == nil {
  98. p = string(x)
  99. }
  100. // else 不是Base64编码,保持原值
  101. }
  102. // explainArgs key, val
  103. cmds := []*Command{{Cmd: "sh", Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt}}
  104. i := 0
  105. // 掠过第一个参数,当前执行程序
  106. kvs = kvs[1:]
  107. for _, kv := range kvs {
  108. key, val := kv.Key, kv.Val
  109. switch key {
  110. case "", "cmd", "command", "c":
  111. cmds = append(cmds, &Command{Cmd: val, Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt})
  112. case "ry", "regex_yes_no":
  113. re, err := regexp.Compile(val)
  114. if err != nil {
  115. logger.Error("arg", i, util.ErrorWithSourceLine(err))
  116. return
  117. } else {
  118. regxyesno = &Regexp{re}
  119. }
  120. case "rc", "regex_passcode", "regex_password":
  121. re, err := regexp.Compile(val)
  122. if err != nil {
  123. logger.Error("arg", i, util.ErrorWithSourceLine(err))
  124. return
  125. } else {
  126. regxpassword = &Regexp{re}
  127. }
  128. case "rp", "regex_prompt":
  129. re, err := regexp.Compile(val)
  130. if err != nil {
  131. logger.Error("arg", i, util.ErrorWithSourceLine(err))
  132. return
  133. } else {
  134. regxprompt = &Regexp{re}
  135. }
  136. case "password", "code", "pass", "p":
  137. cmds[len(cmds)-1].Password = "=" + val
  138. case "passcode", "b64code", "a":
  139. cmds[len(cmds)-1].Password = val
  140. case "re", "r", "regex":
  141. if val == "" {
  142. cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: nil})
  143. } else {
  144. re, err := regexp.Compile(val)
  145. if err != nil {
  146. logger.Error("arg", i, util.ErrorWithSourceLine(err))
  147. return
  148. } else {
  149. cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: &Regexp{re}})
  150. }
  151. }
  152. case "x", "end":
  153. if val == "" {
  154. cmds[len(cmds)-1].Endregx = regxprompt
  155. } else {
  156. re, err := regexp.Compile(val)
  157. if err != nil {
  158. logger.Error("arg", i, util.ErrorWithSourceLine(err))
  159. return
  160. } else {
  161. cmds[len(cmds)-1].Endregx = &Regexp{re}
  162. }
  163. }
  164. case "out", "o", "output":
  165. cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val
  166. case "outln", "n", "outputline":
  167. cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val + "\n"
  168. case "debug", "d":
  169. cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Debug += val
  170. }
  171. }
  172. if strings.Index(cmds[0].Regexps[0].Debug, "a") >= 0 || strings.Index(cmds[0].Regexps[0].Debug, "1") >= 0 {
  173. //bs, _ := json.MarshalIndent(cmds, "", " ")
  174. bs, _ := yaml.Marshal(cmds)
  175. logger.Debug("arguments:\n" + string(bs))
  176. }
  177. client := &Client{
  178. User: u,
  179. Password: p,
  180. Commands: cmds,
  181. }
  182. client.Run()
  183. }
  184. type Regexp struct {
  185. *regexp.Regexp
  186. }
  187. func (r *Regexp) MarshalJSON() ([]byte, error) {
  188. if r == nil || r.Regexp == nil {
  189. return json.Marshal(nil)
  190. }
  191. return json.Marshal(r.String())
  192. }
  193. func (r *Regexp) MarshalYAML() (interface{}, error) {
  194. if r == nil || r.Regexp == nil {
  195. return nil, nil
  196. }
  197. return r.String(), nil
  198. }
  199. type Matcher struct {
  200. Regexp *Regexp
  201. Output string `yaml:"output"`
  202. Debug string `yaml:"debug"`
  203. }
  204. type Command struct {
  205. Cmd string `yaml:"cmd"`
  206. Password string `yaml:"password"`
  207. Regexps []*Matcher
  208. Endregx *Regexp
  209. }
  210. var regxyesno = &Regexp{regexp.MustCompile(`.*(\(yes\/no\)\?)\s*$`)}
  211. var regxpassword = &Regexp{regexp.MustCompile(`.*([Pp]assword:)\s*$`)}
  212. var regxprompt = &Regexp{regexp.MustCompile(`.*([%#\$\>])\s*$`)}
  213. type Client struct {
  214. User string
  215. Password string
  216. Commands []*Command
  217. }
  218. func Pipe(reader io.Reader, writer io.Writer, pfs ...func(lastbs []byte, sin string) (sout string)) {
  219. btr := NewBTReader("", reader, 30*time.Millisecond, 1024)
  220. lastbs := []byte{'\n'}
  221. for {
  222. bs, err := btr.Read()
  223. if err != nil {
  224. if err == io.EOF {
  225. return
  226. }
  227. logger.Error(util.ErrorWithSourceLine(err))
  228. return
  229. }
  230. if len(bs) > 0 {
  231. xbs := bs
  232. for _, pf := range pfs {
  233. if pf != nil {
  234. s := pf(lastbs, string(xbs))
  235. xbs = []byte(s)
  236. }
  237. }
  238. if writer != nil {
  239. writer.Write(xbs)
  240. }
  241. lastbs = bs
  242. }
  243. }
  244. }
  245. func (c *Client) Run() {
  246. logger.Info("msh ready")
  247. // Create arbitrary command.
  248. cmd := exec.Command("sh", "-c", "export PS1='\\h:> ';sh -i")
  249. // Start the command with a pty.
  250. ptmx, err := pty.Start(cmd)
  251. if err != nil {
  252. logger.Error(err)
  253. return
  254. }
  255. // Make sure to close the pty at the end.
  256. defer func() { _ = ptmx.Close() }() // Best effort.
  257. // Handle pty size.
  258. ch := HandlePTYSize(ptmx)
  259. if ch != nil {
  260. defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.
  261. }
  262. // Set stdin in raw mode.
  263. oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
  264. if err != nil {
  265. logger.Error(err)
  266. }
  267. go Pipe(os.Stdin, ptmx)
  268. e := c.IOProc(ptmx, os.Stdout, ptmx)
  269. if oldState != nil {
  270. term.Restore(int(os.Stdin.Fd()), oldState)
  271. fmt.Println()
  272. }
  273. if cmd.ProcessState.ExitCode() == -1 && e != nil {
  274. logger.Error(e)
  275. return
  276. }
  277. logger.Info("msh exit")
  278. return
  279. }
  280. func (c *Client) IOProc(cmdout io.Reader, stdout io.Writer, cmdin io.Writer) error {
  281. cmdidx := 0
  282. as := ""
  283. change := false
  284. stdoutbtr := NewBTReader("", cmdout, 50*time.Millisecond, 1024)
  285. for {
  286. bs, err := stdoutbtr.Read()
  287. if err != nil {
  288. if err == io.EOF {
  289. return nil
  290. }
  291. if _, y := err.(*fs.PathError); y {
  292. return nil
  293. }
  294. return util.ErrorWithSourceLine(err, reflect.TypeOf(err))
  295. }
  296. if len(bs) > 0 {
  297. change = true
  298. s := string(bs)
  299. stdout.Write([]byte(s))
  300. as += s
  301. } else if change {
  302. change = false
  303. if cmdidx < len(c.Commands) {
  304. if msi := c.Commands[cmdidx].Endregx.FindStringSubmatchIndex(as); msi != nil {
  305. // logger.Error("EndregexMatch", as)
  306. match := as[msi[0]:msi[1]]
  307. if strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
  308. logger.Trace("match end:", "'"+match+"'")
  309. }
  310. as = "" // 全清,开始新的命令
  311. cmdidx++
  312. if cmdidx >= len(c.Commands) {
  313. return nil
  314. }
  315. if strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "p") >= 0 || strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
  316. logger.Trace("command:", c.Commands[cmdidx].Cmd)
  317. }
  318. // stdout.Write([]byte(c.Commands[cmdidx].Cmd + "\n"))
  319. cmdin.Write([]byte(c.Commands[cmdidx].Cmd + "\n"))
  320. continue
  321. }
  322. for _, regex := range c.Commands[cmdidx].Regexps {
  323. if regex == nil {
  324. continue
  325. }
  326. if regex.Regexp == nil {
  327. // like match all
  328. if regex.Output != "" {
  329. // stdout.Write([]byte(regex.Output))
  330. cmdin.Write([]byte(regex.Output))
  331. }
  332. } else if msi := regex.Regexp.FindStringSubmatchIndex(as); msi != nil {
  333. match := as[msi[0]:msi[1]]
  334. if len(msi) >= 4 {
  335. match = as[msi[2]:msi[3]]
  336. as = as[msi[3]:] // 清除已处理完的内容
  337. } else {
  338. as = as[msi[1]:] // 清除已处理完的内容
  339. }
  340. if strings.Index(regex.Debug, "m") >= 0 || strings.Index(regex.Debug, "1") >= 0 {
  341. logger.Trace("match regex:", "'"+match+"'")
  342. }
  343. if regex.Output != "" {
  344. // stdout.Write([]byte(regex.Output))
  345. cmdin.Write([]byte(regex.Output))
  346. }
  347. }
  348. }
  349. }
  350. if msi := regxyesno.FindStringSubmatchIndex(as); msi != nil {
  351. match := as[msi[0]:msi[1]]
  352. if len(msi) >= 4 {
  353. as = as[msi[3]:] // 清除已处理完的内容
  354. } else {
  355. as = as[msi[1]:] // 清除已处理完的内容
  356. }
  357. if strings.Index(c.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.Commands[0].Regexps[0].Debug, "1") >= 0 ||
  358. cmdidx < len(c.Commands) &&
  359. (strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
  360. logger.Trace("match yesno:", "'"+match+"'")
  361. }
  362. // stdout.Write([]byte("yes\n"))
  363. cmdin.Write([]byte("yes\n"))
  364. }
  365. if msi := regxpassword.FindStringSubmatchIndex(as); msi != nil {
  366. // logger.Error(as)
  367. match := as[msi[0]:msi[1]]
  368. if len(msi) >= 4 {
  369. as = as[msi[3]:] // 清除已处理完的内容
  370. } else {
  371. as = as[msi[1]:] // 清除已处理完的内容
  372. }
  373. p := c.Commands[0].Password
  374. if cmdidx < len(c.Commands) {
  375. p = c.Commands[cmdidx].Password
  376. }
  377. if strings.Index(c.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.Commands[0].Regexps[0].Debug, "1") >= 0 ||
  378. cmdidx < len(c.Commands) &&
  379. (strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
  380. logger.Trace("match password:", "'"+match+"'")
  381. }
  382. if p != "" {
  383. if p[0:1] == "=" {
  384. p = p[1:]
  385. } else {
  386. x, e := base64.RawStdEncoding.DecodeString(p)
  387. if e == nil {
  388. p = string(x)
  389. }
  390. // else 不是Base64编码,保持原值
  391. }
  392. // don't echo password
  393. if c.Commands[0].Regexps[0].Debug != "" || cmdidx < len(c.Commands) && c.Commands[cmdidx].Regexps[0].Debug != "" {
  394. stdout.Write([]byte(p + "\n"))
  395. }
  396. cmdin.Write([]byte(p + "\n"))
  397. }
  398. }
  399. if len(as) > 1024 {
  400. as = as[len(as)-1024:]
  401. }
  402. }
  403. }
  404. }
  405. type BTReader struct {
  406. *bufio.Reader
  407. flag string
  408. chbs chan []byte
  409. cher chan error
  410. timeout time.Duration
  411. sizeout int
  412. err error
  413. }
  414. func NewBTReader(flag string, reader io.Reader, timeout time.Duration, sizeout int) (me *BTReader) {
  415. me = &BTReader{Reader: bufio.NewReader(reader),
  416. flag: flag,
  417. chbs: make(chan []byte),
  418. cher: make(chan error),
  419. timeout: timeout,
  420. sizeout: sizeout,
  421. }
  422. go func() {
  423. bs := make([]byte, me.Size())
  424. for {
  425. n, err := me.Reader.Read(bs[:1])
  426. if err != nil {
  427. me.cher <- err
  428. return
  429. }
  430. x, err := me.Reader.Read(bs[1 : me.Reader.Buffered()+1])
  431. if err != nil {
  432. me.cher <- err
  433. return
  434. }
  435. n += x
  436. abs := make([]byte, n)
  437. copy(abs, bs[:n])
  438. me.chbs <- abs
  439. }
  440. }()
  441. return
  442. }
  443. // 指定时间内没有新数据进入,且有积累数据,或积累数据超过指定数量,即返回
  444. func (me *BTReader) Read() (rbs []byte, err error) {
  445. if me.err != nil {
  446. return nil, me.err
  447. }
  448. for {
  449. t := time.NewTimer(me.timeout)
  450. select {
  451. case me.err = <-me.cher:
  452. if len(rbs) > 0 {
  453. // 返回最后的数据,下次读时才返回错误
  454. return rbs, nil
  455. }
  456. return nil, me.err
  457. case abs := <-me.chbs:
  458. rbs = append(rbs, abs...)
  459. if len(rbs) > me.sizeout {
  460. return
  461. }
  462. t.Stop()
  463. t.Reset(me.timeout)
  464. case <-t.C:
  465. if len(rbs) == 0 {
  466. t.Stop()
  467. t.Reset(me.timeout)
  468. }
  469. return
  470. }
  471. }
  472. }