2 * lsmem - Show memory configuration
4 * Copyright IBM Corp. 2016
5 * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <closestream.h>
35 #include <libsmartcols.h>
37 #define _PATH_SYS_MEMORY "/sys/devices/system/memory"
38 #define _PATH_SYS_MEMORY_BLOCK_SIZE _PATH_SYS_MEMORY "/block_size_bytes"
40 #define MEMORY_STATE_ONLINE 0
41 #define MEMORY_STATE_OFFLINE 1
42 #define MEMORY_STATE_GOING_OFFLINE 2
43 #define MEMORY_STATE_UNKNOWN 3
50 unsigned int removable
:1;
56 struct memory_block
*blocks
;
62 struct libscols_table
*table
;
63 unsigned int have_nodes
: 1,
89 const char *name
; /* header */
90 double whint
; /* width hint (N < 1 is in percent of termwidth) */
91 int flags
; /* SCOLS_FL_* */
94 int sort_type
; /* SORT_* */
97 /* columns descriptions */
98 static struct coldesc coldescs
[] = {
99 [COL_RANGE
] = { "RANGE", 0, 0, N_("start and end address of the memory range")},
100 [COL_SIZE
] = { "SIZE", 5, SCOLS_FL_RIGHT
, N_("size of the memory range")},
101 [COL_STATE
] = { "STATE", 0, SCOLS_FL_RIGHT
, N_("online status of the memory range")},
102 [COL_REMOVABLE
] = { "REMOVABLE", 0, SCOLS_FL_RIGHT
, N_("memory is removable")},
103 [COL_BLOCK
] = { "BLOCK", 0, SCOLS_FL_RIGHT
, N_("memory block number or blocks range")},
104 [COL_NODE
] = { "NODE", 0, SCOLS_FL_RIGHT
, N_("numa node of memory")},
107 /* columns[] array specifies all currently wanted output column. The columns
108 * are defined by coldescs[] array and you can specify (on command line) each
109 * column twice. That's enough, dynamically allocated array of the columns is
110 * unnecessary overkill and over-engineering in this case */
111 static int columns
[ARRAY_SIZE(coldescs
) * 2];
112 static size_t ncolumns
;
114 static inline size_t err_columns_index(size_t arysz
, size_t idx
)
117 errx(EXIT_FAILURE
, _("too many columns specified, "
118 "the limit is %zu columns"),
123 #define add_column(ary, n, id) \
124 ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
126 static int column_name_to_id(const char *name
, size_t namesz
)
130 for (i
= 0; i
< ARRAY_SIZE(coldescs
); i
++) {
131 const char *cn
= coldescs
[i
].name
;
133 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
136 warnx(_("unknown column: %s"), name
);
140 static inline int get_column_id(int num
)
143 assert((size_t) num
< ncolumns
);
144 assert(columns
[num
] < (int) ARRAY_SIZE(coldescs
));
149 static inline struct coldesc
*get_column_desc(int num
)
151 return &coldescs
[ get_column_id(num
) ];
154 static inline int has_column(int id
)
158 for (i
= 0; i
< ncolumns
; i
++)
159 if (columns
[i
] == id
)
164 static void add_scols_line(struct lsmem
*lsmem
, struct memory_block
*blk
)
167 struct libscols_line
*line
;
169 line
= scols_table_new_line(lsmem
->table
, NULL
);
173 for (i
= 0; i
< ncolumns
; i
++) {
176 switch (get_column_id(i
)) {
179 uint64_t start
= blk
->index
* lsmem
->block_size
;
180 uint64_t size
= blk
->count
* lsmem
->block_size
;
181 xasprintf(&str
, "0x%016"PRIx64
"-0x%016"PRIx64
, start
, start
+ size
- 1);
186 xasprintf(&str
, "%"PRId64
, (uint64_t) blk
->count
* lsmem
->block_size
);
188 str
= size_to_human_string(SIZE_SUFFIX_1LETTER
,
189 (uint64_t) blk
->count
* lsmem
->block_size
);
193 blk
->state
== MEMORY_STATE_ONLINE
? _("online") :
194 blk
->state
== MEMORY_STATE_OFFLINE
? _("offline") :
195 blk
->state
== MEMORY_STATE_GOING_OFFLINE
? _("on->off") :
199 if (blk
->state
== MEMORY_STATE_ONLINE
)
200 str
= xstrdup(blk
->removable
? _("yes") : _("no"));
206 xasprintf(&str
, "%"PRId64
, blk
->index
);
208 xasprintf(&str
, "%"PRId64
"-%"PRId64
,
209 blk
->index
, blk
->index
+ blk
->count
- 1);
212 if (lsmem
->have_nodes
)
213 xasprintf(&str
, "%d", blk
->node
);
219 if (str
&& scols_line_refer_data(line
, i
, str
) != 0)
224 static void fill_scols_table(struct lsmem
*lsmem
)
228 for (i
= 0; i
< lsmem
->nblocks
; i
++)
229 add_scols_line(lsmem
, &lsmem
->blocks
[i
]);
232 static void print_summary(struct lsmem
*lsmem
)
235 printf("%-23s %15"PRId64
"\n",_("Memory block size:"), lsmem
->block_size
);
236 printf("%-23s %15"PRId64
"\n",_("Total online memory:"), lsmem
->mem_online
);
237 printf("%-23s %15"PRId64
"\n",_("Total offline memory:"), lsmem
->mem_offline
);
239 printf("%-23s %5s\n",_("Memory block size:"),
240 size_to_human_string(SIZE_SUFFIX_1LETTER
, lsmem
->block_size
));
241 printf("%-23s %5s\n",_("Total online memory:"),
242 size_to_human_string(SIZE_SUFFIX_1LETTER
, lsmem
->mem_online
));
243 printf("%-23s %5s\n",_("Total offline memory:"),
244 size_to_human_string(SIZE_SUFFIX_1LETTER
, lsmem
->mem_offline
));
248 static int memory_block_get_node(char *name
)
255 path
= path_get(_PATH_SYS_MEMORY
"/%s", name
);
256 if (!path
|| !(dir
= opendir(path
)))
257 err(EXIT_FAILURE
, _("Failed to open %s"), path
? path
: name
);
260 while ((de
= readdir(dir
)) != NULL
) {
261 if (strncmp("node", de
->d_name
, 4))
263 if (!isdigit_string(de
->d_name
+ 4))
265 node
= strtol(de
->d_name
+ 4, NULL
, 10);
272 static void memory_block_read_attrs(struct lsmem
*lsmem
, char *name
,
273 struct memory_block
*blk
)
278 blk
->index
= strtoumax(name
+ 6, NULL
, 10); /* get <num> of "memory<num>" */
279 blk
->removable
= path_read_u64(_PATH_SYS_MEMORY
"/%s/%s", name
, "removable");
280 blk
->state
= MEMORY_STATE_UNKNOWN
;
281 path_read_str(line
, sizeof(line
), _PATH_SYS_MEMORY
"/%s/%s", name
, "state");
282 if (strcmp(line
, "offline") == 0)
283 blk
->state
= MEMORY_STATE_OFFLINE
;
284 else if (strcmp(line
, "online") == 0)
285 blk
->state
= MEMORY_STATE_ONLINE
;
286 else if (strcmp(line
, "going-offline") == 0)
287 blk
->state
= MEMORY_STATE_GOING_OFFLINE
;
288 if (lsmem
->have_nodes
)
289 blk
->node
= memory_block_get_node(name
);
292 static int is_mergeable(struct lsmem
*lsmem
, struct memory_block
*blk
)
294 struct memory_block
*curr
;
298 curr
= &lsmem
->blocks
[lsmem
->nblocks
- 1];
301 if (curr
->index
+ curr
->count
!= blk
->index
)
303 if (lsmem
->want_state
&& curr
->state
!= blk
->state
)
305 if (lsmem
->want_removable
&& curr
->removable
!= blk
->removable
)
307 if (lsmem
->want_node
&& lsmem
->have_nodes
) {
308 if (curr
->node
!= blk
->node
)
314 static void read_info(struct lsmem
*lsmem
)
316 struct memory_block blk
;
320 path_read_str(line
, sizeof(line
), _PATH_SYS_MEMORY_BLOCK_SIZE
);
321 lsmem
->block_size
= strtoumax(line
, NULL
, 16);
323 for (i
= 0; i
< lsmem
->ndirs
; i
++) {
324 memory_block_read_attrs(lsmem
, lsmem
->dirs
[i
]->d_name
, &blk
);
325 if (is_mergeable(lsmem
, &blk
)) {
326 lsmem
->blocks
[lsmem
->nblocks
- 1].count
++;
330 lsmem
->blocks
= xrealloc(lsmem
->blocks
, lsmem
->nblocks
* sizeof(blk
));
331 *&lsmem
->blocks
[lsmem
->nblocks
- 1] = blk
;
333 for (i
= 0; i
< lsmem
->nblocks
; i
++) {
334 if (lsmem
->blocks
[i
].state
== MEMORY_STATE_ONLINE
)
335 lsmem
->mem_online
+= lsmem
->block_size
* lsmem
->blocks
[i
].count
;
337 lsmem
->mem_offline
+= lsmem
->block_size
* lsmem
->blocks
[i
].count
;
341 static int memory_block_filter(const struct dirent
*de
)
343 if (strncmp("memory", de
->d_name
, 6))
345 return isdigit_string(de
->d_name
+ 6);
348 static void read_basic_info(struct lsmem
*lsmem
)
352 if (!path_exist(_PATH_SYS_MEMORY_BLOCK_SIZE
))
353 errx(EXIT_FAILURE
, _("This system does not support memory blocks"));
355 dir
= path_get(_PATH_SYS_MEMORY
);
357 err(EXIT_FAILURE
, _("Failed to read %s"), _PATH_SYS_MEMORY
);
359 lsmem
->ndirs
= scandir(dir
, &lsmem
->dirs
, memory_block_filter
, versionsort
);
360 if (lsmem
->ndirs
<= 0)
361 err(EXIT_FAILURE
, _("Failed to read %s"), _PATH_SYS_MEMORY
);
363 if (memory_block_get_node(lsmem
->dirs
[0]->d_name
) != -1)
364 lsmem
->have_nodes
= 1;
367 static void __attribute__((__noreturn__
)) usage(void)
372 fputs(USAGE_HEADER
, out
);
373 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
375 fputs(USAGE_SEPARATOR
, out
);
376 fputs(_("List the ranges of available memory with their online status.\n"), out
);
378 fputs(USAGE_OPTIONS
, out
);
379 fputs(_(" -J, --json use JSON output format\n"), out
);
380 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out
);
381 fputs(_(" -a, --all list each individual memory block\n"), out
);
382 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out
);
383 fputs(_(" -n, --noheadings don't print headings\n"), out
);
384 fputs(_(" -o, --output <list> output columns\n"), out
);
385 fputs(_(" -r, --raw use raw output format\n"), out
);
386 fputs(_(" -s, --sysroot <dir> use the specified directory as system root\n"), out
);
387 fputs(_(" --summary[=when] print summary information (never,always or only)\n"), out
);
389 fputs(USAGE_SEPARATOR
, out
);
390 printf(USAGE_HELP_OPTIONS(22));
392 fputs(USAGE_COLUMNS
, out
);
393 for (i
= 0; i
< ARRAY_SIZE(coldescs
); i
++)
394 fprintf(out
, " %10s %s\n", coldescs
[i
].name
, _(coldescs
[i
].help
));
396 printf(USAGE_MAN_TAIL("lsmem(1)"));
398 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
401 int main(int argc
, char **argv
)
403 struct lsmem _lsmem
= {
408 const char *outarg
= NULL
;
413 LSMEM_OPT_SUMARRY
= CHAR_MAX
+ 1
416 static const struct option longopts
[] = {
417 {"all", no_argument
, NULL
, 'a'},
418 {"bytes", no_argument
, NULL
, 'b'},
419 {"help", no_argument
, NULL
, 'h'},
420 {"json", no_argument
, NULL
, 'J'},
421 {"noheadings", no_argument
, NULL
, 'n'},
422 {"output", required_argument
, NULL
, 'o'},
423 {"pairs", no_argument
, NULL
, 'P'},
424 {"raw", no_argument
, NULL
, 'r'},
425 {"sysroot", required_argument
, NULL
, 's'},
426 {"version", no_argument
, NULL
, 'V'},
427 {"summary", optional_argument
, NULL
, LSMEM_OPT_SUMARRY
},
430 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
434 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
436 setlocale(LC_ALL
, "");
437 bindtextdomain(PACKAGE
, LOCALEDIR
);
439 atexit(close_stdout
);
441 while ((c
= getopt_long(argc
, argv
, "abhJno:Prs:V", longopts
, NULL
)) != -1) {
443 err_exclusive_options(c
, longopts
, excl
, excl_st
);
457 lsmem
->want_summary
= 0;
460 lsmem
->noheadings
= 1;
467 lsmem
->want_summary
= 0;
471 lsmem
->want_summary
= 0;
474 if(path_set_prefix(optarg
))
475 err(EXIT_FAILURE
, _("invalid argument to %s"), "--sysroot");
478 printf(UTIL_LINUX_VERSION
);
480 case LSMEM_OPT_SUMARRY
:
482 if (strcmp(optarg
, "never") == 0)
483 lsmem
->want_summary
= 0;
484 else if (strcmp(optarg
, "only") == 0)
485 lsmem
->want_table
= 0;
486 else if (strcmp(optarg
, "always") == 0)
487 lsmem
->want_summary
= 1;
489 errx(EXIT_FAILURE
, _("unsupported --summary argument"));
491 lsmem
->want_table
= 0;
494 errtryhelp(EXIT_FAILURE
);
498 if (argc
!= optind
) {
499 warnx(_("bad usage"));
500 errtryhelp(EXIT_FAILURE
);
503 if (lsmem
->want_table
+ lsmem
->want_summary
== 0)
504 errx(EXIT_FAILURE
, _("options --{raw,json,pairs} and --summary=only are mutually exclusive"));
506 /* Shortcut to avoid scols machinery on --summary=only */
507 if (lsmem
->want_table
== 0 && lsmem
->want_summary
) {
508 read_basic_info(lsmem
);
510 print_summary(lsmem
);
518 add_column(columns
, ncolumns
++, COL_RANGE
);
519 add_column(columns
, ncolumns
++, COL_SIZE
);
520 add_column(columns
, ncolumns
++, COL_STATE
);
521 add_column(columns
, ncolumns
++, COL_REMOVABLE
);
522 add_column(columns
, ncolumns
++, COL_BLOCK
);
525 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
526 &ncolumns
, column_name_to_id
) < 0)
534 if (!(lsmem
->table
= scols_new_table()))
535 errx(EXIT_FAILURE
, _("failed to initialize output table"));
536 scols_table_enable_raw(lsmem
->table
, lsmem
->raw
);
537 scols_table_enable_export(lsmem
->table
, lsmem
->export
);
538 scols_table_enable_json(lsmem
->table
, lsmem
->json
);
539 scols_table_enable_noheadings(lsmem
->table
, lsmem
->noheadings
);
542 scols_table_set_name(lsmem
->table
, "memory");
544 for (i
= 0; i
< ncolumns
; i
++) {
545 struct coldesc
*ci
= get_column_desc(i
);
546 if (!scols_table_new_column(lsmem
->table
, ci
->name
, ci
->whint
, ci
->flags
))
547 err(EXIT_FAILURE
, _("Failed to initialize output column"));
550 if (has_column(COL_STATE
))
551 lsmem
->want_state
= 1;
552 if (has_column(COL_NODE
))
553 lsmem
->want_node
= 1;
554 if (has_column(COL_REMOVABLE
))
555 lsmem
->want_removable
= 1;
558 * Read data and print output
560 read_basic_info(lsmem
);
563 if (lsmem
->want_table
) {
564 fill_scols_table(lsmem
);
565 scols_print_table(lsmem
->table
);
567 if (lsmem
->want_summary
)
571 if (lsmem
->want_summary
)
572 print_summary(lsmem
);
574 scols_unref_table(lsmem
->table
);