]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_checkpoint.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / tools / lxc_checkpoint.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: GPL-2.0-only */
735f2c6e 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
735f2c6e 6#include <errno.h>
78485176
CB
7#include <stdio.h>
8#include <string.h>
c9d8f2ee
TA
9#include <sys/types.h>
10#include <sys/wait.h>
d38dd64a 11#include <unistd.h>
735f2c6e
TA
12
13#include <lxc/lxccontainer.h>
14
735f2c6e 15#include "arguments.h"
d38dd64a 16#include "config.h"
124fef5a 17#include "log.h"
18#include "utils.h"
735f2c6e 19
c03b2981 20#define OPT_PREDUMP_DIR (OPT_USAGE + 1)
9f99a33f 21
c03b2981 22lxc_log_define(lxc_checkpoint, lxc);
23
24static int my_parser(struct lxc_arguments *args, int c, char *arg);
25static int my_checker(const struct lxc_arguments *args);
26static bool checkpoint(struct lxc_container *c);
27static bool restore_finalize(struct lxc_container *c);
28static bool restore(struct lxc_container *c);
29
30static char *checkpoint_dir;
31static bool stop;
32static bool verbose;
33static bool do_restore;
34static bool daemonize_set;
35static bool pre_dump;
36static char *predump_dir;
37static char *actionscript_path;
735f2c6e
TA
38
39static const struct option my_longopts[] = {
40 {"checkpoint-dir", required_argument, 0, 'D'},
59019754 41 {"action-script", required_argument, 0, 'A'},
735f2c6e
TA
42 {"stop", no_argument, 0, 's'},
43 {"verbose", no_argument, 0, 'v'},
44 {"restore", no_argument, 0, 'r'},
45 {"daemon", no_argument, 0, 'd'},
46 {"foreground", no_argument, 0, 'F'},
9f99a33f
AR
47 {"pre-dump", no_argument, 0, 'p'},
48 {"predump-dir", required_argument, 0, OPT_PREDUMP_DIR},
735f2c6e
TA
49 LXC_COMMON_OPTIONS
50};
51
c03b2981 52static struct lxc_arguments my_args = {
53 .progname = "lxc-checkpoint",
54 .help = "\
55--name=NAME\n\
56\n\
57lxc-checkpoint checkpoints and restores a container\n\
58 Serializes a container's running state to disk to allow restoring it in\n\
59 its running state at a later time.\n\
60\n\
61Options :\n\
62 -n, --name=NAME NAME of the container\n\
63 -r, --restore Restore container\n\
64 -D, --checkpoint-dir=DIR directory to save the checkpoint in\n\
65 -v, --verbose Enable verbose criu logs\n\
66 -A, --action-script=PATH Path to criu action script\n\
67 Checkpoint options:\n\
68 -s, --stop Stop the container after checkpointing.\n\
69 -p, --pre-dump Only pre-dump the memory of the container.\n\
70 Container keeps on running and following\n\
71 checkpoints will only dump the changes.\n\
72 --predump-dir=DIR path to images from previous dump (relative to -D)\n\
73 Restore options:\n\
74 -d, --daemon Daemonize the container (default)\n\
75 -F, --foreground Start with the current tty attached to /dev/console\n\
76 --rcfile=FILE Load configuration file FILE\n\
77",
78 .options = my_longopts,
79 .parser = my_parser,
80 .daemonize = 1,
81 .checker = my_checker,
82 .log_priority = "ERROR",
83 .log_file = "none",
84};
735f2c6e
TA
85
86static int my_parser(struct lxc_arguments *args, int c, char *arg)
87{
88 switch (c) {
89 case 'D':
90 checkpoint_dir = strdup(arg);
91 if (!checkpoint_dir)
92 return -1;
93 break;
c03b2981 94 case 'A':
59019754
EH
95 actionscript_path = strdup(arg);
96 if (!actionscript_path)
97 return -1;
98 break;
735f2c6e
TA
99 case 's':
100 stop = true;
101 break;
102 case 'v':
103 verbose = true;
104 break;
105 case 'r':
106 do_restore = true;
107 break;
108 case 'd':
109 args->daemonize = 1;
110 daemonize_set = true;
111 break;
112 case 'F':
113 args->daemonize = 0;
114 daemonize_set = true;
115 break;
9f99a33f
AR
116 case 'p':
117 pre_dump = true;
118 break;
119 case OPT_PREDUMP_DIR:
120 predump_dir = strdup(arg);
121 if (!predump_dir)
122 return -1;
123 break;
735f2c6e 124 }
124fef5a 125
735f2c6e
TA
126 return 0;
127}
128
c03b2981 129static int my_checker(const struct lxc_arguments *args)
130{
131 if (do_restore && stop) {
132 ERROR("-s not compatible with -r");
133 return -1;
134
135 } else if (!do_restore && daemonize_set) {
136 ERROR("-d/-F not compatible with -r");
137 return -1;
138 }
139
140 if (!checkpoint_dir) {
141 ERROR("-D is required");
142 return -1;
143 }
144
145 if (pre_dump && do_restore) {
146 ERROR("-p not compatible with -r");
147 return -1;
148 }
149
150 return 0;
151}
735f2c6e 152
fa25c39a 153static bool checkpoint(struct lxc_container *c)
735f2c6e 154{
9f99a33f 155 struct migrate_opts opts;
735f2c6e 156 bool ret;
9f99a33f 157 int mode;
735f2c6e
TA
158
159 if (!c->is_running(c)) {
8fb41a34 160 ERROR("%s not running, not checkpointing", my_args.name);
735f2c6e
TA
161 lxc_container_put(c);
162 return false;
163 }
164
9f99a33f
AR
165 memset(&opts, 0, sizeof(opts));
166
167 opts.directory = checkpoint_dir;
168 opts.stop = stop;
169 opts.verbose = verbose;
170 opts.predump_dir = predump_dir;
497a7863 171 opts.action_script = actionscript_path;
9f99a33f
AR
172
173 if (pre_dump)
174 mode = MIGRATE_PRE_DUMP;
175 else
176 mode = MIGRATE_DUMP;
177
178 ret = c->migrate(c, mode, &opts, sizeof(opts));
735f2c6e
TA
179 lxc_container_put(c);
180
9f99a33f
AR
181 /* the migrate() API does not negate the return code like
182 * checkpoint() and restore() does. */
183 if (ret) {
8fb41a34 184 ERROR("Checkpointing %s failed", my_args.name);
735f2c6e
TA
185 return false;
186 }
187
188 return true;
189}
190
fa25c39a 191static bool restore_finalize(struct lxc_container *c)
735f2c6e 192{
59019754
EH
193 struct migrate_opts opts;
194 bool ret;
195
196 memset(&opts, 0, sizeof(opts));
197
198 opts.directory = checkpoint_dir;
199 opts.verbose = verbose;
200 opts.stop = stop;
201 opts.action_script = actionscript_path;
124fef5a 202
59019754
EH
203 ret = c->migrate(c, MIGRATE_RESTORE, &opts, sizeof(opts));
204 if (ret) {
8fb41a34 205 ERROR("Restoring %s failed", my_args.name);
59019754 206 return false;
c9d8f2ee 207 }
735f2c6e 208
c9d8f2ee 209 lxc_container_put(c);
59019754 210 return true;
c9d8f2ee
TA
211}
212
fa25c39a 213static bool restore(struct lxc_container *c)
c9d8f2ee 214{
735f2c6e 215 if (c->is_running(c)) {
8fb41a34 216 ERROR("%s is running, not restoring", my_args.name);
735f2c6e
TA
217 lxc_container_put(c);
218 return false;
219 }
220
c9d8f2ee
TA
221 if (my_args.daemonize) {
222 pid_t pid;
223
735f2c6e 224 pid = fork();
c9d8f2ee 225 if (pid < 0) {
124fef5a 226 SYSERROR("Failed to fork");
c9d8f2ee
TA
227 return false;
228 }
735f2c6e 229
c9d8f2ee 230 if (pid == 0) {
7943ec56
TA
231 close(0);
232 close(1);
7943ec56 233
124fef5a 234 _exit(!restore_finalize(c));
c9d8f2ee
TA
235 } else {
236 return wait_for_pid(pid) == 0;
735f2c6e 237 }
c9d8f2ee
TA
238 } else {
239 int status;
735f2c6e 240
c9d8f2ee
TA
241 if (!restore_finalize(c))
242 return false;
735f2c6e 243
c9d8f2ee
TA
244 if (waitpid(-1, &status, 0) < 0)
245 return false;
246
247 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
248 }
735f2c6e
TA
249}
250
251int main(int argc, char *argv[])
252{
253 struct lxc_container *c;
73b910a3 254 struct lxc_log log;
735f2c6e
TA
255 bool ret;
256
257 if (lxc_arguments_parse(&my_args, argc, argv))
b52b0595 258 exit(EXIT_FAILURE);
735f2c6e 259
c03b2981 260 log.name = my_args.name;
261 log.file = my_args.log_file;
262 log.level = my_args.log_priority;
263 log.prefix = my_args.progname;
264 log.quiet = my_args.quiet;
265 log.lxcpath = my_args.lxcpath[0];
f6d79ec1 266
c03b2981 267 if (lxc_log_init(&log))
268 exit(EXIT_FAILURE);
b56d64e0 269
735f2c6e
TA
270 c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
271 if (!c) {
124fef5a 272 ERROR("System error loading %s", my_args.name);
b52b0595 273 exit(EXIT_FAILURE);
735f2c6e
TA
274 }
275
50b737a3
WB
276 if (my_args.rcfile) {
277 c->clear_config(c);
124fef5a 278
50b737a3 279 if (!c->load_config(c, my_args.rcfile)) {
124fef5a 280 ERROR("Failed to load rcfile");
50b737a3 281 lxc_container_put(c);
b52b0595 282 exit(EXIT_FAILURE);
50b737a3 283 }
124fef5a 284
6118210e
WB
285 c->configfile = strdup(my_args.rcfile);
286 if (!c->configfile) {
124fef5a 287 ERROR("Out of memory setting new config filename");
6118210e 288 lxc_container_put(c);
b52b0595 289 exit(EXIT_FAILURE);
6118210e 290 }
50b737a3
WB
291 }
292
735f2c6e 293 if (!c->may_control(c)) {
124fef5a 294 ERROR("Insufficent privileges to control %s", my_args.name);
735f2c6e 295 lxc_container_put(c);
b52b0595 296 exit(EXIT_FAILURE);
735f2c6e
TA
297 }
298
299 if (!c->is_defined(c)) {
124fef5a 300 ERROR("%s is not defined", my_args.name);
735f2c6e 301 lxc_container_put(c);
b52b0595 302 exit(EXIT_FAILURE);
735f2c6e
TA
303 }
304
305
306 if (do_restore)
307 ret = restore(c);
308 else
309 ret = checkpoint(c);
124fef5a 310
59019754
EH
311 free(actionscript_path);
312 free(checkpoint_dir);
313 free(predump_dir);
735f2c6e 314
b52b0595
CB
315 if (!ret)
316 exit(EXIT_FAILURE);
317
318 exit(EXIT_SUCCESS);
735f2c6e 319}