1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
18 "git.arvados.org/arvados.git/lib/controller/rpc"
19 "git.arvados.org/arvados.git/sdk/go/arvados"
22 // shellCommand connects the terminal to an interactive shell on a
24 type shellCommand struct{}
26 func (shellCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
27 f := flag.NewFlagSet(prog, flag.ContinueOnError)
30 fmt.Print(stderr, prog+`: open an interactive shell on a running container.
32 Usage: `+prog+` [options] [username@]container-uuid [ssh-options] [remote-command [args...]]
38 detachKeys := f.String("detach-keys", "ctrl-],ctrl-]", "set detach key sequence, as in docker-attach(1)")
41 fmt.Println(stderr, err)
51 if !strings.Contains(target, "@") {
52 target = "root@" + target
54 sshargs := f.Args()[1:]
56 selfbin, err := os.Readlink("/proc/self/exe")
58 fmt.Fprintln(stderr, err)
61 sshargs = append([]string{
62 "-o", "ProxyCommand " + selfbin + " connect-ssh -detach-keys=" + shellescape(*detachKeys) + " " + shellescape(target),
63 "-o", "StrictHostKeyChecking no",
66 cmd := exec.Command("ssh", sshargs...)
73 } else if exiterr, ok := err.(*exec.ExitError); !ok {
74 fmt.Fprintln(stderr, err)
76 } else if status, ok := exiterr.Sys().(syscall.WaitStatus); !ok {
77 fmt.Fprintln(stderr, err)
80 return status.ExitStatus()
84 // connectSSHCommand connects stdin/stdout to a container's gateway
85 // server (see lib/crunchrun/ssh.go).
87 // It is intended to be invoked with OpenSSH client's ProxyCommand
89 type connectSSHCommand struct{}
91 func (connectSSHCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
92 f := flag.NewFlagSet(prog, flag.ContinueOnError)
95 fmt.Fprint(stderr, prog+`: connect to the gateway service for a running container.
97 Usage: `+prog+` [options] [username@]container-uuid
103 detachKeys := f.String("detach-keys", "", "set detach key sequence, as in docker-attach(1)")
104 if err := f.Parse(args); err != nil {
105 fmt.Fprintln(stderr, err)
108 } else if f.NArg() != 1 {
112 targetUUID := f.Args()[0]
113 loginUsername := "root"
114 if i := strings.Index(targetUUID, "@"); i >= 0 {
115 loginUsername = targetUUID[:i]
116 targetUUID = targetUUID[i+1:]
118 insecure := os.Getenv("ARVADOS_API_HOST_INSECURE")
119 rpcconn := rpc.NewConn("",
122 Host: os.Getenv("ARVADOS_API_HOST"),
124 insecure == "1" || insecure == "yes" || insecure == "true",
125 func(context.Context) ([]string, error) {
126 return []string{os.Getenv("ARVADOS_API_TOKEN")}, nil
128 // if strings.Contains(targetUUID, "-xvhdp-") {
129 // cr, err := rpcconn.ContainerRequestGet(context.TODO(), arvados.GetOptions{UUID: targetUUID})
131 // fmt.Fprintln(stderr, err)
134 // if cr.ContainerUUID == "" {
135 // fmt.Fprintf(stderr, "no container assigned, container request state is %s\n", strings.ToLower(cr.State))
138 // targetUUID = cr.ContainerUUID
140 sshconn, err := rpcconn.ContainerSSH(context.TODO(), arvados.ContainerSSHOptions{
142 DetachKeys: *detachKeys,
143 LoginUsername: loginUsername,
146 fmt.Fprintln(stderr, err)
149 defer sshconn.Conn.Close()
151 ctx, cancel := context.WithCancel(context.Background())
154 _, err := io.Copy(stdout, sshconn.Conn)
155 if err != nil && ctx.Err() == nil {
156 fmt.Fprintf(stderr, "receive: %v\n", err)
161 _, err := io.Copy(sshconn.Conn, stdin)
162 if err != nil && ctx.Err() == nil {
163 fmt.Fprintf(stderr, "send: %v\n", err)
170 func shellescape(s string) string {
171 return "'" + strings.Replace(s, "'", "'\\''", -1) + "'"