[01/10] config: move getopt out of main()

Submitted by Adrian Reber on Aug. 1, 2018, 4:31 p.m.

Details

Message ID 20180801163122.19872-2-adrian@lisas.de
State Accepted
Series "Series without cover letter"
Headers show

Commit Message

Adrian Reber Aug. 1, 2018, 4:31 p.m.
From: Adrian Reber <areber@redhat.com>

This moves the whole getopt setup and analysis out of main() into its
own function in config.c.

The main motivation for this change is to be able to call getopt parsing
and configuration file parsing also from the RPC code path.

In one of the discussion emails about configuration files in CLI and RPC
mode the following architecture was proposed:

apply_config(global_conf)
apply_config(user_conf)
apply_config(pre_conf)
parse_command_line()
apply_rpc_options()
apply_config(post_conf)

And especially for the last step the getopt parsing needs to be out of
main() and callable from the RPC code path.

Signed-off-by: Adrian Reber <areber@redhat.com>
---
 criu/config.c             | 577 +++++++++++++++++++++++++++++++++++++-
 criu/crtools.c            | 560 ++----------------------------------
 criu/include/cr_options.h |   7 +-
 criu/include/crtools.h    |   4 -
 4 files changed, 595 insertions(+), 553 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/config.c b/criu/config.c
index a39696dc3..2fd592139 100644
--- a/criu/config.c
+++ b/criu/config.c
@@ -1,16 +1,36 @@ 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
 #include <ctype.h>
+#include <getopt.h>
 #include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
 
 #include "log.h"
+#include "common/list.h"
+
+#include "action-scripts.h"
+#include "cgroup.h"
+#include "cgroup-props.h"
+#include "common/bug.h"
+#include "cpu.h"
 #include "crtools.h"
+#include "cr_options.h"
+#include "filesystems.h"
+#include "file-lock.h"
+#include "irmap.h"
+#include "mount.h"
+#include "namespaces.h"
+#include "net.h"
+#include "sk-inet.h"
+#include "sockets.h"
+#include "tty.h"
+#include "version.h"
 
 #include "common/xmalloc.h"
 
-extern char **global_conf;
-extern char **user_conf;
+char **global_conf = NULL;
+char **user_conf = NULL;
 
 static int count_elements(char **to_count)
 {
@@ -154,7 +174,7 @@  static void init_configuration(int argc, char *argv[], bool no_default_config,
 	}
 }
 
