Browse code

Merge dockerd into docker. 'docker -d' runs in daemon mode. For all other commands, docker auto-detects whether to run standalone or to remote-control the daemon

Solomon Hykes authored on 2013/03/13 16:29:40
Showing 13 changed files
... ...
@@ -1,6 +1,5 @@
1 1
 .vagrant
2 2
 docker/docker
3
-dockerd/dockerd
4 3
 .*.swp
5 4
 a.out
6 5
 *.orig
7 6
deleted file mode 100644
... ...
@@ -1,53 +0,0 @@
1
-package client
2
-
3
-import (
4
-	"github.com/dotcloud/docker/future"
5
-	"github.com/dotcloud/docker/rcli"
6
-	"io"
7
-	"log"
8
-	"os"
9
-)
10
-
11
-// Run docker in "simple mode": run a single command and return.
12
-func SimpleMode(args []string) error {
13
-	var oldState *State
14
-	var err error
15
-	if IsTerminal(0) && os.Getenv("NORAW") == "" {
16
-		oldState, err = MakeRaw(0)
17
-		if err != nil {
18
-			return err
19
-		}
20
-		defer Restore(0, oldState)
21
-	}
22
-	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
23
-	// CloseWrite(), which we need to cleanly signal that stdin is closed without
24
-	// closing the connection.
25
-	// See http://code.google.com/p/go/issues/detail?id=3345
26
-	conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...)
27
-	if err != nil {
28
-		return err
29
-	}
30
-	receive_stdout := future.Go(func() error {
31
-		_, err := io.Copy(os.Stdout, conn)
32
-		return err
33
-	})
34
-	send_stdin := future.Go(func() error {
35
-		_, err := io.Copy(conn, os.Stdin)
36
-		if err := conn.CloseWrite(); err != nil {
37
-			log.Printf("Couldn't send EOF: " + err.Error())
38
-		}
39
-		return err
40
-	})
41
-	if err := <-receive_stdout; err != nil {
42
-		return err
43
-	}
44
-	if oldState != nil {
45
-		Restore(0, oldState)
46
-	}
47
-	if !IsTerminal(0) {
48
-		if err := <-send_stdin; err != nil {
49
-			return err
50
-		}
51
-	}
52
-	return nil
53
-}
54 1
deleted file mode 100644
... ...
@@ -1,143 +0,0 @@
1
-package client
2
-
3
-import (
4
-	"syscall"
5
-	"unsafe"
6
-)
7
-
8
-type Termios struct {
9
-	Iflag  uintptr
10
-	Oflag  uintptr
11
-	Cflag  uintptr
12
-	Lflag  uintptr
13
-	Cc     [20]byte
14
-	Ispeed uintptr
15
-	Ospeed uintptr
16
-}
17
-
18
-const (
19
-	// Input flags
20
-	inpck  = 0x010
21
-	istrip = 0x020
22
-	icrnl  = 0x100
23
-	ixon   = 0x200
24
-
25
-	// Output flags
26
-	opost = 0x1
27
-
28
-	// Control flags
29
-	cs8 = 0x300
30
-
31
-	// Local flags
32
-	icanon = 0x100
33
-	iexten = 0x400
34
-)
35
-
36
-const (
37
-	HUPCL   = 0x4000
38
-	ICANON  = 0x100
39
-	ICRNL   = 0x100
40
-	IEXTEN  = 0x400
41
-	BRKINT  = 0x2
42
-	CFLUSH  = 0xf
43
-	CLOCAL  = 0x8000
44
-	CREAD   = 0x800
45
-	CS5     = 0x0
46
-	CS6     = 0x100
47
-	CS7     = 0x200
48
-	CS8     = 0x300
49
-	CSIZE   = 0x300
50
-	CSTART  = 0x11
51
-	CSTATUS = 0x14
52
-	CSTOP   = 0x13
53
-	CSTOPB  = 0x400
54
-	CSUSP   = 0x1a
55
-	IGNBRK  = 0x1
56
-	IGNCR   = 0x80
57
-	IGNPAR  = 0x4
58
-	IMAXBEL = 0x2000
59
-	INLCR   = 0x40
60
-	INPCK   = 0x10
61
-	ISIG    = 0x80
62
-	ISTRIP  = 0x20
63
-	IUTF8   = 0x4000
64
-	IXANY   = 0x800
65
-	IXOFF   = 0x400
66
-	IXON    = 0x200
67
-	NOFLSH  = 0x80000000
68
-	OCRNL   = 0x10
69
-	OFDEL   = 0x20000
70
-	OFILL   = 0x80
71
-	ONLCR   = 0x2
72
-	ONLRET  = 0x40
73
-	ONOCR   = 0x20
74
-	ONOEOT  = 0x8
75
-	OPOST   = 0x1
76
-	RENB    = 0x1000
77
-	PARMRK  = 0x8
78
-	PARODD  = 0x2000
79
-
80
-	TOSTOP   = 0x400000
81
-	VDISCARD = 0xf
82
-	VDSUSP   = 0xb
83
-	VEOF     = 0x0
84
-	VEOL     = 0x1
85
-	VEOL2    = 0x2
86
-	VERASE   = 0x3
87
-	VINTR    = 0x8
88
-	VKILL    = 0x5
89
-	VLNEXT   = 0xe
90
-	VMIN     = 0x10
91
-	VQUIT    = 0x9
92
-	VREPRINT = 0x6
93
-	VSTART   = 0xc
94
-	VSTATUS  = 0x12
95
-	VSTOP    = 0xd
96
-	VSUSP    = 0xa
97
-	VT0      = 0x0
98
-	VT1      = 0x10000
99
-	VTDLY    = 0x10000
100
-	VTIME    = 0x11
101
-	ECHO     = 0x00000008
102
-
103
-	PENDIN = 0x20000000
104
-)
105
-
106
-type State struct {
107
-	termios Termios
108
-}
109
-
110
-// IsTerminal returns true if the given file descriptor is a terminal.
111
-func IsTerminal(fd int) bool {
112
-	var termios Termios
113
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
114
-	return err == 0
115
-}
116
-
117
-// MakeRaw put the terminal connected to the given file descriptor into raw
118
-// mode and returns the previous state of the terminal so that it can be
119
-// restored.
120
-func MakeRaw(fd int) (*State, error) {
121
-	var oldState State
122
-	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
123
-		return nil, err
124
-	}
125
-
126
-	newState := oldState.termios
127
-	newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
128
-	newState.Iflag |= ICRNL
129
-	newState.Oflag |= ONLCR
130
-	newState.Lflag &^= ECHO | ICANON | ISIG
131
-	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
132
-		return nil, err
133
-	}
134
-
135
-	return &oldState, nil
136
-}
137
-
138
-// Restore restores the terminal connected to the given file descriptor to a
139
-// previous state.
140
-func Restore(fd int, state *State) error {
141
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
142
-	return err
143
-}
144 1
deleted file mode 100644
... ...
@@ -1,8 +0,0 @@
1
-package client
2
-
3
-import "syscall"
4
-
5
-const (
6
-	getTermios = syscall.TIOCGETA
7
-	setTermios = syscall.TIOCSETA
8
-)
9 1
deleted file mode 100644
... ...
@@ -1,8 +0,0 @@
1
-package client
2
-
3
-import "syscall"
4
-
5
-const (
6
-	getTermios = syscall.TCGETS
7
-	setTermios = syscall.TCSETS
8
-)
9 1
new file mode 100644
... ...
@@ -0,0 +1,977 @@
0
+package commands
1
+
2
+import (
3
+	"bufio"
4
+	"bytes"
5
+	"encoding/json"
6
+	"errors"
7
+	"fmt"
8
+	"github.com/dotcloud/docker"
9
+	"github.com/dotcloud/docker/fs"
10
+	"github.com/dotcloud/docker/future"
11
+	"github.com/dotcloud/docker/rcli"
12
+	"io"
13
+	"net/http"
14
+	"net/url"
15
+	"os"
16
+	"path"
17
+	"strconv"
18
+	"strings"
19
+	"sync"
20
+	"text/tabwriter"
21
+	"time"
22
+)
23
+
24
+const VERSION = "0.0.1"
25
+
26
+func (srv *Server) Name() string {
27
+	return "docker"
28
+}
29
+
30
+// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations
31
+func (srv *Server) Help() string {
32
+	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
33
+	for _, cmd := range [][]interface{}{
34
+		{"run", "Run a command in a container"},
35
+		{"ps", "Display a list of containers"},
36
+		{"import", "Create a new filesystem image from the contents of a tarball"},
37
+		{"attach", "Attach to a running container"},
38
+		{"cat", "Write the contents of a container's file to standard output"},
39
+		{"commit", "Create a new image from a container's changes"},
40
+		{"cp", "Create a copy of IMAGE and call it NAME"},
41
+		{"debug", "(debug only) (No documentation available)"},
42
+		{"diff", "Inspect changes on a container's filesystem"},
43
+		{"images", "List images"},
44
+		{"info", "Display system-wide information"},
45
+		{"inspect", "Return low-level information on a container"},
46
+		{"kill", "Kill a running container"},
47
+		{"layers", "(debug only) List filesystem layers"},
48
+		{"logs", "Fetch the logs of a container"},
49
+		{"ls", "List the contents of a container's directory"},
50
+		{"mirror", "(debug only) (No documentation available)"},
51
+		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
52
+		{"ps", "List containers"},
53
+		{"reset", "Reset changes to a container's filesystem"},
54
+		{"restart", "Restart a running container"},
55
+		{"rm", "Remove a container"},
56
+		{"rmimage", "Remove an image"},
57
+		{"run", "Run a command in a new container"},
58
+		{"start", "Start a stopped container"},
59
+		{"stop", "Stop a running container"},
60
+		{"tar", "Stream the contents of a container as a tar archive"},
61
+		{"umount", "(debug only) Mount a container's filesystem"},
62
+		{"version", "Show the docker version information"},
63
+		{"wait", "Block until a container stops, then print its exit code"},
64
+		{"web", "A web UI for docker"},
65
+		{"write", "Write the contents of standard input to a container's file"},
66
+	} {
67
+		help += fmt.Sprintf("    %-10.10s%s\n", cmd...)
68
+	}
69
+	return help
70
+}
71
+
72
+// 'docker wait': block until a container stops
73
+func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
74
+	cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
75
+	if err := cmd.Parse(args); err != nil {
76
+		return nil
77
+	}
78
+	if cmd.NArg() < 1 {
79
+		cmd.Usage()
80
+		return nil
81
+	}
82
+	for _, name := range cmd.Args() {
83
+		if container := srv.containers.Get(name); container != nil {
84
+			fmt.Fprintln(stdout, container.Wait())
85
+		} else {
86
+			return errors.New("No such container: " + name)
87
+		}
88
+	}
89
+	return nil
90
+}
91
+
92
+// 'docker version': show version information
93
+func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
94
+	fmt.Fprintf(stdout, "Version:%s\n", VERSION)
95
+	return nil
96
+}
97
+
98
+// 'docker info': display system-wide information.
99
+func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
100
+	images, _ := srv.images.Images()
101
+	var imgcount int
102
+	if images == nil {
103
+		imgcount = 0
104
+	} else {
105
+		imgcount = len(images)
106
+	}
107
+	cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.")
108
+	if err := cmd.Parse(args); err != nil {
109
+		return nil
110
+	}
111
+	if cmd.NArg() > 0 {
112
+		cmd.Usage()
113
+		return nil
114
+	}
115
+	fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
116
+		len(srv.containers.List()),
117
+		VERSION,
118
+		imgcount)
119
+	return nil
120
+}
121
+
122
+func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
123
+	cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
124
+	if err := cmd.Parse(args); err != nil {
125
+		return nil
126
+	}
127
+	if cmd.NArg() < 1 {
128
+		cmd.Usage()
129
+		return nil
130
+	}
131
+	for _, name := range cmd.Args() {
132
+		if container := srv.containers.Get(name); container != nil {
133
+			if err := container.Stop(); err != nil {
134
+				return err
135
+			}
136
+			fmt.Fprintln(stdout, container.Id)
137
+		} else {
138
+			return errors.New("No such container: " + name)
139
+		}
140
+	}
141
+	return nil
142
+}
143
+
144
+func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
145
+	cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
146
+	if err := cmd.Parse(args); err != nil {
147
+		return nil
148
+	}
149
+	if cmd.NArg() < 1 {
150
+		cmd.Usage()
151
+		return nil
152
+	}
153
+	for _, name := range cmd.Args() {
154
+		if container := srv.containers.Get(name); container != nil {
155
+			if err := container.Restart(); err != nil {
156
+				return err
157
+			}
158
+			fmt.Fprintln(stdout, container.Id)
159
+		} else {
160
+			return errors.New("No such container: " + name)
161
+		}
162
+	}
163
+	return nil
164
+}
165
+
166
+func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
167
+	cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
168
+	if err := cmd.Parse(args); err != nil {
169
+		return nil
170
+	}
171
+	if cmd.NArg() < 1 {
172
+		cmd.Usage()
173
+		return nil
174
+	}
175
+	for _, name := range cmd.Args() {
176
+		if container := srv.containers.Get(name); container != nil {
177
+			if err := container.Start(); err != nil {
178
+				return err
179
+			}
180
+			fmt.Fprintln(stdout, container.Id)
181
+		} else {
182
+			return errors.New("No such container: " + name)
183
+		}
184
+	}
185
+	return nil
186
+}
187
+
188
+func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
189
+	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
190
+	if err := cmd.Parse(args); err != nil {
191
+		return nil
192
+	}
193
+	if cmd.NArg() < 1 {
194
+		cmd.Usage()
195
+		return nil
196
+	}
197
+	for _, name := range cmd.Args() {
198
+		if container := srv.containers.Get(name); container != nil {
199
+			if err := container.Mountpoint.Umount(); err != nil {
200
+				return err
201
+			}
202
+			fmt.Fprintln(stdout, container.Id)
203
+		} else {
204
+			return errors.New("No such container: " + name)
205
+		}
206
+	}
207
+	return nil
208
+}
209
+
210
+func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
211
+	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
212
+	if err := cmd.Parse(args); err != nil {
213
+		return nil
214
+	}
215
+	if cmd.NArg() < 1 {
216
+		cmd.Usage()
217
+		return nil
218
+	}
219
+	for _, name := range cmd.Args() {
220
+		if container := srv.containers.Get(name); container != nil {
221
+			if err := container.Mountpoint.EnsureMounted(); err != nil {
222
+				return err
223
+			}
224
+			fmt.Fprintln(stdout, container.Id)
225
+		} else {
226
+			return errors.New("No such container: " + name)
227
+		}
228
+	}
229
+	return nil
230
+}
231
+
232
+func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
233
+	cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
234
+	if err := cmd.Parse(args); err != nil {
235
+		return nil
236
+	}
237
+	if cmd.NArg() < 2 {
238
+		cmd.Usage()
239
+		return nil
240
+	}
241
+	name, path := cmd.Arg(0), cmd.Arg(1)
242
+	if container := srv.containers.Get(name); container != nil {
243
+		if f, err := container.Mountpoint.OpenFile(path, os.O_RDONLY, 0); err != nil {
244
+			return err
245
+		} else if _, err := io.Copy(stdout, f); err != nil {
246
+			return err
247
+		}
248
+		return nil
249
+	}
250
+	return errors.New("No such container: " + name)
251
+}
252
+
253
+func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
254
+	cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
255
+	if err := cmd.Parse(args); err != nil {
256
+		return nil
257
+	}
258
+	if cmd.NArg() < 2 {
259
+		cmd.Usage()
260
+		return nil
261
+	}
262
+	name, path := cmd.Arg(0), cmd.Arg(1)
263
+	if container := srv.containers.Get(name); container != nil {
264
+		if f, err := container.Mountpoint.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
265
+			return err
266
+		} else if _, err := io.Copy(f, stdin); err != nil {
267
+			return err
268
+		}
269
+		return nil
270
+	}
271
+	return errors.New("No such container: " + name)
272
+}
273
+
274
+func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
275
+	cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
276
+	if err := cmd.Parse(args); err != nil {
277
+		return nil
278
+	}
279
+	if cmd.NArg() < 2 {
280
+		cmd.Usage()
281
+		return nil
282
+	}
283
+	name, path := cmd.Arg(0), cmd.Arg(1)
284
+	if container := srv.containers.Get(name); container != nil {
285
+		if files, err := container.Mountpoint.ReadDir(path); err != nil {
286
+			return err
287
+		} else {
288
+			for _, f := range files {
289
+				fmt.Fprintln(stdout, f.Name())
290
+			}
291
+		}
292
+		return nil
293
+	}
294
+	return errors.New("No such container: " + name)
295
+}
296
+
297
+func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
298
+	cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
299
+	if err := cmd.Parse(args); err != nil {
300
+		return nil
301
+	}
302
+	if cmd.NArg() < 1 {
303
+		cmd.Usage()
304
+		return nil
305
+	}
306
+	name := cmd.Arg(0)
307
+	var obj interface{}
308
+	if container := srv.containers.Get(name); container != nil {
309
+		obj = container
310
+		//} else if image, err := srv.images.List(name); image != nil {
311
+		//	obj = image
312
+	} else {
313
+		return errors.New("No such container or image: " + name)
314
+	}
315
+	data, err := json.Marshal(obj)
316
+	if err != nil {
317
+		return err
318
+	}
319
+	indented := new(bytes.Buffer)
320
+	if err = json.Indent(indented, data, "", "    "); err != nil {
321
+		return err
322
+	}
323
+	if _, err := io.Copy(stdout, indented); err != nil {
324
+		return err
325
+	}
326
+	return nil
327
+}
328
+
329
+func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
330
+	cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
331
+	if err := cmd.Parse(args); err != nil {
332
+		return nil
333
+	}
334
+	if cmd.NArg() != 2 {
335
+		cmd.Usage()
336
+		return nil
337
+	}
338
+	name := cmd.Arg(0)
339
+	privatePort := cmd.Arg(1)
340
+	if container := srv.containers.Get(name); container == nil {
341
+		return errors.New("No such container: " + name)
342
+	} else {
343
+		if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
344
+			return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
345
+		} else {
346
+			fmt.Fprintln(stdout, frontend)
347
+		}
348
+	}
349
+	return nil
350
+}
351
+
352
+// 'docker rmi NAME' removes all images with the name NAME
353
+// func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
354
+// 	cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
355
+// 	fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
356
+// 	if err := cmd.Parse(args); err != nil {
357
+// 		cmd.Usage()
358
+// 		return nil
359
+// 	}
360
+// 	if cmd.NArg() < 1 {
361
+// 		cmd.Usage()
362
+// 		return nil
363
+// 	}
364
+// 	for _, name := range cmd.Args() {
365
+// 		var err error
366
+// 		if *fl_regexp {
367
+// 			err = srv.images.DeleteMatch(name)
368
+// 		} else {
369
+// 			image := srv.images.Find(name)
370
+// 			if image == nil {
371
+// 				return errors.New("No such image: " + name)
372
+// 			}
373
+// 			err = srv.images.Delete(name)
374
+// 		}
375
+// 		if err != nil {
376
+// 			return err
377
+// 		}
378
+// 	}
379
+// 	return nil
380
+// }
381
+
382
+func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
383
+	cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
384
+	if err := cmd.Parse(args); err != nil {
385
+		return nil
386
+	}
387
+	for _, name := range cmd.Args() {
388
+		container := srv.containers.Get(name)
389
+		if container == nil {
390
+			return errors.New("No such container: " + name)
391
+		}
392
+		if err := srv.containers.Destroy(container); err != nil {
393
+			fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
394
+		}
395
+	}
396
+	return nil
397
+}
398
+
399
+// 'docker kill NAME' kills a running container
400
+func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
401
+	cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
402
+	if err := cmd.Parse(args); err != nil {
403
+		return nil
404
+	}
405
+	for _, name := range cmd.Args() {
406
+		container := srv.containers.Get(name)
407
+		if container == nil {
408
+			return errors.New("No such container: " + name)
409
+		}
410
+		if err := container.Kill(); err != nil {
411
+			fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error())
412
+		}
413
+	}
414
+	return nil
415
+}
416
+
417
+func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
418
+	cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball")
419
+	fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin")
420
+	if err := cmd.Parse(args); err != nil {
421
+		return nil
422
+	}
423
+	var archive io.Reader
424
+	name := cmd.Arg(0)
425
+	if name == "" {
426
+		return errors.New("Not enough arguments")
427
+	}
428
+	if *fl_stdin {
429
+		archive = stdin
430
+	} else {
431
+		u, err := url.Parse(name)
432
+		if err != nil {
433
+			return err
434
+		}
435
+		if u.Scheme == "" {
436
+			u.Scheme = "http"
437
+		}
438
+		// FIXME: hardcode a mirror URL that does not depend on a single provider.
439
+		if u.Host == "" {
440
+			u.Host = "s3.amazonaws.com"
441
+			u.Path = path.Join("/docker.io/images", u.Path)
442
+		}
443
+		fmt.Fprintf(stdout, "Downloading from %s\n", u.String())
444
+		// Download with curl (pretty progress bar)
445
+		// If curl is not available, fallback to http.Get()
446
+		archive, err = future.Curl(u.String(), stdout)
447
+		if err != nil {
448
+			if resp, err := http.Get(u.String()); err != nil {
449
+				return err
450
+			} else {
451
+				archive = resp.Body
452
+			}
453
+		}
454
+	}
455
+	fmt.Fprintf(stdout, "Unpacking to %s\n", name)
456
+	img, err := srv.images.Create(archive, nil, name, "")
457
+	if err != nil {
458
+		return err
459
+	}
460
+	fmt.Fprintln(stdout, img.Id)
461
+	return nil
462
+}
463
+
464
+func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
465
+	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
466
+	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
467
+	quiet := cmd.Bool("q", false, "only show numeric IDs")
468
+	if err := cmd.Parse(args); err != nil {
469
+		return nil
470
+	}
471
+	if cmd.NArg() > 1 {
472
+		cmd.Usage()
473
+		return nil
474
+	}
475
+	var nameFilter string
476
+	if cmd.NArg() == 1 {
477
+		nameFilter = cmd.Arg(0)
478
+	}
479
+	w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
480
+	if !*quiet {
481
+		fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
482
+	}
483
+	paths, err := srv.images.Paths()
484
+	if err != nil {
485
+		return err
486
+	}
487
+	for _, name := range paths {
488
+		if nameFilter != "" && nameFilter != name {
489
+			continue
490
+		}
491
+		ids, err := srv.images.List(name)
492
+		if err != nil {
493
+			return err
494
+		}
495
+		for idx, img := range ids {
496
+			if *limit > 0 && idx >= *limit {
497
+				break
498
+			}
499
+			if !*quiet {
500
+				for idx, field := range []string{
501
+					/* NAME */ name,
502
+					/* ID */ img.Id,
503
+					/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
504
+					/* PARENT */ img.Parent,
505
+				} {
506
+					if idx == 0 {
507
+						w.Write([]byte(field))
508
+					} else {
509
+						w.Write([]byte("\t" + field))
510
+					}
511
+				}
512
+				w.Write([]byte{'\n'})
513
+			} else {
514
+				stdout.Write([]byte(img.Id + "\n"))
515
+			}
516
+		}
517
+	}
518
+	if !*quiet {
519
+		w.Flush()
520
+	}
521
+	return nil
522
+
523
+}
524
+
525
+func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
526
+	cmd := rcli.Subcmd(stdout,
527
+		"ps", "[OPTIONS]", "List containers")
528
+	quiet := cmd.Bool("q", false, "Only display numeric IDs")
529
+	fl_all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
530
+	fl_full := cmd.Bool("notrunc", false, "Don't truncate output")
531
+	if err := cmd.Parse(args); err != nil {
532
+		return nil
533
+	}
534
+	w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0)
535
+	if !*quiet {
536
+		fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
537
+	}
538
+	for _, container := range srv.containers.List() {
539
+		comment := container.GetUserData("comment")
540
+		if !container.State.Running && !*fl_all {
541
+			continue
542
+		}
543
+		if !*quiet {
544
+			command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
545
+			if !*fl_full {
546
+				command = docker.Trunc(command, 20)
547
+			}
548
+			for idx, field := range []string{
549
+				/* ID */ container.Id,
550
+				/* IMAGE */ container.GetUserData("image"),
551
+				/* COMMAND */ command,
552
+				/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
553
+				/* STATUS */ container.State.String(),
554
+				/* COMMENT */ comment,
555
+			} {
556
+				if idx == 0 {
557
+					w.Write([]byte(field))
558
+				} else {
559
+					w.Write([]byte("\t" + field))
560
+				}
561
+			}
562
+			w.Write([]byte{'\n'})
563
+		} else {
564
+			stdout.Write([]byte(container.Id + "\n"))
565
+		}
566
+	}
567
+	if !*quiet {
568
+		w.Flush()
569
+	}
570
+	return nil
571
+}
572
+
573
+func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
574
+	cmd := rcli.Subcmd(stdout,
575
+		"layers", "[OPTIONS]",
576
+		"List filesystem layers (debug only)")
577
+	if err := cmd.Parse(args); err != nil {
578
+		return nil
579
+	}
580
+	for _, layer := range srv.images.Layers() {
581
+		fmt.Fprintln(stdout, layer)
582
+	}
583
+	return nil
584
+}
585
+
586
+func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
587
+	cmd := rcli.Subcmd(stdout,
588
+		"cp", "[OPTIONS] IMAGE NAME",
589
+		"Create a copy of IMAGE and call it NAME")
590
+	if err := cmd.Parse(args); err != nil {
591
+		return nil
592
+	}
593
+	if image, err := srv.images.Get(cmd.Arg(0)); err != nil {
594
+		return err
595
+	} else if image == nil {
596
+		return errors.New("Image " + cmd.Arg(0) + " does not exist")
597
+	} else {
598
+		if img, err := image.Copy(cmd.Arg(1)); err != nil {
599
+			return err
600
+		} else {
601
+			fmt.Fprintln(stdout, img.Id)
602
+		}
603
+	}
604
+	return nil
605
+}
606
+
607
+func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
608
+	cmd := rcli.Subcmd(stdout,
609
+		"commit", "[OPTIONS] CONTAINER [DEST]",
610
+		"Create a new image from a container's changes")
611
+	if err := cmd.Parse(args); err != nil {
612
+		return nil
613
+	}
614
+	containerName, imgName := cmd.Arg(0), cmd.Arg(1)
615
+	if containerName == "" || imgName == "" {
616
+		cmd.Usage()
617
+		return nil
618
+	}
619
+	if container := srv.containers.Get(containerName); container != nil {
620
+		// FIXME: freeze the container before copying it to avoid data corruption?
621
+		rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
622
+		if err != nil {
623
+			return err
624
+		}
625
+		// Create a new image from the container's base layers + a new layer from container changes
626
+		parentImg, err := srv.images.Get(container.Image)
627
+		if err != nil {
628
+			return err
629
+		}
630
+
631
+		img, err := srv.images.Create(rwTar, parentImg, imgName, "")
632
+		if err != nil {
633
+			return err
634
+		}
635
+
636
+		fmt.Fprintln(stdout, img.Id)
637
+		return nil
638
+	}
639
+	return errors.New("No such container: " + containerName)
640
+}
641
+
642
+func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
643
+	cmd := rcli.Subcmd(stdout,
644
+		"tar", "CONTAINER",
645
+		"Stream the contents of a container as a tar archive")
646
+	fl_sparse := cmd.Bool("s", false, "Generate a sparse tar stream (top layer + reference to bottom layers)")
647
+	if err := cmd.Parse(args); err != nil {
648
+		return nil
649
+	}
650
+	if *fl_sparse {
651
+		return errors.New("Sparse mode not yet implemented") // FIXME
652
+	}
653
+	name := cmd.Arg(0)
654
+	if container := srv.containers.Get(name); container != nil {
655
+		if err := container.Mountpoint.EnsureMounted(); err != nil {
656
+			return err
657
+		}
658
+		data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
659
+		if err != nil {
660
+			return err
661
+		}
662
+		// Stream the entire contents of the container (basically a volatile snapshot)
663
+		if _, err := io.Copy(stdout, data); err != nil {
664
+			return err
665
+		}
666
+		return nil
667
+	}
668
+	return errors.New("No such container: " + name)
669
+}
670
+
671
+func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
672
+	cmd := rcli.Subcmd(stdout,
673
+		"diff", "CONTAINER [OPTIONS]",
674
+		"Inspect changes on a container's filesystem")
675
+	if err := cmd.Parse(args); err != nil {
676
+		return nil
677
+	}
678
+	if cmd.NArg() < 1 {
679
+		return errors.New("Not enough arguments")
680
+	}
681
+	if container := srv.containers.Get(cmd.Arg(0)); container == nil {
682
+		return errors.New("No such container")
683
+	} else {
684
+		changes, err := srv.images.Changes(container.Mountpoint)
685
+		if err != nil {
686
+			return err
687
+		}
688
+		for _, change := range changes {
689
+			fmt.Fprintln(stdout, change.String())
690
+		}
691
+	}
692
+	return nil
693
+}
694
+
695
+func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
696
+	cmd := rcli.Subcmd(stdout,
697
+		"reset", "CONTAINER [OPTIONS]",
698
+		"Reset changes to a container's filesystem")
699
+	if err := cmd.Parse(args); err != nil {
700
+		return nil
701
+	}
702
+	if cmd.NArg() < 1 {
703
+		return errors.New("Not enough arguments")
704
+	}
705
+	for _, name := range cmd.Args() {
706
+		if container := srv.containers.Get(name); container != nil {
707
+			if err := container.Mountpoint.Reset(); err != nil {
708
+				return errors.New("Reset " + container.Id + ": " + err.Error())
709
+			}
710
+		}
711
+	}
712
+	return nil
713
+}
714
+
715
+func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
716
+	cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
717
+	if err := cmd.Parse(args); err != nil {
718
+		return nil
719
+	}
720
+	if cmd.NArg() != 1 {
721
+		cmd.Usage()
722
+		return nil
723
+	}
724
+	name := cmd.Arg(0)
725
+	if container := srv.containers.Get(name); container != nil {
726
+		if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
727
+			return err
728
+		}
729
+		if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
730
+			return err
731
+		}
732
+		return nil
733
+	}
734
+	return errors.New("No such container: " + cmd.Arg(0))
735
+}
736
+
737
+func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
738
+	id := future.RandomId()[:8]
739
+	container, err := srv.containers.Create(id, cmd, args, img,
740
+		&docker.Config{
741
+			Hostname:  id,
742
+			Ports:     ports,
743
+			User:      user,
744
+			Tty:       tty,
745
+			OpenStdin: openStdin,
746
+			Memory:    memory,
747
+		})
748
+	if err != nil {
749
+		return nil, err
750
+	}
751
+	if err := container.SetUserData("image", img.Id); err != nil {
752
+		srv.containers.Destroy(container)
753
+		return nil, errors.New("Error setting container userdata: " + err.Error())
754
+	}
755
+	if err := container.SetUserData("comment", comment); err != nil {
756
+		srv.containers.Destroy(container)
757
+		return nil, errors.New("Error setting container userdata: " + err.Error())
758
+	}
759
+	return container, nil
760
+}
761
+
762
+func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
763
+	cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
764
+	fl_i := cmd.Bool("i", false, "Attach to stdin")
765
+	fl_o := cmd.Bool("o", true, "Attach to stdout")
766
+	fl_e := cmd.Bool("e", true, "Attach to stderr")
767
+	if err := cmd.Parse(args); err != nil {
768
+		return nil
769
+	}
770
+	if cmd.NArg() != 1 {
771
+		cmd.Usage()
772
+		return nil
773
+	}
774
+	name := cmd.Arg(0)
775
+	container := srv.containers.Get(name)
776
+	if container == nil {
777
+		return errors.New("No such container: " + name)
778
+	}
779
+	var wg sync.WaitGroup
780
+	if *fl_i {
781
+		c_stdin, err := container.StdinPipe()
782
+		if err != nil {
783
+			return err
784
+		}
785
+		wg.Add(1)
786
+		go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
787
+	}
788
+	if *fl_o {
789
+		c_stdout, err := container.StdoutPipe()
790
+		if err != nil {
791
+			return err
792
+		}
793
+		wg.Add(1)
794
+		go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
795
+	}
796
+	if *fl_e {
797
+		c_stderr, err := container.StderrPipe()
798
+		if err != nil {
799
+			return err
800
+		}
801
+		wg.Add(1)
802
+		go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
803
+	}
804
+	wg.Wait()
805
+	return nil
806
+}
807
+
808
+// Ports type - Used to parse multiple -p flags
809
+type ports []int
810
+
811
+func (p *ports) String() string {
812
+	return fmt.Sprint(*p)
813
+}
814
+
815
+func (p *ports) Set(value string) error {
816
+	port, err := strconv.Atoi(value)
817
+	if err != nil {
818
+		return fmt.Errorf("Invalid port: %v", value)
819
+	}
820
+	*p = append(*p, port)
821
+	return nil
822
+}
823
+
824
+func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
825
+	cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
826
+	fl_user := cmd.String("u", "", "Username or UID")
827
+	fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
828
+	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
829
+	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
830
+	fl_comment := cmd.String("c", "", "Comment")
831
+	fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
832
+	var fl_ports ports
833
+	cmd.Var(&fl_ports, "p", "Map a network port to the container")
834
+	if err := cmd.Parse(args); err != nil {
835
+		return nil
836
+	}
837
+	name := cmd.Arg(0)
838
+	var cmdline []string
839
+	if len(cmd.Args()) >= 2 {
840
+		cmdline = cmd.Args()[1:]
841
+	}
842
+	// Choose a default image if needed
843
+	if name == "" {
844
+		name = "base"
845
+	}
846
+	// Choose a default command if needed
847
+	if len(cmdline) == 0 {
848
+		*fl_stdin = true
849
+		*fl_tty = true
850
+		*fl_attach = true
851
+		cmdline = []string{"/bin/bash", "-i"}
852
+	}
853
+	// Find the image
854
+	img, err := srv.images.Find(name)
855
+	if err != nil {
856
+		return err
857
+	} else if img == nil {
858
+		return errors.New("No such image: " + name)
859
+	}
860
+	// Create new container
861
+	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
862
+		*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
863
+	if err != nil {
864
+		return errors.New("Error creating container: " + err.Error())
865
+	}
866
+	if *fl_stdin {
867
+		cmd_stdin, err := container.StdinPipe()
868
+		if err != nil {
869
+			return err
870
+		}
871
+		if *fl_attach {
872
+			future.Go(func() error {
873
+				_, err := io.Copy(cmd_stdin, stdin)
874
+				cmd_stdin.Close()
875
+				return err
876
+			})
877
+		}
878
+	}
879
+	// Run the container
880
+	if *fl_attach {
881
+		cmd_stderr, err := container.StderrPipe()
882
+		if err != nil {
883
+			return err
884
+		}
885
+		cmd_stdout, err := container.StdoutPipe()
886
+		if err != nil {
887
+			return err
888
+		}
889
+		if err := container.Start(); err != nil {
890
+			return err
891
+		}
892
+		sending_stdout := future.Go(func() error {
893
+			_, err := io.Copy(stdout, cmd_stdout)
894
+			return err
895
+		})
896
+		sending_stderr := future.Go(func() error {
897
+			_, err := io.Copy(stdout, cmd_stderr)
898
+			return err
899
+		})
900
+		err_sending_stdout := <-sending_stdout
901
+		err_sending_stderr := <-sending_stderr
902
+		if err_sending_stdout != nil {
903
+			return err_sending_stdout
904
+		}
905
+		if err_sending_stderr != nil {
906
+			return err_sending_stderr
907
+		}
908
+		container.Wait()
909
+	} else {
910
+		if err := container.Start(); err != nil {
911
+			return err
912
+		}
913
+		fmt.Fprintln(stdout, container.Id)
914
+	}
915
+	return nil
916
+}
917
+
918
+func New() (*Server, error) {
919
+	future.Seed()
920
+	// if err != nil {
921
+	// 	return nil, err
922
+	// }
923
+	containers, err := docker.New()
924
+	if err != nil {
925
+		return nil, err
926
+	}
927
+	srv := &Server{
928
+		images:     containers.Store,
929
+		containers: containers,
930
+	}
931
+	return srv, nil
932
+}
933
+
934
+func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
935
+	_, err := io.Copy(stdout, stdin)
936
+	return err
937
+}
938
+
939
+func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
940
+	for {
941
+		if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
942
+			fmt.Printf("--- %s", line)
943
+		} else if err == io.EOF {
944
+			if len(line) > 0 {
945
+				fmt.Printf("--- %s\n", line)
946
+			}
947
+			break
948
+		} else {
949
+			return err
950
+		}
951
+	}
952
+	return nil
953
+}
954
+
955
+func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
956
+	cmd := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker")
957
+	showurl := cmd.Bool("u", false, "Return the URL of the web UI")
958
+	if err := cmd.Parse(args); err != nil {
959
+		return nil
960
+	}
961
+	if *showurl {
962
+		fmt.Fprintln(stdout, "http://localhost:4242/web")
963
+	} else {
964
+		if file, err := os.Open("dockerweb.html"); err != nil {
965
+			return err
966
+		} else if _, err := io.Copy(stdout, file); err != nil {
967
+			return err
968
+		}
969
+	}
970
+	return nil
971
+}
972
+
973
+type Server struct {
974
+	containers *docker.Docker
975
+	images     *fs.Store
976
+}
... ...
@@ -1,13 +1,93 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"github.com/dotcloud/docker/client"
4
+	"flag"
5
+	"github.com/dotcloud/docker"
6
+	"github.com/dotcloud/docker/commands"
7
+	"github.com/dotcloud/docker/future"
8
+	"github.com/dotcloud/docker/rcli"
9
+	"github.com/dotcloud/docker/term"
10
+	"io"
5 11
 	"log"
