11
"github.com/gorilla/websocket"
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"
22
func (f *envFlag) String() string {
26
func (f *envFlag) Set(value string) error {
30
*f = append(*f, value)
40
func (c *execCmd) showByDefault() bool {
44
func (c *execCmd) usage() string {
46
`Execute the specified command in a container.
48
lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>
50
Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`)
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)"))
58
func (c *execCmd) sendTermSize(control *websocket.Conn) error {
59
width, height, err := termios.GetSize(int(syscall.Stdout))
64
shared.Debugf("Window size is now: %dx%d", width, height)
66
w, err := control.NextWriter(websocket.TextMessage)
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)
77
buf, err := json.Marshal(msg)
87
func (c *execCmd) run(config *lxd.Config, args []string) error {
92
remote, name := config.ParseRemoteAndContainer(args[0])
93
d, err := lxd.NewClient(config, remote)
98
env := map[string]string{"HOME": "/root", "USER": "root"}
100
for _, ent := range myEnv {
101
if strings.HasPrefix(ent, "TERM=") {
102
env["TERM"] = ent[len("TERM="):]
106
for _, arg := range c.envArgs {
107
pieces := strings.SplitN(arg, "=", 2)
112
env[pieces[0]] = value
115
cfd := int(syscall.Stdin)
118
if c.modeFlag == "interactive" {
120
} else if c.modeFlag == "non-interactive" {
123
interactive = termios.IsTerminal(cfd) && termios.IsTerminal(int(syscall.Stdout))
126
var oldttystate *termios.State
128
oldttystate, err = termios.MakeRaw(cfd)
132
defer termios.Restore(cfd, oldttystate)
135
handler := c.controlSocketHandler
140
var width, height int
142
width, height, err = termios.GetSize(int(syscall.Stdout))
148
stdout := c.getStdout()
149
ret, err := d.Exec(name, args[1:], env, os.Stdin, stdout, os.Stderr, handler, width, height)
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.
159
* Additionally, since os.Exit() exits without running deferred
160
* functions, we restore the terminal explicitly.
162
termios.Restore(cfd, oldttystate)
165
/* we get the result of waitpid() here so we need to transform it */
167
return fmt.Errorf(i18n.G("unreachable return reached"))