Projects
Mega:23.09
lxc
_service:tar_scm:0001-iSulad-add-json-files-and...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:0001-iSulad-add-json-files-and-adapt-to-meson.patch of Package lxc
From 757bc66c44a58ca2d65eb4c8199ad55cba580d00 Mon Sep 17 00:00:00 2001 From: haozi007 <liuhao27@huawei.com> Date: Fri, 14 Jul 2023 11:21:56 +0800 Subject: [PATCH 1/3] [iSulad] add json files and adapt to meson Signed-off-by: haozi007 <liuhao27@huawei.com> --- meson.build | 30 +- meson_options.txt | 8 + src/lxc/af_unix.c | 50 + src/lxc/af_unix.h | 5 + src/lxc/cgroups/isulad_cgfsng.c | 3229 ++++++++++++++++++++++++++++++ src/lxc/commands.c | 179 ++ src/lxc/commands.h | 10 + src/lxc/commands_utils.c | 57 + src/lxc/commands_utils.h | 7 + src/lxc/exec_commands.c | 477 +++++ src/lxc/exec_commands.h | 77 + src/lxc/isulad_utils.c | 535 +++++ src/lxc/isulad_utils.h | 102 + src/lxc/json/defs.c | 205 ++ src/lxc/json/defs.h | 37 + src/lxc/json/json_common.c | 1153 +++++++++++ src/lxc/json/json_common.h | 194 ++ src/lxc/json/logger_json_file.c | 246 +++ src/lxc/json/logger_json_file.h | 45 + src/lxc/json/oci_runtime_hooks.c | 52 + src/lxc/json/oci_runtime_hooks.h | 15 + src/lxc/json/oci_runtime_spec.c | 195 ++ src/lxc/json/oci_runtime_spec.h | 37 + src/lxc/json/read-file.c | 95 + src/lxc/json/read-file.h | 11 + src/lxc/meson.build | 26 +- src/lxc/path.c | 521 +++++ src/lxc/path.h | 33 + 28 files changed, 7629 insertions(+), 2 deletions(-) create mode 100644 src/lxc/cgroups/isulad_cgfsng.c create mode 100644 src/lxc/exec_commands.c create mode 100644 src/lxc/exec_commands.h create mode 100644 src/lxc/isulad_utils.c create mode 100644 src/lxc/isulad_utils.h create mode 100644 src/lxc/json/defs.c create mode 100644 src/lxc/json/defs.h create mode 100755 src/lxc/json/json_common.c create mode 100755 src/lxc/json/json_common.h create mode 100644 src/lxc/json/logger_json_file.c create mode 100644 src/lxc/json/logger_json_file.h create mode 100644 src/lxc/json/oci_runtime_hooks.c create mode 100644 src/lxc/json/oci_runtime_hooks.h create mode 100644 src/lxc/json/oci_runtime_spec.c create mode 100644 src/lxc/json/oci_runtime_spec.h create mode 100644 src/lxc/json/read-file.c create mode 100644 src/lxc/json/read-file.h create mode 100644 src/lxc/path.c create mode 100644 src/lxc/path.h diff --git a/meson.build b/meson.build index 1b2d673..fda8045 100644 --- a/meson.build +++ b/meson.build @@ -163,6 +163,8 @@ want_seccomp = get_option('seccomp') want_thread_safety = get_option('thread-safety') want_memfd_rexec = get_option('memfd-rexec') want_sd_bus = get_option('sd-bus') +want_isulad = get_option('isulad') +want_securecomplie = get_option('securecomplie') srcconf.set_quoted('DEFAULT_CGROUP_PATTERN', cgrouppattern) if coverity @@ -228,6 +230,19 @@ possible_link_flags = [ '-fstack-protector-strong', ] +if want_isulad + possible_cc_flags += ['-D_FORTIFY_SOURCE=2'] + yajldep = dependency('yajl', version : '>=2') + srcconf.set('HAVE_ISULAD', yajldep.found()) + liblxc_dependencies += yajldep +else + srcconf.set('HAVE_ISULAD', false) +endif + +if want_isulad or want_securecomplie + possible_link_flags += ['-Wl,-z,noexecstack'] +endif + # The gold linker fails with a bus error on sparc64 if build_machine.cpu_family() != 'sparc64' possible_link_flags += '-Wl,-fuse-ld=gold' @@ -881,13 +896,24 @@ template_scripts = [] test_programs = [] # Includes. +if want_isulad liblxc_includes = include_directories( '.', 'src', 'src/include', 'src/lxc', + 'src/lxc/json', 'src/lxc/cgroups', 'src/lxc/storage') +else +liblxc_includes = include_directories( + '.', + 'src', + 'src/include', + 'src/lxc', + 'src/lxc/cgroups', + 'src/lxc/storage') +endif # Our static sub-project binaries don't (and in fact can't) link to our # dependencies directly, but need access to the headers when compiling (most @@ -942,7 +968,9 @@ subdir('doc/ja') subdir('doc/ko') subdir('doc/examples') subdir('doc/rootfs') -subdir('hooks') +if not want_isulad + subdir('hooks') +endif if want_commands subdir('src/lxc/cmd') endif diff --git a/meson_options.txt b/meson_options.txt index 9803473..7146b17 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -120,3 +120,11 @@ option('memfd-rexec', type : 'boolean', value : 'true', option('distrosysconfdir', type : 'string', value: '', description: 'relative path to sysconfdir for distro default configuration') + +# was --{disable,enable}-isulad in autotools +option('isulad', type: 'boolean', value: 'true', + description: 'enable adapt to iSulad') + +# was --{disable,enable}-securecomplie in autotools +option('securecomplie', type: 'boolean', value: 'true', + description: 'enable secure compile') \ No newline at end of file diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 6f05b70..6db1864 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -516,7 +516,11 @@ int lxc_unix_connect_type(struct sockaddr_un *addr, int type) ret = connect(fd, (struct sockaddr *)addr, offsetof(struct sockaddr_un, sun_path) + len); if (ret < 0) +#ifdef HAVE_ISULAD + return syswarn("Failed to connect AF_UNIX socket"); +#else return syserror("Failed to connect AF_UNIX socket"); +#endif return move_fd(fd); } @@ -545,3 +549,49 @@ int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout) return 0; } + +#ifdef HAVE_ISULAD +int lxc_named_unix_open(const char *path, int type, int flags) +{ + __do_close int fd = -EBADF; + int ret; + ssize_t len; + struct sockaddr_un addr; + + fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + if (!path) + return move_fd(fd); + + len = lxc_unix_sockaddr(&addr, path); + if (len < 0) + return -1; + + ret = bind(fd, (struct sockaddr *)&addr, len); + if (ret < 0) + return -1; + + if (chmod(path, 0600) < 0) + return -1; + + if (type == SOCK_STREAM) { + ret = listen(fd, 100); + if (ret < 0) + return -1; + } + + return move_fd(fd); +} + +int lxc_named_unix_connect(const char *path) +{ + struct sockaddr_un addr; + + if (lxc_unix_sockaddr(&addr, path) < 0) + return -1; + + return lxc_unix_connect_type(&addr, SOCK_STREAM); +} +#endif diff --git a/src/lxc/af_unix.h b/src/lxc/af_unix.h index ba119cf..605afc2 100644 --- a/src/lxc/af_unix.h +++ b/src/lxc/af_unix.h @@ -168,4 +168,9 @@ static inline void put_unix_fds(struct unix_fds *fds) } define_cleanup_function(struct unix_fds *, put_unix_fds); +#ifdef HAVE_ISULAD +__hidden extern int lxc_named_unix_open(const char *path, int type, int flags); +__hidden extern int lxc_named_unix_connect(const char *path); +#endif + #endif /* __LXC_AF_UNIX_H */ diff --git a/src/lxc/cgroups/isulad_cgfsng.c b/src/lxc/cgroups/isulad_cgfsng.c new file mode 100644 index 0000000..dcaa229 --- /dev/null +++ b/src/lxc/cgroups/isulad_cgfsng.c @@ -0,0 +1,3229 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * Author: lifeng + * Create: 2020-11-02 + * Description: provide container definition + * lxc: linux Container library + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + ******************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <grp.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <poll.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "af_unix.h" +#include "caps.h" +#include "cgroup.h" +#include "cgroup2_devices.h" +#include "cgroup_utils.h" +#include "commands.h" +#include "conf.h" +#include "config.h" +#include "log.h" +#include "macro.h" +#include "mainloop.h" +#include "memory_utils.h" +#include "storage/storage.h" +#include "utils.h" + +#ifndef HAVE_STRLCPY +#include "include/strlcpy.h" +#endif + +#ifndef HAVE_STRLCAT +#include "include/strlcat.h" +#endif + +lxc_log_define(isulad_cgfsng, cgroup); + +/* Given a pointer to a null-terminated array of pointers, realloc to add one + * entry, and point the new entry to NULL. Do not fail. Return the index to the + * second-to-last entry - that is, the one which is now available for use + * (keeping the list null-terminated). + */ +static int append_null_to_list(void ***list) +{ + int newentry = 0; + + if (*list) + for (; (*list)[newentry]; newentry++) + ; + + *list = must_realloc(*list, (newentry + 2) * sizeof(void **)); + (*list)[newentry + 1] = NULL; + return newentry; +} + +/* Given a null-terminated array of strings, check whether @entry is one of the + * strings. + */ +static bool string_in_list(char **list, const char *entry) +{ + if (!list) + return false; + + for (int i = 0; list[i]; i++) + if (strcmp(list[i], entry) == 0) + return true; + + return false; +} + +/* Return a copy of @entry prepending "name=", i.e. turn "systemd" into + * "name=systemd". Do not fail. + */ +static char *cg_legacy_must_prefix_named(char *entry) +{ + size_t len; + char *prefixed; + + len = strlen(entry); + prefixed = must_realloc(NULL, len + 6); + + memcpy(prefixed, "name=", STRLITERALLEN("name=")); + memcpy(prefixed + STRLITERALLEN("name="), entry, len); + prefixed[len + 5] = '\0'; + + return prefixed; +} + +/* Append an entry to the clist. Do not fail. @clist must be NULL the first time + * we are called. + * + * We also handle named subsystems here. Any controller which is not a kernel + * subsystem, we prefix "name=". Any which is both a kernel and named subsystem, + * we refuse to use because we're not sure which we have here. + * (TODO: We could work around this in some cases by just remounting to be + * unambiguous, or by comparing mountpoint contents with current cgroup.) + * + * The last entry will always be NULL. + */ +static void must_append_controller(char **klist, char **nlist, char ***clist, + char *entry) +{ + int newentry; + char *copy; + + if (string_in_list(klist, entry) && string_in_list(nlist, entry)) { + ERROR("Refusing to use ambiguous controller \"%s\"", entry); + ERROR("It is both a named and kernel subsystem"); + return; + } + + newentry = append_null_to_list((void ***)clist); + + if (strncmp(entry, "name=", 5) == 0) + copy = must_copy_string(entry); + else if (string_in_list(klist, entry)) + copy = must_copy_string(entry); + else + copy = cg_legacy_must_prefix_named(entry); + + (*clist)[newentry] = copy; +} + +/* Given a handler's cgroup data, return the struct hierarchy for the controller + * @c, or NULL if there is none. + */ +struct hierarchy *get_hierarchy(struct cgroup_ops *ops, const char *controller) +{ + if (!ops->hierarchies) + return log_trace_errno(NULL, errno, "There are no useable cgroup controllers"); + + for (int i = 0; ops->hierarchies[i]; i++) { + if (!controller) { + /* This is the empty unified hierarchy. */ + if (ops->hierarchies[i]->controllers && + !ops->hierarchies[i]->controllers[0]) + return ops->hierarchies[i]; + continue; + } else if (pure_unified_layout(ops) && + strcmp(controller, "devices") == 0) { + if (ops->unified->bpf_device_controller) + return ops->unified; + break; + } + + if (string_in_list(ops->hierarchies[i]->controllers, controller)) + return ops->hierarchies[i]; + } + + if (controller) + WARN("There is no useable %s controller", controller); + else + WARN("There is no empty unified cgroup hierarchy"); + + return ret_set_errno(NULL, ENOENT); +} + +#define BATCH_SIZE 50 +static void batch_realloc(char **mem, size_t oldlen, size_t newlen) +{ + int newbatches = (newlen / BATCH_SIZE) + 1; + int oldbatches = (oldlen / BATCH_SIZE) + 1; + + if (!*mem || newbatches > oldbatches) + *mem = must_realloc(*mem, newbatches * BATCH_SIZE); +} + +static void append_line(char **dest, size_t oldlen, char *new, size_t newlen) +{ + size_t full = oldlen + newlen; + + batch_realloc(dest, oldlen, full + 1); + + memcpy(*dest + oldlen, new, newlen + 1); +} + +/* Slurp in a whole file */ +static char *read_file(const char *fnam) +{ + __do_free char *buf = NULL, *line = NULL; + __do_fclose FILE *f = NULL; + size_t len = 0, fulllen = 0; + int linelen; + + f = fopen(fnam, "re"); + if (!f) + return NULL; + + while ((linelen = getline(&line, &len, f)) != -1) { + append_line(&buf, fulllen, line, linelen); + fulllen += linelen; + } + + return move_ptr(buf); +} + +static inline bool is_unified_hierarchy(const struct hierarchy *h) +{ + return h->version == CGROUP2_SUPER_MAGIC; +} + +/* Given two null-terminated lists of strings, return true if any string is in + * both. + */ +static bool controller_lists_intersect(char **l1, char **l2) +{ + if (!l1 || !l2) + return false; + + for (int i = 0; l1[i]; i++) + if (string_in_list(l2, l1[i])) + return true; + + return false; +} + +/* For a null-terminated list of controllers @clist, return true if any of those + * controllers is already listed the null-terminated list of hierarchies @hlist. + * Realistically, if one is present, all must be present. + */ +static bool controller_list_is_dup(struct hierarchy **hlist, char **clist) +{ + if (!hlist) + return false; + + for (int i = 0; hlist[i]; i++) + if (controller_lists_intersect(hlist[i]->controllers, clist)) + return true; + + return false; +} + +/* Return true if the controller @entry is found in the null-terminated list of + * hierarchies @hlist. + */ +static bool controller_found(struct hierarchy **hlist, char *entry) +{ + if (!hlist) + return false; + + for (int i = 0; hlist[i]; i++) + if (string_in_list(hlist[i]->controllers, entry)) + return true; + + return false; +} + +/* Return true if all of the controllers which we require have been found. The + * required list is freezer and anything in lxc.cgroup.use. + */ +static bool all_controllers_found(struct cgroup_ops *ops) +{ + struct hierarchy **hlist; + + if (!ops->cgroup_use) + return true; + + hlist = ops->hierarchies; + for (char **cur = ops->cgroup_use; cur && *cur; cur++) + if (!controller_found(hlist, *cur)) + return log_error(false, "No %s controller mountpoint found", *cur); + + return true; +} + +/* Get the controllers from a mountinfo line There are other ways we could get + * this info. For lxcfs, field 3 is /cgroup/controller-list. For cgroupfs, we + * could parse the mount options. But we simply assume that the mountpoint must + * be /sys/fs/cgroup/controller-list + */ +static char **cg_hybrid_get_controllers(char **klist, char **nlist, char *line, + int type) +{ + /* The fourth field is /sys/fs/cgroup/comma-delimited-controller-list + * for legacy hierarchies. + */ + __do_free_string_list char **aret = NULL; + int i; + char *p2, *tok; + char *p = line, *sep = ","; + + for (i = 0; i < 4; i++) { + p = strchr(p, ' '); + if (!p) + return NULL; + p++; + } + + /* Note, if we change how mountinfo works, then our caller will need to + * verify /sys/fs/cgroup/ in this field. + */ + if (strncmp(p, DEFAULT_CGROUP_MOUNTPOINT "/", 15) != 0) + return log_warn(NULL, "Found hierarchy not under " DEFAULT_CGROUP_MOUNTPOINT ": \"%s\"", p); + + p += 15; + p2 = strchr(p, ' '); + if (!p2) + return log_error(NULL, "Corrupt mountinfo"); + *p2 = '\0'; + + if (type == CGROUP_SUPER_MAGIC) { + __do_free char *dup = NULL; + + /* strdup() here for v1 hierarchies. Otherwise + * lxc_iterate_parts() will destroy mountpoints such as + * "/sys/fs/cgroup/cpu,cpuacct". + */ + dup = must_copy_string(p); + if (!dup) + return NULL; + + lxc_iterate_parts (tok, dup, sep) + must_append_controller(klist, nlist, &aret, tok); + } + *p2 = ' '; + + return move_ptr(aret); +} + +static char **cg_unified_make_empty_controller(void) +{ + __do_free_string_list char **aret = NULL; + int newentry; + + newentry = append_null_to_list((void ***)&aret); + aret[newentry] = NULL; + return move_ptr(aret); +} + +static char **cg_unified_get_controllers(const char *file) +{ + __do_free char *buf = NULL; + __do_free_string_list char **aret = NULL; + char *sep = " \t\n"; + char *tok; + + buf = read_file(file); + if (!buf) + return NULL; + + lxc_iterate_parts(tok, buf, sep) { + int newentry; + char *copy; + + newentry = append_null_to_list((void ***)&aret); + copy = must_copy_string(tok); + aret[newentry] = copy; + } + + return move_ptr(aret); +} + +static struct hierarchy *add_hierarchy(struct hierarchy ***h, char **clist, char *mountpoint, + char *container_base_path, int type) +{ + struct hierarchy *new; + int newentry; + + new = zalloc(sizeof(*new)); + new->controllers = clist; + new->mountpoint = mountpoint; + new->container_base_path = container_base_path; + new->version = type; + new->cgfd_con = -EBADF; + new->cgfd_mon = -EBADF; + + newentry = append_null_to_list((void ***)h); + (*h)[newentry] = new; + return new; +} + +/* Get a copy of the mountpoint from @line, which is a line from + * /proc/self/mountinfo. + */ +static char *cg_hybrid_get_mountpoint(char *line) +{ + char *p = line, *sret = NULL; + size_t len; + char *p2; + + for (int i = 0; i < 4; i++) { + p = strchr(p, ' '); + if (!p) + return NULL; + p++; + } + + if (strncmp(p, DEFAULT_CGROUP_MOUNTPOINT "/", 15) != 0) + return NULL; + + p2 = strchr(p + 15, ' '); + if (!p2) + return NULL; + *p2 = '\0'; + + len = strlen(p); + sret = must_realloc(NULL, len + 1); + memcpy(sret, p, len); + sret[len] = '\0'; + + return sret; +} + +/* Given a multi-line string, return a null-terminated copy of the current line. */ +static char *copy_to_eol(char *p) +{ + char *p2, *sret; + size_t len; + + p2 = strchr(p, '\n'); + if (!p2) + return NULL; + + len = p2 - p; + sret = must_realloc(NULL, len + 1); + memcpy(sret, p, len); + sret[len] = '\0'; + + return sret; +} + +/* cgline: pointer to character after the first ':' in a line in a \n-terminated + * /proc/self/cgroup file. Check whether controller c is present. + */ +static bool controller_in_clist(char *cgline, char *c) +{ + __do_free char *tmp = NULL; + char *tok, *eol; + size_t len; + + eol = strchr(cgline, ':'); + if (!eol) + return false; + + len = eol - cgline; + tmp = must_realloc(NULL, len + 1); + memcpy(tmp, cgline, len); + tmp[len] = '\0'; + + lxc_iterate_parts(tok, tmp, ",") + if (strcmp(tok, c) == 0) + return true; + + return false; +} + +/* @basecginfo is a copy of /proc/$$/cgroup. Return the current cgroup for + * @controller. + */ +static char *cg_hybrid_get_current_cgroup(char *basecginfo, char *controller, + int type) +{ + char *p = basecginfo; + + for (;;) { + bool is_cgv2_base_cgroup = false; + + /* cgroup v2 entry in "/proc/<pid>/cgroup": "0::/some/path" */ + if ((type == CGROUP2_SUPER_MAGIC) && (*p == '0')) + is_cgv2_base_cgroup = true; + + p = strchr(p, ':'); + if (!p) + return NULL; + p++; + + if (is_cgv2_base_cgroup || (controller && controller_in_clist(p, controller))) { + p = strchr(p, ':'); + if (!p) + return NULL; + p++; + return copy_to_eol(p); + } + + p = strchr(p, '\n'); + if (!p) + return NULL; + p++; + } +} + +static void must_append_string(char ***list, char *entry) +{ + int newentry; + char *copy; + + newentry = append_null_to_list((void ***)list); + copy = must_copy_string(entry); + (*list)[newentry] = copy; +} + +static int get_existing_subsystems(char ***klist, char ***nlist) +{ + __do_free char *line = NULL; + __do_fclose FILE *f = NULL; + size_t len = 0; + + f = fopen("/proc/self/cgroup", "re"); + if (!f) + return -1; + + while (getline(&line, &len, f) != -1) { + char *p, *p2, *tok; + p = strchr(line, ':'); + if (!p) + continue; + p++; + p2 = strchr(p, ':'); + if (!p2) + continue; + *p2 = '\0'; + + /* If the kernel has cgroup v2 support, then /proc/self/cgroup + * contains an entry of the form: + * + * 0::/some/path + * + * In this case we use "cgroup2" as controller name. + */ + if ((p2 - p) == 0) { + must_append_string(klist, "cgroup2"); + continue; + } + + lxc_iterate_parts(tok, p, ",") { + if (strncmp(tok, "name=", 5) == 0) + must_append_string(nlist, tok); + else + must_append_string(klist, tok); + } + } + + return 0; +} + +static char *trim(char *s) +{ + size_t len; + + len = strlen(s); + while ((len > 1) && (s[len - 1] == '\n')) + s[--len] = '\0'; + + return s; +} + +static void lxc_cgfsng_print_hierarchies(struct cgroup_ops *ops) +{ + int i; + struct hierarchy **it; + + if (!ops->hierarchies) { + TRACE(" No hierarchies found"); + return; + } + + TRACE(" Hierarchies:"); + for (i = 0, it = ops->hierarchies; it && *it; it++, i++) { + int j; + char **cit; + + TRACE(" %d: base_cgroup: %s", i, (*it)->container_base_path ? (*it)->container_base_path : "(null)"); + TRACE(" mountpoint: %s", (*it)->mountpoint ? (*it)->mountpoint : "(null)"); + TRACE(" controllers:"); + for (j = 0, cit = (*it)->controllers; cit && *cit; cit++, j++) + TRACE(" %d: %s", j, *cit); + } +} + +static void lxc_cgfsng_print_basecg_debuginfo(char *basecginfo, char **klist, + char **nlist) +{ + int k; + char **it; + + TRACE("basecginfo is:"); + TRACE("%s", basecginfo); + + for (k = 0, it = klist; it && *it; it++, k++) + TRACE("kernel subsystem %d: %s", k, *it); + + for (k = 0, it = nlist; it && *it; it++, k++) + TRACE("named subsystem %d: %s", k, *it); +} + +struct generic_userns_exec_data { + struct hierarchy **hierarchies; + const char *container_cgroup; + struct lxc_conf *conf; + uid_t origuid; /* target uid in parent namespace */ + char *path; +}; + +static int isulad_cgroup_tree_remove(struct hierarchy **hierarchies, + const char *container_cgroup) +{ + if (!container_cgroup || !hierarchies) + return 0; + + for (int i = 0; hierarchies[i]; i++) { + struct hierarchy *h = hierarchies[i]; + int ret; + + if (!h->container_full_path) { + h->container_full_path = must_make_path(h->mountpoint, h->container_base_path, container_cgroup, NULL); + } + + ret = lxc_rm_rf(h->container_full_path); + if (ret < 0) { + SYSERROR("Failed to destroy \"%s\"", h->container_full_path); + return -1; + } + + free_disarm(h->container_full_path); + } + + return 0; +} + +static int isulad_cgroup_tree_remove_wrapper(void *data) +{ + struct generic_userns_exec_data *arg = data; + uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid; + gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid; + int ret; + + if (!lxc_setgroups(0, NULL) && errno != EPERM) + return log_error_errno(-1, errno, "Failed to setgroups(0, NULL)"); + + ret = setresgid(nsgid, nsgid, nsgid); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to setresgid(%d, %d, %d)", + (int)nsgid, (int)nsgid, (int)nsgid); + + ret = setresuid(nsuid, nsuid, nsuid); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to setresuid(%d, %d, %d)", + (int)nsuid, (int)nsuid, (int)nsuid); + + return isulad_cgroup_tree_remove(arg->hierarchies, arg->container_cgroup); +} + +__cgfsng_ops static bool isulad_cgfsng_payload_destroy(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + int ret; + + if (!ops) { + ERROR("Called with uninitialized cgroup operations"); + return false; + } + + if (ops->no_controller) { + DEBUG("no controller found, ignore isulad_cgfsng_payload_destroy"); + return true; + } + + if (!ops->hierarchies) { + DEBUG("no hierarchies found, ignore isulad_cgfsng_payload_destroy"); + return true; + } + + if (!handler) { + ERROR("Called with uninitialized handler"); + return false; + } + + if (!handler->conf) { + ERROR("Called with uninitialized conf"); + return false; + } + +#ifdef HAVE_STRUCT_BPF_CGROUP_DEV_CTX + ret = bpf_program_cgroup_detach(handler->conf->cgroup2_devices); + if (ret < 0) + WARN("Failed to detach bpf program from cgroup"); +#endif + + if (handler->conf && !lxc_list_empty(&handler->conf->id_map)) { + struct generic_userns_exec_data wrap = { + .conf = handler->conf, + .container_cgroup = ops->container_cgroup, + .hierarchies = ops->hierarchies, + .origuid = 0, + }; + ret = userns_exec_1(handler->conf, isulad_cgroup_tree_remove_wrapper, + &wrap, "cgroup_tree_remove_wrapper"); + } else { + ret = isulad_cgroup_tree_remove(ops->hierarchies, ops->container_cgroup); + } + if (ret < 0) { + SYSWARN("Failed to destroy cgroups"); + return false; + } + + return true; +} + +__cgfsng_ops static void isulad_cgfsng_monitor_destroy(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + return; +} + +__cgfsng_ops static inline bool isulad_cgfsng_monitor_create(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + return true; +} + +static bool isulad_copy_parent_file(char *path, char *file) +{ + int ret; + int len = 0; + char *value = NULL; + char *current = NULL; + char *fpath = NULL; + char *lastslash = NULL; + char oldv; + + fpath = must_make_path(path, file, NULL); + current = read_file(fpath); + + if (current == NULL) { + SYSERROR("Failed to read file \"%s\"", fpath); + free(fpath); + return false; + } + + if (strcmp(current, "\n") != 0) { + free(fpath); + free(current); + return true; + } + + free(fpath); + free(current); + + lastslash = strrchr(path, '/'); + if (lastslash == NULL) { + ERROR("Failed to detect \"/\" in \"%s\"", path); + return false; + } + oldv = *lastslash; + *lastslash = '\0'; + fpath = must_make_path(path, file, NULL); + *lastslash = oldv; + len = lxc_read_from_file(fpath, NULL, 0); + if (len <= 0) + goto on_error; + + value = must_realloc(NULL, len + 1); + ret = lxc_read_from_file(fpath, value, len); + if (ret != len) + goto on_error; + free(fpath); + + fpath = must_make_path(path, file, NULL); + ret = lxc_write_to_file(fpath, value, len, false, 0666); + if (ret < 0) + SYSERROR("Failed to write \"%s\" to file \"%s\"", value, fpath); + free(fpath); + free(value); + return ret >= 0; + +on_error: + SYSERROR("Failed to read file \"%s\"", fpath); + free(fpath); + free(value); + return false; +} + +static bool build_sub_cpuset_cgroup_dir(char *cgpath) +{ + int ret; + + ret = mkdir_p(cgpath, 0755); + if (ret < 0) { + if (errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", cgpath); + return false; + } + } + + /* copy parent's settings */ + if (!isulad_copy_parent_file(cgpath, "cpuset.cpus")) { + SYSERROR("Failed to copy \"cpuset.cpus\" settings"); + return false; + } + + /* copy parent's settings */ + if (!isulad_copy_parent_file(cgpath, "cpuset.mems")) { + SYSERROR("Failed to copy \"cpuset.mems\" settings"); + return false; + } + + return true; +} + +static bool isulad_cg_legacy_handle_cpuset_hierarchy(struct hierarchy *h, char *cgname) +{ + char *cgpath, *slash; + bool sub_mk_success = false; + + if (is_unified_hierarchy(h)) + return true; + + if (!string_in_list(h->controllers, "cpuset")) + return true; + + cgname += strspn(cgname, "/"); + + slash = strchr(cgname, '/'); + + if (slash != NULL) { + while (slash) { + *slash = '\0'; + cgpath = must_make_path(h->mountpoint, h->container_base_path, cgname, NULL); + sub_mk_success = build_sub_cpuset_cgroup_dir(cgpath); + free(cgpath); + *slash = '/'; + if (!sub_mk_success) { + return false; + } + slash = strchr(slash + 1, '/'); + } + } + + cgpath = must_make_path(h->mountpoint, h->container_base_path, cgname, NULL); + sub_mk_success = build_sub_cpuset_cgroup_dir(cgpath); + free(cgpath); + if (!sub_mk_success) { + return false; + } + + return true; +} + +static int isulad_mkdir_eexist_on_last(const char *dir, mode_t mode) +{ + const char *tmp = dir; + const char *orig = dir; + + do { + int ret; + size_t cur_len; + char *makeme; + + dir = tmp + strspn(tmp, "/"); + tmp = dir + strcspn(dir, "/"); + + errno = ENOMEM; + cur_len = dir - orig; + makeme = strndup(orig, cur_len); + if (!makeme) + return -1; + + ret = mkdir(makeme, mode); + if (ret < 0) { + if (errno != EEXIST) { + SYSERROR("Failed to create directory \"%s\"", makeme); + free(makeme); + return -1; + } + } + free(makeme); + + } while (tmp != dir); + + return 0; +} + +static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname, int errfd) +{ + int ret; + __do_free char *path = NULL; + + path = must_make_path(h->mountpoint, h->container_base_path, cgname, NULL); + + if (file_exists(path)) { // it must not already exist + ERROR("Cgroup path \"%s\" already exist.", path); + lxc_write_error_message(errfd, "%s:%d: Cgroup path \"%s\" already exist.", + __FILE__, __LINE__, path); + return false; + } + + if (!isulad_cg_legacy_handle_cpuset_hierarchy(h, cgname)) { + ERROR("Failed to handle legacy cpuset controller"); + return false; + } + + ret = isulad_mkdir_eexist_on_last(path, 0755); + if (ret < 0) { + ERROR("Failed to create cgroup \"%s\"", path); + return false; + } + + h->cgfd_con = lxc_open_dirfd(path); + if (h->cgfd_con < 0) + return log_error_errno(false, errno, "Failed to open %s", path); + + if (h->container_full_path == NULL) { + h->container_full_path = move_ptr(path); + } + + return true; +} + +/* isulad: create hierarchies path, if fail, return the error */ +__cgfsng_ops static inline bool isulad_cgfsng_payload_create(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + int i; + + if (!ops) + return ret_set_errno(false, ENOENT); + + char *container_cgroup = ops->container_cgroup; + + if (!ops->hierarchies) + return true; + +#ifdef HAVE_ISULAD + if (ops->no_controller) { + DEBUG("no controller found, isgnore isulad_cgfsng_payload_create"); + return true; + } +#endif + + if (!container_cgroup) { + ERROR("cgfsng_create container_cgroup is invalid"); + return false; + } + + for (i = 0; ops->hierarchies[i]; i++) { + if (!create_path_for_hierarchy(ops->hierarchies[i], container_cgroup, ops->errfd)) { + SYSERROR("Failed to create %s", ops->hierarchies[i]->container_full_path); + return false; + } + } + + return true; +} + +__cgfsng_ops static bool isulad_cgfsng_monitor_enter(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + return true; +} + +__cgfsng_ops static bool isulad_cgfsng_payload_enter(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + int len; + char pidstr[INTTYPE_TO_STRLEN(pid_t)]; + + if (!ops) + return ret_set_errno(false, ENOENT); + +#ifdef HAVE_ISULAD + if (ops->no_controller) { + DEBUG("no controller found, isgnore isulad_cgfsng_payload_enter"); + return true; + } +#endif + + if (!ops->hierarchies) + return true; + + if (!ops->container_cgroup) + return ret_set_errno(false, ENOENT); + + if (!handler || !handler->conf) + return ret_set_errno(false, EINVAL); + + len = snprintf(pidstr, sizeof(pidstr), "%d", handler->pid); + + for (int i = 0; ops->hierarchies[i]; i++) { + int ret; + char *fullpath; + int retry_count = 0; + int max_retry = 10; + + fullpath = must_make_path(ops->hierarchies[i]->container_full_path, + "cgroup.procs", NULL); +retry: + ret = lxc_write_to_file(fullpath, pidstr, len, false, 0666); + if (ret != 0) { + if (retry_count < max_retry) { + SYSERROR("Failed to enter cgroup \"%s\" with retry count:%d", fullpath, retry_count); + (void)isulad_cg_legacy_handle_cpuset_hierarchy(ops->hierarchies[i], ops->container_cgroup); + (void)isulad_mkdir_eexist_on_last(ops->hierarchies[i]->container_full_path, 0755); + usleep(100 * 1000); /* 100 millisecond */ + retry_count++; + goto retry; + } + SYSERROR("Failed to enter cgroup \"%s\"", fullpath); + free(fullpath); + return false; + } + free(fullpath); + } + + return true; +} + +static int fchowmodat(int dirfd, const char *path, uid_t chown_uid, + gid_t chown_gid, mode_t chmod_mode) +{ + int ret; + + ret = fchownat(dirfd, path, chown_uid, chown_gid, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (ret < 0) + return log_warn_errno(-1, + errno, "Failed to fchownat(%d, %s, %d, %d, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW )", + dirfd, path, (int)chown_uid, + (int)chown_gid); + + ret = fchmodat(dirfd, (*path != '\0') ? path : ".", chmod_mode, 0); + if (ret < 0) + return log_warn_errno(-1, errno, "Failed to fchmodat(%d, %s, %d, AT_SYMLINK_NOFOLLOW)", + dirfd, path, (int)chmod_mode); + + return 0; +} + +/* chgrp the container cgroups to container group. We leave + * the container owner as cgroup owner. So we must make the + * directories 775 so that the container can create sub-cgroups. + * + * Also chown the tasks and cgroup.procs files. Those may not + * exist depending on kernel version. + */ +static int chown_cgroup_wrapper(void *data) +{ + int ret; + uid_t destuid; + struct generic_userns_exec_data *arg = data; + uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid; + gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid; + + if (!lxc_setgroups(0, NULL) && errno != EPERM) + return log_error_errno(-1, errno, "Failed to setgroups(0, NULL)"); + + ret = setresgid(nsgid, nsgid, nsgid); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to setresgid(%d, %d, %d)", + (int)nsgid, (int)nsgid, (int)nsgid); + + ret = setresuid(nsuid, nsuid, nsuid); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to setresuid(%d, %d, %d)", + (int)nsuid, (int)nsuid, (int)nsuid); + + destuid = get_ns_uid(arg->origuid); + if (destuid == LXC_INVALID_UID) + destuid = 0; + + for (int i = 0; arg->hierarchies[i]; i++) { + int dirfd = arg->hierarchies[i]->cgfd_con; + + (void)fchowmodat(dirfd, "", destuid, nsgid, 0775); + + /* + * Failures to chown() these are inconvenient but not + * detrimental We leave these owned by the container launcher, + * so that container root can write to the files to attach. We + * chmod() them 664 so that container systemd can write to the + * files (which systemd in wily insists on doing). + */ + + if (arg->hierarchies[i]->version == CGROUP_SUPER_MAGIC) + (void)fchowmodat(dirfd, "tasks", destuid, nsgid, 0664); + + (void)fchowmodat(dirfd, "cgroup.procs", destuid, nsgid, 0664); + + if (arg->hierarchies[i]->version != CGROUP2_SUPER_MAGIC) + continue; + + for (char **p = arg->hierarchies[i]->cgroup2_chown; p && *p; p++) + (void)fchowmodat(dirfd, *p, destuid, nsgid, 0664); + } + + return 0; +} + +__cgfsng_ops static bool isulad_cgfsng_chown(struct cgroup_ops *ops, + struct lxc_conf *conf) +{ + struct generic_userns_exec_data wrap; + + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!ops->hierarchies) + return true; + + if (!ops->container_cgroup) + return ret_set_errno(false, ENOENT); + + if (!conf) + return ret_set_errno(false, EINVAL); + + if (lxc_list_empty(&conf->id_map)) + return true; + + wrap.origuid = geteuid(); + wrap.path = NULL; + wrap.hierarchies = ops->hierarchies; + wrap.conf = conf; + + if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap, "chown_cgroup_wrapper") < 0) + return log_error_errno(false, errno, "Error requesting cgroup chown in new user namespace"); + + return true; +} + +__cgfsng_ops void isulad_cgfsng_payload_finalize(struct cgroup_ops *ops) +{ + if (!ops) + return; + +#ifdef HAVE_ISULAD + if (ops->no_controller) { + DEBUG("no controller found, isgnore isulad_cgfsng_payload_finalize"); + return; + } +#endif + + if (!ops->hierarchies) + return; + + for (int i = 0; ops->hierarchies[i]; i++) { + struct hierarchy *h = ops->hierarchies[i]; + /* + * we don't keep the fds for non-unified hierarchies around + * mainly because we don't make use of them anymore after the + * core cgroup setup is done but also because there are quite a + * lot of them. + */ + if (!is_unified_hierarchy(h)) + close_prot_errno_disarm(h->cgfd_con); + } +} + +/* cgroup-full:* is done, no need to create subdirs */ +static inline bool cg_mount_needs_subdirs(int type) +{ + return !(type >= LXC_AUTO_CGROUP_FULL_RO); +} + +/* After $rootfs/sys/fs/container/controller/the/cg/path has been created, + * remount controller ro if needed and bindmount the cgroupfs onto + * control/the/cg/path. + */ +static int cg_legacy_mount_controllers(int type, struct hierarchy *h, + char *controllerpath, char *cgpath, + const char *container_cgroup) +{ + __do_free char *sourcepath = NULL; + int ret, remount_flags; + int flags = MS_BIND; + + if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_MIXED) { + ret = mount(controllerpath, controllerpath, "cgroup", MS_BIND, NULL); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to bind mount \"%s\" onto \"%s\"", + controllerpath, controllerpath); + + remount_flags = add_required_remount_flags(controllerpath, + controllerpath, + flags | MS_REMOUNT); + ret = mount(controllerpath, controllerpath, "cgroup", + remount_flags | MS_REMOUNT | MS_BIND | MS_RDONLY, + NULL); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to remount \"%s\" ro", controllerpath); + + INFO("Remounted %s read-only", controllerpath); + } + + sourcepath = must_make_path(h->mountpoint, h->container_base_path, + container_cgroup, NULL); + if (type == LXC_AUTO_CGROUP_RO) + flags |= MS_RDONLY; + + ret = mount(sourcepath, cgpath, "cgroup", flags, NULL); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to mount \"%s\" onto \"%s\"", + h->controllers[0], cgpath); + INFO("Mounted \"%s\" onto \"%s\"", h->controllers[0], cgpath); + + if (flags & MS_RDONLY) { + remount_flags = add_required_remount_flags(sourcepath, cgpath, + flags | MS_REMOUNT); + ret = mount(sourcepath, cgpath, "cgroup", remount_flags, NULL); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to remount \"%s\" ro", cgpath); + INFO("Remounted %s read-only", cgpath); + } + + INFO("Completed second stage cgroup automounts for \"%s\"", cgpath); + return 0; +} + +/* __cg_mount_direct + * + * Mount cgroup hierarchies directly without using bind-mounts. The main + * uses-cases are mounting cgroup hierarchies in cgroup namespaces and mounting + * cgroups for the LXC_AUTO_CGROUP_FULL option. + */ +static int __cg_mount_direct(int type, struct hierarchy *h, + const char *controllerpath) +{ + __do_free char *controllers = NULL; + char *fstype = "cgroup2"; + unsigned long flags = 0; + int ret; + + flags |= MS_NOSUID; + flags |= MS_NOEXEC; + flags |= MS_NODEV; + flags |= MS_RELATIME; + + if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO) + flags |= MS_RDONLY; + + if (h->version != CGROUP2_SUPER_MAGIC) { + controllers = lxc_string_join(",", (const char **)h->controllers, false); + if (!controllers) + return -ENOMEM; + fstype = "cgroup"; + } + + ret = mount("cgroup", controllerpath, fstype, flags, controllers); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to mount \"%s\" with cgroup filesystem type %s", + controllerpath, fstype); + + DEBUG("Mounted \"%s\" with cgroup filesystem type %s", controllerpath, fstype); + return 0; +} + +static inline int cg_mount_in_cgroup_namespace(int type, struct hierarchy *h, + const char *controllerpath) +{ + return __cg_mount_direct(type, h, controllerpath); +} + +static inline int cg_mount_cgroup_full(int type, struct hierarchy *h, + const char *controllerpath) +{ + if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) + return 0; + + return __cg_mount_direct(type, h, controllerpath); +} + +__cgfsng_ops static bool isulad_cgfsng_mount(struct cgroup_ops *ops, + struct lxc_handler *handler, + const char *root, int type) +{ + int i, ret; + char *tmpfspath = NULL; + char *systemdpath = NULL; + char *unifiedpath = NULL; + bool has_cgns = false, retval = false, wants_force_mount = false; + char **merged = NULL; + + if ((type & LXC_AUTO_CGROUP_MASK) == 0) + return true; + + if (type & LXC_AUTO_CGROUP_FORCE) { + type &= ~LXC_AUTO_CGROUP_FORCE; + wants_force_mount = true; + } + + if (!wants_force_mount) { + if (!lxc_list_empty(&handler->conf->keepcaps)) + wants_force_mount = !in_caplist(CAP_SYS_ADMIN, &handler->conf->keepcaps); + else + wants_force_mount = in_caplist(CAP_SYS_ADMIN, &handler->conf->caps); + } + + has_cgns = cgns_supported(); + if (has_cgns && !wants_force_mount) + return true; + + if (type == LXC_AUTO_CGROUP_NOSPEC) + type = LXC_AUTO_CGROUP_MIXED; + else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) + type = LXC_AUTO_CGROUP_FULL_MIXED; + + /* Mount tmpfs */ + tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL); + if (mkdir_p(tmpfspath, 0755) < 0) { + ERROR("Failed to create directory: %s", tmpfspath); + goto on_error; + } + + if (ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) { + if (has_cgns && wants_force_mount) { + /* + * If cgroup namespaces are supported but the container + * will not have CAP_SYS_ADMIN after it has started we + * need to mount the cgroups manually. + */ + return cg_mount_in_cgroup_namespace(type, ops->unified, tmpfspath) == 0; + } + + return cg_mount_cgroup_full(type, ops->unified, tmpfspath) == 0; + } + + ret = safe_mount(NULL, tmpfspath, "tmpfs", + MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, + "size=10240k,mode=755", root, handler->conf->lsm_se_mount_context); + if (ret < 0) + goto on_error; + + for (i = 0; ops->hierarchies[i]; i++) { + char *controllerpath = NULL; + char *path2 = NULL; + struct hierarchy *h = ops->hierarchies[i]; + char *controller = strrchr(h->mountpoint, '/'); + + if (!controller) + continue; + controller++; + + // isulad: symlink subcgroup + if (strchr(controller, ',') != NULL) { + int pret; + pret = lxc_append_string(&merged, controller); + if (pret < 0) + goto on_error; + } + + controllerpath = must_make_path(tmpfspath, controller, NULL); + if (dir_exists(controllerpath)) { + free(controllerpath); + continue; + } + + ret = mkdir(controllerpath, 0755); + if (ret < 0) { + SYSERROR("Error creating cgroup path: %s", controllerpath); + free(controllerpath); + goto on_error; + } + + if (has_cgns && wants_force_mount) { + /* If cgroup namespaces are supported but the container + * will not have CAP_SYS_ADMIN after it has started we + * need to mount the cgroups manually. + */ + ret = cg_mount_in_cgroup_namespace(type, h, controllerpath); + free(controllerpath); + if (ret < 0) + goto on_error; + + continue; + } + + ret = cg_mount_cgroup_full(type, h, controllerpath); + if (ret < 0) { + free(controllerpath); + goto on_error; + } + + if (!cg_mount_needs_subdirs(type)) { + free(controllerpath); + continue; + } + + // isulad: ignore ops->container_cgroup so we will not see directory lxc after /sys/fs/cgroup/xxx in container, + // isulad: ignore h->container_base_path so we will not see subgroup of /sys/fs/cgroup/xxx/subgroup in container + path2 = must_make_path(controllerpath, NULL); + ret = mkdir_p(path2, 0755); + if (ret < 0) { + free(controllerpath); + free(path2); + goto on_error; + } + + ret = cg_legacy_mount_controllers(type, h, controllerpath, + path2, ops->container_cgroup); + free(controllerpath); + free(path2); + if (ret < 0) + goto on_error; + } + + // isulad: symlink subcgroup + if (merged) { + char **mc = NULL; + for (mc = merged; *mc; mc++) { + char *token = NULL; + char *copy = must_copy_string(*mc); + lxc_iterate_parts(token, copy, ",") { + int mret; + char *link; + link = must_make_path(tmpfspath, token, NULL); + mret = symlink(*mc, link); + if (mret < 0 && errno != EEXIST) { + SYSERROR("Failed to create link %s for target %s", link, *mc); + free(copy); + free(link); + goto on_error; + } + free(link); + } + free(copy); + } + } + + + // isulad: remount /sys/fs/cgroup to readonly + if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_RO) { + ret = mount(tmpfspath, tmpfspath, "bind", + MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME|MS_RDONLY|MS_BIND|MS_REMOUNT, NULL); + if (ret < 0) { + SYSERROR("Failed to remount /sys/fs/cgroup."); + goto on_error; + } + } + + // isulad: remount /sys/fs/cgroup/systemd to readwrite for system container + if (handler->conf->systemd != NULL && strcmp(handler->conf->systemd, "true") == 0) + { + unifiedpath = must_make_path(root, "/sys/fs/cgroup/unified", NULL); + if (dir_exists(unifiedpath)) + { + ret = umount2(unifiedpath, MNT_DETACH); + if (ret < 0) + { + SYSERROR("Failed to umount /sys/fs/cgroup/unified."); + goto on_error; + } + } + + systemdpath = must_make_path(root, "/sys/fs/cgroup/systemd", NULL); + ret = mount(systemdpath, systemdpath, "bind", + MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME | MS_BIND | MS_REMOUNT, NULL); + if (ret < 0) + { + SYSERROR("Failed to remount /sys/fs/cgroup/systemd."); + goto on_error; + } + } + + retval = true; + +on_error: + free(tmpfspath); + if (systemdpath != NULL) + { + free(systemdpath); + } + if (unifiedpath != NULL) + { + free(unifiedpath); + } + lxc_free_array((void **)merged, free); + return retval; +} + +/* Only root needs to escape to the cgroup of its init. */ +__cgfsng_ops static bool isulad_cgfsng_escape(const struct cgroup_ops *ops, + struct lxc_conf *conf) +{ + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!ops->hierarchies) + return true; + + if (!conf) + return ret_set_errno(false, EINVAL); + + if (conf->cgroup_meta.relative || geteuid()) + return true; + + for (int i = 0; ops->hierarchies[i]; i++) { + __do_free char *fullpath = NULL; + int ret; + + fullpath = + must_make_path(ops->hierarchies[i]->mountpoint, + ops->hierarchies[i]->container_base_path, + "cgroup.procs", NULL); + ret = lxc_write_to_file(fullpath, "0", 2, false, 0666); + if (ret != 0) + return log_error_errno(false, errno, "Failed to escape to cgroup \"%s\"", fullpath); + } + + return true; +} + +__cgfsng_ops static int isulad_cgfsng_num_hierarchies(struct cgroup_ops *ops) +{ + int i = 0; + + if (!ops) + return ret_set_errno(-1, ENOENT); + + if (!ops->hierarchies) + return 0; + + for (; ops->hierarchies[i]; i++) + ; + + return i; +} + +__cgfsng_ops static bool isulad_cgfsng_get_hierarchies(struct cgroup_ops *ops, int n, + char ***out) +{ + int i; + + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!ops->hierarchies) + return ret_set_errno(false, ENOENT); + + /* sanity check n */ + for (i = 0; i < n; i++) + if (!ops->hierarchies[i]) + return ret_set_errno(false, ENOENT); + + *out = ops->hierarchies[i]->controllers; + + return true; +} + +static bool cg_legacy_freeze(struct cgroup_ops *ops) +{ + struct hierarchy *h; + + h = get_hierarchy(ops, "freezer"); + if (!h) + return ret_set_errno(-1, ENOENT); + + return lxc_write_openat(h->container_full_path, "freezer.state", + "FROZEN", STRLITERALLEN("FROZEN")); +} + +static int freezer_cgroup_events_cb(int fd, uint32_t events, void *cbdata, + struct lxc_epoll_descr *descr) +{ + __do_close int duped_fd = -EBADF; + __do_free char *line = NULL; + __do_fclose FILE *f = NULL; + int state = PTR_TO_INT(cbdata); + size_t len; + const char *state_string; + + duped_fd = dup(fd); + if (duped_fd < 0) + return LXC_MAINLOOP_ERROR; + + if (lseek(duped_fd, 0, SEEK_SET) < (off_t)-1) + return LXC_MAINLOOP_ERROR; + + f = fdopen(duped_fd, "re"); + if (!f) + return LXC_MAINLOOP_ERROR; + move_fd(duped_fd); + + if (state == 1) + state_string = "frozen 1"; + else + state_string = "frozen 0"; + + while (getline(&line, &len, f) != -1) + if (strncmp(line, state_string, STRLITERALLEN("frozen") + 2) == 0) + return LXC_MAINLOOP_CLOSE; + + return LXC_MAINLOOP_CONTINUE; +} + +static int cg_unified_freeze(struct cgroup_ops *ops, int timeout) +{ + __do_close int fd = -EBADF; + call_cleaner(lxc_mainloop_close) struct lxc_epoll_descr *descr_ptr = NULL; + int ret; + struct lxc_epoll_descr descr; + struct hierarchy *h; + + h = ops->unified; + if (!h) + return ret_set_errno(-1, ENOENT); + + if (!h->container_full_path) + return ret_set_errno(-1, EEXIST); + + if (timeout != 0) { + __do_free char *events_file = NULL; + + events_file = must_make_path(h->container_full_path, "cgroup.events", NULL); + fd = open(events_file, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return log_error_errno(-1, errno, "Failed to open cgroup.events file"); + + ret = lxc_mainloop_open(&descr); + if (ret) + return log_error_errno(-1, errno, "Failed to create epoll instance to wait for container freeze"); + + /* automatically cleaned up now */ + descr_ptr = &descr; + + ret = lxc_mainloop_add_handler(&descr, fd, freezer_cgroup_events_cb, INT_TO_PTR((int){1})); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to add cgroup.events fd handler to mainloop"); + } + + ret = lxc_write_openat(h->container_full_path, "cgroup.freeze", "1", 1); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to open cgroup.freeze file"); + + if (timeout != 0 && lxc_mainloop(&descr, timeout)) + return log_error_errno(-1, errno, "Failed to wait for container to be frozen"); + + return 0; +} + +__cgfsng_ops static int isulad_cgfsng_freeze(struct cgroup_ops *ops, int timeout) +{ + if (!ops->hierarchies) + return ret_set_errno(-1, ENOENT); + + if (ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) + return cg_legacy_freeze(ops); + + return cg_unified_freeze(ops, timeout); +} + +static int cg_legacy_unfreeze(struct cgroup_ops *ops) +{ + struct hierarchy *h; + + h = get_hierarchy(ops, "freezer"); + if (!h) + return ret_set_errno(-1, ENOENT); + + return lxc_write_openat(h->container_full_path, "freezer.state", + "THAWED", STRLITERALLEN("THAWED")); +} + +static int cg_unified_unfreeze(struct cgroup_ops *ops, int timeout) +{ + __do_close int fd = -EBADF; + call_cleaner(lxc_mainloop_close)struct lxc_epoll_descr *descr_ptr = NULL; + int ret; + struct lxc_epoll_descr descr; + struct hierarchy *h; + + h = ops->unified; + if (!h) + return ret_set_errno(-1, ENOENT); + + if (!h->container_full_path) + return ret_set_errno(-1, EEXIST); + + if (timeout != 0) { + __do_free char *events_file = NULL; + + events_file = must_make_path(h->container_full_path, "cgroup.events", NULL); + fd = open(events_file, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return log_error_errno(-1, errno, "Failed to open cgroup.events file"); + + ret = lxc_mainloop_open(&descr); + if (ret) + return log_error_errno(-1, errno, "Failed to create epoll instance to wait for container unfreeze"); + + /* automatically cleaned up now */ + descr_ptr = &descr; + + ret = lxc_mainloop_add_handler(&descr, fd, freezer_cgroup_events_cb, INT_TO_PTR((int){0})); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to add cgroup.events fd handler to mainloop"); + } + + ret = lxc_write_openat(h->container_full_path, "cgroup.freeze", "0", 1); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to open cgroup.freeze file"); + + if (timeout != 0 && lxc_mainloop(&descr, timeout)) + return log_error_errno(-1, errno, "Failed to wait for container to be unfrozen"); + + return 0; +} + +__cgfsng_ops static int isulad_cgfsng_unfreeze(struct cgroup_ops *ops, int timeout) +{ + if (!ops->hierarchies) + return ret_set_errno(-1, ENOENT); + + if (ops->cgroup_layout != CGROUP_LAYOUT_UNIFIED) + return cg_legacy_unfreeze(ops); + + return cg_unified_unfreeze(ops, timeout); +} + +__cgfsng_ops static const char *isulad_cgfsng_get_cgroup(struct cgroup_ops *ops, + const char *controller) +{ + struct hierarchy *h; + + h = get_hierarchy(ops, controller); + if (!h) + return log_warn_errno(NULL, ENOENT, "Failed to find hierarchy for controller \"%s\"", + controller ? controller : "(null)"); + + if (!h->container_full_path) + h->container_full_path = must_make_path(h->mountpoint, h->container_base_path, ops->container_cgroup, NULL); + + return h->container_full_path + ? h->container_full_path + strlen(h->mountpoint) + : NULL; +} + +__cgfsng_ops static const char *isulad_cgfsng_get_cgroup_full_path(struct cgroup_ops *ops, + const char *controller) +{ + struct hierarchy *h; + + h = get_hierarchy(ops, controller); + if (!h) + return log_warn_errno(NULL, ENOENT, "Failed to find hierarchy for controller \"%s\"", + controller ? controller : "(null)"); + + if (!h->container_full_path) + h->container_full_path = must_make_path(h->mountpoint, h->container_base_path, ops->container_cgroup, NULL); + + return h->container_full_path; +} + +/* Given a cgroup path returned from lxc_cmd_get_cgroup_path, build a full path, + * which must be freed by the caller. + */ +static inline char *build_full_cgpath_from_monitorpath(struct hierarchy *h, + const char *inpath, + const char *filename) +{ + return must_make_path(h->mountpoint, inpath, filename, NULL); +} + +static int cgroup_attach_leaf(const struct lxc_conf *conf, int unified_fd, pid_t pid) +{ + int idx = 1; + int ret; + char pidstr[INTTYPE_TO_STRLEN(int64_t) + 1]; + size_t pidstr_len; + + /* Create leaf cgroup. */ + ret = mkdirat(unified_fd, ".lxc", 0755); + if (ret < 0 && errno != EEXIST) + return log_error_errno(-1, errno, "Failed to create leaf cgroup \".lxc\""); + + pidstr_len = sprintf(pidstr, INT64_FMT, (int64_t)pid); + ret = lxc_writeat(unified_fd, ".lxc/cgroup.procs", pidstr, pidstr_len); + if (ret < 0) + ret = lxc_writeat(unified_fd, "cgroup.procs", pidstr, pidstr_len); + if (ret == 0) + return 0; + + /* this is a non-leaf node */ + if (errno != EBUSY) + return log_error_errno(-1, errno, "Failed to attach to unified cgroup"); + + do { + bool rm = false; + char attach_cgroup[STRLITERALLEN(".lxc-/cgroup.procs") + INTTYPE_TO_STRLEN(int) + 1]; + char *slash; + + ret = snprintf(attach_cgroup, sizeof(attach_cgroup), ".lxc-%d/cgroup.procs", idx); + if (ret < 0 || (size_t)ret >= sizeof(attach_cgroup)) + return ret_errno(EIO); + + /* + * This shouldn't really happen but the compiler might complain + * that a short write would cause a buffer overrun. So be on + * the safe side. + */ + if (ret < STRLITERALLEN(".lxc-/cgroup.procs")) + return log_error_errno(-EINVAL, EINVAL, "Unexpected short write would cause buffer-overrun"); + + slash = &attach_cgroup[ret] - STRLITERALLEN("/cgroup.procs"); + *slash = '\0'; + + ret = mkdirat(unified_fd, attach_cgroup, 0755); + if (ret < 0 && errno != EEXIST) + return log_error_errno(-1, errno, "Failed to create cgroup %s", attach_cgroup); + if (ret == 0) + rm = true; + + *slash = '/'; + + ret = lxc_writeat(unified_fd, attach_cgroup, pidstr, pidstr_len); + if (ret == 0) + return 0; + + if (rm && unlinkat(unified_fd, attach_cgroup, AT_REMOVEDIR)) + SYSERROR("Failed to remove cgroup \"%d(%s)\"", unified_fd, attach_cgroup); + + /* this is a non-leaf node */ + if (errno != EBUSY) + return log_error_errno(-1, errno, "Failed to attach to unified cgroup"); + + idx++; + } while (idx < 1000); + + return log_error_errno(-1, errno, "Failed to attach to unified cgroup"); +} + +static int cgroup_attach_create_leaf(const struct lxc_conf *conf, + int unified_fd, int *sk_fd) +{ + __do_close int sk = *sk_fd, target_fd0 = -EBADF, target_fd1 = -EBADF; + int target_fds[2]; + ssize_t ret; + + /* Create leaf cgroup. */ + ret = mkdirat(unified_fd, ".lxc", 0755); + if (ret < 0 && errno != EEXIST) + return log_error_errno(-1, errno, "Failed to create leaf cgroup \".lxc\""); + + target_fd0 = openat(unified_fd, ".lxc/cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW); + if (target_fd0 < 0) + return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\""); + target_fds[0] = target_fd0; + + target_fd1 = openat(unified_fd, "cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW); + if (target_fd1 < 0) + return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\""); + target_fds[1] = target_fd1; + + ret = lxc_abstract_unix_send_fds(sk, target_fds, 2, NULL, 0); + if (ret <= 0) + return log_error_errno(-errno, errno, "Failed to send \".lxc/cgroup.procs\" fds %d and %d", + target_fd0, target_fd1); + + return log_debug(0, "Sent target cgroup fds %d and %d", target_fd0, target_fd1); +} + +static int cgroup_attach_move_into_leaf(const struct lxc_conf *conf, + int *sk_fd, pid_t pid) +{ + __do_close int sk = *sk_fd, target_fd0 = -EBADF, target_fd1 = -EBADF; + int target_fds[2]; + char pidstr[INTTYPE_TO_STRLEN(int64_t) + 1]; + size_t pidstr_len; + ssize_t ret; + + ret = lxc_abstract_unix_recv_fds(sk, target_fds, 2, NULL, 0); + if (ret <= 0) + return log_error_errno(-1, errno, "Failed to receive target cgroup fd"); + target_fd0 = target_fds[0]; + target_fd1 = target_fds[1]; + + pidstr_len = sprintf(pidstr, INT64_FMT, (int64_t)pid); + + ret = lxc_write_nointr(target_fd0, pidstr, pidstr_len); + if (ret > 0 && ret == pidstr_len) + return log_debug(0, "Moved process into target cgroup via fd %d", target_fd0); + + ret = lxc_write_nointr(target_fd1, pidstr, pidstr_len); + if (ret > 0 && ret == pidstr_len) + return log_debug(0, "Moved process into target cgroup via fd %d", target_fd1); + + return log_debug_errno(-1, errno, "Failed to move process into target cgroup via fd %d and %d", + target_fd0, target_fd1); +} + +struct userns_exec_unified_attach_data { + const struct lxc_conf *conf; + int unified_fd; + int sk_pair[2]; + pid_t pid; +}; + +static int cgroup_unified_attach_child_wrapper(void *data) +{ + struct userns_exec_unified_attach_data *args = data; + + if (!args->conf || args->unified_fd < 0 || args->pid <= 0 || + args->sk_pair[0] < 0 || args->sk_pair[1] < 0) + return ret_errno(EINVAL); + + close_prot_errno_disarm(args->sk_pair[0]); + return cgroup_attach_create_leaf(args->conf, args->unified_fd, + &args->sk_pair[1]); +} + +static int cgroup_unified_attach_parent_wrapper(void *data) +{ + struct userns_exec_unified_attach_data *args = data; + + if (!args->conf || args->unified_fd < 0 || args->pid <= 0 || + args->sk_pair[0] < 0 || args->sk_pair[1] < 0) + return ret_errno(EINVAL); + + close_prot_errno_disarm(args->sk_pair[1]); + return cgroup_attach_move_into_leaf(args->conf, &args->sk_pair[0], + args->pid); +} + +int cgroup_attach(const struct lxc_conf *conf, const char *name, + const char *lxcpath, pid_t pid) +{ + __do_close int unified_fd = -EBADF; + int ret; + + if (!conf || !name || !lxcpath || pid <= 0) + return ret_errno(EINVAL); + + unified_fd = lxc_cmd_get_cgroup2_fd(name, lxcpath); + if (unified_fd < 0) + return ret_errno(EBADF); + + if (!lxc_list_empty(&conf->id_map)) { + struct userns_exec_unified_attach_data args = { + .conf = conf, + .unified_fd = unified_fd, + .pid = pid, + }; + + ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, args.sk_pair); + if (ret < 0) + return -errno; + + ret = userns_exec_minimal(conf, + cgroup_unified_attach_parent_wrapper, + &args, + cgroup_unified_attach_child_wrapper, + &args); + } else { + ret = cgroup_attach_leaf(conf, unified_fd, pid); + } + + return ret; +} + +/* Technically, we're always at a delegation boundary here (This is especially + * true when cgroup namespaces are available.). The reasoning is that in order + * for us to have been able to start a container in the first place the root + * cgroup must have been a leaf node. Now, either the container's init system + * has populated the cgroup and kept it as a leaf node or it has created + * subtrees. In the former case we will simply attach to the leaf node we + * created when we started the container in the latter case we create our own + * cgroup for the attaching process. + */ +static int __cg_unified_attach(const struct hierarchy *h, + const struct lxc_conf *conf, const char *name, + const char *lxcpath, pid_t pid, + const char *controller) +{ + __do_close int unified_fd = -EBADF; + __do_free char *path = NULL, *cgroup = NULL; + int ret; + + if (!conf || !name || !lxcpath || pid <= 0) + return ret_errno(EINVAL); + + ret = cgroup_attach(conf, name, lxcpath, pid); + if (ret == 0) + return log_trace(0, "Attached to unified cgroup via command handler"); + if (ret != -EBADF) + return log_error_errno(ret, errno, "Failed to attach to unified cgroup"); + + /* Fall back to retrieving the path for the unified cgroup. */ + cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller); + /* not running */ + if (!cgroup) + return 0; + + path = must_make_path(h->mountpoint, cgroup, NULL); + + unified_fd = open(path, O_PATH | O_DIRECTORY | O_CLOEXEC); + if (unified_fd < 0) + return ret_errno(EBADF); + + if (!lxc_list_empty(&conf->id_map)) { + struct userns_exec_unified_attach_data args = { + .conf = conf, + .unified_fd = unified_fd, + .pid = pid, + }; + + ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, args.sk_pair); + if (ret < 0) + return -errno; + + ret = userns_exec_minimal(conf, + cgroup_unified_attach_parent_wrapper, + &args, + cgroup_unified_attach_child_wrapper, + &args); + } else { + ret = cgroup_attach_leaf(conf, unified_fd, pid); + } + + return ret; +} + +__cgfsng_ops static bool isulad_cgfsng_attach(struct cgroup_ops *ops, + const struct lxc_conf *conf, + const char *name, const char *lxcpath, + pid_t pid) +{ + int len, ret; + char pidstr[INTTYPE_TO_STRLEN(pid_t)]; + + if (!ops) + return ret_set_errno(false, ENOENT); + +#ifdef HAVE_ISULAD + if (ops->no_controller) { + DEBUG("no controller found, isgnore isulad_cgfsng_attach"); + return true; + } +#endif + + if (!ops->hierarchies) + return true; + + len = snprintf(pidstr, sizeof(pidstr), "%d", pid); + if (len < 0 || (size_t)len >= sizeof(pidstr)) + return false; + + for (int i = 0; ops->hierarchies[i]; i++) { + __do_free char *fullpath = NULL, *path = NULL; + struct hierarchy *h = ops->hierarchies[i]; + + if (h->version == CGROUP2_SUPER_MAGIC) { + ret = __cg_unified_attach(h, conf, name, lxcpath, pid, + h->controllers[0]); + if (ret < 0) + return false; + + continue; + } + + path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]); + /* not running */ + if (!path) + return false; + + fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs"); + ret = lxc_write_to_file(fullpath, pidstr, len, false, 0666); + if (ret < 0) + return log_error_errno(false, errno, "Failed to attach %d to %s", + (int)pid, fullpath); + } + + return true; +} + +__cgfsng_ops static int isulad_cgfsng_get(struct cgroup_ops *ops, const char *filename, + char *value, size_t len, const char *name, + const char *lxcpath) +{ + int ret = -1; + size_t controller_len; + char *controller, *p, *path; + struct hierarchy *h; + + controller_len = strlen(filename); + controller = alloca(controller_len + 1); + (void)strlcpy(controller, filename, controller_len + 1); + + p = strchr(controller, '.'); + if (p) + *p = '\0'; + + const char *ori_path = ops->get_cgroup(ops, controller); + if (ori_path == NULL) { + ERROR("Failed to get cgroup path:%s", controller); + return -1; + } + path = safe_strdup(ori_path); + + h = get_hierarchy(ops, controller); + if (h) { + char *fullpath; + + fullpath = build_full_cgpath_from_monitorpath(h, path, filename); + ret = lxc_read_from_file(fullpath, value, len); + free(fullpath); + } + free(path); + + return ret; +} + +static int device_cgroup_parse_access(struct device_item *device, const char *val) +{ + for (int count = 0; count < 3; count++, val++) { + switch (*val) { + case 'r': + device->access[count] = *val; + break; + case 'w': + device->access[count] = *val; + break; + case 'm': + device->access[count] = *val; + break; + case '\n': + case '\0': + count = 3; + break; + default: + return ret_errno(EINVAL); + } + } + + return 0; +} + +int device_cgroup_rule_parse(struct device_item *device, const char *key, + const char *val) +{ + int count, ret; + char temp[50]; + + if (strcmp("devices.allow", key) == 0) + device->allow = 1; + else + device->allow = 0; + + if (strcmp(val, "a") == 0) { + /* global rule */ + device->type = 'a'; + device->major = -1; + device->minor = -1; + device->global_rule = device->allow + ? LXC_BPF_DEVICE_CGROUP_BLACKLIST + : LXC_BPF_DEVICE_CGROUP_WHITELIST; + device->allow = -1; + return 0; + } + + /* local rule */ + device->global_rule = LXC_BPF_DEVICE_CGROUP_LOCAL_RULE; + + switch (*val) { + case 'a': + __fallthrough; + case 'b': + __fallthrough; + case 'c': + device->type = *val; + break; + default: + return -1; + } + + val++; + if (!isspace(*val)) + return -1; + val++; + if (*val == '*') { + device->major = -1; + val++; + } else if (isdigit(*val)) { + memset(temp, 0, sizeof(temp)); + for (count = 0; count < sizeof(temp) - 1; count++) { + temp[count] = *val; + val++; + if (!isdigit(*val)) + break; + } + ret = lxc_safe_int(temp, &device->major); + if (ret) + return -1; + } else { + return -1; + } + if (*val != ':') + return -1; + val++; + + /* read minor */ + if (*val == '*') { + device->minor = -1; + val++; + } else if (isdigit(*val)) { + memset(temp, 0, sizeof(temp)); + for (count = 0; count < sizeof(temp) - 1; count++) { + temp[count] = *val; + val++; + if (!isdigit(*val)) + break; + } + ret = lxc_safe_int(temp, &device->minor); + if (ret) + return -1; + } else { + return -1; + } + if (!isspace(*val)) + return -1; + + return device_cgroup_parse_access(device, ++val); +} + +__cgfsng_ops static int isulad_cgfsng_set(struct cgroup_ops *ops, + const char *filename, const char *value, + const char *name, const char *lxcpath) +{ + int ret = -1; + size_t controller_len; + char *controller, *p, *path; + struct hierarchy *h; + + controller_len = strlen(filename); + controller = alloca(controller_len + 1); + (void)strlcpy(controller, filename, controller_len + 1); + + p = strchr(controller, '.'); + if (p) + *p = '\0'; + + const char *ori_path = ops->get_cgroup(ops, controller); + if (ori_path == NULL) { + ERROR("Failed to get cgroup path:%s", controller); + return -1; + } + path = safe_strdup(ori_path); + + h = get_hierarchy(ops, controller); + if (h) { + char *fullpath; + fullpath = build_full_cgpath_from_monitorpath(h, path, filename); + + if (strcmp(filename, "io.weight") == 0 || strcmp(filename, "io.bfq.weight") == 0) { + if (!file_exists(fullpath)) { + free(path); + free(fullpath); + return 0; + } + } + + ret = lxc_write_to_file(fullpath, value, strlen(value), false, 0666); + free(fullpath); + } + free(path); + + return ret; +} + +/* take devices cgroup line + * /dev/foo rwx + * and convert it to a valid + * type major:minor mode + * line. Return <0 on error. Dest is a preallocated buffer long enough to hold + * the output. + */ +static int device_cgroup_rule_parse_devpath(struct device_item *device, + const char *devpath) +{ + __do_free char *path = NULL; + char *mode = NULL; + int n_parts, ret; + char *p; + struct stat sb; + + path = must_copy_string(devpath); + + /* + * Read path followed by mode. Ignore any trailing text. + * A ' # comment' would be legal. Technically other text is not + * legal, we could check for that if we cared to. + */ + for (n_parts = 1, p = path; *p; p++) { + if (*p != ' ') + continue; + *p = '\0'; + + if (n_parts != 1) + break; + p++; + n_parts++; + + while (*p == ' ') + p++; + + mode = p; + + if (*p == '\0') + return ret_set_errno(-1, EINVAL); + } + + if (device_cgroup_parse_access(device, mode) < 0) + return -1; + + if (n_parts == 1) + return ret_set_errno(-1, EINVAL); + + ret = stat(path, &sb); + if (ret < 0) + return ret_set_errno(-1, errno); + + mode_t m = sb.st_mode & S_IFMT; + switch (m) { + case S_IFBLK: + device->type = 'b'; + break; + case S_IFCHR: + device->type = 'c'; + break; + default: + return log_error_errno(-1, EINVAL, "Unsupported device type %i for \"%s\"", m, path); + } + + device->major = MAJOR(sb.st_rdev); + device->minor = MINOR(sb.st_rdev); + device->allow = 1; + device->global_rule = LXC_BPF_DEVICE_CGROUP_LOCAL_RULE; + + return 0; +} + +static int convert_devpath(const char *invalue, char *dest) +{ + struct device_item device = {0}; + int ret; + + ret = device_cgroup_rule_parse_devpath(&device, invalue); + if (ret < 0) + return -1; + + ret = snprintf(dest, 50, "%c %d:%d %s", device.type, device.major, + device.minor, device.access); + if (ret < 0 || ret >= 50) + return log_error_errno(-1, ENAMETOOLONG, "Error on configuration value \"%c %d:%d %s\" (max 50 chars)", + device.type, device.major, device.minor, device.access); + + return 0; +} + +/* Called from setup_limits - here we have the container's cgroup_data because + * we created the cgroups. + */ +static int isulad_cg_legacy_get_data(struct cgroup_ops *ops, const char *filename, + char *value, size_t len) +{ + char *fullpath = NULL; + char *p = NULL; + struct hierarchy *h = NULL; + int ret = 0; + char *controller = NULL; + + len = strlen(filename); + if (SIZE_MAX - 1 < len) { + errno = EINVAL; + return -1; + } + controller = calloc(1, len + 1); + if (controller == NULL) { + errno = ENOMEM; + return -1; + } + (void)strlcpy(controller, filename, len + 1); + + p = strchr(controller, '.'); + if (p) + *p = '\0'; + + + h = get_hierarchy(ops, controller); + if (!h) { + ERROR("Failed to setup limits for the \"%s\" controller. " + "The controller seems to be unused by \"cgfsng\" cgroup " + "driver or not enabled on the cgroup hierarchy", + controller); + errno = ENOENT; + free(controller); + return -ENOENT; + } + + fullpath = must_make_path(h->container_full_path, filename, NULL); + ret = lxc_read_from_file(fullpath, value, len); + free(fullpath); + free(controller); + return ret; +} + +static int isulad_cg_legacy_set_data(struct cgroup_ops *ops, const char *filename, + const char *value) +{ + size_t len; + char *fullpath, *p; + /* "b|c <2^64-1>:<2^64-1> r|w|m" = 47 chars max */ + char converted_value[50]; + struct hierarchy *h; + int ret = 0; + char *controller = NULL; + int retry_count = 0; + int max_retry = 10; + char *container_cgroup = ops->container_cgroup; + + len = strlen(filename); + controller = alloca(len + 1); + (void)strlcpy(controller, filename, len + 1); + + p = strchr(controller, '.'); + if (p) + *p = '\0'; + + if (strcmp("devices.allow", filename) == 0 && value[0] == '/') { + ret = convert_devpath(value, converted_value); + if (ret < 0) + return ret; + value = converted_value; + } + + h = get_hierarchy(ops, controller); + if (!h) { + ERROR("Failed to setup limits for the \"%s\" controller. " + "The controller seems to be unused by \"cgfsng\" cgroup " + "driver or not enabled on the cgroup hierarchy", + controller); + errno = ENOENT; + return -ENOENT; + } + + fullpath = must_make_path(h->container_full_path, filename, NULL); + +retry: + ret = lxc_write_to_file(fullpath, value, strlen(value), false, 0666); + if (ret != 0) { + if (retry_count < max_retry) { + SYSERROR("setting cgroup config for ready process caused \"failed to write %s to %s\".", value, fullpath); + (void)isulad_cg_legacy_handle_cpuset_hierarchy(h, container_cgroup); + (void)isulad_mkdir_eexist_on_last(h->container_full_path, 0755); + usleep(100 * 1000); /* 100 millisecond */ + retry_count++; + goto retry; + } + lxc_write_error_message(ops->errfd, + "%s:%d: setting cgroup config for ready process caused failed to write %s to %s: %s", + __FILE__, __LINE__, value, fullpath, strerror(errno)); + } + free(fullpath); + return ret; +} + +__cgfsng_ops static bool isulad_cgfsng_setup_limits_legacy(struct cgroup_ops *ops, + struct lxc_conf *conf, + bool do_devices) +{ + __do_free struct lxc_list *sorted_cgroup_settings = NULL; + struct lxc_list *cgroup_settings = &conf->cgroup; + struct lxc_list *iterator, *next; + struct lxc_cgroup *cg; + bool ret = false; + char value[21 + 1] = { 0 }; + long long int readvalue, setvalue; + + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!conf) + return ret_set_errno(false, EINVAL); + + cgroup_settings = &conf->cgroup; + if (lxc_list_empty(cgroup_settings)) + return true; + + if (!ops->hierarchies) + return ret_set_errno(false, EINVAL); + + if (pure_unified_layout(ops)) + return true; + + sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings); + if (!sorted_cgroup_settings) + return false; + + lxc_list_for_each(iterator, sorted_cgroup_settings) { + cg = iterator->elem; + + if (do_devices == !strncmp("devices", cg->subsystem, 7)) { + const char *cgvalue = cg->value; + if (strcmp(cg->subsystem, "files.limit") == 0) { + if (lxc_safe_long_long(cgvalue, &setvalue) != 0) { + SYSERROR("Invalid integer value %s", cgvalue); + goto out; + } + if (setvalue <= 0) { + cgvalue = "max"; + } + } + if (isulad_cg_legacy_set_data(ops, cg->subsystem, cgvalue)) { + if (do_devices && (errno == EACCES || errno == EPERM)) { + SYSWARN("Failed to set \"%s\" to \"%s\"", cg->subsystem, cgvalue); + continue; + } + SYSERROR("Failed to set \"%s\" to \"%s\"", cg->subsystem, cgvalue); + goto out; + } + DEBUG("Set controller \"%s\" set to \"%s\"", cg->subsystem, cgvalue); + } + + // isulad: check cpu shares + if (strcmp(cg->subsystem, "cpu.shares") == 0) { + if (isulad_cg_legacy_get_data(ops, cg->subsystem, value, sizeof(value) - 1) < 0) { + SYSERROR("Error get %s", cg->subsystem); + goto out; + } + trim(value); + if (lxc_safe_long_long(cg->value, &setvalue) != 0) { + SYSERROR("Invalid value %s", cg->value); + goto out; + } + if (lxc_safe_long_long(value, &readvalue) != 0) { + SYSERROR("Invalid value %s", value); + goto out; + } + if (setvalue > readvalue) { + ERROR("The maximum allowed cpu-shares is %s", value); + lxc_write_error_message(ops->errfd, + "%s:%d: setting cgroup config for ready process caused \"The maximum allowed cpu-shares is %s\".", + __FILE__, __LINE__, value); + goto out; + } else if (setvalue < readvalue) { + ERROR("The minimum allowed cpu-shares is %s", value); + lxc_write_error_message(ops->errfd, + "%s:%d: setting cgroup config for ready process caused \"The minimum allowed cpu-shares is %s\".", + __FILE__, __LINE__, value); + goto out; + } + } + } + + ret = true; + INFO("Limits for the legacy cgroup hierarchies have been setup"); +out: + lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) { + lxc_list_del(iterator); + free(iterator); + } + + return ret; +} + +/* + * Some of the parsing logic comes from the original cgroup device v1 + * implementation in the kernel. + */ +static int bpf_device_cgroup_prepare(struct cgroup_ops *ops, + struct lxc_conf *conf, const char *key, + const char *val) +{ +#ifdef HAVE_STRUCT_BPF_CGROUP_DEV_CTX + struct device_item device_item = {0}; + int ret; + + if (strcmp("devices.allow", key) == 0 && *val == '/') + ret = device_cgroup_rule_parse_devpath(&device_item, val); + else + ret = device_cgroup_rule_parse(&device_item, key, val); + if (ret < 0) + return log_error_errno(-1, EINVAL, "Failed to parse device string %s=%s", key, val); + + ret = bpf_list_add_device(conf, &device_item); + if (ret < 0) + return -1; +#endif + return 0; +} + +__cgfsng_ops static bool isulad_cgfsng_setup_limits(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ + __do_free char *path = NULL; + struct lxc_list *cgroup_settings, *iterator; + struct hierarchy *h; + struct lxc_conf *conf; + + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!ops->hierarchies) + return true; + + if (!ops->container_cgroup) + return ret_set_errno(false, EINVAL); + + if (!handler || !handler->conf) + return ret_set_errno(false, EINVAL); + conf = handler->conf; + + if (lxc_list_empty(&conf->cgroup2)) + return true; + cgroup_settings = &conf->cgroup2; + + if (!pure_unified_layout(ops)) + return true; + + if (!ops->unified) + return false; + h = ops->unified; + + lxc_list_for_each (iterator, cgroup_settings) { + struct lxc_cgroup *cg = iterator->elem; + int ret; + + if (strncmp("devices", cg->subsystem, 7) == 0) { + ret = bpf_device_cgroup_prepare(ops, conf, cg->subsystem, + cg->value); + } else if (strcmp(cg->subsystem, "files.limit") == 0) { + long long int setvalue = 0; + const char *cgvalue = cg->value; + + if (lxc_safe_long_long(cgvalue, &setvalue) != 0) + return log_error(false, "Invalid integer value %s", cgvalue); + + if (setvalue <= 0) + cgvalue = "max"; + + ret = lxc_write_openat(h->container_full_path, + cg->subsystem, cgvalue, + strlen(cgvalue)); + if (ret < 0) + return log_error_errno(false, errno, "Failed to set \"%s\" to \"%s\"", + cg->subsystem, cgvalue); + } else { + if (strcmp(cg->subsystem, "io.weight") == 0 || strcmp(cg->subsystem, "io.bfq.weight") == 0) { + path = must_make_path(h->container_full_path, cg->subsystem, NULL); + if (!file_exists(path)) { + continue; + } + } + ret = lxc_write_openat(h->container_full_path, + cg->subsystem, cg->value, + strlen(cg->value)); + if (ret < 0) + return log_error_errno(false, errno, "Failed to set \"%s\" to \"%s\"", + cg->subsystem, cg->value); + } + TRACE("Set \"%s\" to \"%s\"", cg->subsystem, cg->value); + } + + return log_info(true, "Limits for the unified cgroup hierarchy have been setup"); +} + +__cgfsng_ops bool isulad_cgfsng_devices_activate(struct cgroup_ops *ops, + struct lxc_handler *handler) +{ +#ifdef HAVE_STRUCT_BPF_CGROUP_DEV_CTX + __do_bpf_program_free struct bpf_program *devices = NULL; + int ret; + struct lxc_conf *conf; + struct hierarchy *unified; + struct lxc_list *it; + struct bpf_program *devices_old; + + if (!ops) + return ret_set_errno(false, ENOENT); + + if (!ops->hierarchies) + return true; + + if (!ops->container_cgroup) + return ret_set_errno(false, EEXIST); + + if (!handler || !handler->conf) + return ret_set_errno(false, EINVAL); + conf = handler->conf; + + unified = ops->unified; + if (!unified || !unified->bpf_device_controller || + !unified->container_full_path || lxc_list_empty(&conf->devices)) + return true; + + devices = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE); + if (!devices) + return log_error_errno(false, ENOMEM, "Failed to create new bpf program"); + + ret = bpf_program_init(devices); + if (ret) + return log_error_errno(false, ENOMEM, "Failed to initialize bpf program"); + + lxc_list_for_each(it, &conf->devices) { + struct device_item *cur = it->elem; + + ret = bpf_program_append_device(devices, cur); + if (ret) + return log_error_errno(false, ENOMEM, "Failed to add new rule to bpf device program: type %c, major %d, minor %d, access %s, allow %d, global_rule %d", + cur->type, + cur->major, + cur->minor, + cur->access, + cur->allow, + cur->global_rule); + TRACE("Added rule to bpf device program: type %c, major %d, minor %d, access %s, allow %d, global_rule %d", + cur->type, + cur->major, + cur->minor, + cur->access, + cur->allow, + cur->global_rule); + } + + ret = bpf_program_finalize(devices); + if (ret) + return log_error_errno(false, ENOMEM, "Failed to finalize bpf program"); + + ret = bpf_program_cgroup_attach(devices, BPF_CGROUP_DEVICE, + unified->container_full_path, + BPF_F_ALLOW_MULTI); + if (ret) + return log_error_errno(false, ENOMEM, "Failed to attach bpf program"); + + /* Replace old bpf program. */ + devices_old = move_ptr(conf->cgroup2_devices); + conf->cgroup2_devices = move_ptr(devices); + devices = move_ptr(devices_old); +#endif + return true; +} + +bool __cgfsng_delegate_controllers(struct cgroup_ops *ops, const char *cgroup) +{ + __do_free char *add_controllers = NULL, *base_path = NULL; + __do_free_string_list char **parts = NULL; + struct hierarchy *unified = ops->unified; + ssize_t parts_len; + char **it; + size_t full_len = 0; + + if (!ops->hierarchies || !pure_unified_layout(ops) || + !unified->controllers[0]) + return true; + + /* For now we simply enable all controllers that we have detected by + * creating a string like "+memory +pids +cpu +io". + * TODO: In the near future we might want to support "-<controller>" + * etc. but whether supporting semantics like this make sense will need + * some thinking. + */ + for (it = unified->controllers; it && *it; it++) { + full_len += strlen(*it) + 2; + add_controllers = must_realloc(add_controllers, full_len + 1); + + if (unified->controllers[0] == *it) + add_controllers[0] = '\0'; + + (void)strlcat(add_controllers, "+", full_len + 1); + (void)strlcat(add_controllers, *it, full_len + 1); + + if ((it + 1) && *(it + 1)) + (void)strlcat(add_controllers, " ", full_len + 1); + } + + parts = lxc_string_split(cgroup, '/'); + if (!parts) + return false; + + parts_len = lxc_array_len((void **)parts); + if (parts_len > 0) + parts_len--; + + base_path = must_make_path(unified->mountpoint, unified->container_base_path, NULL); + for (ssize_t i = -1; i < parts_len; i++) { + int ret; + __do_free char *target = NULL; + + if (i >= 0) + base_path = must_append_path(base_path, parts[i], NULL); + target = must_make_path(base_path, "cgroup.subtree_control", NULL); + ret = lxc_writeat(-1, target, add_controllers, full_len); + if (ret < 0) + return log_error_errno(false, errno, "Could not enable \"%s\" controllers in the unified cgroup \"%s\"", + add_controllers, target); + TRACE("Enable \"%s\" controllers in the unified cgroup \"%s\"", add_controllers, target); + } + + return true; +} + +__cgfsng_ops bool isulad_cgfsng_monitor_delegate_controllers(struct cgroup_ops *ops) +{ + return true; +} + +__cgfsng_ops bool isulad_cgfsng_payload_delegate_controllers(struct cgroup_ops *ops) +{ + if (!ops) + return ret_set_errno(false, ENOENT); + +#ifdef HAVE_ISULAD + if (ops->no_controller) { + DEBUG("no controller found, isgnore isulad_cgfsng_payload_delegate_controllers"); + return true; + } +#endif + + return __cgfsng_delegate_controllers(ops, ops->container_cgroup); +} + +static bool cgroup_use_wants_controllers(const struct cgroup_ops *ops, + char **controllers) +{ + if (!ops->cgroup_use) + return true; + + for (char **cur_ctrl = controllers; cur_ctrl && *cur_ctrl; cur_ctrl++) { + bool found = false; + + for (char **cur_use = ops->cgroup_use; cur_use && *cur_use; cur_use++) { + if (strcmp(*cur_use, *cur_ctrl) != 0) + continue; + + found = true; + break; + } + + if (found) + continue; + + return false; + } + + return true; +} + +static void cg_unified_delegate(char ***delegate) +{ + __do_free char *buf = NULL; + char *standard[] = {"cgroup.subtree_control", "cgroup.threads", NULL}; + char *token; + int idx; + + buf = read_file("/sys/kernel/cgroup/delegate"); + if (!buf) { + for (char **p = standard; p && *p; p++) { + idx = append_null_to_list((void ***)delegate); + (*delegate)[idx] = must_copy_string(*p); + } + SYSWARN("Failed to read /sys/kernel/cgroup/delegate"); + return; + } + + lxc_iterate_parts (token, buf, " \t\n") { + /* + * We always need to chown this for both cgroup and + * cgroup2. + */ + if (strcmp(token, "cgroup.procs") == 0) + continue; + + idx = append_null_to_list((void ***)delegate); + (*delegate)[idx] = must_copy_string(token); + } +} + +/* At startup, parse_hierarchies finds all the info we need about cgroup + * mountpoints and current cgroups, and stores it in @d. + */ +static int cg_hybrid_init(struct cgroup_ops *ops, bool relative, bool unprivileged) +{ + __do_free char *basecginfo = NULL, *line = NULL; + __do_free_string_list char **klist = NULL, **nlist = NULL; + __do_fclose FILE *f = NULL; + int ret; + size_t len = 0; + + /* Root spawned containers escape the current cgroup, so use init's + * cgroups as our base in that case. + */ + if (!relative && (geteuid() == 0)) + basecginfo = read_file("/proc/1/cgroup"); + else + basecginfo = read_file("/proc/self/cgroup"); + if (!basecginfo) + return ret_set_errno(-1, ENOMEM); + + ret = get_existing_subsystems(&klist, &nlist); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to retrieve available legacy cgroup controllers"); + + f = fopen("/proc/self/mountinfo", "re"); + if (!f) + return log_error_errno(-1, errno, "Failed to open \"/proc/self/mountinfo\""); + + lxc_cgfsng_print_basecg_debuginfo(basecginfo, klist, nlist); + + while (getline(&line, &len, f) != -1) { + __do_free char *base_cgroup = NULL, *mountpoint = NULL; + __do_free_string_list char **controller_list = NULL; + int type; + struct hierarchy *new; + + type = get_cgroup_version(line); + if (type == 0) + continue; + + if (type == CGROUP2_SUPER_MAGIC && ops->unified) + continue; + + if (ops->cgroup_layout == CGROUP_LAYOUT_UNKNOWN) { + if (type == CGROUP2_SUPER_MAGIC) + ops->cgroup_layout = CGROUP_LAYOUT_UNIFIED; + else if (type == CGROUP_SUPER_MAGIC) + ops->cgroup_layout = CGROUP_LAYOUT_LEGACY; + } else if (ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) { + if (type == CGROUP_SUPER_MAGIC) + ops->cgroup_layout = CGROUP_LAYOUT_HYBRID; + } else if (ops->cgroup_layout == CGROUP_LAYOUT_LEGACY) { + if (type == CGROUP2_SUPER_MAGIC) + ops->cgroup_layout = CGROUP_LAYOUT_HYBRID; + } + + controller_list = cg_hybrid_get_controllers(klist, nlist, line, type); + if (!controller_list && type == CGROUP_SUPER_MAGIC) + continue; + + if (type == CGROUP_SUPER_MAGIC) + if (controller_list_is_dup(ops->hierarchies, controller_list)) { + TRACE("Skipping duplicating controller"); + continue; + } + + mountpoint = cg_hybrid_get_mountpoint(line); + if (!mountpoint) { + WARN("Failed parsing mountpoint from \"%s\"", line); + continue; + } + + if (type == CGROUP_SUPER_MAGIC) + base_cgroup = cg_hybrid_get_current_cgroup(basecginfo, controller_list[0], CGROUP_SUPER_MAGIC); + else + base_cgroup = cg_hybrid_get_current_cgroup(basecginfo, NULL, CGROUP2_SUPER_MAGIC); + if (!base_cgroup) { + WARN("Failed to find current cgroup"); + continue; + } + + trim(base_cgroup); + prune_init_scope(base_cgroup); + + /* isulad: do not test writeable, if we run isulad in docker without cgroup namespace. + * the base_cgroup will be docker/XXX.., mountpoint+base_cgroup may be not exist */ + + /* + * reason:base cgroup may be started with /system.slice when cg_hybrid_init + * read /proc/1/cgroup on host, and cgroup init will set all containers + * cgroup path under /sys/fs/cgroup/<controller>/system.slice/xxx/lxc + * directory, this is not consistent with docker. The default cgroup path + * should be under /sys/fs/cgroup/<controller>/lxc directory. + */ + + if (strlen(base_cgroup) > 1 && base_cgroup[0] == '/') { + base_cgroup[1] = '\0'; + } + + if (type == CGROUP2_SUPER_MAGIC) { + char *cgv2_ctrl_path; + + cgv2_ctrl_path = must_make_path(mountpoint, base_cgroup, + "cgroup.controllers", + NULL); + + controller_list = cg_unified_get_controllers(cgv2_ctrl_path); + free(cgv2_ctrl_path); + if (!controller_list) { + controller_list = cg_unified_make_empty_controller(); + TRACE("No controllers are enabled for " + "delegation in the unified hierarchy"); + } + } + + /* Exclude all controllers that cgroup use does not want. */ + if (!cgroup_use_wants_controllers(ops, controller_list)) { + TRACE("Skipping controller"); + continue; + } + + new = add_hierarchy(&ops->hierarchies, move_ptr(controller_list), move_ptr(mountpoint), move_ptr(base_cgroup), type); + if (type == CGROUP2_SUPER_MAGIC && !ops->unified) { + if (unprivileged) + cg_unified_delegate(&new->cgroup2_chown); + ops->unified = new; + } + } + + TRACE("Writable cgroup hierarchies:"); + lxc_cgfsng_print_hierarchies(ops); + + /* verify that all controllers in cgroup.use and all crucial + * controllers are accounted for + */ + if (!all_controllers_found(ops)) + return log_error_errno(-1, ENOENT, "Failed to find all required controllers"); + + return 0; +} + +/* Get current cgroup from /proc/self/cgroup for the cgroupfs v2 hierarchy. */ +static char *cg_unified_get_current_cgroup(bool relative) +{ + __do_free char *basecginfo = NULL; + char *copy; + char *base_cgroup; + + if (!relative && (geteuid() == 0)) + basecginfo = read_file("/proc/1/cgroup"); + else + basecginfo = read_file("/proc/self/cgroup"); + if (!basecginfo) + return NULL; + + base_cgroup = strstr(basecginfo, "0::/"); + if (!base_cgroup) + return NULL; + + base_cgroup = base_cgroup + 3; + copy = copy_to_eol(base_cgroup); + if (!copy) + return NULL; + + return trim(copy); +} + +static int cg_unified_init(struct cgroup_ops *ops, bool relative, + bool unprivileged) +{ + __do_free char *subtree_path = NULL; + int ret; + char *mountpoint; + char **delegatable; + struct hierarchy *new; + char *base_cgroup = NULL; + + ret = unified_cgroup_hierarchy(); + if (ret == -ENOMEDIUM) + return ret_errno(ENOMEDIUM); + + if (ret != CGROUP2_SUPER_MAGIC) + return 0; + + base_cgroup = cg_unified_get_current_cgroup(relative); + if (!base_cgroup) + return ret_errno(EINVAL); + if (!relative) + prune_init_scope(base_cgroup); + + /* + * We assume that the cgroup we're currently in has been delegated to + * us and we are free to further delege all of the controllers listed + * in cgroup.controllers further down the hierarchy. + */ + mountpoint = must_copy_string(DEFAULT_CGROUP_MOUNTPOINT); + subtree_path = must_make_path(mountpoint, base_cgroup, "cgroup.controllers", NULL); + delegatable = cg_unified_get_controllers(subtree_path); + if (!delegatable) + delegatable = cg_unified_make_empty_controller(); + if (!delegatable[0]) { + TRACE("No controllers are enabled for delegation"); +#ifdef HAVE_ISULAD + ops->no_controller = true; +#endif + } + + /* TODO: If the user requested specific controllers via lxc.cgroup.use + * we should verify here. The reason I'm not doing it right is that I'm + * not convinced that lxc.cgroup.use will be the future since it is a + * global property. I much rather have an option that lets you request + * controllers per container. + */ + + new = add_hierarchy(&ops->hierarchies, delegatable, mountpoint, base_cgroup, CGROUP2_SUPER_MAGIC); + if (unprivileged) + cg_unified_delegate(&new->cgroup2_chown); + + if (bpf_devices_cgroup_supported()) + new->bpf_device_controller = 1; + + ops->cgroup_layout = CGROUP_LAYOUT_UNIFIED; + ops->unified = new; + + return CGROUP2_SUPER_MAGIC; +} + +static int isulad_cg_init(struct cgroup_ops *ops, struct lxc_conf *conf) +{ + int ret; + const char *tmp; + bool relative = conf->cgroup_meta.relative; + + tmp = lxc_global_config_value("lxc.cgroup.use"); + if (tmp) { + __do_free char *pin = NULL; + char *chop, *cur; + + pin = must_copy_string(tmp); + chop = pin; + + lxc_iterate_parts(cur, chop, ",") + must_append_string(&ops->cgroup_use, cur); + } + + ret = cg_unified_init(ops, relative, !lxc_list_empty(&conf->id_map)); + if (ret < 0) + return -1; + + if (ret == CGROUP2_SUPER_MAGIC) + return 0; + + return cg_hybrid_init(ops, relative, !lxc_list_empty(&conf->id_map)); +} + +__cgfsng_ops static int isulad_cgfsng_data_init(struct cgroup_ops *ops, struct lxc_conf *conf) +{ + const char *cgroup_pattern; + const char *cgroup_tree; + __do_free char *container_cgroup = NULL, *__cgroup_tree = NULL; + size_t len; + + if (!ops) + return ret_set_errno(-1, ENOENT); + + /* copy system-wide cgroup information */ + cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); + if (cgroup_pattern && strcmp(cgroup_pattern, "") != 0) + ops->cgroup_pattern = must_copy_string(cgroup_pattern); + + if (conf->cgroup_meta.dir) { + cgroup_tree = conf->cgroup_meta.dir; + container_cgroup = must_concat(&len, cgroup_tree, "/", conf->name, NULL); + } else if (ops->cgroup_pattern) { + __cgroup_tree = lxc_string_replace("%n", conf->name, ops->cgroup_pattern); + if (!__cgroup_tree) + return ret_set_errno(-1, ENOMEM); + + cgroup_tree = __cgroup_tree; + container_cgroup = must_concat(&len, cgroup_tree, NULL); + } else { + cgroup_tree = NULL; + container_cgroup = must_concat(&len, conf->name, NULL); + } + if (!container_cgroup) + return ret_set_errno(-1, ENOMEM); + + ops->container_cgroup = move_ptr(container_cgroup); + + return 0; +} + +struct cgroup_ops *cgfsng_ops_init(struct lxc_conf *conf) +{ + __do_free struct cgroup_ops *cgfsng_ops = NULL; + + cgfsng_ops = malloc(sizeof(struct cgroup_ops)); + if (!cgfsng_ops) + return ret_set_errno(NULL, ENOMEM); + + memset(cgfsng_ops, 0, sizeof(struct cgroup_ops)); + cgfsng_ops->cgroup_layout = CGROUP_LAYOUT_UNKNOWN; + + if (isulad_cg_init(cgfsng_ops, conf)) + return NULL; + + cgfsng_ops->data_init = isulad_cgfsng_data_init; + + cgfsng_ops->errfd = conf ? conf->errpipe[1] : -1; + cgfsng_ops->get_cgroup_full_path = isulad_cgfsng_get_cgroup_full_path; + cgfsng_ops->payload_destroy = isulad_cgfsng_payload_destroy; + cgfsng_ops->monitor_destroy = isulad_cgfsng_monitor_destroy; + cgfsng_ops->monitor_create = isulad_cgfsng_monitor_create; + cgfsng_ops->monitor_enter = isulad_cgfsng_monitor_enter; + cgfsng_ops->monitor_delegate_controllers = isulad_cgfsng_monitor_delegate_controllers; + cgfsng_ops->payload_delegate_controllers = isulad_cgfsng_payload_delegate_controllers; + cgfsng_ops->payload_create = isulad_cgfsng_payload_create; + cgfsng_ops->payload_enter = isulad_cgfsng_payload_enter; + cgfsng_ops->payload_finalize = isulad_cgfsng_payload_finalize; + cgfsng_ops->escape = isulad_cgfsng_escape; + cgfsng_ops->num_hierarchies = isulad_cgfsng_num_hierarchies; + cgfsng_ops->get_hierarchies = isulad_cgfsng_get_hierarchies; + cgfsng_ops->get_cgroup = isulad_cgfsng_get_cgroup; + cgfsng_ops->get = isulad_cgfsng_get; + cgfsng_ops->set = isulad_cgfsng_set; + cgfsng_ops->freeze = isulad_cgfsng_freeze; + cgfsng_ops->unfreeze = isulad_cgfsng_unfreeze; + cgfsng_ops->setup_limits_legacy = isulad_cgfsng_setup_limits_legacy; + cgfsng_ops->setup_limits = isulad_cgfsng_setup_limits; + cgfsng_ops->driver = "isulad_cgfsng"; + cgfsng_ops->version = "1.0.0"; + cgfsng_ops->attach = isulad_cgfsng_attach; + cgfsng_ops->chown = isulad_cgfsng_chown; + cgfsng_ops->mount = isulad_cgfsng_mount; + cgfsng_ops->devices_activate = isulad_cgfsng_devices_activate; + + return move_ptr(cgfsng_ops); +} diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 07be1d5..2188b31 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -90,6 +90,10 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd) [LXC_CMD_GET_CGROUP_FD] = "get_cgroup_fd", [LXC_CMD_GET_LIMIT_CGROUP_FD] = "get_limit_cgroup_fd", [LXC_CMD_GET_SYSTEMD_SCOPE] = "get_systemd_scope", +#ifdef HAVE_ISULAD + [LXC_CMD_SET_TERMINAL_FIFOS] = "set_terminal_fifos", + [LXC_CMD_SET_TERMINAL_WINCH] = "set_terminal_winch", +#endif }; if (cmd >= LXC_CMD_MAX) @@ -223,6 +227,12 @@ static ssize_t lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) break; } +#ifdef HAVE_ISULAD + /*isulad: add timeout 3s to avoid long block due to [lxc monitor] error*/ + if (lxc_socket_set_timeout(sock, 3, 3) != 0) { + return syserror_ret(-1, "Failed to set timeout"); + } +#endif /* Receive the first response including file descriptors if any. */ bytes_recv = lxc_cmd_rsp_recv_fds(sock, fds, rsp, cur_cmdstr); if (bytes_recv < 0) @@ -1576,7 +1586,12 @@ int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); if (ret < 0) +#ifdef HAVE_ISULAD + // for check connect failed, we just log warnning + return log_warn_errno(-1, errno, "Failed to serve state clients"); +#else return log_error_errno(-1, errno, "Failed to serve state clients"); +#endif return 0; } @@ -1916,6 +1931,125 @@ static int lxc_cmd_rsp_send_enosys(int fd, int id) return syserror_set(-ENOSYS, "Invalid command id %d", id); } +#ifdef HAVE_ISULAD +/* + * isulad: lxc_cmd_set_terminal_fifos: Set the fifos used for the container as terminal input/output + * + * @hashed_sock_name: hashed socket name + * + * Returns 0 when success, else when fail. + */ +int lxc_cmd_set_terminal_fifos(const char *name, const char *lxcpath, const char *in_fifo, + const char *out_fifo, const char *err_fifo) +{ + int ret = 0; + bool stopped = false; + int len = 0; + char *tmp = NULL; + const char *split = "&&&&", *none_fifo_name = "none"; + const char *cmd_in_fifo = in_fifo ? in_fifo : none_fifo_name; + const char *cmd_out_fifo = out_fifo ? out_fifo : none_fifo_name; + const char *cmd_err_fifo = err_fifo ? err_fifo : none_fifo_name; + + if (len + strlen(cmd_in_fifo) + strlen(split) + strlen(cmd_out_fifo) + + strlen(split) + strlen(cmd_err_fifo) == SIZE_MAX) + return -1; + len += strlen(cmd_in_fifo) + strlen(split) + strlen(cmd_out_fifo) + strlen(split) + strlen(cmd_err_fifo) + 1; + tmp = malloc(len); + if (tmp == NULL) + return -1; + ret = snprintf(tmp, len, "%s%s%s%s%s", cmd_in_fifo, split, cmd_out_fifo, split, cmd_err_fifo); + if (ret < 0 || ret >= len) { + ERROR("Failed to snprintf in fifo of command"); + free(tmp); + return -1; + } + + struct lxc_cmd_rr cmd = { + .req = { + .cmd = LXC_CMD_SET_TERMINAL_FIFOS, + .datalen = strlen(tmp)+1, + .data = tmp, + }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); + if (ret < 0) { + ERROR("Failed to send command to container"); + free(tmp); + return -1; + } + + if (cmd.rsp.ret != 0) { + ERROR("Command response error:%d", cmd.rsp.ret); + free(tmp); + return -1; + } + + free(tmp); + return 0; +} + +static int lxc_cmd_set_terminal_fifos_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler, struct lxc_epoll_descr *descr) +{ + struct lxc_cmd_rsp rsp; + memset(&rsp, 0, sizeof(rsp)); + + rsp.ret = lxc_terminal_add_fifos(handler->conf, req->data);; + + return lxc_cmd_rsp_send_reap(fd, &rsp); +} + +struct lxc_cmd_set_terminal_winch_request { + unsigned int height; + unsigned int width; +}; + +int lxc_cmd_set_terminal_winch(const char *name, const char *lxcpath, unsigned int height, unsigned int width) +{ + int ret = 0; + bool stopped = false; + struct lxc_cmd_set_terminal_winch_request data = { 0 }; + + data.height = height; + data.width = width; + + struct lxc_cmd_rr cmd = { + .req = { + .cmd = LXC_CMD_SET_TERMINAL_WINCH, + .datalen = sizeof(struct lxc_cmd_set_terminal_winch_request), + .data = &data, + }, + }; + + ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); + if (ret < 0) { + ERROR("Failed to send command to container"); + return -1; + } + + if (cmd.rsp.ret != 0) { + ERROR("Command response error:%d", cmd.rsp.ret); + return -1; + } + return 0; +} + +static int lxc_cmd_set_terminal_winch_callback(int fd, struct lxc_cmd_req *req, + struct lxc_handler *handler, struct lxc_epoll_descr *descr) +{ + struct lxc_cmd_rsp rsp; + struct lxc_cmd_set_terminal_winch_request *data = (struct lxc_cmd_set_terminal_winch_request *)(req->data); + memset(&rsp, 0, sizeof(rsp)); + + rsp.ret = lxc_set_terminal_winsz(&handler->conf->console, data->height, data->width);; + + return lxc_cmd_rsp_send_reap(fd, &rsp); + +} +#endif + static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler, struct lxc_async_descr *descr) @@ -1951,6 +2085,10 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, [LXC_CMD_GET_CGROUP_FD] = lxc_cmd_get_cgroup_fd_callback, [LXC_CMD_GET_LIMIT_CGROUP_FD] = lxc_cmd_get_limit_cgroup_fd_callback, [LXC_CMD_GET_SYSTEMD_SCOPE] = lxc_cmd_get_systemd_scope_callback, +#ifdef HAVE_ISULAD + [LXC_CMD_SET_TERMINAL_FIFOS] = lxc_cmd_set_terminal_fifos_callback, + [LXC_CMD_SET_TERMINAL_WINCH] = lxc_cmd_set_terminal_winch_callback, +#endif }; if (req->cmd >= LXC_CMD_MAX) @@ -2104,6 +2242,46 @@ static int lxc_cmd_accept(int fd, uint32_t events, void *data, return ret; } +#ifdef HAVE_ISULAD +// isulad required named unix socket to make safe connect, +// so need to replace abstrace unix socket +int lxc_server_init(const char *name, const char *lxcpath, const char *suffix) +{ + __do_close int fd = -EBADF; + int ret; + char path[LXC_AUDS_ADDR_LEN] = {0}; + __do_free char *runtime_sock_dir = NULL; + + runtime_sock_dir = generate_named_unix_sock_dir(name); + if (runtime_sock_dir == NULL) + return -1; + + if (mkdir_p(runtime_sock_dir, 0700) < 0) + return log_error_errno(-1, errno, "Failed to create container runtime unix sock directory %s", path); + + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) { + if (errno == EADDRINUSE) { + WARN("Container \"%s\" appears to be already running", name); + (void)unlink(path); + + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } else + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to set FD_CLOEXEC on command socket file descriptor"); + + return log_trace(move_fd(fd), "Created unix socket \"%s\"", path); +} +#else int lxc_server_init(const char *name, const char *lxcpath, const char *suffix) { __do_close int fd = -EBADF; @@ -2128,6 +2306,7 @@ int lxc_server_init(const char *name, const char *lxcpath, const char *suffix) return log_trace(move_fd(fd), "Created abstract unix socket \"%s\"", &path[1]); } +#endif int lxc_cmd_mainloop_add(const char *name, struct lxc_async_descr *descr, struct lxc_handler *handler) diff --git a/src/lxc/commands.h b/src/lxc/commands.h index 2a39748..d5d1a0e 100644 --- a/src/lxc/commands.h +++ b/src/lxc/commands.h @@ -53,6 +53,10 @@ typedef enum { LXC_CMD_GET_CGROUP_FD = 24, LXC_CMD_GET_LIMIT_CGROUP_FD = 25, LXC_CMD_GET_SYSTEMD_SCOPE = 26, +#ifdef HAVE_ISULAD + LXC_CMD_SET_TERMINAL_FIFOS = 27, + LXC_CMD_SET_TERMINAL_WINCH = 28, +#endif LXC_CMD_MAX, } lxc_cmd_t; @@ -179,4 +183,10 @@ __hidden extern int lxc_cmd_get_limit_cgroup_fd(const char *name, struct cgroup_fd *ret_fd); __hidden extern int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath); +#ifdef HAVE_ISULAD +__hidden extern int lxc_cmd_set_terminal_fifos(const char *name, const char *lxcpath, + const char *in_fifo, const char *out_fifo, const char *err_fifo); +__hidden extern int lxc_cmd_set_terminal_winch(const char *name, const char *lxcpath, unsigned int height, unsigned int width); +#endif + #endif /* __commands_h */ diff --git a/src/lxc/commands_utils.c b/src/lxc/commands_utils.c index 3558ff7..33780dd 100644 --- a/src/lxc/commands_utils.c +++ b/src/lxc/commands_utils.c @@ -143,12 +143,69 @@ int lxc_make_abstract_socket_name(char *path, size_t pathlen, return 0; } +#ifdef HAVE_ISULAD +char *generate_named_unix_sock_dir(const char *name) +{ + __do_free char *exec_sock_dir = NULL; + __do_free char *rundir = NULL; + + rundir = get_rundir(); + if (!rundir) + rundir = strdup("/var/run"); + + if (asprintf(&exec_sock_dir, "%s/lxc/%s", rundir, name) < 0) { + return log_error_errno(NULL, errno, "Failed to allocate memory"); + } + + return move_ptr(exec_sock_dir); +} + +int generate_named_unix_sock_path(const char *container_name, const char *sock_name, + char *out_path, size_t len) +{ +#define MAX_SOCK_NAME_LENGTH 12 + int ret; + __do_free char *sock_dir = NULL; + __do_free char *short_sock_name = NULL; + + if (container_name == NULL || sock_name == NULL) + return -1; + + sock_dir = generate_named_unix_sock_dir(container_name); + if (sock_dir == NULL) + return -1; + + short_sock_name = strdup(sock_name); + if (strlen(short_sock_name) > MAX_SOCK_NAME_LENGTH) + short_sock_name[MAX_SOCK_NAME_LENGTH] = '\0'; + + ret = snprintf(out_path, len, "%s/%s.sock", sock_dir, short_sock_name); + if (ret < 0 || (size_t)ret >= len) + return log_error_errno(-1, errno, "Failed to allocate memory"); + + return 0; +} +#endif + int lxc_cmd_connect(const char *name, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { int ret, client_fd; char path[LXC_AUDS_ADDR_LEN] = {0}; +#ifdef HAVE_ISULAD + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + if (file_exists(path)) { + client_fd = lxc_named_unix_connect(path); + if (client_fd < 0) + return -1; + + return client_fd; + } +#endif + ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, hashed_sock_name, suffix); if (ret < 0) diff --git a/src/lxc/commands_utils.h b/src/lxc/commands_utils.h index 28ce490..2704ae2 100644 --- a/src/lxc/commands_utils.h +++ b/src/lxc/commands_utils.h @@ -67,4 +67,11 @@ __hidden extern int lxc_cmd_connect(const char *name, const char *lxcpath, __hidden extern void lxc_cmd_notify_state_listeners(const char *name, const char *lxcpath, lxc_state_t state); + +#ifdef HAVE_ISULAD +__hidden extern char *generate_named_unix_sock_dir(const char *name); +__hidden extern int generate_named_unix_sock_path(const char *container_name, const char *sock_name, + char *out_path, size_t len); +#endif /* HAVE ISULAD */ + #endif /* __LXC_COMMANDS_UTILS_H */ diff --git a/src/lxc/exec_commands.c b/src/lxc/exec_commands.c new file mode 100644 index 0000000..bd81d66 --- /dev/null +++ b/src/lxc/exec_commands.c @@ -0,0 +1,477 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * Author: lifeng + * Create: 2019-12-08 + * Description: provide container definition + * lxc: linux Container library + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + ******************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#include <caps.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include "af_unix.h" +#include "cgroup.h" +#include "exec_commands.h" +#include "commands_utils.h" +#include "conf.h" +#include "config.h" +#include "confile.h" +#include "log.h" +#include "lxc.h" +#include "lxclock.h" +#include "mainloop.h" +#include "monitor.h" +#include "string_utils.h" +#include "terminal.h" +#include "utils.h" + +lxc_log_define(commands_exec, lxc); + +static const char *lxc_exec_cmd_str(lxc_exec_cmd_t cmd) +{ + static const char *const cmdname[LXC_EXEC_CMD_MAX] = { + [LXC_EXEC_CMD_SET_TERMINAL_WINCH] = "set_exec_terminal_winch", + }; + + if (cmd >= LXC_EXEC_CMD_MAX) + return "Invalid request"; + + return cmdname[cmd]; +} + +static int lxc_exec_cmd_rsp_recv(int sock, struct lxc_exec_cmd_rr *cmd) +{ + int ret, rspfd; + struct lxc_exec_cmd_rsp *rsp = &cmd->rsp; + + /*isulad: add timeout 1s to avoid long block due to [lxc monitor] error*/ + if (lxc_socket_set_timeout(sock, 1, 1) != 0) { + return syserror_ret(-1, "Failed to set timeout"); + } + + ret = lxc_cmd_rsp_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); + if (ret < 0) { + SYSERROR("Failed to receive response for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + + if (errno == ECONNRESET || errno == EAGAIN || errno == EWOULDBLOCK) { + errno = ECONNRESET; /*isulad set errno ECONNRESET when timeout */ + return -1; + } + + return -1; + } + TRACE("Command \"%s\" received response", lxc_exec_cmd_str(cmd->req.cmd)); + + if (rsp->datalen == 0) { + DEBUG("Response data length for command \"%s\" is 0", + lxc_exec_cmd_str(cmd->req.cmd)); + return ret; + } + + if (rsp->datalen > LXC_CMD_DATA_MAX) { + ERROR("Response data for command \"%s\" is too long: %d bytes > %d", + lxc_exec_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX); + return -1; + } + + rsp->data = malloc(rsp->datalen); + if (!rsp->data) { + errno = ENOMEM; + ERROR("Failed to allocate response buffer for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0); + if (ret != rsp->datalen) { + SYSERROR("Failed to receive response data for command \"%s\"", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + return ret; +} + +static int lxc_exec_cmd_rsp_send(int fd, struct lxc_exec_cmd_rsp *rsp) +{ + ssize_t ret; + + errno = EMSGSIZE; + ret = lxc_send_nointr(fd, rsp, sizeof(*rsp), MSG_NOSIGNAL); + if (ret < 0 || (size_t)ret != sizeof(*rsp)) { + SYSERROR("Failed to send command response %zd", ret); + return -1; + } + + if (!rsp->data || rsp->datalen <= 0) + return 0; + + errno = EMSGSIZE; + ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)rsp->datalen) { + SYSWARN("Failed to send command response data %zd", ret); + return -1; + } + + return 0; +} + +static int lxc_exec_cmd_send(const char *name, struct lxc_exec_cmd_rr *cmd, + const char *lxcpath, const char *hashed_sock_name, const char *suffix) +{ + int client_fd, saved_errno; + ssize_t ret = -1; + + client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name, suffix); + if (client_fd < 0) + return -1; + + ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, + sizeof(cmd->req)); + if (ret < 0 || (size_t)ret != sizeof(cmd->req)) + goto on_error; + + if (cmd->req.datalen <= 0) + return client_fd; + + errno = EMSGSIZE; + ret = lxc_send_nointr(client_fd, (void *)cmd->req.data, + cmd->req.datalen, MSG_NOSIGNAL); + if (ret < 0 || ret != (ssize_t)cmd->req.datalen) + goto on_error; + + return client_fd; + +on_error: + saved_errno = errno; + close(client_fd); + errno = saved_errno; + + return -1; +} + +static int lxc_exec_cmd(const char *name, struct lxc_exec_cmd_rr *cmd, const char *lxcpath, const char *hashed_sock_name, const char *suffix) +{ + int client_fd = -1; + int saved_errno; + int ret = -1; + + client_fd = lxc_exec_cmd_send(name, cmd, lxcpath, hashed_sock_name, suffix); + if (client_fd < 0) { + SYSTRACE("Command \"%s\" failed to connect command socket", + lxc_exec_cmd_str(cmd->req.cmd)); + return -1; + } + + ret = lxc_exec_cmd_rsp_recv(client_fd, cmd); + + saved_errno = errno; + close(client_fd); + errno = saved_errno; + return ret; +} + +int lxc_exec_cmd_set_terminal_winch(const char *name, const char *lxcpath, const char *suffix, unsigned int height, unsigned int width) +{ + int ret = 0; + struct lxc_exec_cmd_set_terminal_winch_request data = { 0 }; + + data.height = height; + data.width = width; + + struct lxc_exec_cmd_rr cmd = { + .req = { + .cmd = LXC_EXEC_CMD_SET_TERMINAL_WINCH, + .datalen = sizeof(struct lxc_exec_cmd_set_terminal_winch_request), + .data = &data, + }, + }; + + ret = lxc_exec_cmd(name, &cmd, lxcpath, NULL, suffix); + if (ret < 0) { + ERROR("Failed to send command to container"); + return -1; + } + + if (cmd.rsp.ret != 0) { + ERROR("Command response error:%d", cmd.rsp.ret); + return -1; + } + return 0; +} + +static int lxc_exec_cmd_set_terminal_winch_callback(int fd, struct lxc_exec_cmd_req *req, + struct lxc_exec_command_handler *handler) +{ + struct lxc_exec_cmd_rsp rsp; + struct lxc_exec_cmd_set_terminal_winch_request *data = (struct lxc_exec_cmd_set_terminal_winch_request *)(req->data); + memset(&rsp, 0, sizeof(rsp)); + + rsp.ret = lxc_set_terminal_winsz(handler->terminal, data->height, data->width);; + + return lxc_exec_cmd_rsp_send(fd, &rsp); + +} + +static int lxc_exec_cmd_process(int fd, struct lxc_exec_cmd_req *req, + struct lxc_exec_command_handler *handler) +{ + typedef int (*callback)(int, struct lxc_exec_cmd_req *, struct lxc_exec_command_handler *); + + callback cb[LXC_EXEC_CMD_MAX] = { + [LXC_EXEC_CMD_SET_TERMINAL_WINCH] = lxc_exec_cmd_set_terminal_winch_callback, + }; + + if (req->cmd >= LXC_EXEC_CMD_MAX) { + ERROR("Undefined command id %d", req->cmd); + return -1; + } + return cb[req->cmd](fd, req, handler); +} + +static void lxc_exec_cmd_fd_cleanup(int fd, struct lxc_epoll_descr *descr) +{ + lxc_mainloop_del_handler(descr, fd); + close(fd); + return; +} + +static int lxc_exec_cmd_handler(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + int ret; + struct lxc_exec_cmd_req req; + void *reqdata = NULL; + struct lxc_exec_command_handler *handler = data; + + ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); + if (ret < 0) { + SYSERROR("Failed to receive data on command socket for command " + "\"%s\"", lxc_exec_cmd_str(req.cmd)); + + if (errno == EACCES) { + /* We don't care for the peer, just send and close. */ + struct lxc_exec_cmd_rsp rsp = {.ret = ret}; + + lxc_exec_cmd_rsp_send(fd, &rsp); + } + + goto out_close; + } + + if (ret == 0) + goto out_close; + + if (ret != sizeof(req)) { + WARN("Failed to receive full command request. Ignoring request " + "for \"%s\"", lxc_exec_cmd_str(req.cmd)); + ret = -1; + goto out_close; + } + + if (req.datalen > LXC_CMD_DATA_MAX) { + ERROR("Received command data length %d is too large for " + "command \"%s\"", req.datalen, lxc_exec_cmd_str(req.cmd)); + errno = EFBIG; + ret = -EFBIG; + goto out_close; + } + + if (req.datalen > 0) { + reqdata = alloca(req.datalen); + if (!reqdata) { + ERROR("Failed to allocate memory for \"%s\" command", + lxc_exec_cmd_str(req.cmd)); + errno = ENOMEM; + ret = -ENOMEM; + goto out_close; + } + + ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0); + if (ret != req.datalen) { + WARN("Failed to receive full command request. Ignoring " + "request for \"%s\"", lxc_exec_cmd_str(req.cmd)); + ret = LXC_MAINLOOP_ERROR; + goto out_close; + } + + req.data = reqdata; + } + + ret = lxc_exec_cmd_process(fd, &req, handler); + if (ret) { + /* This is not an error, but only a request to close fd. */ + ret = LXC_MAINLOOP_CONTINUE; + goto out_close; + } + +out: + return ret; + +out_close: + lxc_exec_cmd_fd_cleanup(fd, descr); + goto out; +} + +static int lxc_exec_cmd_accept(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + int connection = -1; + int opt = 1, ret = -1; + + connection = accept(fd, NULL, 0); + if (connection < 0) { + SYSERROR("Failed to accept connection to run command"); + return LXC_MAINLOOP_ERROR; + } + + ret = fcntl(connection, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + SYSERROR("Failed to set close-on-exec on incoming command connection"); + goto out_close; + } + + ret = setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); + if (ret < 0) { + SYSERROR("Failed to enable necessary credentials on command socket"); + goto out_close; + } + + ret = lxc_mainloop_add_handler(descr, connection, lxc_exec_cmd_handler, data); + if (ret) { + ERROR("Failed to add command handler"); + goto out_close; + } + +out: + return ret; + +out_close: + close(connection); + goto out; +} +#ifdef HAVE_ISULAD +int lxc_exec_unix_sock_delete(const char *name, const char *suffix) +{ + char path[LXC_AUDS_ADDR_LEN] = {0}; + + if (name == NULL || suffix == NULL) + return -1; + + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + (void)unlink(path); + + return 0; +} + +int lxc_exec_cmd_init(const char *name, const char *lxcpath, const char *suffix) +{ + __do_close int fd = -EBADF; + int ret; + char path[LXC_AUDS_ADDR_LEN] = {0}; + __do_free char *exec_sock_dir = NULL; + + exec_sock_dir = generate_named_unix_sock_dir(name); + if (exec_sock_dir == NULL) + return -1; + + if (mkdir_p(exec_sock_dir, 0600) < 0) + return log_error_errno(-1, errno, "Failed to create exec sock directory %s", path); + + if (generate_named_unix_sock_path(name, suffix, path, sizeof(path)) != 0) + return -1; + + TRACE("Creating unix socket \"%s\"", path); + + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) { + if (errno == EADDRINUSE) { + WARN("Container \"%s\" exec unix sock is occupied", name); + (void)unlink(path); + fd = lxc_named_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } else { + return log_error_errno(-1, errno, "Failed to create command socket %s", path); + } + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) + return log_error_errno(-1, errno, "Failed to set FD_CLOEXEC on command socket file descriptor"); + + return log_trace(move_fd(fd), "Created unix socket \"%s\"", path); +} +#else +int lxc_exec_cmd_init(const char *name, const char *lxcpath, const char *suffix) +{ + int fd, ret; + char path[LXC_AUDS_ADDR_LEN] = {0}; + + ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, NULL, suffix); + if (ret < 0) + return -1; + TRACE("Creating abstract unix socket \"%s\"", &path[1]); + + fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); + if (fd < 0) { + SYSERROR("Failed to create command socket %s", &path[1]); + if (errno == EADDRINUSE) + ERROR("Container \"%s\" appears to be already running", name); + + return -1; + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + SYSERROR("Failed to set FD_CLOEXEC on command socket file descriptor"); + close(fd); + return -1; + } + + return fd; +} +#endif + +int lxc_exec_cmd_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_exec_command_handler *handler) +{ + int ret; + int fd = handler->maincmd_fd; + + ret = lxc_mainloop_add_handler(descr, fd, lxc_exec_cmd_accept, handler); + if (ret < 0) { + ERROR("Failed to add handler for command socket"); + close(fd); + } + + return ret; +} diff --git a/src/lxc/exec_commands.h b/src/lxc/exec_commands.h new file mode 100644 index 0000000..3ec2a22 --- /dev/null +++ b/src/lxc/exec_commands.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. + * Author: lifeng + * Create: 2019-12-08 + * Description: provide container definition + * lxc: linux Container library + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + ******************************************************************************/ + +#ifndef __LXC_EXEC_COMMANDS_H +#define __LXC_EXEC_COMMANDS_H + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lxccontainer.h" +#include "macro.h" +#include "state.h" +#include "terminal.h" + +struct lxc_exec_command_handler { + int maincmd_fd; + struct lxc_terminal *terminal; +}; + +typedef enum { + LXC_EXEC_CMD_SET_TERMINAL_WINCH, + LXC_EXEC_CMD_MAX, +} lxc_exec_cmd_t; + +struct lxc_exec_cmd_req { + lxc_exec_cmd_t cmd; + int datalen; + const void *data; +}; + +struct lxc_exec_cmd_rsp { + int ret; /* 0 on success, -errno on failure */ + int datalen; + void *data; +}; + +struct lxc_exec_cmd_rr { + struct lxc_exec_cmd_req req; + struct lxc_exec_cmd_rsp rsp; +}; + +struct lxc_exec_cmd_set_terminal_winch_request { + unsigned int height; + unsigned int width; +}; + +struct lxc_epoll_descr; +struct lxc_handler; + +extern int lxc_exec_cmd_init(const char *name, const char *lxcpath, const char *suffix); +extern int lxc_exec_cmd_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_exec_command_handler *handler); +extern int lxc_exec_cmd_set_terminal_winch(const char *name, const char *lxcpath, const char *suffix, unsigned int height, unsigned int width); + +#ifdef HAVE_ISULAD +extern int lxc_exec_unix_sock_delete(const char *name, const char *suffix); +#endif + +#endif /* __exec_commands_h */ diff --git a/src/lxc/isulad_utils.c b/src/lxc/isulad_utils.c new file mode 100644 index 0000000..ee39302 --- /dev/null +++ b/src/lxc/isulad_utils.c @@ -0,0 +1,535 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved + * Description: isulad utils + * Author: lifeng + * Create: 2020-04-11 +******************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <stdio_ext.h> + +#include "isulad_utils.h" +#include "log.h" +#include "path.h" +#include "file_utils.h" + +lxc_log_define(isulad_utils, lxc); + +void *lxc_common_calloc_s(size_t size) +{ + if (size == 0 || size > SIZE_MAX) { + return NULL; + } + + return calloc((size_t)1, size); +} + +int lxc_mem_realloc(void **newptr, size_t newsize, void *oldptr, size_t oldsize) +{ + void *tmp = NULL; + + if (newsize == 0) { + goto err_out; + } + + tmp = lxc_common_calloc_s(newsize); + if (tmp == NULL) { + ERROR("Failed to malloc memory"); + goto err_out; + } + + if (oldptr != NULL) { + memcpy(tmp, oldptr, (newsize < oldsize) ? newsize : oldsize); + + memset(oldptr, 0, oldsize); + + free(oldptr); + } + + *newptr = tmp; + return 0; + +err_out: + return -1; +} + +char *safe_strdup(const char *src) +{ + char *dst = NULL; + + if (src == NULL) { + return NULL; + } + + dst = strdup(src); + if (dst == NULL) { + abort(); + } + + return dst; +} + +int lxc_open(const char *filename, int flags, mode_t mode) +{ + char rpath[PATH_MAX] = {0x00}; + + if (cleanpath(filename, rpath, sizeof(rpath)) == NULL) { + return -1; + } + if (mode) { + return open(rpath, (int)((unsigned int)flags | O_CLOEXEC), mode); + } else { + return open(rpath, (int)((unsigned int)flags | O_CLOEXEC)); + } +} + +FILE *lxc_fopen(const char *filename, const char *mode) +{ + char rpath[PATH_MAX] = {0x00}; + + if (cleanpath(filename, rpath, sizeof(rpath)) == NULL) { + return NULL; + } + + return fopen_cloexec(rpath, mode); +} + +/* isulad: write error message */ +void lxc_write_error_message(int errfd, const char *format, ...) +{ + int ret; + char errbuf[BUFSIZ + 1] = {0}; + ssize_t sret; + va_list argp; + + if (errfd <= 0) + return; + + va_start(argp, format); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + ret = vsnprintf(errbuf, BUFSIZ, format, argp); +#pragma GCC diagnostic pop + va_end(argp); + if (ret < 0 || ret >= BUFSIZ) + SYSERROR("Failed to call vsnprintf"); + sret = write(errfd, errbuf, strlen(errbuf)); + if (sret < 0) + SYSERROR("Write errbuf failed"); +} + +/* isulad: read file to buffer */ +int lxc_file2str(const char *filename, char ret[], int cap) +{ + int fd, num_read; + + if ((fd = lxc_open(filename, O_RDONLY | O_CLOEXEC, 0)) == -1) + return -1; + if ((num_read = read(fd, ret, cap - 1)) <= 0) + num_read = -1; + else + ret[num_read] = 0; + close(fd); + + return num_read; +} + +/* isuald: lxc_stat2proc() makes sure it can handle arbitrary executable file basenames + * for `cmd', i.e. those with embedded whitespace or embedded ')'s. + * Such names confuse %s (see scanf(3)), so the string is split and %39c + * is used instead. (except for embedded ')' "(%[^)]c)" would work. + */ +static proc_t *lxc_stat2proc(const char *S) +{ + int num; + proc_t *P = NULL; + char *tmp = NULL; + + if (!S) + return NULL; + + tmp = strrchr(S, ')'); /* split into "PID (cmd" and "<rest>" */ + if (!tmp) + return NULL; + *tmp = '\0'; /* replace trailing ')' with NUL */ + + P = malloc(sizeof(proc_t)); + if (P == NULL) + return NULL; + (void)memset(P, 0x00, sizeof(proc_t)); + + /* parse these two strings separately, skipping the leading "(". */ + /* https://www.openwall.com/lists/musl/2013/11/15/5: musl's sscanf("%15c",cmd) + requires exactly 15 characters; anything shorter is a matching failure. */ +#ifdef __MUSL__ + num = sscanf(S, "%d (%15s", &P->pid, P->cmd); /* comm[16] in kernel */ +#else + num = sscanf(S, "%d (%15c", &P->pid, P->cmd); /* comm[16] in kernel */ +#endif + if (num != 2) { + ERROR("Call sscanf error: %s", errno ? strerror(errno) : ""); + free(P); + return NULL; + } + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu " + "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ + "%ld %ld %ld %ld " + "%Lu " /* start_time */ + "%lu " + "%ld " + "%lu %lu %lu %lu %lu %lu " + "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ + "%lu %lu %lu " + "%d %d " + "%lu %lu", + &P->state, + &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, + &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, + &P->utime, &P->stime, &P->cutime, &P->cstime, + &P->priority, &P->nice, &P->timeout, &P->it_real_value, + &P->start_time, + &P->vsize, + &P->rss, + &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, + &P->kstk_eip, + &P->wchan, &P->nswap, &P->cnswap, + &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */ + &P->rtprio, &P->sched /* both added to 2.5.18 */ + ); + if (num != 35) { + ERROR("Call sscanf error: %s", errno ? strerror(errno) : ""); + free(P); + return NULL; + } + if (P->tty == 0) + P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */ + return P; +} + +/* isulad: get starttime of process pid */ +unsigned long long lxc_get_process_startat(pid_t pid) +{ + int sret = 0; + unsigned long long startat = 0; + proc_t *pid_info = NULL; + char filename[PATH_MAX] = {0}; + char sbuf[1024] = {0}; /* bufs for stat */ + + sret = snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); + if (sret < 0 || sret >= sizeof(filename)) { + ERROR("Failed to sprintf filename"); + goto out; + } + + if ((lxc_file2str(filename, sbuf, sizeof(sbuf))) == -1) { + SYSERROR("Failed to read pidfile %s", filename); + goto out; + } + + pid_info = lxc_stat2proc(sbuf); + if (!pid_info) { + ERROR("Failed to get proc stat info"); + goto out; + } + + startat = pid_info->start_time; +out: + free(pid_info); + return startat; +} + +// isulad: set env home in container +int lxc_setup_env_home(uid_t uid) +{ +#define __PASSWD_FILE__ "/etc/passwd" + char *homedir = "/"; // default home dir is / + FILE *stream = NULL; + struct passwd pw, *pwbufp = NULL; + char buf[BUFSIZ]; + const char *curr_home = NULL; + + curr_home = getenv("HOME"); + // if user set or image set, just use it. + if (curr_home != NULL && strcmp(curr_home, "") != 0) { + return 0; + } + + stream = fopen_cloexec(__PASSWD_FILE__, "r"); + if (stream == NULL) { + SYSWARN("Failed to open %s", __PASSWD_FILE__); + goto set_env; + } + +#if IS_BIONIC + while (util_getpwent_r(stream, &pw, buf, sizeof(buf), &pwbufp) == 0 && pwbufp != NULL) { +#else + while (fgetpwent_r(stream, &pw, buf, sizeof(buf), &pwbufp) == 0 && pwbufp != NULL) { +#endif + if (pwbufp->pw_uid == uid) { + homedir = pwbufp->pw_dir; + goto set_env; + } + } + WARN("User invalid, can not find user '%u'", uid); + +set_env: + if (stream) + fclose(stream); + + // if we didn't configure HOME, set it based on uid; + // override it if reach here. + if (setenv("HOME", homedir, 1) < 0) { + SYSERROR("Unable to set env 'HOME'"); + return -1; + } + + NOTICE("Setted env 'HOME' to %s", homedir); + return 0; +} + +bool lxc_process_alive(pid_t pid, unsigned long long start_time) +{ + int sret = 0; + bool alive = true; + proc_t *pid_info = NULL; + char filename[PATH_MAX] = {0}; + char sbuf[1024] = {0}; /* bufs for stat */ + + sret = kill(pid, 0); + if (sret < 0 && errno == ESRCH) + return false; + + sret = snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); + if (sret < 0 || sret >= sizeof(filename)) { + ERROR("Failed to sprintf filename"); + goto out; + } + + if ((lxc_file2str(filename, sbuf, sizeof(sbuf))) == -1) { + ERROR("Failed to read pidfile %s", filename); + alive = false; + goto out; + } + + pid_info = lxc_stat2proc(sbuf); + if (!pid_info) { + ERROR("Failed to get proc stat info"); + alive = false; + goto out; + } + + if (start_time != pid_info->start_time) + alive = false; +out: + free(pid_info); + return alive; +} + +bool is_non_negative_num(const char *s) +{ + if (!s || !strcmp(s, "")) + return false; + while(*s != '\0') { + if(!isdigit(*s)) + return false; + ++s; + } + return true; +} + +static int hold_int(const char delim, bool required, char **src, unsigned int *dst) +{ + unsigned long long int res = 0; + char *err_str = NULL; + + // ensure *src not a empty string + if (**src == '\0') { + ERROR("Empty subject on given entrie is not allowed."); + return -1; + } + + errno = 0; + // covert string to long long + res = strtoull(*src, &err_str, 0); + if (errno != 0 && errno != ERANGE) { + ERROR("Parse int from string failed."); + return -1; + } + + // **src is not a digit + if (err_str == *src) { + if (!required) { + ERROR("Integer part is missing."); + return -1; + } + // if required, just set 0 + *dst = 0; + } else { + if (sizeof(void *) > 4 && res > UINT_MAX) { // make sure 64-bit platform behave same as 32-bit + res = UINT_MAX; + } + res = res & UINT_MAX; + *dst = (uint32_t)res; + } + + // normal case + if (*err_str == delim) { + err_str++; + } else if (*err_str != '\0') { + ERROR("Invalid digit string."); + return -1; + } + + *src = err_str; // update src to next valid context in line. + return 0; +} + +static void hold_string(const char delim, char **src, char **dst) +{ + for (*dst = *src; **src != delim; ++(*src)) { + if (**src == '\0') { + break; + } + } + + if (**src == delim) { + **src = '\0'; + ++(*src); + } +} + +static int parse_line_pw(const char delim, char *line, struct passwd *result) +{ + int ret = 0; + bool required = false; + char *walker = NULL; + + walker = strpbrk(line, "\n"); + if (walker != NULL) { + // clear newline char + *walker = '\0'; + } + + hold_string(delim, &line, &result->pw_name); + + required = (result->pw_name[0] == '+' || result->pw_name[0] == '-') ? true : false; + + hold_string(delim, &line, &result->pw_passwd); + + ret = hold_int(delim, required, &line, &result->pw_uid); + if (ret != 0) { + // a legitimate line must have uid + ERROR("Parse uid error."); + return ret; + } + + ret = hold_int(delim, required, &line, &result->pw_gid); + if (ret != 0) { + // it's ok to not provide gid + ERROR("Parse gid error."); + return ret; + } + + hold_string(delim, &line, &result->pw_gecos); + + hold_string(delim, &line, &result->pw_dir); + + result->pw_shell = line; + return 0; +} + +char *util_left_trim_space(char *str) +{ + char *begin = str; + char *tmp = str; + while (isspace(*begin)) { + begin++; + } + while ((*tmp++ = *begin++)) { + } + return str; +} + +int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result) +{ + const char delim = ':'; + char *buff_end = NULL; + char *walker = NULL; + bool got = false; + int ret = 0; + + if (stream == NULL || resbuf == NULL || buffer == NULL || result == NULL) { + ERROR("Password obj, params is NULL."); + return -1; + } + + if (buflen <= 1) { + ERROR("Inadequate buffer length was given."); + return -1; + } + + buff_end = buffer + buflen - 1; + flockfile(stream); + + while (1) { + *buff_end = '\xff'; + walker = fgets_unlocked(buffer, buflen, stream); + // if get NULL string + if (walker == NULL) { + *result = NULL; + // reach end of file, return error + if (feof(stream)) { + ret = ENOENT; + goto out; + } + // overflow buffer + ret = ERANGE; + goto out; + } + // just overflow last char in buffer + if (*buff_end != '\xff') { + *result = NULL; + ret = ERANGE; + goto out; + } + + (void)util_left_trim_space(buffer); + // skip comment line and empty line + if (walker[0] == '#' || walker[0] == '\0') { + continue; + } + + if (parse_line_pw(delim, walker, resbuf) == 0) { + got = true; + break; + } + } + if (!got) { + *result = NULL; + ret = ERANGE; + goto out; + } + + *result = resbuf; + ret = 0; +out: + funlockfile(stream); + return ret; +} diff --git a/src/lxc/isulad_utils.h b/src/lxc/isulad_utils.h new file mode 100644 index 0000000..7a5eb89 --- /dev/null +++ b/src/lxc/isulad_utils.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved + * Description: isulad utils + * Author: lifeng + * Create: 2020-04-11 +******************************************************************************/ +#ifndef __iSULAD_UTILS_H +#define __iSULAD_UTILS_H + +#include <stdio.h> +#include <stdbool.h> +#include <pwd.h> + +/* isulad: replace space with SPACE_MAGIC_STR */ +#define SPACE_MAGIC_STR "[#)" + +/* isulad: + ld cutime, cstime, priority, nice, timeout, it_real_value, rss, + c state, + d ppid, pgrp, session, tty, tpgid, + s signal, blocked, sigignore, sigcatch, + lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime, + lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip, + lu start_time, vsize, wchan, nswap, cnswap, +*/ + +/* Basic data structure which holds all information we can get about a process. + * (unless otherwise specified, fields are read from /proc/#/stat) + * + * Most of it comes from task_struct in linux/sched.h + */ +typedef struct proc_t { + // 1st 16 bytes + int pid; /* process id */ + int ppid; /* pid of parent process */ + + char state; /* single-char code for process state (S=sleeping) */ + + unsigned long long + utime, /* user-mode CPU time accumulated by process */ + stime, /* kernel-mode CPU time accumulated by process */ + // and so on... + cutime, /* cumulative utime of process and reaped children */ + cstime, /* cumulative stime of process and reaped children */ + start_time; /* start time of process -- seconds since 1-1-70 */ + + long + priority, /* kernel scheduling priority */ + timeout, /* ? */ + nice, /* standard unix nice level of process */ + rss, /* resident set size from /proc/#/stat (pages) */ + it_real_value; /* ? */ + unsigned long + rtprio, /* real-time priority */ + sched, /* scheduling class */ + vsize, /* number of pages of virtual memory ... */ + rss_rlim, /* resident set size limit? */ + flags, /* kernel flags for the process */ + min_flt, /* number of minor page faults since process start */ + maj_flt, /* number of major page faults since process start */ + cmin_flt, /* cumulative min_flt of process and child processes */ + cmaj_flt, /* cumulative maj_flt of process and child processes */ + nswap, /* ? */ + cnswap, /* cumulative nswap ? */ + start_code, /* address of beginning of code segment */ + end_code, /* address of end of code segment */ + start_stack, /* address of the bottom of stack for the process */ + kstk_esp, /* kernel stack pointer */ + kstk_eip, /* kernel instruction pointer */ + wchan; /* address of kernel wait channel proc is sleeping in */ + + char cmd[16]; /* basename of executable file in call to exec(2) */ + int + pgrp, /* process group id */ + session, /* session id */ + tty, /* full device number of controlling terminal */ + tpgid, /* terminal process group id */ + exit_signal, /* might not be SIGCHLD */ + processor; /* current (or most recent?) CPU */ +} proc_t; + +extern int lxc_mem_realloc(void **newptr, size_t newsize, void *oldptr, size_t oldsize); +extern void *lxc_common_calloc_s(size_t size); +extern char *safe_strdup(const char *src); + +extern int lxc_open(const char *filename, int flags, mode_t mode); +extern FILE *lxc_fopen(const char *filename, const char *mode); + +extern void lxc_write_error_message(int errfd, const char *format, ...); +extern int lxc_file2str(const char *filename, char ret[], int cap); +extern int unsigned long long lxc_get_process_startat(pid_t pid); +// set env home in container +extern int lxc_setup_env_home(uid_t uid); + +extern bool lxc_process_alive(pid_t pid, unsigned long long start_time); + +extern bool is_non_negative_num(const char *s); + +int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result); + +#endif diff --git a/src/lxc/json/defs.c b/src/lxc/json/defs.c new file mode 100644 index 0000000..4bf569a --- /dev/null +++ b/src/lxc/json/defs.c @@ -0,0 +1,205 @@ +// Generated from defs.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <string.h> +#include <read-file.h> +#include "defs.h" + +defs_hook *make_defs_hook(yajl_val tree, struct parser_context *ctx, parser_error *err) { + defs_hook *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val val = get_val(tree, "path", yajl_t_string); + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->path = safe_strdup(str ? str : ""); + } + } + { + yajl_val tmp = get_val(tree, "args", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->args_len = YAJL_GET_ARRAY(tmp)->len; + if (YAJL_GET_ARRAY(tmp)->len > SIZE_MAX / sizeof(*ret->args) - 1) { + free_defs_hook(ret); + return NULL; + } + ret->args = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->args)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->args[i] = safe_strdup(str ? str : ""); + } + } + } + } + { + yajl_val tmp = get_val(tree, "env", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->env_len = YAJL_GET_ARRAY(tmp)->len; + if (YAJL_GET_ARRAY(tmp)->len > SIZE_MAX / sizeof(*ret->env) - 1) { + free_defs_hook(ret); + return NULL; + } + ret->env = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->env)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->env[i] = safe_strdup(str ? str : ""); + } + } + } + } + { + yajl_val val = get_val(tree, "timeout", yajl_t_number); + if (val != NULL) { + int invalid = common_safe_int(YAJL_GET_NUMBER(val), (int *)&ret->timeout); + if (invalid) { + if (asprintf(err, "Invalid value '%s' with type 'integer' for key 'timeout': %s", YAJL_GET_NUMBER(val), strerror(-invalid)) < 0) + *err = safe_strdup("error allocating memory"); + free_defs_hook(ret); + return NULL; + } + } + } + if (ret->path == NULL) { + if (asprintf(err, "Required field '%s' not present", "path") < 0) + *err = safe_strdup("error allocating memory"); + free_defs_hook(ret); + return NULL; + } + + if (tree->type == yajl_t_object && (ctx->options & PARSE_OPTIONS_STRICT)) { + int i; + for (i = 0; i < tree->u.object.len; i++) + if (strcmp(tree->u.object.keys[i], "path") && + strcmp(tree->u.object.keys[i], "args") && + strcmp(tree->u.object.keys[i], "env") && + strcmp(tree->u.object.keys[i], "timeout")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_defs_hook(defs_hook *ptr) { + if (ptr == NULL) + return; + free(ptr->path); + ptr->path = NULL; + if (ptr->args != NULL) { + size_t i; + for (i = 0; i < ptr->args_len; i++) { + if (ptr->args[i] != NULL) { + free(ptr->args[i]); + ptr->args[i] = NULL; + } + } + free(ptr->args); + ptr->args = NULL; + } + if (ptr->env != NULL) { + size_t i; + for (i = 0; i < ptr->env_len; i++) { + if (ptr->env[i] != NULL) { + free(ptr->env[i]); + ptr->env[i] = NULL; + } + } + free(ptr->env); + ptr->env = NULL; + } + free(ptr); +} + +yajl_gen_status gen_defs_hook(yajl_gen g, defs_hook *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + *err = 0; + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->path != NULL)) { + char *str = ""; + stat = reformat_map_key(g, "path", strlen("path")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->path != NULL) { + str = ptr->path; + } + stat = reformat_string(g, str, strlen(str)); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) || (ptr != NULL && ptr->args != NULL)) { + size_t len = 0, i; + stat = reformat_map_key(g, "args", strlen("args")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->args != NULL) { + len = ptr->args_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = reformat_string(g, ptr->args[i], strlen(ptr->args[i])); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) || (ptr != NULL && ptr->env != NULL)) { + size_t len = 0, i; + stat = reformat_map_key(g, "env", strlen("env")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->env != NULL) { + len = ptr->env_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = reformat_string(g, ptr->env[i], strlen(ptr->env[i])); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->timeout)) { + long long int num = 0; + stat = reformat_map_key(g, "timeout", strlen("timeout")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->timeout) { + num = (long long int)ptr->timeout; + } + stat = reformat_int(g, num); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} diff --git a/src/lxc/json/defs.h b/src/lxc/json/defs.h new file mode 100644 index 0000000..0bbd8ac --- /dev/null +++ b/src/lxc/json/defs.h @@ -0,0 +1,37 @@ +// Generated from defs.json. Do not edit! +#ifndef DEFS_SCHEMA_H +#define DEFS_SCHEMA_H + +#include <sys/types.h> +#include <stdint.h> +#include "json_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char *path; + + char **args; + size_t args_len; + + char **env; + size_t env_len; + + int timeout; + +} +defs_hook; + +void free_defs_hook(defs_hook *ptr); + +defs_hook *make_defs_hook(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_defs_hook(yajl_gen g, defs_hook *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/json_common.c b/src/lxc/json/json_common.c new file mode 100755 index 0000000..ec20c59 --- /dev/null +++ b/src/lxc/json/json_common.c @@ -0,0 +1,1153 @@ +// Auto generated file. Do not edit! +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include "json_common.h" + +#define MAX_NUM_STR_LEN 21 + +yajl_gen_status reformat_number(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_number(g, str, len); +} + +yajl_gen_status reformat_uint(void *ctx, long long unsigned int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = snprintf(numstr, MAX_NUM_STR_LEN, "%llu", num); + if (ret < 0 || ret >= MAX_NUM_STR_LEN) { + return yajl_gen_in_error_state; + } + return reformat_number(ctx, (const char *)numstr, strlen(numstr)); +} + +yajl_gen_status reformat_int(void *ctx, long long int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", num); + if (ret < 0 || ret >= MAX_NUM_STR_LEN) { + return yajl_gen_in_error_state; + } + return reformat_number(ctx, (const char *)numstr, strlen(numstr)); +} + +yajl_gen_status reformat_double(void *ctx, double num) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_double(g, num); +} + +yajl_gen_status reformat_string(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_string(g, (const unsigned char *)str, len); +} + +yajl_gen_status reformat_null(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_null(g); +} + +yajl_gen_status reformat_bool(void *ctx, int boolean) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_bool(g, boolean); +} + +yajl_gen_status reformat_map_key(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_string(g, (const unsigned char *)str, len); +} + +yajl_gen_status reformat_start_map(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_map_open(g); +} + +yajl_gen_status reformat_end_map(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_map_close(g); +} + +yajl_gen_status reformat_start_array(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_array_open(g); +} + +yajl_gen_status reformat_end_array(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_array_close(g); +} + +bool json_gen_init(yajl_gen *g, struct parser_context *ctx) { + *g = yajl_gen_alloc(NULL); + if (NULL == *g) { + return false; + + } + yajl_gen_config(*g, yajl_gen_beautify, !(ctx->options & GEN_OPTIONS_SIMPLIFY)); + yajl_gen_config(*g, yajl_gen_validate_utf8, !(ctx->options & GEN_OPTIONS_NOT_VALIDATE_UTF8)); + return true; +} + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type) { + const char *path[] = { name, NULL }; + return yajl_tree_get(tree, path, type); +} + +void *safe_malloc(size_t size) { + void *ret = NULL; + if (size == 0) { + abort(); + } + ret = calloc(1, size); + if (ret == NULL) { + abort(); + } + return ret; +} + +int common_safe_double(const char *numstr, double *converted) { + char *err_str = NULL; + double d; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + d = strtod(numstr, &err_str); + if (errno > 0) { + return -errno; + } + + if (err_str == NULL || err_str == numstr || *err_str != '\0') { + return -EINVAL; + } + + *converted = d; + return 0; +} + +int common_safe_uint8(const char *numstr, uint8_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (uli > UINT8_MAX) { + return -ERANGE; + } + + *converted = (uint8_t)uli; + return 0; +} + +int common_safe_uint16(const char *numstr, uint16_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (uli > UINT16_MAX) { + return -ERANGE; + } + + *converted = (uint16_t)uli; + return 0; +} + +int common_safe_uint32(const char *numstr, uint32_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (ull > UINT32_MAX) { + return -ERANGE; + } + + *converted = (uint32_t)ull; + return 0; +} + +int common_safe_uint64(const char *numstr, uint64_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + *converted = (uint64_t)ull; + return 0; +} + +int common_safe_uint(const char *numstr, unsigned int *converted) { + char *err = NULL; + unsigned long long int ull; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (ull > UINT_MAX) { + return -ERANGE; + } + + *converted = (unsigned int)ull; + return 0; +} + +int common_safe_int8(const char *numstr, int8_t *converted) { + char *err = NULL; + long int li; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (li > INT8_MAX || li < INT8_MIN) { + return -ERANGE; + } + + *converted = (int8_t)li; + return 0; +} + +int common_safe_int16(const char *numstr, int16_t *converted) { + char *err = NULL; + long int li; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (li > INT16_MAX || li < INT16_MIN) { + return -ERANGE; + } + + *converted = (int16_t)li; + return 0; +} + +int common_safe_int32(const char *numstr, int32_t *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (lli > INT32_MAX || lli < INT32_MIN) { + return -ERANGE; + } + + *converted = (int32_t)lli; + return 0; +} + +int common_safe_int64(const char *numstr, int64_t *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtoll(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + *converted = (int64_t)lli; + return 0; +} + +int common_safe_int(const char *numstr, int *converted) { + char *err = NULL; + long long int lli; + + if (numstr == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (lli > INT_MAX || lli < INT_MIN) { + return -ERANGE; + } + + *converted = (int)lli; + return 0; +} + +yajl_gen_status gen_json_map_int_int(void *ctx, json_map_int_int *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_int(json_map_int_int *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + // No need to free value for type int + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_int *make_json_map_int_int(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_int *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(int) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + + if (srcval != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_int(json_map_int_int *map, int key, int val) { + size_t len; + int *keys = NULL; + int *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(int)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_bool(void *ctx, json_map_int_bool *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_bool(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_bool(json_map_int_bool *map) { + if (map != NULL) { + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_bool *make_json_map_int_bool(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_bool *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(int) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + + if (srcval != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val) { + size_t len; + int *keys = NULL; + bool *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(bool)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_string(void *ctx, json_map_int_string *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_string(g, map->values[i], strlen(map->values[i]));; + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_string(json_map_int_string *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_string *make_json_map_int_string(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_string *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + } + + if (srcval != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_int_string(json_map_int_string *map, int key, const char *val) { + size_t len; + int *keys = NULL; + char **vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(char *)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_int(void *ctx, json_map_string_int *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_int(json_map_string_int *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + // No need to free value for type int + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_int *make_json_map_string_int(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_int *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (*err == NULL && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_string_int(json_map_string_int *map, const char *key, int val) { + size_t len; + char **keys = NULL; + int *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(int)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_bool(void *ctx, json_map_string_bool *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_bool(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_bool(json_map_string_bool *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + // No need to free value for type bool + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_bool *make_json_map_string_bool(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_bool *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_bool(ret); + return NULL; + } + } + } + } + return ret; +} + +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val) { + size_t len; + char **keys = NULL; + bool *vals = NULL; + + if (map == NULL) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(bool)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_string(void *ctx, json_map_string_string *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map != NULL) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_string(g, map->values[i], strlen(map->values[i]));; + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_string(json_map_string_string *map) { + if (map != NULL) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_string *make_json_map_string_string(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_string *ret = NULL; + if (src != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val) { + size_t len, i; + char **keys = NULL; + char **vals = NULL; + + if (map == NULL) { + return -1; + } + + for (i = 0; i < map->len; i++) { + if (strcmp(map->keys[i], key) == 0) { + free(map->values[i]); + map->values[i] = safe_strdup(val ? val : ""); + return 0; + } + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(char *)); + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} diff --git a/src/lxc/json/json_common.h b/src/lxc/json/json_common.h new file mode 100755 index 0000000..bcac13e --- /dev/null +++ b/src/lxc/json/json_common.h @@ -0,0 +1,194 @@ +// Auto generated file. Do not edit! +#ifndef _JSON_COMMON_H +#define _JSON_COMMON_H + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <yajl/yajl_tree.h> +#include <yajl/yajl_gen.h> +#include "isulad_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +# undef linux + +#ifdef __MUSL__ +#undef stdin +#undef stdout +#undef stderr +#define stdin stdin +#define stdout stdout +#define stderr stderr +#endif + +//options to report error if there is unknown key found in json +# define PARSE_OPTIONS_STRICT 0x01 +//options to generate all key and value +# define GEN_OPTIONS_ALLKEYVALUE 0x02 +//options to generate simplify(no indent) json string +# define GEN_OPTIONS_SIMPLIFY 0x04 +//options not to validate utf8 data +# define GEN_OPTIONS_NOT_VALIDATE_UTF8 0x08 + +#define GEN_SET_ERROR_AND_RETURN(stat, err) { \ + if (*(err) == NULL) {\ + if (asprintf(err, "%s: %s: %d: error generating json, errcode: %d", __FILE__, __func__, __LINE__, stat) < 0) { \ + *(err) = safe_strdup("error allocating memory"); \ + } \ + }\ + return stat; \ + } + +typedef char *parser_error; + +struct parser_context { + unsigned int options; + FILE *stderr; +}; + +yajl_gen_status reformat_number(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_uint(void *ctx, long long unsigned int num); + +yajl_gen_status reformat_int(void *ctx, long long int num); + +yajl_gen_status reformat_double(void *ctx, double num); + +yajl_gen_status reformat_string(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_null(void *ctx); + +yajl_gen_status reformat_bool(void *ctx, int boolean); + +yajl_gen_status reformat_map_key(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_start_map(void *ctx); + +yajl_gen_status reformat_end_map(void *ctx); + +yajl_gen_status reformat_start_array(void *ctx); + +yajl_gen_status reformat_end_array(void *ctx); + +bool json_gen_init(yajl_gen *g, struct parser_context *ctx); + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type); + +void *safe_malloc(size_t size); + +int common_safe_double(const char *numstr, double *converted); + +int common_safe_uint8(const char *numstr, uint8_t *converted); + +int common_safe_uint16(const char *numstr, uint16_t *converted); + +int common_safe_uint32(const char *numstr, uint32_t *converted); + +int common_safe_uint64(const char *numstr, uint64_t *converted); + +int common_safe_uint(const char *numstr, unsigned int *converted); + +int common_safe_int8(const char *numstr, int8_t *converted); + +int common_safe_int16(const char *numstr, int16_t *converted); + +int common_safe_int32(const char *numstr, int32_t *converted); + +int common_safe_int64(const char *numstr, int64_t *converted); + +int common_safe_int(const char *numstr, int *converted); + +typedef struct { + int *keys; + int *values; + size_t len; +} json_map_int_int; + +void free_json_map_int_int(json_map_int_int *map); + +json_map_int_int *make_json_map_int_int(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_int(void *ctx, json_map_int_int *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_int(json_map_int_int *map, int key, int val); + +typedef struct { + int *keys; + bool *values; + size_t len; +} json_map_int_bool; + +void free_json_map_int_bool(json_map_int_bool *map); + +json_map_int_bool *make_json_map_int_bool(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_bool(void *ctx, json_map_int_bool *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val); + +typedef struct { + int *keys; + char **values; + size_t len; +} json_map_int_string; + +void free_json_map_int_string(json_map_int_string *map); + +json_map_int_string *make_json_map_int_string(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_string(void *ctx, json_map_int_string *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_string(json_map_int_string *map, int key, const char *val); + +typedef struct { + char **keys; + int *values; + size_t len; +} json_map_string_int; + +void free_json_map_string_int(json_map_string_int *map); + +json_map_string_int *make_json_map_string_int(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_int(void *ctx, json_map_string_int *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_int(json_map_string_int *map, const char *key, int val); + +typedef struct { + char **keys; + bool *values; + size_t len; +} json_map_string_bool; + +void free_json_map_string_bool(json_map_string_bool *map); + +json_map_string_bool *make_json_map_string_bool(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_bool(void *ctx, json_map_string_bool *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val); + +typedef struct { + char **keys; + char **values; + size_t len; +} json_map_string_string; + +void free_json_map_string_string(json_map_string_string *map); + +json_map_string_string *make_json_map_string_string(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_string(void *ctx, json_map_string_string *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/lxc/json/logger_json_file.c b/src/lxc/json/logger_json_file.c new file mode 100644 index 0000000..6abeef4 --- /dev/null +++ b/src/lxc/json/logger_json_file.c @@ -0,0 +1,246 @@ +// Generated from json-file.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <string.h> +#include <read-file.h> +#include "logger_json_file.h" + +logger_json_file *make_logger_json_file(yajl_val tree, struct parser_context *ctx, parser_error *err) { + logger_json_file *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val tmp = get_val(tree, "log", yajl_t_string); + if (tmp != NULL) { + char *str = YAJL_GET_STRING(tmp); + ret->log = (uint8_t *)safe_strdup(str ? str : ""); + ret->log_len = str != NULL ? strlen(str) : 0; + } + } + { + yajl_val val = get_val(tree, "stream", yajl_t_string); + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->stream = safe_strdup(str ? str : ""); + } + } + { + yajl_val val = get_val(tree, "time", yajl_t_string); + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->time = safe_strdup(str ? str : ""); + } + } + { + yajl_val tmp = get_val(tree, "attrs", yajl_t_string); + if (tmp != NULL) { + char *str = YAJL_GET_STRING(tmp); + ret->attrs = (uint8_t *)safe_strdup(str ? str : ""); + ret->attrs_len = str != NULL ? strlen(str) : 0; + } + } + + if (tree->type == yajl_t_object && (ctx->options & PARSE_OPTIONS_STRICT)) { + int i; + for (i = 0; i < tree->u.object.len; i++) + if (strcmp(tree->u.object.keys[i], "log") && + strcmp(tree->u.object.keys[i], "stream") && + strcmp(tree->u.object.keys[i], "time") && + strcmp(tree->u.object.keys[i], "attrs")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_logger_json_file(logger_json_file *ptr) { + if (ptr == NULL) + return; + free(ptr->log); + ptr->log = NULL; + free(ptr->stream); + ptr->stream = NULL; + free(ptr->time); + ptr->time = NULL; + free(ptr->attrs); + ptr->attrs = NULL; + free(ptr); +} + +yajl_gen_status gen_logger_json_file(yajl_gen g, logger_json_file *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + *err = 0; + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) || (ptr != NULL && ptr->log != NULL && ptr->log_len)) { + const char *str = ""; + size_t len = 0; + stat = reformat_map_key(g, "log", strlen("log")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->log != NULL) { + str = (const char *)ptr->log; + len = ptr->log_len; + } + stat = reformat_string(g, str, len); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->stream != NULL)) { + char *str = ""; + stat = reformat_map_key(g, "stream", strlen("stream")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->stream != NULL) { + str = ptr->stream; + } + stat = reformat_string(g, str, strlen(str)); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->time != NULL)) { + char *str = ""; + stat = reformat_map_key(g, "time", strlen("time")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->time != NULL) { + str = ptr->time; + } + stat = reformat_string(g, str, strlen(str)); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) || (ptr != NULL && ptr->attrs != NULL && ptr->attrs_len)) { + const char *str = ""; + size_t len = 0; + stat = reformat_map_key(g, "attrs", strlen("attrs")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->attrs != NULL) { + str = (const char *)ptr->attrs; + len = ptr->attrs_len; + } + stat = reformat_string(g, str, len); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} + + +logger_json_file *logger_json_file_parse_file(const char *filename, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + size_t filesize; + char *content = NULL; + + if (filename == NULL || err == NULL) + return NULL; + + *err = NULL; + content = read_file(filename, &filesize); + if (content == NULL) { + if (asprintf(err, "cannot read the file: %s", filename) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = logger_json_file_parse_data(content, ctx, err); + free(content); + return ptr; +} + +logger_json_file *logger_json_file_parse_file_stream(FILE *stream, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + size_t filesize; + char *content = NULL ; + + if (stream == NULL || err == NULL) + return NULL; + + *err = NULL; + content = fread_file(stream, &filesize); + if (content == NULL) { + *err = safe_strdup("cannot read the file"); + return NULL; + } + ptr = logger_json_file_parse_data(content, ctx, err); + free(content); + return ptr; +} + +logger_json_file *logger_json_file_parse_data(const char *jsondata, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + yajl_val tree; + char errbuf[1024]; + struct parser_context tmp_ctx; + + if (jsondata == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + tree = yajl_tree_parse(jsondata, errbuf, sizeof(errbuf)); + if (tree == NULL) { + if (asprintf(err, "cannot parse the data: %s", errbuf) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = make_logger_json_file(tree, ctx, err); + yajl_tree_free(tree); + return ptr; +} +char *logger_json_file_generate_json(logger_json_file *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen g = NULL; + struct parser_context tmp_ctx; + const unsigned char *gen_buf = NULL; + char *json_buf = NULL; + size_t gen_len = 0; + + if (ptr == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + + if (!json_gen_init(&g, ctx)) { + *err = safe_strdup("Json_gen init failed"); + goto out; + } + if (yajl_gen_status_ok != gen_logger_json_file(g, ptr, ctx, err)) { + if (*err == NULL) + *err = safe_strdup("Failed to generate json"); + goto free_out; + } + yajl_gen_get_buf(g, &gen_buf, &gen_len); + if (gen_buf == NULL) { + *err = safe_strdup("Error to get generated json"); + goto free_out; + } + + if (gen_len == SIZE_MAX) { + *err = safe_strdup("Invalid buffer length"); + goto free_out; + } + json_buf = safe_malloc(gen_len + 1); + (void)memcpy(json_buf, gen_buf, gen_len); + json_buf[gen_len] = '\0'; + +free_out: + yajl_gen_clear(g); + yajl_gen_free(g); +out: + return json_buf; +} diff --git a/src/lxc/json/logger_json_file.h b/src/lxc/json/logger_json_file.h new file mode 100644 index 0000000..ad5af7b --- /dev/null +++ b/src/lxc/json/logger_json_file.h @@ -0,0 +1,45 @@ +// Generated from json-file.json. Do not edit! +#ifndef LOGGER_JSON_FILE_SCHEMA_H +#define LOGGER_JSON_FILE_SCHEMA_H + +#include <sys/types.h> +#include <stdint.h> +#include "json_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t *log; + size_t log_len; + + char *stream; + + char *time; + + uint8_t *attrs; + size_t attrs_len; + +} +logger_json_file; + +void free_logger_json_file(logger_json_file *ptr); + +logger_json_file *make_logger_json_file(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_logger_json_file(yajl_gen g, logger_json_file *ptr, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_file(const char *filename, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_file_stream(FILE *stream, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_data(const char *jsondata, struct parser_context *ctx, parser_error *err); + +char *logger_json_file_generate_json(logger_json_file *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/oci_runtime_hooks.c b/src/lxc/json/oci_runtime_hooks.c new file mode 100644 index 0000000..41ddb67 --- /dev/null +++ b/src/lxc/json/oci_runtime_hooks.c @@ -0,0 +1,52 @@ +/****************************************************************************** + * Copyright (C), 1988-1999, Huawei Tech. Co., Ltd. + * FileName: oci_runtime_hooks.c + * Author: maoweiyong Version: 0.1 Date: 2018-11-07 + * Explanation: provide oci runtime hooks functions + ******************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <read-file.h> +#include "oci_runtime_hooks.h" + +#include "log.h" +#include "utils.h" + +#define PARSE_ERR_BUFFER_SIZE 1024 + +oci_runtime_spec_hooks *oci_runtime_spec_hooks_parse_file(const char *filename, + struct parser_context *ctx, parser_error *err) +{ + yajl_val tree; + size_t filesize; + + if (!filename || !err) { + return NULL; + } + *err = NULL; + struct parser_context tmp_ctx; + if (!ctx) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + char *content = read_file(filename, &filesize); + char errbuf[PARSE_ERR_BUFFER_SIZE]; + if (content == NULL) { + if (asprintf(err, "cannot read the file: %s", filename) < 0) { + *err = safe_strdup("error allocating memory"); + } + return NULL; + } + tree = yajl_tree_parse(content, errbuf, sizeof(errbuf)); + free(content); + if (tree == NULL) { + if (asprintf(err, "cannot parse the file: %s", errbuf) < 0) { + *err = safe_strdup("error allocating memory"); + } + return NULL; + } + oci_runtime_spec_hooks *ptr = make_oci_runtime_spec_hooks(tree, ctx, err); + yajl_tree_free(tree); + return ptr; +} diff --git a/src/lxc/json/oci_runtime_hooks.h b/src/lxc/json/oci_runtime_hooks.h new file mode 100644 index 0000000..bf570c9 --- /dev/null +++ b/src/lxc/json/oci_runtime_hooks.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * Copyright (C), 1988-1999, Huawei Tech. Co., Ltd. + * FileName: oci_runtime_hooks.h + * Author: tanyifeng Version: 0.1 Date: 2018-11-08 + * Explanation: provide container oci runtime hooks function definition + ******************************************************************************/ +#ifndef _CONTAINER_HOOKS_H +# define _CONTAINER_HOOKS_H + +# include "oci_runtime_spec.h" + +oci_runtime_spec_hooks *oci_runtime_spec_hooks_parse_file(const char *filename, + struct parser_context *ctx, parser_error *err); + +#endif diff --git a/src/lxc/json/oci_runtime_spec.c b/src/lxc/json/oci_runtime_spec.c new file mode 100644 index 0000000..fd342de --- /dev/null +++ b/src/lxc/json/oci_runtime_spec.c @@ -0,0 +1,195 @@ +// Generated from spec.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <string.h> +#include <read-file.h> +#include "oci_runtime_spec.h" + +oci_runtime_spec_hooks *make_oci_runtime_spec_hooks(yajl_val tree, struct parser_context *ctx, parser_error *err) { + oci_runtime_spec_hooks *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val tmp = get_val(tree, "prestart", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->prestart_len = YAJL_GET_ARRAY(tmp)->len; + ret->prestart = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->prestart)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->prestart[i] = make_defs_hook(val, ctx, err); + if (ret->prestart[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + { + yajl_val tmp = get_val(tree, "poststart", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->poststart_len = YAJL_GET_ARRAY(tmp)->len; + ret->poststart = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->poststart)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->poststart[i] = make_defs_hook(val, ctx, err); + if (ret->poststart[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + { + yajl_val tmp = get_val(tree, "poststop", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->poststop_len = YAJL_GET_ARRAY(tmp)->len; + ret->poststop = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->poststop)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->poststop[i] = make_defs_hook(val, ctx, err); + if (ret->poststop[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + + if (tree->type == yajl_t_object && (ctx->options & PARSE_OPTIONS_STRICT)) { + int i; + for (i = 0; i < tree->u.object.len; i++) + if (strcmp(tree->u.object.keys[i], "prestart") && + strcmp(tree->u.object.keys[i], "poststart") && + strcmp(tree->u.object.keys[i], "poststop")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_oci_runtime_spec_hooks(oci_runtime_spec_hooks *ptr) { + if (ptr == NULL) + return; + if (ptr->prestart != NULL) { + size_t i; + for (i = 0; i < ptr->prestart_len; i++) + if (ptr->prestart[i] != NULL) { + free_defs_hook(ptr->prestart[i]); + ptr->prestart[i] = NULL; + } + free(ptr->prestart); + ptr->prestart = NULL; + } + if (ptr->poststart != NULL) { + size_t i; + for (i = 0; i < ptr->poststart_len; i++) + if (ptr->poststart[i] != NULL) { + free_defs_hook(ptr->poststart[i]); + ptr->poststart[i] = NULL; + } + free(ptr->poststart); + ptr->poststart = NULL; + } + if (ptr->poststop != NULL) { + size_t i; + for (i = 0; i < ptr->poststop_len; i++) + if (ptr->poststop[i] != NULL) { + free_defs_hook(ptr->poststop[i]); + ptr->poststop[i] = NULL; + } + free(ptr->poststop); + ptr->poststop = NULL; + } + free(ptr); +} + +yajl_gen_status gen_oci_runtime_spec_hooks(yajl_gen g, oci_runtime_spec_hooks *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + *err = 0; + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->prestart != NULL)) { + size_t len = 0, i; + stat = reformat_map_key(g, "prestart", strlen("prestart")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->prestart != NULL) { + len = ptr->prestart_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->prestart[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->poststart != NULL)) { + size_t len = 0, i; + stat = reformat_map_key(g, "poststart", strlen("poststart")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->poststart != NULL) { + len = ptr->poststart_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->poststart[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->poststop != NULL)) { + size_t len = 0, i; + stat = reformat_map_key(g, "poststop", strlen("poststop")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->poststop != NULL) { + len = ptr->poststop_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->poststop[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} diff --git a/src/lxc/json/oci_runtime_spec.h b/src/lxc/json/oci_runtime_spec.h new file mode 100644 index 0000000..ef3f161 --- /dev/null +++ b/src/lxc/json/oci_runtime_spec.h @@ -0,0 +1,37 @@ +// Generated from spec.json. Do not edit! +#ifndef OCI_RUNTIME_SPEC_SCHEMA_H +#define OCI_RUNTIME_SPEC_SCHEMA_H + +#include <sys/types.h> +#include <stdint.h> +#include "json_common.h" +#include "defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + defs_hook **prestart; + size_t prestart_len; + + defs_hook **poststart; + size_t poststart_len; + + defs_hook **poststop; + size_t poststop_len; + +} +oci_runtime_spec_hooks; + +void free_oci_runtime_spec_hooks(oci_runtime_spec_hooks *ptr); + +oci_runtime_spec_hooks *make_oci_runtime_spec_hooks(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_oci_runtime_spec_hooks(yajl_gen g, oci_runtime_spec_hooks *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/read-file.c b/src/lxc/json/read-file.c new file mode 100644 index 0000000..34ebeed --- /dev/null +++ b/src/lxc/json/read-file.c @@ -0,0 +1,95 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include <config.h> +#include "read-file.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 02000000 +#endif + +char *fread_file(FILE *stream, size_t *length) +{ + char *buf = NULL, *tmpbuf = NULL; + size_t off = 0; + + while (1) { + size_t ret, newsize; + + newsize = off + BUFSIZ + 1; + tmpbuf = (char *)calloc(1, newsize); + if (tmpbuf == NULL) { + goto out; + } + + if (buf) { + memcpy(tmpbuf, buf, off); + + memset(buf, 0, off); + + free(buf); + } + + buf = tmpbuf; + ret = fread(buf + off, 1, BUFSIZ, stream); + if (!ret && ferror(stream)) { + tmpbuf = NULL; + goto out; + } + if (ret < BUFSIZ || feof(stream)) { + *length = off + ret + 1; + buf[*length - 1] = '\0'; + return buf; + } + off += BUFSIZ; + } +out: + if (buf) { + free(buf); + } + if (tmpbuf) { + free(tmpbuf); + } + return NULL; + +} + +char *read_file(const char *path, size_t *length) +{ + char *buf = NULL; + char rpath[PATH_MAX + 1] = {0}; + int fd = -1; + int tmperrno; + FILE *fp = NULL; + + if (!path || !length) { + return NULL; + } + + if (strlen(path) > PATH_MAX || NULL == realpath(path, rpath)) { + return NULL; + } + + fd = open(rpath, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return NULL; + } + + fp = fdopen(fd, "r"); + tmperrno = errno; + if (!fp) { + close(fd); + errno = tmperrno; + return NULL; + } + + buf = fread_file(fp, length); + fclose(fp); + return buf; +} diff --git a/src/lxc/json/read-file.h b/src/lxc/json/read-file.h new file mode 100644 index 0000000..5d6e0eb --- /dev/null +++ b/src/lxc/json/read-file.h @@ -0,0 +1,11 @@ +#ifndef READ_FILE_H +#define READ_FILE_H + +#include <stddef.h> +#include <stdio.h> + +extern char *fread_file(FILE *stream, size_t *length); + +extern char *read_file(const char *path, size_t *length); + +#endif diff --git a/src/lxc/meson.build b/src/lxc/meson.build index f33257c..3166401 100644 --- a/src/lxc/meson.build +++ b/src/lxc/meson.build @@ -25,7 +25,6 @@ liblxcfs_version_file = configure_file( ) liblxc_sources = files( - 'cgroups/cgfsng.c', 'cgroups/cgroup.c', 'cgroups/cgroup.h', 'cgroups/cgroup2_devices.c', @@ -139,6 +138,31 @@ liblxc_sources = files( 'uuid.c', 'uuid.h') +if want_isulad + liblxc_sources += files( + 'cgroups/isulad_cgfsng.c', + 'exec_commands.c', + 'exec_commands.h', + 'isulad_utils.c', + 'isulad_utils.h', + 'path.c', + 'path.h', + 'json/defs.c', + 'json/defs.h', + 'json/json_common.c', + 'json/json_common.h', + 'json/logger_json_file.c', + 'json/logger_json_file.h', + 'json/oci_runtime_hooks.c', + 'json/oci_runtime_hooks.h', + 'json/oci_runtime_spec.c', + 'json/oci_runtime_spec.h', + 'json/read-file.c', + 'json/read-file.h') +else + liblxc_sources += files('cgroups/cgfsng.c') +endif + if want_apparmor and libapparmor.found() liblxc_sources += files('lsm/apparmor.c') endif diff --git a/src/lxc/path.c b/src/lxc/path.c new file mode 100644 index 0000000..3881058 --- /dev/null +++ b/src/lxc/path.c @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved + * Description: isulad utils + * Author: lifeng + * Create: 2020-04-11 +******************************************************************************/ +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <libgen.h> + +#include "path.h" +#include "log.h" +#include "isulad_utils.h" + +lxc_log_define(lxc_path_ui, lxc); + +#define ISSLASH(C) ((C) == '/') +#define IS_ABSOLUTE_FILE_NAME(F) (ISSLASH ((F)[0])) + +static bool do_clean_path_continue(const char *endpos, const char *stpos, const char *respath, char **dst) +{ + if (endpos - stpos == 1 && stpos[0] == '.') { + return true; + } else if (endpos - stpos == 2 && stpos[0] == '.' && stpos[1] == '.') { + char *dest = *dst; + if (dest <= respath + 1) { + return true; + } + for (--dest; dest > respath && !ISSLASH(dest[-1]); --dest) { + *dst = dest; + return true; + } + *dst = dest; + return true; + } + return false; +} + +int do_clean_path(const char *respath, const char *limit_respath, + const char *stpos, char **dst) +{ + char *dest = *dst; + const char *endpos = NULL; + + for (endpos = stpos; *stpos; stpos = endpos) { + while (ISSLASH(*stpos)) { + ++stpos; + } + + for (endpos = stpos; *endpos && !ISSLASH(*endpos); ++endpos) { + } + + if (endpos - stpos == 0) { + break; + } else if (do_clean_path_continue(endpos, stpos, respath, &dest)) { + continue; + } + + if (!ISSLASH(dest[-1])) { + *dest++ = '/'; + } + + if (dest + (endpos - stpos) >= limit_respath) { + ERROR("Path is too long"); + if (dest > respath + 1) { + dest--; + } + *dest = '\0'; + return -1; + } + + memcpy(dest, stpos, (size_t)(endpos - stpos)); + dest += endpos - stpos; + *dest = '\0'; + } + *dst = dest; + return 0; +} + +char *cleanpath(const char *path, char *realpath, size_t realpath_len) +{ + char *respath = NULL; + char *dest = NULL; + const char *stpos = NULL; + const char *limit_respath = NULL; + + if (path == NULL || path[0] == '\0' || \ + realpath == NULL || (realpath_len < PATH_MAX)) { + return NULL; + } + + respath = realpath; + + memset(respath, 0, realpath_len); + limit_respath = respath + PATH_MAX; + + if (!IS_ABSOLUTE_FILE_NAME(path)) { + if (!getcwd(respath, PATH_MAX)) { + ERROR("Failed to getcwd"); + respath[0] = '\0'; + goto error; + } + dest = strchr(respath, '\0'); + if (dest == NULL) { + ERROR("Failed to get the end of respath"); + goto error; + } + if (strlen(path) > (PATH_MAX - strlen(respath) - 1)) { + ERROR("Path is too long"); + goto error; + } + strcat(respath, path); + stpos = path; + } else { + dest = respath; + *dest++ = '/'; + stpos = path; + } + + if (do_clean_path(respath, limit_respath, stpos, &dest)) { + goto error; + } + + if (dest > respath + 1 && ISSLASH(dest[-1])) { + --dest; + } + *dest = '\0'; + + return respath; + +error: + return NULL; +} + +static int do_path_realloc(const char *start, const char *end, + char **rpath, char **dest, const char **rpath_limit) +{ + long long dest_offset = *dest - *rpath; + char *new_rpath = NULL; + size_t new_size; + int nret = 0; + size_t gap = 0; + + if (*dest + (end - start) < *rpath_limit) { + return 0; + } + + gap = (size_t)(end - start) + 1; + new_size = (size_t)(*rpath_limit - *rpath); + if (new_size > SIZE_MAX - gap) { + ERROR("Out of range!"); + return -1; + } + + if (gap > PATH_MAX) { + new_size += gap; + } else { + new_size += PATH_MAX; + } + nret = lxc_mem_realloc((void **)&new_rpath, new_size, *rpath, PATH_MAX); + if (nret) { + ERROR("Failed to realloc memory for files limit variables"); + return -1; + } + *rpath = new_rpath; + *rpath_limit = *rpath + new_size; + + *dest = *rpath + dest_offset; + + return 0; +} + +static int do_get_symlinks_copy_buf(const char *buf, const char *prefix, size_t prefix_len, + char **rpath, char **dest) +{ + if (IS_ABSOLUTE_FILE_NAME(buf)) { + if (prefix_len) { + memcpy(*rpath, prefix, prefix_len); + } + *dest = *rpath + prefix_len; + *(*dest)++ = '/'; + } else { + if (*dest > *rpath + prefix_len + 1) { + for (--(*dest); *dest > *rpath && !ISSLASH((*dest)[-1]); --(*dest)) { + continue; + } + } + } + return 0; +} + +static int do_get_symlinks(const char **fullpath, const char *prefix, size_t prefix_len, + char **rpath, char **dest, const char **end, + int *num_links, char **extra_buf) +{ + char *buf = NULL; + size_t len; + ssize_t n; + int ret = -1; + + if (++(*num_links) > MAXSYMLINKS) { + ERROR("Too many links in '%s'", *fullpath); + goto out; + } + + buf = lxc_common_calloc_s(PATH_MAX); + if (buf == NULL) { + ERROR("Out of memory"); + goto out; + } + + n = readlink(*rpath, buf, PATH_MAX - 1); + if (n < 0) { + goto out; + } + buf[n] = '\0'; + + if (*extra_buf == NULL) { + *extra_buf = lxc_common_calloc_s(PATH_MAX); + if (*extra_buf == NULL) { + ERROR("Out of memory"); + goto out; + } + } + + len = strlen(*end); + if (len >= (size_t)(PATH_MAX - n)) { + ERROR("Path is too long"); + goto out; + } + + memmove(&(*extra_buf)[n], *end, len + 1); + memcpy(*extra_buf, buf, (size_t)n); + + *fullpath = *end = *extra_buf; + + if (do_get_symlinks_copy_buf(buf, prefix, prefix_len, rpath, dest) != 0) { + goto out; + } + + ret = 0; +out: + free(buf); + return ret; +} + +static bool do_eval_symlinks_in_scope_is_symlink(const char *path) +{ + struct stat st; + + if (lstat(path, &st) < 0) { + return true; + } + + if (!S_ISLNK(st.st_mode)) { + return true; + } + return false; +} + +static void do_eval_symlinks_skip_slash(const char **start, const char **end) +{ + while (ISSLASH(**start)) { + ++(*start); + } + + for (*end = *start; **end && !ISSLASH(**end); ++(*end)) { + } +} + +static inline void skip_dest_traling_slash(char **dest, char **rpath, size_t prefix_len) +{ + if (*dest > *rpath + prefix_len + 1) { + for (--(*dest); *dest > *rpath && !ISSLASH((*dest)[-1]); --(*dest)) { + continue; + } + } +} + +static inline bool is_current_char(const char c) +{ + return c == '.'; +} + +static inline bool is_specify_current(const char *end, const char *start) +{ + return (end - start == 1) && is_current_char(start[0]); +} + +static inline bool is_specify_parent(const char *end, const char *start) +{ + return (end - start == 2) && is_current_char(start[0]) && is_current_char(start[1]); +} + +static int do_eval_symlinks_in_scope(const char *fullpath, const char *prefix, + size_t prefix_len, + char **rpath, char **dest, const char *rpath_limit) +{ + const char *start = NULL; + const char *end = NULL; + char *extra_buf = NULL; + int nret = 0; + int num_links = 0; + + start = fullpath + prefix_len; + for (end = start; *start; start = end) { + do_eval_symlinks_skip_slash(&start, &end); + if (end - start == 0) { + break; + } else if (is_specify_current(end, start)) { + ; + } else if (is_specify_parent(end, start)) { + skip_dest_traling_slash(dest, rpath, prefix_len); + } else { + if (!ISSLASH((*dest)[-1])) { + *(*dest)++ = '/'; + } + + nret = do_path_realloc(start, end, rpath, dest, &rpath_limit); + if (nret != 0) { + nret = -1; + goto out; + } + + memcpy(*dest, start, (size_t)(end - start)); + *dest += end - start; + **dest = '\0'; + + if (do_eval_symlinks_in_scope_is_symlink(*rpath)) { + continue; + } + + nret = do_get_symlinks(&fullpath, prefix, prefix_len, rpath, dest, &end, &num_links, &extra_buf); + if (nret != 0) { + nret = -1; + goto out; + } + } + } +out: + free(extra_buf); + return nret; +} + +static char *eval_symlinks_in_scope(const char *fullpath, const char *rootpath) +{ + char resroot[PATH_MAX] = {0}; + char *root = NULL; + char *rpath = NULL; + char *dest = NULL; + char *prefix = NULL; + const char *rpath_limit = NULL; + size_t prefix_len; + + if (fullpath == NULL || rootpath == NULL) { + return NULL; + } + + root = cleanpath(rootpath, resroot, sizeof(resroot)); + if (root == NULL) { + ERROR("Failed to get cleaned path"); + return NULL; + } + + if (!strcmp(fullpath, root)) { + return safe_strdup(fullpath); + } + + if (strstr(fullpath, root) == NULL) { + ERROR("Path '%s' is not in '%s'", fullpath, root); + return NULL; + } + + rpath = lxc_common_calloc_s(PATH_MAX); + if (rpath == NULL) { + ERROR("Out of memory"); + goto out; + } + rpath_limit = rpath + PATH_MAX; + + prefix = root; + prefix_len = (size_t)strlen(prefix); + if (!strcmp(prefix, "/")) { + prefix_len = 0; + } + + dest = rpath; + if (prefix_len) { + memcpy(rpath, prefix, prefix_len); + dest += prefix_len; + } + *dest++ = '/'; + + if (do_eval_symlinks_in_scope(fullpath, prefix, prefix_len, &rpath, &dest, + rpath_limit)) { + goto out; + } + + if (dest > rpath + prefix_len + 1 && ISSLASH(dest[-1])) { + --dest; + } + *dest = '\0'; + return rpath; + +out: + free(rpath); + return NULL; +} + +// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an +// absolute path. This function handles paths in a platform-agnostic manner. +char *follow_symlink_in_scope(const char *fullpath, const char *rootpath) +{ + char resfull[PATH_MAX] = {0}, *full = NULL; + char resroot[PATH_MAX] = {0}, *root = NULL; + + full = cleanpath(fullpath, resfull, PATH_MAX); + if (!full) { + ERROR("Failed to get cleaned path"); + return NULL; + } + + root = cleanpath(rootpath, resroot, PATH_MAX); + if (!root) { + ERROR("Failed to get cleaned path"); + return NULL; + } + + return eval_symlinks_in_scope(full, root); +} + +// Rel returns a relative path that is lexically equivalent to targpath when +// joined to basepath with an intervening separator. That is, +// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. +// On success, the returned path will always be relative to basepath, +// even if basepath and targpath share no elements. +// An error is returned if targpath can't be made relative to basepath or if +// knowing the current working directory would be necessary to compute it. +// Rel calls Clean on the result. +char *path_relative(const char *basepath, const char *targpath) +{ + char resbase[PATH_MAX] = {0}, *base = NULL; + char restarg[PATH_MAX] = {0}, *targ = NULL; + size_t bl = 0, tl = 0, b0 = 0, bi = 0, t0 = 0, ti = 0; + + base = cleanpath(basepath, resbase, PATH_MAX); + if (!base) { + ERROR("Failed to get cleaned path"); + return NULL; + } + + targ = cleanpath(targpath, restarg, PATH_MAX); + if (!targ) { + ERROR("Failed to get cleaned path"); + return NULL; + } + + if (strcmp(base, targ) == 0) + return safe_strdup("."); + + bl = strlen(base); + tl = strlen(targ); + while(true) { + while(bi < bl && !ISSLASH(base[bi])) + bi++; + while(ti < tl && !ISSLASH(targ[ti])) + ti++; + //not the same string + if (((bi - b0) != (ti - t0)) || strncmp(base + b0, targ + t0, bi - b0)) + break; + if (bi < bl) + bi++; + if (ti < tl) + ti++; + b0 = bi; + t0 = ti; + } + + if (b0 != bl) { + // Base elements left. Must go up before going down. + size_t seps = 0, i; + size_t ncopyed = 0, seps_size; + char *buf = NULL; + + for (bi = b0; bi < bl; bi++) { + if (ISSLASH(base[bi])) + seps++; + } + //strlen(..) + strlen(/..) + '\0' + seps_size = 2 + seps * 3 + 1; + if (t0 != tl) + seps_size += 1 + tl - t0; + + buf = calloc(seps_size, 1); + if (!buf) { + ERROR("Out of memory"); + return NULL; + } + buf[ncopyed++] = '.'; + buf[ncopyed++] = '.'; + for (i = 0; i < seps; i++) { + buf[ncopyed++] = '/'; + buf[ncopyed++] = '.'; + buf[ncopyed++] = '.'; + } + if (t0 != tl) { + buf[ncopyed++] = '/'; + memcpy(buf + ncopyed, targ + t0, tl - t0 + 1); + } + return buf; + } + + return safe_strdup(targ + t0); +} diff --git a/src/lxc/path.h b/src/lxc/path.h new file mode 100644 index 0000000..59bbc59 --- /dev/null +++ b/src/lxc/path.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2020. Allrights reserved + * Description: isulad utils + * Author: lifeng + * Create: 2020-04-11 +******************************************************************************/ +#ifndef __ISULAD_PATH_H_ +#define __ISULAD_PATH_H_ + +#include <stdbool.h> + +/* + * cleanpath is similar to realpath of glibc, but not expands symbolic links, + * and not check the existence of components of the path. + */ +char *cleanpath(const char *path, char *realpath, size_t realpath_len); + +// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an +// absolute path. This function handles paths in a platform-agnostic manner. +char *follow_symlink_in_scope(const char *fullpath, const char *rootpath); + +// Rel returns a relative path that is lexically equivalent to targpath when +// joined to basepath with an intervening separator. That is, +// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. +// On success, the returned path will always be relative to basepath, +// even if basepath and targpath share no elements. +// An error is returned if targpath can't be made relative to basepath or if +// knowing the current working directory would be necessary to compute it. +// Rel calls Clean on the result. +char *path_relative(const char *basepath, const char *targpath); + +#endif -- 2.25.1
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2