github.com/portworx/docker@v1.12.1/daemon/monitor.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "runtime" 8 "strconv" 9 10 "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/libcontainerd" 12 "github.com/docker/docker/runconfig" 13 ) 14 15 // StateChanged updates daemon state changes from containerd 16 func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { 17 c := daemon.containers.Get(id) 18 if c == nil { 19 return fmt.Errorf("no such container: %s", id) 20 } 21 22 switch e.State { 23 case libcontainerd.StateOOM: 24 // StateOOM is Linux specific and should never be hit on Windows 25 if runtime.GOOS == "windows" { 26 return errors.New("Received StateOOM from libcontainerd on Windows. This should never happen.") 27 } 28 daemon.updateHealthMonitor(c) 29 daemon.LogContainerEvent(c, "oom") 30 case libcontainerd.StateExit: 31 c.Lock() 32 defer c.Unlock() 33 c.Wait() 34 c.Reset(false) 35 c.SetStopped(platformConstructExitStatus(e)) 36 attributes := map[string]string{ 37 "exitCode": strconv.Itoa(int(e.ExitCode)), 38 } 39 daemon.updateHealthMonitor(c) 40 daemon.LogContainerEventWithAttributes(c, "die", attributes) 41 daemon.Cleanup(c) 42 // FIXME: here is race condition between two RUN instructions in Dockerfile 43 // because they share same runconfig and change image. Must be fixed 44 // in builder/builder.go 45 if err := c.ToDisk(); err != nil { 46 return err 47 } 48 return daemon.postRunProcessing(c, e) 49 case libcontainerd.StateRestart: 50 c.Lock() 51 defer c.Unlock() 52 c.Reset(false) 53 c.RestartCount++ 54 c.SetRestarting(platformConstructExitStatus(e)) 55 attributes := map[string]string{ 56 "exitCode": strconv.Itoa(int(e.ExitCode)), 57 } 58 daemon.LogContainerEventWithAttributes(c, "die", attributes) 59 daemon.updateHealthMonitor(c) 60 return c.ToDisk() 61 case libcontainerd.StateExitProcess: 62 c.Lock() 63 defer c.Unlock() 64 if execConfig := c.ExecCommands.Get(e.ProcessID); execConfig != nil { 65 ec := int(e.ExitCode) 66 execConfig.ExitCode = &ec 67 execConfig.Running = false 68 execConfig.Wait() 69 if err := execConfig.CloseStreams(); err != nil { 70 logrus.Errorf("%s: %s", c.ID, err) 71 } 72 73 // remove the exec command from the container's store only and not the 74 // daemon's store so that the exec command can be inspected. 75 c.ExecCommands.Delete(execConfig.ID) 76 } else { 77 logrus.Warnf("Ignoring StateExitProcess for %v but no exec command found", e) 78 } 79 case libcontainerd.StateStart, libcontainerd.StateRestore: 80 // Container is already locked in this case 81 c.SetRunning(int(e.Pid), e.State == libcontainerd.StateStart) 82 c.HasBeenManuallyStopped = false 83 if err := c.ToDisk(); err != nil { 84 c.Reset(false) 85 return err 86 } 87 daemon.initHealthMonitor(c) 88 daemon.LogContainerEvent(c, "start") 89 case libcontainerd.StatePause: 90 // Container is already locked in this case 91 c.Paused = true 92 daemon.updateHealthMonitor(c) 93 daemon.LogContainerEvent(c, "pause") 94 case libcontainerd.StateResume: 95 // Container is already locked in this case 96 c.Paused = false 97 daemon.updateHealthMonitor(c) 98 daemon.LogContainerEvent(c, "unpause") 99 } 100 101 return nil 102 } 103 104 // AttachStreams is called by libcontainerd to connect the stdio. 105 func (daemon *Daemon) AttachStreams(id string, iop libcontainerd.IOPipe) error { 106 var s *runconfig.StreamConfig 107 c := daemon.containers.Get(id) 108 if c == nil { 109 ec, err := daemon.getExecConfig(id) 110 if err != nil { 111 return fmt.Errorf("no such exec/container: %s", id) 112 } 113 s = ec.StreamConfig 114 } else { 115 s = c.StreamConfig 116 if err := daemon.StartLogging(c); err != nil { 117 c.Reset(false) 118 return err 119 } 120 } 121 122 copyFunc := func(w io.Writer, r io.Reader) { 123 s.Add(1) 124 go func() { 125 if _, err := io.Copy(w, r); err != nil { 126 logrus.Errorf("%v stream copy error: %v", id, err) 127 } 128 s.Done() 129 }() 130 } 131 132 if iop.Stdout != nil { 133 copyFunc(s.Stdout(), iop.Stdout) 134 } 135 if iop.Stderr != nil { 136 copyFunc(s.Stderr(), iop.Stderr) 137 } 138 139 if stdin := s.Stdin(); stdin != nil { 140 if iop.Stdin != nil { 141 go func() { 142 io.Copy(iop.Stdin, stdin) 143 iop.Stdin.Close() 144 }() 145 } 146 } else { 147 if c != nil && !c.Config.Tty { 148 // tty is enabled, so dont close containerd's iopipe stdin. 149 if iop.Stdin != nil { 150 iop.Stdin.Close() 151 } 152 } 153 } 154 155 return nil 156 }