func meshageHandler() { for { m := <-meshageCommandChan go func() { mCmd := m.Body.(meshageCommand) cmd, err := minicli.Compile(mCmd.Original) if err != nil { log.Error("invalid command from mesh: `%s`", mCmd.Original) return } resps := []minicli.Responses{} for resp := range runCommand(cmd) { resps = append(resps, resp) } if len(resps) > 1 || len(resps[0]) > 1 { // This should never happen because the only commands that // return multiple responses are `read` and `mesh send` which // aren't supposed to be sent across meshage. log.Error("unsure how to process multiple responses!!") } resp := meshageResponse{Response: *resps[0][0], TID: mCmd.TID} recipient := []string{m.Source} _, err = meshageNode.Set(recipient, resp) if err != nil { log.Errorln(err) } }() } }
func cliRead(c *minicli.Command, respChan chan minicli.Responses) { resp := &minicli.Response{Host: hostname} file, err := os.Open(c.StringArgs["file"]) if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { var cmd *minicli.Command command := scanner.Text() log.Debug("read command: %v", command) // commands don't have their newlines removed cmd, err = minicli.Compile(command) if err != nil { break } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Make sure we don't have a recursive read command if hasCommand(cmd, "read") { err = errors.New("cannot run nested `read` commands") break } for resp := range minicli.ProcessCommand(cmd) { respChan <- resp // Stop processing at the first error if there is one response. // TODO: What should we do if the command was mesh send and there // is a mixture of success and failure? if len(resp) == 1 && resp[0].Error != "" { break } } } if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } if err := scanner.Err(); err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } }
func main() { flag.Parse() logSetup() // register CLI handlers for i := range cliHandlers { err := minicli.Register(&cliHandlers[i]) if err != nil { log.Fatal("invalid handler, `%v` -- %v", cliHandlers[i].HelpShort, err) } } var err error hostname, err = os.Hostname() if err != nil { log.Fatal("unable to get hostname: %v", hostname) } rond, err = ron.NewServer(*f_port, *f_path) if err != nil { log.Fatal("unable to create server: %v", err) } for { line, err := goreadline.Readline("rond$ ", true) if err != nil { return } command := string(line) log.Debug("got from stdin: `%s`", line) cmd, err := minicli.Compile(command) if err != nil { log.Error("%v", err) continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } for resp := range minicli.ProcessCommand(cmd) { minipager.DefaultPager.Page(resp.String()) errs := resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } } }
func readLocalCommand(dec *json.Decoder) (*minicli.Command, error) { var cmd minicli.Command if err := dec.Decode(&cmd); err != nil { return nil, err } log.Debug("got command over socket: %v", cmd) // HAX: Reprocess the original command since the Call target cannot be // serialized... is there a cleaner way to do this? return minicli.Compile(cmd.Original) }
func TestParseInject(t *testing.T) { for _, cmdStr := range validInjectCommands { cmd, err := minicli.Compile(cmdStr) if err != nil { t.Fatalf("invalid command: %s", cmdStr) } inject := parseInject(cmd) if inject.err != nil { t.Errorf("unable to parse `%s`: %v", cmdStr, inject.err) } } }
func meshageHandler() { for { m := <-meshageCommandChan go func() { mCmd := m.Body.(meshageCommand) cmd, err := minicli.Compile(mCmd.Original) if err != nil { log.Error("invalid command from mesh: `%s`", mCmd.Original) return } // Copy the Record flag at each level of nested command for c, c2 := cmd, &mCmd.Command; c != nil && c2 != nil; { // could all be the post statement... c.Record = c2.Record c.Source = c2.Source c, c2 = c.Subcommand, c2.Subcommand } resps := []minicli.Responses{} for resp := range RunCommands(cmd) { resps = append(resps, resp) } if len(resps) > 1 || len(resps[0]) > 1 { // This should never happen because the only commands that // return multiple responses are `read` and `mesh send` which // aren't supposed to be sent across meshage. log.Error("unsure how to process multiple responses!!") } resp := meshageResponse{Response: *resps[0][0], TID: mCmd.TID} recipient := []string{m.Source} _, err = meshageNode.Set(recipient, resp) if err != nil { log.Errorln(err) } }() } }
// local command line interface, wrapping readline func cliLocal() { goreadline.FilenameCompleter = iomCompleter prompt := "minimega$ " for { line, err := goreadline.Rlwrap(prompt, true) if err != nil { break // EOF } command := string(line) log.Debug("got from stdin:", command) cmd, err := minicli.Compile(command) if err != nil { log.Error("%v", err) //fmt.Printf("closest match: TODO\n") continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Don't record the read command if hasCommand(cmd, "read") { cmd.Record = false } for resp := range runCommand(cmd) { // print the responses miniclient.Pager(resp.String()) errs := resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } } }
func cliRead(c *minicli.Command, respChan chan minicli.Responses) { resp := &minicli.Response{Host: hostname} file, err := os.Open(c.StringArgs["file"]) if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} return } defer file.Close() // HACK: We *don't* want long-running read commands to cause all other // commands to block so we *unlock* the command lock here and *lock* it // again for each command that we read (well, `runCommand` handles the // locking for us). cmdLock.Unlock() defer cmdLock.Lock() scanner := bufio.NewScanner(file) for scanner.Scan() { var cmd *minicli.Command command := scanner.Text() log.Debug("read command: %v", command) // commands don't have their newlines removed cmd, err = minicli.Compile(command) if err != nil { break } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Make sure we don't have a recursive read command if hasCommand(cmd, "read") { err = errors.New("cannot run nested `read` commands") break } for resp := range runCommand(cmd) { respChan <- resp // Stop processing if any of the responses have an error. for _, r := range resp { if r.Error != "" { break } } } } if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } if err := scanner.Err(); err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } }
func cliRead(c *minicli.Command, respChan chan<- minicli.Responses) { resp := &minicli.Response{Host: hostname} check := c.BoolArgs["check"] file, err := os.Open(c.StringArgs["file"]) if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} return } defer file.Close() // HACK: We *don't* want long-running read commands to cause all other // commands to block so we *unlock* the command lock here and *lock* it // again for each command that we read (well, `RunCommands` handles the // locking for us). cmdLock.Unlock() defer cmdLock.Lock() scanner := bufio.NewScanner(file) for scanner.Scan() { var cmd *minicli.Command command := scanner.Text() log.Debug("read command: %v", command) cmd, err = minicli.Compile(command) if err != nil { break } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Make sure we don't have a recursive read command if hasCommand(cmd, "read") { err = errors.New("cannot run nested `read` commands") break } // If we're checking the syntax, don't actually execute the command if check { continue } forward(RunCommands(cmd), respChan) } if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } if err := scanner.Err(); err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } }
// local command line interface, wrapping readline func cliLocal() { goreadline.FilenameCompleter = iomCompleter sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) go func() { for range sig { goreadline.Signal() } }() defer signal.Stop(sig) for { namespace := GetNamespaceName() prompt := "minimega$ " if namespace != "" { prompt = fmt.Sprintf("minimega[%v]$ ", namespace) } line, err := goreadline.Readline(prompt, true) if err != nil { return } command := string(line) log.Debug("got from stdin: `%v`", command) cmd, err := minicli.Compile(command) if err != nil { log.Error("%v", err) //fmt.Printf("closest match: TODO\n") continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Don't record the read command if hasCommand(cmd, "read") { cmd.SetRecord(false) } // The namespace changed between when we prompted the user (and could // still change before we actually run the command). if namespace != GetNamespaceName() { // TODO: should we abort the command? log.Warn("namespace changed between prompt and execution") } for resp := range RunCommands(cmd) { // print the responses minipager.DefaultPager.Page(resp.String()) errs := resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } } }
// Attach creates a CLI interface to the dialed minimega instance func (mm *Conn) Attach() { // set up signal handling sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) go func() { for s := range sig { if s == os.Interrupt { goreadline.Signal() } else { log.Debug("caught term signal, disconnecting") goreadline.Rlcleanup() os.Exit(0) } } }() defer signal.Stop(sig) // start our own rlwrap fmt.Println("CAUTION: calling 'quit' will cause the minimega daemon to exit") fmt.Println("use 'disconnect' or ^d to exit just the minimega command line") fmt.Println() defer goreadline.Rlcleanup() var exitNext bool for { prompt := fmt.Sprintf("minimega:%v$ ", mm.url) line, err := goreadline.Readline(prompt, true) if err != nil { return } command := string(line) log.Debug("got from stdin: `%s`", line) // HAX: Shortcut some commands without using minicli if command == "disconnect" { log.Debugln("disconnecting") return } else if command == "quit" { if !exitNext { fmt.Println("CAUTION: calling 'quit' will cause the minimega daemon to exit") fmt.Println("If you really want to stop the minimega daemon, enter 'quit' again") exitNext = true continue } } exitNext = false cmd, err := minicli.Compile(command) if err != nil { log.Error("%v", err) //fmt.Println("closest match: TODO") continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } mm.RunAndPrint(cmd, true) if command == "quit" { return } } }