diff --git a/criu/config.c b/criu/config.c index 57684b45f..2e57a2af4 100644 --- a/criu/config.c +++ b/criu/config.c @@ -149,12 +149,6 @@ static char ** parse_config(char *filepath) return configuration; } -#define PARSING_GLOBAL_CONF 1 -#define PARSING_USER_CONF 2 -#define PARSING_ENV_CONF 3 -#define PARSING_CMDLINE_CONF 4 -#define PARSING_ARGV 5 - static int next_config(char **argv, char ***_argv, bool no_default_config, int state, char *cfg_file) { @@ -162,7 +156,7 @@ static int next_config(char **argv, char ***_argv, bool no_default_config, char *home_dir = NULL; char *cfg_from_env = NULL; - if (state > PARSING_ARGV) + if (state >= PARSING_LAST) return 0; switch(state) { @@ -197,6 +191,11 @@ static int next_config(char **argv, char ***_argv, bool no_default_config, case PARSING_ARGV: *_argv = argv; break; + case PARSING_RPC_CONF: + if (!rpc_cfg_file) + break; + *_argv = parse_config(rpc_cfg_file); + break; default: break; } @@ -403,9 +402,9 @@ static int parse_join_ns(const char *ptr) * 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 parse_options(int argc, char **argv, bool *usage_error, + bool *has_exec_cmd, int state) { - int state = PARSING_GLOBAL_CONF; int ret; int opt = -1; int idx; @@ -516,7 +515,6 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd) free(_argv[i]); } free(_argv); - ; } /* This needs to be reset for a new getopt() run */ _argc = 0; diff --git a/criu/cr-service.c b/criu/cr-service.c index c7c28857e..dc2ff05f7 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -242,7 +242,11 @@ static int setup_opts_from_req(int sk, CriuOpts *req) char images_dir_path[PATH_MAX]; char work_dir_path[PATH_MAX]; char status_fd[PATH_MAX]; + bool output_changed_by_rpc_conf = false; + bool work_changed_by_rpc_conf = false; + bool imgs_changed_by_rpc_conf = false; int i; + bool dummy = false; if (getsockopt(sk, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) { pr_perror("Can't get socket options"); @@ -257,8 +261,68 @@ static int setup_opts_from_req(int sk, CriuOpts *req) BUG_ON(st.st_ino == -1); service_sk_ino = st.st_ino; - /* open images_dir */ - sprintf(images_dir_path, "/proc/%d/fd/%d", ids.pid, req->images_dir_fd); + /* + * Evaluate an additional configuration file if specified. + * This needs to happen twice, because it is needed early to detect + * things like work_dir, imgs_dir and logfile. The second parsing + * of the optional RPC configuration file happens at the end and + * overwrites all options set via RPC. + */ + if (req->config_file) { + char *tmp_output = NULL; + char *tmp_work = NULL; + char *tmp_imgs = NULL; + + if (opts.output) + tmp_output = xstrdup(opts.output); + if (opts.work_dir) + tmp_work = xstrdup(opts.work_dir); + if (opts.imgs_dir) + tmp_imgs = xstrdup(opts.imgs_dir); + xfree(opts.output); + xfree(opts.work_dir); + xfree(opts.imgs_dir); + opts.output = NULL; + opts.work_dir = NULL; + opts.imgs_dir = NULL; + rpc_cfg_file = req->config_file; + i = parse_options(0, NULL, &dummy, &dummy, PARSING_RPC_CONF); + pr_warn("parse_options returns %d\n", i); + if (i) { + xfree(tmp_output); + xfree(tmp_work); + xfree(tmp_imgs); + goto err; + } + if (tmp_output && opts.output && !strncmp(tmp_output, opts.output, PATH_MAX)) + output_changed_by_rpc_conf = true; + if (tmp_work && opts.work_dir && !strncmp(tmp_work, opts.work_dir, PATH_MAX)) + work_changed_by_rpc_conf = true; + if (tmp_imgs && opts.imgs_dir && !strncmp(tmp_imgs, opts.imgs_dir, PATH_MAX)) + imgs_changed_by_rpc_conf = true; + xfree(tmp_output); + xfree(tmp_work); + xfree(tmp_imgs); + } + + /* + * open images_dir + * This assumes that if opts.imgs_dir is set we have a value + * from the configuration file parser. The test to see that + * imgs_changed_by_rpc_conf is true is used to make sure the value + * is not the same as from one of the other configuration files. + * The idea is that only the RPC configuration file is able to + * overwrite RPC settings: + * * apply_config(global_conf) + * * apply_config(user_conf) + * * apply_config(environment variable) + * * apply_rpc_options() + * * apply_config(rpc_conf) + */ + if (opts.imgs_dir && imgs_changed_by_rpc_conf) + strncpy(images_dir_path, opts.imgs_dir, PATH_MAX); + else + sprintf(images_dir_path, "/proc/%d/fd/%d", ids.pid, req->images_dir_fd); if (req->parent_img) SET_CHAR_OPTS(img_parent, req->parent_img); @@ -275,7 +339,9 @@ static int setup_opts_from_req(int sk, CriuOpts *req) } /* chdir to work dir */ - if (req->has_work_dir_fd) + if (opts.work_dir && work_changed_by_rpc_conf) + strncpy(work_dir_path, opts.work_dir, PATH_MAX); + else if (req->has_work_dir_fd) sprintf(work_dir_path, "/proc/%d/fd/%d", ids.pid, req->work_dir_fd); else strcpy(work_dir_path, images_dir_path); @@ -286,15 +352,16 @@ static int setup_opts_from_req(int sk, CriuOpts *req) } /* initiate log file in work dir */ - if (req->log_file) { + if (req->log_file && !(opts.output && output_changed_by_rpc_conf)) { if (strchr(req->log_file, '/')) { pr_perror("No subdirs are allowed in log_file name"); goto err; } SET_CHAR_OPTS(output, req->log_file); - } else + } else if (!opts.output) { SET_CHAR_OPTS(output, DEFAULT_LOG_FILENAME); + } log_set_loglevel(req->log_level); if (log_init(opts.output) == -1) { @@ -381,7 +448,10 @@ static int setup_opts_from_req(int sk, CriuOpts *req) if (!opts.lazy_pages) { opts.use_page_server = true; - SET_CHAR_OPTS(addr, req->ps->address); + if (req->ps->address) + SET_CHAR_OPTS(addr, req->ps->address); + else + opts.addr = NULL; if (req->ps->has_fd) { if (!opts.swrk_restore) @@ -539,6 +609,16 @@ static int setup_opts_from_req(int sk, CriuOpts *req) if (check_namespace_opts()) goto err; + /* Evaluate additional configuration file a second time to overwrite + * all RPC settings. */ + if (req->config_file) { + rpc_cfg_file = req->config_file; + i = parse_options(0, NULL, &dummy, &dummy, PARSING_RPC_CONF); + if (i) + goto err; + } + log_set_loglevel(opts.log_level); + return 0; err: diff --git a/criu/crtools.c b/criu/crtools.c index 3c6ca078b..5d555ae56 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -101,6 +101,7 @@ int main(int argc, char *argv[], char *envp[]) bool usage_error = true; bool has_exec_cmd = false; bool has_sub_command; + int state = PARSING_GLOBAL_CONF; BUILD_BUG_ON(CTL_32 != SYSCTL_TYPE__CTL_32); BUILD_BUG_ON(__CTL_STR != SYSCTL_TYPE__CTL_STR); @@ -120,7 +121,7 @@ int main(int argc, char *argv[], char *envp[]) init_opts(); - ret = parse_options(argc, argv, &usage_error, &has_exec_cmd); + ret = parse_options(argc, argv, &usage_error, &has_exec_cmd, state); if (ret == 1) return 1; diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index bcf7d1c98..68281e8d5 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -6,6 +6,15 @@ #include "common/config.h" #include "common/list.h" +/* Configuration and CLI parsing order defines */ +#define PARSING_GLOBAL_CONF 1 +#define PARSING_USER_CONF 2 +#define PARSING_ENV_CONF 3 +#define PARSING_CMDLINE_CONF 4 +#define PARSING_ARGV 5 +#define PARSING_RPC_CONF 6 +#define PARSING_LAST 7 + #define SET_CHAR_OPTS(__dest, __src) \ do { \ free(opts.__dest); \ @@ -132,8 +141,9 @@ struct cr_options { }; extern struct cr_options opts; +char *rpc_cfg_file; -extern int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd); +extern int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd, int state); extern void init_opts(); #endif /* __CR_OPTIONS_H__ */ diff --git a/images/rpc.proto b/images/rpc.proto index 71f47d594..abf2f5d79 100644 --- a/images/rpc.proto +++ b/images/rpc.proto @@ -111,6 +111,7 @@ message criu_opts { optional bool lazy_pages = 48; optional int32 status_fd = 49; optional bool orphan_pts_master = 50; + optional string config_file = 51; } message criu_dump_resp {