-int init_config(int argc, char **argv, int *global_cfg_argc, int *user_cfg_argc,
+static int init_config(int argc, char **argv, int *global_cfg_argc, int *user_cfg_argc,
 		bool *usage_error)
 {
 	bool no_default_config = false;
@@ -198,3 +218,548 @@  int init_config(int argc, char **argv, int *global_cfg_argc, int *user_cfg_argc,
 
 	return 0;
 }
+
+static int parse_cpu_cap(struct cr_options *opts, const char *optarg)
+{
+	bool inverse = false;
+
+#define ____cpu_set_cap(__opts, __cap, __inverse)	\
+	do {						\
+		if ((__inverse))			\
+			(__opts)->cpu_cap &= ~(__cap);	\
+		else					\
+			(__opts)->cpu_cap |=  (__cap);	\
+	} while (0)
+
+	if (!optarg) {
+		____cpu_set_cap(opts, CPU_CAP_ALL, false);
+		return 0;
+	}
+
+	while (*optarg) {
+		if (optarg[0] == '^') {
+			inverse = !inverse;
+			optarg++;
+			continue;
+		} else if (optarg[0] == ',') {
+			inverse = false;
+			optarg++;
+			continue;
+		}
+
+		if (!strncmp(optarg, "fpu", 3)) {
+			____cpu_set_cap(opts, CPU_CAP_FPU, inverse);
+			optarg += 3;
+		} else if (!strncmp(optarg, "all", 3)) {
+			____cpu_set_cap(opts, CPU_CAP_ALL, inverse);
+			optarg += 3;
+		} else if (!strncmp(optarg, "none", 4)) {
+			if (inverse)
+				opts->cpu_cap = CPU_CAP_ALL;
+			else
+				opts->cpu_cap = CPU_CAP_NONE;
+			optarg += 4;
+		} else if (!strncmp(optarg, "cpu", 3)) {
+			____cpu_set_cap(opts, CPU_CAP_CPU, inverse);
+			optarg += 3;
+		} else if (!strncmp(optarg, "ins", 3)) {
+			____cpu_set_cap(opts, CPU_CAP_INS, inverse);
+			optarg += 3;
+		} else
+			goto Esyntax;
+	}
+#undef ____cpu_set_cap
+
+	return 0;
+
+Esyntax:
+	pr_err("Unknown FPU mode `%s' selected\n", optarg);
+	return -1;
+}
+
+static int parse_manage_cgroups(struct cr_options *opts, const char *optarg)
+{
+	if (!optarg) {
+		opts->manage_cgroups = CG_MODE_SOFT;
+		return 0;
+	}
+
+	if (!strcmp(optarg, "none")) {
+		opts->manage_cgroups = CG_MODE_NONE;
+	} else if (!strcmp(optarg, "props")) {
+		opts->manage_cgroups = CG_MODE_PROPS;
+	} else if (!strcmp(optarg, "soft")) {
+		opts->manage_cgroups = CG_MODE_SOFT;
+	} else if (!strcmp(optarg, "full")) {
+		opts->manage_cgroups = CG_MODE_FULL;
+	} else if (!strcmp(optarg, "strict")) {
+		opts->manage_cgroups = CG_MODE_STRICT;
+	} else
+		goto Esyntax;
+
+	return 0;
+
+Esyntax:
+	pr_err("Unknown cgroups mode `%s' selected\n", optarg);
+	return -1;
+}
+
+static size_t parse_size(char *optarg)
+{
+	if (index(optarg, 'K'))
+		return (size_t)KILO(atol(optarg));
+	else if (index(optarg, 'M'))
+		return (size_t)MEGA(atol(optarg));
+	else if (index(optarg, 'G'))
+		return (size_t)GIGA(atol(optarg));
+	return (size_t)atol(optarg);
+}
+
+static int parse_join_ns(const char *ptr)
+{
+	char *aux, *ns_file, *extra_opts = NULL;
+
+	aux = strchr(ptr, ':');
+	if (aux == NULL)
+		return -1;
+	*aux = '\0';
+
+	ns_file = aux + 1;
+	aux = strchr(ns_file, ',');
+	if (aux != NULL) {
+		*aux = '\0';
+		extra_opts = aux + 1;
+	} else {
+		extra_opts = NULL;
+	}
+	if (join_ns_add(ptr, ns_file, extra_opts))
+		return -1;
+
+	return 0;
+}
+
+#define PARSING_GLOBAL_CONF	1
+#define PARSING_USER_CONF	2
+#define PARSING_ARGV		3
+
+/*
+ * parse_options() is the point where the getopt parsing happens. The CLI
+ * parsing as well as the configuration file parsing happens here.
+ * This used to be all part of main() and to integrate the new code flow
+ * in main() this function (parse_options()) returns '0' if everything is
+ * correct, '1' if something failed and '2' if the CRIU help text should
+ * be displayed.
+ */
+int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd)
+{
+	int state = PARSING_GLOBAL_CONF;
+	int global_cfg_argc = 0, user_cfg_argc = 0;
+	/* remove pid, unused */
+	pid_t pid = 0;
+	int ret;
+	int opt;
+	int idx;
+
+
+#define BOOL_OPT(OPT_NAME, SAVE_TO) \
+		{OPT_NAME, no_argument, SAVE_TO, true},\
+		{"no-" OPT_NAME, no_argument, SAVE_TO, false}
+
+	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:v::x::Vr:jJ:lW:L:M:";
+	static struct option long_opts[] = {
+		{ "tree",			required_argument,	0, 't'	},
+		{ "pid",			required_argument,	0, 'p'	},
+		{ "leave-stopped",		no_argument,		0, 's'	},
+		{ "leave-running",		no_argument,		0, 'R'	},
+		BOOL_OPT("restore-detached", &opts.restore_detach),
+		BOOL_OPT("restore-sibling", &opts.restore_sibling),
+		BOOL_OPT("daemon", &opts.restore_detach),
+		{ "contents",			no_argument,		0, 'c'	},
+		{ "file",			required_argument,	0, 'f'	},
+		{ "fields",			required_argument,	0, 'F'	},
+		{ "images-dir",			required_argument,	0, 'D'	},
+		{ "work-dir",			required_argument,	0, 'W'	},
+		{ "log-file",			required_argument,	0, 'o'	},
+		{ "join-ns",			required_argument,	0, 'J'	},
+		{ "root",			required_argument,	0, 'r'	},
+		{ USK_EXT_PARAM,		optional_argument,	0, 'x'	},
+		{ "help",			no_argument,		0, 'h'	},
+		BOOL_OPT(SK_EST_PARAM, &opts.tcp_established_ok),
+		{ "close",			required_argument,	0, 1043	},
+		BOOL_OPT("log-pid", &opts.log_file_per_pid),
+		{ "version",			no_argument,		0, 'V'	},
+		BOOL_OPT("evasive-devices", &opts.evasive_devices),
+		{ "pidfile",			required_argument,	0, 1046	},
+		{ "veth-pair",			required_argument,	0, 1047	},
+		{ "action-script",		required_argument,	0, 1049	},
+		BOOL_OPT(LREMAP_PARAM, &opts.link_remap_ok),
+		BOOL_OPT(OPT_SHELL_JOB, &opts.shell_job),
+		BOOL_OPT(OPT_FILE_LOCKS, &opts.handle_file_locks),
+		BOOL_OPT("page-server", &opts.use_page_server),
+		{ "address",			required_argument,	0, 1051	},
+		{ "port",			required_argument,	0, 1052	},
+		{ "prev-images-dir",		required_argument,	0, 1053	},
+		{ "ms",				no_argument,		0, 1054	},
+		BOOL_OPT("track-mem", &opts.track_mem),
+		BOOL_OPT("auto-dedup", &opts.auto_dedup),
+		{ "libdir",			required_argument,	0, 'L'	},
+		{ "cpu-cap",			optional_argument,	0, 1057	},
+		BOOL_OPT("force-irmap", &opts.force_irmap),
+		{ "ext-mount-map",		required_argument,	0, 'M'	},
+		{ "exec-cmd",			no_argument,		0, 1059	},
+		{ "manage-cgroups",		optional_argument,	0, 1060	},
+		{ "cgroup-root",		required_argument,	0, 1061	},
+		{ "inherit-fd",			required_argument,	0, 1062	},
+		{ "feature",			required_argument,	0, 1063	},
+		{ "skip-mnt",			required_argument,	0, 1064 },
+		{ "enable-fs",			required_argument,	0, 1065 },
+		{ "enable-external-sharing",	no_argument,		&opts.enable_external_sharing, true	},
+		{ "enable-external-masters",	no_argument,		&opts.enable_external_masters, true	},
+		{ "freeze-cgroup",		required_argument,	0, 1068 },
+		{ "ghost-limit",		required_argument,	0, 1069 },
+		{ "irmap-scan-path",		required_argument,	0, 1070 },
+		{ "lsm-profile",		required_argument,	0, 1071 },
+		{ "timeout",			required_argument,	0, 1072 },
+		{ "external",			required_argument,	0, 1073	},
+		{ "empty-ns",			required_argument,	0, 1074	},
+		{ "lazy-pages",			no_argument,		0, 1076 },
+		BOOL_OPT("extra", &opts.check_extra_features),
+		BOOL_OPT("experimental", &opts.check_experimental_features),
+		{ "all",			no_argument,		0, 1079	},
+		{ "cgroup-props",		required_argument,	0, 1080	},
+		{ "cgroup-props-file",		required_argument,	0, 1081	},
+		{ "cgroup-dump-controller",	required_argument,	0, 1082	},
+		BOOL_OPT(SK_INFLIGHT_PARAM, &opts.tcp_skip_in_flight),
+		BOOL_OPT("deprecated", &opts.deprecated_ok),
+		BOOL_OPT("display-stats", &opts.display_stats),
+		BOOL_OPT("weak-sysctls", &opts.weak_sysctls),
+		{ "status-fd",			required_argument,	0, 1088 },
+		BOOL_OPT(SK_CLOSE_PARAM, &opts.tcp_close),
+		{ "verbosity",			optional_argument,	0, 'v'	},
+		{ "ps-socket",			required_argument,	0, 1091},
+		BOOL_OPT("remote", &opts.remote),
+		{ "config",			required_argument,	0, 1089},
+		{ "no-default-config",		no_argument,		0, 1090},
+		{ },
+	};
+
+#undef BOOL_OPT
+
+
+
+	ret = init_config(argc, argv, &global_cfg_argc, &user_cfg_argc,
+			  usage_error);
+	if (ret)
+		return 2;
+
+	while (1) {
+		char **_argv = NULL;
+		int _argc = 0;
+
+		idx = -1;
+
+		switch (state) {
+		case PARSING_GLOBAL_CONF:
+			_argc = global_cfg_argc;
+			_argv = global_conf;
+			break;
+		case PARSING_USER_CONF:
+			_argc = user_cfg_argc;
+			_argv = user_conf;
+			break;
+		case PARSING_ARGV:
+			_argc = argc;
+			_argv = argv;
+			break;
+		default:
+			BUG();
+		}
+		opt = getopt_long(_argc, _argv, short_opts, long_opts, &idx);
+		if (opt == -1) {
+			switch (state) {
+			case PARSING_GLOBAL_CONF:
+			case PARSING_USER_CONF:
+				if (optind < _argc) {
+					pr_err("Unknown config parameter: %s\n", _argv[optind]);
+					return -1;
+				}
+				break;
+			}
+			if (state < PARSING_ARGV) {
+				state++;
+				optind = 0;
+				continue;
+			} else {
+				break;
+			}
+		}
+		if (!opt)
+			continue;
+
+		switch (opt) {
+		case 's':
+			opts.final_state = TASK_STOPPED;
+			break;
+		case 'R':
+			opts.final_state = TASK_ALIVE;
+			break;
+		case 'x':
+			if (optarg && unix_sk_ids_parse(optarg) < 0)
+				return 1;
+			opts.ext_unix_sk = true;
+			break;
+		case 'p':
+			pid = atoi(optarg);
+			if (pid <= 0)
+				goto bad_arg;
+			break;
+		case 't':
+			opts.tree_id = atoi(optarg);
+			if (opts.tree_id <= 0)
+				goto bad_arg;
+			break;
+		case 'c':
+			opts.show_pages_content	= true;
+			break;
+		case 'f':
+			opts.show_dump_file = optarg;
+			break;
+		case 'F':
+			opts.show_fmt = optarg;
+			break;
+		case 'r':
+			opts.root = optarg;
+			break;
+		case 'd':
+			opts.restore_detach = true;
+			break;
+		case 'S':
+			opts.restore_sibling = true;
+			break;
+		case 'D':
+			opts.imgs_dir = optarg;
+			break;
+		case 'W':
+			opts.work_dir = optarg;
+			break;
+		case 'o':
+			opts.output = optarg;
+			break;
+		case 'J':
+			if (parse_join_ns(optarg))
+				goto bad_arg;
+			break;
+		case 'v':
+			if (optarg) {
+				if (optarg[0] == 'v')
+					/* handle -vvvvv */
+					opts.log_level += strlen(optarg) + 1;
+				else
+					opts.log_level = atoi(optarg);
+			} else
+				opts.log_level++;
+			break;
+		case 1043: {
+			int fd;
+
+			fd = atoi(optarg);
+			pr_info("Closing fd %d\n", fd);
+			close(fd);
+			break;
+		}
+		case 1046:
+			opts.pidfile = optarg;
+			break;
+		case 1047:
+			{
+				char *aux;
+
+				aux = strchr(optarg, '=');
+				if (aux == NULL)
+					goto bad_arg;
+
+				*aux = '\0';
+				if (veth_pair_add(optarg, aux + 1))
+					return 1;
+			}
+			break;
+		case 1049:
+			if (add_script(optarg))
+				return 1;
+			break;
+		case 1051:
+			opts.addr = optarg;
+			break;
+		case 1052:
+			opts.port = atoi(optarg);
+			if (!opts.port)
+				goto bad_arg;
+			break;
+		case 'j':
+			opts.shell_job = true;
+			break;
+		case 'l':
+			opts.handle_file_locks = true;
+			break;
+		case 1053:
+			opts.img_parent = optarg;
+			break;
+		case 1057:
+			if (parse_cpu_cap(&opts, optarg))
+				return 2;
+			break;
+		case 1058:
+			opts.force_irmap = true;
+			break;
+		case 1054:
+			pr_err("--ms is deprecated; see \"Check options\" of criu --help\n");
+			return 1;
+		case 'L':
+			opts.libdir = optarg;
+			break;
+		case 1059:
+			*has_exec_cmd = true;
+			break;
+		case 1060:
+			if (parse_manage_cgroups(&opts, optarg))
+				return 2;
+			break;
+		case 1061:
+			{
+				char *path, *ctl;
+
+				path = strchr(optarg, ':');
+				if (path) {
+					*path = '\0';
+					path++;
+					ctl = optarg;
+				} else {
+					path = optarg;
+					ctl = NULL;
+				}
+
+				if (new_cg_root_add(ctl, path))
+					return -1;
+			}
+			break;
+		case 1062:
+			if (inherit_fd_parse(optarg) < 0)
+				return 1;
+			break;
+		case 1063:
+			ret = check_add_feature(optarg);
+			if (ret < 0)	/* invalid kernel feature name */
+				return 1;
+			if (ret > 0)	/* list kernel features and exit */
+				return 0;
+			break;
+		case 1064:
+			if (!add_skip_mount(optarg))
+				return 1;
+			break;
+		case 1065:
+			if (!add_fsname_auto(optarg))
+				return 1;
+			break;
+		case 1068:
+			opts.freeze_cgroup = optarg;
+			break;
+		case 1069:
+			opts.ghost_limit = parse_size(optarg);
+			break;
+		case 1070:
+			if (irmap_scan_path_add(optarg))
+				return -1;
+			break;
+		case 1071:
+			opts.lsm_profile = optarg;
+			opts.lsm_supplied = true;
+			break;
+		case 1072:
+			opts.timeout = atoi(optarg);
+			break;
+		case 1076:
+			opts.lazy_pages = true;
+			break;
+		case 'M':
+			{
+				char *aux;
+
+				if (strcmp(optarg, "auto") == 0) {
+					opts.autodetect_ext_mounts = true;
+					break;
+				}
+
+				aux = strchr(optarg, ':');
+				if (aux == NULL)
+					goto bad_arg;
+
+				*aux = '\0';
+				if (ext_mount_add(optarg, aux + 1))
+					return 1;
+			}
+			break;
+		case 1073:
+			if (add_external(optarg))
+				return 1;
+			break;
+		case 1074:
+			if (!strcmp("net", optarg))
+				opts.empty_ns |= CLONE_NEWNET;
+			else {
+				pr_err("Unsupported empty namespace: %s\n",
+						optarg);
+				return 1;
+			}
+			break;
+		case 1079:
+			opts.check_extra_features = true;
+			opts.check_experimental_features = true;
+			break;
+		case 1080:
+			opts.cgroup_props = optarg;
+			break;
+		case 1081:
+			opts.cgroup_props_file = optarg;
+			break;
+		case 1082:
+			if (!cgp_add_dump_controller(optarg))
+				return 1;
+			break;
+		case 1088:
+			if (sscanf(optarg, "%d", &opts.status_fd) != 1) {
+				pr_err("Unable to parse a value of --status-fd\n");
+				return 1;
+			}
+			break;
+		case 1089:
+			break;
+		case 1090:
+			break;
+		case 1091:
+			opts.ps_socket = atoi(optarg);
+			break;
+		case 'V':
+			pr_msg("Version: %s\n", CRIU_VERSION);
+			if (strcmp(CRIU_GITID, "0"))
+				pr_msg("GitID: %s\n", CRIU_GITID);
+			exit(0);
+		case 'h':
+			*usage_error = false;
+			return 2;
+		default:
+			return 2;
+		}
+	}
+
+	return 0;
+
+bad_arg:
+	if (idx < 0) /* short option */
+		pr_msg("Error: invalid argument for -%c: %s\n",
+				opt, optarg);
+	else /* long option */
+		pr_msg("Error: invalid argument for --%s: %s\n",
+				long_opts[idx].name, optarg);
+	return 1;
+}
diff --git a/criu/crtools.c b/criu/crtools.c
index 89fb948e8..d300da61e 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -33,7 +33,6 @@ 
 #include "files.h"
 #include "sk-inet.h"
 #include "net.h"
-#include "version.h"
 #include "page-xfer.h"
 #include "tty.h"
 #include "file-lock.h"
@@ -41,14 +40,10 @@ 
 #include "plugin.h"
 #include "criu-log.h"
 #include "util.h"
-#include "mount.h"
-#include "filesystems.h"
+#include "protobuf-desc.h"
 #include "namespaces.h"
 #include "cgroup.h"
-#include "cgroup-props.h"
 #include "cpu.h"
-#include "action-scripts.h"
-#include "irmap.h"
 #include "fault-injection.h"
 #include "lsm.h"
 #include "proc_parse.h"
@@ -59,8 +54,6 @@ 
 #include "img-remote.h"
 
 struct cr_options opts;
-char **global_conf = NULL;
-char **user_conf = NULL;
 
 void init_opts(void)
 {
@@ -82,125 +75,7 @@  void init_opts(void)
 	opts.timeout = DEFAULT_TIMEOUT;
 	opts.empty_ns = 0;
 	opts.status_fd = -1;
-}
-
-static int parse_join_ns(const char *ptr)
-{
-	char *aux, *ns_file, *extra_opts = NULL;
-
-	aux = strchr(ptr, ':');
-	if (aux == NULL)
-		return -1;
-	*aux = '\0';
-
-	ns_file = aux + 1;
-	aux = strchr(ns_file, ',');
-	if (aux != NULL) {
-		*aux = '\0';
-		extra_opts = aux + 1;
-	} else {
-		extra_opts = NULL;
-	}
-	if (join_ns_add(ptr, ns_file, extra_opts))
-		return -1;
-
-	return 0;
-}
-
-static int parse_cpu_cap(struct cr_options *opts, const char *optarg)
-{
-	bool inverse = false;
-
-#define ____cpu_set_cap(__opts, __cap, __inverse)	\
-	do {						\
-		if ((__inverse))			\
-			(__opts)->cpu_cap &= ~(__cap);	\
-		else					\
-			(__opts)->cpu_cap |=  (__cap);	\
-	} while (0)
-
-	if (!optarg) {
-		____cpu_set_cap(opts, CPU_CAP_ALL, false);
-		return 0;
-	}
-
-	while (*optarg) {
-		if (optarg[0] == '^') {
-			inverse = !inverse;
-			optarg++;
-			continue;
-		} else if (optarg[0] == ',') {
-			inverse = false;
-			optarg++;
-			continue;
-		}
-
-		if (!strncmp(optarg, "fpu", 3)) {
-			____cpu_set_cap(opts, CPU_CAP_FPU, inverse);
-			optarg += 3;
-		} else if (!strncmp(optarg, "all", 3)) {
-			____cpu_set_cap(opts, CPU_CAP_ALL, inverse);
-			optarg += 3;
-		} else if (!strncmp(optarg, "none", 4)) {
-			if (inverse)
-				opts->cpu_cap = CPU_CAP_ALL;
-			else
-				opts->cpu_cap = CPU_CAP_NONE;
-			optarg += 4;
-		} else if (!strncmp(optarg, "cpu", 3)) {
-			____cpu_set_cap(opts, CPU_CAP_CPU, inverse);
-			optarg += 3;
-		} else if (!strncmp(optarg, "ins", 3)) {
-			____cpu_set_cap(opts, CPU_CAP_INS, inverse);
-			optarg += 3;
-		} else
-			goto Esyntax;
-	}
-#undef ____cpu_set_cap
-
-	return 0;
-
-Esyntax:
-	pr_err("Unknown FPU mode `%s' selected\n", optarg);
-	return -1;
-}
-
-static int parse_manage_cgroups(struct cr_options *opts, const char *optarg)
-{
-	if (!optarg) {
-		opts->manage_cgroups = CG_MODE_SOFT;
-		return 0;
-	}
-
-	if (!strcmp(optarg, "none")) {
-		opts->manage_cgroups = CG_MODE_NONE;
-	} else if (!strcmp(optarg, "props")) {
-		opts->manage_cgroups = CG_MODE_PROPS;
-	} else if (!strcmp(optarg, "soft")) {
-		opts->manage_cgroups = CG_MODE_SOFT;
-	} else if (!strcmp(optarg, "full")) {
-		opts->manage_cgroups = CG_MODE_FULL;
-	} else if (!strcmp(optarg, "strict")) {
-		opts->manage_cgroups = CG_MODE_STRICT;
-	} else
-		goto Esyntax;
-
-	return 0;
-
-Esyntax:
-	pr_err("Unknown cgroups mode `%s' selected\n", optarg);
-	return -1;
-}
-
-static size_t parse_size(char *optarg)
-{
-	if (index(optarg, 'K'))
-		return (size_t)KILO(atol(optarg));
-	else if (index(optarg, 'M'))
-		return (size_t)MEGA(atol(optarg));
-	else if (index(optarg, 'G'))
-		return (size_t)GIGA(atol(optarg));
-	return (size_t)atol(optarg);
+	opts.log_level = DEFAULT_LOGLEVEL;
 }
 
 bool deprecated_ok(char *what)
@@ -259,104 +134,10 @@  static int early_init(void)
 
 int main(int argc, char *argv[], char *envp[])
 {
-#define PARSING_GLOBAL_CONF	1
-#define PARSING_USER_CONF	2
-#define PARSING_ARGV		3
-
-	pid_t pid = 0, tree_id = 0;
 	int ret = -1;
 	bool usage_error = true;
 	bool has_exec_cmd = false;
 	bool has_sub_command;
-	int opt = 0, idx;
-	int global_cfg_argc = 0, user_cfg_argc = 0;
-	int state = PARSING_GLOBAL_CONF;
-	int log_level = DEFAULT_LOGLEVEL;
-	char *imgs_dir = ".";
-
-#define BOOL_OPT(OPT_NAME, SAVE_TO) \
-		{OPT_NAME, no_argument, SAVE_TO, true},\
-		{"no-" OPT_NAME, no_argument, SAVE_TO, false}
-
-	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:v::x::Vr:jJ:lW:L:M:";
-	static struct option long_opts[] = {
-		{ "tree",			required_argument,	0, 't'	},
-		{ "pid",			required_argument,	0, 'p'	},
-		{ "leave-stopped",		no_argument,		0, 's'	},
-		{ "leave-running",		no_argument,		0, 'R'	},
-		BOOL_OPT("restore-detached", &opts.restore_detach),
-		BOOL_OPT("restore-sibling", &opts.restore_sibling),
-		BOOL_OPT("daemon", &opts.restore_detach),
-		{ "contents",			no_argument,		0, 'c'	},
-		{ "file",			required_argument,	0, 'f'	},
-		{ "fields",			required_argument,	0, 'F'	},
-		{ "images-dir",			required_argument,	0, 'D'	},
-		{ "work-dir",			required_argument,	0, 'W'	},
-		{ "log-file",			required_argument,	0, 'o'	},
-		{ "join-ns",			required_argument,	0, 'J'	},
-		{ "root",			required_argument,	0, 'r'	},
-		{ USK_EXT_PARAM,		optional_argument,	0, 'x'	},
-		{ "help",			no_argument,		0, 'h'	},
-		BOOL_OPT(SK_EST_PARAM, &opts.tcp_established_ok),
-		{ "close",			required_argument,	0, 1043	},
-		BOOL_OPT("log-pid", &opts.log_file_per_pid),
-		{ "version",			no_argument,		0, 'V'	},
-		BOOL_OPT("evasive-devices", &opts.evasive_devices),
-		{ "pidfile",			required_argument,	0, 1046	},
-		{ "veth-pair",			required_argument,	0, 1047	},
-		{ "action-script",		required_argument,	0, 1049	},
-		BOOL_OPT(LREMAP_PARAM, &opts.link_remap_ok),
-		BOOL_OPT(OPT_SHELL_JOB, &opts.shell_job),
-		BOOL_OPT(OPT_FILE_LOCKS, &opts.handle_file_locks),
-		BOOL_OPT("page-server", &opts.use_page_server),
-		{ "address",			required_argument,	0, 1051	},
-		{ "port",			required_argument,	0, 1052	},
-		{ "prev-images-dir",		required_argument,	0, 1053	},
-		{ "ms",				no_argument,		0, 1054	},
-		BOOL_OPT("track-mem", &opts.track_mem),
-		BOOL_OPT("auto-dedup", &opts.auto_dedup),
-		{ "libdir",			required_argument,	0, 'L'	},
-		{ "cpu-cap",			optional_argument,	0, 1057	},
-		BOOL_OPT("force-irmap", &opts.force_irmap),
-		{ "ext-mount-map",		required_argument,	0, 'M'	},
-		{ "exec-cmd",			no_argument,		0, 1059	},
-		{ "manage-cgroups",		optional_argument,	0, 1060	},
-		{ "cgroup-root",		required_argument,	0, 1061	},
-		{ "inherit-fd",			required_argument,	0, 1062	},
-		{ "feature",			required_argument,	0, 1063	},
-		{ "skip-mnt",			required_argument,	0, 1064 },
-		{ "enable-fs",			required_argument,	0, 1065 },
-		{ "enable-external-sharing", 	no_argument, 		&opts.enable_external_sharing, true	},
-		{ "enable-external-masters", 	no_argument, 		&opts.enable_external_masters, true	},
-		{ "freeze-cgroup",		required_argument,	0, 1068 },
-		{ "ghost-limit",		required_argument,	0, 1069 },
-		{ "irmap-scan-path",		required_argument,	0, 1070 },
-		{ "lsm-profile",		required_argument,	0, 1071 },
-		{ "timeout",			required_argument,	0, 1072 },
-		{ "external",			required_argument,	0, 1073	},
-		{ "empty-ns",			required_argument,	0, 1074	},
-		{ "lazy-pages",			no_argument,		0, 1076 },
-		BOOL_OPT("extra", &opts.check_extra_features),
-		BOOL_OPT("experimental", &opts.check_experimental_features),
-		{ "all",			no_argument,		0, 1079	},
-		{ "cgroup-props",		required_argument,	0, 1080	},
-		{ "cgroup-props-file",		required_argument,	0, 1081	},
-		{ "cgroup-dump-controller",	required_argument,	0, 1082	},
-		BOOL_OPT(SK_INFLIGHT_PARAM, &opts.tcp_skip_in_flight),
-		BOOL_OPT("deprecated", &opts.deprecated_ok),
-		BOOL_OPT("display-stats", &opts.display_stats),
-		BOOL_OPT("weak-sysctls", &opts.weak_sysctls),
-		{ "status-fd",			required_argument,	0, 1088 },
-		BOOL_OPT(SK_CLOSE_PARAM, &opts.tcp_close),
-		{ "verbosity",			optional_argument,	0, 'v'	},
-		{ "ps-socket",			required_argument,	0, 1091},
-		BOOL_OPT("remote", &opts.remote),
-		{ "config",			required_argument,	0, 1089},
-		{ "no-default-config",		no_argument,		0, 1090},
-		{ },
-	};
-
-#undef BOOL_OPT
 
 	BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32);
 	BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR);
