diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go index c17d343c16..30c50ecf38 100644 --- a/dockerd/dockerd.go +++ b/dockerd/dockerd.go @@ -16,6 +16,8 @@ import ( "os" "time" "net/http" + "encoding/json" + "bytes" ) @@ -70,6 +72,152 @@ func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string return nil } +func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 1 { + cmd.Usage() + return nil + } + for _, name := range cmd.Args() { + if container, exists := srv.findContainer(name); exists { + if err := container.Filesystem.Umount(); err != nil { + return err + } + fmt.Fprintln(stdout, container.Id) + } else { + return errors.New("No such container: " + name) + } + } + return nil +} + +func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 1 { + cmd.Usage() + return nil + } + for _, name := range cmd.Args() { + if container, exists := srv.findContainer(name); exists { + if err := container.Filesystem.Mount(); err != nil { + return err + } + fmt.Fprintln(stdout, container.Id) + } else { + return errors.New("No such container: " + name) + } + } + return nil +} + +func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 2 { + cmd.Usage() + return nil + } + name, path := cmd.Arg(0), cmd.Arg(1) + if container, exists := srv.findContainer(name); exists { + if f, err := container.Filesystem.OpenFile(path, os.O_RDONLY, 0); err != nil { + return err + } else if _, err := io.Copy(stdout, f); err != nil { + return err + } + return nil + } + return errors.New("No such container: " + name) +} + +func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 2 { + cmd.Usage() + return nil + } + name, path := cmd.Arg(0), cmd.Arg(1) + if container, exists := srv.findContainer(name); exists { + if f, err := container.Filesystem.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil { + return err + } else if _, err := io.Copy(f, stdin); err != nil { + return err + } + return nil + } + return errors.New("No such container: " + name) +} + + +func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 2 { + cmd.Usage() + return nil + } + name, path := cmd.Arg(0), cmd.Arg(1) + if container, exists := srv.findContainer(name); exists { + if files, err := container.Filesystem.ReadDir(path); err != nil { + return err + } else { + for _, f := range files { + fmt.Fprintln(stdout, f.Name()) + } + } + return nil + } + return errors.New("No such container: " + name) +} + +func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 1 { + cmd.Usage() + return nil + } + name := cmd.Arg(0) + if container, exists := srv.findContainer(name); exists { + data, err := json.Marshal(container) + if err != nil { + return err + } + indented := new(bytes.Buffer) + if err = json.Indent(indented, data, "", " "); err != nil { + return err + } + if _, err := io.Copy(stdout, indented); err != nil { + return err + } + return nil + } + return errors.New("No such container: " + name) +} + + + + func (srv *Server) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error { flags := rcli.Subcmd(stdout, "list", "[OPTIONS] [NAME]", "List containers") limit := flags.Int("l", 0, "Only show the N most recent versions of each name") diff --git a/filesystem.go b/filesystem.go index 71c3335404..543770dc6b 100644 --- a/filesystem.go +++ b/filesystem.go @@ -7,6 +7,8 @@ import ( "path/filepath" "strings" "syscall" + "io" + "io/ioutil" ) type Filesystem struct { @@ -180,16 +182,35 @@ func (fs *Filesystem) Changes() ([]Change, error) { return changes, nil } +// Reset removes all changes to the filesystem, reverting it to its initial state. func (fs *Filesystem) Reset() error { if err := os.RemoveAll(fs.RWPath); err != nil { return err } + // We removed the RW directory itself along with its content: let's re-create an empty one. if err := fs.createMountPoints(); err != nil { return err } return nil } +// Open opens the named file for reading. +func (fs *Filesystem) OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { + if err := fs.EnsureMounted(); err != nil { + return nil, err + } + return os.OpenFile(filepath.Join(fs.RootFS, path), flag, perm) +} + +// ReadDir reads the directory named by dirname, relative to the Filesystem's root, +// and returns a list of sorted directory entries +func (fs *Filesystem) ReadDir(dirname string) ([]os.FileInfo, error) { + if err := fs.EnsureMounted(); err != nil { + return nil, err + } + return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname)) +} + func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem { return &Filesystem{ RootFS: rootfs,