mainx.go 16 KB

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