@@ -392,312 +173,14 @@  int main(int argc, char *argv[], char *envp[])
 		return cr_service_work(atoi(argv[2]));
 	}
 
-	ret = init_config(argc, argv, &global_cfg_argc, &user_cfg_argc,
-			  &usage_error);
-	if (ret)
-		goto usage;
+	ret = parse_options(argc, argv, &usage_error, &has_exec_cmd);
 
-	while (1) {
-		char **_argv = NULL;
-		int _argc = 0;
-
-		idx = -1;
-
-		switch (state) {
-		case PARSING_GLOBAL_CONF:
-			_argc = global_cfg_argc;
-			_argv = global_conf;
-			break;
-		case PARSING_USER_CONF:
-			_argc = user_cfg_argc;
-			_argv = user_conf;
-			break;
-		case PARSING_ARGV:
-			_argc = argc;
-			_argv = argv;
-			break;
-		default:
-			BUG();
-		}
-		opt = getopt_long(_argc, _argv, short_opts, long_opts, &idx);
-		if (opt == -1) {
-			switch (state) {
-			case PARSING_GLOBAL_CONF:
-			case PARSING_USER_CONF:
-				if (optind < _argc) {
-					pr_err("Unknown config parameter: %s\n", _argv[optind]);
-					return -1;
-				}
-				break;
-			}
-			if (state < PARSING_ARGV) {
-				state++;
-				optind = 0;
-				continue;
-			} else {
-				break;
-			}
-		}
-		if (!opt)
-			continue;
-
-		switch (opt) {
-		case 's':
-			opts.final_state = TASK_STOPPED;
-			break;
-		case 'R':
-			opts.final_state = TASK_ALIVE;
-			break;
-		case 'x':
-			if (optarg && unix_sk_ids_parse(optarg) < 0)
-				return 1;
-			opts.ext_unix_sk = true;
-			break;
-		case 'p':
-			pid = atoi(optarg);
-			if (pid <= 0)
-				goto bad_arg;
-			break;
-		case 't':
-			tree_id = atoi(optarg);
-			if (tree_id <= 0)
-				goto bad_arg;
-			break;
-		case 'c':
-			opts.show_pages_content	= true;
-			break;
-		case 'f':
-			opts.show_dump_file = optarg;
-			break;
-		case 'F':
-			opts.show_fmt = optarg;
-			break;
-		case 'r':
-			opts.root = optarg;
-			break;
-		case 'd':
-			opts.restore_detach = true;
-			break;
-		case 'S':
-			opts.restore_sibling = true;
-			break;
-		case 'D':
-			imgs_dir = optarg;
-			break;
-		case 'W':
-			opts.work_dir = optarg;
-			break;
-		case 'o':
-			opts.output = optarg;
-			break;
-		case 'J':
-			if (parse_join_ns(optarg))
-				goto bad_arg;
-			break;
-		case 'v':
-			if (optarg) {
-				if (optarg[0] == 'v')
-					/* handle -vvvvv */
-					log_level += strlen(optarg) + 1;
-				else
-					log_level = atoi(optarg);
-			} else
-				log_level++;
-			break;
-		case 1043: {
-			int fd;
-
-			fd = atoi(optarg);
-			pr_info("Closing fd %d\n", fd);
-			close(fd);
-			break;
-		}
-		case 1046:
-			opts.pidfile = optarg;
-			break;
-		case 1047:
-			{
-				char *aux;
-
-				aux = strchr(optarg, '=');
-				if (aux == NULL)
-					goto bad_arg;
-
-				*aux = '\0';
-				if (veth_pair_add(optarg, aux + 1))
-					return 1;
-			}
-			break;
-		case 1049:
-			if (add_script(optarg))
-				return 1;
-			break;
-		case 1051:
-			opts.addr = optarg;
-			break;
-		case 1052:
-			opts.port = atoi(optarg);
-			if (!opts.port)
-				goto bad_arg;
-			break;
-		case 'j':
-			opts.shell_job = true;
-			break;
-		case 'l':
-			opts.handle_file_locks = true;
-			break;
-		case 1053:
-			opts.img_parent = optarg;
-			break;
-		case 1057:
-			if (parse_cpu_cap(&opts, optarg))
-				goto usage;
-			break;
-		case 1058:
-			opts.force_irmap = true;
-			break;
-		case 1054:
-			pr_err("--ms is deprecated; see \"Check options\" of criu --help\n");
-			return 1;
-		case 'L':
-			opts.libdir = optarg;
-			break;
-		case 1059:
-			has_exec_cmd = true;
-			break;
-		case 1060:
-			if (parse_manage_cgroups(&opts, optarg))
-				goto usage;
-			break;
-		case 1061:
-			{
-				char *path, *ctl;
-
-				path = strchr(optarg, ':');
-				if (path) {
-					*path = '\0';
-					path++;
-					ctl = optarg;
-				} else {
-					path = optarg;
-					ctl = NULL;
-				}
-
-				if (new_cg_root_add(ctl, path))
-					return -1;
-			}
-			break;
-		case 1062:
-			if (inherit_fd_parse(optarg) < 0)
-				return 1;
-			break;
-		case 1063:
-			ret = check_add_feature(optarg);
-			if (ret < 0)	/* invalid kernel feature name */
-				return 1;
-			if (ret > 0)	/* list kernel features and exit */
-				return 0;
-			break;
-		case 1064:
-			if (!add_skip_mount(optarg))
-				return 1;
-			break;
-		case 1065:
-			if (!add_fsname_auto(optarg))
-				return 1;
-			break;
-		case 1068:
-			opts.freeze_cgroup = optarg;
-			break;
-		case 1069:
-			opts.ghost_limit = parse_size(optarg);
-			break;
-		case 1070:
-			if (irmap_scan_path_add(optarg))
-				return -1;
-			break;
-		case 1071:
-			opts.lsm_profile = optarg;
-			opts.lsm_supplied = true;
-			break;
-		case 1072:
-			opts.timeout = atoi(optarg);
-			break;
-		case 1076:
-			opts.lazy_pages = true;
-			break;
-		case 'M':
-			{
-				char *aux;
-
-				if (strcmp(optarg, "auto") == 0) {
-					opts.autodetect_ext_mounts = true;
-					break;
-				}
-
-				aux = strchr(optarg, ':');
-				if (aux == NULL)
-					goto bad_arg;
-
-				*aux = '\0';
-				if (ext_mount_add(optarg, aux + 1))
-					return 1;
-			}
-			break;
-		case 1073:
-			if (add_external(optarg))
-				return 1;
-			break;
-		case 1074:
-			if (!strcmp("net", optarg))
-				opts.empty_ns |= CLONE_NEWNET;
-			else {
-				pr_err("Unsupported empty namespace: %s\n",
-						optarg);
-				return 1;
-			}
-			break;
-		case 1079:
-			opts.check_extra_features = true;
-			opts.check_experimental_features = true;
-			break;
-		case 1080:
-			opts.cgroup_props = optarg;
-			break;
-		case 1081:
-			opts.cgroup_props_file = optarg;
-			break;
-		case 1082:
-			if (!cgp_add_dump_controller(optarg))
-				return 1;
-			break;
-		case 1088:
-			if (sscanf(optarg, "%d", &opts.status_fd) != 1) {
-				pr_err("Unable to parse a value of --status-fd\n");
-				return 1;
-			}
-			break;
-		case 1089:
-			break;
-		case 1090:
-			break;
-		case 1091:
-			opts.ps_socket = atoi(optarg);
-			break;
-		case 'V':
-			pr_msg("Version: %s\n", CRIU_VERSION);
-			if (strcmp(CRIU_GITID, "0"))
-				pr_msg("GitID: %s\n", CRIU_GITID);
-			return 0;
-		case 'h':
-			usage_error = false;
-			goto usage;
-		default:
-			goto usage;
-		}
-	}
+	if (ret == 1)
+		return 1;
+	if (ret == 2)
+		goto usage;
 
