]>
Commit | Line | Data |
---|---|---|
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 | ||
22 | lxc_log_define(lxc_info, lxc); | |
0ad19a3f | 23 | |
211e51e8 | 24 | static bool ips; |
5d42011a DL |
25 | static bool state; |
26 | static bool pid; | |
b9d957c3 DE |
27 | static bool stats; |
28 | static bool humanize = true; | |
9c631ea7 | 29 | static char **key = NULL; |
e69abbb1 | 30 | static int nr_keys = 0; |
5444216b | 31 | static int filter_count = 0; |
5d42011a | 32 | |
469711c5 | 33 | static 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 | 56 | static 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 | ||
66 | static struct lxc_arguments my_args = { | |
67 | .progname = "lxc-info", | |
68 | .help = "\ | |
69 | --name=NAME\n\ | |
70 | \n\ | |
5d42011a | 71 | lxc-info display some information about a container with the identifier NAME\n\ |
4237c6ca MN |
72 | \n\ |
73 | Options :\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 | 88 | static 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 |
99 | static 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 |
118 | static 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 | 133 | static 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 | ||
199 | static 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 | 256 | static 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 | 269 | static 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 |
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); | |
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 | |
393 | int 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 | } |