Branch data Line data Source code
1 : : #include <stdio.h>
2 : : #include <stdlib.h>
3 : : #include <limits.h>
4 : : #include <unistd.h>
5 : : #include <errno.h>
6 : : #include <getopt.h>
7 : : #include <string.h>
8 : : #include <ctype.h>
9 : :
10 : : #include <fcntl.h>
11 : :
12 : : #include <sys/types.h>
13 : : #include <sys/stat.h>
14 : :
15 : : #include <sys/socket.h>
16 : : #include <netinet/in.h>
17 : : #include <arpa/inet.h>
18 : :
19 : : #include <dlfcn.h>
20 : :
21 : : #include "asm/types.h"
22 : :
23 : : #include "compiler.h"
24 : : #include "crtools.h"
25 : : #include "cr_options.h"
26 : : #include "sockets.h"
27 : : #include "syscall.h"
28 : : #include "files.h"
29 : : #include "sk-inet.h"
30 : : #include "net.h"
31 : : #include "version.h"
32 : : #include "page-xfer.h"
33 : : #include "tty.h"
34 : : #include "file-lock.h"
35 : : #include "cr-service.h"
36 : : #include "plugin.h"
37 : :
38 : : struct cr_options opts;
39 : :
40 : 3702 : void init_opts(void)
41 : : {
42 : : memset(&opts, 0, sizeof(opts));
43 : :
44 : : /* Default options */
45 : 3702 : opts.final_state = TASK_DEAD;
46 : : INIT_LIST_HEAD(&opts.veth_pairs);
47 : : INIT_LIST_HEAD(&opts.scripts);
48 : :
49 : 3702 : opts.cpu_cap = CPU_CAP_ALL;
50 : 3702 : }
51 : :
52 : 0 : static int parse_ns_string(const char *ptr)
53 : : {
54 : 0 : const char *end = ptr + strlen(ptr);
55 : :
56 : : do {
57 [ # # ]: 0 : if (ptr[3] != ',' && ptr[3] != '\0')
58 : : goto bad_ns;
59 [ # # ]: 0 : if (!strncmp(ptr, "uts", 3))
60 : 0 : opts.rst_namespaces_flags |= CLONE_NEWUTS;
61 [ # # ]: 0 : else if (!strncmp(ptr, "ipc", 3))
62 : 0 : opts.rst_namespaces_flags |= CLONE_NEWIPC;
63 [ # # ]: 0 : else if (!strncmp(ptr, "mnt", 3))
64 : 0 : opts.rst_namespaces_flags |= CLONE_NEWNS;
65 [ # # ]: 0 : else if (!strncmp(ptr, "pid", 3))
66 : 0 : opts.rst_namespaces_flags |= CLONE_NEWPID;
67 [ # # ]: 0 : else if (!strncmp(ptr, "net", 3))
68 : 0 : opts.rst_namespaces_flags |= CLONE_NEWNET;
69 : : else
70 : : goto bad_ns;
71 : 0 : ptr += 4;
72 [ # # ]: 0 : } while (ptr < end);
73 : : return 0;
74 : :
75 : : bad_ns:
76 : 0 : pr_msg("Error: unknown namespace: %s\n", ptr);
77 : 0 : return -1;
78 : : }
79 : :
80 : 0 : static int parse_cpu_cap(struct cr_options *opts, const char *optarg)
81 : : {
82 : : bool inverse = false;
83 : :
84 : : #define ____cpu_set_cap(__opts, __cap, __inverse) \
85 : : do { \
86 : : if ((__inverse)) \
87 : : (__opts)->cpu_cap &= ~(__cap); \
88 : : else \
89 : : (__opts)->cpu_cap |= (__cap); \
90 : : } while (0)
91 : :
92 [ # # ]: 0 : for (; *optarg; optarg++) {
93 [ # # ]: 0 : if (optarg[0] == '^') {
94 : 0 : inverse = !inverse;
95 : 0 : continue;
96 [ # # ]: 0 : } else if (optarg[0] == ',') {
97 : : inverse = false;
98 : 0 : continue;
99 : : }
100 : :
101 [ # # ]: 0 : if (!strncmp(optarg, "fpu", 3))
102 [ # # ]: 0 : ____cpu_set_cap(opts, CPU_CAP_FPU, inverse);
103 [ # # ]: 0 : if (!strncmp(optarg, "all", 3))
104 [ # # ]: 0 : ____cpu_set_cap(opts, CPU_CAP_ALL, inverse);
105 : : else
106 : : goto Esyntax;
107 : : }
108 : : #undef ____cpu_set_cap
109 : :
110 : : return 0;
111 : :
112 : : Esyntax:
113 : 0 : pr_err("Unknown FPU mode `%s' selected\n", optarg);
114 : : return -1;
115 : : }
116 : :
117 : 3702 : int main(int argc, char *argv[])
118 : : {
119 : : pid_t pid = 0, tree_id = 0;
120 : : int ret = -1;
121 : : bool usage_error = true;
122 : : bool has_exec_cmd = false;
123 : : int opt, idx;
124 : : int log_level = LOG_UNSET;
125 : : char *imgs_dir = ".";
126 : : char *work_dir = NULL;
127 : : static const char short_opts[] = "dsRf:F:t:p:hcD:o:n:v::xVr:jlW:L:";
128 : : static struct option long_opts[] = {
129 : : { "tree", required_argument, 0, 't' },
130 : : { "pid", required_argument, 0, 'p' },
131 : : { "leave-stopped", no_argument, 0, 's' },
132 : : { "leave-running", no_argument, 0, 'R' },
133 : : { "restore-detached", no_argument, 0, 'd' },
134 : : { "daemon", no_argument, 0, 'd' },
135 : : { "contents", no_argument, 0, 'c' },
136 : : { "file", required_argument, 0, 'f' },
137 : : { "fields", required_argument, 0, 'F' },
138 : : { "images-dir", required_argument, 0, 'D' },
139 : : { "work-dir", required_argument, 0, 'W' },
140 : : { "log-file", required_argument, 0, 'o' },
141 : : { "namespaces", required_argument, 0, 'n' },
142 : : { "root", required_argument, 0, 'r' },
143 : : { USK_EXT_PARAM, no_argument, 0, 'x' },
144 : : { "help", no_argument, 0, 'h' },
145 : : { SK_EST_PARAM, no_argument, 0, 42 },
146 : : { "close", required_argument, 0, 43 },
147 : : { "log-pid", no_argument, 0, 44},
148 : : { "version", no_argument, 0, 'V'},
149 : : { "evasive-devices", no_argument, 0, 45},
150 : : { "pidfile", required_argument, 0, 46},
151 : : { "veth-pair", required_argument, 0, 47},
152 : : { "action-script", required_argument, 0, 49},
153 : : { LREMAP_PARAM, no_argument, 0, 41},
154 : : { OPT_SHELL_JOB, no_argument, 0, 'j'},
155 : : { OPT_FILE_LOCKS, no_argument, 0, 'l'},
156 : : { "page-server", no_argument, 0, 50},
157 : : { "address", required_argument, 0, 51},
158 : : { "port", required_argument, 0, 52},
159 : : { "prev-images-dir", required_argument, 0, 53},
160 : : { "ms", no_argument, 0, 54},
161 : : { "track-mem", no_argument, 0, 55},
162 : : { "auto-dedup", no_argument, 0, 56},
163 : : { "libdir", required_argument, 0, 'L'},
164 : : { "cpu-cap", required_argument, 0, 57},
165 : : { "force-irmap", no_argument, 0, 58},
166 : : { "exec-cmd", no_argument, 0, 59},
167 : : { },
168 : : };
169 : :
170 : : BUILD_BUG_ON(PAGE_SIZE != PAGE_IMAGE_SIZE);
171 : :
172 : 3702 : cr_pb_init();
173 : 3702 : restrict_uid(getuid(), getgid());
174 : :
175 [ + - ]: 3702 : if (argc < 2)
176 : : goto usage;
177 : :
178 : 3702 : init_opts();
179 : :
180 [ + - ]: 3702 : if (init_service_fd())
181 : : return 1;
182 : :
183 : : while (1) {
184 : 44470 : idx = -1;
185 : 44470 : opt = getopt_long(argc, argv, short_opts, long_opts, &idx);
186 [ + + ]: 44470 : if (opt == -1)
187 : : break;
188 : :
189 [ - - + + : 40768 : switch (opt) {
- + - - -
+ + + - +
- + + + -
- + + - +
+ + + - +
+ + + - -
- + - -
- ]
190 : : case 's':
191 : 0 : opts.final_state = TASK_STOPPED;
192 : 0 : break;
193 : : case 'R':
194 : 1344 : opts.final_state = TASK_ALIVE;
195 : 1344 : break;
196 : : case 'x':
197 : 2804 : opts.ext_unix_sk = true;
198 : 2804 : break;
199 : : case 'p':
200 : 0 : pid = atoi(optarg);
201 [ # # ]: 0 : if (pid <= 0)
202 : : goto bad_arg;
203 : : break;
204 : : case 't':
205 : 1792 : tree_id = atoi(optarg);
206 [ + - ]: 1792 : if (tree_id <= 0)
207 : : goto bad_arg;
208 : : break;
209 : : case 'c':
210 : 0 : opts.show_pages_content = true;
211 : 0 : break;
212 : : case 'f':
213 : 0 : opts.show_dump_file = optarg;
214 : 0 : break;
215 : : case 'F':
216 : 0 : opts.show_fmt = optarg;
217 : 0 : break;
218 : : case 'r':
219 : 1260 : opts.root = optarg;
220 : 1260 : break;
221 : : case 'd':
222 : 1908 : opts.restore_detach = true;
223 : 1908 : break;
224 : : case 'D':
225 : 3700 : imgs_dir = optarg;
226 : 3700 : break;
227 : : case 'W':
228 : 0 : work_dir = optarg;
229 : 0 : break;
230 : : case 'o':
231 : 3700 : opts.output = optarg;
232 : 3700 : break;
233 : : case 'n':
234 [ # # ]: 0 : if (parse_ns_string(optarg))
235 : : goto bad_arg;
236 : : break;
237 : : case 'v':
238 [ + - ]: 3700 : if (log_level == LOG_UNSET)
239 : : log_level = 0;
240 [ + - ]: 3700 : if (optarg) {
241 [ - + ]: 3700 : if (optarg[0] == 'v')
242 : : /* handle -vvvvv */
243 : 0 : log_level += strlen(optarg) + 1;
244 : : else
245 : : log_level = atoi(optarg);
246 : : } else
247 : 0 : log_level++;
248 : : break;
249 : : case 41:
250 : 112 : pr_info("Will allow link remaps on FS\n");
251 : 112 : opts.link_remap_ok = true;
252 : 112 : break;
253 : : case 42:
254 : 2804 : pr_info("Will dump TCP connections\n");
255 : 2804 : opts.tcp_established_ok = true;
256 : 2804 : break;
257 : : case 43: {
258 : : int fd;
259 : :
260 : 0 : fd = atoi(optarg);
261 : 0 : pr_info("Closing fd %d\n", fd);
262 : 0 : close(fd);
263 : 40768 : break;
264 : : }
265 : : case 44:
266 : 0 : opts.log_file_per_pid = 1;
267 : 0 : break;
268 : : case 45:
269 : 1792 : opts.evasive_devices = true;
270 : 1792 : break;
271 : : case 46:
272 : 1260 : opts.pidfile = optarg;
273 : 1260 : break;
274 : : case 47:
275 : : {
276 : : char *aux;
277 : :
278 : 0 : aux = strchr(optarg, '=');
279 [ # # ]: 0 : if (aux == NULL)
280 : : goto bad_arg;
281 : :
282 : 0 : *aux = '\0';
283 [ # # ]: 0 : if (veth_pair_add(optarg, aux + 1))
284 : : return 1;
285 : : }
286 : : break;
287 : : case 49:
288 : : {
289 : : struct script *script;
290 : :
291 [ - + ]: 1344 : script = xmalloc(sizeof(struct script));
292 [ + - ]: 1344 : if (script == NULL)
293 : : return 1;
294 : :
295 : 1344 : script->path = optarg;
296 : 1344 : list_add(&script->node, &opts.scripts);
297 : : }
298 : : break;
299 : : case 50:
300 : 896 : opts.use_page_server = true;
301 : 896 : break;
302 : : case 51:
303 : 896 : opts.addr = optarg;
304 : 896 : break;
305 : : case 52:
306 [ - + ]: 1792 : opts.ps_port = htons(atoi(optarg));
307 [ + - ]: 1792 : if (!opts.ps_port)
308 : : goto bad_arg;
309 : : break;
310 : : case 'j':
311 : 0 : opts.shell_job = true;
312 : 0 : break;
313 : : case 'l':
314 : 2804 : opts.handle_file_locks = true;
315 : 2804 : break;
316 : : case 53:
317 : 1344 : opts.img_parent = optarg;
318 : 1344 : break;
319 : : case 55:
320 : 1792 : opts.track_mem = true;
321 : 1792 : break;
322 : : case 56:
323 : 3700 : opts.auto_dedup = true;
324 : 3700 : break;
325 : : case 57:
326 [ # # ]: 0 : if (parse_cpu_cap(&opts, optarg))
327 : : goto usage;
328 : : break;
329 : : case 58:
330 : 0 : opts.force_irmap = true;
331 : 0 : break;
332 : : case 54:
333 : 0 : opts.check_ms_kernel = true;
334 : 0 : break;
335 : : case 'L':
336 : 24 : opts.libdir = optarg;
337 : 24 : break;
338 : : case 59:
339 : : has_exec_cmd = true;
340 : : break;
341 : : case 'V':
342 : 0 : pr_msg("Version: %s\n", CRIU_VERSION);
343 : : if (strcmp(CRIU_GITID, "0"))
344 : 0 : pr_msg("GitID: %s\n", CRIU_GITID);
345 : 0 : return 0;
346 : : case 'h':
347 : : usage_error = false;
348 : 0 : goto usage;
349 : : default:
350 : : goto usage;
351 : : }
352 : : }
353 : :
354 [ + - ]: 3702 : if (work_dir == NULL)
355 : : work_dir = imgs_dir;
356 : :
357 [ - + ]: 3702 : if (optind >= argc) {
358 : 0 : pr_msg("Error: command is required\n");
359 : 0 : goto usage;
360 : : }
361 : :
362 [ - + ]: 3702 : if (has_exec_cmd) {
363 [ # # ]: 0 : if (argc - optind <= 1) {
364 : 0 : pr_msg("Error: --exec-cmd requires a command\n");
365 : 0 : goto usage;
366 : : }
367 : :
368 [ # # ]: 0 : if (strcmp(argv[optind], "restore")) {
369 : 0 : pr_msg("Error: --exec-cmd is available for the restore command only\n");
370 : 0 : goto usage;
371 : : }
372 : :
373 [ # # ]: 0 : if (opts.restore_detach) {
374 : 0 : pr_msg("Error: --restore-detached and --exec-cmd cannot be used together\n");
375 : 0 : goto usage;
376 : : }
377 : :
378 [ # # ]: 0 : opts.exec_cmd = xmalloc((argc - optind) * sizeof(char *));
379 : 0 : memcpy(opts.exec_cmd, &argv[optind + 1], (argc - optind - 1) * sizeof(char *));
380 : 0 : opts.exec_cmd[argc - optind - 1] = NULL;
381 : : }
382 : :
383 : : /* We must not open imgs dir, if service is called */
384 [ + - ]: 3702 : if (strcmp(argv[optind], "service")) {
385 : 3702 : ret = open_image_dir(imgs_dir);
386 [ + - ]: 3702 : if (ret < 0)
387 : : return 1;
388 : : }
389 : :
390 [ - + ]: 3702 : if (chdir(work_dir)) {
391 : 0 : pr_perror("Can't change directory to %s", work_dir);
392 : 0 : return 1;
393 : : }
394 : :
395 : 3702 : log_set_loglevel(log_level);
396 : :
397 [ + - ]: 3702 : if (log_init(opts.output))
398 : : return 1;
399 : :
400 [ + + ]: 3702 : if (opts.img_parent)
401 : 1344 : pr_info("Will do snapshot from %s\n", opts.img_parent);
402 : :
403 [ + + ]: 3702 : if (!strcmp(argv[optind], "dump")) {
404 [ + - ]: 448 : if (!tree_id)
405 : : goto opt_pid_missing;
406 : 448 : return cr_dump_tasks(tree_id);
407 : : }
408 : :
409 [ + + ]: 3254 : if (!strcmp(argv[optind], "pre-dump")) {
410 [ + - ]: 1344 : if (!tree_id)
411 : : goto opt_pid_missing;
412 : :
413 : 1344 : return cr_pre_dump_tasks(tree_id) != 0;
414 : : }
415 : :
416 [ + + ]: 1910 : if (!strcmp(argv[optind], "restore")) {
417 [ - + ]: 1012 : if (tree_id)
418 : 0 : pr_warn("Using -t with criu restore is obsoleted\n");
419 : :
420 : 1012 : ret = cr_restore_tasks();
421 [ + - ][ - + ]: 448 : if (ret == 0 && opts.exec_cmd) {
422 : 0 : close_pid_proc();
423 : 0 : execvp(opts.exec_cmd[0], opts.exec_cmd);
424 : 0 : pr_perror("Failed to exec command %s", opts.exec_cmd[0]);
425 : : ret = 1;
426 : : }
427 : :
428 : 448 : return ret != 0;
429 : : }
430 : :
431 [ - + ]: 898 : if (!strcmp(argv[optind], "show"))
432 : 0 : return cr_show(pid) != 0;
433 : :
434 [ + + ]: 898 : if (!strcmp(argv[optind], "check"))
435 : 2 : return cr_check() != 0;
436 : :
437 [ - + ]: 896 : if (!strcmp(argv[optind], "exec")) {
438 [ # # ]: 0 : if (!pid)
439 : : pid = tree_id; /* old usage */
440 [ # # ]: 0 : if (!pid)
441 : : goto opt_pid_missing;
442 : 0 : return cr_exec(pid, argv + optind + 1) != 0;
443 : : }
444 : :
445 [ + - ]: 896 : if (!strcmp(argv[optind], "page-server"))
446 : 896 : return cr_page_server(opts.restore_detach) > 0 ? 0 : 1;
447 : :
448 [ # # ]: 0 : if (!strcmp(argv[optind], "service"))
449 : 0 : return cr_service(opts.restore_detach);
450 : :
451 [ # # ]: 0 : if (!strcmp(argv[optind], "dedup"))
452 : 0 : return cr_dedup() != 0;
453 : :
454 : 0 : pr_msg("Error: unknown command: %s\n", argv[optind]);
455 : : usage:
456 : 0 : pr_msg("\n"
457 : : "Usage:\n"
458 : : " criu dump|pre-dump -t PID [<options>]\n"
459 : : " criu restore [<options>]\n"
460 : : " criu show (-D DIR)|(-f FILE) [<options>]\n"
461 : : " criu check [--ms]\n"
462 : : " criu exec -p PID <syscall-string>\n"
463 : : " criu page-server\n"
464 : : " criu service [<options>]\n"
465 : : " criu dedup\n"
466 : : "\n"
467 : : "Commands:\n"
468 : : " dump checkpoint a process/tree identified by pid\n"
469 : : " pre-dump pre-dump task(s) minimizing their frozen time\n"
470 : : " restore restore a process/tree\n"
471 : : " show show dump file(s) contents\n"
472 : : " check checks whether the kernel support is up-to-date\n"
473 : : " exec execute a system call by other task\n"
474 : : " page-server launch page server\n"
475 : : " service launch service\n"
476 : : " dedup remove duplicates in memory dump\n"
477 : : );
478 : :
479 [ # # ]: 0 : if (usage_error) {
480 : 0 : pr_msg("\nTry -h|--help for more info\n");
481 : 0 : return 1;
482 : : }
483 : :
484 : 0 : pr_msg("\n"
485 : : "Dump/Restore options:\n"
486 : : "\n"
487 : : "* Generic:\n"
488 : : " -t|--tree PID checkpoint a process tree identified by PID\n"
489 : : " -d|--restore-detached detach after restore\n"
490 : : " -s|--leave-stopped leave tasks in stopped state after checkpoint\n"
491 : : " -R|--leave-running leave tasks in running state after checkpoint\n"
492 : : " -D|--images-dir DIR directory for image files\n"
493 : : " --pidfile FILE write root task, service or page-server pid to FILE\n"
494 : : " -W|--work-dir DIR directory to cd and write logs/pidfiles/stats to\n"
495 : : " (if not specified, value of --images-dir is used)\n"
496 : : " --cpu-cap CAP require certain cpu capability. CAP: may be one of:\n"
497 : : " 'fpu','all'. To disable capability, prefix it with '^'.\n"
498 : : " --exec-cmd execute the command specified after '--' on successful\n"
499 : : " restore making it the parent of the restored process\n"
500 : : "\n"
501 : : "* Special resources support:\n"
502 : : " -x|--" USK_EXT_PARAM " allow external unix connections\n"
503 : : " --" SK_EST_PARAM " checkpoint/restore established TCP connections\n"
504 : : " -r|--root PATH change the root filesystem (when run in mount namespace)\n"
505 : : " --evasive-devices use any path to a device file if the original one\n"
506 : : " is inaccessible\n"
507 : : " --veth-pair IN=OUT map inside veth device name to outside one\n"
508 : : " --link-remap allow to link unlinked files back when possible\n"
509 : : " --action-script FILE add an external action script\n"
510 : : " -j|--" OPT_SHELL_JOB " allow to dump and restore shell jobs\n"
511 : : " -l|--" OPT_FILE_LOCKS " handle file locks, for safety, only used for container\n"
512 : : " -L|--libdir path to a plugin directory (by default " CR_PLUGIN_DEFAULT ")\n"
513 : : " --force-irmap force resolving names for inotify/fsnotify watches\n"
514 : : "\n"
515 : : "* Logging:\n"
516 : : " -o|--log-file FILE log file name\n"
517 : : " --log-pid enable per-process logging to separate FILE.pid files\n"
518 : : " -v[NUM] set logging level (higher level means more output):\n"
519 : : " -v1|-v - only errors and messages\n"
520 : : " -v2|-vv - also warnings (default level)\n"
521 : : " -v3|-vvv - also information messages and timestamps\n"
522 : : " -v4|-vvvv - lots of debug\n"
523 : : "\n"
524 : : "* Memory dumping options:\n"
525 : : " --track-mem turn on memory changes tracker in kernel\n"
526 : : " --prev-images-dir DIR path to images from previous dump (relative to -D)\n"
527 : : " --page-server send pages to page server (see options below as well)\n"
528 : : " --auto-dedup when used on dump it will deduplicate \"old\" data in\n"
529 : : " pages images of previous dump\n"
530 : : " when used on restore, as soon as page is restored, it\n"
531 : : " will be punched from the image.\n"
532 : : "\n"
533 : : "Page/Service server options:\n"
534 : : " --address ADDR address of server or service\n"
535 : : " --port PORT port of page server\n"
536 : : " -d|--daemon run in the background after creating socket\n"
537 : : "\n"
538 : : "Show options:\n"
539 : : " -f|--file FILE show contents of a checkpoint file\n"
540 : : " -F|--fields FIELDS show specified fields (comma separated)\n"
541 : : " -D|--images-dir DIR directory where to get images from\n"
542 : : " -c|--contents show contents of pages dumped in hexdump format\n"
543 : : " -p|--pid PID show files relevant to PID (filter -D flood)\n"
544 : : "\n"
545 : : "Other options:\n"
546 : : " -h|--help show this text\n"
547 : : " -V|--version show version\n"
548 : : " --ms don't check not yet merged kernel features\n"
549 : : );
550 : :
551 : 0 : return 0;
552 : :
553 : : opt_pid_missing:
554 : 0 : pr_msg("Error: pid not specified\n");
555 : 0 : return 1;
556 : :
557 : : bad_arg:
558 [ # # ]: 0 : if (idx < 0) /* short option */
559 : 0 : pr_msg("Error: invalid argument for -%c: %s\n",
560 : : opt, optarg);
561 : : else /* long option */
562 : 0 : pr_msg("Error: invalid argument for --%s: %s\n",
563 : : long_opts[idx].name, optarg);
564 : : return 1;
565 : : }
|