Commit 99d52ec7 authored by Adrian Reber's avatar Adrian Reber Committed by Andrei Vagin

cr-service: add support for configuration files in RPC mode

With this commit it is possible to specify a configuration file via
RPC. In python this would look like this:

 req.opts.config_file = 'path/to/config_file'

With this commit CRIU's configuration file handling works like this:

 * apply_config(global_conf)
 * apply_config(user_conf)
 * apply_config(environment variable)
 * apply_config(config file via CLI)
 * apply_rpc_options() or apply_cli_options()
 * apply_config(rpc_conf) (only for RPC)

This is at least (probably) the third iteration of the RPC configuration
file code and it still is complicated.

Most CRIU options are correctly used by just writing the new values to
the corresponding fields of the opts structure. For the RPC case there
are, however, a few options (output, work_dir, imgs_dir) which need
special handling.

So the RPC configuration file is parsed twice. First time to get output,
work_dir and imgs_dir. Once those are read and correctly used, the RPC
code overwrites all options again by values set by the RPC interface. At
the end the RPC configuration file is read a second time and finally
overwrites the values set via RPC.
Signed-off-by: 's avatarAdrian Reber <areber@redhat.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent dabe778c
......@@ -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;
......
......@@ -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:
......
......@@ -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;
......
......@@ -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__ */
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment