~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/lxc/lxd/lxc/exec.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package main
 
2
 
 
3
import (
 
4
        "encoding/json"
 
5
        "fmt"
 
6
        "os"
 
7
        "strconv"
 
8
        "strings"
 
9
        "syscall"
 
10
 
 
11
        "github.com/gorilla/websocket"
 
12
 
 
13
        "github.com/lxc/lxd"
 
14
        "github.com/lxc/lxd/shared"
 
15
        "github.com/lxc/lxd/shared/gnuflag"
 
16
        "github.com/lxc/lxd/shared/i18n"
 
17
        "github.com/lxc/lxd/shared/termios"
 
18
)
 
19
 
 
20
type envFlag []string
 
21
 
 
22
func (f *envFlag) String() string {
 
23
        return fmt.Sprint(*f)
 
24
}
 
25
 
 
26
func (f *envFlag) Set(value string) error {
 
27
        if f == nil {
 
28
                *f = make(envFlag, 1)
 
29
        } else {
 
30
                *f = append(*f, value)
 
31
        }
 
32
        return nil
 
33
}
 
34
 
 
35
type execCmd struct {
 
36
        modeFlag string
 
37
        envArgs  envFlag
 
38
}
 
39
 
 
40
func (c *execCmd) showByDefault() bool {
 
41
        return true
 
42
}
 
43
 
 
44
func (c *execCmd) usage() string {
 
45
        return i18n.G(
 
46
                `Execute the specified command in a container.
 
47
 
 
48
lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>
 
49
 
 
50
Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`)
 
51
}
 
52
 
 
53
func (c *execCmd) flags() {
 
54
        gnuflag.Var(&c.envArgs, "env", i18n.G("An environment variable of the form HOME=/home/foo"))
 
55
        gnuflag.StringVar(&c.modeFlag, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)"))
 
56
}
 
57
 
 
58
func (c *execCmd) sendTermSize(control *websocket.Conn) error {
 
59
        width, height, err := termios.GetSize(int(syscall.Stdout))
 
60
        if err != nil {
 
61
                return err
 
62
        }
 
63
 
 
64
        shared.Debugf("Window size is now: %dx%d", width, height)
 
65
 
 
66
        w, err := control.NextWriter(websocket.TextMessage)
 
67
        if err != nil {
 
68
                return err
 
69
        }
 
70
 
 
71
        msg := shared.ContainerExecControl{}
 
72
        msg.Command = "window-resize"
 
73
        msg.Args = make(map[string]string)
 
74
        msg.Args["width"] = strconv.Itoa(width)
 
75
        msg.Args["height"] = strconv.Itoa(height)
 
76
 
 
77
        buf, err := json.Marshal(msg)
 
78
        if err != nil {
 
79
                return err
 
80
        }
 
81
        _, err = w.Write(buf)
 
82
 
 
83
        w.Close()
 
84
        return err
 
85
}
 
86
 
 
87
func (c *execCmd) run(config *lxd.Config, args []string) error {
 
88
        if len(args) < 2 {
 
89
                return errArgs
 
90
        }
 
91
 
 
92
        remote, name := config.ParseRemoteAndContainer(args[0])
 
93
        d, err := lxd.NewClient(config, remote)
 
94
        if err != nil {
 
95
                return err
 
96
        }
 
97
 
 
98
        env := map[string]string{"HOME": "/root", "USER": "root"}
 
99
        myEnv := os.Environ()
 
100
        for _, ent := range myEnv {
 
101
                if strings.HasPrefix(ent, "TERM=") {
 
102
                        env["TERM"] = ent[len("TERM="):]
 
103
                }
 
104
        }
 
105
 
 
106
        for _, arg := range c.envArgs {
 
107
                pieces := strings.SplitN(arg, "=", 2)
 
108
                value := ""
 
109
                if len(pieces) > 1 {
 
110
                        value = pieces[1]
 
111
                }
 
112
                env[pieces[0]] = value
 
113
        }
 
114
 
 
115
        cfd := int(syscall.Stdin)
 
116
 
 
117
        var interactive bool
 
118
        if c.modeFlag == "interactive" {
 
119
                interactive = true
 
120
        } else if c.modeFlag == "non-interactive" {
 
121
                interactive = false
 
122
        } else {
 
123
                interactive = termios.IsTerminal(cfd) && termios.IsTerminal(int(syscall.Stdout))
 
124
        }
 
125
 
 
126
        var oldttystate *termios.State
 
127
        if interactive {
 
128
                oldttystate, err = termios.MakeRaw(cfd)
 
129
                if err != nil {
 
130
                        return err
 
131
                }
 
132
                defer termios.Restore(cfd, oldttystate)
 
133
        }
 
134
 
 
135
        handler := c.controlSocketHandler
 
136
        if !interactive {
 
137
                handler = nil
 
138
        }
 
139
 
 
140
        var width, height int
 
141
        if interactive {
 
142
                width, height, err = termios.GetSize(int(syscall.Stdout))
 
143
                if err != nil {
 
144
                        return err
 
145
                }
 
146
        }
 
147
 
 
148
        stdout := c.getStdout()
 
149
        ret, err := d.Exec(name, args[1:], env, os.Stdin, stdout, os.Stderr, handler, width, height)
 
150
        if err != nil {
 
151
                return err
 
152
        }
 
153
 
 
154
        if oldttystate != nil {
 
155
                /* A bit of a special case here: we want to exit with the same code as
 
156
                 * the process inside the container, so we explicitly exit here
 
157
                 * instead of returning an error.
 
158
                 *
 
159
                 * Additionally, since os.Exit() exits without running deferred
 
160
                 * functions, we restore the terminal explicitly.
 
161
                 */
 
162
                termios.Restore(cfd, oldttystate)
 
163
        }
 
164
 
 
165
        /* we get the result of waitpid() here so we need to transform it */
 
166
        os.Exit(ret >> 8)
 
167
        return fmt.Errorf(i18n.G("unreachable return reached"))
 
168
}