/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved. * iSulad licensed under the Mulan PSL v1. * You can use this software according to the terms and conditions of the Mulan PSL v1. * You may obtain a copy of Mulan PSL v1 at: * http://license.coscl.org.cn/MulanPSL * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR * PURPOSE. * See the Mulan PSL v1 for more details. * Author: maoweiyong * Create: 2017-11-22 * Description: provide system information functions ******************************************************************************/ #include #include #include #include #include #include #include "error.h" #include "liblcrd.h" #include "sysinfo.h" #include "log.h" #include "read_file.h" // Cgroup Item Definition #define CGROUP_BLKIO_WEIGHT "blkio.weight" #define CGROUP_BLKIO_WEIGHT_DEVICE "blkio.weight_device" #define CGROUP_BLKIO_READ_BPS_DEVICE "blkio.throttle.read_bps_device" #define CGROUP_BLKIO_WRITE_BPS_DEVICE "blkio.throttle.write_bps_device" #define CGROUP_BLKIO_READ_IOPS_DEVICE "blkio.throttle.read_iops_device" #define CGROUP_BLKIO_WRITE_IOPS_DEVICE "blkio.throttle.write_iops_device" #define CGROUP_CPU_SHARES "cpu.shares" #define CGROUP_CPU_PERIOD "cpu.cfs_period_us" #define CGROUP_CPU_QUOTA "cpu.cfs_quota_us" #define CGROUP_CPU_RT_PERIOD "cpu.rt_period_us" #define CGROUP_CPU_RT_RUNTIME "cpu.rt_runtime_us" #define CGROUP_CPUSET_CPUS "cpuset.cpus" #define CGROUP_CPUSET_MEMS "cpuset.mems" #define CGROUP_MEMORY_LIMIT "memory.limit_in_bytes" #define CGROUP_MEMORY_SWAP "memory.memsw.limit_in_bytes" #define CGROUP_MEMORY_SWAPPINESS "memory.swappiness" #define CGROUP_MEMORY_RESERVATION "memory.soft_limit_in_bytes" #define CGROUP_KENEL_MEMORY_LIMIT "memory.kmem.limit_in_bytes" #define CGROUP_MEMORY_OOM_CONTROL "memory.oom_control" struct layer { char **controllers; char *mountpoint; }; static void free_list(char **str_list) { int i; if (str_list == NULL) { return; } for (i = 0; str_list[i]; i++) { free(str_list[i]); str_list[i] = NULL; } free(str_list); } static void free_layer(struct layer **layers) { struct layer **it = NULL; if (layers == NULL) { return; } for (it = layers; it && *it; it++) { free((*it)->mountpoint); (*it)->mountpoint = NULL; free_list((*it)->controllers); (*it)->controllers = NULL; free(*it); *it = NULL; } free(layers); } static int add_null_to_list(void ***list) { int ret = 0; size_t index = 0; size_t newsize, oldsize; void **newlist = NULL; if (*list != NULL) { for (; (*list)[index] != NULL; index++) { } } if (index > SIZE_MAX / sizeof(void **) - 2) { ERROR("Out of range"); return -1; } newsize = (index + 2) * sizeof(void **); oldsize = index * sizeof(void **); ret = mem_realloc((void **)&newlist, newsize, (*list), oldsize); if (ret < 0) { ERROR("Out of memory"); return -1; } *list = newlist; (*list)[index + 1] = NULL; return (int)index; } static int append_string(char ***list, const char *entry) { int index; char *dup_entry = NULL; index = add_null_to_list((void ***)list); if (index < 0) { return -1; } dup_entry = util_strdup_s(entry); if (dup_entry == NULL) { return -1; } (*list)[index] = dup_entry; return 0; } static int append_subsystem_to_list(char ***klist, char ***nlist, const char *ptoken) { int ret = 0; if (strncmp(ptoken, "name=", 5) == 0) { ret = append_string(nlist, ptoken); if (ret != 0) { ERROR("Failed to append string"); return -1; } } else { ret = append_string(klist, ptoken); if (ret != 0) { ERROR("Failed to append string"); return -1; } } return 0; } static int get_cgroup_subsystems(char ***klist, char ***nlist) { int ret = 0; size_t length = 0; FILE *fp = NULL; char *pline = NULL; fp = util_fopen("/proc/self/cgroup", "r"); if (fp == NULL) { return -1; } while (getline(&pline, &length, fp) != -1) { char *pos = NULL; char *pos2 = NULL; char *ptoken = NULL; char *psave = NULL; pos = strchr(pline, ':'); if (pos == NULL) { ERROR("Invalid cgroup entry: must contain at least two colons: %s", pline); ret = -1; goto out; } pos++; pos2 = strchr(pos, ':'); if (pos2 == NULL) { ERROR("Invalid cgroup entry: must contain at least two colons: %s", pline); ret = -1; goto out; } *pos2 = '\0'; if ((pos2 - pos) == 0) { INFO("Not supported cgroup entry: %s", pline); continue; } for (ptoken = strtok_r(pos, ",", &psave); ptoken; ptoken = strtok_r(NULL, ",", &psave)) { if (append_subsystem_to_list(klist, nlist, ptoken)) { goto out; } } } out: free(pline); fclose(fp); if (ret != 0) { free_list(*klist); *klist = NULL; free_list(*nlist); *nlist = NULL; } return ret; } static bool list_contain_string(const char **a_list, const char *str) { int i; if (a_list == NULL) { return false; } for (i = 0; a_list[i]; i++) { if (strcmp(a_list[i], str) == 0) { return true; } } return false; } static char *cgroup_legacy_must_prefix_named(const char *entry) { size_t len; char *prefixed = NULL; const char *prefix = "name="; len = strlen(entry); if (((SIZE_MAX - len) - 1) < strlen(prefix)) { ERROR("Out of memory"); return NULL; } prefixed = util_common_calloc_s(len + strlen(prefix) + 1); if (prefixed == NULL) { ERROR("Out of memory"); return NULL; } (void)memcpy(prefixed, prefix, strlen(prefix)); (void)memcpy(prefixed + strlen(prefix), entry, len); prefixed[len + strlen(prefix)] = '\0'; return prefixed; } static int append_controller(const char **klist, const char **nlist, char ***clist, const char *entry) { int index; char *dup_entry = NULL; if (list_contain_string(klist, entry) && list_contain_string(nlist, entry)) { ERROR("Refusing to use ambiguous controller \"%s\"", entry); ERROR("It is both a named and kernel subsystem"); return -1; } index = add_null_to_list((void ***)clist); if (index < 0) { return -1; } if (strncmp(entry, "name=", 5) == 0) { dup_entry = util_strdup_s(entry); } else if (list_contain_string(klist, entry)) { dup_entry = util_strdup_s(entry); } else { dup_entry = cgroup_legacy_must_prefix_named(entry); } if (dup_entry == NULL) { return -1; } (*clist)[index] = dup_entry; return 0; } static inline bool is_cgroup_mountpoint(const char *mp) { return strncmp(mp, "/sys/fs/cgroup/", strlen("/sys/fs/cgroup/")) == 0; } static void set_char_to_terminator(char *p) { *p = '\0'; } static char **cgroup_get_controllers(const char **klist, const char **nlist, const char *line) { int index; char *dup = NULL; char *pos2 = NULL; char *tok = NULL; const char *pos = line; char *psave = NULL; char *sep = ","; char **pret = NULL; for (index = 0; index < 4; index++) { pos = strchr(pos, ' '); if (pos == NULL) { ERROR("Invalid mountinfo format \"%s\"", line); return NULL; } pos++; } if (!is_cgroup_mountpoint(pos)) { return NULL; } pos += strlen("/sys/fs/cgroup/"); pos2 = strchr(pos, ' '); if (pos2 == NULL) { ERROR("Invalid mountinfo format \"%s\"", line); return NULL; } set_char_to_terminator(pos2); dup = util_strdup_s(pos); *pos2 = ' '; for (tok = strtok_r(dup, sep, &psave); tok; tok = strtok_r(NULL, sep, &psave)) { if (append_controller(klist, nlist, &pret, tok)) { ERROR("Failed to append controller"); free_list(pret); pret = NULL; break; } } free(dup); return pret; } /* add hierarchy */ static int cgroup_add_layer(struct layer ***layers, char **clist, char *mountpoint) { int index; struct layer *newh = NULL; newh = util_common_calloc_s(sizeof(struct layer)); if (newh == NULL) { return -1; } newh->controllers = clist; newh->mountpoint = mountpoint; index = add_null_to_list((void ***)layers); if (index < 0) { free(newh); return -1; } (*layers)[index] = newh; return 0; } int cgroup_get_mountpoint_and_root(char *pline, char **mountpoint, char **root) { int index; char *posmp = NULL; char *posrt = NULL; char *pos = pline; // find root for (index = 0; index < 3; index++) { pos = strchr(pos, ' '); if (pos == NULL) { return -1; } pos++; } posrt = pos; // find mountpoint pos = strchr(pos, ' '); if (pos == NULL) { return -1; } *pos = '\0'; if (root != NULL) { *root = util_strdup_s(posrt); } pos++; posmp = pos; if (!is_cgroup_mountpoint(posmp)) { return -1; } pos = strchr(pos + strlen("/sys/fs/cgroup/"), ' '); if (pos == NULL) { return -1; } *pos = '\0'; if (mountpoint != NULL) { *mountpoint = util_strdup_s(posmp); } return 0; } static bool lists_intersect(const char **controllers, const char **list) { int index; if (controllers == NULL || list == NULL) { return false; } for (index = 0; controllers[index]; index++) { if (list_contain_string(list, controllers[index])) { return true; } } return false; } static bool controller_list_is_dup(struct layer **llist, const char **clist) { int index; if (llist == NULL) { return false; } for (index = 0; llist[index]; index++) { if (lists_intersect((const char **)llist[index]->controllers, (const char **)clist)) { return true; } } return false; } static struct layer **cgroup_layers_find(void) { int nret; FILE *fp = NULL; size_t length = 0; char *pline = NULL; char **klist = NULL; char **nlist = NULL; struct layer **layers = NULL; nret = get_cgroup_subsystems(&klist, &nlist); if (nret < 0) { ERROR("Failed to retrieve available legacy cgroup controllers\n"); return NULL; } fp = util_fopen("/proc/self/mountinfo", "r"); if (fp == NULL) { ERROR("Failed to open \"/proc/self/mountinfo\"\n"); goto out; } while (getline(&pline, &length, fp) != -1) { char *mountpoint = NULL; char **clist = NULL; int mret; clist = cgroup_get_controllers((const char **)klist, (const char **)nlist, pline); if (clist == NULL) { goto list_out; } if (controller_list_is_dup(layers, (const char **)clist)) { goto list_out; } mret = cgroup_get_mountpoint_and_root(pline, &mountpoint, NULL); if (mret != 0 || mountpoint == NULL) { ERROR("Failed parsing mountpoint from \"%s\"\n", pline); goto list_out; } nret = cgroup_add_layer(&layers, clist, mountpoint); if (nret != 0) { ERROR("Failed to add hierarchies"); goto list_out; } continue; list_out: free_list(clist); free(mountpoint); } out: free_list(klist); free_list(nlist); if (fp != NULL) { fclose(fp); } free(pline); return layers; } /* cgroup enabled */ static bool cgroup_enabled(const char *mountpoint, const char *name) { char path[PATH_MAX] = { 0 }; int nret; nret = snprintf(path, sizeof(path), "%s/%s", mountpoint, name); if (nret < 0 || (size_t)nret >= sizeof(path)) { ERROR("Path is too long"); return false; } return util_file_exists(path); } static char *cgroup_get_pagesize(const char *pline) { size_t headlen; char *pos2 = NULL; const char *pos = pline; headlen = strlen("Hugepagesize"); if (strncmp(pos, "Hugepagesize", headlen) != 0) { return NULL; } pos2 = strchr(pos + headlen, ':'); if (pos2 == NULL) { ERROR("Invalid Hugepagesize format \"%s\"", pline); return NULL; } *pos2 = '\0'; pos2++; return util_string_delchar(pos2, ' '); } /* get default huge page size */ char *get_default_huge_page_size(void) { int ret = 0; int64_t sizenum = 0; size_t length = 0; FILE *fp = NULL; char *pagesize = NULL; char *humansize = NULL; char *pline = NULL; fp = util_fopen("/proc/meminfo", "r"); if (fp == NULL) { ERROR("Failed to open \"/proc/meminfo\"\n"); return NULL; } while (getline(&pline, &length, fp) != -1) { pagesize = cgroup_get_pagesize(pline); if (pagesize != NULL) { break; } } if (pagesize == NULL) { ERROR("Failed to get hugepage size"); goto out; } util_trim_newline(pagesize); ret = util_parse_byte_size_string(pagesize, &sizenum); if (ret != 0) { ERROR("Invalid page size: %s", pagesize); goto out; } humansize = util_human_size((uint64_t)sizenum); out: fclose(fp); free(pagesize); free(pline); return humansize; } /* get default total mem size */ uint64_t get_default_total_mem_size(void) { FILE *fp = NULL; size_t len = 0; char *line = NULL; char *p = NULL; uint64_t sysmem_limit = 0; fp = util_fopen("/proc/meminfo", "r"); if (fp == NULL) { ERROR("Failed to open /proc/meminfo: %s", strerror(errno)); return sysmem_limit; } while (getline(&line, &len, fp) != -1) { p = strchr(line, ' '); if (p == NULL) { goto out; } *p = '\0'; p++; if (strcmp(line, "MemTotal:") == 0) { while (*p == ' ' || *p == '\t') { p++; } if (*p == '\0') { goto out; } sysmem_limit = strtoull(p, NULL, 0); break; } } out: fclose(fp); free(line); return sysmem_limit * SIZE_KB; } /* get default operating system */ char *get_operating_system(void) { size_t len = 0; FILE *fp; char *prettyname = NULL; char *pretty_name = "PRETTY_NAME="; char *line = NULL; fp = fopen(etcOsRelease, "r"); if (fp == NULL) { INFO("Failed to open %s :%s", etcOsRelease, strerror(errno)); fp = fopen(altOsRelease, "r"); if (fp == NULL) { ERROR("Failed to open %s :%s", altOsRelease, strerror(errno)); goto out; } } while (getline(&line, &len, fp) != -1) { if ((strncmp(line, pretty_name, strlen(pretty_name))) == 0) { prettyname = util_strdup_s(line + strlen(pretty_name)); break; } } prettyname = util_trim_quotation(prettyname); out: if (fp != NULL) { fclose(fp); } free(line); if (prettyname != NULL) { return prettyname; } return util_strdup_s("Linux"); } static void cgroup_do_log(bool quiet, bool do_log, const char *msg) { if (!quiet && do_log) { WARN("%s", msg); } } static char *find_cgroup_subsystem_mountpoint(struct layer **layers, const char *subsystem) { struct layer **it = NULL; for (it = layers; it && *it; it++) { char **cit = NULL; for (cit = (*it)->controllers; cit && *cit; cit++) { if (strcmp(*cit, subsystem) == 0) { return (*it)->mountpoint; } } } return NULL; } /* check cgroup mem */ static void check_cgroup_mem(struct layer **layers, bool quiet, cgroup_mem_info_t *meminfo) { char *mountpoint = NULL; mountpoint = find_cgroup_subsystem_mountpoint(layers, "memory"); if (mountpoint == NULL) { cgroup_do_log(quiet, true, "Your kernel does not support cgroup memory limit"); return; } meminfo->limit = true; meminfo->swap = cgroup_enabled(mountpoint, CGROUP_MEMORY_SWAP); cgroup_do_log(quiet, !(meminfo->swap), "Your kernel does not support swap memory limit"); meminfo->reservation = cgroup_enabled(mountpoint, CGROUP_MEMORY_RESERVATION); cgroup_do_log(quiet, !(meminfo->reservation), "Your kernel does not support memory reservation"); meminfo->oomkilldisable = cgroup_enabled(mountpoint, CGROUP_MEMORY_OOM_CONTROL); cgroup_do_log(quiet, !(meminfo->oomkilldisable), "Your kernel does not support oom control"); meminfo->swappiness = cgroup_enabled(mountpoint, CGROUP_MEMORY_SWAPPINESS); cgroup_do_log(quiet, !(meminfo->swappiness), "Your kernel does not support memory swappiness"); meminfo->kernel = cgroup_enabled(mountpoint, CGROUP_KENEL_MEMORY_LIMIT); cgroup_do_log(quiet, !(meminfo->kernel), "Your kernel does not support kernel memory limit"); } /* check cgroup cpu */ static void check_cgroup_cpu(struct layer **layers, bool quiet, cgroup_cpu_info_t *cpuinfo) { char *mountpoint = NULL; mountpoint = find_cgroup_subsystem_mountpoint(layers, "cpu"); if (mountpoint == NULL) { cgroup_do_log(quiet, true, "Unable to find cpu cgroup in mounts"); return; } cpuinfo->cpu_rt_period = cgroup_enabled(mountpoint, CGROUP_CPU_RT_PERIOD); cgroup_do_log(quiet, !(cpuinfo->cpu_rt_period), "Your kernel does not support cgroup rt period"); cpuinfo->cpu_rt_runtime = cgroup_enabled(mountpoint, CGROUP_CPU_RT_RUNTIME); cgroup_do_log(quiet, !(cpuinfo->cpu_rt_runtime), "Your kernel does not support cgroup rt runtime"); cpuinfo->cpu_shares = cgroup_enabled(mountpoint, CGROUP_CPU_SHARES); cgroup_do_log(quiet, !(cpuinfo->cpu_shares), "Your kernel does not support cgroup cpu shares"); cpuinfo->cpu_cfs_period = cgroup_enabled(mountpoint, CGROUP_CPU_PERIOD); cgroup_do_log(quiet, !(cpuinfo->cpu_cfs_period), "Your kernel does not support cgroup cfs period"); cpuinfo->cpu_cfs_quota = cgroup_enabled(mountpoint, CGROUP_CPU_QUOTA); cgroup_do_log(quiet, !(cpuinfo->cpu_cfs_quota), "Your kernel does not support cgroup cfs quota"); } /* check cgroup blkio info */ static void check_cgroup_blkio_info(struct layer **layers, bool quiet, cgroup_blkio_info_t *blkioinfo) { char *mountpoint = NULL; mountpoint = find_cgroup_subsystem_mountpoint(layers, "blkio"); if (mountpoint == NULL) { cgroup_do_log(quiet, true, "Unable to find blkio cgroup in mounts"); return; } blkioinfo->blkio_weight = cgroup_enabled(mountpoint, CGROUP_BLKIO_WEIGHT); cgroup_do_log(quiet, !(blkioinfo->blkio_weight), "Your kernel does not support cgroup blkio weight"); blkioinfo->blkio_weight_device = cgroup_enabled(mountpoint, CGROUP_BLKIO_WEIGHT_DEVICE); cgroup_do_log(quiet, !(blkioinfo->blkio_weight_device), "Your kernel does not support cgroup blkio weight_device"); blkioinfo->blkio_read_bps_device = cgroup_enabled(mountpoint, CGROUP_BLKIO_READ_BPS_DEVICE); cgroup_do_log(quiet, !(blkioinfo->blkio_read_bps_device), "Your kernel does not support cgroup blkio throttle.read_bps_device"); blkioinfo->blkio_write_bps_device = cgroup_enabled(mountpoint, CGROUP_BLKIO_WRITE_BPS_DEVICE); cgroup_do_log(quiet, !(blkioinfo->blkio_write_bps_device), "Your kernel does not support cgroup blkio throttle.write_bps_device"); blkioinfo->blkio_read_iops_device = cgroup_enabled(mountpoint, CGROUP_BLKIO_READ_IOPS_DEVICE); cgroup_do_log(quiet, !(blkioinfo->blkio_read_iops_device), "Your kernel does not support cgroup blkio throttle.read_iops_device"); blkioinfo->blkio_write_iops_device = cgroup_enabled(mountpoint, CGROUP_BLKIO_WRITE_IOPS_DEVICE); cgroup_do_log(quiet, !(blkioinfo->blkio_write_iops_device), "Your kernel does not support cgroup blkio throttle.write_iops_device"); } /* check cgroup cpuset info */ static void check_cgroup_cpuset_info(struct layer **layers, bool quiet, cgroup_cpuset_info_t *cpusetinfo) { size_t file_size = 0; char *mountpoint = NULL; char cpuset_cpus_path[PATH_MAX] = { 0 }; char cpuset_mems_path[PATH_MAX] = { 0 }; mountpoint = find_cgroup_subsystem_mountpoint(layers, "cpuset"); if (mountpoint == NULL) { cgroup_do_log(quiet, true, ("Unable to find cpuset cgroup in mounts")); return; } int nret = snprintf(cpuset_cpus_path, sizeof(cpuset_cpus_path), "%s/%s", mountpoint, CGROUP_CPUSET_CPUS); if (nret < 0 || (size_t)nret >= sizeof(cpuset_cpus_path)) { ERROR("Path is too long"); goto error; } cpusetinfo->cpus = read_file(cpuset_cpus_path, &file_size); if (cpusetinfo->cpus == NULL) { ERROR("Failed to read the file: %s", cpuset_cpus_path); goto error; } nret = snprintf(cpuset_mems_path, sizeof(cpuset_mems_path), "%s/%s", mountpoint, CGROUP_CPUSET_MEMS); if (nret < 0 || (size_t)nret >= sizeof(cpuset_mems_path)) { ERROR("Path is too long"); goto error; } cpusetinfo->mems = read_file(cpuset_mems_path, &file_size); if (cpusetinfo->mems == NULL) { ERROR("Failed to read the file: %s", cpuset_mems_path); goto error; } cpusetinfo->cpus = util_trim_space(cpusetinfo->cpus); cpusetinfo->mems = util_trim_space(cpusetinfo->mems); cpusetinfo->cpuset = true; return; error: free(cpusetinfo->cpus); cpusetinfo->cpus = NULL; free(cpusetinfo->mems); cpusetinfo->mems = NULL; } /* check cgroup pids */ static void check_cgroup_pids(bool quiet, cgroup_pids_info_t *pidsinfo) { int ret = 0; char *pidsmp = NULL; ret = find_cgroup_mountpoint_and_root("pids", &pidsmp, NULL); if (ret != 0 || pidsmp == NULL) { if (!quiet) { WARN("Unable to find pids cgroup in mounts"); } goto out; } pidsinfo->pidslimit = true; out: free(pidsmp); } /* check cgroup files */ static void check_cgroup_files(bool quiet, cgroup_files_info_t *filesinfo) { int ret = 0; char *filesmp = NULL; ret = find_cgroup_mountpoint_and_root("files", &filesmp, NULL); if (ret != 0 || filesmp == NULL) { if (!quiet) { WARN("Unable to find pids cgroup in mounts"); } goto out; } filesinfo->fileslimit = true; out: free(filesmp); } /* find cgroup mountpoint and root */ int find_cgroup_mountpoint_and_root(const char *subsystem, char **mountpoint, char **root) { int ret = 0; FILE *fp = NULL; size_t length = 0; char *pline = NULL; fp = util_fopen("/proc/self/mountinfo", "r"); if (fp == NULL) { ERROR("Failed to open \"/proc/self/mountinfo\"\n"); ret = -1; goto free_out; } while (getline(&pline, &length, fp) != -1) { char *dup = NULL; char *p = NULL; char *tok = NULL; char *mp = NULL; char *rt = NULL; char *saveptr = NULL; char *sep = ","; int mret; mret = cgroup_get_mountpoint_and_root(pline, &mp, &rt); if (mret != 0 || mp == NULL || rt == NULL) { goto mp_out; } p = mp; p += strlen("/sys/fs/cgroup/"); dup = util_strdup_s(p); if (dup == NULL) { ERROR("Out of memory"); free(mp); ret = -1; goto free_out; } for (tok = strtok_r(dup, sep, &saveptr); tok; tok = strtok_r(NULL, sep, &saveptr)) { if (strcmp(tok, subsystem) != 0) { continue; } if (mountpoint != NULL) { *mountpoint = mp; } else { free(mp); } if (root != NULL) { *root = rt; } else { free(rt); } free(dup); goto free_out; } free(dup); mp_out: free(mp); free(rt); continue; } free_out: if (fp != NULL) { fclose(fp); } free(pline); return ret; } /* check cgroup hugetlb */ static void check_cgroup_hugetlb(struct layer **layers, bool quiet, cgroup_hugetlb_info_t *hugetlbinfo) { int nret; char *mountpoint = NULL; char *defaultpagesize = NULL; char hugetlbpath[64] = { 0x00 }; mountpoint = find_cgroup_subsystem_mountpoint(layers, "hugetlb"); if (mountpoint == NULL) { cgroup_do_log(quiet, true, "Your kernel does not support cgroup hugetlb limit"); return; } defaultpagesize = get_default_huge_page_size(); if (defaultpagesize == NULL) { WARN("Your kernel does not support cgroup hugetlb limit"); return; } nret = snprintf(hugetlbpath, sizeof(hugetlbpath), "hugetlb.%s.limit_in_bytes", defaultpagesize); if (nret < 0 || (size_t)nret >= sizeof(hugetlbpath)) { WARN("Failed to print hugetlb path"); goto free_out; } hugetlbinfo->hugetlblimit = cgroup_enabled(mountpoint, hugetlbpath); cgroup_do_log(quiet, !hugetlbinfo->hugetlblimit, ("Your kernel does not support hugetlb limit")); free_out: free(defaultpagesize); } /* get huge page sizes */ static char **get_huge_page_sizes() { int index; int ret = 0; char *hugetlbmp = NULL; char **hps = NULL; DIR *dir = NULL; struct dirent *info_archivo = NULL; ret = find_cgroup_mountpoint_and_root("hugetlb", &hugetlbmp, NULL); if (ret != 0 || hugetlbmp == NULL) { ERROR("Hugetlb cgroup not supported"); return NULL; } dir = opendir(hugetlbmp); if (dir == NULL) { ERROR("Failed to open hugetlb cgroup directory: %s", hugetlbmp); goto free_out; } info_archivo = readdir(dir); for (; info_archivo != NULL; info_archivo = readdir(dir)) { char *contain = NULL; char *dup = NULL; char *pos = NULL; char *dot2 = NULL; contain = strstr(info_archivo->d_name, "limit_in_bytes"); if (contain == NULL) { continue; } dup = util_strdup_s(info_archivo->d_name); if (dup == NULL) { goto free_out; } pos = dup; pos = strchr(pos, '.'); if (pos == NULL) { goto dup_free; } *pos = '\0'; pos++; dot2 = strchr(pos, '.'); if (dot2 == NULL) { goto dup_free; } *dot2 = '\0'; index = add_null_to_list((void ***)&hps); if (index < 0) { free(dup); free_list(hps); hps = NULL; goto free_out; } hps[index] = util_strdup_s(pos); dup_free: free(dup); continue; } free_out: free(hugetlbmp); if (dir != NULL) { closedir(dir); } return hps; } /* is huge pagesize valid */ static bool is_huge_pagesize_valid(const char *pagesize) { int nret; bool bret = false; size_t hps_len; char **hps = NULL; char **it = NULL; char hpsbuf[BUFSIZ] = { 0 }; hps = get_huge_page_sizes(); if (hps == NULL) { ERROR("Hugetlb cgroup not supported"); goto free_out; } hps_len = util_array_len((const char **)hps); if (hps_len == 0) { ERROR("Hugetlb cgroup not supported"); goto free_out; } for (it = hps; *it; it++) { nret = snprintf(hpsbuf, sizeof(hpsbuf), "%s ", *it); if (nret < 0 || (size_t)nret >= sizeof(hpsbuf)) { ERROR("hps buf is too short"); goto free_out; } if (strcmp(*it, pagesize) == 0) { bret = true; } } hpsbuf[strlen(hpsbuf) - 1] = '\0'; free_out: if (!bret) { ERROR("Invalid hugepage size: %s, should be one of [%s]", pagesize, hpsbuf); lcrd_set_error_message("Invalid hugepage size: %s, should be one of [%s]", pagesize, hpsbuf); if (g_lcrd_errmsg == NULL) { ERROR("Out of memory"); } } free_list(hps); return bret; } // isHugeLimitValid check whether input hugetlb limit legal // it will check whether the limit size is times of size static void is_hugelimit_valid(const char *pagesize, uint64_t limit) { int ret; int64_t sizeint = 0; ret = util_parse_byte_size_string(pagesize, &sizeint); if (ret < 0 || !sizeint) { WARN("Invalid pagesize: %s", pagesize); return; } if (limit % (uint64_t)sizeint != 0) { WARN("HugeTlb limit should be times of hugepage size. " "cgroup will down round to the nearest multiple"); } } // check whether hugetlb pagesize and limit legal char *validate_hugetlb(const char *pagesize, uint64_t limit) { char *newpagesize = NULL; int64_t sizeint = 0; if (pagesize != NULL && strlen(pagesize)) { int nret = util_parse_byte_size_string(pagesize, &sizeint); if (nret < 0) { ERROR("Invalid pagesize: %s", pagesize); return NULL; } newpagesize = util_human_size((uint64_t)sizeint); if (newpagesize == NULL) { ERROR("Invalid pagesize: %s", pagesize); return NULL; } bool valid = is_huge_pagesize_valid(newpagesize); if (!valid) { free(newpagesize); return NULL; } } else { newpagesize = get_default_huge_page_size(); if (newpagesize == NULL) { ERROR("Failed to get system hugepage size"); return NULL; } } is_hugelimit_valid(newpagesize, limit); return newpagesize; } /* free sysinfo */ void free_sysinfo(sysinfo_t *sysinfo) { if (sysinfo == NULL) { return; } free(sysinfo->cpusetinfo.cpus); sysinfo->cpusetinfo.cpus = NULL; free(sysinfo->cpusetinfo.mems); sysinfo->cpusetinfo.mems = NULL; free(sysinfo); } /* get sys info */ sysinfo_t *get_sys_info(bool quiet) { struct layer **layers = NULL; sysinfo_t *sysinfo = NULL; bool ret = true; sysinfo = util_common_calloc_s(sizeof(sysinfo_t)); if (sysinfo == NULL) { ERROR("Out of memory"); return NULL; } layers = cgroup_layers_find(); if (layers == NULL) { ERROR("Failed to parse cgroup information"); ret = false; goto out; } check_cgroup_mem(layers, quiet, &sysinfo->cgmeminfo); check_cgroup_cpu(layers, quiet, &sysinfo->cgcpuinfo); check_cgroup_hugetlb(layers, quiet, &sysinfo->hugetlbinfo); check_cgroup_blkio_info(layers, quiet, &sysinfo->blkioinfo); check_cgroup_cpuset_info(layers, quiet, &sysinfo->cpusetinfo); check_cgroup_pids(quiet, &sysinfo->pidsinfo); check_cgroup_files(quiet, &sysinfo->filesinfo); out: free_layer(layers); if (!ret) { free_sysinfo(sysinfo); sysinfo = NULL; } return sysinfo; } /* free mount info */ void free_mount_info(mountinfo_t *info) { if (info == NULL) { return; } free(info->root); info->root = NULL; free(info->mountpoint); info->mountpoint = NULL; free(info->opts); info->opts = NULL; free(info->optional); info->optional = NULL; free(info->fstype); info->fstype = NULL; free(info->source); info->source = NULL; free(info->vfsopts); info->vfsopts = NULL; free(info); } mountinfo_t *get_mount_info(const char *pline) { size_t length; int ret = 0; mountinfo_t *info = NULL; char **list = NULL; info = util_common_calloc_s(sizeof(mountinfo_t)); if (info == NULL) { ERROR("Out of memory"); return NULL; } list = util_string_split(pline, ' '); if (list == NULL) { ERROR("Out of memory"); ret = -1; goto free_out; } length = util_array_len((const char **)list); if (length < 8) { ERROR("Invalid mountinfo '%s'", pline); ret = -1; goto free_out; } info->mountpoint = util_strdup_s(list[4]); if (strcmp(list[6], "-")) { info->optional = util_strdup_s(list[6]); } free_out: util_free_array(list); if (ret != 0) { free_mount_info(info); info = NULL; } return info; } /* free mounts info */ void free_mounts_info(mountinfo_t **minfos) { mountinfo_t **it = NULL; if (minfos == NULL) { return; } for (it = minfos; it && *it; it++) { free_mount_info(*it); *it = NULL; } free(minfos); } /* find mount info */ mountinfo_t *find_mount_info(mountinfo_t **minfos, const char *dir) { mountinfo_t **it = NULL; for (it = minfos; it && *it; it++) { if ((*it)->mountpoint && !strcmp((*it)->mountpoint, dir)) { return *it; } } return NULL; } /* getmountsinfo */ mountinfo_t **getmountsinfo(void) { mountinfo_t **minfos = NULL; int ret = 0; FILE *fp = NULL; size_t length; char *pline = NULL; fp = util_fopen("/proc/self/mountinfo", "r"); if (fp == NULL) { ERROR("Failed to open \"/proc/self/mountinfo\"\n"); return NULL; } while (getline(&pline, &length, fp) != -1) { int index; mountinfo_t *info; info = get_mount_info(pline); if (info == NULL) { ret = -1; goto free_out; } index = add_null_to_list((void ***)&minfos); if (index < 0) { free_mount_info(info); ret = -1; goto free_out; } minfos[index] = info; } free_out: fclose(fp); free(pline); if (ret != 0) { free_mounts_info(minfos); minfos = NULL; } return minfos; }