func hijack(conn net.Conn, br *bufio.Reader) int { var in io.Reader term, err := pty.OpenRawTerm() if err == nil { defer term.Restore() in = term } else { in = os.Stdin } encoder := json.NewEncoder(conn) decoder := json.NewDecoder(br) resized := pty.ResizeNotifier() go func() { for { <-resized // TODO json race sendSize(encoder) } }() go io.Copy(&stdinWriter{encoder}, in) var exitStatus int for { var output atc.HijackOutput err := decoder.Decode(&output) if err != nil { break } if output.ExitStatus != nil { exitStatus = *output.ExitStatus } else if len(output.Error) > 0 { fmt.Fprintf(os.Stderr, "%s\n", ansi.Color(output.Error, "red+b")) exitStatus = 255 } else if len(output.Stdout) > 0 { os.Stdout.Write(output.Stdout) } else if len(output.Stderr) > 0 { os.Stderr.Write(output.Stderr) } } return exitStatus }
func (command *HijackCommand) Execute(args []string) error { target, err := rc.SelectTarget(Fly.Target) if err != nil { return err } containers, err := getContainerIDs(command) if err != nil { return err } var chosenContainer atc.Container if len(containers) == 0 { displayhelpers.Failf("no containers matched your search parameters!\n\nthey may have expired if your build hasn't recently finished.") } else if len(containers) > 1 { var choices []interact.Choice for _, container := range containers { var infos []string if container.BuildID != 0 { if container.JobName != "" { infos = append(infos, fmt.Sprintf("build #%s", container.BuildName)) } else { infos = append(infos, fmt.Sprintf("build id: %d", container.BuildID)) } } if container.StepType != "" { infos = append(infos, fmt.Sprintf("step: %s", container.StepName)) infos = append(infos, fmt.Sprintf("type: %s", container.StepType)) } else if container.ResourceName != "" { infos = append(infos, fmt.Sprintf("resource: %s", container.ResourceName)) infos = append(infos, "type: check") } else { infos = append(infos, fmt.Sprintf("step: %s", container.StepName)) infos = append(infos, "type: check") } if len(container.Attempts) != 0 { attempt := SliceItoa(container.Attempts) infos = append(infos, fmt.Sprintf("attempt: %s", attempt)) } choices = append(choices, interact.Choice{ Display: strings.Join(infos, ", "), Value: container, }) } err = interact.NewInteraction("choose a container", choices...).Resolve(&chosenContainer) if err == io.EOF { return nil } if err != nil { return err } } else { chosenContainer = containers[0] } path, args := remoteCommand(args) privileged := true reqGenerator := rata.NewRequestGenerator(target.API, atc.Routes) tlsConfig := &tls.Config{InsecureSkipVerify: target.Insecure} var ttySpec *atc.HijackTTYSpec rows, cols, err := pty.Getsize(os.Stdin) if err == nil { ttySpec = &atc.HijackTTYSpec{ WindowSize: atc.HijackWindowSize{ Columns: cols, Rows: rows, }, } } envVariables := append(chosenContainer.EnvironmentVariables, "TERM="+os.Getenv("TERM")) spec := atc.HijackProcessSpec{ Path: path, Args: args, Env: envVariables, User: chosenContainer.User, Dir: chosenContainer.WorkingDirectory, Privileged: privileged, TTY: ttySpec, } result, err := func() (int, error) { // so the term.Restore() can run before the os.Exit() var in io.Reader term, err := pty.OpenRawTerm() if err == nil { defer term.Restore() in = term } else { in = os.Stdin } io := hijacker.ProcessIO{ In: in, Out: os.Stdout, Err: os.Stderr, } h := hijacker.New(tlsConfig, reqGenerator, target.Token) return h.Hijack(chosenContainer.ID, spec, io) }() if err != nil { return err } os.Exit(result) return nil }