func (ctrl *Controller) terminal(ctx scope.Context, ch ssh.Channel) { defer ch.Close() lines := make(chan string) term := terminal.NewTerminal(ch, "> ") go func() { for ctx.Err() == nil { line, err := term.ReadLine() if err != nil { ctx.Terminate(err) return } lines <- line } }() for { var line string select { case <-ctx.Done(): return case line = <-lines: } cmd := parse(line) fmt.Printf("[control] > %v\n", cmd) switch cmd[0] { case "": continue case "quit": return case "shutdown": ctrl.ctx.Terminate(fmt.Errorf("shutdown initiated from console")) default: runCommand(ctx.Fork(), ctrl, cmd[0], term, cmd[1:]) } } }
func startTerminal(c *config) { var clear, protected bool fmt.Println("Starting terminal mode.") fmt.Println("Enter h for [h]elp.") fmt.Println("Enter l for [l]ist of commands.") fmt.Println("Enter q for [q]uit.") termState, err := terminal.MakeRaw(int(os.Stdin.Fd())) if err != nil { fmt.Fprintf(os.Stderr, "Failed to set raw mode on STDIN: %v\n", err) return } n := terminal.NewTerminal(os.Stdin, "> ") n.SetSize(int(^uint(0)>>1), 0) var ln string for { if !protected { ln, err = n.ReadLine() } else { ln, err = n.ReadPassword(">*") } terminal.Restore(int(os.Stdin.Fd()), termState) if err != nil { break } quit := execute(&protected, c, ln, &clear) if quit { break } if clear { fmt.Println("Clearing history...") termState, err = terminal.MakeRaw(int(os.Stdin.Fd())) if err != nil { fmt.Fprintf(os.Stderr, "Failed to set raw "+ "mode on STDIN: %v\n", err) break } n = terminal.NewTerminal(os.Stdin, "> ") n.SetSize(int(^uint(0)>>1), 0) clear = false } else { termState, err = terminal.MakeRaw(int(os.Stdin.Fd())) if err != nil { fmt.Fprintf(os.Stderr, "Failed to set raw "+ "mode on STDIN: %v\n", err) break } } } fmt.Println("exiting...") }
// NewCLI creates a new cliUI instance func NewCLI(version string) UI { oldState, err := terminal.MakeRaw(0) if err != nil { panic(err.Error()) } term := terminal.NewTerminal(os.Stdin, "") updateTerminalSize(term) term.SetBracketedPasteMode(true) resizeChan := make(chan os.Signal) go func() { for _ = range resizeChan { updateTerminalSize(term) } }() signal.Notify(resizeChan, syscall.SIGWINCH) return &cliUI{ term: term, oldState: oldState, terminate: make(chan bool), input: &input{ term: term, uidComplete: new(priorityList), }, RosterEditor: RosterEditor{ PendingRosterChan: make(chan *RosterEdit), }, events: make(chan interface{}), commands: make(chan interface{}, 5), } }
// promptConfirm prompts a user to confirm (or deny) something. // // True is returned iff the prompt is confirmed. // Errors are reported to the log, and return false. // // Valid confirmations: // y, yes, true, t, aye-aye // // Valid denials: // n, no, f, false // // Any other prompt response will return false, and issue a warning to the // user. func promptConfirm(msg string) bool { oldState, err := terminal.MakeRaw(0) if err != nil { log.Err("Could not get terminal: %s", err) return false } defer terminal.Restore(0, oldState) f := readerWriter(log.Stdin, log.Stdout) t := terminal.NewTerminal(f, msg+" (y/N) ") res, err := t.ReadLine() if err != nil { log.Err("Could not read line: %s", err) return false } res = strings.ToLower(res) switch res { case "yes", "y", "true", "t", "aye-aye": return true case "no", "n", "false", "f": return false } log.Warn("Did not understand answer %q, assuming No", res) return false }
func (self *ServerMode) handleSSHRequest(connection *net.Conn, config *ssh.ServerConfig) { // TODO: Check if we need to close anything. // Before use, a handshake must be performed on the incoming net.Conn. _, channels, requests, err := ssh.NewServerConn(*connection, config) if err != nil { panic("failed to handshake") } // The incoming Request channel must be serviced. go ssh.DiscardRequests(requests) // Service the incoming Channel channel. for newChannel := range channels { // Channels have a type, depending on the application level protocol intended. In the case of a shell, the type is // "session" and ServerShell may be used to present a simple terminal interface. if newChannel.ChannelType() != "session" { newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") continue } channel, requests, err := newChannel.Accept() if err != nil { panic("could not accept channel.") } // Sessions have out-of-band requests such as "shell", "pty-req" and "env". // Here we handle only the "shell" request. go func(in <-chan *ssh.Request) { for req := range in { ok := false switch req.Type { case "shell": ok = true if len(req.Payload) > 0 { // We don't accept any commands, only the default shell. ok = false } } req.Reply(ok, nil) } }(requests) term := terminal.NewTerminal(channel, "> ") go func() { defer channel.Close() for { line, err := term.ReadLine() if err != nil { break } // TODO: Likely we need to interpret the incoming commands in here. fmt.Println("INPUT-SSH:" + line) } }() } }
func sshHandleChannel(conn net.Conn, newChannel ssh.NewChannel) { // Channels have a type, depending on the application level protocol // intended. In the case of a shell, the type is "session" and ServerShell // may be used to present a simple terminal interface. if newChannel.ChannelType() != "session" { newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") return } channel, requests, err := newChannel.Accept() if err != nil { log.Errorln(err) return } // Sessions have out-of-band requests such as "shell", "pty-req" and "env". // Here we handle only the "shell" request. go func(in <-chan *ssh.Request) { for req := range in { ok := false switch req.Type { case "shell": ok = true if len(req.Payload) > 0 { // We don't accept any commands, only the default shell. ok = false } case "pty-req": ok = true } req.Reply(ok, nil) } }(requests) term := terminal.NewTerminal(channel, "> ") go func() { defer channel.Close() for { line, err := term.ReadLine() start := time.Now().UnixNano() if err != nil { if err != io.EOF { log.Errorln(err) } return } sshReportChan <- uint64(len(line)) // just echo the message log.Debugln("ssh received: ", line) term.Write([]byte(line)) term.Write([]byte{'\r', '\n'}) stop := time.Now().UnixNano() log.Info("ssh %v %vns", conn.RemoteAddr(), uint64(stop-start)) } }() }
func (s *shellHandler) startTerminal(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel) error { defer channel.Close() prompt := ">>> " term := terminal.NewTerminal(channel, prompt) // // Try to make the terminal raw // oldState, err := terminal.MakeRaw(0) // if err != nil { // logger.Warn("Error making terminal raw: ", err.Error()) // } // defer terminal.Restore(0, oldState) // Get username username, ok := sshConn.Permissions.Extensions["username"] if !ok { username = "******" } // Write ascii text term.Write([]byte(fmt.Sprintf("\r\n Nice job, %s! You are connected!\r\n", username))) defer term.Write([]byte(fmt.Sprintf("\r\nGoodbye, %s!\r\n", username))) // Start REPL for { select { case <-parentTomb.Dying(): return nil default: s.logger.Info("Reading line...") input, err := term.ReadLine() if err != nil { fmt.Errorf("Readline() error") return err } // Process line line := strings.TrimSpace(input) if len(line) > 0 { // Log input and handle exit requests if line == "exit" || line == "quit" { s.logger.Info("Closing connection") return nil } // Echo input channel.Write(term.Escape.Green) channel.Write([]byte(line + "\r\n")) channel.Write(term.Escape.Reset) } } } return nil }
func newTerm(prompt string) *Term { term := new(Term) var err error term.s, err = terminal.MakeRaw(0) if err != nil { panic(err) } term.t = terminal.NewTerminal(os.Stdin, prompt) return term }
func (i *Interactive) terminalInit() { var err error i.oldTermState, err = terminal.MakeRaw(syscall.Stdin) if err != nil { i.fatalf("Failed to get raw terminal: %v", err) } i.term = terminal.NewTerminal(os.Stdin, i.Prompt) i.term.AutoCompleteCallback = i.defaultAutoComplete }
func terminalInit() { var err error oldTermState, err = terminal.MakeRaw(syscall.Stdin) if err != nil { fatalf("Failed to get raw terminal: %v", err) } term = terminal.NewTerminal(os.Stdin, prompt) term.AutoCompleteCallback = autoComplete }
// Read a password from stdin without echoing input to stdout. func ReadPassword(prompt string) (string, error) { oldState, err := terminal.MakeRaw(0) if err != nil { return "", err } defer terminal.Restore(0, oldState) term := terminal.NewTerminal(os.Stdin, "") pass, err := term.ReadPassword(prompt) return pass, err }
func (server *SshServer) handleChannel(user string, newChannel ssh.NewChannel) { if t := newChannel.ChannelType(); t != "session" { newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) return } connection, requests, err := newChannel.Accept() if err != nil { fmt.Printf("Could not accept channel (%s)", err) return } defer connection.Close() logfile, err := os.Create(fmt.Sprintf("%s_%s", user, storyLogFilename(server.story))) if err != nil { panic(err) } defer logfile.Close() logger := log.New(logfile, "", log.LstdFlags) terminal := terminal.NewTerminal(connection, "") zsshterm := &gork.ZSshTerminal{Term: terminal} zm, err := gork.NewZMachine(server.mem, server.header, zsshterm, logger) if err != nil { fmt.Println(err) return } go func() { for req := range requests { switch req.Type { case "shell": if len(req.Payload) == 0 { req.Reply(true, nil) } case "pty-req": termLen := req.Payload[3] w, h := parseDims(req.Payload[termLen+4:]) terminal.SetSize(w, h) case "window-change": w, h := parseDims(req.Payload) terminal.SetSize(w, h) } } }() defer func() { recover() }() zm.InterpretAll() }
// Read credentials from standard input func readCredentials() (username string, password string, err error) { state, err := terminal.MakeRaw(0) if err == nil { t := terminal.NewTerminal(os.Stdin, "Username: "******"Password: ") } terminal.Restore(0, state) } return username, password, nil }
func handleConn(nConn net.Conn, config *ssh.ServerConfig) { addr := nConn.RemoteAddr().String() log.Printf("Connection opened from %s", addr) defer log.Printf("Connection closed from %s", addr) // Negotiate SSH. This is where passwords are checked. _, chans, _, err := ssh.NewServerConn(nConn, config) if err != nil { log.Printf("Couldn't handshake %s: %s", addr, err) } // A ServerConn multiplexes several channels, which must // themselves be Accepted. for newChannel := range chans { // Accept reads from the connection, demultiplexes packets // to their corresponding channels and returns when a new // channel request is seen. Some goroutine must always be // calling Accept; otherwise no messages will be forwarded // to the channels. if newChannel.ChannelType() != "session" { newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") continue } channel, _, err := newChannel.Accept() if err != nil { break } // Channels have a type, depending on the application level // protocol intended. In the case of a shell, the type is // "session" and ServerShell may be used to present a simple // terminal interface. log.Printf("Channel from %s : %s", addr, newChannel.ChannelType()) prompt := "$ " term := terminal.NewTerminal(channel, prompt) go func() { defer log.Printf("Channel closed from %s", addr) defer channel.Close() // Just log every line that the attacker sends. for { line, err := term.ReadLine() if err != nil { break } log.Printf("%s > %s", addr, line) } }() } }
func askConfig() (cfg, error) { stdinState, err := terminal.MakeRaw(syscall.Stdin) if err != nil { return cfg{}, err } defer terminal.Restore(syscall.Stdin, stdinState) stdoutState, err := terminal.MakeRaw(syscall.Stdout) if err != nil { return cfg{}, err } defer terminal.Restore(syscall.Stdout, stdoutState) t := struct { io.Reader io.Writer }{os.Stdin, os.Stdout} term := terminal.NewTerminal(t, "") msg := "Configure git-erickson for first time use.\nErickson server URL: " if _, err := term.Write([]byte(msg)); err != nil { return cfg{}, err } url, err := term.ReadLine() if err != nil { return cfg{}, err } cmd := exec.Command("git", "config", "--global", "erickson.url", string(url)) if err := cmd.Run(); err != nil { return cfg{}, err } if _, err := term.Write([]byte("Erickson username: "******"git", "config", "--global", "erickson.username", string(username)) if err := cmd.Run(); err != nil { return cfg{}, err } return cfg{url: url, username: username}, nil }
func runTerm(cmd *cobra.Command, args []string) { if len(args) != 0 { cmd.Usage() return } db := makeSQLClient() readWriter := struct { io.Reader io.Writer }{ Reader: os.Stdin, Writer: os.Stdout, } // We need to switch to raw mode. Unfortunately, this masks // signals-from-keyboard, meaning that ctrl-C cannot be caught. oldState, err := terminal.MakeRaw(0) if err != nil { panic(err) } defer func() { _ = terminal.Restore(0, oldState) }() term := terminal.NewTerminal(readWriter, "> ") for { line, err := term.ReadLine() if err != nil { if err != io.EOF { fmt.Fprintf(os.Stderr, "Input error: %s\n", err) } break } if len(line) == 0 { continue } shouldExit, err := processOneLine(db, line, term) if err != nil { fmt.Fprintf(term, "Error: %s\n", err) } if shouldExit { break } } }
func main() { if len(os.Args) > 1 { input := strings.Replace(strings.Join(os.Args[1:], ""), " ", "", -1) res, err := compute.Evaluate(input) if err != nil { fmt.Println("Error: " + err.Error()) return } fmt.Printf("%s\n", decimalToString(res)) return } var err error regularState, err = terminal.MakeRaw(0) if err != nil { panic(err) } defer terminal.Restore(0, regularState) term := terminal.NewTerminal(os.Stdin, "> ") term.AutoCompleteCallback = handleKey for { text, err := term.ReadLine() if err != nil { if err == io.EOF { // Quit without error on Ctrl^D fmt.Println() break } panic(err) } text = strings.Replace(text, " ", "", -1) if text == "exit" || text == "quit" { break } res, err := compute.Evaluate(text) if err != nil { fmt.Println("Error: " + err.Error()) continue } fmt.Printf("%s\n", res) } }
// NewShell returns a new Shell Adapter. func NewShell(b bot.Bot) bot.Adapter { // Get terminal old state. state, err := t.MakeRaw(int(os.Stdin.Fd())) if err != nil { panic(err) } // Create a new terminal struct. term := t.NewTerminal(os.Stdin, "") prompt := fmt.Sprintf("\033[36m%s\033[0m> ", b.Name) term.SetPrompt(prompt) return Shell{ bot: b, MessagePool: bot.NewMessagePool(), Terminal: term, State: state, } }
func askCredentials(username string) (password string, err error) { stdinState, err := terminal.MakeRaw(syscall.Stdin) if err != nil { return } defer terminal.Restore(syscall.Stdin, stdinState) stdoutState, err := terminal.MakeRaw(syscall.Stdout) if err != nil { return } defer terminal.Restore(syscall.Stdout, stdoutState) t := struct { io.Reader io.Writer }{os.Stdin, os.Stdout} term := terminal.NewTerminal(t, "") msg := fmt.Sprintf("Password for %s: ", username) password, err = term.ReadPassword(msg) return }
func termechoMain() error { oldState, err := terminal.MakeRaw(int(syscall.Stdin)) if err != nil { return err } defer terminal.Restore(int(syscall.Stdin), oldState) fmt.Println("enter 'q' to quit") t := terminal.NewTerminal(os.Stdin, "> ") for { line, err := t.ReadLine() if err != nil { return err } if line == "q" { break } fmt.Println(line) } return nil }
func handleChannel(ch ssh.Channel) { term := terminal.NewTerminal(ch, "> ") serverTerm := &ssh.ServerTerminal{ Term: term, Channel: ch, } ch.Accept() defer ch.Close() for { line, err := serverTerm.ReadLine() if err == io.EOF { return } if err != nil { log.Println("handleChannel readLine err:", err) continue } fmt.Println(line) } }
func (fk *filterKeys) filter(keys []openpgp.Key, symmetric bool) ([]byte, error) { var keyid string var k openpgp.Key for keyid == "" { if fk.index >= len(keys) { return nil, io.EOF } k = keys[fk.index] fk.index++ if k.PublicKey != nil { if keyid = k.PublicKey.KeyIdShortString(); keyid != "" { break } } } fd := int(os.Stdin.Fd()) state, err := terminal.MakeRaw(fd) if err != nil { return nil, err } defer terminal.Restore(fd, state) t := terminal.NewTerminal(os.Stdin, "> ") pw, err := t.ReadPassword(fmt.Sprintf("Enter the passphrase for %v: ", keyid)) if err != nil { return nil, err } if symmetric { return []byte(strings.TrimSpace(pw)), nil } if k.PrivateKey.Encrypted { if err := k.PrivateKey.Decrypt([]byte(strings.TrimSpace(pw))); err != nil { return nil, err } } return nil, nil }
func (c *cliClient) Start() { oldState, err := terminal.MakeRaw(0) if err != nil { panic(err.Error()) } defer terminal.Restore(0, oldState) signal.Notify(make(chan os.Signal), os.Interrupt) wrapper, interruptChan := NewTerminalWrapper(os.Stdin) wrapper.SetErrorOnInterrupt(true) c.interrupt = interruptChan c.termWrapper = wrapper c.term = terminal.NewTerminal(wrapper, "> ") updateTerminalSize(c.term) resizeChan := make(chan os.Signal) go func() { for _ = range resizeChan { updateTerminalSize(c.term) } }() signal.Notify(resizeChan, syscall.SIGWINCH) c.loadUI() if c.writerChan != nil { c.save() } if c.writerChan != nil { close(c.writerChan) <-c.writerDone } if c.fetchNowChan != nil { close(c.fetchNowChan) } if c.stateLock != nil { c.stateLock.Close() } }
func startTerminal(c *config) { fmt.Printf("Starting terminal mode.\n") fmt.Printf("Enter h for [h]elp.\n") fmt.Printf("Enter q for [q]uit.\n") done := make(chan bool) initState, err := terminal.GetState(0) protected := false if err != nil { fmt.Printf("error getting terminal state: %v\n", err.Error()) return } go func() { terminal.MakeRaw(int(os.Stdin.Fd())) n := terminal.NewTerminal(os.Stdin, "> ") for { var ln string var err error if !protected { ln, err = n.ReadLine() if err != nil { done <- true } } else { ln, err = n.ReadPassword(">*") if err != nil { done <- true } } execute(done, &protected, c, ln) } }() select { case <-done: fmt.Printf("exiting...\n") terminal.Restore(0, initState) close(done) } }
func (s *SSHServer) shellHandler(channel ssh.Channel) error { defer channel.Close() term := terminal.NewTerminal(channel, "# ") for { line, err := term.ReadLine() if err != nil { break } if line == "" { return nil } result, status, err := s.runCmd(line) if err != nil { return fmt.Errorf("failed to run command: %v", err) } if err := sendCmdResult(channel, result, status); err != nil { return fmt.Errorf("failed to send result: %v", err) } } return nil }
// Make new terminal from a session channel func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { if ch.ChannelType() != "session" { return nil, errors.New("terminal requires session channel") } channel, requests, err := ch.Accept() if err != nil { return nil, err } term := Terminal{ *terminal.NewTerminal(channel, "Connecting..."), sshConn{conn}, channel, } go term.listen(requests) go func() { // FIXME: Is this necessary? conn.Wait() channel.Close() }() return &term, nil }
// NewStartedQShell returns an initialized QShell, ready to Interact() with. // Prints mongo startup message. func NewStartedQShell(driver *strata.Driver, mountPath string, showSize bool, mongodPath, mongoPath string) (*QShell, error) { finished := false sh := QShell{driver: driver, mountPath: mountPath, showSize: showSize, mongodPath: mongodPath, mongoPath: mongoPath} // Put terminal in raw mode var err error sh.stdinFd = int(os.Stdin.Fd()) sh.oldState, err = terminal.MakeRaw(sh.stdinFd) if err != nil { return &sh, err } // Convention seems to be that caller invokes Close() only if there is no error. // But we need to clean up if there is an error. defer func() { if !finished { sh.Close() } }() sh.term = terminal.NewTerminal(&readerWriter{r: os.Stdin, w: os.Stdout}, "\r> ") sh.mongoStateHolder, err = newMongoStateHolder(sh.mongoPath) if err != nil { return &sh, err } if err := sh.print("Wrapper around MongoDB shell with additional commands for querying backups.\n"); err != nil { return &sh, err } if err := sh.printMongoOutput(true); err != nil { return &sh, err } finished = true return &sh, nil }
func (r *sshRunner) Run() error { logrus.WithFields(logrus.Fields{ "user": r.user, "host": r.host, }).Info("ssh") sshHost := fmt.Sprintf("%s:%d", r.host, r.port) // Key Auth key, err := ioutil.ReadFile(r.keyPath) if err != nil { return err } privateKey, err := ssh.ParsePrivateKey(key) if err != nil { return err } if err := sshConnect(r.user, sshHost, ssh.PublicKeys(privateKey)); err != nil { // Password Auth if Key Auth Fails fd := os.Stdin.Fd() state, err := terminal.MakeRaw(int(fd)) if err != nil { return err } defer terminal.Restore(int(fd), state) t := terminal.NewTerminal(os.Stdout, ">") password, err := t.ReadPassword("Password: ") if err != nil { return err } if err := sshConnect(r.user, sshHost, ssh.Password(string(password))); err != nil { return err } } return err }
func (app *h2i) Main() error { cfg := &tls.Config{ ServerName: app.host, NextProtos: strings.Split(*flagNextProto, ","), InsecureSkipVerify: *flagInsecure, } hostAndPort := withPort(app.host) log.Printf("Connecting to %s ...", hostAndPort) tc, err := tls.Dial("tcp", hostAndPort, cfg) if err != nil { return fmt.Errorf("Error dialing %s: %v", withPort(app.host), err) } log.Printf("Connected to %v", tc.RemoteAddr()) defer tc.Close() if err := tc.Handshake(); err != nil { return fmt.Errorf("TLS handshake: %v", err) } if !*flagInsecure { if err := tc.VerifyHostname(app.host); err != nil { return fmt.Errorf("VerifyHostname: %v", err) } } state := tc.ConnectionState() log.Printf("Negotiated protocol %q", state.NegotiatedProtocol) if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" { return fmt.Errorf("Could not negotiate protocol mutually") } if _, err := io.WriteString(tc, http2.ClientPreface); err != nil { return err } app.framer = http2.NewFramer(tc, tc) oldState, err := terminal.MakeRaw(0) if err != nil { return err } defer terminal.Restore(0, oldState) var screen = struct { io.Reader io.Writer }{os.Stdin, os.Stdout} app.term = terminal.NewTerminal(screen, "h2i> ") lastWord := regexp.MustCompile(`.+\W(\w+)$`) app.term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) { if key != '\t' { return } if pos != len(line) { // TODO: we're being lazy for now, only supporting tab completion at the end. return } // Auto-complete for the command itself. if !strings.Contains(line, " ") { var name string name, _, ok = lookupCommand(line) if !ok { return } return name, len(name), true } _, c, ok := lookupCommand(line[:strings.IndexByte(line, ' ')]) if !ok || c.complete == nil { return } if strings.HasSuffix(line, " ") { app.logf("%s", strings.Join(c.complete(), " ")) return line, pos, true } m := lastWord.FindStringSubmatch(line) if m == nil { return line, len(line), true } soFar := m[1] var match []string for _, cand := range c.complete() { if len(soFar) > len(cand) || !strings.EqualFold(cand[:len(soFar)], soFar) { continue } match = append(match, cand) } if len(match) == 0 { return } if len(match) > 1 { // TODO: auto-complete any common prefix app.logf("%s", strings.Join(match, " ")) return line, pos, true } newLine = line[:len(line)-len(soFar)] + match[0] return newLine, len(newLine), true } errc := make(chan error, 2) go func() { errc <- app.readFrames() }() go func() { errc <- app.readConsole() }() return <-errc }
func remoteShellHandler(ws *websocket.Conn) { var httpErr *errors.HTTP defer func() { defer ws.Close() if httpErr != nil { var msg string switch httpErr.Code { case http.StatusUnauthorized: msg = "no token provided or session expired, please login again\n" default: msg = httpErr.Message + "\n" } ws.Write([]byte("Error: " + msg)) } }() r := ws.Request() token := context.GetAuthToken(r) if token == nil { httpErr = &errors.HTTP{ Code: http.StatusUnauthorized, Message: "no token provided", } return } appName := r.URL.Query().Get(":appname") a, err := getAppFromContext(appName, r) if err != nil { if herr, ok := err.(*errors.HTTP); ok { httpErr = herr } else { httpErr = &errors.HTTP{ Code: http.StatusInternalServerError, Message: err.Error(), } } return } allowed := permission.Check(token, permission.PermAppRunShell, contextsForApp(&a)...) if !allowed { httpErr = permission.ErrUnauthorized return } buf := &optionalWriterCloser{} var term *terminal.Terminal unitID := r.URL.Query().Get("unit") width, _ := strconv.Atoi(r.URL.Query().Get("width")) height, _ := strconv.Atoi(r.URL.Query().Get("height")) clientTerm := r.URL.Query().Get("term") evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppRunShell, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), DisableLock: true, }) if err != nil { httpErr = &errors.HTTP{ Code: http.StatusInternalServerError, Message: err.Error(), } return } defer func() { var finalErr error if httpErr != nil { finalErr = httpErr } for term != nil { buf.disableWrite = true var line string line, err = term.ReadLine() if err != nil { break } fmt.Fprintf(evt, "> %s\n", line) } evt.Done(finalErr) }() term = terminal.NewTerminal(buf, "") opts := provision.ShellOptions{ Conn: &cmdLogger{base: ws, term: term}, Width: width, Height: height, Unit: unitID, Term: clientTerm, } err = a.Shell(opts) if err != nil { httpErr = &errors.HTTP{ Code: http.StatusInternalServerError, Message: err.Error(), } } }