Beispiel #1
0
static int setup_opts_from_req(int sk, CriuOpts *req)
{
	struct ucred ids;
	struct stat st;
	socklen_t ids_len = sizeof(struct ucred);
	char images_dir_path[PATH_MAX];
	char work_dir_path[PATH_MAX];
	char status_fd[PATH_MAX];
	int i;

	if (getsockopt(sk, SOL_SOCKET, SO_PEERCRED, &ids, &ids_len)) {
		pr_perror("Can't get socket options");
		goto err;
	}

	if (fstat(sk, &st)) {
		pr_perror("Can't get socket stat");
		goto err;
	}

	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);

	if (req->parent_img)
		opts.img_parent = req->parent_img;

	if (open_image_dir(images_dir_path) < 0) {
		pr_perror("Can't open images directory");
		goto err;
	}

	/* get full path to images_dir to use in process title */
	if (readlink(images_dir_path, images_dir, PATH_MAX) == -1) {
		pr_perror("Can't readlink %s", images_dir_path);
		goto err;
	}

	/* chdir to work dir */
	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);

	if (chdir(work_dir_path)) {
		pr_perror("Can't chdir to work_dir");
		goto err;
	}

	/* initiate log file in work dir */
	if (req->log_file) {
		if (strchr(req->log_file, '/')) {
			pr_perror("No subdirs are allowed in log_file name");
			goto err;
		}

		opts.output = req->log_file;
	} else
		opts.output = DEFAULT_LOG_FILENAME;

	log_set_loglevel(req->log_level);
	if (log_init(opts.output) == -1) {
		pr_perror("Can't initiate log");
		goto err;
	}

	if (log_keep_err()) {
		pr_perror("Can't tune log");
		goto err;
	}

	/* checking flags from client */
	if (req->has_leave_running && req->leave_running)
		opts.final_state = TASK_ALIVE;

	if (!req->has_pid) {
		req->has_pid = true;
		req->pid = ids.pid;
	}

	if (req->has_ext_unix_sk) {
		opts.ext_unix_sk = req->ext_unix_sk;
		for (i = 0; i < req->n_unix_sk_ino; i++) {
			if (unix_sk_id_add((unsigned int)req->unix_sk_ino[i]->inode) < 0)
				goto err;
		}
	}

	if (req->root)
		opts.root = req->root;

	if (req->has_rst_sibling) {
		if (!opts.swrk_restore) {
			pr_err("rst_sibling is not allowed in standalone service\n");
			goto err;
		}

		opts.restore_sibling = req->rst_sibling;
	}

	if (req->has_tcp_established)
		opts.tcp_established_ok = req->tcp_established;

	if (req->has_tcp_skip_in_flight)
		opts.tcp_skip_in_flight = req->tcp_skip_in_flight;

	if (req->has_weak_sysctls)
		opts.weak_sysctls = req->weak_sysctls;

	if (req->has_evasive_devices)
		opts.evasive_devices = req->evasive_devices;

	if (req->has_shell_job)
		opts.shell_job = req->shell_job;

	if (req->has_file_locks)
		opts.handle_file_locks = req->file_locks;

	if (req->has_track_mem)
		opts.track_mem = req->track_mem;

	if (req->has_link_remap)
		opts.link_remap_ok = req->link_remap;

	if (req->has_auto_dedup)
		opts.auto_dedup = req->auto_dedup;

	if (req->has_force_irmap)
		opts.force_irmap = req->force_irmap;

	if (req->n_exec_cmd > 0) {
		opts.exec_cmd = xmalloc((req->n_exec_cmd + 1) * sizeof(char *));
		memcpy(opts.exec_cmd, req->exec_cmd, req->n_exec_cmd * sizeof(char *));
		opts.exec_cmd[req->n_exec_cmd] = NULL;
	}

	if (req->has_lazy_pages) {
		opts.lazy_pages = req->lazy_pages;
	}

	if (req->ps) {
		opts.port = htons((short)req->ps->port);

		if (!opts.lazy_pages) {
			opts.use_page_server = true;
			opts.addr = req->ps->address;

			if (req->ps->has_fd) {
				if (!opts.swrk_restore)
					goto err;

				opts.ps_socket = req->ps->fd;
			}
		}
	}

	if (req->notify_scripts && add_rpc_notify(sk))
		goto err;

	for (i = 0; i < req->n_veths; i++) {
		if (veth_pair_add(req->veths[i]->if_in, req->veths[i]->if_out))
			goto err;
	}

	for (i = 0; i < req->n_ext_mnt; i++) {
		if (ext_mount_add(req->ext_mnt[i]->key, req->ext_mnt[i]->val))
			goto err;
	}

	for (i = 0; i < req->n_join_ns; i++) {
		if (join_ns_add(req->join_ns[i]->ns, req->join_ns[i]->ns_file, req->join_ns[i]->extra_opt))
			goto err;
	}

	if (req->n_inherit_fd && !opts.swrk_restore) {
		pr_err("inherit_fd is not allowed in standalone service\n");
		goto err;
	}
	for (i = 0; i < req->n_inherit_fd; i++) {
		if (inherit_fd_add(req->inherit_fd[i]->fd, req->inherit_fd[i]->key))
			goto err;
	}

	for (i = 0; i < req->n_external; i++)
		if (add_external(req->external[i]))
			goto err;

	for (i = 0; i < req->n_cg_root; i++) {
		if (new_cg_root_add(req->cg_root[i]->ctrl,
					req->cg_root[i]->path))
			goto err;
	}

	for (i = 0; i < req->n_enable_fs; i++) {
		if (!add_fsname_auto(req->enable_fs[i]))
			goto err;
	}

	for (i = 0; i < req->n_skip_mnt; i++) {
		if (!add_skip_mount(req->skip_mnt[i]))
			goto err;
	}

	if (req->has_cpu_cap)
		opts.cpu_cap = req->cpu_cap;

	/*
	 * FIXME: For backward compatibility we setup
	 * soft mode here, need to enhance to support
	 * other modes as well via separate option
	 * probably.
	 */
	if (req->has_manage_cgroups)
		opts.manage_cgroups = req->manage_cgroups ? CG_MODE_SOFT : CG_MODE_IGNORE;

	/* Override the manage_cgroup if mode is set explicitly */
	if (req->has_manage_cgroups_mode) {
		unsigned int mode;

		switch (req->manage_cgroups_mode) {
		case CRIU_CG_MODE__IGNORE:
			mode = CG_MODE_IGNORE;
			break;
		case CRIU_CG_MODE__CG_NONE:
			mode = CG_MODE_NONE;
			break;
		case CRIU_CG_MODE__PROPS:
			mode = CG_MODE_PROPS;
			break;
		case CRIU_CG_MODE__SOFT:
			mode = CG_MODE_SOFT;
			break;
		case CRIU_CG_MODE__FULL:
			mode = CG_MODE_FULL;
			break;
		case CRIU_CG_MODE__STRICT:
			mode = CG_MODE_STRICT;
			break;
		case CRIU_CG_MODE__DEFAULT:
			mode = CG_MODE_DEFAULT;
			break;
		default:
			goto err;
		}

		opts.manage_cgroups = mode;
	}

	if (req->freeze_cgroup)
		opts.freeze_cgroup = req->freeze_cgroup;

	if (req->has_timeout)
		opts.timeout = req->timeout;

	if (req->cgroup_props)
		opts.cgroup_props = req->cgroup_props;

	if (req->cgroup_props_file)
		opts.cgroup_props_file = req->cgroup_props_file;

	for (i = 0; i < req->n_cgroup_dump_controller; i++) {
		if (!cgp_add_dump_controller(req->cgroup_dump_controller[i]))
			goto err;
	}

	if (req->has_auto_ext_mnt)
		opts.autodetect_ext_mounts = req->auto_ext_mnt;

	if (req->has_ext_sharing)
		opts.enable_external_sharing = req->ext_sharing;

	if (req->has_ext_masters)
		opts.enable_external_masters = req->ext_masters;

	if (req->has_ghost_limit)
		opts.ghost_limit = req->ghost_limit;

	if (req->has_empty_ns) {
		opts.empty_ns = req->empty_ns;
		if (req->empty_ns & ~(CLONE_NEWNET))
			goto err;
	}

	if (req->n_irmap_scan_paths) {
		for (i = 0; i < req->n_irmap_scan_paths; i++) {
			if (irmap_scan_path_add(req->irmap_scan_paths[i]))
				goto err;
		}
	}

	if (req->has_status_fd) {
		sprintf(status_fd, "/proc/%d/fd/%d", ids.pid, req->status_fd);
		opts.status_fd = open(status_fd, O_WRONLY);
		if (opts.status_fd < 0)
			goto err;
	}

	if (req->orphan_pts_master)
		opts.orphan_pts_master = true;

	if (check_namespace_opts())
		goto err;

	return 0;