6 12
 	"os"
7 13
 )
8 14
 
9 15
 func main() {
10
-	if err := client.SimpleMode(os.Args[1:]); err != nil {
11
-		log.Fatal(err)
16
+	if docker.SelfPath() == "/sbin/init" {
17
+		// Running in init mode
18
+		docker.SysInit()
19
+		return
12 20
 	}
21
+	fl_daemon := flag.Bool("d", false, "Daemon mode")
22
+	flag.Parse()
23
+	if *fl_daemon {
24
+		if flag.NArg() != 0 {
25
+			flag.Usage()
26
+			return
27
+		}
28
+		if err := daemon(); err != nil {
29
+			log.Fatal(err)
30
+		}
31
+	} else {
32
+		if err := runCommand(flag.Args()); err != nil {
33
+			log.Fatal(err)
34
+		}
35
+	}
36
+}
37
+
38
+func daemon() error {
39
+	service, err := commands.New()
40
+	if err != nil {
41
+		return err
42
+	}
43
+	return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service)
44
+}
45
+
46
+func runCommand(args []string) error {
47
+	var oldState *term.State
48
+	var err error
49
+	if term.IsTerminal(0) && os.Getenv("NORAW") == "" {
50
+		oldState, err = term.MakeRaw(0)
51
+		if err != nil {
52
+			return err
53
+		}
54
+		defer term.Restore(0, oldState)
55
+	}
56
+	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
57
+	// CloseWrite(), which we need to cleanly signal that stdin is closed without
58
+	// closing the connection.
59
+	// See http://code.google.com/p/go/issues/detail?id=3345
60
+	if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
61
+		receive_stdout := future.Go(func() error {
62
+			_, err := io.Copy(os.Stdout, conn)
63
+			return err
64
+		})
65
+		send_stdin := future.Go(func() error {
66
+			_, err := io.Copy(conn, os.Stdin)
67
+			if err := conn.CloseWrite(); err != nil {
68
+				log.Printf("Couldn't send EOF: " + err.Error())
69
+			}
70
+			return err
71
+		})
72
+		if err := <-receive_stdout; err != nil {
73
+			return err
74
+		}
75
+		if !term.IsTerminal(0) {
76
+			if err := <-send_stdin; err != nil {
77
+				return err
78
+			}
79
+		}
80
+	} else {
81
+		service, err := commands.New()
82
+		if err != nil {
83
+			return err
84
+		}
85
+		if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil {
86
+			return err
87
+		}
88
+	}
89
+	if oldState != nil {
90
+		term.Restore(0, oldState)
91
+	}
92
+	return nil
13 93
 }
