| /* |
| * Copyright (C) 2000-2013, Parallels, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <sys/wait.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <arpa/inet.h> |
| |
| #include <getopt.h> |
| #include <fnmatch.h> |
| #include <sys/ioctl.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <math.h> |
| #include <linux/vzcalluser.h> |
| |
| #include "vzlist.h" |
| #include "vzconfig.h" |
| #include "fs.h" |
| #include "res.h" |
| #include "logger.h" |
| #include "util.h" |
| #include "types.h" |
| #include "image.h" |
| #include "vzfeatures.h" |
| #include "io.h" |
| |
| static struct Cveinfo *veinfo = NULL; |
| static int n_veinfo = 0; |
| |
| static char g_buf[4096] = ""; |
| static char *p_buf = g_buf; |
| static char *e_buf = g_buf + sizeof(g_buf) - 1; |
| static char *host_pattern = NULL; |
| static char *name_pattern = NULL; |
| static char *desc_pattern = NULL; |
| static char *dumpdir = NULL; |
| static int vzctlfd; |
| static struct Cfield_order *g_field_order = NULL; |
| static int is_last_field = 1; |
| static char *default_field_order = "ctid,numproc,status,ip,hostname"; |
| static char *default_nm_field_order = "smart_name,numproc,status,ip,hostname"; |
| static int g_sort_field = 0; |
| static int *g_ve_list = NULL; |
| static int n_ve_list = 0; |
| static int sort_rev = 0; |
| static int show_hdr = 1; |
| static int trim = 1; |
| static int all_ve = 0; |
| static int only_stopped_ve = 0; |
| static long __clk_tck = -1; |
| static int fmt_json = 0; |
| |
| char logbuf[32]; |
| static int get_run_ve_proc(int); |
| #if HAVE_VZLIST_IOCTL |
| static int get_run_ve_ioctl(int); |
| static inline int get_run_ve(int update) |
| { |
| int ret; |
| ret = get_run_ve_ioctl(update); |
| if (ret) |
| ret = get_run_ve_proc(update); |
| return ret; |
| } |
| #else |
| #define get_run_ve get_run_ve_proc |
| #endif |
| |
| /* Print functions */ |
| static void print_json_str(const char *s) |
| { |
| if (s) |
| printf("\"%s\"", s); |
| else |
| printf("null"); |
| } |
| |
| #define PRINT_STR_FIELD_FNAME(funcname, fieldname, length) \ |
| static void print_ ## funcname(struct Cveinfo *p, int index) \ |
| { \ |
| int r; \ |
| char *str = "-"; \ |
| \ |
| if (fmt_json) \ |
| return print_json_str(p->fieldname); \ |
| \ |
| if (p->fieldname != NULL) \ |
| str = p->fieldname; \ |
| r = snprintf(p_buf, e_buf - p_buf, \ |
| "%" #length "s", str); \ |
| if (!is_last_field) \ |
| r = abs(length); \ |
| p_buf += r; \ |
| } |
| |
| #define PRINT_STR_FIELD(name, length) \ |
| PRINT_STR_FIELD_FNAME(name, name, length) |
| |
| PRINT_STR_FIELD(private, -32) |
| PRINT_STR_FIELD(root, -32) |
| PRINT_STR_FIELD(mount_opts, -16) |
| PRINT_STR_FIELD(origin_sample, -32) |
| PRINT_STR_FIELD(hostname, -32) |
| PRINT_STR_FIELD(name, -32) |
| PRINT_STR_FIELD(description, -32) |
| PRINT_STR_FIELD(ostemplate, -32) |
| PRINT_STR_FIELD_FNAME(name_short, name, 10) |
| |
| static void print_json_list(const char *list) |
| { |
| static const char spc[] = " \t"; |
| int first_item = 1; |
| const char *item; |
| const char *endp; |
| |
| if (!list) { |
| printf("[]"); |
| return; |
| } |
| |
| printf("["); |
| item = list; |
| endp = list + strlen(list); |
| while (item < endp) { |
| int toklen; |
| |
| item += strspn(item, spc); |
| if (item >= endp) |
| break; |
| |
| toklen = strcspn(item, spc); |
| printf("%s\"%.*s\"", first_item ? "" : ", ", toklen, item); |
| first_item = 0; |
| item += toklen; |
| } |
| printf("]"); |
| } |
| |
| static void print_strlist(char *list) |
| { |
| int r; |
| char *str = "-"; |
| char *ch; |
| |
| if (fmt_json) |
| return print_json_list(list); |
| |
| if (list != NULL) |
| str = list; |
| if (!is_last_field) |
| { |
| /* Fixme: dont destroy original string */ |
| if ((ch = strchr(str, ' ')) != NULL) |
| *ch = 0; |
| } |
| r = snprintf(p_buf, e_buf - p_buf, "%-15s", str); |
| if (!is_last_field) |
| r = 15; |
| p_buf += r; |
| } |
| |
| static void print_ip(struct Cveinfo *p, int index) |
| { |
| print_strlist(p->ip); |
| } |
| |
| static void print_nameserver(struct Cveinfo *p, int index) |
| { |
| print_strlist(p->nameserver); |
| } |
| |
| static void print_searchdomain(struct Cveinfo *p, int index) |
| { |
| print_strlist(p->searchdomain); |
| } |
| |
| static void print_veid(struct Cveinfo *p, int index) |
| { |
| if (fmt_json) |
| printf("%d", p->veid); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%10d", p->veid); |
| } |
| |
| static void print_smart_name(struct Cveinfo *p, int index) |
| { |
| if (p->name != NULL) |
| print_name_short(p, index); |
| else |
| print_veid(p, index); |
| } |
| |
| static void print_status(struct Cveinfo *p, int index) |
| { |
| if (fmt_json) |
| print_json_str(ve_status[p->status]); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%-9s", ve_status[p->status]); |
| } |
| |
| static void print_laverage(struct Cveinfo *p, int index) |
| { |
| if (p->cpustat == NULL) { |
| if (fmt_json) |
| printf("null"); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%14s", "-"); |
| } |
| else { |
| if (fmt_json) |
| printf("[%1.2f, %1.2f, %1.2f]", |
| p->cpustat->la[0], |
| p->cpustat->la[1], |
| p->cpustat->la[2]); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%1.2f/%1.2f/%1.2f", |
| p->cpustat->la[0], |
| p->cpustat->la[1], |
| p->cpustat->la[2]); |
| } |
| } |
| |
| static void print_uptime(struct Cveinfo *p, int index) |
| { |
| |
| if (fmt_json) { |
| printf("%.3f", p->cpustat ? p->cpustat->uptime : 0.); |
| return; |
| } |
| |
| if (p->cpustat == NULL) |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%15s", "-"); |
| else |
| { |
| unsigned int days, hours, min, secs; |
| |
| days = (unsigned int)(p->cpustat->uptime / (60 * 60 * 24)); |
| min = (unsigned int)(p->cpustat->uptime / 60); |
| hours = min / 60; |
| hours = hours % 24; |
| min = min % 60; |
| secs = (unsigned int)(p->cpustat->uptime - |
| (60ull * min + 60ull * 60 * hours + |
| 60ull * 60 * 24 * days)); |
| |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%.3dd%.2dh:%.2dm:%.2ds", |
| days, hours, min, secs); |
| } |
| } |
| |
| #define PRINT_CPU(name) \ |
| static void print_cpu ## name(struct Cveinfo *p, int index) \ |
| { \ |
| if (p->cpu == NULL) { \ |
| if (fmt_json) \ |
| printf("null"); \ |
| else \ |
| p_buf += snprintf(p_buf, e_buf - p_buf, \ |
| "%7s", "-"); \ |
| } \ |
| else { \ |
| if (fmt_json) \ |
| printf("%lu", p->cpu->name[index]); \ |
| else \ |
| p_buf += snprintf(p_buf, e_buf - p_buf, \ |
| "%7lu", p->cpu->name[index]); \ |
| } \ |
| } |
| |
| PRINT_CPU(limit) |
| PRINT_CPU(units) |
| |
| #define PRINT_IO(name, fmt, min) \ |
| static void print_io ## name(struct Cveinfo *p, int index) \ |
| { \ |
| int i = p->io.io ## name; \ |
| \ |
| if (fmt_json) { \ |
| printf("%d", (i < 0) ? 0 : i); \ |
| return; \ |
| } \ |
| \ |
| if (i < min) \ |
| p_buf += snprintf(p_buf, e_buf - p_buf, \ |
| fmt "s", "-"); \ |
| else \ |
| p_buf += snprintf(p_buf, e_buf - p_buf, \ |
| fmt "d", i); \ |
| } |
| |
| PRINT_IO(prio, "%3", 0) |
| PRINT_IO(limit, "%10", 1) |
| PRINT_IO(pslimit, "%4", 1) |
| #undef PRINT_IO |
| |
| static void print_bool(const char *fmt, int val) |
| { |
| if (fmt_json) |
| printf(val ? "true" : "false"); |
| else |
| p_buf += snprintf(p_buf, e_buf-p_buf, |
| fmt, val ? "yes" : "no"); |
| } |
| |
| static void print_onboot(struct Cveinfo *p, int index) |
| { |
| /* ONBOOT value is NONE (0), YES (1) or NO (2) */ |
| if (p->onboot) |
| return print_bool("%6s", p->onboot == YES); |
| |
| if (fmt_json) |
| printf("null"); |
| else |
| p_buf += snprintf(p_buf, e_buf-p_buf, "%6s", "-"); |
| } |
| |
| static void print_bootorder(struct Cveinfo *p, int index) |
| { |
| if (p->bootorder == NULL) { |
| if (fmt_json) |
| printf("null"); |
| else |
| p_buf += snprintf(p_buf, e_buf-p_buf, |
| "%10s", "-"); |
| } |
| else { |
| if (fmt_json) |
| printf("%lu", p->bootorder[index]); |
| else |
| p_buf += snprintf(p_buf, e_buf-p_buf, |
| "%10lu", p->bootorder[index]); |
| } |
| } |
| |
| static void print_cpunum(struct Cveinfo *p, int index) |
| { |
| if (fmt_json) { |
| printf("%d", p->cpunum < 0 ? 0 : p->cpunum); |
| return; |
| } |
| |
| if (p->cpunum <= 0) |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%5s", "-"); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%5d", p->cpunum); |
| } |
| |
| static const char *layout2str(int layout) |
| { |
| switch (layout) |
| { |
| case VE_LAYOUT_SIMFS: |
| return "simfs"; |
| case VE_LAYOUT_PLOOP: |
| return "ploop"; |
| } |
| return "???"; |
| } |
| |
| static void print_layout(struct Cveinfo *p, int index) |
| { |
| if (fmt_json) |
| print_json_str(layout2str(p->layout)); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| "%-6s", layout2str(p->layout)); |
| } |
| |
| static void print_features(struct Cveinfo *p, int index) |
| { |
| int r; |
| char str[64]="-"; |
| |
| if (fmt_json) |
| return print_json_features(p->features_mask, |
| p->features_known); |
| |
| features_mask2str(p->features_mask, p->features_known, |
| ",", str, sizeof(str)); |
| r = snprintf(p_buf, e_buf - p_buf, "%-15s", str); |
| if (!is_last_field) |
| r = 15; |
| p_buf += r; |
| } |
| |
| static void print_ubc(struct Cveinfo *p, size_t res_off, int index) |
| { |
| int running = p->status == VE_RUNNING; |
| unsigned long *res = p->ubc ? |
| (unsigned long *)(p->ubc) + res_off : NULL; |
| |
| if (fmt_json) { |
| if (res) { |
| printf("{\n" |
| " \"held\": %lu,\n" |
| " \"maxheld\": %lu,\n" |
| " \"barrier\": %lu,\n" |
| " \"limit\": %lu,\n" |
| " \"failcnt\": %lu\n" |
| " }", |
| running ? res[0] : 0, |
| running ? res[1] : 0, |
| res[2], res[3], |
| running ? res[4] : 0); |
| } else |
| printf("null"); |
| return; |
| } |
| |
| if (!res || (!running && (index == 0 || index == 1 || index == 4))) |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%10s", "-"); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%10lu", |
| res[index]); |
| } |
| |
| #ifndef offsetof |
| # define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) |
| #endif |
| |
| #define PRINT_UBC(name) \ |
| static void print_ubc_ ## name(struct Cveinfo *p, int index) \ |
| { \ |
| print_ubc(p, offsetof(struct Cubc, name) / \ |
| sizeof(unsigned long), index); \ |
| } |
| |
| FOR_ALL_UBC(PRINT_UBC) |
| |
| static void print_vswap(struct Cveinfo *p, int index) |
| { |
| /* Same conditions as used in is_vswap_config() */ |
| int vswap = (p->ubc != NULL) && |
| (p->ubc->physpages[3] != LONG_MAX) && |
| (p->ubc->physpages[3] != INT_MAX); |
| |
| print_bool("%5s", vswap); |
| } |
| |
| static void print_disabled(struct Cveinfo *p, int index) |
| { |
| print_bool("%6s", p->disabled == YES); |
| } |
| |
| static void print_dq(struct Cveinfo *p, size_t res_off, int index) |
| { |
| unsigned long *res = p->quota ? |
| (unsigned long *)(p->quota) + res_off : NULL; |
| |
| if (fmt_json) { |
| if (res) { |
| printf("{\n" |
| " \"usage\": %lu,\n" |
| " \"softlimit\": %lu,\n" |
| " \"hardlimit\": %lu\n" |
| " }", |
| res[0], res[1], res[2]); |
| } else |
| printf("null"); |
| return; |
| } |
| |
| if (!res || (index == 0 && res[index] == 0)) |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%10s", "-"); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%10lu", |
| res[index]); |
| } |
| |
| #define PRINT_DQ(name) \ |
| static void print_ ## name(struct Cveinfo *p, int index) \ |
| { \ |
| print_dq(p, offsetof(struct Cquota, name) / \ |
| sizeof(unsigned long), index); \ |
| } |
| |
| PRINT_DQ(diskspace) |
| PRINT_DQ(diskinodes) |
| |
| static void print_vm_overcommit(struct Cveinfo *p, int index) |
| { |
| if (fmt_json) { |
| printf("%g", p->vm_overcommit); |
| return; |
| } |
| |
| if (p->vm_overcommit == 0) |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%6s", "-"); |
| else |
| p_buf += snprintf(p_buf, e_buf - p_buf, "%6g", |
| p->vm_overcommit); |
| } |
| |
| /* Sort functions */ |
| |
| static inline int check_empty_param(const void *val1, const void *val2) |
| { |
| if (val1 == NULL && val2 == NULL) |
| return 0; |
| else if (val1 == NULL) |
| return -1; |
| else if (val2 == NULL) |
| return 1; |
| return 2; |
| } |
| |
| static int none_sort_fn(const void *val1, const void *val2) |
| { |
| return 0; |
| } |
| |
| static int laverage_sort_fn(const void *val1, const void *val2) |
| { |
| const struct Ccpustat *st1 = ((const struct Cveinfo *)val1)->cpustat; |
| const struct Ccpustat *st2 = ((const struct Cveinfo *)val2)->cpustat; |
| int res; |
| |
| if ((res = check_empty_param(st1, st2)) != 2) |
| return res; |
| res = (st1->la[0] - st2->la[0]) * 100; |
| if (res != 0) |
| return res; |
| res = (st1->la[1] - st2->la[1]) * 100; |
| if (res != 0) |
| return res; |
| return (st1->la[2] - st2->la[2]) * 100; |
| } |
| |
| static int uptime_sort_fn(const void *val1, const void *val2) |
| { |
| struct Ccpustat *st1 = ((const struct Cveinfo *)val1)->cpustat; |
| struct Ccpustat *st2 = ((const struct Cveinfo *)val2)->cpustat; |
| int res; |
| |
| if ((res = check_empty_param(st1, st2)) != 2) |
| return res; |
| return (st2->uptime - st1->uptime); |
| } |
| |
| static int id_sort_fn(const void *val1, const void *val2) |
| { |
| int ret; |
| ret = (((const struct Cveinfo*)val1)->veid > |
| ((const struct Cveinfo*)val2)->veid); |
| return ret; |
| } |
| |
| static int status_sort_fn(const void *val1, const void *val2) |
| { |
| int res; |
| res = ((const struct Cveinfo*)val1)->status - |
| ((const struct Cveinfo*)val2)->status; |
| if (!res) |
| res = id_sort_fn(val1, val2); |
| return res; |
| } |
| |
| static int bootorder_sort_fn(const void *val1, const void *val2) |
| { |
| int ret; |
| unsigned long *r1 = ((const struct Cveinfo*)val1)->bootorder; |
| unsigned long *r2 = ((const struct Cveinfo*)val2)->bootorder; |
| |
| ret = check_empty_param(r1, r2); |
| switch (ret) { |
| case 0: /* both NULL */ |
| return !id_sort_fn(val1, val2); |
| case 2: /* both not NULL */ |
| break; |
| default: /* one is NULL, other is not */ |
| return ret; |
| } |
| |
| if (*r1 == *r2) |
| return !id_sort_fn(val1, val2); |
| |
| return (*r1 > *r2); |
| } |
| |
| #define IO_SORT_FN(name) \ |
| static int io ## name ## _sort_fn(const void *val1, const void *val2) \ |
| { \ |
| return ((const struct Cveinfo *)val1)->io.io ## name > \ |
| ((const struct Cveinfo *)val2)->io.io ## name; \ |
| } |
| |
| IO_SORT_FN(prio) |
| IO_SORT_FN(limit) |
| IO_SORT_FN(pslimit) |
| #undef IO_SORT_FN |
| |
| static int cpunum_sort_fn(const void *val1, const void *val2) |
| { |
| return ((const struct Cveinfo *)val1)->cpunum > |
| ((const struct Cveinfo *)val2)->cpunum; |
| } |
| |
| static int layout_sort_fn(const void *val1, const void *val2) |
| { |
| return ((const struct Cveinfo *)val1)->layout > |
| ((const struct Cveinfo *)val2)->layout; |
| } |
| |
| #define SORT_STR_FN(name) \ |
| static int name ## _sort_fn(const void *val1, const void *val2) \ |
| { \ |
| const char *h1 = ((const struct Cveinfo*)val1)->name; \ |
| const char *h2 = ((const struct Cveinfo*)val2)->name; \ |
| int ret; \ |
| if ((ret = check_empty_param(h1, h2)) == 2) \ |
| ret = strcmp(h1, h2); \ |
| return ret; \ |
| } |
| |
| SORT_STR_FN(private) |
| SORT_STR_FN(root) |
| SORT_STR_FN(mount_opts) |
| SORT_STR_FN(origin_sample) |
| SORT_STR_FN(hostname) |
| SORT_STR_FN(name) |
| SORT_STR_FN(description) |
| SORT_STR_FN(ostemplate) |
| SORT_STR_FN(ip) |
| SORT_STR_FN(nameserver) |
| SORT_STR_FN(searchdomain) |
| |
| #define SORT_UL_RES(fn, res, name, index) \ |
| static int fn(const void *val1, const void *val2) \ |
| { \ |
| const struct C ## res *r1 = ((const struct Cveinfo *)val1)->res;\ |
| const struct C ## res *r2 = ((const struct Cveinfo *)val2)->res;\ |
| int ret; \ |
| if ((ret = check_empty_param(r1, r2)) == 2) \ |
| ret = r1->name[index] > r2->name[index]; \ |
| return ret; \ |
| } |
| |
| #define SORT_UBC(res) \ |
| SORT_UL_RES(res ## _h_sort_fn, ubc, res, 0) \ |
| SORT_UL_RES(res ## _m_sort_fn, ubc, res, 1) \ |
| SORT_UL_RES(res ## _l_sort_fn, ubc, res, 2) \ |
| SORT_UL_RES(res ## _b_sort_fn, ubc, res, 3) \ |
| SORT_UL_RES(res ## _f_sort_fn, ubc, res, 4) |
| |
| FOR_ALL_UBC(SORT_UBC) |
| |
| #define SORT_DQ(res) \ |
| SORT_UL_RES(res ## _u_sort_fn, quota, res, 0) \ |
| SORT_UL_RES(res ## _s_sort_fn, quota, res, 1) \ |
| SORT_UL_RES(res ## _h_sort_fn, quota, res, 2) |
| |
| SORT_DQ(diskspace) |
| SORT_DQ(diskinodes) |
| |
| SORT_UL_RES(cpulimit_sort_fn, cpu, limit, 0) |
| SORT_UL_RES(cpuunits_sort_fn, cpu, units, 0) |
| |
| #define UBC_FIELD(name, header) \ |
| {#name, #header, "%10s", 0, RES_UBC, print_ubc_ ## name, name ## _h_sort_fn}, \ |
| {#name ".m", #header ".M", "%10s", 1, RES_UBC, print_ubc_ ## name, name ## _m_sort_fn}, \ |
| {#name ".b", #header ".B", "%10s", 2, RES_UBC, print_ubc_ ## name, name ## _b_sort_fn}, \ |
| {#name ".l", #header ".L", "%10s", 3, RES_UBC, print_ubc_ ## name, name ## _l_sort_fn}, \ |
| {#name ".f", #header ".F", "%10s", 4, RES_UBC, print_ubc_ ## name, name ## _f_sort_fn} |
| |
| static struct Cfield field_names[] = |
| { |
| /* ctid should have index 0 */ |
| {"ctid", "CTID", "%10s", 0, RES_NONE, print_veid, id_sort_fn}, |
| /* veid is for backward compatibility -- will be removed later */ |
| {"veid", "CTID", "%10s", 0, RES_NONE, print_veid, id_sort_fn}, |
| /* vpsid is for backward compatibility -- will be removed later */ |
| {"vpsid", "CTID", "%10s", 0, RES_NONE, print_veid, id_sort_fn}, |
| |
| {"private", "PRIVATE", "%-32s", 0, RES_NONE, print_private, private_sort_fn}, |
| {"root", "ROOT", "%-32s", 0, RES_NONE, print_root, root_sort_fn}, |
| {"mount_opts", "MOUNT_OPTS", "%-16s", 0, RES_NONE, print_mount_opts, mount_opts_sort_fn}, |
| {"origin_sample", "ORIGIN_SAMPLE", "%32s", 0, RES_NONE, print_origin_sample, origin_sample_sort_fn}, |
| {"hostname", "HOSTNAME", "%-32s", 0, RES_HOSTNAME, print_hostname, hostname_sort_fn}, |
| {"name", "NAME", "%-32s", 0, RES_NONE, print_name, name_sort_fn}, |
| {"smart_name", "SMARTNAME", "%10s", 0, RES_NONE, print_smart_name, name_sort_fn}, |
| {"description", "DESCRIPTION", "%-32s", 0, RES_NONE, print_description, description_sort_fn }, |
| {"ostemplate", "OSTEMPLATE", "%-32s", 0, RES_NONE, print_ostemplate, ostemplate_sort_fn }, |
| {"ip", "IP_ADDR", "%-15s", 0, RES_IP, print_ip, ip_sort_fn}, |
| {"nameserver", "NAMESERVER", "%-15s", 0, RES_NONE, print_nameserver, nameserver_sort_fn}, |
| {"searchdomain", "SEARCHDOMAIN", "%-15s", 0, RES_NONE, print_searchdomain, searchdomain_sort_fn}, |
| {"status", "STATUS", "%-9s", 0, RES_STATUS, print_status, status_sort_fn}, |
| /* UBC */ |
| UBC_FIELD(kmemsize, KMEMSIZE), |
| UBC_FIELD(lockedpages, LOCKEDP), |
| UBC_FIELD(privvmpages, PRIVVMP), |
| UBC_FIELD(shmpages, SHMP), |
| UBC_FIELD(numproc, NPROC), |
| UBC_FIELD(physpages, PHYSP), |
| UBC_FIELD(vmguarpages, VMGUARP), |
| UBC_FIELD(oomguarpages, OOMGUARP), |
| UBC_FIELD(numtcpsock, NTCPSOCK), |
| UBC_FIELD(numflock, NFLOCK), |
| UBC_FIELD(numpty, NPTY), |
| UBC_FIELD(numsiginfo, NSIGINFO), |
| UBC_FIELD(tcpsndbuf, TCPSNDB), |
| UBC_FIELD(tcprcvbuf, TCPRCVB), |
| UBC_FIELD(othersockbuf, OTHSOCKB), |
| UBC_FIELD(dgramrcvbuf, DGRAMRB), |
| UBC_FIELD(numothersock, NOTHSOCK), |
| UBC_FIELD(dcachesize, DCACHESZ), |
| UBC_FIELD(numfile, NFILE), |
| UBC_FIELD(numiptent, NIPTENT), |
| UBC_FIELD(swappages, SWAPP), |
| |
| {"diskspace", "DSPACE", "%10s", 0, RES_QUOTA, print_diskspace, diskspace_u_sort_fn}, |
| {"diskspace.s", "DSPACE.S", "%10s", 1, RES_QUOTA, print_diskspace, diskspace_s_sort_fn}, |
| {"diskspace.h", "DSPACE.H", "%10s", 2, RES_QUOTA, print_diskspace, diskspace_h_sort_fn}, |
| |
| {"diskinodes", "DINODES", "%10s", 0, RES_QUOTA, print_diskinodes, diskinodes_u_sort_fn}, |
| {"diskinodes.s", "DINODES.S", "%10s", 1, RES_QUOTA, print_diskinodes, diskinodes_s_sort_fn}, |
| {"diskinodes.h", "DINODES.H", "%10s", 2, RES_QUOTA, print_diskinodes, diskinodes_h_sort_fn}, |
| |
| {"laverage", "LAVERAGE", "%14s", 0, RES_CPUSTAT, print_laverage, laverage_sort_fn}, |
| {"uptime", "UPTIME", "%15s", 0, RES_CPUSTAT, print_uptime, uptime_sort_fn}, |
| |
| {"cpulimit", "CPULIM", "%7s", 0, RES_CPU, print_cpulimit, cpulimit_sort_fn}, |
| {"cpuunits", "CPUUNI", "%7s", 0, RES_CPU, print_cpuunits, cpuunits_sort_fn}, |
| {"cpus", "CPUS", "%5s", 0, RES_CPUNUM, print_cpunum, cpunum_sort_fn}, |
| |
| {"ioprio", "IOP", "%3s", 0, RES_NONE, print_ioprio, ioprio_sort_fn}, |
| {"iolimit", "IOLIMIT", "%10s", 0, RES_IO, print_iolimit, iolimit_sort_fn}, |
| {"iopslimit", "IOPS", "%4s", 0, RES_IO, print_iopslimit, iopslimit_sort_fn}, |
| |
| {"onboot", "ONBOOT", "%6s", 0, RES_NONE, print_onboot, none_sort_fn}, |
| {"bootorder", "BOOTORDER", "%10s", 0, RES_NONE, |
| print_bootorder, bootorder_sort_fn}, |
| {"layout", "LAYOUT", "%6s", 0, RES_NONE, print_layout, layout_sort_fn}, |
| {"features", "FEATURES", "%-15s", 0, RES_NONE, print_features, none_sort_fn}, |
| {"vswap", "VSWAP", "%5s", 0, RES_NONE, print_vswap, none_sort_fn}, |
| {"disabled", "DISABL", "%6s", 0, RES_NONE, print_disabled, none_sort_fn}, |
| {"vm_overcommit", "VM_OVC", "%6s", 0, RES_NONE, |
| print_vm_overcommit, none_sort_fn}, |
| }; |
| |
| static void *x_malloc(int size) |
| { |
| void *p; |
| if ((p = malloc(size)) == NULL) { |
| fprintf(stderr, "Error: unable to allocate %d bytes\n", size); |
| exit(1); |
| } |
| return p; |
| } |
| |
| static void *x_realloc(void *ptr, int size) |
| { |
| void *tmp; |
| |
| if ((tmp = realloc(ptr, size)) == NULL) { |
| fprintf(stderr, "Error: unable to allocate %d bytes\n", size); |
| exit(1); |
| } |
| return tmp; |
| } |
| |
| static void usage() |
| { |
| printf( |
| "Usage: vzlist [-a | -S] [-n] [-H] [-o field[,field...] | -1] [-s [-]field]\n" |
| " [-h pattern] [-N pattern] [-d pattern] [CTID [CTID ...]]\n" |
| " vzlist -L | --list\n" |
| "\n" |
| "Options:\n" |
| " -a, --all list all containers\n" |
| " -S, --stopped list stopped containers\n" |
| " -n, --name display containers' names\n" |
| " -H, --no-header suppress columns header\n" |
| " -t, --no-trim do not trim long values\n" |
| " -j, --json output in JSON format\n" |
| " -o, --output output only specified fields\n" |
| " -1 synonym for -H -octid\n" |
| " -s, --sort sort by the specified field\n" |
| " ('-field' to reverse sort order)\n" |
| " -h, --hostname filter CTs by hostname pattern\n" |
| " -N, --name_filter filter CTs by name pattern\n" |
| " -d, --description filter CTs by description pattern\n" |
| " -L, --list get possible field names\n" |
| ); |
| } |
| |
| static int id_search_fn(const void* val1, const void* val2) |
| { |
| return (*(const int *)val1 - ((const struct Cveinfo*)val2)->veid); |
| } |
| |
| static int veid_search_fn(const void* val1, const void* val2) |
| { |
| return (*(const int *)val1 - *(const int *)val2); |
| } |
| |
| static char* trim_eol_space(char *sp, char *ep) |
| { |
| /* if (ep == NULL) |
| ep = sp + strlen(sp); */ |
| |
| ep--; |
| while (isspace(*ep) && ep >= sp) *ep-- = '\0'; |
| |
| return sp; |
| } |
| |
| static void print_hdr() |
| { |
| struct Cfield_order *p; |
| int f; |
| |
| for (p = g_field_order; p != NULL; p = p->next) { |
| f = p->order; |
| p_buf += snprintf(p_buf, e_buf - p_buf, |
| field_names[f].hdr_fmt, field_names[f].hdr); |
| if (p_buf >= e_buf) |
| break; |
| if (p->next != NULL) |
| *p_buf++ = ' '; |
| } |
| printf("%s\n", trim_eol_space(g_buf, p_buf)); |
| g_buf[0] = 0; |
| p_buf = g_buf; |
| } |
| |
| /* |
| 1 - match |
| 0 - do not match |
| */ |
| static inline int check_pattern(char *str, char *pat) |
| { |
| if (pat == NULL) |
| return 1; |
| if (str == NULL) |
| return 0; |
| return !fnmatch(pat, str, 0); |
| } |
| |
| static void filter_by_hostname() |
| { |
| int i; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if (!check_pattern(veinfo[i].hostname, host_pattern)) |
| veinfo[i].hide = 1; |
| } |
| } |
| |
| static void filter_by_name() |
| { |
| int i; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if (!check_pattern(veinfo[i].name, name_pattern)) |
| veinfo[i].hide = 1; |
| } |
| } |
| |
| static void filter_by_description() |
| { |
| int i; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if (!check_pattern(veinfo[i].description, desc_pattern)) |
| veinfo[i].hide = 1; |
| } |
| } |
| |
| static void print_one_ve(struct Cveinfo *ve) |
| { |
| struct Cfield_order *p; |
| int f; |
| |
| if (trim) |
| is_last_field = 0; |
| |
| for (p = g_field_order; p != NULL; p = p->next) { |
| f = p->order; |
| if (p->next == NULL) |
| is_last_field = 1; |
| field_names[f].print_fn(ve, field_names[f].index); |
| if (p_buf >= e_buf) |
| break; |
| if (p->next != NULL) |
| *p_buf++ = ' '; |
| } |
| |
| printf("%s\n", trim_eol_space(g_buf, p_buf)); |
| g_buf[0] = 0; |
| p_buf = g_buf; |
| } |
| |
| static void print_field_json(struct Cveinfo *ve, int fi) |
| { |
| static struct Cveinfo *prev_ve = NULL; |
| |
| printf("%s\n \"%s\": ", prev_ve == ve ? "," : "", |
| field_names[fi].name); |
| field_names[fi].print_fn(ve, field_names[fi].index); |
| prev_ve = ve; |
| } |
| |
| static void print_one_ve_json(struct Cveinfo *ve) |
| { |
| static int first_entry = 1; |
| |
| printf("%s\n {", first_entry ? "" : ","); |
| first_entry = 0; |
| if (g_field_order) { |
| struct Cfield_order *p; |
| for (p = g_field_order; p != NULL; p = p->next) |
| print_field_json(ve, p->order); |
| } else { |
| unsigned long i; |
| for (i = 0; i < ARRAY_SIZE(field_names); i++) |
| if (!field_names[i].index) |
| print_field_json(ve, i); |
| } |
| printf("\n }"); |
| } |
| static void print_ve() |
| { |
| int i, idx; |
| |
| /* If sort order != veid (already sorted by) */ |
| if (g_sort_field) { |
| qsort(veinfo, n_veinfo, sizeof(struct Cveinfo), |
| field_names[g_sort_field].sort_fn); |
| } |
| if (!(!show_hdr || fmt_json)) |
| print_hdr(); |
| if (fmt_json) |
| printf("["); |
| for (i = 0; i < n_veinfo; i++) { |
| if (sort_rev) |
| idx = n_veinfo - i - 1; |
| else |
| idx = i; |
| if (veinfo[idx].hide) |
| continue; |
| if (only_stopped_ve && veinfo[idx].status == VE_RUNNING) |
| continue; |
| if (fmt_json) |
| print_one_ve_json(&veinfo[idx]); |
| else |
| print_one_ve(&veinfo[idx]); |
| } |
| if (fmt_json) |
| printf("\n]\n"); |
| } |
| |
| static void add_elem(struct Cveinfo *ve) |
| { |
| veinfo = (struct Cveinfo *)x_realloc(veinfo, |
| sizeof(struct Cveinfo) * ++n_veinfo); |
| ve->cpunum = -1; |
| memcpy(&veinfo[n_veinfo - 1], ve, sizeof(struct Cveinfo)); |
| return; |
| } |
| |
| static struct Cveinfo *find_ve(int veid) |
| { |
| return (struct Cveinfo *) bsearch(&veid, veinfo, n_veinfo, |
| sizeof(struct Cveinfo), id_search_fn); |
| } |
| |
| static void update_ve(int veid, char *ip, int status) |
| { |
| struct Cveinfo *tmp, ve; |
| |
| tmp = find_ve(veid); |
| if (tmp == NULL) { |
| memset(&ve, 0, sizeof(struct Cveinfo)); |
| ve.veid = veid; |
| ve.status = status; |
| ve.ip = ip; |
| add_elem(&ve); |
| qsort(veinfo, n_veinfo, sizeof(struct Cveinfo), id_sort_fn); |
| return; |
| } else { |
| if (tmp->ip == NULL) |
| tmp->ip = ip; |
| else |
| free(ip); |
| tmp->status = status; |
| } |
| return; |
| } |
| |
| static void update_ubc(int veid, const struct Cubc *ubc) |
| { |
| struct Cveinfo *tmp; |
| |
| if ((tmp = find_ve(veid)) != NULL) { |
| if (tmp->status != VE_RUNNING) |
| return; |
| if (tmp->ubc == NULL) |
| tmp->ubc = x_malloc(sizeof(*ubc)); |
| memcpy(tmp->ubc, ubc, sizeof(*ubc)); |
| } |
| return ; |
| } |
| |
| static void update_quota(int veid, struct Cquota *quota) |
| { |
| struct Cveinfo *tmp; |
| |
| if ((tmp = find_ve(veid)) == NULL) |
| return; |
| tmp->quota = x_malloc(sizeof(*quota)); |
| memcpy(tmp->quota, quota, sizeof(*quota)); |
| return; |
| } |
| |
| static void update_cpu(int veid, unsigned long limit, unsigned long units) |
| { |
| struct Cveinfo *tmp; |
| struct Ccpu *cpu; |
| |
| if ((tmp = find_ve(veid)) == NULL) |
| return; |
| cpu = x_malloc(sizeof(*cpu)); |
| cpu->limit[0] = limit; |
| cpu->units[0] = units; |
| tmp->cpu = cpu; |
| return; |
| } |
| |
| #define MERGE_QUOTA(name, quota, dq) \ |
| do { \ |
| if (dq.name != NULL) { \ |
| quota->name[1] = dq.name[0]; \ |
| quota->name[2] = dq.name[1]; \ |
| } \ |
| } while(0); |
| |
| static void merge_conf(struct Cveinfo *ve, vps_res *res, vps_opt *opt) |
| { |
| if (ve->ubc == NULL) { |
| ve->ubc = x_malloc(sizeof(struct Cubc)); |
| memset(ve->ubc, 0, sizeof(struct Cubc)); |
| #define MERGE_UBC(name) \ |
| if (res != NULL && res->ub.name != NULL) { \ |
| ve->ubc->name[2] = res->ub.name[0]; \ |
| ve->ubc->name[3] = res->ub.name[1]; \ |
| } |
| |
| FOR_ALL_UBC(MERGE_UBC) |
| |
| #undef MERGE_UBC |
| } |
| |
| if (res->ub.vm_overcommit != NULL) |
| ve->vm_overcommit = *res->ub.vm_overcommit; |
| |
| if (ve->ip == NULL && !list_empty(&res->net.ip)) { |
| ve->ip = list2str(NULL, &res->net.ip); |
| } |
| if (ve->quota == NULL && ( |
| res->dq.diskspace != NULL || |
| res->dq.diskinodes != NULL)) |
| { |
| ve->quota = x_malloc(sizeof(struct Cquota)); |
| memset(ve->quota, 0, sizeof(struct Cquota)); |
| |
| if (res->dq.diskspace) |
| MERGE_QUOTA(diskspace, ve->quota, res->dq); |
| if (res->dq.diskinodes) |
| MERGE_QUOTA(diskinodes, ve->quota, res->dq); |
| |
| } |
| if (ve->cpu == NULL && |
| (res->cpu.units != NULL || res->cpu.limit != NULL)) |
| { |
| ve->cpu = x_malloc(sizeof(struct Ccpu)); |
| memset(ve->cpu, 0, sizeof(struct Ccpu)); |
| if (res->cpu.limit != NULL) |
| ve->cpu->limit[0] = *res->cpu.limit; |
| if (res->cpu.units != NULL) |
| ve->cpu->units[0] = *res->cpu.units; |
| } |
| if (res->misc.hostname != NULL) |
| ve->hostname = strdup(res->misc.hostname); |
| if (res->misc.description != NULL) |
| ve->description = strdup(res->misc.description); |
| if (res->tmpl.ostmpl != NULL) |
| ve->ostemplate = strdup(res->tmpl.ostmpl); |
| if (res->name.name != NULL) { |
| int veid_nm = get_veid_by_name(res->name.name); |
| if (veid_nm == ve->veid) |
| ve->name = strdup(res->name.name); |
| } |
| if (!list_empty(&res->misc.nameserver)) |
| ve->nameserver = list2str(NULL, &res->misc.nameserver); |
| if (!list_empty(&res->misc.searchdomain)) |
| ve->searchdomain = list2str(NULL, &res->misc.searchdomain); |
| |
| if (res->fs.root != NULL) |
| ve->root = strdup(res->fs.root); |
| if (res->fs.private != NULL) |
| ve->private = strdup(res->fs.private); |
| if (res->fs.mount_opts != NULL) |
| ve->mount_opts = strdup(res->fs.mount_opts); |
| ve->onboot = res->misc.onboot; |
| if (res->misc.bootorder != NULL) { |
| ve->bootorder = x_malloc(sizeof(*ve->bootorder)); |
| *ve->bootorder = *res->misc.bootorder; |
| } |
| |
| ve->io.ioprio = res->io.ioprio; |
| ve->io.iolimit = res->io.iolimit; |
| ve->io.iopslimit = res->io.iopslimit; |
| |
| if (ve->cpunum == -1 && res->cpu.vcpus != NULL) |
| ve->cpunum = *res->cpu.vcpus; |
| ve->features_mask = res->env.features_mask; |
| ve->features_known = res->env.features_known; |
| ve->disabled = opt->start_disabled; |
| if (opt->origin_sample != NULL) |
| ve->origin_sample = strdup(opt->origin_sample); |
| ve->layout = res->fs.layout; |
| } |
| |
| static int read_ves_param() |
| { |
| int i; |
| char buf[128]; |
| vps_param *param; |
| char *ve_root = NULL; |
| char *ve_private = NULL; |
| int veid; |
| |
| param = init_vps_param(); |
| /* Parse global config file */ |
| vps_parse_config(0, GLOBAL_CFG, param, NULL); |
| if (param->res.fs.root != NULL) |
| ve_root = strdup(param->res.fs.root_orig); |
| if (param->res.fs.private != NULL) |
| ve_private = strdup(param->res.fs.private_orig); |
| if (param->res.cpt.dumpdir != NULL) |
| dumpdir = strdup(param->res.cpt.dumpdir); |
| free_vps_param(param); |
| for (i = 0; i < n_veinfo; i++) { |
| veid = veinfo[i].veid; |
| param = init_vps_param(); |
| snprintf(buf, sizeof(buf), VPSCONFDIR "/%d.conf", veid); |
| vps_parse_config(veid, buf, param, NULL); |
| merge_conf(&veinfo[i], ¶m->res, ¶m->opt); |
| if (veinfo[i].root == NULL) |
| veinfo[i].root = subst_VEID(veinfo[i].veid, ve_root); |
| if (veinfo[i].private == NULL) |
| veinfo[i].private = subst_VEID(veinfo[i].veid, |
| ve_private); |
| free_vps_param(param); |
| } |
| free(ve_root); |
| free(ve_private); |
| |
| return 0; |
| } |
| |
| |
| static int check_veid_restr(int veid) |
| { |
| if (g_ve_list == NULL) |
| return 1; |
| return (bsearch(&veid, g_ve_list, n_ve_list, |
| sizeof(*g_ve_list), veid_search_fn) != NULL); |
| } |
| |
| #define UPDATE_UBC(param) \ |
| if (!strcmp(name, #param)) { \ |
| ubc.param[0] = held; \ |
| ubc.param[1] = maxheld; \ |
| ubc.param[2] = barrier; \ |
| ubc.param[3] = limit; \ |
| ubc.param[4] = failcnt; \ |
| } |
| |
| static int get_ub() |
| { |
| char buf[256]; |
| int veid, prev_veid; |
| unsigned long held, maxheld, barrier, limit, failcnt; |
| char name[32]; |
| FILE *fp; |
| char *s; |
| struct Cubc ubc = {}; |
| |
| if ((fp = fopen(PROC_BC_RES, "r")) == NULL) { |
| if ((fp = fopen(PROCUBC, "r")) == NULL) { |
| fprintf(stderr, "Unable to open %s: %s\n", |
| PROCUBC, strerror(errno)); |
| return 1; |
| } |
| } |
| |
| veid = 0; |
| while (!feof(fp)) { |
| if (fgets(buf, sizeof(buf), fp) == NULL) |
| break; |
| if ((s = strchr(buf, ':')) != NULL) { |
| prev_veid = veid; |
| if (sscanf(buf, "%d:", &veid) != 1) |
| continue; |
| if (prev_veid && check_veid_restr(prev_veid)) { |
| update_ubc(prev_veid, &ubc); |
| } |
| memset(&ubc, 0, sizeof(struct Cubc)); |
| s++; |
| } else { |
| s = buf; |
| } |
| if (sscanf(s, "%s %lu %lu %lu %lu %lu", |
| name, &held, &maxheld, &barrier, &limit, &failcnt) != 6) |
| { |
| continue; |
| } |
| |
| FOR_ALL_UBC(UPDATE_UBC) |
| } |
| if (veid && check_veid_restr(veid)) { |
| update_ubc(veid, &ubc); |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| static char *invert_ip(char *ips) |
| { |
| char *tmp, *p, *ep, *tp; |
| size_t len; |
| int rc; |
| unsigned int ip[4]; |
| int family; |
| char ip_str[INET6_ADDRSTRLEN]; |
| |
| if (ips == NULL) |
| return NULL; |
| len = strlen(ips); |
| tp = tmp = x_malloc(len + 2); |
| tmp[0] = 0; |
| p = ep = ips + len; |
| /* Iterate in reverse order */ |
| while (p-- > ips) { |
| /* Skip spaces */ |
| while (p > ips && isspace(*p)) {--p;} |
| ep = p; |
| /* find the string begin from */ |
| while (p > ips && !isspace(*(p - 1))) { --p;} |
| len = ep - p + 1; |
| if (len >= sizeof(ip_str)) |
| continue; |
| strncpy(ip_str, p, len); |
| ip_str[len] = 0; |
| if ((family = get_netaddr(ip_str, ip)) == -1) |
| continue; |
| if ((inet_ntop(family, ip, ip_str, sizeof(ip_str) - 1)) == NULL) |
| continue; |
| rc = sprintf(tp, "%s ", ip_str); |
| if (rc == -1) |
| break; |
| tp += rc; |
| } |
| return tmp; |
| } |
| |
| static int get_run_ve_proc(int update) |
| { |
| FILE *fp; |
| struct Cveinfo ve; |
| char buf[16384]; |
| char ips[16384]; |
| int res, veid, classid, nproc; |
| |
| if ((fp = fopen(PROCVEINFO, "r")) == NULL) { |
| fprintf(stderr, "Unable to open %s: %s\n", |
| PROCVEINFO, strerror(errno)); |
| return 1; |
| } |
| memset(&ve, 0, sizeof(struct Cveinfo)); |
| while (!feof(fp)) { |
| if (fgets(buf, sizeof(buf), fp) == NULL) |
| break; |
| res = sscanf(buf, "%d %d %d %[^\n]", |
| &veid, &classid, &nproc, ips); |
| if (res < 3 || !veid) |
| continue; |
| if (!check_veid_restr(veid)) |
| continue; |
| memset(&ve, 0, sizeof(struct Cveinfo)); |
| if (res == 4) { |
| ve.ip = invert_ip(ips); |
| |
| } |
| ve.veid = veid; |
| ve.status = VE_RUNNING; |
| if (update) |
| update_ve(veid, ve.ip, ve.status); |
| else |
| add_elem(&ve); |
| } |
| if (!update) |
| qsort(veinfo, n_veinfo, sizeof(struct Cveinfo), id_sort_fn); |
| fclose(fp); |
| return 0; |
| } |
| |
| #if HAVE_VZLIST_IOCTL |
| static inline int get_ve_ips(unsigned int id, char **str) |
| { |
| int ret = -1; |
| struct vzlist_veipv4ctl veip; |
| uint32_t *addr; |
| |
| veip.veid = id; |
| veip.num = 256; |
| addr = x_malloc(veip.num * sizeof(*veip.ip)); |
| for (;;) { |
| veip.ip = addr; |
| ret = ioctl(vzctlfd, VZCTL_GET_VEIPS, &veip); |
| if (ret < 0) |
| goto out; |
| else if (ret <= veip.num) |
| break; |
| veip.num = ret; |
| addr = x_realloc(addr, veip.num * sizeof(*veip.ip)); |
| } |
| if (ret > 0) { |
| char *buf, *cp; |
| int i; |
| |
| buf = x_malloc(ret * (16 * sizeof(char)) + 1); |
| cp = buf; |
| for (i = ret - 1; i >= 0; i--) |
| cp += sprintf(cp, "%d.%d.%d.%d ", NIPQUAD(addr[i])); |
| *cp = '\0'; |
| *str = buf; |
| } else |
| *str = strdup(""); |
| out: |
| free(addr); |
| return ret; |
| } |
| |
| static int get_run_ve_ioctl(int update) |
| { |
| int ret = -1; |
| struct vzlist_veidctl veid; |
| int nves; |
| void *buf = NULL; |
| int i; |
| |
| vzctlfd = open(VZCTLDEV, O_RDWR); |
| if (vzctlfd < 0) |
| goto error; |
| veid.num = 256; |
| buf = x_malloc(veid.num * sizeof(envid_t)); |
| while (1) { |
| veid.id = buf; |
| ret = ioctl(vzctlfd, VZCTL_GET_VEIDS, &veid); |
| if (ret < 0) |
| goto out; |
| if (ret <= veid.num) |
| break; |
| veid.num = ret + 20; |
| buf = x_realloc(buf, veid.num * sizeof(envid_t)); |
| } |
| nves = ret; |
| for (i = 0; i < nves; i++) { |
| struct Cveinfo ve; |
| envid_t id; |
| |
| id = veid.id[i]; |
| if (!id || !check_veid_restr(id)) |
| continue; |
| memset(&ve, '\0', sizeof(ve)); |
| ve.veid = id; |
| ve.status = VE_RUNNING; |
| ret = get_ve_ips(id, &ve.ip); |
| if (ret < 0) { |
| if (errno != ESRCH) |
| goto out; |
| continue; |
| } |
| if (update) |
| update_ve(id, ve.ip, ve.status); |
| else |
| add_elem(&ve); |
| } |
| if (!update) |
| qsort(veinfo, n_veinfo, sizeof(struct Cveinfo), id_sort_fn); |
| ret = 0; |
| out: |
| free(buf); |
| close(vzctlfd); |
| error: |
| return ret; |
| } |
| #endif |
| |
| static int get_run_quota_stat() |
| { |
| unsigned long usage, softlimit, hardlimit, time, exp; |
| int veid = 0, prev_veid = 0; |
| struct Cquota quota; |
| FILE *fp; |
| char buf[128]; |
| char str[11]; |
| |
| if ((fp = fopen(PROCQUOTA, "r")) == NULL) { |
| return 1; |
| } |
| veid = 0; |
| while (!feof(fp)) { |
| if (fgets(buf, sizeof(buf), fp) == NULL) |
| break; |
| if (strchr(buf, ':') != NULL) { |
| prev_veid = veid; |
| if (sscanf(buf, "%d:", &veid) != 1) |
| continue; |
| if (prev_veid) |
| update_quota(prev_veid, "a); |
| memset("a, 0, sizeof(quota)); |
| continue; |
| |
| } |
| if (sscanf(buf, "%10s %lu %lu %lu %lu %lu", str, &usage, |
| &softlimit, &hardlimit, &time, &exp) == 6) |
| { |
| if (!strcmp(str, "1k-blocks")) { |
| quota.diskspace[0] = usage; |
| quota.diskspace[1] = softlimit; |
| quota.diskspace[2] = hardlimit; |
| } else if (!strcmp(str, "inodes")) { |
| quota.diskinodes[0] = usage; |
| quota.diskinodes[1] = softlimit; |
| quota.diskinodes[2] = hardlimit; |
| } |
| } |
| } |
| if (veid) |
| update_quota(veid, "a); |
| fclose(fp); |
| return 0; |
| } |
| |
| static int get_ve_ploop_info(struct Cveinfo *ve) |
| { |
| #ifdef HAVE_PLOOP |
| char descr[PATH_MAX]; |
| struct ploop_info i = {}; |
| |
| ploop.set_verbose_level(PLOOP_LOG_NOSTDOUT); |
| GET_DISK_DESCRIPTOR(descr, ve->private); |
| if (ploop.get_info_by_descr(descr, &i)) |
| return -1; |
| |
| if (ve->quota == NULL) { |
| ve->quota = x_malloc(sizeof(struct Cquota)); |
| memset(ve->quota, 0, sizeof(struct Cquota)); |
| } |
| |
| // space avail |
| ve->quota->diskspace[1] = ve->quota->diskspace[2] = |
| (i.fs_blocks * i.fs_bsize) >> 10; |
| // space used |
| ve->quota->diskspace[0] = |
| ((i.fs_blocks - i.fs_bfree) * i.fs_bsize) >> 10; |
| |
| // inodes avail |
| ve->quota->diskinodes[1] = ve->quota->diskinodes[2] = |
| i.fs_inodes; |
| // inodes used |
| ve->quota->diskinodes[0] = i.fs_inodes - i.fs_ifree; |
| #else |
| static int shown = 0; |
| |
| if (!shown) { |
| fprintf(stderr, "Warning: ploop support is not compiled in\n"); |
| shown = 1; |
| } |
| #endif /* HAVE_PLOOP */ |
| |
| return 0; |
| } |
| |
| static int get_ves_ploop_info() |
| { |
| int i; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if (veinfo[i].layout == VE_LAYOUT_PLOOP && !veinfo[i].hide |
| && is_ploop_supported()) |
| get_ve_ploop_info(&veinfo[i]); |
| } |
| return 0; |
| } |
| |
| static long get_clk_tck() |
| { |
| if (__clk_tck != -1) |
| return __clk_tck; |
| __clk_tck = sysconf(_SC_CLK_TCK); |
| return __clk_tck; |
| } |
| |
| static int get_ve_cpustat(struct Cveinfo *ve) |
| { |
| struct vz_cpu_stat stat; |
| struct vzctl_cpustatctl statctl; |
| struct Ccpustat st; |
| |
| statctl.veid = ve->veid; |
| statctl.cpustat = &stat; |
| if (ioctl(vzctlfd, VZCTL_GET_CPU_STAT, &statctl) != 0) |
| return 1; |
| st.la[0] = stat.avenrun[0].val_int + (stat.avenrun[0].val_frac * 0.01); |
| st.la[1] = stat.avenrun[1].val_int + (stat.avenrun[1].val_frac * 0.01); |
| st.la[2] = stat.avenrun[2].val_int + (stat.avenrun[2].val_frac * 0.01); |
| |
| st.uptime = (float) stat.uptime_jif / get_clk_tck(); |
| |
| ve->cpustat = x_malloc(sizeof(st)); |
| memcpy(ve->cpustat, &st, sizeof(st)); |
| return 0; |
| } |
| |
| static int get_ves_cpustat() |
| { |
| int i; |
| |
| if ((vzctlfd = open(VZCTLDEV, O_RDWR)) < 0) |
| return 1; |
| for (i = 0; i < n_veinfo; i++) { |
| if (veinfo[i].hide) |
| continue; |
| get_ve_cpustat(&veinfo[i]); |
| } |
| close(vzctlfd); |
| return 0; |
| } |
| |
| static int get_mounted_status(int detailed) |
| { |
| int i; |
| char buf[512]; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if (veinfo[i].layout == 0) { |
| int ploop = guess_ve_private_is_ploop(veinfo[i].private); |
| veinfo[i].layout = ploop ? VE_LAYOUT_PLOOP : VE_LAYOUT_SIMFS; |
| } |
| if (veinfo[i].status == VE_RUNNING) |
| continue; |
| if (veinfo[i].private == NULL || |
| stat_file(veinfo[i].private) != 1) |
| { |
| veinfo[i].hide = 1; |
| continue; |
| } |
| if (!detailed) |
| continue; |
| get_dump_file(veinfo[i].veid, dumpdir, buf, sizeof(buf)); |
| if (stat_file(buf) == 1) |
| veinfo[i].status = VE_SUSPENDED; |
| if (veinfo[i].root == NULL) |
| continue; |
| if (vps_is_mounted(veinfo[i].root, veinfo[i].private) == 1) |
| veinfo[i].status = VE_MOUNTED; |
| } |
| return 0; |
| } |
| |
| static int get_ve_cpunum(struct Cveinfo *ve) { |
| char path[] = "/proc/vz/fairsched/2147483647/cpu.nr_cpus"; |
| int veid = ve->veid; |
| FILE *fp; |
| int ret = -1; |
| |
| snprintf(path, sizeof(path), |
| "/proc/vz/fairsched/%d/cpu.nr_cpus", veid); |
| fp = fopen(path, "r"); |
| if (fp == NULL) { |
| fprintf(stderr, "Unable to open %s: %s\n", |
| path, strerror(errno)); |
| return -1; |
| } |
| if (fscanf(fp, "%d", &ve->cpunum) != 1) |
| goto out; |
| |
| ret = 0; |
| out: |
| fclose(fp); |
| |
| return ret; |
| } |
| |
| static void get_ves_cpunum() |
| { |
| int i; |
| |
| if (access("/proc/vz/fairsched/", F_OK)) |
| /* pre-RHEL6 kernel */ |
| return; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if ((veinfo[i].hide) || (veinfo[i].status != VE_RUNNING)) |
| continue; |
| get_ve_cpunum(&veinfo[i]); |
| } |
| } |
| |
| static int get_ves_cpu() |
| { |
| unsigned long tmp; |
| int veid, id, weight, rate; |
| FILE *fp; |
| char buf[128]; |
| |
| if ((fp = fopen(PROCFSHED, "r")) == NULL) { |
| fprintf(stderr, "Unable to open %s: %s\n", |
| PROCFSHED, strerror(errno)); |
| return 1; |
| } |
| veid = 0; |
| while (!feof(fp)) { |
| if (fgets(buf, sizeof(buf), fp) == NULL) |
| break; |
| if (sscanf(buf, "%d %d %lu %d %d", |
| &veid, &id, &tmp, &weight, &rate) != 5) |
| { |
| continue; |
| } |
| if (id && !veid) { |
| rate = rint((double)rate * 100 / 1024); |
| weight = rint((double)MAXCPUUNITS / weight); |
| update_cpu(id, rate, weight); |
| } |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| static int update_ves_io(void) |
| { |
| int limit, i; |
| |
| if ((vzctlfd = open(VZCTLDEV, O_RDWR)) < 0) |
| return 1; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| if ((veinfo[i].hide) || (veinfo[i].status != VE_RUNNING)) |
| continue; |
| |
| if (vzctl_get_iolimit(vzctlfd, veinfo[i].veid, &limit) == 0) |
| veinfo[i].io.iolimit = limit; |
| |
| if (vzctl_get_iopslimit(vzctlfd, veinfo[i].veid, &limit) == 0) |
| veinfo[i].io.iopslimit = limit; |
| } |
| close(vzctlfd); |
| |
| return 0; |
| } |
| |
| static int get_ve_list() |
| { |
| DIR *dp; |
| struct dirent *ep; |
| int veid, res; |
| struct Cveinfo ve; |
| char str[6]; |
| |
| dp = opendir(VPSCONFDIR); |
| if (dp == NULL) { |
| return -1; |
| } |
| memset(&ve, 0, sizeof(struct Cveinfo)); |
| ve.status = VE_STOPPED; |
| while ((ep = readdir (dp))) { |
| res = sscanf(ep->d_name, "%d.%5s", &veid, str); |
| if (!(res == 2 && !strcmp(str, "conf"))) |
| continue; |
| if (veid < 0 || veid > VEID_MAX) { |
| fprintf(stderr, "Warning: invalid CTID in config file " |
| "name: %s, skipping\n", ep->d_name); |
| continue; |
| } |
| if (!check_veid_restr(veid)) |
| continue; |
| ve.veid = veid; |
| add_elem(&ve); |
| } |
| closedir(dp); |
| if (veinfo != NULL) |
| qsort(veinfo, n_veinfo, sizeof(struct Cveinfo), id_sort_fn); |
| return 0; |
| } |
| |
| static int search_field(char *name) |
| { |
| unsigned int i; |
| |
| if (name == NULL) |
| return -1; |
| for (i = 0; i < ARRAY_SIZE(field_names); i++) { |
| if (!strcmp(name, field_names[i].name)) |
| return i; |
| } |
| return -1; |
| } |
| |
| static int build_field_order(char *fields) |
| { |
| struct Cfield_order *tmp, *prev = NULL; |
| char *sp, *ep, *p; |
| char name[32]; |
| int order; |
| size_t nm_len; |
| |
| sp = fields; |
| if (fields == NULL) { |
| if (fmt_json) |
| return 0; |
| sp = default_field_order; |
| } |
| ep = sp + strlen(sp); |
| do { |
| if ((p = strchr(sp, ',')) == NULL) |
| p = ep; |
| nm_len = p - sp + 1; |
| if (nm_len > sizeof(name) - 1) { |
| fprintf(stderr, "Invalid field: %s\n", sp); |
| return 1; |
| } |
| snprintf(name, nm_len, "%s", sp); |
| sp = p + 1; |
| if ((order = search_field(name)) < 0) { |
| fprintf(stderr, "Unknown field: %s\n", name); |
| return 1; |
| } |
| if (fmt_json && field_names[order].index) { |
| fprintf(stderr, "Field `%s' is not available " |
| "in JSON output\n", name); |
| return 1; |
| } |
| tmp = x_malloc(sizeof(struct Cfield_order)); |
| tmp->order = order; |
| tmp->next = NULL; |
| if (prev == NULL) |
| g_field_order = tmp; |
| else |
| prev->next = tmp; |
| prev = tmp; |
| } while (sp < ep); |
| return 0; |
| } |
| |
| static int check_param(int res_type) |
| { |
| struct Cfield_order *p; |
| |
| if (fmt_json && !g_field_order) |
| return 1; |
| |
| for (p = g_field_order; p != NULL; p = p->next) { |
| if (field_names[p->order].res_type == res_type) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int collect() |
| { |
| int update = 0; |
| int ret; |
| |
| if (all_ve || g_ve_list != NULL || only_stopped_ve) { |
| get_ve_list(); |
| update = 1; |
| } |
| get_run_ve(update); |
| if (!only_stopped_ve && (ret = get_ub())) |
| return ret; |
| /* No CT found, exit with error */ |
| if (!n_veinfo) { |
| if (fmt_json) |
| printf("[]\n"); |
| else |
| fprintf(stderr, "Container(s) not found\n"); |
| return 1; |
| } |
| if (check_param(RES_CPUSTAT)) |
| get_ves_cpustat(); |
| if (check_param(RES_CPU)) |
| if (!only_stopped_ve && (ret = get_ves_cpu())) |
| return ret; |
| if (check_param(RES_CPUNUM) && !only_stopped_ve) |
| get_ves_cpunum(); |
| read_ves_param(); |
| if (check_param(RES_IO)) |
| update_ves_io(); |
| get_mounted_status(check_param(RES_STATUS)); |
| if (check_param(RES_QUOTA)) { |
| get_run_quota_stat(); |
| get_ves_ploop_info(); |
| } |
| if (host_pattern != NULL) |
| filter_by_hostname(); |
| if (name_pattern != NULL) |
| filter_by_name(); |
| if (desc_pattern != NULL) |
| filter_by_description(); |
| return 0; |
| } |
| |
| static void print_names() |
| { |
| unsigned int i; |
| |
| for (i = 0; i < ARRAY_SIZE(field_names); i++) |
| printf("%-15s %-15s\n", field_names[i].name, |
| field_names[i].hdr); |
| return; |
| } |
| |
| static void free_veinfo() |
| { |
| int i; |
| |
| for (i = 0; i < n_veinfo; i++) { |
| free(veinfo[i].ip); |
| free(veinfo[i].nameserver); |
| free(veinfo[i].searchdomain); |
| free(veinfo[i].hostname); |
| free(veinfo[i].name); |
| free(veinfo[i].description); |
| free(veinfo[i].ostemplate); |
| free(veinfo[i].ubc); |
| free(veinfo[i].quota); |
| free(veinfo[i].cpustat); |
| free(veinfo[i].cpu); |
| free(veinfo[i].root); |
| free(veinfo[i].private); |
| free(veinfo[i].mount_opts); |
| free(veinfo[i].origin_sample); |
| free(veinfo[i].bootorder); |
| } |
| } |
| |
| static struct option list_options[] = |
| { |
| {"no-header", no_argument, NULL, 'H'}, |
| {"no-trim", no_argument, NULL, 't'}, |
| {"stopped", no_argument, NULL, 'S'}, |
| {"all", no_argument, NULL, 'a'}, |
| {"name", no_argument, NULL, 'n'}, |
| {"json", no_argument, NULL, 'j'}, |
| {"name_filter", required_argument, NULL, 'N'}, |
| {"hostname", required_argument, NULL, 'h'}, |
| {"description", required_argument, NULL, 'd'}, |
| {"output", required_argument, NULL, 'o'}, |
| {"sort", required_argument, NULL, 's'}, |
| {"list", no_argument, NULL, 'L'}, |
| {"help", no_argument, NULL, 'e'}, |
| { NULL, 0, NULL, 0 } |
| }; |
| |
| int main(int argc, char **argv) |
| { |
| int ret; |
| char *f_order = NULL; |
| char *ep, *p; |
| int veid, c; |
| |
| while (1) { |
| int option_index = -1; |
| c = getopt_long(argc, argv, "HtSanjN:h:d:o:s:Le1", |
| list_options, &option_index); |
| if (c == -1) |
| break; |
| |
| switch(c) { |
| case 'S' : |
| only_stopped_ve = 1; |
| break; |
| case 't' : |
| trim = 0; |
| break; |
| case 'H' : |
| show_hdr = 0; |
| break; |
| case 'L' : |
| print_names(); |
| return 0; |
| case 'a' : |
| all_ve = 1; |
| break; |
| case 'h' : |
| host_pattern = strdup(optarg); |
| break; |
| case 'd' : |
| desc_pattern = strdup(optarg); |
| break; |
| case 'o' : |
| f_order = strdup(optarg); |
| break; |
| case 's' : |
| p = optarg; |
| if (p[0] == '-') { |
| p++; |
| sort_rev = 1; |
| } |
| if ((g_sort_field = search_field(p)) < 0) { |
| fprintf(stderr, "Invalid sort field name: " |
| "%s\n", optarg); |
| return 1; |
| } |
| break; |
| case '1' : |
| f_order = strdup("veid"); |
| show_hdr = 0; |
| break; |
| case 'n' : |
| f_order = strdup(default_nm_field_order); |
| break; |
| case 'N' : |
| name_pattern = strdup(optarg); |
| break; |
| case 'j' : |
| fmt_json = 1; |
| p_buf = e_buf = NULL; |
| break; |
| default : |
| /* "Unknown option" error msg is printed by getopt */ |
| usage(); |
| return 1; |
| } |
| } |
| if (optind < argc) { |
| while (optind < argc) { |
| veid = strtol(argv[optind], &ep, 10); |
| if (*ep != 0 || !veid) { |
| veid = get_veid_by_name(argv[optind]); |
| } |
| if (veid < 0 || veid > VEID_MAX) { |
| fprintf(stderr, "CT ID %s is invalid.\n", |
| argv[optind]); |
| return 1; |
| } |
| optind++; |
| g_ve_list = x_realloc(g_ve_list, |
| sizeof(*g_ve_list) * ++n_ve_list); |
| g_ve_list[n_ve_list - 1] = veid; |
| } |
| qsort(g_ve_list, n_ve_list, sizeof(*g_ve_list), id_sort_fn); |
| } |
| init_log(NULL, 0, 0, 0, 0, NULL); |
| if (build_field_order(f_order)) |
| return 1; |
| if (getuid()) { |
| fprintf(stderr, "This program can only be run under root.\n"); |
| return 1; |
| } |
| if ((ret = collect())) { |
| /* If no specific CTIDs are specified in arguments, |
| * 'no containers found' is not an error (bug #2149) |
| */ |
| if (g_ve_list == NULL) |
| return 0; |
| else |
| return ret; |
| } |
| print_ve(); |
| free_veinfo(); |
| free(host_pattern); |
| free(name_pattern); |
| free(desc_pattern); |
| free(f_order); |
| |
| return 0; |
| } |