From 0a1ed6dffb303ce950620558c9509f8f3ce9043e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 3 Nov 2016 16:47:07 +0100 Subject: lsmem: cleanup, use libsmartcols for all output * add --pairs, --raw a --json outputs * add --noheadings to disable header * add --bytes * add --output Signed-off-by: Karel Zak --- sys-utils/lsmem.c | 401 +++++++++++++++++++++++++++++------------------------- 1 file changed, 213 insertions(+), 188 deletions(-) (limited to 'sys-utils/lsmem.c') diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c index 13e2414d8..608a862ed 100644 --- a/sys-utils/lsmem.c +++ b/sys-utils/lsmem.c @@ -51,26 +51,27 @@ struct memory_block { unsigned int removable:1; }; -enum { - OUTPUT_READABLE = 0, /* default */ - OUTPUT_PARSABLE, /* -p */ -}; - struct lsmem_desc { struct dirent **dirs; int ndirs; struct memory_block *blocks; int nblocks; - unsigned int have_nodes : 1; uint64_t block_size; uint64_t mem_online; uint64_t mem_offline; -}; -struct lsmem_modifier { - int mode; /* OUTPUT_* */ - unsigned int compat : 1; - unsigned int list_all_blocks : 1; + struct libscols_table *table; + unsigned int have_nodes : 1, + raw : 1, + export : 1, + json : 1, + noheadings : 1, + summary : 1, + list_all : 1, + bytes : 1, + want_node : 1, + want_state : 1, + want_removable : 1; }; enum { @@ -82,21 +83,45 @@ enum { COL_NODE, }; -struct lsmem_coldesc { - const int flags; - const char *name; - const char *help; +/* column names */ +struct coldesc { + const char *name; /* header */ + double whint; /* width hint (N < 1 is in percent of termwidth) */ + int flags; /* SCOLS_FL_* */ + const char *help; + + int sort_type; /* SORT_* */ }; -static struct lsmem_coldesc coldescs[] = { - [COL_RANGE] = { 0, "RANGE", N_("adress range")}, - [COL_SIZE] = { SCOLS_FL_RIGHT, "SIZE", N_("size of memory")}, - [COL_STATE] = { 0, "STATE", N_("state of memory")}, - [COL_REMOVABLE] = { 0, "REMOVABLE", N_("memory is removable")}, - [COL_BLOCK] = { 0, "BLOCK", N_("memory block")}, - [COL_NODE] = { 0, "NODE", N_("node information")}, +/* columns descriptions */ +static struct coldesc coldescs[] = { + [COL_RANGE] = { "RANGE", 0, 0, N_("adress range")}, + [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of memory")}, + [COL_STATE] = { "STATE", 0, 0, N_("state of memory")}, + [COL_REMOVABLE] = { "REMOVABLE", 0, SCOLS_FL_RIGHT, N_("memory is removable")}, + [COL_BLOCK] = { "BLOCK", 0, SCOLS_FL_RIGHT, N_("memory block")}, + [COL_NODE] = { "NODE", 0, SCOLS_FL_RIGHT, N_("node information")}, }; +/* columns[] array specifies all currently wanted output column. The columns + * are defined by coldescs[] array and you can specify (on command line) each + * column twice. That's enough, dynamically allocated array of the columns is + * unnecessary overkill and over-engineering in this case */ +static int columns[ARRAY_SIZE(coldescs) * 2]; +static size_t ncolumns; + +static inline size_t err_columns_index(size_t arysz, size_t idx) +{ + if (idx >= arysz) + errx(EXIT_FAILURE, _("too many columns specified, " + "the limit is %zu columns"), + arysz - 1); + return idx; +} + +#define add_column(ary, n, id) \ + ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) + static int column_name_to_id(const char *name, size_t namesz) { size_t i; @@ -111,131 +136,96 @@ static int column_name_to_id(const char *name, size_t namesz) return -1; } -static char *get_cell_header(int col, char *buf, size_t bufsz) +static inline int get_column_id(int num) { - snprintf(buf, bufsz, "%s", coldescs[col].name); - return buf; + assert(num >= 0); + assert((size_t) num < ncolumns); + assert(columns[num] < (int) ARRAY_SIZE(coldescs)); + + return columns[num]; } -static char *get_cell_data(struct lsmem_desc *desc, int idx, int col, - struct lsmem_modifier *mod, - char *buf, size_t bufsz) +static inline struct coldesc *get_column_desc(int num) { - struct memory_block *blk; - uint64_t start, size; - - blk = &desc->blocks[idx]; - start = blk->index * desc->block_size; - size = blk->count * desc->block_size; - - switch (col) { - case COL_RANGE: - snprintf(buf, bufsz, "0x%016"PRIx64"-0x%016"PRIx64, start, start + size - 1); - break; - case COL_SIZE: - if (mod->mode == OUTPUT_PARSABLE) - snprintf(buf, bufsz, "%"PRId64, size); - else - snprintf(buf, bufsz, "%s", size_to_human_string(SIZE_SUFFIX_1LETTER, size)); - break; - case COL_STATE: - if (blk->state == MEMORY_STATE_ONLINE) - snprintf(buf, bufsz, _("online")); - else if (blk->state == MEMORY_STATE_OFFLINE) - snprintf(buf, bufsz, _("offline")); - else if (blk->state == MEMORY_STATE_GOING_OFFLINE) - snprintf(buf, bufsz, _("on->off")); - else /* unknown */ - snprintf(buf, bufsz, "?"); - break; - case COL_REMOVABLE: - if (blk->state == MEMORY_STATE_ONLINE) - snprintf(buf, bufsz, "%s", blk->removable ? _("yes") : _("no")); - else - snprintf(buf, bufsz, "-"); - break; - case COL_BLOCK: - if (blk->count == 1) - snprintf(buf, bufsz, "%"PRId64, blk->index); - else - snprintf(buf, bufsz, "%"PRId64"-%"PRId64, - blk->index, blk->index + blk->count - 1); - break; - case COL_NODE: - if (desc->have_nodes) - snprintf(buf, bufsz, "%d", blk->node); - else - snprintf(buf, bufsz, "-"); - break; - } - return buf; + return &coldescs[ get_column_id(num) ]; } -static void print_parsable(struct lsmem_desc *desc, int *cols, int ncols, - struct lsmem_modifier *mod) +static inline int has_column(int id) { - char buf[BUFSIZ], *data; - int c, i; - - fputs("# ", stdout); - for (i = 0; i < ncols; i++) { - data = get_cell_header(cols[i], buf, sizeof(buf)); - if (i > 0) - fputc(',', stdout); - fputs(data && *data ? data : "", stdout); - } - fputc('\n', stdout); + size_t i; - for (i = 0; i < desc->nblocks; i++) { - for (c = 0; c < ncols; c++) { - if (c > 0) - putchar(','); - data = get_cell_data(desc, i, cols[c], mod, buf, sizeof(buf)); - fputs(data && *data ? data : "", stdout); - } - fputc('\n', stdout); - } + for (i = 0; i < ncolumns; i++) + if (columns[i] == id) + return 1; + return 0; } -static void print_readable_table(struct lsmem_desc *desc, int *cols, int ncols, - struct lsmem_modifier *mod) +static void add_scols_line(struct lsmem_desc *desc, struct memory_block *blk) { - struct libscols_table *table; - char buf[BUFSIZ], *data; - int c, i; + size_t i; + struct libscols_line *line; - scols_init_debug(0); - table = scols_new_table(); - if (!table) - err(EXIT_FAILURE, _("Failed to initialize output table")); - for (i = 0; i < ncols; i++) { - data = get_cell_header(cols[i], buf, sizeof(buf)); - if (!scols_table_new_column(table, xstrdup(data), 0, coldescs[i].flags)) - err(EXIT_FAILURE, _("Failed to initialize output column")); - } - for (i = 0; i < desc->nblocks; i++) { - struct libscols_line *line; + line = scols_table_new_line(desc->table, NULL); + if (!line) + err_oom(); - line = scols_table_new_line(table, NULL); - if (!line) - err(EXIT_FAILURE, _("Failed to initialize output line")); + for (i = 0; i < ncolumns; i++) { + char *str = NULL; - for (c = 0; c < ncols; c++) { - data = get_cell_data(desc, i, cols[c], mod, buf, sizeof(buf)); - if (!data || !*data) - data = "-"; - scols_line_set_data(line, c, data); + switch (get_column_id(i)) { + case COL_RANGE: + { + uint64_t start = blk->index * desc->block_size; + uint64_t size = blk->count * desc->block_size; + xasprintf(&str, "0x%016"PRIx64"-0x%016"PRIx64, start, start + size - 1); + break; + } + case COL_SIZE: + if (desc->bytes) + xasprintf(&str, "%"PRId64, (uint64_t) blk->count * desc->block_size); + else + str = size_to_human_string(SIZE_SUFFIX_1LETTER, + (uint64_t) blk->count * desc->block_size); + break; + case COL_STATE: + str = xstrdup( + blk->state == MEMORY_STATE_ONLINE ? _("online") : + blk->state == MEMORY_STATE_OFFLINE ? _("offline") : + blk->state == MEMORY_STATE_GOING_OFFLINE ? _("on->off") : + "?"); + break; + case COL_REMOVABLE: + if (blk->state == MEMORY_STATE_ONLINE) + str = xstrdup(blk->removable ? _("yes") : _("no")); + break; + case COL_BLOCK: + if (blk->count == 1) + xasprintf(&str, "%"PRId64, blk->index); + else + xasprintf(&str, "%"PRId64"-%"PRId64, + blk->index, blk->index + blk->count - 1); + break; + case COL_NODE: + if (desc->have_nodes) + xasprintf(&str, "%d", blk->node); + break; } + + if (str && scols_line_refer_data(line, i, str) != 0) + err_oom(); } - scols_print_table(table); - scols_unref_table(table); } -static void print_readable(struct lsmem_desc *desc, int *cols, int ncols, - struct lsmem_modifier *mod) +static void fill_scols_table(struct lsmem_desc *desc) +{ + int i; + + for (i = 0; i < desc->nblocks; i++) + add_scols_line(desc, &desc->blocks[i]); +} + +static void print_summary(struct lsmem_desc *desc) { - print_readable_table(desc, cols, ncols, mod); - fputc('\n', stdout); fprintf(stdout, _("Memory block size : %8s\n"), size_to_human_string(SIZE_SUFFIX_1LETTER, desc->block_size)); fprintf(stdout, _("Total online memory : %8s\n"), @@ -288,34 +278,30 @@ static void memory_block_read_attrs(struct lsmem_desc *desc, char *name, blk->node = memory_block_get_node(name); } -static int is_mergeable(struct lsmem_desc *desc, char *enabled, - struct memory_block *blk, - struct lsmem_modifier *mod) +static int is_mergeable(struct lsmem_desc *desc, struct memory_block *blk) { struct memory_block *curr; if (!desc->nblocks) return 0; curr = &desc->blocks[desc->nblocks - 1]; - if (mod->list_all_blocks) + if (desc->list_all) return 0; if (curr->index + curr->count != blk->index) return 0; - if (enabled[COL_STATE] && (curr->state != blk->state)) + if (desc->want_state && curr->state != blk->state) return 0; - if (enabled[COL_REMOVABLE] && (curr->removable != blk->removable)) + if (desc->want_removable && curr->removable != blk->removable) return 0; - if (enabled[COL_NODE] && desc->have_nodes) { + if (desc->want_node && desc->have_nodes) { if (curr->node != blk->node) return 0; } return 1; } -static void read_info(struct lsmem_desc *desc, int *cols, int ncols, - struct lsmem_modifier *mod) +static void read_info(struct lsmem_desc *desc) { - char enabled[ARRAY_SIZE(coldescs)]; struct memory_block blk; char line[BUFSIZ]; int i; @@ -323,12 +309,9 @@ static void read_info(struct lsmem_desc *desc, int *cols, int ncols, path_read_str(line, sizeof(line), _PATH_SYS_MEMORY_BLOCK_SIZE); desc->block_size = strtoumax(line, NULL, 16); - memset(enabled, 0, sizeof(enabled)); - for (i = 0; i < ncols; i++) - enabled[cols[i]] = 1; for (i = 0; i < desc->ndirs; i++) { memory_block_read_attrs(desc, desc->dirs[i]->d_name, &blk); - if (is_mergeable(desc, enabled, &blk, mod)) { + if (is_mergeable(desc, &blk)) { desc->blocks[desc->nblocks - 1].count++; continue; } @@ -379,10 +362,15 @@ static void __attribute__((__noreturn__)) lsmem_usage(FILE *out) fputs(_("List the ranges of available memory with their online status.\n"), out); fputs(USAGE_OPTIONS, out); - fputs(_(" -a, --all list each individiual memory block\n"), out); - fputs(_(" -e, --extended[=] print customized output in a readable format\n"), out); - fputs(_(" -p, --parse[=] print customized output in a parsable format\n"), out); - fputs(_(" -s, --sysroot use the specified directory as system root\n"), out); + fputs(_(" -J, --json use JSON output format\n"), out); + fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); + fputs(_(" -a, --all list each individiual memory block\n"), out); + fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); + fputs(_(" -n, --noheadings don't print headings\n"), out); + fputs(_(" -o, --output output columns\n"), out); + fputs(_(" -r, --raw use raw output format\n"), out); + fputs(_(" -s, --sysroot use the specified directory as system root\n"), out); + fputs(USAGE_SEPARATOR, out); fputs(USAGE_HELP, out); fputs(USAGE_VERSION, out); @@ -399,22 +387,26 @@ static void __attribute__((__noreturn__)) lsmem_usage(FILE *out) int main(int argc, char **argv) { - struct lsmem_modifier _mod = { .mode = OUTPUT_READABLE, .compat = 1 }, *mod = &_mod; struct lsmem_desc _desc = { }, *desc = &_desc; - int columns[ARRAY_SIZE(coldescs)], ncolumns = 0; + const char *outarg = NULL; int c; + size_t i; static const struct option longopts[] = { {"all", no_argument, NULL, 'a'}, - {"extended", optional_argument, NULL, 'e'}, + {"bytes", no_argument, NULL, 'b'}, {"help", no_argument, NULL, 'h'}, - {"parse", optional_argument, NULL, 'p'}, + {"json", no_argument, NULL, 'J'}, + {"noheadings", no_argument, NULL, 'n'}, + {"output", required_argument, NULL, 'o'}, + {"pairs", no_argument, NULL, 'P'}, + {"raw", no_argument, NULL, 'r'}, {"sysroot", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ - { 'e','p' }, + { 'J', 'P', 'r' }, { 0 } }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; @@ -424,30 +416,34 @@ int main(int argc, char **argv) textdomain(PACKAGE); atexit(close_stdout); - while ((c = getopt_long(argc, argv, "ae::hp::s:V", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "abhJno:Prs:V", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch (c) { case 'a': - mod->list_all_blocks = 1; + desc->list_all = 1; + break; + case 'b': + desc->bytes = 1; break; case 'h': lsmem_usage(stdout); break; - case 'p': - case 'e': - if (optarg) { - if (*optarg == '=') - optarg++; - ncolumns = string_to_idarray(optarg, - columns, ARRAY_SIZE(columns), - column_name_to_id); - if (ncolumns < 0) - return EXIT_FAILURE; - } - mod->mode = c == 'p' ? OUTPUT_PARSABLE : OUTPUT_READABLE; - mod->compat = 0; + case 'J': + desc->json = 1; + break; + case 'n': + desc->noheadings = 1; + break; + case 'o': + outarg = optarg; + break; + case 'P': + desc->export = 1; + break; + case 'r': + desc->raw = 1; break; case 's': path_set_prefix(optarg); @@ -463,32 +459,61 @@ int main(int argc, char **argv) if (argc != optind) lsmem_usage(stderr); - read_basic_info(desc); - + /* + * Default columns + */ if (!ncolumns) { - /* No list was given. Print the following lines by default */ - columns[ncolumns++] = COL_RANGE; - columns[ncolumns++] = COL_SIZE; - columns[ncolumns++] = COL_STATE; - columns[ncolumns++] = COL_REMOVABLE; - columns[ncolumns++] = COL_BLOCK; - if (!mod->compat) { - /* Print everything else what is there if the - * extended or parsable mode has been specified */ - if (desc->have_nodes) - columns[ncolumns++] = COL_NODE; - } + add_column(columns, ncolumns++, COL_RANGE); + add_column(columns, ncolumns++, COL_SIZE); + add_column(columns, ncolumns++, COL_STATE); + add_column(columns, ncolumns++, COL_REMOVABLE); + add_column(columns, ncolumns++, COL_BLOCK); } - read_info(desc, columns, ncolumns, mod); + if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), + &ncolumns, column_name_to_id) < 0) + return EXIT_FAILURE; + + /* + * Initialize output + */ + scols_init_debug(0); + + if (!(desc->table = scols_new_table())) + errx(EXIT_FAILURE, _("failed to initialize output table")); + scols_table_enable_raw(desc->table, desc->raw); + scols_table_enable_export(desc->table, desc->export); + scols_table_enable_json(desc->table, desc->json); + scols_table_enable_noheadings(desc->table, desc->noheadings); + + if (desc->json) + scols_table_set_name(desc->table, "memory"); - switch (mod->mode) { - case OUTPUT_READABLE: - print_readable(desc, columns, ncolumns, mod); - break; - case OUTPUT_PARSABLE: - print_parsable(desc, columns, ncolumns, mod); - break; + for (i = 0; i < ncolumns; i++) { + struct coldesc *ci = get_column_desc(i); + if (!scols_table_new_column(desc->table, ci->name, ci->whint, ci->flags)) + err(EXIT_FAILURE, _("Failed to initialize output column")); } + + if (has_column(COL_STATE)) + desc->want_state = 1; + if (has_column(COL_NODE)) + desc->want_node = 1; + if (has_column(COL_REMOVABLE)) + desc->want_removable = 1; + + /* + * Read data and print output + */ + read_basic_info(desc); + read_info(desc); + + fill_scols_table(desc); + scols_print_table(desc->table); + + fputc('\n', stdout); + print_summary(desc); + + scols_unref_table(desc->table); return 0; } -- cgit v1.2.3-55-g7522