コード例 #1
0
ファイル: pty_ssh.go プロジェクト: cfstras/pcm
func connect(c *types.Connection, terminal types.Terminal, moreCommands func() *string) bool {

	cmd := &exec.Cmd{}
	cmd.Path = "/usr/bin/ssh"
	cmd.Args = []string{"-v", "-p", fmt.Sprint(c.Info.Port), "-l", c.Login.User, c.Info.Host}
	var procExit int32 = 0

	outFunc := func(pipe *os.File, name string, nextCommand func() *string,
		startWait *sync.Cond) {
		buf := make([]byte, 1024)
		wrotePassword := false
		for {
			if atomic.LoadInt32(&procExit) != 0 {
				return
			}
			n, err := pipe.Read(buf)
			str := string(buf[:n])
			fmt.Fprint(terminal.Stdout(), str)
			if strings.HasSuffix(str, "assword: ") && !wrotePassword && c.Options.LoginMacro {
				pipe.Write([]byte(c.Login.Password))
				pipe.Write([]byte("\n"))
				wrotePassword = true
			} else if strings.HasSuffix(str, "assword: ") || strings.HasSuffix(str, "$ ") ||
				strings.HasSuffix(str, "# ") {
				if answer := nextCommand(); answer != nil {
					pipe.Write([]byte(*answer))
					pipe.Write([]byte("\n"))
				}
			}
			if strings.HasSuffix(str, "Are you sure you want to continue connecting (yes/no)? ") {
				pipe.Write([]byte("yes\n"))
			}
			if err != nil {
				if err != io.EOF {
					return
				}
				fmt.Fprintln(terminal.Stderr(), "pipe", name, "error", err)
				return
			}
		}
	}

	inFunc := func(pipe io.WriteCloser, inputConsole io.Reader, startWait *sync.Cond) {
		input := make(chan []byte, 32)
		buffers := &sync.Pool{
			New: func() interface{} { return make([]byte, 1024) },
		}

		go func(pipe io.WriteCloser, input chan []byte, startWait *sync.Cond,
			buffers *sync.Pool) {
			for {
				buf := buffers.Get().([]byte)
				n, err := inputConsole.Read(buf)

				if err != nil && err != io.EOF {
					fmt.Fprintln(terminal.Stderr(), "my stdin got error", err)
					input <- nil
					startWait.Broadcast()
					return
				}
				var write []byte
				if err == io.EOF {
					write = []byte{0x04}
					input <- nil
					startWait.Broadcast()
					return
				}
				for _, c := range []byte{0x04, 0x03, 0x1a} {
					if bytes.Contains(buf[:n], []byte{c}) {
						write = append(write, c)
					}
				}
				if write != nil {
					_, err = pipe.Write(write)
					if err != nil {
						fmt.Fprintln(terminal.Stderr(), "stdin got error", err)
						input <- nil
						startWait.Broadcast()
						return
					}
				} else {
					input <- buf[:n]
				}
			}
		}(pipe, input, startWait, buffers)

		startWait.L.Lock()
		startWait.Wait()
		startWait.L.Unlock()
		for buf := range input {
			if buf == nil {
				fmt.Fprintln(terminal.Stderr(), "closing stdIn:", pipe.Close())
				return
			}
			_, err := pipe.Write(buf)

			if err != nil {
				fmt.Fprintln(terminal.Stderr(), "stdin got error", err)
				fmt.Fprintln(terminal.Stderr(), "closing stdIn:", pipe.Close())
				return
			}
			if cap(buf) > 256 {
				buffers.Put(buf)
			}
		}
	}

	startWait := sync.NewCond(&sync.Mutex{})
	nextCommand := util.GetCommandFunc(c, startWait, moreCommands)

	sendSize := func(out *os.File, cmd *exec.Cmd) {
		var row, col C.int
		C.getsize(&row, &col)
		C.setsize(C.int(out.Fd()), row, col)
		cmd.Process.Signal(syscall.SIGWINCH)
	}

	signalWatcher := func(out *os.File, cmd *exec.Cmd) {
		for s := range terminal.Signals() {
			if s == syscall.SIGWINCH {
				sendSize(out, cmd)
			} else if s == os.Interrupt {
				cmd.Process.Signal(syscall.SIGINT)
			} else if s == syscall.SIGSTOP {
				cmd.Process.Signal(syscall.SIGSTOP)
			}
		}
	}

	pty, err := pty.Start(cmd)
	p(err, "starting ssh")

	go outFunc(pty, "pty", nextCommand, startWait)
	go inFunc(pty, terminal.Stdin(), startWait)
	go signalWatcher(pty, cmd)
	sendSize(pty, cmd)

	go func(exit <-chan bool) {
		for _ = range exit {
			if atomic.LoadInt32(&procExit) != 0 {
				return
			}
			err := cmd.Process.Kill()
			p(err, "Killing ssh process")
			return
		}
	}(terminal.ExitRequests())

	err = cmd.Wait()
	atomic.StoreInt32(&procExit, 1)
	if err != nil {
		fmt.Fprintln(terminal.Stderr(), "SSH Process ended:", err)
	}
	return false
}
コード例 #2
0
ファイル: pcm.go プロジェクト: cfstras/pcm
func main() {
	defer func() {
		if err := recover(); err != nil {
			color.Redln(err)
			if DEBUG {
				debug.PrintStack()
			}
			os.Exit(1)
		}
	}()
	if DEBUG {
		go http.ListenAndServe(":3000", nil)
	}

	pathP := flag.String("connectionsPath", connectionsPath, "Path to PuTTY connections.xml")
	verbose := false
	useFuzzySimple := false
	useOwnSSH := false
	flag.BoolVar(&verbose, "verbose", false, "Display more info, such as hostnames and passwords")
	flag.BoolVar(&verbose, "v", false, "Display more info, such as hostnames and passwords")
	flag.BoolVar(&useFuzzySimple, "simple", false, "Use simple interface")
	flag.BoolVar(&useOwnSSH, "ssh", true, "Use golang ssh client instead of os-client")

	flag.Parse()
	if pathP != nil {
		connectionsPath = *pathP
	}

	if flag.NArg() > 1 {
		flag.Usage()
		color.Yellowln("Usage: pcm [search term]")
		panic("Only one arg allowed.")
	}
	var searchFor string
	if flag.NArg() == 1 {
		searchFor = flag.Arg(0)
	}

	conf := loadConns()

	var conn *types.Connection
	if useFuzzySimple {
		conn = fuzzySimple(&conf, searchFor)
	} else {
		conn = selectConnection(&conf, searchFor)
	}

	if conn == nil {
		return
	}

	color.Yellowln("Using", conn.Info.Name)
	color.Redln(conn.Info.Host, conn.Info.Port)
	if verbose {
		color.Yellow("User: "******"Password: "******"Commands:")
		f := util.GetCommandFunc(conn, nil, func() *string { return nil })
		for {
			if v := f(); v == nil {
				break
			} else {
				fmt.Println(v)
			}
		}
	}
	//fmt.Println(conn.Login)
	//fmt.Println(conn.Command)

	var console types.Terminal = &consoleTerminal{
		exit: make(chan bool),
	}
	oldState, err := util.SetupTerminal()
	p(err, "making terminal raw")
	defer util.RestoreTerminal(oldState)
	var changed bool
	if useOwnSSH {
		changed = ssh.Connect(conn, console, func() *string { return nil })
	} else {
		changed = connect(conn, console, func() *string { return nil })
	}
	if changed {
		saveConns(&conf)
	}
}
コード例 #3
0
ファイル: connect.go プロジェクト: cfstras/pcm
func (inst *instance) connect(moreCommands func() *string) bool {
	config := &ssh.ClientConfig{
		User:            inst.conn.Login.User,
		Auth:            []ssh.AuthMethod{ssh.Password(inst.conn.Login.Password)},
		HostKeyCallback: inst.hostKeyCallback,
		Timeout:         20 * time.Second,
	}

	addr := fmt.Sprint(inst.conn.Info.Host, ":", inst.conn.Info.Port)
	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		color.Redln("Connecting to", addr, ":", err)
		return inst.changed
	}

	// Each ClientConn can support multiple interactive sessions,
	// represented by a Session.
	inst.session, err = client.NewSession()
	if err != nil {
		color.Redln("Failed to create session:", err)
		return inst.changed
	}
	defer inst.session.Close()

	// Set up terminal modes
	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	exit := make(chan bool, 1)
	go func() {
		for e := range inst.terminal.ExitRequests() {
			exit <- e
		}
	}()

	var procExit int32 = 0
	shellOutFunc := func(stdErrOut io.Reader, stdin io.Writer, name string, nextCommand func() *string,
		startWait *sync.Cond) {
		buf := make([]byte, 1024)
		for {
			if atomic.LoadInt32(&procExit) != 0 {
				return
			}
			n, err := stdErrOut.Read(buf)
			if err != nil {
				if err == io.EOF {
					exit <- true
					return
				}
				fmt.Fprintln(inst.terminal.Stderr(), "ssh", name, "error", err)
				exit <- true
				return
			}

			_, err = inst.terminal.Stdout().Write(buf[:n])
			if err != nil {
				if err == io.EOF {
					exit <- true
					return
				}
				fmt.Fprintln(inst.terminal.Stderr(), "ssh", name, "error", err)
				return
			}
			//fmt.Fprint(inst.terminal.Stderr(), str)

			str := string(buf[:n])
			if strings.HasSuffix(str, "assword: ") || strings.HasSuffix(str, "$ ") ||
				strings.HasSuffix(str, "# ") {
				if answer := nextCommand(); answer != nil {
					stdin.Write([]byte(*answer))
					stdin.Write([]byte("\n"))
				}
			}

		}
	}
	inFunc := func(sshStdin io.WriteCloser, startWait *sync.Cond) {
		inputBufChan := make(chan []byte, 32)
		buffers := &sync.Pool{
			New: func() interface{} { return make([]byte, 1024) },
		}

		go func() {
			writeRightNow := make([]byte, 0, 3)
			for {
				buf := buffers.Get().([]byte)
				n, err := inst.terminal.Stdin().Read(buf)
				buf = buf[:n]
				//fmt.Fprintln(inst.terminal.Stdout(), "my stdin got", util.DebugString(buf))

				if err != nil && err != io.EOF {
					fmt.Fprintln(inst.terminal.Stdout(), "my stdin got error", err)
					inputBufChan <- nil
					startWait.Broadcast()
					return
				}
				writeRightNow = writeRightNow[:0]
				if err == io.EOF {
					writeRightNow = append(writeRightNow, 0x04)
					inputBufChan <- nil
					startWait.Broadcast()
					return
				}
				// ctrl+c, ctrl+d, ctrl+z
				for _, c := range []byte{0x04, 0x03, 0x1a} {
					if bytes.Contains(buf, []byte{c}) {
						writeRightNow = append(writeRightNow, c)
					}
				}

				// handle questions
				if newLinePos := bytes.Index(buf, []byte{'\r'}); newLinePos != -1 {
					// see if we have any answer handlers
					select {
					case handler := <-inst.questions:
						answer := string(buf[:newLinePos])
						handler(answer)
						// munch the string
						buf = buf[newLinePos+1:]
						if len(buf) == 0 {
							if cap(buf) > 256 {
								buffers.Put(buf)
							}
							continue
						}
					default:
						// do nothing
					}
				}

				if len(writeRightNow) > 0 {
					_, err = sshStdin.Write(writeRightNow)
					if err != nil {
						fmt.Fprintln(inst.terminal.Stderr(), "stdin got error", err)
						inputBufChan <- nil
						startWait.Broadcast()
						return
					}
				} else {
					inputBufChan <- buf
				}
			}
		}()

		// wait for the start commands to finish
		startWait.L.Lock()
		startWait.Wait()
		startWait.L.Unlock()
		for buf := range inputBufChan {
			if buf == nil {
				fmt.Fprintln(inst.terminal.Stderr(), "closing stdin:", sshStdin.Close())
				exit <- true
				return
			}
			trans := util.TransformInput(buf)
			//fmt.Fprintln(inst.terminal.Stderr(), "sending", util.DebugString(trans))
			_, err := sshStdin.Write(trans)

			if err != nil {
				fmt.Fprintln(inst.terminal.Stderr(), "stdin got error", err)
				fmt.Fprintln(inst.terminal.Stderr(), "closing stdin:", sshStdin.Close())
				exit <- true
				return
			}
			if cap(buf) > 256 {
				buffers.Put(buf)
			}
		}
	}

	defer func() {
		atomic.StoreInt32(&procExit, 1)
	}()

	startWait := sync.NewCond(&sync.Mutex{})
	nextCommand := util.GetCommandFunc(inst.conn, startWait, moreCommands)

	sshStdin, err := inst.session.StdinPipe()
	if err != nil {
		color.Redln("Error opening stdin pipe", err)
		return inst.changed
	}
	go inFunc(sshStdin, startWait)

	sshStdout, err := inst.session.StdoutPipe()
	if err != nil {
		color.Redln("Error opening stdout pipe", err)
		return inst.changed
	}
	go shellOutFunc(sshStdout, sshStdin, "stdout", nextCommand, startWait)

	sshStderr, err := inst.session.StderrPipe()
	if err != nil {
		color.Redln("Error opening stderr pipe", err)
		return inst.changed
	}
	go shellOutFunc(sshStderr, sshStdin, "stderr", nextCommand, startWait)

	// Request pseudo terminal
	if err := inst.session.RequestPty("xterm", 80, 40, modes); err != nil {
		color.Redln("request for pseudo terminal failed:", err)
		return inst.changed
	}
	// Start remote shell
	if err := inst.session.Shell(); err != nil {
		color.Redln("failed to start shell:", err)
		return inst.changed
	}
	inst.SendWindowSize()

	signalWatcher := func() {
		for s := range inst.terminal.Signals() {
			sshSignal, ok := signalMap[s]
			if !ok {
				color.Yellowln("Unknown signal", s)
			} else if sshSignal != "" {
				inst.session.Signal(sshSignal)
			}
			if s == util.GetSigwinch() {
				inst.SendWindowSize()
			} else if s == syscall.SIGTERM {
				exit <- true
			}
		}
	}
	go signalWatcher()

	<-exit
	atomic.StoreInt32(&procExit, 1)
	return inst.changed
}