diff --git a/Makefile.am b/Makefile.am index 6905f5fa2e..9a14385fdb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,7 @@ EXTRA_DIST = COPYING COPYING.libcrun README.md NEWS SECURITY.md rpm/crun.spec au src/libcrun/linux.h src/libcrun/utils.h src/libcrun/error.h src/libcrun/criu.h \ src/libcrun/scheduler.h src/libcrun/mempolicy.h src/libcrun/status.h src/libcrun/terminal.h \ src/libcrun/mount_flags.h src/libcrun/intelrdt.h src/libcrun/ring_buffer.h src/libcrun/string_map.h \ + src/libcrun/json_gen_utils.h \ src/libcrun/net_device.h src/libcrun/spec.h \ src/libcrun/syscalls.h \ crun.1.md crun.1 libcrun.lds \ diff --git a/src/create.c b/src/create.c index af4d3c2bfe..c77371cf6f 100644 --- a/src/create.c +++ b/src/create.c @@ -74,7 +74,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OPTION_PRESERVE_FDS: - crun_context.preserve_fds = parse_int_or_fail (argp_mandatory_argument (arg, state), "preserve-fds"); + crun_context.preserve_fds = parse_id_or_fail (argp_mandatory_argument (arg, state), NULL, "preserve-fds"); break; case OPTION_NO_SUBREAPER: diff --git a/src/crun.c b/src/crun.c index cc290ce643..13fbba4d32 100644 --- a/src/crun.c +++ b/src/crun.c @@ -389,20 +389,29 @@ argp_mandatory_argument (char *arg, struct argp_state *state) return state->argv[state->next++]; } +/* Parse a numeric value from STR, aborting on an out-of-range or malformed + value. KIND names the field for error messages. If ENDPTR is not NULL it + is set past the parsed number (so the caller can continue parsing, e.g. a + "UID:GID" pair); otherwise the whole string must be a valid number. */ int -parse_int_or_fail (const char *str, const char *kind) +parse_id_or_fail (const char *str, char **endptr, const char *kind) { - char *endptr = NULL; + char *end = NULL; long long l; errno = 0; - l = strtoll (str, &endptr, 10); - if (errno != 0) - libcrun_fail_with_error (errno, "invalid value for `%s`", kind); - if (endptr != NULL && *endptr != '\0') - libcrun_fail_with_error (EINVAL, "invalid value for `%s`", kind); + l = strtoll (str, &end, 10); + if (end == str) + libcrun_fail_with_error (0, "invalid %s specified", kind); + if (errno == ERANGE) + libcrun_fail_with_error (0, "invalid %s specified", kind); if (l < INT_MIN || l > INT_MAX) - libcrun_fail_with_error (ERANGE, "invalid value for `%s`", kind); + libcrun_fail_with_error (0, "invalid %s specified", kind); + + if (endptr) + *endptr = end; + else if (*end != '\0') + libcrun_fail_with_error (0, "invalid %s specified", kind); return (int) l; } diff --git a/src/crun.h b/src/crun.h index 7efead21c4..fd522f1f91 100644 --- a/src/crun.h +++ b/src/crun.h @@ -37,7 +37,7 @@ struct crun_global_arguments }; char *argp_mandatory_argument (char *arg, struct argp_state *state); -int parse_int_or_fail (const char *str, const char *kind); +int parse_id_or_fail (const char *str, char **endptr, const char *kind); int init_libcrun_context (libcrun_context_t *con, const char *id, struct crun_global_arguments *glob, libcrun_error_t *err); void crun_assert_n_args (int n, int min, int max); diff --git a/src/exec.c b/src/exec.c index e51acbd6a4..db4d52f0c0 100644 --- a/src/exec.c +++ b/src/exec.c @@ -88,26 +88,25 @@ static struct argp_option options[] static char args_doc[] = "exec CONTAINER cmd"; +static void +append_to_string_array (char ***arr, size_t *size, const char *arg) +{ + *arr = xrealloc (*arr, (*size + 2) * sizeof (**arr)); + (*arr)[*size + 1] = NULL; + (*arr)[*size] = xstrdup (arg); + (*size)++; +} + static void append_env (const char *arg) { - exec_options.env = realloc (exec_options.env, (exec_options.env_size + 2) * sizeof (*exec_options.env)); - if (exec_options.env == NULL) - error (EXIT_FAILURE, errno, "cannot allocate memory"); - exec_options.env[exec_options.env_size + 1] = NULL; - exec_options.env[exec_options.env_size] = xstrdup (arg); - exec_options.env_size++; + append_to_string_array (&exec_options.env, &exec_options.env_size, arg); } static void append_cap (const char *arg) { - exec_options.cap = realloc (exec_options.cap, (exec_options.cap_size + 2) * sizeof (*exec_options.cap)); - if (exec_options.cap == NULL) - error (EXIT_FAILURE, errno, "cannot allocate memory"); - exec_options.cap[exec_options.cap_size + 1] = NULL; - exec_options.cap[exec_options.cap_size] = xstrdup (arg); - exec_options.cap_size++; + append_to_string_array (&exec_options.cap, &exec_options.cap_size, arg); } static char ** @@ -152,7 +151,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OPTION_PRESERVE_FDS: - exec_options.preserve_fds = parse_int_or_fail (argp_mandatory_argument (arg, state), "preserve-fds"); + exec_options.preserve_fds = parse_id_or_fail (argp_mandatory_argument (arg, state), NULL, "preserve-fds"); break; case OPTION_CGROUP: @@ -205,38 +204,21 @@ make_oci_process_user (const char *userspec) runtime_spec_schema_config_schema_process_user *u; char *endptr = NULL; char *gidstr = NULL; - long long l; if (userspec == NULL) return NULL; u = xmalloc0 (sizeof (runtime_spec_schema_config_schema_process_user)); - errno = 0; - l = strtoll (userspec, &endptr, 10); - if (endptr == userspec) - libcrun_fail_with_error (0, "invalid UID specified"); - if (errno == ERANGE) - libcrun_fail_with_error (0, "invalid UID specified"); - if (l < INT_MIN || l > INT_MAX) - libcrun_fail_with_error (0, "invalid UID specified"); - u->uid = (int) l; + u->uid = parse_id_or_fail (userspec, &endptr, "UID"); if (*endptr == '\0') return u; if (*endptr != ':') libcrun_fail_with_error (0, "invalid USERSPEC specified"); - errno = 0; gidstr = endptr + 1; - l = strtoll (gidstr, &endptr, 10); - if (endptr == gidstr) - libcrun_fail_with_error (0, "invalid GID specified"); - if (errno == ERANGE) - libcrun_fail_with_error (0, "invalid GID specified"); - if (l < INT_MIN || l > INT_MAX) - libcrun_fail_with_error (0, "invalid GID specified"); + u->gid = parse_id_or_fail (gidstr, &endptr, "GID"); if (*endptr != '\0') libcrun_fail_with_error (0, "invalid USERSPEC specified"); - u->gid = (int) l; return u; } diff --git a/src/libcrun/cgroup-systemd.c b/src/libcrun/cgroup-systemd.c index 3837f8bd1f..dde835c288 100644 --- a/src/libcrun/cgroup-systemd.c +++ b/src/libcrun/cgroup-systemd.c @@ -300,6 +300,18 @@ setup_cpuset_for_systemd_v1 (runtime_spec_schema_config_linux_resources *resourc return 0; } +/* Build PATH from the cgroup slice FROM, appending SUFFIX when set. */ +static int +compute_finalized_path (char **path, const char *from, const char *suffix, libcrun_error_t *err) +{ + if (suffix == NULL) + { + *path = xstrdup (from); + return 0; + } + return append_paths (path, err, from, suffix, NULL); +} + static int systemd_finalize (struct libcrun_cgroup_args *args, char **path_out, int cgroup_mode, const char *suffix, libcrun_error_t *err) @@ -344,15 +356,10 @@ systemd_finalize (struct libcrun_cgroup_args *args, char **path_out, if (UNLIKELY (to == NULL)) return crun_make_error (err, 0, "cannot parse `%s`", PROC_SELF_CGROUP); *to = '\0'; - if (suffix == NULL) - path = xstrdup (from); - else - { - ret = append_paths (&path, err, from, suffix, NULL); - if (UNLIKELY (ret < 0)) - return ret; - } + ret = compute_finalized_path (&path, from, suffix, err); *to = '\n'; + if (UNLIKELY (ret < 0)) + return ret; if (geteuid ()) return 0; @@ -411,15 +418,10 @@ systemd_finalize (struct libcrun_cgroup_args *args, char **path_out, if (UNLIKELY (to == NULL)) return crun_make_error (err, 0, "cannot parse `%s`", PROC_SELF_CGROUP); *to = '\0'; - if (suffix == NULL) - path = xstrdup (from); - else - { - ret = append_paths (&path, err, from, suffix, NULL); - if (UNLIKELY (ret < 0)) - return ret; - } + ret = compute_finalized_path (&path, from, suffix, err); *to = '\n'; + if (UNLIKELY (ret < 0)) + return ret; ret = append_paths (&dir, err, CGROUP_ROOT, path, NULL); if (UNLIKELY (ret < 0)) @@ -1326,9 +1328,6 @@ append_devices (sd_bus_message *m, return ret; } - if (resources == NULL) - return 0; - for (i = find_first_rule_no_default (resources->devices, resources->devices_len); i < resources->devices_len; i++) { runtime_spec_schema_defs_linux_device_cgroup *d = resources->devices[i]; @@ -1729,6 +1728,21 @@ verify_ebpf_device_filter_installed (const char *cgroup_path, libcrun_error_t *e return 0; } +/* Release the sd-bus objects used by a D-Bus call and return RET. */ +static int +cleanup_sd_bus_and_return (sd_bus *bus, sd_bus_message *m, sd_bus_message *reply, + sd_bus_error *error, int ret) +{ + if (bus) + sd_bus_unref (bus); + if (m) + sd_bus_message_unref (m); + if (reply) + sd_bus_message_unref (reply); + sd_bus_error_free (error); + return ret; +} + static int enter_systemd_cgroup_scope (runtime_spec_schema_config_linux_resources *resources, int cgroup_mode, @@ -1933,14 +1947,7 @@ enter_systemd_cgroup_scope (runtime_spec_schema_config_linux_resources *resource ret = systemd_check_job_status (bus, &job_data, object, "creating", err); exit: - if (bus) - sd_bus_unref (bus); - if (m) - sd_bus_message_unref (m); - if (reply) - sd_bus_message_unref (reply); - sd_bus_error_free (&error); - return ret; + return cleanup_sd_bus_and_return (bus, m, reply, &error, ret); } static int @@ -1999,14 +2006,7 @@ libcrun_destroy_systemd_cgroup_scope (struct libcrun_cgroup_status *cgroup_statu reset_failed_unit (bus, scope); exit: - if (bus) - sd_bus_unref (bus); - if (m) - sd_bus_message_unref (m); - if (reply) - sd_bus_message_unref (reply); - sd_bus_error_free (&error); - return ret; + return cleanup_sd_bus_and_return (bus, m, reply, &error, ret); } static const char * @@ -2263,14 +2263,7 @@ libcrun_update_resources_systemd (struct libcrun_cgroup_status *cgroup_status, ret = 0; exit: - if (bus) - sd_bus_unref (bus); - if (m) - sd_bus_message_unref (m); - if (reply) - sd_bus_message_unref (reply); - sd_bus_error_free (&error); - return ret; + return cleanup_sd_bus_and_return (bus, m, reply, &error, ret); } #else diff --git a/src/libcrun/container.c b/src/libcrun/container.c index 84e308d8b8..eac187e26d 100644 --- a/src/libcrun/container.c +++ b/src/libcrun/container.c @@ -22,6 +22,7 @@ #include #include "container.h" #include "utils.h" +#include "json_gen_utils.h" #include "seccomp.h" #include "mempolicy.h" #ifdef HAVE_SECCOMP @@ -494,6 +495,29 @@ initialize_security (libcrun_container_t *container, runtime_spec_schema_config_ return 0; } +/* Emit the "annotations" map. Returns a json_gen_status; callers can wrap + the call with GEN_OR_FAIL. */ +static int +gen_annotations (json_gen_ctx *gen, json_map_string_string *annotations) +{ + size_t i; + int r; + + GEN_KEY (gen, "annotations"); + GEN_OR_FAIL (json_gen_map_open (gen)); + + for (i = 0; i < annotations->len; i++) + { + GEN_STR (gen, annotations->keys[i]); + GEN_STR (gen, annotations->values[i]); + } + + return json_gen_map_close (gen); + +gen_error: + return r; +} + static int do_hooks (runtime_spec_schema_config_schema *def, pid_t pid, const char *id, bool keep_going, const char *cwd, const char *status, hook **hooks, size_t hooks_len, int out_fd, int err_fd, bool can_ignore_chdir_errors, libcrun_error_t *err) @@ -503,7 +527,7 @@ do_hooks (runtime_spec_schema_config_schema *def, pid_t pid, const char *id, boo char *stdin = NULL; cleanup_free char *cwd_allocated = NULL; const char *rootfs = def->root ? def->root->path : ""; - json_gen_ctx *gen = NULL; + cleanup_json_gen json_gen_ctx *gen = NULL; if (cwd == NULL) { @@ -517,93 +541,32 @@ do_hooks (runtime_spec_schema_config_schema *def, pid_t pid, const char *id, boo json_gen_config (gen, json_gen_beautify, 0); - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "ociVersion", strlen ("ociVersion")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_map_open (gen)); - r = json_gen_string (gen, "1.0", strlen ("1.0")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "ociVersion"); + GEN_STR (gen, "1.0"); - r = json_gen_string (gen, "id", strlen ("id")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "id"); + GEN_STR (gen, id); - r = json_gen_string (gen, id, strlen (id)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "pid"); + GEN_OR_FAIL (map_int (gen, pid)); - r = json_gen_string (gen, "pid", strlen ("pid")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "root"); + GEN_STR (gen, rootfs); - r = map_int (gen, pid); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "bundle"); + GEN_STR (gen, cwd); - r = json_gen_string (gen, "root", strlen ("root")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, rootfs, strlen (rootfs)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "bundle", strlen ("bundle")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, cwd, strlen (cwd)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "status", strlen ("status")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, status, strlen (status)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "status"); + GEN_STR (gen, status); if (def && def->annotations && def->annotations->len) - { - r = json_gen_string (gen, "annotations", strlen ("annotations")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (gen_annotations (gen, def->annotations)); - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_map_close (gen)); - for (i = 0; i < def->annotations->len; i++) - { - const char *key = def->annotations->keys[i]; - const char *val = def->annotations->values[i]; - - r = json_gen_string (gen, key, strlen (key)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, val, strlen (val)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - } - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - } - - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_get_buf (gen, (const char **) &stdin, &stdin_len); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_get_buf (gen, (const char **) &stdin, &stdin_len)); ret = 0; @@ -639,14 +602,9 @@ do_hooks (runtime_spec_schema_config_schema *def, pid_t pid, const char *id, boo } } - if (gen) - json_gen_free (gen); - return ret; gen_error: - if (gen) - json_gen_free (gen); return json_gen_error_to_crun_error (r, err); } @@ -684,41 +642,19 @@ get_seccomp_receiver_fd_payload (libcrun_container_t *container, const char *sta json_gen_config (gen, json_gen_beautify, 1); - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_OR_FAIL (json_gen_map_open (gen)); - r = json_gen_string (gen, "ociVersion", strlen ("ociVersion")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "ociVersion"); + GEN_STR (gen, OCI_VERSION); - r = json_gen_string (gen, OCI_VERSION, strlen (OCI_VERSION)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "fds"); + GEN_OR_FAIL (json_gen_array_open (gen)); - r = json_gen_string (gen, "fds", strlen ("fds")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "seccompFd"); + GEN_OR_FAIL (json_gen_array_close (gen)); - r = json_gen_array_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, "seccompFd", strlen ("seccompFd")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_array_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, "pid", strlen ("pid")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = map_int (gen, own_pid); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "pid"); + GEN_OR_FAIL (map_int (gen, own_pid)); if (def && def->linux && def->linux->seccomp) { @@ -726,113 +662,47 @@ get_seccomp_receiver_fd_payload (libcrun_container_t *container, const char *sta if (metadata) { - r = json_gen_string (gen, "metadata", strlen ("metadata")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, metadata, strlen (metadata)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "metadata"); + GEN_STR (gen, metadata); } } /* State. */ - r = json_gen_string (gen, "state", strlen ("state")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "state"); + GEN_OR_FAIL (json_gen_map_open (gen)); - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, "ociVersion", strlen ("ociVersion")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, OCI_VERSION, strlen (OCI_VERSION)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "ociVersion"); + GEN_STR (gen, OCI_VERSION); if (container->context && container->context->id) { - r = json_gen_string (gen, "id", strlen ("id")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, container->context->id, strlen (container->context->id)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "id"); + GEN_STR (gen, container->context->id); } - r = json_gen_string (gen, "status", strlen ("status")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, status, strlen (status)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, "pid", strlen ("pid")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "status"); + GEN_STR (gen, status); - r = map_int (gen, own_pid); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "pid"); + GEN_OR_FAIL (map_int (gen, own_pid)); if (container->context && container->context->bundle) { - r = json_gen_string (gen, "bundle", strlen ("bundle")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, container->context->bundle, strlen (container->context->bundle)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_KEY (gen, "bundle"); + GEN_STR (gen, container->context->bundle); } if (def->annotations && def->annotations->len) - { - size_t i; - - r = json_gen_string (gen, "annotations", strlen ("annotations")); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_OR_FAIL (gen_annotations (gen, def->annotations)); - for (i = 0; i < def->annotations->len; i++) - { - const char *key = def->annotations->keys[i]; - const char *val = def->annotations->values[i]; - - r = json_gen_string (gen, key, strlen (key)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - - r = json_gen_string (gen, val, strlen (val)); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - } - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; - } - - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_OR_FAIL (json_gen_map_close (gen)); /* End state. */ - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto exit; + GEN_OR_FAIL (json_gen_map_close (gen)); r = get_json_gen_result (gen, seccomp_fd_payload, seccomp_fd_payload_len); -exit: +gen_error: json_gen_free (gen); return json_gen_error_to_crun_error (r, err); @@ -1933,6 +1803,16 @@ struct wait_for_process_args const char *seccomp_notify_plugins; }; +static int +write_pid_file (const char *pid_file, pid_t pid, libcrun_error_t *err) +{ + char buf[32]; + int buf_len = snprintf (buf, sizeof (buf), "%d", pid); + if (UNLIKELY (buf_len >= (int) sizeof (buf))) + return crun_make_error (err, 0, "internal error: static buffer too small"); + return write_file_at_with_flags (AT_FDCWD, O_CREAT | O_TRUNC, 0700, pid_file, buf, buf_len, err); +} + static int wait_for_process (struct wait_for_process_args *args, libcrun_error_t *err) { @@ -1966,11 +1846,7 @@ wait_for_process (struct wait_for_process_args *args, libcrun_error_t *err) if (args->context->pid_file) { - char buf[32]; - int buf_len = snprintf (buf, sizeof (buf), "%d", args->pid); - if (UNLIKELY (buf_len >= (int) sizeof (buf))) - return crun_make_error (err, 0, "internal error: static buffer too small"); - ret = write_file_at_with_flags (AT_FDCWD, O_CREAT | O_TRUNC, 0700, args->context->pid_file, buf, buf_len, err); + ret = write_pid_file (args->context->pid_file, args->pid, err); if (UNLIKELY (ret < 0)) return ret; } @@ -2934,6 +2810,18 @@ force_delete_container_status (libcrun_context_t *context, runtime_spec_schema_c crun_error_release (&tmp_err); } +/* Serialize ERR to FD as an int errno followed by a NUL-terminated message. + Used by forked children to report a failure to the parent process. */ +static void +write_error_to_pipe (int fd, libcrun_error_t *err) +{ + const char *msg = (*err)->msg; + int errcode = crun_error_get_errno (err); + + TEMP_FAILURE_RETRY (write (fd, &errcode, sizeof (errcode))); + TEMP_FAILURE_RETRY (write (fd, msg, strlen (msg) + 1)); +} + int libcrun_container_run (libcrun_context_t *context, libcrun_container_t *container, unsigned int options, libcrun_error_t *err) @@ -3037,8 +2925,7 @@ libcrun_container_run (libcrun_context_t *context, libcrun_container_t *containe force_delete_container_status (context, def); if (tmp_err) { - TEMP_FAILURE_RETRY (write (pipefd1, &(tmp_err->status), sizeof (tmp_err->status))); - TEMP_FAILURE_RETRY (write (pipefd1, tmp_err->msg, strlen (tmp_err->msg) + 1)); + write_error_to_pipe (pipefd1, &tmp_err); crun_error_release (&tmp_err); } @@ -3325,6 +3212,7 @@ libcrun_container_state (libcrun_context_t *context, const char *id, FILE *out, json_gen_ctx *gen = NULL; const char *buf; int ret = 0; + int r = json_gen_status_ok; int running; size_t len; @@ -3342,83 +3230,54 @@ libcrun_container_state (libcrun_context_t *context, const char *id, FILE *out, json_gen_config (gen, json_gen_beautify, 1); - json_gen_map_open (gen); - json_gen_string (gen, "ociVersion", strlen ("ociVersion")); - json_gen_string (gen, OCI_CONFIG_VERSION, strlen (OCI_CONFIG_VERSION)); + GEN_OR_FAIL (json_gen_map_open (gen)); + GEN_KEY (gen, "ociVersion"); + GEN_STR (gen, OCI_CONFIG_VERSION); - json_gen_string (gen, "id", strlen ("id")); - json_gen_string (gen, id, strlen (id)); + GEN_KEY (gen, "id"); + GEN_STR (gen, id); - json_gen_string (gen, "pid", strlen ("pid")); - map_int (gen, running ? status.pid : 0); + GEN_KEY (gen, "pid"); + GEN_OR_FAIL (map_int (gen, running ? status.pid : 0)); - json_gen_string (gen, "status", strlen ("status")); - json_gen_string (gen, container_status, strlen (container_status)); + GEN_KEY (gen, "status"); + GEN_STR (gen, container_status); - json_gen_string (gen, "bundle", strlen ("bundle")); - json_gen_string (gen, status.bundle, strlen (status.bundle)); + GEN_KEY (gen, "bundle"); + GEN_STR (gen, status.bundle); - json_gen_string (gen, "rootfs", strlen ("rootfs")); - json_gen_string (gen, status.rootfs, strlen (status.rootfs)); + GEN_KEY (gen, "rootfs"); + GEN_STR (gen, status.rootfs); - json_gen_string (gen, "created", strlen ("created")); - json_gen_string (gen, status.created, strlen (status.created)); + GEN_KEY (gen, "created"); + GEN_STR (gen, status.created); if (status.scope) { - json_gen_string (gen, "systemd-scope", strlen ("systemd-scope")); - json_gen_string (gen, status.scope, strlen (status.scope)); + GEN_KEY (gen, "systemd-scope"); + GEN_STR (gen, status.scope); } if (status.owner) { - json_gen_string (gen, "owner", strlen ("owner")); - json_gen_string (gen, status.owner, strlen (status.owner)); + GEN_KEY (gen, "owner"); + GEN_STR (gen, status.owner); } { - size_t i; - cleanup_free char *config_file = NULL; cleanup_container libcrun_container_t *container = NULL; - cleanup_free char *dir = NULL; - - ret = libcrun_get_state_directory (&dir, state_root, id, err); - if (UNLIKELY (ret < 0)) - goto exit; - ret = append_paths (&config_file, err, dir, "config.json", NULL); + ret = read_container_config_from_state (&container, state_root, id, err); if (UNLIKELY (ret < 0)) goto exit; - container = libcrun_container_load_from_file (config_file, err); - if (UNLIKELY (container == NULL)) - { - ret = -1; - goto exit; - } - if (container->container_def->annotations && container->container_def->annotations->len) - { - json_gen_string (gen, "annotations", strlen ("annotations")); - json_gen_map_open (gen); - for (i = 0; i < container->container_def->annotations->len; i++) - { - const char *key = container->container_def->annotations->keys[i]; - const char *val = container->container_def->annotations->values[i]; - json_gen_string (gen, key, strlen (key)); - json_gen_string (gen, val, strlen (val)); - } - json_gen_map_close (gen); - } + GEN_OR_FAIL (gen_annotations (gen, container->container_def->annotations)); } - json_gen_map_close (gen); + GEN_OR_FAIL (json_gen_map_close (gen)); - if (json_gen_get_buf (gen, &buf, &len) != json_gen_status_ok) - { - ret = crun_make_error (err, 0, "error generating JSON"); - goto exit; - } + GEN_OR_FAIL (json_gen_get_buf (gen, &buf, &len)); fprintf (out, "%s\n", buf); @@ -3427,6 +3286,10 @@ libcrun_container_state (libcrun_context_t *context, const char *id, FILE *out, json_gen_free (gen); libcrun_free_container_status (&status); return ret; + +gen_error: + ret = json_gen_error_to_crun_error (r, err); + goto exit; } int @@ -3464,6 +3327,35 @@ cleanup_process_schemap (runtime_spec_schema_config_schema_process **p) (void) free_runtime_spec_schema_config_schema_process (process); } +/* Build the seccomp receiver payload (when needed), apply the seccomp profile and + close the seccomp file descriptors. Used from exec_process_entrypoint both before + and after the capabilities are set, depending on process->no_new_privileges. */ +static int +apply_seccomp_for_exec (libcrun_container_t *container, pid_t own_pid, int seccomp_fd, + int seccomp_receiver_fd, char **seccomp_flags, size_t seccomp_flags_len, + libcrun_error_t *err) +{ + cleanup_free char *seccomp_fd_payload = NULL; + size_t seccomp_fd_payload_len = 0; + int ret; + + if (seccomp_receiver_fd >= 0) + { + ret = get_seccomp_receiver_fd_payload (container, "running", own_pid, &seccomp_fd_payload, &seccomp_fd_payload_len, err); + if (UNLIKELY (ret < 0)) + return ret; + } + + ret = libcrun_apply_seccomp (seccomp_fd, seccomp_receiver_fd, seccomp_fd_payload, + seccomp_fd_payload_len, seccomp_flags, seccomp_flags_len, err); + if (UNLIKELY (ret < 0)) + return ret; + + close_and_reset (&seccomp_fd); + close_and_reset (&seccomp_receiver_fd); + return 0; +} + static int exec_process_entrypoint (libcrun_context_t *context, libcrun_container_t *container, @@ -3575,23 +3467,10 @@ exec_process_entrypoint (libcrun_context_t *context, if (! process->no_new_privileges) { - cleanup_free char *seccomp_fd_payload = NULL; - size_t seccomp_fd_payload_len = 0; - - if (seccomp_receiver_fd >= 0) - { - ret = get_seccomp_receiver_fd_payload (container, "running", own_pid, &seccomp_fd_payload, &seccomp_fd_payload_len, err); - if (UNLIKELY (ret < 0)) - return ret; - } - - ret = libcrun_apply_seccomp (seccomp_fd, seccomp_receiver_fd, seccomp_fd_payload, - seccomp_fd_payload_len, seccomp_flags, seccomp_flags_len, err); + ret = apply_seccomp_for_exec (container, own_pid, seccomp_fd, seccomp_receiver_fd, + seccomp_flags, seccomp_flags_len, err); if (UNLIKELY (ret < 0)) return ret; - - close_and_reset (&seccomp_fd); - close_and_reset (&seccomp_receiver_fd); } ret = libcrun_container_setgroups (container, process, err); @@ -3634,22 +3513,10 @@ exec_process_entrypoint (libcrun_context_t *context, if (process->no_new_privileges) { - cleanup_free char *seccomp_fd_payload = NULL; - size_t seccomp_fd_payload_len = 0; - - if (seccomp_receiver_fd >= 0) - { - ret = get_seccomp_receiver_fd_payload (container, "running", own_pid, &seccomp_fd_payload, &seccomp_fd_payload_len, err); - if (UNLIKELY (ret < 0)) - return ret; - } - ret = libcrun_apply_seccomp (seccomp_fd, seccomp_receiver_fd, seccomp_fd_payload, - seccomp_fd_payload_len, seccomp_flags, seccomp_flags_len, err); + ret = apply_seccomp_for_exec (container, own_pid, seccomp_fd, seccomp_receiver_fd, + seccomp_flags, seccomp_flags_len, err); if (UNLIKELY (ret < 0)) return ret; - - close_and_reset (&seccomp_fd); - close_and_reset (&seccomp_receiver_fd); } if (process->user) @@ -3701,9 +3568,7 @@ libcrun_container_exec_with_options (libcrun_context_t *context, const char *id, cleanup_close int terminal_fd = -1; cleanup_close int seccomp_fd = -1; cleanup_terminal void *orig_terminal = NULL; - cleanup_free char *config_file = NULL; cleanup_container libcrun_container_t *container = NULL; - cleanup_free char *dir = NULL; int container_ret_status[2]; cleanup_close int pipefd0 = -1; cleanup_close int pipefd1 = -1; @@ -3725,18 +3590,10 @@ libcrun_container_exec_with_options (libcrun_context_t *context, const char *id, return ret; container_status = ret; - ret = libcrun_get_state_directory (&dir, state_root, id, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = append_paths (&config_file, err, dir, "config.json", NULL); + ret = read_container_config_from_state (&container, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; - container = libcrun_container_load_from_file (config_file, err); - if (UNLIKELY (container == NULL)) - return -1; - container->context = context; if (container_status == 0) @@ -3877,10 +3734,7 @@ libcrun_container_exec_with_options (libcrun_context_t *context, const char *id, libcrun_fail_with_error ((*err)->status, "%s", (*err)->msg); else { - const char *msg = (*err)->msg; - ret = crun_error_get_errno (err); - TEMP_FAILURE_RETRY (write (pipefd1, &ret, sizeof (ret))); - TEMP_FAILURE_RETRY (write (pipefd1, msg, strlen (msg) + 1)); + write_error_to_pipe (pipefd1, err); TEMP_FAILURE_RETRY (close (pipefd1)); pipefd1 = -1; } @@ -4034,7 +3888,7 @@ libcrun_container_update (libcrun_context_t *context, const char *id, const char def, err); if (UNLIKELY (ret < 0)) - return ret; + goto cleanup; } ret = libcrun_linux_container_update (&status, state_root, resources, err); @@ -4304,42 +4158,48 @@ libcrun_container_get_features (libcrun_context_t *context, struct features_info return 0; } -int -libcrun_container_pause (libcrun_context_t *context, const char *id, libcrun_error_t *err) +/* Read the container status and fail if the container is not running. */ +static int +read_status_require_running (libcrun_context_t *context, const char *id, + libcrun_container_status_t *status, libcrun_error_t *err) { int ret; - const char *state_root = context->state_root; - libcrun_container_status_t status = {}; - ret = libcrun_read_container_status (&status, state_root, id, err); + ret = libcrun_read_container_status (status, context->state_root, id, err); if (UNLIKELY (ret < 0)) return ret; - ret = libcrun_is_container_running (&status, err); + ret = libcrun_is_container_running (status, err); if (UNLIKELY (ret < 0)) return ret; if (ret == 0) return crun_make_error (err, 0, "the container `%s` is not running", id); - return libcrun_container_pause_linux (&status, err); + return 0; } int -libcrun_container_unpause (libcrun_context_t *context, const char *id, libcrun_error_t *err) +libcrun_container_pause (libcrun_context_t *context, const char *id, libcrun_error_t *err) { int ret; - const char *state_root = context->state_root; libcrun_container_status_t status = {}; - ret = libcrun_read_container_status (&status, state_root, id, err); + ret = read_status_require_running (context, id, &status, err); if (UNLIKELY (ret < 0)) return ret; - ret = libcrun_is_container_running (&status, err); + return libcrun_container_pause_linux (&status, err); +} + +int +libcrun_container_unpause (libcrun_context_t *context, const char *id, libcrun_error_t *err) +{ + int ret; + libcrun_container_status_t status = {}; + + ret = read_status_require_running (context, id, &status, err); if (UNLIKELY (ret < 0)) return ret; - if (ret == 0) - return crun_make_error (err, 0, "the container `%s` is not running", id); return libcrun_container_unpause_linux (&status, err); } @@ -4353,16 +4213,10 @@ libcrun_container_checkpoint (libcrun_context_t *context, const char *id, libcru libcrun_container_status_t status = {}; cleanup_container libcrun_container_t *container = NULL; - ret = libcrun_read_container_status (&status, state_root, id, err); + ret = read_status_require_running (context, id, &status, err); if (UNLIKELY (ret < 0)) return ret; - ret = libcrun_is_container_running (&status, err); - if (UNLIKELY (ret < 0)) - return ret; - if (ret == 0) - return crun_make_error (err, 0, "the container `%s` is not running", id); - ret = read_container_config_from_state (&container, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; @@ -4626,12 +4480,7 @@ libcrun_container_restore (libcrun_context_t *context, const char *id, libcrun_c if (context->pid_file) { - char buf[32]; - int buf_len = snprintf (buf, sizeof (buf), "%d", status.pid); - if (UNLIKELY (buf_len >= (int) sizeof (buf))) - return crun_make_error (err, 0, "internal error: static buffer too small"); - - ret = write_file_at_with_flags (AT_FDCWD, O_CREAT | O_TRUNC, 0700, context->pid_file, buf, buf_len, err); + ret = write_pid_file (context->pid_file, status.pid, err); if (UNLIKELY (ret < 0)) return ret; } @@ -4766,22 +4615,12 @@ int libcrun_container_update_intel_rdt (libcrun_context_t *context, const char *id, struct libcrun_intel_rdt_update *update, libcrun_error_t *err) { cleanup_container libcrun_container_t *container = NULL; - cleanup_free char *config_file = NULL; - cleanup_free char *dir = NULL; int ret; - ret = libcrun_get_state_directory (&dir, context->state_root, id, err); + ret = read_container_config_from_state (&container, context->state_root, id, err); if (UNLIKELY (ret < 0)) return ret; - ret = append_paths (&config_file, err, dir, "config.json", NULL); - if (UNLIKELY (ret < 0)) - return ret; - - container = libcrun_container_load_from_file (config_file, err); - if (UNLIKELY (container == NULL)) - return -1; - return libcrun_update_intel_rdt (id, container, update->l3_cache_schema, update->mem_bw_schema, update->schemata, err); } diff --git a/src/libcrun/criu.c b/src/libcrun/criu.c index fe44191796..55b6b0a082 100644 --- a/src/libcrun/criu.c +++ b/src/libcrun/criu.c @@ -317,6 +317,36 @@ register_masked_paths_mounts (runtime_spec_schema_config_schema *def, libcrun_co return 0; } +/* Parse one /proc/self/cgroup LINE into its subsystem, normalizing the name and + stripping any "name=" prefix. When SUBPATH_OUT is not NULL, the in-hierarchy + path is stored there. Returns the subsystem, or NULL if the line carries no + controller and should be skipped. LINE is modified in place. */ +static char * +parse_cgroup_subsystem (char *line, char **subpath_out) +{ + char *subsystem, *subpath, *it; + + subsystem = strchr (line, ':') + 1; + subpath = strchr (subsystem, ':') + 1; + *(subpath - 1) = '\0'; + + if (subsystem[0] == '\0') + return NULL; + + it = strstr (subsystem, "name="); + if (it) + subsystem = it + 5; + + if (strcmp (subsystem, "net_prio,net_cls") == 0) + subsystem = "net_cls,net_prio"; + if (strcmp (subsystem, "cpuacct,cpu") == 0) + subsystem = "cpu,cpuacct"; + + if (subpath_out) + *subpath_out = subpath; + return subsystem; +} + static int restore_cgroup_v1_mount (runtime_spec_schema_config_schema *def, libcrun_error_t *err) { @@ -362,24 +392,11 @@ restore_cgroup_v1_mount (runtime_spec_schema_config_schema *def, libcrun_error_t cleanup_free char *source = NULL; char *subsystem; char *subpath; - char *it; - - subsystem = strchr (from, ':') + 1; - subpath = strchr (subsystem, ':') + 1; - *(subpath - 1) = '\0'; - if (subsystem[0] == '\0') + subsystem = parse_cgroup_subsystem (from, &subpath); + if (subsystem == NULL) continue; - it = strstr (subsystem, "name="); - if (it) - subsystem = it + 5; - - if (strcmp (subsystem, "net_prio,net_cls") == 0) - subsystem = "net_cls,net_prio"; - if (strcmp (subsystem, "cpuacct,cpu") == 0) - subsystem = "cpu,cpuacct"; - ret = append_paths (&source, err, CGROUP_ROOT, subsystem, NULL); if (UNLIKELY (ret < 0)) return ret; @@ -431,25 +448,11 @@ checkpoint_cgroup_v1_mount (runtime_spec_schema_config_schema *def, libcrun_erro { cleanup_free char *source_path = NULL; char *subsystem; - char *subpath; - char *it; - - subsystem = strchr (from, ':') + 1; - subpath = strchr (subsystem, ':') + 1; - *(subpath - 1) = '\0'; - if (subsystem[0] == '\0') + subsystem = parse_cgroup_subsystem (from, NULL); + if (subsystem == NULL) continue; - it = strstr (subsystem, "name="); - if (it) - subsystem = it + 5; - - if (strcmp (subsystem, "net_prio,net_cls") == 0) - subsystem = "net_cls,net_prio"; - if (strcmp (subsystem, "cpuacct,cpu") == 0) - subsystem = "cpu,cpuacct"; - ret = append_paths (&source_path, err, CGROUP_ROOT, subsystem, NULL); if (UNLIKELY (ret < 0)) return ret; @@ -545,6 +548,33 @@ show_criu_log (const char *work_path, const char *log) libcrun_error (0, "--- end of excerpt"); } +/* work_dir is the place CRIU will put its logfiles. If not explicitly set, CRIU + * will put the logfiles into the images_dir. When set, create and open it and + * hand the fd to CRIU; ownership of the returned fd stays with the caller. */ +static int +setup_criu_work_dir (libcrun_checkpoint_restore_t *cr_options, int *work_fd_out, libcrun_error_t *err) +{ + int work_fd; + + if (cr_options->work_path == NULL) + { + /* This is only for the error message later. */ + cr_options->work_path = cr_options->image_path; + return 0; + } + + if (UNLIKELY ((mkdir (cr_options->work_path, 0700) == -1) && (errno != EEXIST))) + return crun_make_error (err, errno, "error creating CRIU work directory `%s`", cr_options->work_path); + + work_fd = open (cr_options->work_path, O_DIRECTORY | O_CLOEXEC); + if (UNLIKELY (work_fd == -1)) + return crun_make_error (err, errno, "error opening CRIU work directory `%s`", cr_options->work_path); + + libcriu_wrapper->criu_set_work_dir_fd (work_fd); + *work_fd_out = work_fd; + return 0; +} + int libcrun_container_checkpoint_linux_criu (libcrun_container_status_t *status, libcrun_container_t *container, libcrun_checkpoint_restore_t *cr_options, libcrun_error_t *err) @@ -619,26 +649,9 @@ libcrun_container_checkpoint_linux_criu (libcrun_container_status_t *status, lib * and all of its children. */ libcriu_wrapper->criu_set_pid (status->pid); - /* work_dir is the place CRIU will put its logfiles. If not explicitly set, - * CRIU will put the logfiles into the images_dir from above. No need for - * crun to set it if the user has not selected a specific directory. */ - if (cr_options->work_path != NULL) - { - ret = mkdir (cr_options->work_path, 0700); - if (UNLIKELY ((ret == -1) && (errno != EEXIST))) - return crun_make_error (err, errno, "error creating CRIU work directory `%s`", cr_options->work_path); - - work_fd = open (cr_options->work_path, O_DIRECTORY | O_CLOEXEC); - if (UNLIKELY (work_fd == -1)) - return crun_make_error (err, errno, "error opening CRIU work directory `%s`", cr_options->work_path); - - libcriu_wrapper->criu_set_work_dir_fd (work_fd); - } - else - { - /* This is only for the error message later. */ - cr_options->work_path = cr_options->image_path; - } + ret = setup_criu_work_dir (cr_options, &work_fd, err); + if (UNLIKELY (ret < 0)) + return ret; # ifdef CRIU_PRE_DUMP_SUPPORT @@ -1019,26 +1032,9 @@ libcrun_container_restore_linux_criu (libcrun_container_status_t *status, libcru json_object_put (doc); } - /* work_dir is the place CRIU will put its logfiles. If not explicitly set, - * CRIU will put the logfiles into the images_dir from above. No need for - * crun to set it if the user has not selected a specific directory. */ - if (cr_options->work_path != NULL) - { - ret = mkdir (cr_options->work_path, 0700); - if (UNLIKELY ((ret == -1) && (errno != EEXIST))) - return crun_make_error (err, errno, "error creating CRIU work directory `%s`", cr_options->work_path); - - work_fd = open (cr_options->work_path, O_DIRECTORY | O_CLOEXEC); - if (UNLIKELY (work_fd == -1)) - return crun_make_error (err, errno, "error opening CRIU work directory `%s`", cr_options->work_path); - - libcriu_wrapper->criu_set_work_dir_fd (work_fd); - } - else - { - /* This is only for the error message later. */ - cr_options->work_path = cr_options->image_path; - } + ret = setup_criu_work_dir (cr_options, &work_fd, err); + if (UNLIKELY (ret < 0)) + return ret; if (cr_options->lsm_profile != NULL) { diff --git a/src/libcrun/handlers/krun.c b/src/libcrun/handlers/krun.c index 4702a1a34f..9da18849df 100644 --- a/src/libcrun/handlers/krun.c +++ b/src/libcrun/handlers/krun.c @@ -641,6 +641,18 @@ libkrun_start_passt (void *cookie, libcrun_container_t *container) return 0; } +/* Return true if the spec already declares a device with the given PATH. */ +static bool +spec_has_device (runtime_spec_schema_config_schema *def, const char *path) +{ + size_t i; + + for (i = 0; i < def->linux->devices_len; i++) + if (strcmp (def->linux->devices[i]->path, path) == 0) + return true; + return false; +} + /* libkrun_create_kvm_device: explicitly adds kvm device. */ static int libkrun_configure_container (void *cookie, enum handler_configure_phase phase, @@ -648,7 +660,6 @@ libkrun_configure_container (void *cookie, enum handler_configure_phase phase, const char *rootfs, libcrun_error_t *err) { int ret, rootfsfd; - size_t i; struct krun_config *kconf = (struct krun_config *) cookie; struct device_s kvm_device = { "/dev/kvm", "c", 10, 232, 0666, 0, 0 }; struct device_s sev_device = { "/dev/sev", "c", 10, 124, 0666, 0, 0 }; @@ -711,37 +722,14 @@ libkrun_configure_container (void *cookie, enum handler_configure_phase phase, return crun_make_error (err, errno, "start passt"); /* Do nothing if /dev/kvm is already present in spec */ - for (i = 0; i < def->linux->devices_len; i++) - { - if (strcmp (def->linux->devices[i]->path, "/dev/kvm") == 0) - return 0; - } + if (spec_has_device (def, "/dev/kvm")) + return 0; if (kconf->handle_sev != NULL) - { - create_sev = true; - for (i = 0; i < def->linux->devices_len; i++) - { - if (strcmp (def->linux->devices[i]->path, "/dev/sev") == 0) - { - create_sev = false; - break; - } - } - } + create_sev = ! spec_has_device (def, "/dev/sev"); if (kconf->handle_awsnitro != NULL) - { - create_awsnitro = true; - for (i = 0; i < def->linux->devices_len; i++) - { - if (strcmp (def->linux->devices[i]->path, "/dev/nitro_enclaves") == 0) - { - create_awsnitro = false; - break; - } - } - } + create_awsnitro = ! spec_has_device (def, "/dev/nitro_enclaves"); devfd = safe_openat (rootfsfd, rootfs, "dev", O_PATH | O_DIRECTORY | O_CLOEXEC, 0, err); if (UNLIKELY (devfd < 0)) @@ -907,6 +895,20 @@ make_oci_spec_dev (const char *type, dev_t device, bool allow, const char *acces return dev; } +/* stat an optional device: on ENOENT clear *PRESENT, any other error fails. */ +static int +stat_optional_device (const char *path, struct stat *st, bool *present, libcrun_error_t *err) +{ + int ret = stat (path, st); + if (UNLIKELY (ret < 0)) + { + if (errno != ENOENT) + return crun_make_error (err, errno, "stat `%s`", path); + *present = false; + } + return 0; +} + static int libkrun_modify_oci_configuration (void *cookie arg_unused, libcrun_context_t *context arg_unused, runtime_spec_schema_config_schema *def, @@ -921,29 +923,17 @@ libkrun_modify_oci_configuration (void *cookie arg_unused, libcrun_context_t *co /* Always allow the /dev/kvm device. */ - ret = stat ("/dev/kvm", &st_kvm); + ret = stat_optional_device ("/dev/kvm", &st_kvm, &has_kvm, err); if (UNLIKELY (ret < 0)) - { - if (errno != ENOENT) - return crun_make_error (err, errno, "stat `/dev/kvm`"); - has_kvm = false; - } + return ret; - ret = stat ("/dev/sev", &st_sev); + ret = stat_optional_device ("/dev/sev", &st_sev, &has_sev, err); if (UNLIKELY (ret < 0)) - { - if (errno != ENOENT) - return crun_make_error (err, errno, "stat `/dev/sev`"); - has_sev = false; - } + return ret; - ret = stat ("/dev/nitro_enclaves", &st_awsnitro); + ret = stat_optional_device ("/dev/nitro_enclaves", &st_awsnitro, &has_awsnitro, err); if (UNLIKELY (ret < 0)) - { - if (errno != ENOENT) - return crun_make_error (err, errno, "stat `/dev/nitro_enclaves`"); - has_awsnitro = false; - } + return ret; kconf->has_kvm = has_kvm; kconf->has_awsnitro = has_awsnitro; diff --git a/src/libcrun/json_gen_utils.h b/src/libcrun/json_gen_utils.h new file mode 100644 index 0000000000..43bb5fe119 --- /dev/null +++ b/src/libcrun/json_gen_utils.h @@ -0,0 +1,52 @@ +/* + * crun - OCI runtime written in C + * + * Copyright (C) 2024 Giuseppe Scrivano + * crun is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * crun is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with crun. If not, see . + */ +#ifndef JSON_GEN_UTILS_H +#define JSON_GEN_UTILS_H + +#include +#include +#include +#include "utils.h" + +/* Helpers to reduce the boilerplate around the json_gen_* API. Each macro + evaluates a json_gen_* call, stores the result in the local variable `r` + and jumps to the `gen_error` label when the call fails. A function using + them must declare `int r;` and provide a `gen_error:` label. */ +#define GEN_OR_FAIL(EXPR) \ + do \ + { \ + r = (EXPR); \ + if (UNLIKELY (r != json_gen_status_ok)) \ + goto gen_error; \ + } while (0) + +/* Emit a string literal (typically a map key). */ +#define GEN_KEY(GEN, LIT) GEN_OR_FAIL (json_gen_string ((GEN), (LIT), sizeof (LIT) - 1)) + +/* Emit a runtime string value. */ +#define GEN_STR(GEN, STR) GEN_OR_FAIL (json_gen_string ((GEN), (STR), strlen (STR))) + +static inline void +cleanup_json_genp (json_gen_ctx **p) +{ + if (*p) + json_gen_free (*p); +} +#define cleanup_json_gen __attribute__ ((cleanup (cleanup_json_genp))) + +#endif diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c index c9d8e140ab..f2c93de387 100644 --- a/src/libcrun/linux.c +++ b/src/libcrun/linux.c @@ -1491,6 +1491,132 @@ sysfs_userns_fallback (libcrun_container_t *container, const char *target, } } +#ifdef HAVE_FGETXATTR +/* Best-effort application of the SELinux security.selinux xattr to a + freshly mounted target when LABEL_XATTR labeling is requested. */ +static void +apply_selinux_xattr (libcrun_container_t *container, int fd, int extra_flags, const char *label) +{ + libcrun_error_t tmp_err = NULL; + int procfd; + + if ((extra_flags & LABEL_MASK) != LABEL_XATTR) + return; + + /* Best-effort: swallow any error so it does not leak into the caller. */ + procfd = get_procfd (get_private_data (container), &tmp_err); + if (procfd >= 0) + { + proc_fd_path_t proc_self_path; + cleanup_close int xfd = -1; + + get_self_fd_path (proc_self_path, fd); + xfd = openat (procfd, proc_self_path, O_RDONLY | O_CLOEXEC); + if (xfd >= 0) + (void) fsetxattr (xfd, "security.selinux", label, strlen (label), 0); + } + crun_error_release (&tmp_err); +} +#endif + +/* After a mount replaced the rootfs (target is empty), the current + targetfd sits underneath the new mount. Re-enter the mount namespace + and reopen the rootfs, refreshing both *fd and the cached rootfsfd. */ +static int +do_mount_replace_rootfs (libcrun_container_t *container, int *fd, libcrun_error_t *err) +{ + int procfd = get_procfd (get_private_data (container), err); + int tmp; + int ret; + + if (UNLIKELY (procfd < 0)) + return procfd; + + { + cleanup_close int nsfd = openat (procfd, "self/ns/mnt", O_RDONLY | O_CLOEXEC); + if (UNLIKELY (nsfd < 0)) + return crun_make_error (err, errno, "open `/proc/self/ns/mnt`"); + + ret = setns (nsfd, CLONE_NEWNS); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "setns `CLONE_NEWNS`"); + } + + close_and_reset (fd); + *fd = open (get_private_data (container)->rootfs, O_PATH | O_CLOEXEC); + if (UNLIKELY (*fd < 0)) + return crun_make_error (err, errno, "reopen rootfs after mount on /"); + + tmp = dup (*fd); + if (UNLIKELY (tmp < 0)) + return crun_make_error (err, errno, "dup"); + + TEMP_FAILURE_RETRY (close (get_private_data (container)->rootfsfd)); + get_private_data (container)->rootfsfd = tmp; + + return 0; +} + +/* Apply the propagation flags (MS_SHARED, MS_SLAVE, ...) to a mount, + preferring mount_setattr when a target fd is available and falling + back to mount(2). */ +static int +apply_propagation_flags (const char *target, int targetfd, const char *real_target, + unsigned long mountflags, libcrun_error_t *err) +{ + bool propagation_done = false; + int ret; + + if (targetfd >= 0) + { + libcrun_error_t tmp_err = NULL; + ret = do_mount_setattr (false, target, targetfd, 0, mountflags & ALL_PROPAGATIONS, &tmp_err); + if (LIKELY (ret == 0)) + propagation_done = true; + else + crun_error_release (&tmp_err); + } + + if (! propagation_done) + { + ret = mount (NULL, real_target, NULL, mountflags & ALL_PROPAGATIONS, NULL); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "set propagation for `%s`", target); + } + + return 0; +} + +/* Perform (or defer) the remount needed to apply bind/read-only/single + instance flags. A read-write remount and MOUNT_NO_DEFERRED_REMOUNT are + done immediately; otherwise the remount is queued to run later. *fd is + consumed when the remount is deferred. */ +static int +schedule_or_do_remount (libcrun_container_t *container, int *fd, int targetfd, + const char *target, const char *real_target, + bool single_instance, unsigned long mountflags, + const void *data, int extra_flags, libcrun_error_t *err) +{ + unsigned long remount_flags = MS_REMOUNT | (single_instance ? 0 : MS_BIND) | (mountflags & ~ALL_PROPAGATIONS); + struct remount_s *r; + + if ((remount_flags & MS_RDONLY) == 0 || (extra_flags & MOUNT_NO_DEFERRED_REMOUNT)) + return do_remount (*fd >= 0 ? *fd : targetfd, real_target, remount_flags, data, err); + + if (*fd < 0) + { + *fd = dup (targetfd); + if (UNLIKELY (*fd < 0)) + return crun_make_error (err, errno, "dup `%d`", targetfd); + } + + /* The remount owns the fd. */ + r = make_remount (get_and_reset (fd), target, remount_flags, data, get_private_data (container)->remounts); + get_private_data (container)->remounts = r; + + return 0; +} + static int do_mount (libcrun_container_t *container, const char *source, int targetfd, const char *target, const char *fstype, unsigned long mountflags, const void *data, @@ -1635,49 +1761,13 @@ do_mount (libcrun_container_t *container, const char *source, int targetfd, /* We are replacing the rootfs, reopen it. */ if (is_empty_string (target)) { - int procfd = get_procfd (get_private_data (container), err); - int tmp; - if (UNLIKELY (procfd < 0)) - return procfd; - - { - cleanup_close int nsfd = openat (procfd, "self/ns/mnt", O_RDONLY | O_CLOEXEC); - if (UNLIKELY (nsfd < 0)) - return crun_make_error (err, errno, "open `/proc/self/ns/mnt`"); - - ret = setns (nsfd, CLONE_NEWNS); - if (UNLIKELY (ret < 0)) - return crun_make_error (err, errno, "setns `CLONE_NEWNS`"); - } - - close_and_reset (&fd); - fd = open (get_private_data (container)->rootfs, O_PATH | O_CLOEXEC); - if (UNLIKELY (fd < 0)) - return crun_make_error (err, errno, "reopen rootfs after mount on /"); - - tmp = dup (fd); - if (UNLIKELY (tmp < 0)) - return crun_make_error (err, errno, "dup"); - - TEMP_FAILURE_RETRY (close (get_private_data (container)->rootfsfd)); - get_private_data (container)->rootfsfd = tmp; + ret = do_mount_replace_rootfs (container, &fd, err); + if (UNLIKELY (ret < 0)) + return ret; } #ifdef HAVE_FGETXATTR - if ((extra_flags & LABEL_MASK) == LABEL_XATTR) - { - int procfd = get_procfd (get_private_data (container), err); - if (procfd >= 0) - { - proc_fd_path_t proc_self_path; - cleanup_close int xfd = -1; - - get_self_fd_path (proc_self_path, fd); - xfd = openat (procfd, proc_self_path, O_RDONLY | O_CLOEXEC); - if (xfd >= 0) - (void) fsetxattr (xfd, "security.selinux", label, strlen (label), 0); - } - } + apply_selinux_xattr (container, fd, extra_flags, label); #endif targetfd = fd; @@ -1694,24 +1784,9 @@ do_mount (libcrun_container_t *container, const char *source, int targetfd, if (mountflags & ALL_PROPAGATIONS_NO_REC) { - bool propagation_done = false; - - if (targetfd >= 0) - { - libcrun_error_t tmp_err = NULL; - ret = do_mount_setattr (false, target, targetfd, 0, mountflags & ALL_PROPAGATIONS, &tmp_err); - if (LIKELY (ret == 0)) - propagation_done = true; - else - crun_error_release (&tmp_err); - } - - if (! propagation_done) - { - ret = mount (NULL, real_target, NULL, mountflags & ALL_PROPAGATIONS, NULL); - if (UNLIKELY (ret < 0)) - return crun_make_error (err, errno, "set propagation for `%s`", target); - } + ret = apply_propagation_flags (target, targetfd, real_target, mountflags, err); + if (UNLIKELY (ret < 0)) + return ret; } if (mountflags & (MS_BIND | MS_RDONLY)) @@ -1725,34 +1800,10 @@ do_mount (libcrun_container_t *container, const char *source, int targetfd, if (needs_remount) { - unsigned long remount_flags = MS_REMOUNT | (single_instance ? 0 : MS_BIND) | (mountflags & ~ALL_PROPAGATIONS); - - if ((remount_flags & MS_RDONLY) == 0) - { - ret = do_remount (fd >= 0 ? fd : targetfd, real_target, remount_flags, data, err); - if (UNLIKELY (ret < 0)) - return ret; - } - else if (extra_flags & MOUNT_NO_DEFERRED_REMOUNT) - { - ret = do_remount (fd >= 0 ? fd : targetfd, real_target, remount_flags, data, err); - if (UNLIKELY (ret < 0)) - return ret; - } - else - { - struct remount_s *r; - if (fd < 0) - { - fd = dup (targetfd); - if (UNLIKELY (fd < 0)) - return crun_make_error (err, errno, "dup `%d`", targetfd); - } - - /* The remount owns the fd. */ - r = make_remount (get_and_reset (&fd), target, remount_flags, data, get_private_data (container)->remounts); - get_private_data (container)->remounts = r; - } + ret = schedule_or_do_remount (container, &fd, targetfd, target, real_target, + single_instance, mountflags, data, extra_flags, err); + if (UNLIKELY (ret < 0)) + return ret; } return ret; @@ -2035,6 +2086,44 @@ struct device_s needed_devs[] = { { "/dev/null", "c", 1, 3, 0666, 0, 0 }, { "/dev/urandom", "c", 1, 9, 0666, 0, 0 }, {} }; +/* Pre-open (via open_tree) the needed device fds and the notify socket + while the host file system is still reachable. Failures are ignored: + the fds are optional optimizations and a later mount(2) fallback + handles the missing entries. */ +static void +preopen_needed_devs_and_notify (libcrun_container_t *container) +{ + struct private_data_s *private_data = get_private_data (container); + size_t i; + + if (private_data->needed_devs_fds) + { + struct libcrun_fd_map *dev_fds = private_data->needed_devs_fds; + + for (i = 0; needed_devs[i].path; i++) + { + if (i < dev_fds->nfds && dev_fds->fds[i] >= 0) + continue; + + int fd = syscall_open_tree (AT_FDCWD, needed_devs[i].path, + OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); + if (fd >= 0 && i < dev_fds->nfds) + dev_fds->fds[i] = fd; + else if (fd >= 0) + close (fd); + } + } + + if (private_data->notify_socket_tree_fd < 0 + && private_data->host_notify_socket_path) + { + int fd = syscall_open_tree (AT_FDCWD, private_data->host_notify_socket_path, + OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC); + if (fd >= 0) + private_data->notify_socket_tree_fd = fd; + } +} + /* Check if the specified path is a direct child of /dev. If it is return a pointer to the basename. */ static const char * @@ -3735,34 +3824,7 @@ setup_mount_namespace (libcrun_container_t *container, bool no_pivot, char **roo below since open_tree needs CAP_SYS_ADMIN in the user namespace that owns the mount namespace. */ if (tree_fd >= 0) - { - if (get_private_data (container)->needed_devs_fds) - { - struct libcrun_fd_map *dev_fds = get_private_data (container)->needed_devs_fds; - - for (i = 0; needed_devs[i].path; i++) - { - if (i < dev_fds->nfds && dev_fds->fds[i] >= 0) - continue; - - int fd = syscall_open_tree (AT_FDCWD, needed_devs[i].path, - OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); - if (fd >= 0 && i < dev_fds->nfds) - dev_fds->fds[i] = fd; - else if (fd >= 0) - close (fd); - } - } - - if (get_private_data (container)->notify_socket_tree_fd < 0 - && get_private_data (container)->host_notify_socket_path) - { - int fd = syscall_open_tree (AT_FDCWD, get_private_data (container)->host_notify_socket_path, - OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC); - if (fd >= 0) - get_private_data (container)->notify_socket_tree_fd = fd; - } - } + preopen_needed_devs_and_notify (container); if (tree_fd >= 0) { @@ -3853,34 +3915,7 @@ setup_mount_namespace (libcrun_container_t *container, bool no_pivot, char **roo get_private_data (container)->rootfs = NULL; if (tree_fd < 0) - { - if (get_private_data (container)->needed_devs_fds) - { - struct libcrun_fd_map *dev_fds = get_private_data (container)->needed_devs_fds; - - for (i = 0; needed_devs[i].path; i++) - { - if (i < dev_fds->nfds && dev_fds->fds[i] >= 0) - continue; - - int fd = syscall_open_tree (AT_FDCWD, needed_devs[i].path, - OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); - if (fd >= 0 && i < dev_fds->nfds) - dev_fds->fds[i] = fd; - else if (fd >= 0) - close (fd); - } - } - - if (get_private_data (container)->notify_socket_tree_fd < 0 - && get_private_data (container)->host_notify_socket_path) - { - int fd = syscall_open_tree (AT_FDCWD, get_private_data (container)->host_notify_socket_path, - OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC); - if (fd >= 0) - get_private_data (container)->notify_socket_tree_fd = fd; - } - } + preopen_needed_devs_and_notify (container); get_old_root_fd (get_private_data (container)); @@ -4140,6 +4175,63 @@ is_single_mapping (runtime_spec_schema_defs_id_mapping **mappings, size_t len, return 1; } +static int +write_id_map (libcrun_container_t *container, pid_t pid, const char *map_name, + int (*idmap_helper) (pid_t, const char *, libcrun_error_t *), const char *helper_name, + bool warn_on_failure, const char *map, size_t map_len, + runtime_spec_schema_defs_id_mapping **mappings, size_t mappings_len, + uint32_t host_id, uint32_t container_id, bool skip_if_setgroups_denied, + libcrun_error_t *err) +{ + int ret = 0; + + if (container->host_uid) + ret = idmap_helper (pid, map, err); + if (container->host_uid == 0 || ret < 0) + { + cleanup_close int fd = -1; + + if (ret < 0) + { + if (warn_on_failure) + libcrun_warning ("unable to invoke `%s`, will try creating a user namespace with single mapping as an alternative", helper_name); + crun_error_release (err); + } + + fd = libcrun_open_proc_pid_file (container, pid, map_name, O_WRONLY, err); + if (UNLIKELY (fd < 0)) + return fd; + + ret = safe_write (fd, map_name, map, map_len, err); + if (ret < 0 && (! mappings_len || is_single_mapping (mappings, mappings_len, host_id, container_id))) + { + size_t single_mapping_len; + cleanup_free char *single_mapping = NULL; + crun_error_release (err); + + if (! skip_if_setgroups_denied || ! get_private_data (container)->deny_setgroups) + { + ret = deny_setgroups (container, pid, err); + if (UNLIKELY (ret < 0)) + return ret; + } + + ret = format_mount_mapping (&single_mapping, container_id, host_id, 1, &single_mapping_len, err); + if (UNLIKELY (ret < 0)) + return ret; + + close_and_reset (&fd); + + fd = libcrun_open_proc_pid_file (container, pid, map_name, O_WRONLY, err); + if (UNLIKELY (fd < 0)) + return fd; + + ret = safe_write (fd, map_name, single_mapping, single_mapping_len, err); + } + } + return ret; +} + int libcrun_set_usernamespace (libcrun_container_t *container, pid_t pid, libcrun_error_t *err) { @@ -4197,94 +4289,17 @@ libcrun_set_usernamespace (libcrun_container_t *container, pid_t pid, libcrun_er } } - if (container->host_uid) - ret = newgidmap (pid, gid_map, err); - if (container->host_uid == 0 || ret < 0) - { - if (ret < 0) - { - if (! def->linux->uid_mappings_len) - libcrun_warning ("unable to invoke `newgidmap`, will try creating a user namespace with single mapping as an alternative"); - crun_error_release (err); - } - - cleanup_close int gid_fd = -1; - - gid_fd = libcrun_open_proc_pid_file (container, pid, "gid_map", O_WRONLY, err); - if (UNLIKELY (gid_fd < 0)) - return gid_fd; - - ret = safe_write (gid_fd, "gid_map", gid_map, gid_map_len, err); - if (ret < 0 && (! def->linux->gid_mappings_len || is_single_mapping (def->linux->gid_mappings, def->linux->gid_mappings_len, container->host_gid, container->container_gid))) - { - size_t single_mapping_len; - cleanup_free char *single_mapping = NULL; - crun_error_release (err); - - ret = deny_setgroups (container, pid, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = format_mount_mapping (&single_mapping, container->container_gid, container->host_gid, 1, &single_mapping_len, err); - if (UNLIKELY (ret < 0)) - return ret; - - close_and_reset (&gid_fd); - - gid_fd = libcrun_open_proc_pid_file (container, pid, "gid_map", O_WRONLY, err); - if (UNLIKELY (gid_fd < 0)) - return gid_fd; - - ret = safe_write (gid_fd, "gid_map", single_mapping, single_mapping_len, err); - } - } + ret = write_id_map (container, pid, "gid_map", newgidmap, "newgidmap", + ! def->linux->uid_mappings_len, gid_map, gid_map_len, + def->linux->gid_mappings, def->linux->gid_mappings_len, + container->host_gid, container->container_gid, /* skip_if_setgroups_denied */ false, err); if (UNLIKELY (ret < 0)) return ret; - if (container->host_uid) - ret = newuidmap (pid, uid_map, err); - if (container->host_uid == 0 || ret < 0) - { - if (ret < 0) - { - if (! def->linux->uid_mappings_len) - libcrun_warning ("unable to invoke `newuidmap`, will try creating a user namespace with single mapping as an alternative"); - crun_error_release (err); - } - - cleanup_close int uid_fd = -1; - - uid_fd = libcrun_open_proc_pid_file (container, pid, "uid_map", O_WRONLY, err); - if (UNLIKELY (uid_fd < 0)) - return uid_fd; - - ret = safe_write (uid_fd, "uid_map", uid_map, uid_map_len, err); - if (ret < 0 && (! def->linux->uid_mappings_len || is_single_mapping (def->linux->uid_mappings, def->linux->uid_mappings_len, container->host_uid, container->container_uid))) - { - size_t single_mapping_len; - cleanup_free char *single_mapping = NULL; - crun_error_release (err); - - if (! get_private_data (container)->deny_setgroups) - { - ret = deny_setgroups (container, pid, err); - if (UNLIKELY (ret < 0)) - return ret; - } - - ret = format_mount_mapping (&single_mapping, container->container_uid, container->host_uid, 1, &single_mapping_len, err); - if (UNLIKELY (ret < 0)) - return ret; - - close_and_reset (&uid_fd); - - uid_fd = libcrun_open_proc_pid_file (container, pid, "uid_map", O_WRONLY, err); - if (UNLIKELY (uid_fd < 0)) - return uid_fd; - - ret = safe_write (uid_fd, "uid_map", single_mapping, single_mapping_len, err); - } - } + ret = write_id_map (container, pid, "uid_map", newuidmap, "newuidmap", + ! def->linux->uid_mappings_len, uid_map, uid_map_len, + def->linux->uid_mappings, def->linux->uid_mappings_len, + container->host_uid, container->container_uid, /* skip_if_setgroups_denied */ true, err); if (UNLIKELY (ret < 0)) return ret; diff --git a/src/libcrun/seccomp.c b/src/libcrun/seccomp.c index be293a39f4..2be5514004 100644 --- a/src/libcrun/seccomp.c +++ b/src/libcrun/seccomp.c @@ -579,6 +579,29 @@ evict_cache (int root_dfd, libcrun_error_t *err) return 0; } +/* Open the run-directory dirfd and build the "/seccomp.bpf" path + relative to it. Returns the (caller-owned) dirfd or a negative error. */ +static int +open_seccomp_bpf_dirfd (libcrun_container_t *container, char **rel_path, libcrun_error_t *err) +{ + int ret; + int dirfd; + + dirfd = open_rundir_dirfd (container->context ? container->context->state_root : NULL, err); + if (UNLIKELY (dirfd < 0)) + return dirfd; + + /* relative path to dirfd. */ + ret = append_paths (rel_path, err, container->context->id, "seccomp.bpf", NULL); + if (UNLIKELY (ret < 0)) + { + TEMP_FAILURE_RETRY (close (dirfd)); + return ret; + } + + return dirfd; +} + static int store_seccomp_cache (struct libcrun_seccomp_gen_ctx_s *ctx, libcrun_error_t *err) { @@ -594,15 +617,10 @@ store_seccomp_cache (struct libcrun_seccomp_gen_ctx_s *ctx, libcrun_error_t *err if (is_empty_string (ctx->checksum)) return 0; - dirfd = open_rundir_dirfd ((container->context ? container->context->state_root : NULL), err); + dirfd = open_seccomp_bpf_dirfd (container, &src_path, err); if (UNLIKELY (dirfd < 0)) return dirfd; - /* relative path to dirfd. */ - ret = append_paths (&src_path, err, container->context->id, "seccomp.bpf", NULL); - if (UNLIKELY (ret < 0)) - return ret; - ret = append_paths (&dest_path, err, SECCOMP_CACHE_DIR, ctx->checksum, NULL); if (UNLIKELY (ret < 0)) return ret; @@ -879,15 +897,10 @@ libcrun_open_seccomp_bpf (struct libcrun_seccomp_gen_ctx_s *ctx, int *fd, libcru if (container == NULL || container->context == NULL) return crun_make_error (err, EINVAL, "invalid internal state"); - dirfd = open_rundir_dirfd ((container->context ? container->context->state_root : NULL), err); + dirfd = open_seccomp_bpf_dirfd (container, &dest_path, err); if (UNLIKELY (dirfd < 0)) return dirfd; - /* relative path to dirfd. */ - ret = append_paths (&dest_path, err, container->context->id, "seccomp.bpf", NULL); - if (UNLIKELY (ret < 0)) - return ret; - if (ctx->create) { bool created = false; diff --git a/src/libcrun/status.c b/src/libcrun/status.c index 2be1649927..b8a6286e6f 100644 --- a/src/libcrun/status.c +++ b/src/libcrun/status.c @@ -20,6 +20,7 @@ #include #include "status.h" #include "utils.h" +#include "json_gen_utils.h" #include #include #include @@ -152,25 +153,14 @@ libcrun_get_state_directory (char **out, const char *state_root, const char *id, static int get_state_directory_status_file (char **out, const char *state_root, const char *id, libcrun_error_t *err) { - cleanup_free char *root = NULL; - cleanup_free char *path = NULL; + cleanup_free char *dir = NULL; int ret; - ret = validate_id (id, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = get_run_directory (&root, state_root, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = append_paths (&path, err, root, id, "status", NULL); + ret = libcrun_get_state_directory (&dir, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; - STEAL_POINTER (out, path); - - return 0; + return append_paths (out, err, dir, "status", NULL); } static int @@ -250,7 +240,7 @@ libcrun_write_container_status (const char *state_root, const char *id, libcrun_ const char *buf = NULL; struct pid_stat st; const char *tmp; - json_gen_ctx *gen = NULL; + cleanup_json_gen json_gen_ctx *gen = NULL; ret = get_state_directory_status_file (&file, state_root, id, err); if (UNLIKELY (ret < 0)) @@ -272,133 +262,62 @@ libcrun_write_container_status (const char *state_root, const char *id, libcrun_ json_gen_config (gen, json_gen_beautify, 1); - r = json_gen_map_open (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_map_open (gen)); - r = json_gen_string (gen, "pid", strlen ("pid")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "pid"); + GEN_OR_FAIL (map_int (gen, status->pid)); - r = map_int (gen, status->pid); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "process-start-time", strlen ("process-start-time")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = map_uint (gen, status->process_start_time); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "cgroup-path", strlen ("cgroup-path")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "process-start-time"); + GEN_OR_FAIL (map_uint (gen, status->process_start_time)); + GEN_KEY (gen, "cgroup-path"); tmp = status->cgroup_path ? status->cgroup_path : ""; - r = json_gen_string (gen, tmp, strlen (tmp)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "scope", strlen ("scope")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_STR (gen, tmp); + GEN_KEY (gen, "scope"); tmp = status->scope ? status->scope : ""; - r = json_gen_string (gen, tmp, strlen (tmp)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "rootfs", strlen ("rootfs")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, status->rootfs, strlen (status->rootfs)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "systemd-cgroup", strlen ("systemd-cgroup")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_STR (gen, tmp); - r = json_gen_bool (gen, status->systemd_cgroup); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "rootfs"); + GEN_STR (gen, status->rootfs); - r = json_gen_string (gen, "bundle", strlen ("bundle")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "systemd-cgroup"); + GEN_OR_FAIL (json_gen_bool (gen, status->systemd_cgroup)); - r = json_gen_string (gen, status->bundle, strlen (status->bundle)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "bundle"); + GEN_STR (gen, status->bundle); - r = json_gen_string (gen, "created", strlen ("created")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, status->created, strlen (status->created)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "created"); + GEN_STR (gen, status->created); if (status->owner) { - r = json_gen_string (gen, "owner", strlen ("owner")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, status->owner, strlen (status->owner)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "owner"); + GEN_STR (gen, status->owner); } - r = json_gen_string (gen, "detached", strlen ("detached")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_bool (gen, status->detached); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; - - r = json_gen_string (gen, "external_descriptors", strlen ("external_descriptors")); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "detached"); + GEN_OR_FAIL (json_gen_bool (gen, status->detached)); - r = json_gen_string (gen, status->external_descriptors, strlen (status->external_descriptors)); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_KEY (gen, "external_descriptors"); + GEN_STR (gen, status->external_descriptors); - r = json_gen_map_close (gen); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_map_close (gen)); - r = json_gen_get_buf (gen, &buf, &len); - if (UNLIKELY (r != json_gen_status_ok)) - goto gen_error; + GEN_OR_FAIL (json_gen_get_buf (gen, &buf, &len)); ret = safe_write (fd_write, "status file", buf, len, err); if (UNLIKELY (ret < 0)) - goto exit; + return ret; close_and_reset (&fd_write); if (UNLIKELY (rename (file_tmp, file) < 0)) - { - ret = crun_make_error (err, errno, "cannot rename status file"); - goto exit; - } + return crun_make_error (err, errno, "cannot rename status file"); -exit: - if (gen) - json_gen_free (gen); - - return ret; + return 0; gen_error: - if (gen) - json_gen_free (gen); - return json_gen_error_to_crun_error (r, err); } @@ -812,18 +731,26 @@ libcrun_is_container_running (libcrun_container_status_t *status, libcrun_error_ return 0; /* stopped */ } -int -libcrun_status_create_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err) +static int +get_exec_fifo_path (char **out, const char *state_root, const char *id, libcrun_error_t *err) { cleanup_free char *state_dir = NULL; - cleanup_free char *fifo_path = NULL; - int ret, fd = -1; + int ret; ret = libcrun_get_state_directory (&state_dir, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; - ret = append_paths (&fifo_path, err, state_dir, "exec.fifo", NULL); + return append_paths (out, err, state_dir, "exec.fifo", NULL); +} + +int +libcrun_status_create_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err) +{ + cleanup_free char *fifo_path = NULL; + int ret, fd = -1; + + ret = get_exec_fifo_path (&fifo_path, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; @@ -842,7 +769,6 @@ libcrun_status_create_exec_fifo (const char *state_root, const char *id, libcrun int libcrun_status_write_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err) { - cleanup_free char *state_dir = NULL; cleanup_free char *fifo_path = NULL; char buffer[1] = { 0, @@ -850,11 +776,7 @@ libcrun_status_write_exec_fifo (const char *state_root, const char *id, libcrun_ cleanup_close int fd = -1; int ret; - ret = libcrun_get_state_directory (&state_dir, state_root, id, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = append_paths (&fifo_path, err, state_dir, "exec.fifo", NULL); + ret = get_exec_fifo_path (&fifo_path, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; @@ -876,15 +798,10 @@ libcrun_status_write_exec_fifo (const char *state_root, const char *id, libcrun_ int libcrun_status_has_read_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err) { - cleanup_free char *state_dir = NULL; cleanup_free char *fifo_path = NULL; int ret; - ret = libcrun_get_state_directory (&state_dir, state_root, id, err); - if (UNLIKELY (ret < 0)) - return ret; - - ret = append_paths (&fifo_path, err, state_dir, "exec.fifo", NULL); + ret = get_exec_fifo_path (&fifo_path, state_root, id, err); if (UNLIKELY (ret < 0)) return ret; diff --git a/src/run.c b/src/run.c index a5f183dc36..0da1b4427a 100644 --- a/src/run.c +++ b/src/run.c @@ -87,7 +87,7 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OPTION_PRESERVE_FDS: - crun_context.preserve_fds = parse_int_or_fail (argp_mandatory_argument (arg, state), "preserve-fds"); + crun_context.preserve_fds = parse_id_or_fail (argp_mandatory_argument (arg, state), NULL, "preserve-fds"); break; case OPTION_NO_SUBREAPER: diff --git a/src/run_create.c b/src/run_create.c index f795a97d8b..736c7e606e 100644 --- a/src/run_create.c +++ b/src/run_create.c @@ -96,7 +96,7 @@ crun_run_create_internal (struct crun_global_arguments *global_args, int argc, c crun_context->bundle = bundle; if (getenv ("LISTEN_FDS")) { - crun_context->listen_fds = parse_int_or_fail (getenv ("LISTEN_FDS"), "LISTEN_FDS"); + crun_context->listen_fds = parse_id_or_fail (getenv ("LISTEN_FDS"), NULL, "LISTEN_FDS"); crun_context->preserve_fds += crun_context->listen_fds; } return container_run_create_func (crun_context, container, options, err);