main.go 13 KB

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