]>
Commit | Line | Data |
---|---|---|
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 | 22 | lxc_log_define(lxc_checkpoint, lxc); |
23 | ||
24 | static int my_parser(struct lxc_arguments *args, int c, char *arg); | |
25 | static int my_checker(const struct lxc_arguments *args); | |
26 | static bool checkpoint(struct lxc_container *c); | |
27 | static bool restore_finalize(struct lxc_container *c); | |
28 | static bool restore(struct lxc_container *c); | |
29 | ||
30 | static char *checkpoint_dir; | |
31 | static bool stop; | |
32 | static bool verbose; | |
33 | static bool do_restore; | |
34 | static bool daemonize_set; | |
35 | static bool pre_dump; | |
36 | static char *predump_dir; | |
37 | static char *actionscript_path; | |
735f2c6e TA |
38 | |
39 | static 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 | 52 | static struct lxc_arguments my_args = { |
53 | .progname = "lxc-checkpoint", | |
54 | .help = "\ | |
55 | --name=NAME\n\ | |
56 | \n\ | |
57 | lxc-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\ | |
61 | Options :\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 | |
86 | static 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 | 129 | static 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 | 153 | static 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 | 191 | static 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 | 213 | static 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 | ||
251 | int 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 | } |