Add cause to error messages

This is the inital port of the libcontainer.Error to added a cause to
all the existing error messages.  Going forward, when an error can be
wrapped because it is not being checked at the higher levels for
something like `os.IsNotExist` we can add more information to the error
message like cause and stack file/line information.  This will help
higher level tools to know what cause a container start or operation to
fail.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2016-04-18 11:37:26 -07:00
parent d1e0015032
commit 6978875298
4 changed files with 85 additions and 65 deletions

View File

@ -141,7 +141,7 @@ func (c *linuxContainer) State() (*State, error) {
func (c *linuxContainer) Processes() ([]int, error) { func (c *linuxContainer) Processes() ([]int, error) {
pids, err := c.cgroupManager.GetAllPids() pids, err := c.cgroupManager.GetAllPids()
if err != nil { if err != nil {
return nil, newSystemError(err) return nil, newSystemErrorWithCause(err, "getting all container pids from cgroups")
} }
return pids, nil return pids, nil
} }
@ -152,14 +152,14 @@ func (c *linuxContainer) Stats() (*Stats, error) {
stats = &Stats{} stats = &Stats{}
) )
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil { if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
return stats, newSystemError(err) return stats, newSystemErrorWithCause(err, "getting container stats from cgroups")
} }
for _, iface := range c.config.Networks { for _, iface := range c.config.Networks {
switch iface.Type { switch iface.Type {
case "veth": case "veth":
istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) istats, err := getNetworkInterfaceStats(iface.HostInterfaceName)
if err != nil { if err != nil {
return stats, newSystemError(err) return stats, newSystemErrorWithCausef(err, "getting network stats for interface %q", iface.HostInterfaceName)
} }
stats.Interfaces = append(stats.Interfaces, istats) stats.Interfaces = append(stats.Interfaces, istats)
} }
@ -184,14 +184,14 @@ func (c *linuxContainer) Start(process *Process) error {
doInit := status == Destroyed doInit := status == Destroyed
parent, err := c.newParentProcess(process, doInit) parent, err := c.newParentProcess(process, doInit)
if err != nil { if err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "creating new parent process")
} }
if err := parent.start(); err != nil { if err := parent.start(); err != nil {
// terminate the process to ensure that it properly is reaped. // terminate the process to ensure that it properly is reaped.
if err := parent.terminate(); err != nil { if err := parent.terminate(); err != nil {
logrus.Warn(err) logrus.Warn(err)
} }
return newSystemError(err) return newSystemErrorWithCause(err, "starting container process")
} }
// generate a timestamp indicating when the container was started // generate a timestamp indicating when the container was started
c.created = time.Now().UTC() c.created = time.Now().UTC()
@ -211,12 +211,12 @@ func (c *linuxContainer) Start(process *Process) error {
Root: c.config.Rootfs, Root: c.config.Rootfs,
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"), BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
} }
for _, hook := range c.config.Hooks.Poststart { for i, hook := range c.config.Hooks.Poststart {
if err := hook.Run(s); err != nil { if err := hook.Run(s); err != nil {
if err := parent.terminate(); err != nil { if err := parent.terminate(); err != nil {
logrus.Warn(err) logrus.Warn(err)
} }
return newSystemError(err) return newSystemErrorWithCausef(err, "running poststart hook %d", i)
} }
} }
} }
@ -226,7 +226,7 @@ func (c *linuxContainer) Start(process *Process) error {
func (c *linuxContainer) Signal(s os.Signal) error { func (c *linuxContainer) Signal(s os.Signal) error {
if err := c.initProcess.signal(s); err != nil { if err := c.initProcess.signal(s); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "signaling init process")
} }
return nil return nil
} }
@ -234,11 +234,11 @@ func (c *linuxContainer) Signal(s os.Signal) error {
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) { func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
parentPipe, childPipe, err := newPipe() parentPipe, childPipe, err := newPipe()
if err != nil { if err != nil {
return nil, newSystemError(err) return nil, newSystemErrorWithCause(err, "creating new init pipe")
} }
cmd, err := c.commandTemplate(p, childPipe) cmd, err := c.commandTemplate(p, childPipe)
if err != nil { if err != nil {
return nil, newSystemError(err) return nil, newSystemErrorWithCause(err, "creating new command template")
} }
if !doInit { if !doInit {
return c.newSetnsProcess(p, cmd, parentPipe, childPipe) return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
@ -299,7 +299,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns)) cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
state, err := c.currentState() state, err := c.currentState()
if err != nil { if err != nil {
return nil, newSystemError(err) return nil, newSystemErrorWithCause(err, "getting container's current state")
} }
// for setns process, we dont have to set cloneflags as the process namespaces // for setns process, we dont have to set cloneflags as the process namespaces
// will only be set via setns syscall // will only be set via setns syscall
@ -955,9 +955,9 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
Pid: int(notify.GetPid()), Pid: int(notify.GetPid()),
Root: c.config.Rootfs, Root: c.config.Rootfs,
} }
for _, hook := range c.config.Hooks.Prestart { for i, hook := range c.config.Hooks.Prestart {
if err := hook.Run(s); err != nil { if err := hook.Run(s); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "running prestart hook %d", i)
} }
} }
} }
@ -1046,7 +1046,7 @@ func (c *linuxContainer) isRunning() (bool, error) {
if err == syscall.ESRCH { if err == syscall.ESRCH {
return false, nil return false, nil
} }
return false, newSystemError(err) return false, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", c.initProcess.pid())
} }
return true, nil return true, nil
} }
@ -1057,7 +1057,7 @@ func (c *linuxContainer) isPaused() (bool, error) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false, nil return false, nil
} }
return false, newSystemError(err) return false, newSystemErrorWithCause(err, "checking if container is paused")
} }
return bytes.Equal(bytes.TrimSpace(data), []byte("FROZEN")), nil return bytes.Equal(bytes.TrimSpace(data), []byte("FROZEN")), nil
} }
@ -1125,7 +1125,7 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp
} }
// only set to join this namespace if it exists // only set to join this namespace if it exists
if _, err := os.Lstat(p); err != nil { if _, err := os.Lstat(p); err != nil {
return nil, newSystemError(err) return nil, newSystemErrorWithCausef(err, "running lstat on namespace path %q", p)
} }
// do not allow namespace path with comma as we use it to separate // do not allow namespace path with comma as we use it to separate
// the namespace paths // the namespace paths

