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