err:
	set_cr_errno(EBADRQC);
	return -1;
}
Beispiel #2
0
static int setup_opts_from_req(int sk, CriuOpts *req)
{
	struct ucred ids;
	struct stat st;
	socklen_t ids_len = sizeof(struct ucred);
	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");
		goto err;
	}

	if (fstat(sk, &st)) {
		pr_perror("Can't get socket stat");
		goto err;
	}

	BUG_ON(st.st_ino == -1);
	service_sk_ino = st.st_ino;

	/*
	 * 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 = opts.output;
		char *tmp_work = opts.work_dir;
		char *tmp_imgs = 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);
		if (i) {
			xfree(tmp_output);
			xfree(tmp_work);
			xfree(tmp_imgs);
			goto err;
		}
		/* If this is non-NULL, the RPC configuration file had a value, use it.*/
		if (opts.output)
			output_changed_by_rpc_conf = true;
		/* If this is NULL, use the old value if it was set. */
		if (!opts.output && tmp_output) {
			opts.output = tmp_output;
			tmp_output = NULL;
		}

		if (opts.work_dir)
			work_changed_by_rpc_conf = true;
		if (!opts.work_dir && tmp_work) {
			opts.work_dir = tmp_work;
			tmp_work = NULL;
		}

		if (opts.imgs_dir)
			imgs_changed_by_rpc_conf = true;
		/*
		 * As the images directory is a required RPC setting, it is not
		 * necessary to use the value from other configuration files.
		 * Either it is set in the RPC configuration file or it is set
		 * via RPC.
		 */
		xfree(tmp_output);
		xfree(tmp_work);
		xfree(tmp_imgs);
	}

	/*
	 * open images_dir - images_dir_fd is a required RPC parameter
	 *
	 * 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 from the RPC configuration file.
	 * 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 (imgs_changed_by_rpc_conf)
		strncpy(images_dir_path, opts.imgs_dir, PATH_MAX - 1);
	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);

	if (open_image_dir(images_dir_path) < 0) {
		pr_perror("Can't open images directory");
		goto err;
	}

	/* get full path to images_dir to use in process title */
	if (readlink(images_dir_path, images_dir, PATH_MAX) == -1) {
		pr_perror("Can't readlink %s", images_dir_path);
		goto err;
	}

	/* chdir to work dir */
	if (work_changed_by_rpc_conf)
		/* Use the value from the RPC configuration file first. */
		strncpy(work_dir_path, opts.work_dir, PATH_MAX - 1);
	else if (req->has_work_dir_fd)
		/* Use the value set via RPC. */
		sprintf(work_dir_path, "/proc/%d/fd/%d", ids.pid, req->work_dir_fd);
	else if (opts.work_dir)
		/* Use the value from one of the other configuration files. */
		strncpy(work_dir_path, opts.work_dir, PATH_MAX - 1);
	else
		/* Use the images directory a work directory. */
		strcpy(work_dir_path, images_dir_path);

	if (chdir(work_dir_path)) {
		pr_perror("Can't chdir to work_dir");
		goto err;
	}

	/* initiate log file in work dir */
	if (req->log_file && !output_changed_by_rpc_conf) {
		/*
		 * If RPC sets a log file and if there nothing from the
		 * RPC configuration file, use the RPC value.
		 */
		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 if (!opts.output) {
		SET_CHAR_OPTS(output, DEFAULT_LOG_FILENAME);
	}

	/* This is needed later to correctly set the log_level */
	opts.log_level = req->log_level;
	log_set_loglevel(req->log_level);
	if (log_init(opts.output) == -1) {
		pr_perror("Can't initiate log");
		goto err;
	}

	if (req->config_file) {
		pr_debug("Overwriting RPC settings with values from %s\n", req->config_file);
	}

	if (kerndat_init())
		return 1;

	if (log_keep_err()) {
		pr_perror("Can't tune log");
		goto err;
	}

	/* checking flags from client */
	if (req->has_leave_running && req->leave_running)
		opts.final_state = TASK_ALIVE;

	if (!req->has_pid) {
		req->has_pid = true;
		req->pid = ids.pid;
	}

	if (req->has_ext_unix_sk) {
		opts.ext_unix_sk = req->ext_unix_sk;
		for (i = 0; i < req->n_unix_sk_ino; i++) {
			if (unix_sk_id_add((unsigned int)req->unix_sk_ino[i]->inode) < 0)
				goto err;
		}
	}

	if (req->root)
		SET_CHAR_OPTS(root, req->root);

	if (req->has_rst_sibling) {
		if (!opts.swrk_restore) {
			pr_err("rst_sibling is not allowed in standalone service\n");
			goto err;
		}

		opts.restore_sibling = req->rst_sibling;
	}

	if (req->has_tcp_established)
		opts.tcp_established_ok = req->tcp_established;

	if (req->has_tcp_skip_in_flight)
		opts.tcp_skip_in_flight = req->tcp_skip_in_flight;

	if (req->has_tcp_close)
		opts.tcp_close = req->tcp_close;

	if (req->has_weak_sysctls)
		opts.weak_sysctls = req->weak_sysctls;

	if (req->has_evasive_devices)
		opts.evasive_devices = req->evasive_devices;

	if (req->has_shell_job)
		opts.shell_job = req->shell_job;

	if (req->has_file_locks)
		opts.handle_file_locks = req->file_locks;

	if (req->has_track_mem)
		opts.track_mem = req->track_mem;

	if (req->has_link_remap)
		opts.link_remap_ok = req->link_remap;

	if (req->has_auto_dedup)
		opts.auto_dedup = req->auto_dedup;

	if (req->has_force_irmap)
		opts.force_irmap = req->force_irmap;

	if (req->n_exec_cmd > 0) {
		opts.exec_cmd = xmalloc((req->n_exec_cmd + 1) * sizeof(char *));
		memcpy(opts.exec_cmd, req->exec_cmd, req->n_exec_cmd * sizeof(char *));
		opts.exec_cmd[req->n_exec_cmd] = NULL;
	}

	if (req->has_lazy_pages) {
		opts.lazy_pages = req->lazy_pages;
	}

	if (req->ps) {
		opts.port = (short)req->ps->port;

		if (!opts.lazy_pages) {
			opts.use_page_server = true;
			if (req->ps->address)
				SET_CHAR_OPTS(addr, req->ps->address);
			else
				opts.addr = NULL;

			if (req->ps->has_fd) {
				if (!opts.swrk_restore)
					goto err;

				opts.ps_socket = req->ps->fd;
			}
		}
	}

	if (req->notify_scripts && add_rpc_notify(sk))
		goto err;

	for (i = 0; i < req->n_veths; i++) {
		if (veth_pair_add(req->veths[i]->if_in, req->veths[i]->if_out))
			goto err;
	}

	for (i = 0; i < req->n_ext_mnt; i++) {
		if (ext_mount_add(req->ext_mnt[i]->key, req->ext_mnt[i]->val))
			goto err;
	}

	for (i = 0; i < req->n_join_ns; i++) {
		if (join_ns_add(req->join_ns[i]->ns, req->join_ns[i]->ns_file, req->join_ns[i]->extra_opt))
			goto err;
	}

	if (req->n_inherit_fd && !opts.swrk_restore) {
		pr_err("inherit_fd is not allowed in standalone service\n");
		goto err;
	}
	for (i = 0; i < req->n_inherit_fd; i++) {
		if (inherit_fd_add(req->inherit_fd[i]->fd, req->inherit_fd[i]->key))
			goto err;
	}

	for (i = 0; i < req->n_external; i++)
		if (add_external(req->external[i]))
			goto err;

	for (i = 0; i < req->n_cg_root; i++) {
		if (new_cg_root_add(req->cg_root[i]->ctrl,
					req->cg_root[i]->path))
			goto err;
	}

	for (i = 0; i < req->n_enable_fs; i++) {
		if (!add_fsname_auto(req->enable_fs[i]))
			goto err;
	}

	for (i = 0; i < req->n_skip_mnt; i++) {
		if (!add_skip_mount(req->skip_mnt[i]))
			goto err;
	}

	if (req->has_cpu_cap) {
		opts.cpu_cap = req->cpu_cap;
		opts.cpu_cap |= CPU_CAP_IMAGE;
	}

	/*
	 * FIXME: For backward compatibility we setup
	 * soft mode here, need to enhance to support
	 * other modes as well via separate option
	 * probably.
	 */
	if (req->has_manage_cgroups)
		opts.manage_cgroups = req->manage_cgroups ? CG_MODE_SOFT : CG_MODE_IGNORE;

	/* Override the manage_cgroup if mode is set explicitly */
	if (req->has_manage_cgroups_mode) {
		unsigned int mode;

		switch (req->manage_cgroups_mode) {
		case CRIU_CG_MODE__IGNORE:
			mode = CG_MODE_IGNORE;
			break;
		case CRIU_CG_MODE__CG_NONE:
			mode = CG_MODE_NONE;
			break;
		case CRIU_CG_MODE__PROPS:
			mode = CG_MODE_PROPS;
			break;
		case CRIU_CG_MODE__SOFT:
			mode = CG_MODE_SOFT;
			break;
		case CRIU_CG_MODE__FULL:
			mode = CG_MODE_FULL;
			break;
		case CRIU_CG_MODE__STRICT:
			mode = CG_MODE_STRICT;
			break;
		case CRIU_CG_MODE__DEFAULT:
			mode = CG_MODE_DEFAULT;
			break;
		default:
			goto err;
		}

		opts.manage_cgroups = mode;
	}

	if (req->freeze_cgroup)
		SET_CHAR_OPTS(freeze_cgroup, req->freeze_cgroup);

	if (req->lsm_profile) {
		opts.lsm_supplied = true;
		SET_CHAR_OPTS(lsm_profile, req->lsm_profile);
	}

	if (req->has_timeout)
		opts.timeout = req->timeout;

	if (req->cgroup_props)
		SET_CHAR_OPTS(cgroup_props, req->cgroup_props);

	if (req->cgroup_props_file)
		SET_CHAR_OPTS(cgroup_props_file, req->cgroup_props_file);

	for (i = 0; i < req->n_cgroup_dump_controller; i++) {
		if (!cgp_add_dump_controller(req->cgroup_dump_controller[i]))
			goto err;
	}

	if (req->has_auto_ext_mnt)
		opts.autodetect_ext_mounts = req->auto_ext_mnt;

	if (req->has_ext_sharing)
		opts.enable_external_sharing = req->ext_sharing;

	if (req->has_ext_masters)
		opts.enable_external_masters = req->ext_masters;

	if (req->has_ghost_limit)
		opts.ghost_limit = req->ghost_limit;

	if (req->has_empty_ns) {
		opts.empty_ns = req->empty_ns;
		if (req->empty_ns & ~(CLONE_NEWNET))
			goto err;
	}

	if (req->n_irmap_scan_paths) {
		for (i = 0; i < req->n_irmap_scan_paths; i++) {
			if (irmap_scan_path_add(req->irmap_scan_paths[i]))
				goto err;
		}
	}

	if (req->has_status_fd) {
		sprintf(status_fd, "/proc/%d/fd/%d", ids.pid, req->status_fd);
		opts.status_fd = open(status_fd, O_WRONLY);
		if (opts.status_fd < 0)
			goto err;
	}

	if (req->orphan_pts_master)
		opts.orphan_pts_master = true;


	/* 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);
	if (check_options())
		goto err;

	return 0;

err:
	set_cr_errno(EBADRQC);
	return -1;
}