View File

@ -1,6 +1,7 @@
package libcontainer package libcontainer
import ( import (
"fmt"
"io" "io"
"text/template" "text/template"
"time" "time"
@ -51,6 +52,21 @@ func newGenericError(err error, c ErrorCode) Error {
} }
func newSystemError(err error) Error { func newSystemError(err error) Error {
return createSystemError(err, "")
}
func newSystemErrorWithCausef(err error, cause string, v ...interface{}) Error {
return createSystemError(err, fmt.Sprintf(cause, v...))
}
func newSystemErrorWithCause(err error, cause string) Error {
return createSystemError(err, cause)
}
// createSystemError creates the specified error with the correct number of
// stack frames skipped. This is only to be called by the other functions for
// formatting the error.
func createSystemError(err error, cause string) Error {
if le, ok := err.(Error); ok { if le, ok := err.(Error); ok {
return le return le
} }
@ -58,7 +74,8 @@ func newSystemError(err error) Error {
Timestamp: time.Now(), Timestamp: time.Now(),
Err: err, Err: err,
ECode: SystemError, ECode: SystemError,
Stack: stacktrace.Capture(1), Cause: cause,
Stack: stacktrace.Capture(2),
} }
if err != nil { if err != nil {
gerr.Message = err.Error() gerr.Message = err.Error()
@ -70,12 +87,17 @@ type genericError struct {
Timestamp time.Time Timestamp time.Time
ECode ErrorCode ECode ErrorCode
Err error `json:"-"` Err error `json:"-"`
Cause string
Message string Message string
Stack stacktrace.Stacktrace Stack stacktrace.Stacktrace
} }
func (e *genericError) Error() string { func (e *genericError) Error() string {
return e.Message if e.Cause == "" {
return e.Message
}
frame := e.Stack.Frames[0]
return fmt.Sprintf("%s:%d: %s caused %q", frame.File, frame.Line, e.Cause, e.Message)
} }
func (e *genericError) Code() ErrorCode { func (e *genericError) Code() ErrorCode {

View File

@ -70,47 +70,47 @@ func (p *setnsProcess) start() (err error) {
err = p.cmd.Start() err = p.cmd.Start()
p.childPipe.Close() p.childPipe.Close()
if err != nil { if err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "starting setns process")
} }
if p.bootstrapData != nil { if p.bootstrapData != nil {
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "copying bootstrap data to pipe")
} }
} }
if err = p.execSetns(); err != nil { if err = p.execSetns(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "executing setns process")
} }
if len(p.cgroupPaths) > 0 { if len(p.cgroupPaths) > 0 {
if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil { if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "adding pid %d to cgroups", p.pid())
} }
} }
// set oom_score_adj // set oom_score_adj
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting oom score")
} }
// set rlimits, this has to be done here because we lose permissions // set rlimits, this has to be done here because we lose permissions
// to raise the limits once we enter a user-namespace // to raise the limits once we enter a user-namespace
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting rlimits for process")
} }
if err := utils.WriteJSON(p.parentPipe, p.config); err != nil { if err := utils.WriteJSON(p.parentPipe, p.config); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "writing config to pipe")
} }
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "calling shutdown on init pipe")
} }
// wait for the child process to fully complete and receive an error message // wait for the child process to fully complete and receive an error message
// if one was encoutered // if one was encoutered
var ierr *genericError var ierr *genericError
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
return newSystemError(err) return newSystemErrorWithCause(err, "decoding init error from pipe")
} }
// Must be done after Shutdown so the child will exit and we can wait for it. // Must be done after Shutdown so the child will exit and we can wait for it.
if ierr != nil { if ierr != nil {
p.wait() p.wait()
return newSystemError(ierr) return ierr
} }
return nil return nil
} }
@ -123,7 +123,7 @@ func (p *setnsProcess) execSetns() error {
status, err := p.cmd.Process.Wait() status, err := p.cmd.Process.Wait()
if err != nil { if err != nil {
p.cmd.Wait() p.cmd.Wait()
return newSystemError(err) return newSystemErrorWithCause(err, "waiting on setns process to finish")
} }
if !status.Success() { if !status.Success() {
p.cmd.Wait() p.cmd.Wait()
@ -132,7 +132,7 @@ func (p *setnsProcess) execSetns() error {
var pid *pid var pid *pid
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
p.cmd.Wait() p.cmd.Wait()
return newSystemError(err) return newSystemErrorWithCause(err, "reading pid from init pipe")
} }
process, err := os.FindProcess(pid.Pid) process, err := os.FindProcess(pid.Pid)
if err != nil { if err != nil {
@ -231,26 +231,26 @@ func (p *initProcess) start() error {
p.childPipe.Close() p.childPipe.Close()
if err != nil { if err != nil {
p.process.ops = nil p.process.ops = nil
return newSystemError(err) return newSystemErrorWithCause(err, "starting init process command")
} }
if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
return err return err
} }
if err := p.execSetns(); err != nil { if err := p.execSetns(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "running exec setns process for init")
} }
// Save the standard descriptor names before the container process // Save the standard descriptor names before the container process
// can potentially move them (e.g., via dup2()). If we don't do this now, // can potentially move them (e.g., via dup2()). If we don't do this now,
// we won't know at checkpoint time which file descriptor to look up. // we won't know at checkpoint time which file descriptor to look up.
fds, err := getPipeFds(p.pid()) fds, err := getPipeFds(p.pid())
if err != nil { if err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid())
} }
p.setExternalDescriptors(fds) p.setExternalDescriptors(fds)
// Do this before syncing with child so that no children // Do this before syncing with child so that no children
// can escape the cgroup // can escape the cgroup
if err := p.manager.Apply(p.pid()); err != nil { if err := p.manager.Apply(p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "applying cgroup configuration for process")
} }
defer func() { defer func() {
if err != nil { if err != nil {
@ -259,10 +259,10 @@ func (p *initProcess) start() error {
} }
}() }()
if err := p.createNetworkInterfaces(); err != nil { if err := p.createNetworkInterfaces(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "creating nework interfaces")
} }
if err := p.sendConfig(); err != nil { if err := p.sendConfig(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "sending config to init process")
} }
var ( var (
procSync syncT procSync syncT
@ -278,21 +278,21 @@ loop:
if err == io.EOF { if err == io.EOF {
break loop break loop
} }
return newSystemError(err) return newSystemErrorWithCause(err, "decoding sync type from init pipe")
} }
switch procSync.Type { switch procSync.Type {
case procReady: case procReady:
if err := p.manager.Set(p.config.Config); err != nil { if err := p.manager.Set(p.config.Config); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting cgroup config for ready process")
} }
// set oom_score_adj // set oom_score_adj
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting oom score for ready process")
} }
// set rlimits, this has to be done here because we lose permissions // set rlimits, this has to be done here because we lose permissions
// to raise the limits once we enter a user-namespace // to raise the limits once we enter a user-namespace
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting rlimits for ready process")
} }
// call prestart hooks // call prestart hooks
if !p.config.Config.Namespaces.Contains(configs.NEWNS) { if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
@ -303,16 +303,16 @@ loop:
Pid: p.pid(), Pid: p.pid(),
Root: p.config.Config.Rootfs, Root: p.config.Config.Rootfs,
} }
for _, hook := range p.config.Config.Hooks.Prestart { for i, hook := range p.config.Config.Hooks.Prestart {
if err := hook.Run(s); err != nil { if err := hook.Run(s); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "running prestart hook %d", i)
} }
} }
} }
} }
// Sync with child. // Sync with child.
if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil { if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "reading syncT run type")
} }
sentRun = true sentRun = true
case procHooks: case procHooks:
@ -324,22 +324,22 @@ loop:
Root: p.config.Config.Rootfs, Root: p.config.Config.Rootfs,
BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"), BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"),
} }
for _, hook := range p.config.Config.Hooks.Prestart { for i, hook := range p.config.Config.Hooks.Prestart {
if err := hook.Run(s); err != nil { if err := hook.Run(s); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "running prestart hook %d", i)
} }
} }
} }
// Sync with child. // Sync with child.
if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil { if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "reading syncT resume type")
} }
sentResume = true sentResume = true
case procError: case procError:
// wait for the child process to fully complete and receive an error message // wait for the child process to fully complete and receive an error message
// if one was encoutered // if one was encoutered
if err := dec.Decode(&ierr); err != nil && err != io.EOF { if err := dec.Decode(&ierr); err != nil && err != io.EOF {
return newSystemError(err) return newSystemErrorWithCause(err, "decoding proc error from init")
} }
if ierr != nil { if ierr != nil {
break loop break loop
@ -347,22 +347,22 @@ loop:
// Programmer error. // Programmer error.
panic("No error following JSON procError payload.") panic("No error following JSON procError payload.")
default: default:
return newSystemError(fmt.Errorf("invalid JSON synchronisation payload from child")) return newSystemError(fmt.Errorf("invalid JSON payload from child"))
} }
} }
if !sentRun { if !sentRun {
return newSystemError(fmt.Errorf("could not synchronise with container process: %v", ierr)) return newSystemErrorWithCause(ierr, "container init failed")
} }
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process"))
} }
if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "shutting down init pipe")
} }
// Must be done after Shutdown so the child will exit and we can wait for it. // Must be done after Shutdown so the child will exit and we can wait for it.
if ierr != nil { if ierr != nil {
p.wait() p.wait()
return newSystemError(ierr) return ierr
} }
return nil return nil
} }