-	log_set_loglevel(log_level);
+	log_set_loglevel(opts.log_level);
 
 	if (early_init())
 		return -1;
@@ -728,8 +211,11 @@  int main(int argc, char *argv[], char *envp[])
 		return 1;
 	}
 
+	if (opts.imgs_dir == NULL)
+		opts.imgs_dir = ".";
+
 	if (opts.work_dir == NULL)
-		opts.work_dir = imgs_dir;
+		opts.work_dir = opts.imgs_dir;
 
 	if (optind >= argc) {
 		pr_msg("Error: command is required\n");
@@ -775,7 +261,7 @@  int main(int argc, char *argv[], char *envp[])
 
 	/* We must not open imgs dir, if service is called */
 	if (strcmp(argv[optind], "service")) {
-		ret = open_image_dir(imgs_dir);
+		ret = open_image_dir(opts.imgs_dir);
 		if (ret < 0)
 			return 1;
 	}
@@ -795,7 +281,6 @@  int main(int argc, char *argv[], char *envp[])
 		return 1;
 	}
 
-
 	if (log_init(opts.output))
 		return 1;
 
@@ -815,20 +300,20 @@  int main(int argc, char *argv[], char *envp[])
 		pr_info("Will do snapshot from %s\n", opts.img_parent);
 
 	if (!strcmp(argv[optind], "dump")) {
-		if (!tree_id)
+		if (!opts.tree_id)
 			goto opt_pid_missing;
-		return cr_dump_tasks(tree_id);
+		return cr_dump_tasks(opts.tree_id);
 	}
 
 	if (!strcmp(argv[optind], "pre-dump")) {
-		if (!tree_id)
+		if (!opts.tree_id)
 			goto opt_pid_missing;
 
-		return cr_pre_dump_tasks(tree_id) != 0;
+		return cr_pre_dump_tasks(opts.tree_id) != 0;
 	}
 
 	if (!strcmp(argv[optind], "restore")) {
-		if (tree_id)
+		if (opts.tree_id)
 			pr_warn("Using -t with criu restore is obsoleted\n");
 
 		ret = cr_restore_tasks();
@@ -1082,13 +567,4 @@  opt_port_missing:
 opt_pid_missing:
 	pr_msg("Error: pid not specified\n");
 	return 1;
-
-bad_arg:
-	if (idx < 0) /* short option */
-		pr_msg("Error: invalid argument for -%c: %s\n",
-				opt, optarg);
-	else /* long option */
-		pr_msg("Error: invalid argument for --%s: %s\n",
-				long_opts[idx].name, optarg);
-	return 1;
 }
diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h
index acbca6ef5..eda07e892 100644
--- a/criu/include/cr_options.h
+++ b/criu/include/cr_options.h
@@ -1,6 +1,7 @@ 
 #ifndef __CR_OPTIONS_H__
 #define __CR_OPTIONS_H__
 
+#include <sys/types.h>
 #include <stdbool.h>
 #include "common/config.h"
 #include "common/list.h"
@@ -122,10 +123,14 @@  struct cr_options {
 	int			status_fd;
 	bool			orphan_pts_master;
 	int			remote;
+	pid_t			tree_id;
+	int			log_level;
+	char			*imgs_dir;
 };
 
 extern struct cr_options opts;
 
-extern void init_opts(void);
+extern int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd);
+extern void init_opts();
 
 #endif /* __CR_OPTIONS_H__ */
diff --git a/criu/include/crtools.h b/criu/include/crtools.h
index 5ffacb697..7b2dc4523 100644
--- a/criu/include/crtools.h
+++ b/criu/include/crtools.h
@@ -32,10 +32,6 @@  extern int cr_lazy_pages(bool daemon);
 extern int check_add_feature(char *arg);
 extern void pr_check_features(const char *offset, const char *sep, int width);
 
-extern int init_config(int argc, char **argv,
-		       int *global_cfg_argc, int *user_cfg_argc,
-		       bool *usage_error);
-
 #define PPREP_HEAD_INACTIVE	((struct pprep_head *)-1)
 
 #define add_post_prepare_cb_once(phead) do {		 \