14 94
deleted file mode 100644
... ...
@@ -1,24 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"flag"
5
-	"github.com/dotcloud/docker"
6
-	"github.com/dotcloud/docker/server"
7
-	"log"
8
-)
9
-
10
-func main() {
11
-	if docker.SelfPath() == "/sbin/init" {
12
-		// Running in init mode
13
-		docker.SysInit()
14
-		return
15
-	}
16
-	flag.Parse()
17
-	d, err := server.New()
18
-	if err != nil {
19
-		log.Fatal(err)
20
-	}
21
-	if err := d.ListenAndServe(); err != nil {
22
-		log.Fatal(err)
23
-	}
24
-}
... ...
@@ -25,7 +25,12 @@ type Service interface {
25 25
 type Cmd func(io.ReadCloser, io.Writer, ...string) error
26 26
 type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
27 27
 
28
+// FIXME: For reverse compatibility
28 29
 func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
30
+	return LocalCall(service, stdin, stdout, args...)
31
+}
32
+
33
+func LocalCall(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
29 34
 	if len(args) == 0 {
30 35
 		args = []string{"help"}
31 36
 	}
32 37
deleted file mode 100644
... ...
@@ -1,986 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"bufio"
5
-	"bytes"
6
-	"encoding/json"
7
-	"errors"
8
-	"fmt"
9
-	"github.com/dotcloud/docker"
10
-	"github.com/dotcloud/docker/fs"
11
-	"github.com/dotcloud/docker/future"
12
-	"github.com/dotcloud/docker/rcli"
13
-	"io"
14
-	"net/http"
15
-	"net/url"
16
-	"os"
17
-	"path"
18
-	"strconv"
19
-	"strings"
20
-	"sync"
21
-	"text/tabwriter"
22
-	"time"
23
-)
24
-
25
-const VERSION = "0.0.1"
26
-
27
-func (srv *Server) ListenAndServe() error {
28
-	go rcli.ListenAndServeHTTP("127.0.0.1:8080", srv)
29
-	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
30
-	// CloseWrite(), which we need to cleanly signal that stdin is closed without
31
-	// closing the connection.
32
-	// See http://code.google.com/p/go/issues/detail?id=3345
33
-	return rcli.ListenAndServe("tcp", "127.0.0.1:4242", srv)
34
-}
35
-
36
-func (srv *Server) Name() string {
37
-	return "docker"
38
-}
39
-
40
-// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations
41
-func (srv *Server) Help() string {
42
-	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
43
-	for _, cmd := range [][]interface{}{
44
-		{"run", "Run a command in a container"},
45
-		{"ps", "Display a list of containers"},
46
-		{"import", "Create a new filesystem image from the contents of a tarball"},
47
-		{"attach", "Attach to a running container"},
48
-		{"cat", "Write the contents of a container's file to standard output"},
49
-		{"commit", "Create a new image from a container's changes"},
50
-		{"cp", "Create a copy of IMAGE and call it NAME"},
51
-		{"debug", "(debug only) (No documentation available)"},
52
-		{"diff", "Inspect changes on a container's filesystem"},
53
-		{"images", "List images"},
54
-		{"info", "Display system-wide information"},
55
-		{"inspect", "Return low-level information on a container"},
56
-		{"kill", "Kill a running container"},
57
-		{"layers", "(debug only) List filesystem layers"},
58
-		{"logs", "Fetch the logs of a container"},
59
-		{"ls", "List the contents of a container's directory"},
60
-		{"mirror", "(debug only) (No documentation available)"},
61
-		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
62
-		{"ps", "List containers"},
63
-		{"reset", "Reset changes to a container's filesystem"},
64
-		{"restart", "Restart a running container"},
65
-		{"rm", "Remove a container"},
66
-		{"rmimage", "Remove an image"},
67
-		{"run", "Run a command in a new container"},
68
-		{"start", "Start a stopped container"},
69
-		{"stop", "Stop a running container"},
70
-		{"tar", "Stream the contents of a container as a tar archive"},
71
-		{"umount", "(debug only) Mount a container's filesystem"},
72
-		{"version", "Show the docker version information"},
73
-		{"wait", "Block until a container stops, then print its exit code"},
74
-		{"web", "A web UI for docker"},
75
-		{"write", "Write the contents of standard input to a container's file"},
76
-	} {
77
-		help += fmt.Sprintf("    %-10.10s%s\n", cmd...)
78
-	}
79
-	return help
80
-}
81
-
82
-// 'docker wait': block until a container stops
83
-func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
84
-	cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
85
-	if err := cmd.Parse(args); err != nil {
86
-		return nil
87
-	}
88
-	if cmd.NArg() < 1 {
89
-		cmd.Usage()
90
-		return nil
91
-	}
92
-	for _, name := range cmd.Args() {
93
-		if container := srv.containers.Get(name); container != nil {
94
-			fmt.Fprintln(stdout, container.Wait())
95
-		} else {
96
-			return errors.New("No such container: " + name)
97
-		}
98
-	}
99
-	return nil
100
-}
101
-
102
-// 'docker version': show version information
103
-func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
104
-	fmt.Fprintf(stdout, "Version:%s\n", VERSION)
105
-	return nil
106
-}
107
-
108
-// 'docker info': display system-wide information.
109
-func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
110
-	images, _ := srv.images.Images()
111
-	var imgcount int
112
-	if images == nil {
113
-		imgcount = 0
114
-	} else {
115
-		imgcount = len(images)
116
-	}
117
-	cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.")
118
-	if err := cmd.Parse(args); err != nil {
119
-		return nil
120
-	}
121
-	if cmd.NArg() > 0 {
122
-		cmd.Usage()
123
-		return nil
124
-	}
125
-	fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
126
-		len(srv.containers.List()),
127
-		VERSION,
128
-		imgcount)
129
-	return nil
130
-}
131
-
132
-func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
133
-	cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
134
-	if err := cmd.Parse(args); err != nil {
135
-		return nil
136
-	}
137
-	if cmd.NArg() < 1 {
138
-		cmd.Usage()
139
-		return nil
140
-	}
141
-	for _, name := range cmd.Args() {
142
-		if container := srv.containers.Get(name); container != nil {
143
-			if err := container.Stop(); err != nil {
144
-				return err
145
-			}
146
-			fmt.Fprintln(stdout, container.Id)
147
-		} else {
148
-			return errors.New("No such container: " + name)
149
-		}
150
-	}
151
-	return nil
152
-}
153
-
154
-func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
155
-	cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
156
-	if err := cmd.Parse(args); err != nil {
157
-		return nil
158
-	}
159
-	if cmd.NArg() < 1 {
160
-		cmd.Usage()
161
-		return nil
162
-	}
163
-	for _, name := range cmd.Args() {
164
-		if container := srv.containers.Get(name); container != nil {
165
-			if err := container.Restart(); err != nil {
166
-				return err
167
-			}
168
-			fmt.Fprintln(stdout, container.Id)
169
-		} else {
170
-			return errors.New("No such container: " + name)
171
-		}
172
-	}
173
-	return nil
174
-}
175
-
176
-func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
177
-	cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
178
-	if err := cmd.Parse(args); err != nil {
179
-		return nil
180
-	}
181
-	if cmd.NArg() < 1 {
182
-		cmd.Usage()
183
-		return nil
184
-	}
185
-	for _, name := range cmd.Args() {
186
-		if container := srv.containers.Get(name); container != nil {
187
-			if err := container.Start(); err != nil {
188
-				return err
189
-			}
190
-			fmt.Fprintln(stdout, container.Id)
191
-		} else {
192
-			return errors.New("No such container: " + name)
193
-		}
194
-	}
195
-	return nil
196
-}
197
-
198
-func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
199
-	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
200
-	if err := cmd.Parse(args); err != nil {
201
-		return nil
202
-	}
203
-	if cmd.NArg() < 1 {
204
-		cmd.Usage()
205
-		return nil
206
-	}
207
-	for _, name := range cmd.Args() {
208
-		if container := srv.containers.Get(name); container != nil {
209
-			if err := container.Mountpoint.Umount(); err != nil {
210
-				return err
211
-			}
212
-			fmt.Fprintln(stdout, container.Id)
213
-		} else {
214
-			return errors.New("No such container: " + name)
215
-		}
216
-	}
217
-	return nil
218
-}
219
-
220
-func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
221
-	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
222
-	if err := cmd.Parse(args); err != nil {
223
-		return nil
224
-	}
225
-	if cmd.NArg() < 1 {
226
-		cmd.Usage()
227
-		return nil
228
-	}
229
-	for _, name := range cmd.Args() {
230
-		if container := srv.containers.Get(name); container != nil {
231
-			if err := container.Mountpoint.EnsureMounted(); err != nil {
232
-				return err
233
-			}
234
-			fmt.Fprintln(stdout, container.Id)
235
-		} else {
236
-			return errors.New("No such container: " + name)
237
-		}
238
-	}
239
-	return nil
240
-}
241
-
242
-func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
243
-	cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
244
-	if err := cmd.Parse(args); err != nil {
245
-		return nil
246
-	}
247
-	if cmd.NArg() < 2 {
248
-		cmd.Usage()
249
-		return nil
250
-	}
251
-	name, path := cmd.Arg(0), cmd.Arg(1)
252
-	if container := srv.containers.Get(name); container != nil {
253
-		if f, err := container.Mountpoint.OpenFile(path, os.O_RDONLY, 0); err != nil {
254
-			return err
255
-		} else if _, err := io.Copy(stdout, f); err != nil {
256
-			return err
257
-		}
258
-		return nil
259
-	}
260
-	return errors.New("No such container: " + name)
261
-}
262
-
263
-func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
264
-	cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
265
-	if err := cmd.Parse(args); err != nil {
266
-		return nil
267
-	}
268
-	if cmd.NArg() < 2 {
269
-		cmd.Usage()
270
-		return nil
271
-	}
272
-	name, path := cmd.Arg(0), cmd.Arg(1)
273
-	if container := srv.containers.Get(name); container != nil {
274
-		if f, err := container.Mountpoint.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
275
-			return err
276
-		} else if _, err := io.Copy(f, stdin); err != nil {
277
-			return err
278
-		}
279
-		return nil
280
-	}
281
-	return errors.New("No such container: " + name)
282
-}
283
-
284
-func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
285
-	cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
286
-	if err := cmd.Parse(args); err != nil {
287
-		return nil
288
-	}
289
-	if cmd.NArg() < 2 {
290
-		cmd.Usage()
291
-		return nil
292
-	}
293
-	name, path := cmd.Arg(0), cmd.Arg(1)
294
-	if container := srv.containers.Get(name); container != nil {
295
-		if files, err := container.Mountpoint.ReadDir(path); err != nil {
296
-			return err
297
-		} else {
298
-			for _, f := range files {
299
-				fmt.Fprintln(stdout, f.Name())
300
-			}
301
-		}
302
-		return nil
303
-	}
304
-	return errors.New("No such container: " + name)
305
-}
306
-
307
-func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
308
-	cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
309
-	if err := cmd.Parse(args); err != nil {
310
-		return nil
311
-	}
312
-	if cmd.NArg() < 1 {
313
-		cmd.Usage()
314
-		return nil
315
-	}
316
-	name := cmd.Arg(0)
317
-	var obj interface{}
318
-	if container := srv.containers.Get(name); container != nil {
319
-		obj = container
320
-		//} else if image, err := srv.images.List(name); image != nil {
321
-		//	obj = image
322
-	} else {
323
-		return errors.New("No such container or image: " + name)
324
-	}
325
-	data, err := json.Marshal(obj)
326
-	if err != nil {
327
-		return err
328
-	}
329
-	indented := new(bytes.Buffer)
330
-	if err = json.Indent(indented, data, "", "    "); err != nil {
331
-		return err
332
-	}
333
-	if _, err := io.Copy(stdout, indented); err != nil {
334
-		return err
335
-	}
336
-	return nil
337
-}
338
-
339
-func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
340
-	cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
341
-	if err := cmd.Parse(args); err != nil {
342
-		return nil
343
-	}
344
-	if cmd.NArg() != 2 {
345
-		cmd.Usage()
346
-		return nil
347
-	}
348
-	name := cmd.Arg(0)
349
-	privatePort := cmd.Arg(1)
350
-	if container := srv.containers.Get(name); container == nil {
351
-		return errors.New("No such container: " + name)
352
-	} else {
353
-		if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
354
-			return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
355
-		} else {
356
-			fmt.Fprintln(stdout, frontend)
357
-		}
358
-	}
359
-	return nil
360
-}
361
-
362
-// 'docker rmi NAME' removes all images with the name NAME
363
-// func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
364
-// 	cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
365
-// 	fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
366
-// 	if err := cmd.Parse(args); err != nil {
367
-// 		cmd.Usage()
368
-// 		return nil
369
-// 	}
370
-// 	if cmd.NArg() < 1 {
371
-// 		cmd.Usage()
372
-// 		return nil
373
-// 	}
374
-// 	for _, name := range cmd.Args() {
375
-// 		var err error
376
-// 		if *fl_regexp {
377
-// 			err = srv.images.DeleteMatch(name)
378
-// 		} else {
379
-// 			image := srv.images.Find(name)
380
-// 			if image == nil {
381
-// 				return errors.New("No such image: " + name)
382
-// 			}
383
-// 			err = srv.images.Delete(name)
384
-// 		}
385
-// 		if err != nil {
386
-// 			return err
387
-// 		}
388
-// 	}
389
-// 	return nil
390
-// }
391
-
392
-func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
393
-	cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
394
-	if err := cmd.Parse(args); err != nil {
395
-		return nil
396
-	}
397
-	for _, name := range cmd.Args() {
398
-		container := srv.containers.Get(name)
399
-		if container == nil {
400
-			return errors.New("No such container: " + name)
401
-		}
402
-		if err := srv.containers.Destroy(container); err != nil {
403
-			fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
404
-		}
405
-	}
406
-	return nil
407
-}
408
-
409
-// 'docker kill NAME' kills a running container
410
-func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
411
-	cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
412
-	if err := cmd.Parse(args); err != nil {
413
-		return nil
414
-	}
415
-	for _, name := range cmd.Args() {
416
-		container := srv.containers.Get(name)
417
-		if container == nil {
418
-			return errors.New("No such container: " + name)
419
-		}
420
-		if err := container.Kill(); err != nil {
421
-			fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error())
422
-		}
423
-	}
424
-	return nil
425
-}
426
-
427
-func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
428
-	cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball")
429
-	fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin")
430
-	if err := cmd.Parse(args); err != nil {
431
-		return nil
432
-	}
433
-	var archive io.Reader
434
-	name := cmd.Arg(0)
435
-	if name == "" {
436
-		return errors.New("Not enough arguments")
437
-	}
438
-	if *fl_stdin {
439
-		archive = stdin
440
-	} else {
441
-		u, err := url.Parse(name)
442
-		if err != nil {
443
-			return err
444
-		}
445
-		if u.Scheme == "" {
446
-			u.Scheme = "http"
447
-		}
448
-		// FIXME: hardcode a mirror URL that does not depend on a single provider.
449
-		if u.Host == "" {
450
-			u.Host = "s3.amazonaws.com"
451
-			u.Path = path.Join("/docker.io/images", u.Path)
452
-		}
453
-		fmt.Fprintf(stdout, "Downloading from %s\n", u.String())
454
-		// Download with curl (pretty progress bar)
455
-		// If curl is not available, fallback to http.Get()
456
-		archive, err = future.Curl(u.String(), stdout)
457
-		if err != nil {
458
-			if resp, err := http.Get(u.String()); err != nil {
459
-				return err
460
-			} else {
461
-				archive = resp.Body
462
-			}
463
-		}
464
-	}
465
-	fmt.Fprintf(stdout, "Unpacking to %s\n", name)
466
-	img, err := srv.images.Create(archive, nil, name, "")
467
-	if err != nil {
468
-		return err
469
-	}
470
-	fmt.Fprintln(stdout, img.Id)
471
-	return nil
472
-}
473
-
474
-func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
475
-	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
476
-	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
477
-	quiet := cmd.Bool("q", false, "only show numeric IDs")
478
-	if err := cmd.Parse(args); err != nil {
479
-		return nil
480
-	}
481
-	if cmd.NArg() > 1 {
482
-		cmd.Usage()
483
-		return nil
484
-	}
485
-	var nameFilter string
486
-	if cmd.NArg() == 1 {
487
-		nameFilter = cmd.Arg(0)
488
-	}
489
-	w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
490
-	if !*quiet {
491
-		fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
492
-	}
493
-	paths, err := srv.images.Paths()
494
-	if err != nil {
495
-		return err
496
-	}
497
-	for _, name := range paths {
498
-		if nameFilter != "" && nameFilter != name {
499
-			continue
500
-		}
501
-		ids, err := srv.images.List(name)
502
-		if err != nil {
503
-			return err
504
-		}
505
-		for idx, img := range ids {
506
-			if *limit > 0 && idx >= *limit {
507
-				break
508
-			}
509
-			if !*quiet {
510
-				for idx, field := range []string{
511
-					/* NAME */ name,
512
-					/* ID */ img.Id,
513
-					/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
514
-					/* PARENT */ img.Parent,
515
-				} {
516
-					if idx == 0 {
517
-						w.Write([]byte(field))
518
-					} else {
519
-						w.Write([]byte("\t" + field))
520
-					}
521
-				}
522
-				w.Write([]byte{'\n'})
523
-			} else {
524
-				stdout.Write([]byte(img.Id + "\n"))
525
-			}
526
-		}
527
-	}
528
-	if !*quiet {
529
-		w.Flush()
530
-	}
531
-	return nil
532
-
533
-}
534
-
535
-func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
536
-	cmd := rcli.Subcmd(stdout,
537
-		"ps", "[OPTIONS]", "List containers")
538
-	quiet := cmd.Bool("q", false, "Only display numeric IDs")
539
-	fl_all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
540
-	fl_full := cmd.Bool("notrunc", false, "Don't truncate output")
541
-	if err := cmd.Parse(args); err != nil {
542
-		return nil
543
-	}
544
-	w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0)
545
-	if !*quiet {
546
-		fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
547
-	}
548
-	for _, container := range srv.containers.List() {
549
-		comment := container.GetUserData("comment")
550
-		if !container.State.Running && !*fl_all {
551
-			continue
552
-		}
553
-		if !*quiet {
554
-			command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
555
-			if !*fl_full {
556
-				command = docker.Trunc(command, 20)
557
-			}
558
-			for idx, field := range []string{
559
-				/* ID */ container.Id,
560
-				/* IMAGE */ container.GetUserData("image"),
561
-				/* COMMAND */ command,
562
-				/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
563
-				/* STATUS */ container.State.String(),
564
-				/* COMMENT */ comment,
565
-			} {
566
-				if idx == 0 {
567
-					w.Write([]byte(field))
568
-				} else {
569
-					w.Write([]byte("\t" + field))
570
-				}
571
-			}
572
-			w.Write([]byte{'\n'})
573
-		} else {
574
-			stdout.Write([]byte(container.Id + "\n"))
575
-		}
576
-	}
577
-	if !*quiet {
578
-		w.Flush()
579
-	}
580
-	return nil
581
-}
582
-
583
-func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
584
-	cmd := rcli.Subcmd(stdout,
585
-		"layers", "[OPTIONS]",
586
-		"List filesystem layers (debug only)")
587
-	if err := cmd.Parse(args); err != nil {
588
-		return nil
589
-	}
590
-	for _, layer := range srv.images.Layers() {
591
-		fmt.Fprintln(stdout, layer)
592
-	}
593
-	return nil
594
-}
595
-
596
-func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
597
-	cmd := rcli.Subcmd(stdout,
598
-		"cp", "[OPTIONS] IMAGE NAME",
599
-		"Create a copy of IMAGE and call it NAME")
600
-	if err := cmd.Parse(args); err != nil {
601
-		return nil
602
-	}
603
-	if image, err := srv.images.Get(cmd.Arg(0)); err != nil {
604
-		return err
605
-	} else if image == nil {
606
-		return errors.New("Image " + cmd.Arg(0) + " does not exist")
607
-	} else {
608
-		if img, err := image.Copy(cmd.Arg(1)); err != nil {
609
-			return err
610
-		} else {
611
-			fmt.Fprintln(stdout, img.Id)
612
-		}
613
-	}
614
-	return nil
615
-}
616
-
617
-func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
618
-	cmd := rcli.Subcmd(stdout,
619
-		"commit", "[OPTIONS] CONTAINER [DEST]",
620
-		"Create a new image from a container's changes")
621
-	if err := cmd.Parse(args); err != nil {
622
-		return nil
623
-	}
624
-	containerName, imgName := cmd.Arg(0), cmd.Arg(1)
625
-	if containerName == "" || imgName == "" {
626
-		cmd.Usage()
627
-		return nil
628
-	}
629
-	if container := srv.containers.Get(containerName); container != nil {
630
-		// FIXME: freeze the container before copying it to avoid data corruption?
631
-		rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
632
-		if err != nil {
633
-			return err
634
-		}
635
-		// Create a new image from the container's base layers + a new layer from container changes
636
-		parentImg, err := srv.images.Get(container.Image)
637
-		if err != nil {
638
-			return err
639
-		}
640
-
641
-		img, err := srv.images.Create(rwTar, parentImg, imgName, "")
642
-		if err != nil {
643
-			return err
644
-		}
645
-
646
-		fmt.Fprintln(stdout, img.Id)
647
-		return nil
648
-	}
649
-	return errors.New("No such container: " + containerName)
650
-}
651
-
652
-func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
653
-	cmd := rcli.Subcmd(stdout,
654
-		"tar", "CONTAINER",
655
-		"Stream the contents of a container as a tar archive")
656
-	fl_sparse := cmd.Bool("s", false, "Generate a sparse tar stream (top layer + reference to bottom layers)")
657
-	if err := cmd.Parse(args); err != nil {
658
-		return nil
659
-	}
660
-	if *fl_sparse {
661
-		return errors.New("Sparse mode not yet implemented") // FIXME
662
-	}
663
-	name := cmd.Arg(0)
664
-	if container := srv.containers.Get(name); container != nil {
665
-		if err := container.Mountpoint.EnsureMounted(); err != nil {
666
-			return err
667
-		}
668
-		data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
669
-		if err != nil {
670
-			return err
671
-		}
672
-		// Stream the entire contents of the container (basically a volatile snapshot)
673
-		if _, err := io.Copy(stdout, data); err != nil {
674
-			return err
675
-		}
676
-		return nil
677
-	}
678
-	return errors.New("No such container: " + name)
679
-}
680
-
681
-func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
682
-	cmd := rcli.Subcmd(stdout,
683
-		"diff", "CONTAINER [OPTIONS]",
684
-		"Inspect changes on a container's filesystem")
685
-	if err := cmd.Parse(args); err != nil {
686
-		return nil
687
-	}
688
-	if cmd.NArg() < 1 {
689
-		return errors.New("Not enough arguments")
690
-	}
691
-	if container := srv.containers.Get(cmd.Arg(0)); container == nil {
692
-		return errors.New("No such container")
693
-	} else {
694
-		changes, err := srv.images.Changes(container.Mountpoint)
695
-		if err != nil {
696
-			return err
697
-		}
698
-		for _, change := range changes {
699
-			fmt.Fprintln(stdout, change.String())
700
-		}
701
-	}
702
-	return nil
703
-}
704
-
705
-func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
706
-	cmd := rcli.Subcmd(stdout,
707
-		"reset", "CONTAINER [OPTIONS]",
708
-		"Reset changes to a container's filesystem")
709
-	if err := cmd.Parse(args); err != nil {
710
-		return nil
711
-	}
712
-	if cmd.NArg() < 1 {
713
-		return errors.New("Not enough arguments")
714
-	}
715
-	for _, name := range cmd.Args() {
716
-		if container := srv.containers.Get(name); container != nil {
717
-			if err := container.Mountpoint.Reset(); err != nil {
718
-				return errors.New("Reset " + container.Id + ": " + err.Error())
719
-			}
720
-		}
721
-	}
722
-	return nil
723
-}
724
-
725
-func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
726
-	cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
727
-	if err := cmd.Parse(args); err != nil {
728
-		return nil
729
-	}
730
-	if cmd.NArg() != 1 {
731
-		cmd.Usage()
732
-		return nil
733
-	}
734
-	name := cmd.Arg(0)
735
-	if container := srv.containers.Get(name); container != nil {
736
-		if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
737
-			return err
738
-		}
739
-		if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
740
-			return err
741
-		}
742
-		return nil
743
-	}
744
-	return errors.New("No such container: " + cmd.Arg(0))
745
-}
746
-
747
-func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
748
-	id := future.RandomId()[:8]
749
-	container, err := srv.containers.Create(id, cmd, args, img,
750
-		&docker.Config{
751
-			Hostname:  id,
752
-			Ports:     ports,
753
-			User:      user,
754
-			Tty:       tty,
755
-			OpenStdin: openStdin,
756
-			Memory:    memory,
757
-		})
758
-	if err != nil {
759
-		return nil, err
760
-	}
761
-	if err := container.SetUserData("image", img.Id); err != nil {
762
-		srv.containers.Destroy(container)
763
-		return nil, errors.New("Error setting container userdata: " + err.Error())
764
-	}
765
-	if err := container.SetUserData("comment", comment); err != nil {
766
-		srv.containers.Destroy(container)
767
-		return nil, errors.New("Error setting container userdata: " + err.Error())
768
-	}
769
-	return container, nil
770
-}
771
-
772
-func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
773
-	cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
774
-	fl_i := cmd.Bool("i", false, "Attach to stdin")
775
-	fl_o := cmd.Bool("o", true, "Attach to stdout")
776
-	fl_e := cmd.Bool("e", true, "Attach to stderr")
777
-	if err := cmd.Parse(args); err != nil {
778
-		return nil
779
-	}
780
-	if cmd.NArg() != 1 {
781
-		cmd.Usage()
782
-		return nil
783
-	}
784
-	name := cmd.Arg(0)
785
-	container := srv.containers.Get(name)
786
-	if container == nil {
787
-		return errors.New("No such container: " + name)
788
-	}
789
-	var wg sync.WaitGroup
790
-	if *fl_i {
791
-		c_stdin, err := container.StdinPipe()
792
-		if err != nil {
793
-			return err
794
-		}
795
-		wg.Add(1)
796
-		go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
797
-	}
798
-	if *fl_o {
799
-		c_stdout, err := container.StdoutPipe()
800
-		if err != nil {
801
-			return err
802
-		}
803
-		wg.Add(1)
804
-		go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
805
-	}
806
-	if *fl_e {
807
-		c_stderr, err := container.StderrPipe()
808
-		if err != nil {
809
-			return err
810
-		}
811
-		wg.Add(1)
812
-		go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
813
-	}
814
-	wg.Wait()
815
-	return nil
816
-}
817
-
818
-// Ports type - Used to parse multiple -p flags
819
-type ports []int
820
-
821
-func (p *ports) String() string {
822
-	return fmt.Sprint(*p)
823
-}
824
-
825
-func (p *ports) Set(value string) error {
826
-	port, err := strconv.Atoi(value)
827
-	if err != nil {
828
-		return fmt.Errorf("Invalid port: %v", value)
829
-	}
830
-	*p = append(*p, port)
831
-	return nil
832
-}
833
-
834
-func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
835
-	cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
836
-	fl_user := cmd.String("u", "", "Username or UID")
837
-	fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
838
-	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
839
-	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
840
-	fl_comment := cmd.String("c", "", "Comment")
841
-	fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
842
-	var fl_ports ports
843
-	cmd.Var(&fl_ports, "p", "Map a network port to the container")
844
-	if err := cmd.Parse(args); err != nil {
845
-		return nil
846
-	}
847
-	name := cmd.Arg(0)
848
-	var cmdline []string
849
-	if len(cmd.Args()) >= 2 {
850
-		cmdline = cmd.Args()[1:]
851
-	}
852
-	// Choose a default image if needed
853
-	if name == "" {
854
-		name = "base"
855
-	}
856
-	// Choose a default command if needed
857
-	if len(cmdline) == 0 {
858
-		*fl_stdin = true
859
-		*fl_tty = true
860
-		*fl_attach = true
861
-		cmdline = []string{"/bin/bash", "-i"}
862
-	}
863
-	// Find the image
864
-	img, err := srv.images.Find(name)
865
-	if err != nil {
866
-		return err
867
-	} else if img == nil {
868
-		return errors.New("No such image: " + name)
869
-	}
870
-	// Create new container
871
-	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
872
-		*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
873
-	if err != nil {
874
-		return errors.New("Error creating container: " + err.Error())
875
-	}
876
-	if *fl_stdin {
877
-		cmd_stdin, err := container.StdinPipe()
878
-		if err != nil {
879
-			return err
880
-		}
881
-		if *fl_attach {
882
-			future.Go(func() error {
883
-				_, err := io.Copy(cmd_stdin, stdin)
884
-				cmd_stdin.Close()
885
-				return err
886
-			})
887
-		}
888
-	}
889
-	// Run the container
890
-	if *fl_attach {
891
-		cmd_stderr, err := container.StderrPipe()
892
-		if err != nil {
893
-			return err
894
-		}
895
-		cmd_stdout, err := container.StdoutPipe()
896
-		if err != nil {
897
-			return err
898
-		}
899
-		if err := container.Start(); err != nil {
900
-			return err
901
-		}
902
-		sending_stdout := future.Go(func() error {
903
-			_, err := io.Copy(stdout, cmd_stdout)
904
-			return err
905
-		})
906
-		sending_stderr := future.Go(func() error {
907
-			_, err := io.Copy(stdout, cmd_stderr)
908
-			return err
909
-		})
910
-		err_sending_stdout := <-sending_stdout
911
-		err_sending_stderr := <-sending_stderr
912
-		if err_sending_stdout != nil {
913
-			return err_sending_stdout
914
-		}
915
-		if err_sending_stderr != nil {
916
-			return err_sending_stderr
917
-		}
918
-		container.Wait()
919
-	} else {
920
-		if err := container.Start(); err != nil {
921
-			return err
922
-		}
923
-		fmt.Fprintln(stdout, container.Id)
924
-	}
925
-	return nil
926
-}
927
-
928
-func New() (*Server, error) {
929
-	future.Seed()
930
-	// if err != nil {
931
-	// 	return nil, err
932
-	// }
933
-	containers, err := docker.New()
934
-	if err != nil {
935
-		return nil, err
936
-	}
937
-	srv := &Server{
938
-		images:     containers.Store,
939
-		containers: containers,
940
-	}
941
-	return srv, nil
942
-}
943
-
944
-func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
945
-	_, err := io.Copy(stdout, stdin)
946
-	return err
947
-}
948
-
949
-func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
950
-	for {
951
-		if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
952
-			fmt.Printf("--- %s", line)
953
-		} else if err == io.EOF {
954
-			if len(line) > 0 {
955
-				fmt.Printf("--- %s\n", line)
956
-			}
957
-			break
958
-		} else {
959
-			return err
960
-		}
961
-	}
962
-	return nil
963
-}
964
-
965
-func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
966
-	cmd := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker")
967
-	showurl := cmd.Bool("u", false, "Return the URL of the web UI")
968
-	if err := cmd.Parse(args); err != nil {
969
-		return nil
970
-	}
971
-	if *showurl {
972
-		fmt.Fprintln(stdout, "http://localhost:4242/web")
973
-	} else {
974
-		if file, err := os.Open("dockerweb.html"); err != nil {
975
-			return err
976
-		} else if _, err := io.Copy(stdout, file); err != nil {
977
-			return err
978
-		}
979
-	}
980
-	return nil
981
-}
982
-
983
-type Server struct {
984
-	containers *docker.Docker
985
-	images     *fs.Store
986
-}
987 1
new file mode 100644
... ...
@@ -0,0 +1,143 @@
0
+package term
1
+
2
+import (
3
+	"syscall"
4
+	"unsafe"
5
+)
6
+
7
+type Termios struct {
8
+	Iflag  uintptr
9
+	Oflag  uintptr
10
+	Cflag  uintptr
11
+	Lflag  uintptr
12
+	Cc     [20]byte
13
+	Ispeed uintptr
14
+	Ospeed uintptr
15
+}
16
+
17
+const (
18
+	// Input flags
19
+	inpck  = 0x010
20
+	istrip = 0x020
21
+	icrnl  = 0x100
22
+	ixon   = 0x200
23
+
24
+	// Output flags
25
+	opost = 0x1
26
+
27
+	// Control flags
28
+	cs8 = 0x300
29
+
30
+	// Local flags
31
+	icanon = 0x100
32
+	iexten = 0x400
33
+)
34
+
35
+const (
36
+	HUPCL   = 0x4000
37
+	ICANON  = 0x100
38
+	ICRNL   = 0x100
39
+	IEXTEN  = 0x400
40
+	BRKINT  = 0x2
41
+	CFLUSH  = 0xf
42
+	CLOCAL  = 0x8000
43
+	CREAD   = 0x800
44
+	CS5     = 0x0
45
+	CS6     = 0x100
46
+	CS7     = 0x200
47
+	CS8     = 0x300
48
+	CSIZE   = 0x300
49
+	CSTART  = 0x11
50
+	CSTATUS = 0x14
51
+	CSTOP   = 0x13
52
+	CSTOPB  = 0x400
53
+	CSUSP   = 0x1a
54
+	IGNBRK  = 0x1
55
+	IGNCR   = 0x80
56
+	IGNPAR  = 0x4
57
+	IMAXBEL = 0x2000
58
+	INLCR   = 0x40
59
+	INPCK   = 0x10
60
+	ISIG    = 0x80
61
+	ISTRIP  = 0x20
62
+	IUTF8   = 0x4000
63
+	IXANY   = 0x800
64
+	IXOFF   = 0x400
65
+	IXON    = 0x200
66
+	NOFLSH  = 0x80000000
67
+	OCRNL   = 0x10
68
+	OFDEL   = 0x20000
69
+	OFILL   = 0x80
70
+	ONLCR   = 0x2
71
+	ONLRET  = 0x40
72
+	ONOCR   = 0x20
73
+	ONOEOT  = 0x8
74
+	OPOST   = 0x1
75
+	RENB    = 0x1000
76
+	PARMRK  = 0x8
77
+	PARODD  = 0x2000
78
+
79
+	TOSTOP   = 0x400000
80
+	VDISCARD = 0xf
81
+	VDSUSP   = 0xb
82
+	VEOF     = 0x0
83
+	VEOL     = 0x1
84
+	VEOL2    = 0x2
85
+	VERASE   = 0x3
86
+	VINTR    = 0x8
87
+	VKILL    = 0x5
88
+	VLNEXT   = 0xe
89
+	VMIN     = 0x10
90
+	VQUIT    = 0x9
91
+	VREPRINT = 0x6
92
+	VSTART   = 0xc
93
+	VSTATUS  = 0x12
94
+	VSTOP    = 0xd
95
+	VSUSP    = 0xa
96
+	VT0      = 0x0
97
+	VT1      = 0x10000
98
+	VTDLY    = 0x10000
99
+	VTIME    = 0x11
100
+	ECHO     = 0x00000008
101
+
102
+	PENDIN = 0x20000000
103
+)
104
+
105
+type State struct {
106
+	termios Termios
107
+}
108
+
109
+// IsTerminal returns true if the given file descriptor is a terminal.
110
+func IsTerminal(fd int) bool {
111
+	var termios Termios
112
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
113
+	return err == 0
114
+}
115
+
116
+// MakeRaw put the terminal connected to the given file descriptor into raw
117
+// mode and returns the previous state of the terminal so that it can be
118
+// restored.
119
+func MakeRaw(fd int) (*State, error) {
120
+	var oldState State
121
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
122
+		return nil, err
123
+	}
124
+
125
+	newState := oldState.termios
126
+	newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
127
+	newState.Iflag |= ICRNL
128
+	newState.Oflag |= ONLCR
129
+	newState.Lflag &^= ECHO | ICANON | ISIG
130
+	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
131
+		return nil, err
132
+	}
133
+
134
+	return &oldState, nil
135
+}
136
+
137
+// Restore restores the terminal connected to the given file descriptor to a
138
+// previous state.
139
+func Restore(fd int, state *State) error {
140
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
141
+	return err
142
+}
0 143
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+package term
1
+
2
+import "syscall"
3
+
4
+const (
5
+	getTermios = syscall.TIOCGETA
6
+	setTermios = syscall.TIOCSETA
7
+)
0 8
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+package term
1
+
2
+import "syscall"
3
+
4
+const (
5
+	getTermios = syscall.TCGETS
6
+	setTermios = syscall.TCSETS
7
+)