View File

@ -39,35 +39,35 @@ func needsSetupDev(config *configs.Config) bool {
// new mount namespace. // new mount namespace.
func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) { func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWriter) (err error) {
if err := prepareRoot(config); err != nil { if err := prepareRoot(config); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "preparing rootfs")
} }
setupDev := needsSetupDev(config) setupDev := needsSetupDev(config)
for _, m := range config.Mounts { for _, m := range config.Mounts {
for _, precmd := range m.PremountCmds { for _, precmd := range m.PremountCmds {
if err := mountCmd(precmd); err != nil { if err := mountCmd(precmd); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "running premount command")
} }
} }
if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil { if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "mounting %q to rootfs %q", m.Destination, config.Rootfs)
} }
for _, postcmd := range m.PostmountCmds { for _, postcmd := range m.PostmountCmds {
if err := mountCmd(postcmd); err != nil { if err := mountCmd(postcmd); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "running postmount command")
} }
} }
} }
if setupDev { if setupDev {
if err := createDevices(config); err != nil { if err := createDevices(config); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "creating device nodes")
} }
if err := setupPtmx(config, console); err != nil { if err := setupPtmx(config, console); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting up ptmx")
} }
if err := setupDevSymlinks(config.Rootfs); err != nil { if err := setupDevSymlinks(config.Rootfs); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting up /dev symlinks")
} }
} }
// Signal the parent to run the pre-start hooks. // Signal the parent to run the pre-start hooks.
@ -78,7 +78,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
return err return err
} }
if err := syscall.Chdir(config.Rootfs); err != nil { if err := syscall.Chdir(config.Rootfs); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "changing dir to %q", config.Rootfs)
} }
if config.NoPivotRoot { if config.NoPivotRoot {
err = msMoveRoot(config.Rootfs) err = msMoveRoot(config.Rootfs)
@ -86,11 +86,11 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
err = pivotRoot(config.Rootfs, config.PivotDir) err = pivotRoot(config.Rootfs, config.PivotDir)
} }
if err != nil { if err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "jailing process inside rootfs")
} }
if setupDev { if setupDev {
if err := reOpenDevNull(); err != nil { if err := reOpenDevNull(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "reopening /dev/null inside container")
} }
} }
// remount dev as ro if specifed // remount dev as ro if specifed
@ -98,7 +98,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
if m.Destination == "/dev" { if m.Destination == "/dev" {
if m.Flags&syscall.MS_RDONLY != 0 { if m.Flags&syscall.MS_RDONLY != 0 {
if err := remountReadonly(m.Destination); err != nil { if err := remountReadonly(m.Destination); err != nil {
return newSystemError(err) return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination)
} }
} }
break break
@ -107,7 +107,7 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
// set rootfs ( / ) as readonly // set rootfs ( / ) as readonly
if config.Readonlyfs { if config.Readonlyfs {
if err := setReadonly(); err != nil { if err := setReadonly(); err != nil {
return newSystemError(err) return newSystemErrorWithCause(err, "setting rootfs as readonly")
} }
} }
syscall.Umask(0022) syscall.Umask(0022)
@ -115,14 +115,12 @@ func setupRootfs(config *configs.Config, console *linuxConsole, pipe io.ReadWrit
} }
func mountCmd(cmd configs.Command) error { func mountCmd(cmd configs.Command) error {
command := exec.Command(cmd.Path, cmd.Args[:]...) command := exec.Command(cmd.Path, cmd.Args[:]...)
command.Env = cmd.Env command.Env = cmd.Env
command.Dir = cmd.Dir command.Dir = cmd.Dir
if out, err := command.CombinedOutput(); err != nil { if out, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err) return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err)
} }
return nil return nil
} }