]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_info.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / tools / lxc_info.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
211e51e8 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
8765242a
CB
6#include <libgen.h>
7#include <limits.h>
5d42011a 8#include <stdbool.h>
8765242a 9#include <stdio.h>
ef6e34ee 10#include <stdlib.h>
8765242a 11#include <string.h>
0ad19a3f 12#include <sys/types.h>
d38dd64a 13#include <unistd.h>
0ad19a3f 14
a9ac16e2 15#include <lxc/lxccontainer.h>
f549edcc 16
4237c6ca 17#include "arguments.h"
d38dd64a 18#include "config.h"
469711c5 19#include "log.h"
20#include "utils.h"
21
22lxc_log_define(lxc_info, lxc);
0ad19a3f 23
211e51e8 24static bool ips;
5d42011a
DL
25static bool state;
26static bool pid;
b9d957c3
DE
27static bool stats;
28static bool humanize = true;
9c631ea7 29static char **key = NULL;
e69abbb1 30static int nr_keys = 0;
5444216b 31static int filter_count = 0;
5d42011a 32
469711c5 33static int my_parser(struct lxc_arguments *args, int c, char *arg)
5d42011a 34{
5135b555 35 char **newk;
469711c5 36
5d42011a 37 switch (c) {
9c631ea7 38 case 'c':
e69abbb1 39 newk = realloc(key, (nr_keys + 1) * sizeof(key[0]));
5135b555
SH
40 if (!newk)
41 return -1;
469711c5 42
5135b555 43 key = newk;
e69abbb1
CB
44 key[nr_keys] = arg;
45 nr_keys++;
9c631ea7 46 break;
5444216b
SG
47 case 'i': ips = true; filter_count += 1; break;
48 case 's': state = true; filter_count += 1; break;
49 case 'p': pid = true; filter_count += 1; break;
50 case 'S': stats = true; filter_count += 5; break;
b9d957c3 51 case 'H': humanize = false; break;
5d42011a
DL
52 }
53 return 0;
54}
55
4237c6ca 56static const struct option my_longopts[] = {
9c631ea7 57 {"config", required_argument, 0, 'c'},
211e51e8 58 {"ips", no_argument, 0, 'i'},
5d42011a
DL
59 {"state", no_argument, 0, 's'},
60 {"pid", no_argument, 0, 'p'},
b9d957c3
DE
61 {"stats", no_argument, 0, 'S'},
62 {"no-humanize", no_argument, 0, 'H'},
5d42011a 63 LXC_COMMON_OPTIONS,
4237c6ca
MN
64};
65
66static struct lxc_arguments my_args = {
67 .progname = "lxc-info",
68 .help = "\
69--name=NAME\n\
70\n\
5d42011a 71lxc-info display some information about a container with the identifier NAME\n\
4237c6ca
MN
72\n\
73Options :\n\
5e8757ed 74 -n, --name=NAME NAME of the container\n\
9c631ea7 75 -c, --config=KEY show configuration variable KEY from running container\n\
211e51e8 76 -i, --ips shows the IP addresses\n\
b917ef75 77 -p, --pid shows the process id of the init container\n\
b9d957c3
DE
78 -S, --stats shows usage stats\n\
79 -H, --no-humanize shows stats as raw numbers, not humanized\n\
50b737a3
WB
80 -s, --state shows the state of the container\n\
81 --rcfile=FILE Load configuration file FILE\n",
5444216b 82 .name = NULL,
4237c6ca 83 .options = my_longopts,
5d42011a 84 .parser = my_parser,
4237c6ca
MN
85 .checker = NULL,
86};
0ad19a3f 87
b9d957c3 88static void str_chomp(char *buf)
0ad19a3f 89{
b9d957c3 90 char *ch;
0ad19a3f 91
b9d957c3
DE
92 /* remove trailing whitespace from buf */
93 for(ch = &buf[strlen(buf)-1];
94 ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' ');
95 ch--)
96 *ch = '\0';
97}
a9ac16e2 98
b9d957c3
DE
99static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
100{
101 if (val > 1 << 30) {
0b3410ee
CB
102 (void)snprintf(buf, bufsz, "%u.%2.2u GiB",
103 (unsigned int)(val >> 30),
104 (unsigned int)(val & ((1 << 30) - 1)) / 10737419);
b9d957c3 105 } else if (val > 1 << 20) {
6fc7d8b6 106 unsigned int x = val + 5243; /* for rounding */
0b3410ee
CB
107 (void)snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20,
108 ((x & ((1 << 20) - 1)) * 100) >> 20);
b9d957c3 109 } else if (val > 1 << 10) {
6fc7d8b6 110 unsigned int x = val + 5; /* for rounding */
0b3410ee
CB
111 (void)snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10,
112 ((x & ((1 << 10) - 1)) * 100) >> 10);
b9d957c3 113 } else {
0b3410ee 114 (void)snprintf(buf, bufsz, "%u bytes", (unsigned int)val);
b9d957c3
DE
115 }
116}
0ad19a3f 117
b9d957c3
DE
118static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
119{
120 unsigned long long val;
121 char *end = NULL;
f5abd74d 122
b9d957c3
DE
123 val = strtoull(iobuf, &end, 0);
124 if (humanize) {
125 if (*end == '\0' || *end == '\n')
126 size_humanize(val, iobuf, iobufsz);
127 else
128 *iobuf = '\0';
129 }
130 return val;
131}
132
8ac18377 133static void print_net_stats(struct lxc_container *c)
b9d957c3
DE
134{
135 int rc,netnr;
136 unsigned long long rx_bytes = 0, tx_bytes = 0;
137 char *ifname, *type;
138 char path[PATH_MAX];
139 char buf[256];
140
141 for(netnr = 0; ;netnr++) {
7fa3f2e9 142 sprintf(buf, "lxc.net.%d.type", netnr);
469711c5 143
8ac18377 144 type = c->get_running_config_item(c, buf);
b9d957c3
DE
145 if (!type)
146 break;
a9ac16e2 147
b9d957c3 148 if (!strcmp(type, "veth")) {
7fa3f2e9 149 sprintf(buf, "lxc.net.%d.veth.pair", netnr);
b9d957c3 150 } else {
7fa3f2e9 151 sprintf(buf, "lxc.net.%d.link", netnr);
b9d957c3
DE
152 }
153 free(type);
469711c5 154
8ac18377 155 ifname = c->get_running_config_item(c, buf);
b9d957c3
DE
156 if (!ifname)
157 return;
469711c5 158
b9d957c3 159 printf("%-15s %s\n", "Link:", ifname);
d52f2fd3 160 fflush(stdout);
b9d957c3
DE
161
162 /* XXX: tx and rx are reversed from the host vs container
163 * perspective, print them from the container perspective
164 */
0b3410ee
CB
165 rc = snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname);
166 if (rc < 0 || (size_t)rc >= sizeof(path))
167 return;
168
b9d957c3
DE
169 rc = lxc_read_from_file(path, buf, sizeof(buf));
170 if (rc > 0) {
a5ec15d9 171 buf[rc - 1] = '\0';
b9d957c3
DE
172 str_chomp(buf);
173 rx_bytes = str_size_humanize(buf, sizeof(buf));
174 printf("%-15s %s\n", " TX bytes:", buf);
d52f2fd3 175 fflush(stdout);
b9d957c3
DE
176 }
177
0b3410ee
CB
178 rc = snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname);
179 if (rc < 0 || (size_t)rc >= sizeof(path))
180 return;
181
b9d957c3
DE
182 rc = lxc_read_from_file(path, buf, sizeof(buf));
183 if (rc > 0) {
a5ec15d9 184 buf[rc - 1] = '\0';
b9d957c3
DE
185 str_chomp(buf);
186 tx_bytes = str_size_humanize(buf, sizeof(buf));
187 printf("%-15s %s\n", " RX bytes:", buf);
d52f2fd3 188 fflush(stdout);
b9d957c3
DE
189 }
190
191 sprintf(buf, "%llu", rx_bytes + tx_bytes);
192 str_size_humanize(buf, sizeof(buf));
193 printf("%-15s %s\n", " Total bytes:", buf);
d52f2fd3 194 fflush(stdout);
b9d957c3
DE
195 free(ifname);
196 }
197}
198
199static void print_stats(struct lxc_container *c)
200{
201 int i, ret;
1bbc6275 202 char buf[4096];
b9d957c3
DE
203
204 ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
8765242a 205 if (ret > 0 && (size_t)ret < sizeof(buf)) {
b9d957c3
DE
206 str_chomp(buf);
207 if (humanize) {
208 float seconds = strtof(buf, NULL) / 1000000000.0;
209 printf("%-15s %.2f seconds\n", "CPU use:", seconds);
210 } else {
211 printf("%-15s %s\n", "CPU use:", buf);
212 }
d52f2fd3 213 fflush(stdout);
b9d957c3
DE
214 }
215
216 ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
8765242a 217 if (ret > 0 && (size_t)ret < sizeof(buf)) {
b9d957c3
DE
218 char *ch;
219
220 /* put ch on last "Total" line */
221 str_chomp(buf);
222 for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--)
223 ;
224 if (*ch == '\n')
225 ch++;
226
227 if (strncmp(ch, "Total", 5) == 0) {
228 ch += 6;
229 memmove(buf, ch, strlen(ch)+1);
230 str_size_humanize(buf, sizeof(buf));
231 printf("%-15s %s\n", "BlkIO use:", buf);
232 }
d52f2fd3 233 fflush(stdout);
b9d957c3
DE
234 }
235
236 static const struct {
237 const char *name;
238 const char *file;
239 } lxstat[] = {
240 { "Memory use:", "memory.usage_in_bytes" },
241 { "KMem use:", "memory.kmem.usage_in_bytes" },
242 { NULL, NULL },
243 };
244
245 for (i = 0; lxstat[i].name; i++) {
246 ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
8765242a 247 if (ret > 0 && (size_t)ret < sizeof(buf)) {
b9d957c3
DE
248 str_chomp(buf);
249 str_size_humanize(buf, sizeof(buf));
250 printf("%-15s %s\n", lxstat[i].name, buf);
d52f2fd3 251 fflush(stdout);
b9d957c3
DE
252 }
253 }
254}
255
a7547c5c 256static void print_info_msg_int(const char *k, int value)
5444216b
SG
257{
258 if (humanize)
a7547c5c 259 printf("%-15s %d\n", k, value);
5444216b
SG
260 else {
261 if (filter_count == 1)
262 printf("%d\n", value);
263 else
a7547c5c 264 printf("%-15s %d\n", k, value);
5444216b 265 }
d52f2fd3 266 fflush(stdout);
5444216b
SG
267}
268
a7547c5c 269static void print_info_msg_str(const char *k, const char *value)
5444216b
SG
270{
271 if (humanize)
a7547c5c 272 printf("%-15s %s\n", k, value);
5444216b
SG
273 else {
274 if (filter_count == 1)
275 printf("%s\n", value);
276 else
a7547c5c 277 printf("%-15s %s\n", k, value);
5444216b 278 }
d52f2fd3 279 fflush(stdout);
5444216b
SG
280}
281
b9d957c3
DE
282static int print_info(const char *name, const char *lxcpath)
283{
284 int i;
285 struct lxc_container *c;
286
287 c = lxc_container_new(name, lxcpath);
5444216b 288 if (!c) {
915614c7
SH
289 fprintf(stderr, "Failure to retrieve information on %s:%s\n", lxcpath ? lxcpath : "null",
290 name ? name : "null");
a9ac16e2 291 return -1;
5444216b 292 }
50b737a3
WB
293
294 if (my_args.rcfile) {
295 c->clear_config(c);
469711c5 296
50b737a3
WB
297 if (!c->load_config(c, my_args.rcfile)) {
298 fprintf(stderr, "Failed to load rcfile\n");
299 lxc_container_put(c);
300 return -1;
301 }
469711c5 302
6118210e
WB
303 c->configfile = strdup(my_args.rcfile);
304 if (!c->configfile) {
305 fprintf(stderr, "Out of memory setting new config filename\n");
306 lxc_container_put(c);
307 return -1;
308 }
50b737a3 309 }
51cab631 310
f5abd74d
SG
311 if (!c->may_control(c)) {
312 fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
f3e52710 313 lxc_container_put(c);
f5abd74d
SG
314 return -1;
315 }
316
5444216b
SG
317 if (!c->is_running(c) && !c->is_defined(c)) {
318 fprintf(stderr, "%s doesn't exist\n", c->name);
319 lxc_container_put(c);
320 return -1;
321 }
5d42011a 322
e69abbb1 323 if (!state && !pid && !ips && !stats && nr_keys <= 0) {
5444216b
SG
324 state = pid = ips = stats = true;
325 print_info_msg_str("Name:", c->name);
326 }
5d42011a 327
469711c5 328 if (state)
5444216b 329 print_info_msg_str("State:", c->state(c));
0ad19a3f 330
2177dbc7
SG
331 if (c->is_running(c)) {
332 if (pid) {
333 pid_t initpid;
ef6e34ee 334
2177dbc7
SG
335 initpid = c->init_pid(c);
336 if (initpid >= 0)
337 print_info_msg_int("PID:", initpid);
338 }
211e51e8 339
2177dbc7 340 if (ips) {
e2426f0b 341 fflush(stdout);
469711c5 342
2177dbc7
SG
343 char **addresses = c->get_ips(c, NULL, NULL, 0);
344 if (addresses) {
345 char *address;
346 i = 0;
469711c5 347
2177dbc7
SG
348 while (addresses[i]) {
349 address = addresses[i];
350 print_info_msg_str("IP:", address);
351 i++;
352 }
bd79cb22 353 }
211e51e8 354 }
ef6e34ee 355 }
0ad19a3f 356
b9d957c3
DE
357 if (stats) {
358 print_stats(c);
8ac18377 359 print_net_stats(c);
b9d957c3
DE
360 }
361
e69abbb1 362 for(i = 0; i < nr_keys; i++) {
a9ac16e2
SG
363 int len = c->get_config_item(c, key[i], NULL, 0);
364
2eee1b96 365 if (len > 0) {
a9ac16e2 366 char *val = (char*) malloc(sizeof(char)*len + 1);
9c631ea7 367
a9ac16e2
SG
368 if (c->get_config_item(c, key[i], val, len + 1) != len) {
369 fprintf(stderr, "unable to read %s from configuration\n", key[i]);
370 } else {
e69abbb1 371 if (!humanize && nr_keys == 1)
44fa8e7e
SG
372 printf("%s\n", val);
373 else
374 printf("%s = %s\n", key[i], val);
a9ac16e2 375 }
469711c5 376
9c631ea7 377 free(val);
2eee1b96 378 } else if (len == 0) {
e69abbb1 379 if (!humanize && nr_keys == 1)
44fa8e7e
SG
380 printf("\n");
381 else
382 printf("%s =\n", key[i]);
9c631ea7 383 } else {
2eee1b96 384 fprintf(stderr, "%s invalid\n", key[i]);
9c631ea7 385 }
d52f2fd3 386 fflush(stdout);
9c631ea7
DE
387 }
388
f3e52710 389 lxc_container_put(c);
0ad19a3f 390 return 0;
391}
b9d957c3
DE
392
393int main(int argc, char *argv[])
394{
5444216b 395 int ret = EXIT_FAILURE;
73b910a3 396 struct lxc_log log;
b9d957c3
DE
397
398 if (lxc_arguments_parse(&my_args, argc, argv))
b52b0595 399 exit(ret);
b9d957c3 400
f6d79ec1
CB
401 /* Only create log if explicitly instructed */
402 if (my_args.log_file || my_args.log_priority) {
403 log.name = my_args.name;
404 log.file = my_args.log_file;
405 log.level = my_args.log_priority;
406 log.prefix = my_args.progname;
407 log.quiet = my_args.quiet;
408 log.lxcpath = my_args.lxcpath[0];
409
410 if (lxc_log_init(&log))
411 exit(ret);
412 }
b9d957c3 413
5444216b
SG
414 if (print_info(my_args.name, my_args.lxcpath[0]) == 0)
415 ret = EXIT_SUCCESS;
b9d957c3 416
b52b0595 417 exit(ret);
b9d957c3 418}