]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_info.c
tree-wide: fix lxc header inclusion
[mirror_lxc.git] / src / lxc / tools / lxc_info.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <libgen.h>
7 #include <limits.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14
15 #include "lxc.h"
16
17 #include "arguments.h"
18 #include "config.h"
19 #include "log.h"
20 #include "utils.h"
21
22 lxc_log_define(lxc_info, lxc);
23
24 static bool ips;
25 static bool state;
26 static bool pid;
27 static bool stats;
28 static bool humanize = true;
29 static char **key = NULL;
30 static int nr_keys = 0;
31 static int filter_count = 0;
32
33 static int my_parser(struct lxc_arguments *args, int c, char *arg)
34 {
35 char **newk;
36
37 switch (c) {
38 case 'c':
39 newk = realloc(key, (nr_keys + 1) * sizeof(key[0]));
40 if (!newk)
41 return -1;
42
43 key = newk;
44 key[nr_keys] = arg;
45 nr_keys++;
46 break;
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;
51 case 'H': humanize = false; break;
52 }
53 return 0;
54 }
55
56 static const struct option my_longopts[] = {
57 {"config", required_argument, 0, 'c'},
58 {"ips", no_argument, 0, 'i'},
59 {"state", no_argument, 0, 's'},
60 {"pid", no_argument, 0, 'p'},
61 {"stats", no_argument, 0, 'S'},
62 {"no-humanize", no_argument, 0, 'H'},
63 LXC_COMMON_OPTIONS,
64 };
65
66 static struct lxc_arguments my_args = {
67 .progname = "lxc-info",
68 .help = "\
69 --name=NAME\n\
70 \n\
71 lxc-info display some information about a container with the identifier NAME\n\
72 \n\
73 Options :\n\
74 -n, --name=NAME NAME of the container\n\
75 -c, --config=KEY show configuration variable KEY from running container\n\
76 -i, --ips shows the IP addresses\n\
77 -p, --pid shows the process id of the init container\n\
78 -S, --stats shows usage stats\n\
79 -H, --no-humanize shows stats as raw numbers, not humanized\n\
80 -s, --state shows the state of the container\n\
81 --rcfile=FILE Load configuration file FILE\n",
82 .name = NULL,
83 .options = my_longopts,
84 .parser = my_parser,
85 .checker = NULL,
86 };
87
88 static void str_chomp(char *buf)
89 {
90 char *ch;
91
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 }
98
99 static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
100 {
101 if (val > 1 << 30) {
102 (void)snprintf(buf, bufsz, "%u.%2.2u GiB",
103 (unsigned int)(val >> 30),
104 (unsigned int)(val & ((1 << 30) - 1)) / 10737419);
105 } else if (val > 1 << 20) {
106 unsigned int x = val + 5243; /* for rounding */
107 (void)snprintf(buf, bufsz, "%u.%2.2u MiB", x >> 20,
108 ((x & ((1 << 20) - 1)) * 100) >> 20);
109 } else if (val > 1 << 10) {
110 unsigned int x = val + 5; /* for rounding */
111 (void)snprintf(buf, bufsz, "%u.%2.2u KiB", x >> 10,
112 ((x & ((1 << 10) - 1)) * 100) >> 10);
113 } else {
114 (void)snprintf(buf, bufsz, "%u bytes", (unsigned int)val);
115 }
116 }
117
118 static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
119 {
120 unsigned long long val;
121 char *end = NULL;
122
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
133 static void print_net_stats(struct lxc_container *c)
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++) {
142 sprintf(buf, "lxc.net.%d.type", netnr);
143
144 type = c->get_running_config_item(c, buf);
145 if (!type)
146 break;
147
148 if (!strcmp(type, "veth")) {
149 sprintf(buf, "lxc.net.%d.veth.pair", netnr);
150 } else {
151 sprintf(buf, "lxc.net.%d.link", netnr);
152 }
153 free(type);
154
155 ifname = c->get_running_config_item(c, buf);
156 if (!ifname)
157 return;
158
159 printf("%-15s %s\n", "Link:", ifname);
160 fflush(stdout);
161
162 /* XXX: tx and rx are reversed from the host vs container
163 * perspective, print them from the container perspective
164 */
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
169 rc = lxc_read_from_file(path, buf, sizeof(buf));
170 if (rc > 0) {
171 buf[rc - 1] = '\0';
172 str_chomp(buf);
173 rx_bytes = str_size_humanize(buf, sizeof(buf));
174 printf("%-15s %s\n", " TX bytes:", buf);
175 fflush(stdout);
176 }
177
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
182 rc = lxc_read_from_file(path, buf, sizeof(buf));
183 if (rc > 0) {
184 buf[rc - 1] = '\0';
185 str_chomp(buf);
186 tx_bytes = str_size_humanize(buf, sizeof(buf));
187 printf("%-15s %s\n", " RX bytes:", buf);
188 fflush(stdout);
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);
194 fflush(stdout);
195 free(ifname);
196 }
197 }
198
199 static void print_stats(struct lxc_container *c)
200 {
201 int i, ret;
202 char buf[4096];
203
204 ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
205 if (ret > 0 && (size_t)ret < sizeof(buf)) {
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 }
213 fflush(stdout);
214 }
215
216 ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
217 if (ret > 0 && (size_t)ret < sizeof(buf)) {
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 }
233 fflush(stdout);
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));
247 if (ret > 0 && (size_t)ret < sizeof(buf)) {
248 str_chomp(buf);
249 str_size_humanize(buf, sizeof(buf));
250 printf("%-15s %s\n", lxstat[i].name, buf);
251 fflush(stdout);
252 }
253 }
254 }
255
256 static void print_info_msg_int(const char *k, int value)
257 {
258 if (humanize)
259 printf("%-15s %d\n", k, value);
260 else {
261 if (filter_count == 1)
262 printf("%d\n", value);
263 else
264 printf("%-15s %d\n", k, value);
265 }
266 fflush(stdout);
267 }
268
269 static void print_info_msg_str(const char *k, const char *value)
270 {
271 if (humanize)
272 printf("%-15s %s\n", k, value);
273 else {
274 if (filter_count == 1)
275 printf("%s\n", value);
276 else
277 printf("%-15s %s\n", k, value);
278 }
279 fflush(stdout);
280 }
281
282 static 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);
288 if (!c) {
289 fprintf(stderr, "Failure to retrieve information on %s:%s\n", lxcpath ? lxcpath : "null",
290 name ? name : "null");
291 return -1;
292 }
293
294 if (my_args.rcfile) {
295 c->clear_config(c);
296
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 }
302
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 }
309 }
310
311 if (!c->may_control(c)) {
312 fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
313 lxc_container_put(c);
314 return -1;
315 }
316
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 }
322
323 if (!state && !pid && !ips && !stats && nr_keys <= 0) {
324 state = pid = ips = stats = true;
325 print_info_msg_str("Name:", c->name);
326 }
327
328 if (state)
329 print_info_msg_str("State:", c->state(c));
330
331 if (c->is_running(c)) {
332 if (pid) {
333 pid_t initpid;
334
335 initpid = c->init_pid(c);
336 if (initpid >= 0)
337 print_info_msg_int("PID:", initpid);
338 }
339
340 if (ips) {
341 fflush(stdout);
342
343 char **addresses = c->get_ips(c, NULL, NULL, 0);
344 if (addresses) {
345 char *address;
346 i = 0;
347
348 while (addresses[i]) {
349 address = addresses[i];
350 print_info_msg_str("IP:", address);
351 i++;
352 }
353 }
354 }
355 }
356
357 if (stats) {
358 print_stats(c);
359 print_net_stats(c);
360 }
361
362 for(i = 0; i < nr_keys; i++) {
363 int len = c->get_config_item(c, key[i], NULL, 0);
364
365 if (len > 0) {
366 char *val = (char*) malloc(sizeof(char)*len + 1);
367
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 {
371 if (!humanize && nr_keys == 1)
372 printf("%s\n", val);
373 else
374 printf("%s = %s\n", key[i], val);
375 }
376
377 free(val);
378 } else if (len == 0) {
379 if (!humanize && nr_keys == 1)
380 printf("\n");
381 else
382 printf("%s =\n", key[i]);
383 } else {
384 fprintf(stderr, "%s invalid\n", key[i]);
385 }
386 fflush(stdout);
387 }
388
389 lxc_container_put(c);
390 return 0;
391 }
392
393 int main(int argc, char *argv[])
394 {
395 int ret = EXIT_FAILURE;
396 struct lxc_log log;
397
398 if (lxc_arguments_parse(&my_args, argc, argv))
399 exit(ret);
400
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 }
413
414 if (print_info(my_args.name, my_args.lxcpath[0]) == 0)
415 ret = EXIT_SUCCESS;
416
417 exit(ret);
418 }