@@ -87,72 +87,101 @@ type SaveOpts struct {
8787 // after checkpointing.
8888 Resume bool
8989
90- // SaveRestoreExecArgv is the argv of the save/restore binary split by spaces.
90+ // ExecOpts contains options for executing a binary during save/restore.
91+ ExecOpts SaveRestoreExecOpts
92+ }
93+
94+ // SaveRestoreExecOpts contains options for executing a binary
95+ // during save/restore.
96+ type SaveRestoreExecOpts struct {
97+ // Argv is the argv of the save/restore binary split by spaces.
9198 // The first element is the path to the binary.
92- SaveRestoreExecArgv string
99+ Argv string
93100
94- // SaveRestoreExecTimeout is the timeout for waiting for the save/restore
95- // binary.
96- SaveRestoreExecTimeout time.Duration
101+ // Timeout is the timeout for waiting for the save/restore binary.
102+ Timeout time.Duration
97103
98- // SaveRestoreExecContainerID is the ID of the container that the
99- // save/restore binary executes in.
100- SaveRestoreExecContainerID string
104+ // ContainerID is the ID of the container that the save/restore binary executes in.
105+ ContainerID string
101106}
102107
103- // Save saves the running system.
104- func (s * State ) Save (o * SaveOpts , _ * struct {}) error {
108+ // ConvertToStateSaveOpts converts a control.SaveOpts to a state.SaveOpts.
109+ // Returns a cleanup function that must be called after the SaveOpts is no longer
110+ // needed.
111+ func ConvertToStateSaveOpts (o * SaveOpts ) (* state.SaveOpts , func (), error ) {
105112 wantFiles := 1
106113 if o .HavePagesFile {
107114 wantFiles += 2
108115 }
109116 if gotFiles := len (o .FilePayload .Files ); gotFiles != wantFiles {
110- return fmt .Errorf ("got %d files, wanted %d" , gotFiles , wantFiles )
117+ return nil , nil , fmt .Errorf ("got %d files, wanted %d" , gotFiles , wantFiles )
111118 }
112119
113120 // Save to the first provided stream.
114121 stateFile , err := o .ReleaseFD (0 )
115122 if err != nil {
116- return err
123+ return nil , nil , err
117124 }
118- defer stateFile .Close ()
119- saveOpts := state.SaveOpts {
120- Destination : stateFile ,
125+ cu := cleanup .Make (func () { stateFile .Close () })
126+ defer cu .Clean ()
127+
128+ saveOpts := & state.SaveOpts {
129+ Destination : o .FilePayload .Files [0 ],
121130 Key : o .Key ,
122131 Metadata : o .Metadata ,
123132 MemoryFileSaveOpts : o .MemoryFileSaveOpts ,
124133 Resume : o .Resume ,
125134 }
135+
126136 if o .HavePagesFile {
127137 saveOpts .PagesMetadata , err = o .ReleaseFD (1 )
128138 if err != nil {
129- return err
139+ return nil , nil , err
130140 }
131- defer saveOpts .PagesMetadata .Close ()
141+ cu . Add ( func () { saveOpts .PagesMetadata .Close () } )
132142
133143 saveOpts .PagesFile , err = o .ReleaseFD (2 )
134144 if err != nil {
135- return err
145+ return nil , nil , err
136146 }
137- defer saveOpts .PagesFile .Close ()
147+ cu . Add ( func () { saveOpts .PagesFile .Close () } )
138148 }
139- if err := PreSave (s .Kernel , o ); err != nil {
149+
150+ return saveOpts , cu .Release (), nil
151+ }
152+
153+ // Save saves the running system.
154+ func (s * State ) Save (o * SaveOpts , _ * struct {}) error {
155+ saveOpts , cleanup , err := ConvertToStateSaveOpts (o )
156+ if err != nil {
157+ return err
158+ }
159+ defer cleanup ()
160+
161+ return s .SaveWithOpts (saveOpts , & o .ExecOpts )
162+ }
163+
164+ // SaveWithOpts saves the running system with the given options.
165+ func (s * State ) SaveWithOpts (saveOpts * state.SaveOpts , execOpts * SaveRestoreExecOpts ) error {
166+ if err := preSave (s .Kernel , saveOpts , execOpts ); err != nil {
140167 return err
141168 }
142169 if err := saveOpts .Save (s .Kernel .SupervisorContext (), s .Kernel , s .Watchdog ); err != nil {
143170 return err
144171 }
145- if o .Resume {
146- err = PostResume (s .Kernel , nil )
172+ if saveOpts .Resume {
173+ if err := PostResume (s .Kernel , nil ); err != nil {
174+ return err
175+ }
147176 }
148- return err
177+ return nil
149178}
150179
151- // PreSave is called before saving the kernel.
152- func PreSave (k * kernel.Kernel , o * SaveOpts ) error {
153- if o . SaveRestoreExecArgv != "" {
154- saveRestoreExecArgv := strings .Split (o . SaveRestoreExecArgv , " " )
155- if err := ConfigureSaveRestoreExec (k , saveRestoreExecArgv , o . SaveRestoreExecTimeout , o . SaveRestoreExecContainerID ); err != nil {
180+ // preSave is called before saving the kernel.
181+ func preSave (k * kernel.Kernel , o * state. SaveOpts , execOpts * SaveRestoreExecOpts ) error {
182+ if execOpts . Argv != "" {
183+ argv := strings .Split (execOpts . Argv , " " )
184+ if err := ConfigureSaveRestoreExec (k , argv , execOpts . Timeout , execOpts . ContainerID ); err != nil {
156185 return fmt .Errorf ("failed to configure save/restore binary: %w" , err )
157186 }
158187 if err := SaveRestoreExec (k , SaveRestoreExecSave ); err != nil {
0 commit comments