@@ -37,6 +37,7 @@ type linuxContainer struct {
3737 criuPath string
3838 m sync.Mutex
3939 criuVersion int
40+ state containerState
4041}
4142
4243// State represents a running container's state
@@ -183,7 +184,14 @@ func (c *linuxContainer) Start(process *Process) error {
183184 return newSystemError (err )
184185 }
185186 if doInit {
186- c .updateState (parent )
187+ if err := c .updateState (parent ); err != nil {
188+ return err
189+ }
190+ } else {
191+ c .state .transition (& nullState {
192+ c : c ,
193+ s : Running ,
194+ })
187195 }
188196 if c .config .Hooks != nil {
189197 s := configs.HookState {
@@ -320,48 +328,29 @@ func newPipe() (parent *os.File, child *os.File, err error) {
320328func (c * linuxContainer ) Destroy () error {
321329 c .m .Lock ()
322330 defer c .m .Unlock ()
323- status , err := c .currentStatus ()
324- if err != nil {
325- return err
326- }
327- if status != Destroyed {
328- return newGenericError (fmt .Errorf ("container is not destroyed" ), ContainerNotStopped )
329- }
330- if ! c .config .Namespaces .Contains (configs .NEWPID ) {
331- if err := killCgroupProcesses (c .cgroupManager ); err != nil {
332- logrus .Warn (err )
333- }
334- }
335- err = c .cgroupManager .Destroy ()
336- if rerr := os .RemoveAll (c .root ); err == nil {
337- err = rerr
338- }
339- c .initProcess = nil
340- if c .config .Hooks != nil {
341- s := configs.HookState {
342- Version : c .config .Version ,
343- ID : c .id ,
344- Root : c .config .Rootfs ,
345- }
346- for _ , hook := range c .config .Hooks .Poststop {
347- if err := hook .Run (s ); err != nil {
348- return err
349- }
350- }
351- }
352- return err
331+ return c .state .destroy ()
353332}
354333
355334func (c * linuxContainer ) Pause () error {
356335 c .m .Lock ()
357336 defer c .m .Unlock ()
358- return c .cgroupManager .Freeze (configs .Frozen )
337+ if err := c .cgroupManager .Freeze (configs .Frozen ); err != nil {
338+ return err
339+ }
340+ return c .state .transition (& pausedState {
341+ c : c ,
342+ })
359343}
360344
361345func (c * linuxContainer ) Resume () error {
362346 c .m .Lock ()
363347 defer c .m .Unlock ()
364- return c .cgroupManager .Freeze (configs .Thawed )
348+ if err := c .cgroupManager .Freeze (configs .Thawed ); err != nil {
349+ return err
350+ }
351+ return c .state .transition (& runningState {
352+ c : c ,
353+ })
365354}
366355
367356func (c * linuxContainer ) NotifyOOM () (<- chan struct {}, error ) {
@@ -459,7 +448,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
459448 }
460449
461450 if criuOpts .ImagesDirectory == "" {
462- criuOpts . ImagesDirectory = filepath . Join ( c . root , "criu.image " )
451+ return fmt . Errorf ( "invalid directory to save checkpoint " )
463452 }
464453
465454 // Since a container can be C/R'ed multiple times,
@@ -578,11 +567,9 @@ func (c *linuxContainer) addCriuRestoreMount(req *criurpc.CriuReq, m *configs.Mo
578567func (c * linuxContainer ) Restore (process * Process , criuOpts * CriuOpts ) error {
579568 c .m .Lock ()
580569 defer c .m .Unlock ()
581-
582570 if err := c .checkCriuVersion ("1.5.2" ); err != nil {
583571 return err
584572 }
585-
586573 if criuOpts .WorkDirectory == "" {
587574 criuOpts .WorkDirectory = filepath .Join (c .root , "criu.work" )
588575 }
@@ -591,22 +578,19 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
591578 if err := os .Mkdir (criuOpts .WorkDirectory , 0655 ); err != nil && ! os .IsExist (err ) {
592579 return err
593580 }
594-
595581 workDir , err := os .Open (criuOpts .WorkDirectory )
596582 if err != nil {
597583 return err
598584 }
599585 defer workDir .Close ()
600-
601586 if criuOpts .ImagesDirectory == "" {
602- criuOpts . ImagesDirectory = filepath . Join ( c . root , "criu.image " )
587+ return fmt . Errorf ( "invalid directory to restore checkpoint " )
603588 }
604589 imageDir , err := os .Open (criuOpts .ImagesDirectory )
605590 if err != nil {
606591 return err
607592 }
608593 defer imageDir .Close ()
609-
610594 // CRIU has a few requirements for a root directory:
611595 // * it must be a mount point
612596 // * its parent must not be overmounted
@@ -617,18 +601,15 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
617601 return err
618602 }
619603 defer os .Remove (root )
620-
621604 root , err = filepath .EvalSymlinks (root )
622605 if err != nil {
623606 return err
624607 }
625-
626608 err = syscall .Mount (c .config .Rootfs , root , "" , syscall .MS_BIND | syscall .MS_REC , "" )
627609 if err != nil {
628610 return err
629611 }
630612 defer syscall .Unmount (root , syscall .MNT_DETACH )
631-
632613 t := criurpc .CriuReqType_RESTORE
633614 req := & criurpc.CriuReq {
634615 Type : & t ,
@@ -696,15 +677,13 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
696677 fds []string
697678 fdJSON []byte
698679 )
699-
700680 if fdJSON , err = ioutil .ReadFile (filepath .Join (criuOpts .ImagesDirectory , descriptorsFilename )); err != nil {
701681 return err
702682 }
703683
704- if err = json .Unmarshal (fdJSON , & fds ); err != nil {
684+ if err : = json .Unmarshal (fdJSON , & fds ); err != nil {
705685 return err
706686 }
707-
708687 for i := range fds {
709688 if s := fds [i ]; strings .Contains (s , "pipe:" ) {
710689 inheritFd := new (criurpc.InheritFd )
@@ -713,12 +692,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
713692 req .Opts .InheritFd = append (req .Opts .InheritFd , inheritFd )
714693 }
715694 }
716-
717- err = c .criuSwrk (process , req , criuOpts , true )
718- if err != nil {
719- return err
720- }
721- return nil
695+ return c .criuSwrk (process , req , criuOpts , true )
722696}
723697
724698func (c * linuxContainer ) criuApplyCgroups (pid int , req * criurpc.CriuReq ) error {
@@ -913,82 +887,123 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
913887 if notify == nil {
914888 return fmt .Errorf ("invalid response: %s" , resp .String ())
915889 }
916-
917890 switch {
918891 case notify .GetScript () == "post-dump" :
919- if ! opts .LeaveRunning {
920- f , err := os .Create (filepath .Join (c .root , "checkpoint" ))
921- if err != nil {
922- return err
923- }
924- f .Close ()
892+ f , err := os .Create (filepath .Join (c .root , "checkpoint" ))
893+ if err != nil {
894+ return err
925895 }
926- break
927-
896+ f .Close ()
928897 case notify .GetScript () == "network-unlock" :
929898 if err := unlockNetwork (c .config ); err != nil {
930899 return err
931900 }
932- break
933-
934901 case notify .GetScript () == "network-lock" :
935902 if err := lockNetwork (c .config ); err != nil {
936903 return err
937904 }
938- break
939-
940905 case notify .GetScript () == "post-restore" :
941906 pid := notify .GetPid ()
942907 r , err := newRestoredProcess (int (pid ), fds )
943908 if err != nil {
944909 return err
945910 }
946-
947- // TODO: crosbymichael restore previous process information by saving the init process information in
948- // the container's state file or separate process state files.
911+ process .ops = r
912+ if err := c .state .transition (& restoredState {
913+ imageDir : opts .ImagesDirectory ,
914+ c : c ,
915+ }); err != nil {
916+ return err
917+ }
949918 if err := c .updateState (r ); err != nil {
950919 return err
951920 }
952- process .ops = r
953- break
921+ if err := os .Remove (filepath .Join (c .root , "checkpoint" )); err != nil {
922+ if ! os .IsNotExist (err ) {
923+ logrus .Error (err )
924+ }
925+ }
954926 }
955-
956927 return nil
957928}
958929
959930func (c * linuxContainer ) updateState (process parentProcess ) error {
960931 c .initProcess = process
932+ if err := c .refreshState (); err != nil {
933+ return err
934+ }
961935 state , err := c .currentState ()
962936 if err != nil {
963937 return err
964938 }
939+ return c .saveState (state )
940+ }
941+
942+ func (c * linuxContainer ) saveState (s * State ) error {
965943 f , err := os .Create (filepath .Join (c .root , stateFilename ))
966944 if err != nil {
967945 return err
968946 }
969947 defer f .Close ()
970- os .Remove (filepath .Join (c .root , "checkpoint" ))
971- return json .NewEncoder (f ).Encode (state )
948+ return json .NewEncoder (f ).Encode (s )
949+ }
950+
951+ func (c * linuxContainer ) deleteState () error {
952+ return os .Remove (filepath .Join (c .root , stateFilename ))
972953}
973954
974955func (c * linuxContainer ) currentStatus () (Status , error ) {
975- if _ , err := os .Stat (filepath .Join (c .root , "checkpoint" )); err == nil {
976- return Checkpointed , nil
956+ if err := c .refreshState (); err != nil {
957+ return - 1 , err
958+ }
959+ return c .state .status (), nil
960+ }
961+
962+ // refreshState needs to be called to verify that the current state on the
963+ // container is what is true. Because consumers of libcontainer can use it
964+ // out of process we need to verify the container's status based on runtime
965+ // information and not rely on our in process info.
966+ func (c * linuxContainer ) refreshState () error {
967+ paused , err := c .isPaused ()
968+ if err != nil {
969+ return err
977970 }
971+ if paused {
972+ return c .state .transition (& pausedState {c : c })
973+ }
974+ running , err := c .isRunning ()
975+ if err != nil {
976+ return err
977+ }
978+ if running {
979+ return c .state .transition (& runningState {c : c })
980+ }
981+ return c .state .transition (& stoppedState {c : c })
982+ }
983+
984+ func (c * linuxContainer ) isRunning () (bool , error ) {
978985 if c .initProcess == nil {
979- return Destroyed , nil
986+ return false , nil
980987 }
981988 // return Running if the init process is alive
982989 if err := syscall .Kill (c .initProcess .pid (), 0 ); err != nil {
983990 if err == syscall .ESRCH {
984- return Destroyed , nil
991+ return false , nil
985992 }
986- return 0 , newSystemError (err )
993+ return false , newSystemError (err )
987994 }
988- if c .config .Cgroups != nil && c .config .Cgroups .Resources != nil && c .config .Cgroups .Resources .Freezer == configs .Frozen {
989- return Paused , nil
995+ return true , nil
996+ }
997+
998+ func (c * linuxContainer ) isPaused () (bool , error ) {
999+ data , err := ioutil .ReadFile (filepath .Join (c .cgroupManager .GetPaths ()["freezer" ], "freezer.state" ))
1000+ if err != nil {
1001+ if os .IsNotExist (err ) {
1002+ return false , nil
1003+ }
1004+ return false , newSystemError (err )
9901005 }
991- return Running , nil
1006+ return bytes . Equal ( bytes . TrimSpace ( data ), [] byte ( "FROZEN" )) , nil
9921007}
9931008
9941009func (c * linuxContainer ) currentState () (* State , error ) {
0 commit comments