diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index ae58e5e3ba..8ba375df9c 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -953,9 +953,7 @@
Note that in contrast to the on-disk ringbuffer logfile this file
will keep growing potentially filling up the users disks if not
rotated and deleted. This problem can also be avoided by using the
- in-memory ringbuffer options
- and
- .
+ in-memory ringbuffer option .
@@ -974,8 +972,7 @@
Users wishing to prevent the console log file from filling the
disk should rotate the logfile and delete it if unneeded. This
problem can also be avoided by using the in-memory ringbuffer
- options and
- .
+ option .
diff --git a/src/lxc/caps.c b/src/lxc/caps.c
index 206488ff43..f6c5770ed3 100644
--- a/src/lxc/caps.c
+++ b/src/lxc/caps.c
@@ -88,7 +88,7 @@ int lxc_caps_up(void)
int lxc_ambient_caps_up(void)
{
call_cleaner(cap_free) cap_t caps = NULL;
- __do_free char *cap_names = NULL;
+ char *cap_names = NULL;
int ret;
cap_value_t cap;
cap_value_t last_cap = CAP_LAST_CAP;
@@ -135,6 +135,7 @@ int lxc_ambient_caps_up(void)
return log_warn_errno(0, errno, "Failed to convert capabilities %d", cap);
TRACE("Raised %s in inheritable and ambient capability set", cap_names);
+ cap_free(cap_names);
return 0;
}
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index ea3a31bb4a..ba3d988b5b 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -3533,23 +3533,22 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
const char *newtype, int flags, const char *bdevdata,
uint64_t newsize)
{
- struct lxc_storage *bdev;
+ char *bdev_src = NULL;
bool need_rdep;
if (should_default_to_snapshot(c0, c))
flags |= LXC_CLONE_SNAPSHOT;
- bdev = storage_copy(c0, c->name, c->config_path, newtype, flags,
+ bdev_src = storage_copy(c0, c->name, c->config_path, newtype, flags,
bdevdata, newsize, &need_rdep);
- if (!bdev) {
+ if (!bdev_src) {
ERROR("Error copying storage.");
return -1;
}
/* Set new rootfs. */
free(c->lxc_conf->rootfs.path);
- c->lxc_conf->rootfs.path = strdup(bdev->src);
- storage_put(bdev);
+ c->lxc_conf->rootfs.path = bdev_src;
if (!c->lxc_conf->rootfs.path) {
ERROR("Out of memory while setting storage path.");
diff --git a/src/lxc/storage/storage.c b/src/lxc/storage/storage.c
index 2490d3c333..25f6e353b1 100644
--- a/src/lxc/storage/storage.c
+++ b/src/lxc/storage/storage.c
@@ -296,12 +296,13 @@ bool storage_can_backup(struct lxc_conf *conf)
/* If we're not snapshotting, then storage_copy becomes a simple case of mount
* the original, mount the new, and rsync the contents.
*/
-struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
- const char *lxcpath, const char *bdevtype,
- int flags, const char *bdevdata,
- uint64_t newsize, bool *needs_rdep)
+char *storage_copy(struct lxc_container *c, const char *cname,
+ const char *lxcpath, const char *bdevtype,
+ int flags, const char *bdevdata,
+ uint64_t newsize, bool *needs_rdep)
{
int ret;
+ char *ret_str;
const char *src_no_prefix;
struct lxc_storage *new, *orig;
bool snap = (flags & LXC_CLONE_SNAPSHOT);
@@ -319,6 +320,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
.dfd_host = -EBADF,
.fd_path_pin = -EBADF,
.dfd_idmapped = -EBADF,
+ .storage = NULL,
};
if (!src) {
@@ -421,6 +423,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
}
TRACE("Initialized %s storage driver", new->type);
new->rootfs = &new_rootfs;
+ new_rootfs.storage = new;
/* create new paths */
ret = new->ops->clone_paths(orig, new, oldname, cname, oldpath, lxcpath,
@@ -516,17 +519,16 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
}
on_success:
- /* The only caller, copy_storage, doesn't ever close this. */
- close_prot_errno_disarm(new_rootfs.dfd_idmapped);
+ ret_str = must_copy_string(new->src);
lxc_storage_put(c->lxc_conf);
+ put_lxc_rootfs(&new_rootfs, true);
- return new;
+ return ret_str;
on_error_put_new:
- storage_put(new);
+ put_lxc_rootfs(&new_rootfs, true);
on_error_put_orig:
- close_prot_errno_disarm(new_rootfs.dfd_idmapped);
lxc_storage_put(c->lxc_conf);
return NULL;
diff --git a/src/lxc/storage/storage.h b/src/lxc/storage/storage.h
index 148023bad1..2fb68b361f 100644
--- a/src/lxc/storage/storage.h
+++ b/src/lxc/storage/storage.h
@@ -103,10 +103,10 @@ struct lxc_storage {
__hidden extern bool storage_lxc_is_dir(struct lxc_conf *conf);
__hidden extern bool storage_can_backup(struct lxc_conf *conf);
__hidden extern struct lxc_storage *storage_init(struct lxc_conf *conf);
-__hidden extern struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
- const char *lxcpath, const char *bdevtype,
- int flags, const char *bdevdata, uint64_t newsize,
- bool *needs_rdep);
+__hidden extern char *storage_copy(struct lxc_container *c, const char *cname,
+ const char *lxcpath, const char *bdevtype,
+ int flags, const char *bdevdata, uint64_t newsize,
+ bool *needs_rdep);
__hidden extern struct lxc_storage *storage_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs,
const struct lxc_conf *conf);
diff --git a/src/lxc/tools/lxc_autostart.c b/src/lxc/tools/lxc_autostart.c
index 608398ae08..ad32c0224f 100644
--- a/src/lxc/tools/lxc_autostart.c
+++ b/src/lxc/tools/lxc_autostart.c
@@ -309,7 +309,7 @@ static int toss_list(struct lxc_list *c_groups_list)
int __attribute__((weak, alias("lxc_autostart_main"))) main(int argc, char *argv[]);
int lxc_autostart_main(int argc, char *argv[])
{
- int count = 0, failed = 0, i = 0, ret = 0;
+ int count = 0, failed = 0, attempted = 0, i = 0, ret = 0;
struct lxc_list *cmd_group;
struct lxc_container **containers = NULL;
struct lxc_list **c_groups_lists = NULL;
@@ -408,6 +408,7 @@ int lxc_autostart_main(int argc, char *argv[])
}
/* We have a candidate container to process */
+ attempted++;
c->want_daemonize(c, 1);
if (my_args.shutdown) {
@@ -506,7 +507,7 @@ int lxc_autostart_main(int argc, char *argv[])
toss_list(cmd_groups_list);
free(containers);
- if (failed > 0 && failed == count)
+ if (failed > 0 && failed == attempted)
exit(EXIT_FAILURE); /* Total failure */
else if (failed > 0)
exit(2); /* Partial failure */
diff --git a/src/lxc/tools/lxc_info.c b/src/lxc/tools/lxc_info.c
index 681b597521..cfcb6aff0a 100644
--- a/src/lxc/tools/lxc_info.c
+++ b/src/lxc/tools/lxc_info.c
@@ -16,6 +16,7 @@
#include "arguments.h"
#include "log.h"
#include "utils.h"
+#include "macro.h"
lxc_log_define(lxc_info, lxc);
@@ -196,14 +197,158 @@ static void print_net_stats(struct lxc_container *c)
}
}
-static void print_stats(struct lxc_container *c)
+static int cg2_stat_iter(struct lxc_container *c, const char *file, void *cookie,
+ int (*iter)(char *key, char *val, void *cookie))
{
- int i, ret;
+ char *fileptr, *lineptr, *line, *metric, *value;
char buf[4096];
+ int ret;
+
+ ret = c->get_cgroup_item(c, file, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ if ((size_t)ret >= sizeof(buf)) {
+ fprintf(stderr, "Internal buffer too small to read '%s'\n", file);
+ return -EMSGSIZE;
+ }
+
+ for (line = strtok_r(buf, "\n", &fileptr); line; line = strtok_r(NULL, "\n", &fileptr)) {
+ metric = strtok_r(line, " ", &lineptr);
+ if (!metric)
+ goto err;
+
+ value = strtok_r(NULL, " ", &lineptr);
+ if (!value)
+ goto err;
+
+ ret = iter(metric, value, cookie);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+
+err:
+ fprintf(stderr, "Unexpected syntax of memory.stat\n");
+ return -EIO;
+}
+
+static int cg1_cpu_usage(struct lxc_container *c, char *usage)
+{
+ char buf[64];
+ int ret;
ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
if (ret > 0 && (size_t)ret < sizeof(buf)) {
str_chomp(buf);
+ strcpy(usage, buf);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int cg2_cpu_usage_iter(char *metric, char *value, void *cookie)
+{
+ if (strcmp(metric, "usage_usec"))
+ return 0;
+
+ strcpy(cookie, value);
+ strcat(cookie, "000");
+ return 1;
+}
+
+static int cg2_cpu_usage(struct lxc_container *c, char *usage)
+{
+ int ret;
+
+ ret = cg2_stat_iter(c, "cpu.stat", usage, cg2_cpu_usage_iter);
+ return ret == 1 ? 0 : ret ?: -ESRCH;
+}
+
+static int search_array(const char *array[], const char *needle, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (!strcmp(array[i], needle))
+ return 1;
+ return 0;
+}
+
+struct cg2_mem_usage_s {
+ unsigned long long user, kernel;
+};
+
+static int cg2_mem_usage_iter(char *metric, char *value, void *cookie)
+{
+ const char *kernel_fields[] = { "kernel_stack", "pagetables", "sock", "slab" };
+ const char *user_fields[] = { "anon", "file" };
+ struct cg2_mem_usage_s *usage = cookie;
+ unsigned long long numvalue;
+ char *end;
+
+ numvalue = strtoull(value, &end, 10);
+ if (numvalue == ULLONG_MAX || *end) {
+ fprintf(stderr, "Unexpected syntax of memory.stat\n");
+ return -EIO;
+ }
+
+ if (search_array(kernel_fields, metric, ARRAY_SIZE(kernel_fields)))
+ usage->kernel += numvalue;
+ else if (search_array(user_fields, metric, ARRAY_SIZE(user_fields)))
+ usage->user += numvalue;
+ return 0;
+}
+
+static int cg2_mem_usage(struct lxc_container *c, char *user, char *kernel)
+{
+ struct cg2_mem_usage_s usage = { 0 };
+ int ret;
+
+ ret = cg2_stat_iter(c, "memory.stat", &usage, cg2_mem_usage_iter);
+ if (ret < 0)
+ return ret;
+
+ sprintf(kernel, "%llu", usage.kernel);
+ sprintf(user, "%llu", usage.user);
+
+ return 0;
+}
+
+static int cg1_mem_usage(struct lxc_container *c, char *user, char *kernel)
+{
+ struct {
+ const char *file;
+ char *target;
+ } lxstat[] = {
+ { "memory.usage_in_bytes", user },
+ { "memory.kmem.usage_in_bytes", kernel },
+ };
+ int ret, i, match = 0;
+ char buf[64];
+
+ for (i = 0; i < (int)ARRAY_SIZE(lxstat); i++) {
+ ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
+ if (ret > 0 && (size_t)ret < sizeof(buf)) {
+ str_chomp(buf);
+ strcpy(lxstat[i].target, buf);
+ match++;
+ }
+ }
+
+ return match == ARRAY_SIZE(lxstat) ? 0 : -ESRCH;
+}
+
+static void print_stats(struct lxc_container *c)
+{
+ int ret;
+ char buf[4096];
+ char *user = buf;
+ char *kernel = buf + sizeof(buf) / 2;
+
+ if (!cg1_cpu_usage(c, buf) || !cg2_cpu_usage(c, buf)) {
if (humanize) {
float seconds = strtof(buf, NULL) / 1000000000.0;
printf("%-15s %.2f seconds\n", "CPU use:", seconds);
@@ -233,23 +378,12 @@ static void print_stats(struct lxc_container *c)
fflush(stdout);
}
- static const struct {
- const char *name;
- const char *file;
- } lxstat[] = {
- { "Memory use:", "memory.usage_in_bytes" },
- { "KMem use:", "memory.kmem.usage_in_bytes" },
- { NULL, NULL },
- };
-
- for (i = 0; lxstat[i].name; i++) {
- ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
- if (ret > 0 && (size_t)ret < sizeof(buf)) {
- str_chomp(buf);
- str_size_humanize(buf, sizeof(buf));
- printf("%-15s %s\n", lxstat[i].name, buf);
- fflush(stdout);
- }
+ if (!cg1_mem_usage(c, user, kernel) || !cg2_mem_usage(c, user, kernel)) {
+ str_size_humanize(user, sizeof(buf) / 2 - 1);
+ printf("%-15s %s\n", "Memory use:", user);
+ str_size_humanize(kernel, sizeof(buf) / 2 - 1);
+ printf("%-15s %s\n", "KMem use:", kernel);
+ fflush(stdout);
}
}
diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
index aa6e7209e3..e67349d2a5 100644
--- a/src/lxc/tools/lxc_top.c
+++ b/src/lxc/tools/lxc_top.c
@@ -22,7 +22,6 @@
#include "mainloop.h"
#include "utils.h"
-#define USER_HZ 100
#define ESC "\033"
#define TERMCLEAR ESC "[H" ESC "[J"
#define TERMNORM ESC "[0m"
@@ -35,16 +34,32 @@ struct blkio_stats {
uint64_t total;
};
-struct stats {
- uint64_t mem_used;
- uint64_t mem_limit;
- uint64_t memsw_used;
- uint64_t memsw_limit;
+struct cpu_stats {
+ uint64_t use_nanos;
+ uint64_t use_user;
+ uint64_t use_sys;
+};
+
+struct mem_stats {
+ union {
+ struct {
+ uint64_t swap_used;
+ uint64_t swap_limit;
+ }; /* v2 only */
+ struct {
+ uint64_t memsw_used;
+ uint64_t memsw_limit;
+ }; /* v1 only */
+ };
+ uint64_t used;
+ uint64_t limit;
uint64_t kmem_used;
uint64_t kmem_limit;
- uint64_t cpu_use_nanos;
- uint64_t cpu_use_user;
- uint64_t cpu_use_sys;
+};
+
+struct stats {
+ struct mem_stats mem;
+ struct cpu_stats cpu;
struct blkio_stats io_service_bytes;
struct blkio_stats io_serviced;
};
@@ -62,6 +77,7 @@ static int sort_reverse = 0;
static struct termios oldtios;
static struct container_stats *container_stats = NULL;
static int ct_alloc_cnt = 0;
+static long user_hz = 0;
static int my_parser(struct lxc_arguments *args, int c, char *arg)
{
@@ -204,7 +220,6 @@ static uint64_t stat_get_int(struct lxc_container *c, const char *item)
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
if (len <= 0) {
- fprintf(stderr, "Unable to read cgroup item %s\n", item);
return 0;
}
@@ -223,7 +238,6 @@ static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
if (len <= 0) {
- fprintf(stderr, "Unable to read cgroup item %s\n", item);
goto out;
}
@@ -275,21 +289,21 @@ static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
8:0 Total 149327872
Total 149327872
*/
-static void stat_get_blk_stats(struct lxc_container *c, const char *item,
+static int cg1_get_blk_stats(struct lxc_container *c, const char *item,
struct blkio_stats *stats) {
char buf[4096];
int i, len;
char **lines, **cols;
+ int ret = -1;
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
if (len <= 0 || (size_t)len >= sizeof(buf)) {
- fprintf(stderr, "Unable to read cgroup item %s\n", item);
- return;
+ return ret;
}
lines = lxc_string_split_and_trim(buf, '\n');
if (!lines)
- return;
+ return ret;
memset(stats, 0, sizeof(struct blkio_stats));
@@ -308,40 +322,131 @@ static void stat_get_blk_stats(struct lxc_container *c, const char *item,
lxc_free_array((void **)cols, free);
}
+ ret = 0;
+out:
+ lxc_free_array((void **)lines, free);
+ return ret;
+}
+
+static int cg2_get_blk_stats(struct lxc_container *c, const char *item,
+ struct blkio_stats *stats) {
+ char buf[4096];
+ int i, j, len;
+ char **lines, **cols;
+ int ret = -1;
+
+ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
+ if (len <= 0 || (size_t)len >= sizeof(buf)) {
+ return ret;
+ }
+
+ lines = lxc_string_split_and_trim(buf, '\n');
+ if (!lines)
+ return ret;
+
+ memset(stats, 0, sizeof(struct blkio_stats));
+
+ for (i = 0; lines[i]; i++) {
+ cols = lxc_string_split_and_trim(lines[i], ' ');
+ if (!cols)
+ goto out;
+
+ for (j = 0; cols[j]; j++) {
+ if (strncmp(cols[j], "rbytes=", 7) == 0) {
+ stats->read += strtoull(&cols[j][7], NULL, 0);
+ } else if (strncmp(cols[j], "wbytes=", 7) == 0) {
+ stats->write += strtoull(&cols[j][7], NULL, 0);
+ }
+ }
+ lxc_free_array((void **)cols, free);
+ }
+ stats->total = stats->read + stats->write;
+ ret = 0;
out:
lxc_free_array((void **)lines, free);
- return;
+ return ret;
+}
+
+static int cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+{
+ mem->used = stat_get_int(c, "memory.usage_in_bytes");
+ mem->limit = stat_get_int(c, "memory.limit_in_bytes");
+ mem->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
+ mem->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
+ mem->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
+ mem->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
+ return mem->used > 0 ? 0 : -1;
+}
+
+static int cg2_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+{
+ mem->used = stat_get_int(c, "memory.current");
+ mem->limit = stat_get_int(c, "memory.max");
+ mem->swap_used = stat_get_int(c, "memory.swap.current");
+ mem->swap_limit = stat_get_int(c, "memory.swap.max");
+ mem->kmem_used = stat_match_get_int(c, "memory.stat", "kernel", 1);
+ /* does not exist in cgroup v2 */
+ // mem->kmem_limit = 0;
+ return mem->used > 0 ? 0 : -1;
+}
+
+static int cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+{
+ cpu->use_nanos = stat_get_int(c, "cpuacct.usage");
+ cpu->use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
+ cpu->use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
+ return cpu->use_nanos > 0 ? 0 : -1;
+}
+
+static int cg2_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+{
+ /* convert microseconds to nanoseconds */
+ cpu->use_nanos = stat_match_get_int(c, "cpu.stat", "usage_usec", 1) * 1000;
+
+ cpu->use_user = stat_match_get_int(c, "cpu.stat", "user_usec", 1) * user_hz / 1000000;
+ cpu->use_sys = stat_match_get_int(c, "cpu.stat", "system_usec", 1) * user_hz / 1000000;
+ return cpu->use_nanos > 0 ? 0 : -1;
}
static void stats_get(struct lxc_container *c, struct container_stats *ct, struct stats *total)
{
ct->c = c;
- ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
- ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
- ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
- ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
- ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
- ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
- ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
- ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
- ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
-
- stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
- stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
+ if (cg1_mem_stats(c, &ct->stats->mem) < 0) {
+ if (cg2_mem_stats(c, &ct->stats->mem) < 0) {
+ fprintf(stderr, "Unable to read memory stats\n");
+ }
+ }
+ if (cg1_cpu_stats(c, &ct->stats->cpu) < 0) {
+ if (cg2_cpu_stats(c, &ct->stats->cpu) < 0) {
+ fprintf(stderr, "Unable to read CPU stats\n");
+ }
+ }
+
+ if (cg1_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes) < 0) {
+ if (cg2_get_blk_stats(c, "io.stat", &ct->stats->io_service_bytes) < 0) {
+ fprintf(stderr, "Unable to read IO stats\n");
+ }
+ } else {
+ /* only with cgroups v1 */
+ cg1_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
+ }
+
if (total) {
- total->mem_used = total->mem_used + ct->stats->mem_used;
- total->mem_limit = total->mem_limit + ct->stats->mem_limit;
- total->memsw_used = total->memsw_used + ct->stats->memsw_used;
- total->memsw_limit = total->memsw_limit + ct->stats->memsw_limit;
- total->kmem_used = total->kmem_used + ct->stats->kmem_used;
- total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
- total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
- total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
- total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
+ total->mem.used += ct->stats->mem.used;
+ total->mem.limit += ct->stats->mem.limit;
+ total->mem.swap_used += ct->stats->mem.swap_used;
+ total->mem.swap_limit += ct->stats->mem.swap_limit;
+ total->mem.kmem_used += ct->stats->mem.kmem_used;
+ total->mem.kmem_limit += ct->stats->mem.kmem_limit;
+
+ total->cpu.use_nanos += ct->stats->cpu.use_nanos;
+ total->cpu.use_user += ct->stats->cpu.use_user;
+ total->cpu.use_sys += ct->stats->cpu.use_sys;
+
total->io_service_bytes.total += ct->stats->io_service_bytes.total;
- total->io_service_bytes.read += ct->stats->io_service_bytes.read;
+ total->io_service_bytes.read += ct->stats->io_service_bytes.read;
total->io_service_bytes.write += ct->stats->io_service_bytes.write;
}
}
@@ -351,19 +456,19 @@ static void stats_print_header(struct stats *stats)
printf(TERMRVRS TERMBOLD);
printf("%-18s %12s %12s %12s %36s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
- if (stats->memsw_used > 0)
+ if (stats->mem.swap_used > 0)
printf(" %10s", "MemSw");
- if (stats->kmem_used > 0)
+ if (stats->mem.kmem_used > 0)
printf(" %10s", "KMem");
printf("\n");
printf("%-18s %12s %12s %12s %36s %10s", "Name", "Used", "Sys", "User", "Total(Read/Write)", "Used");
- if (stats->memsw_used > 0)
+ if (stats->mem.swap_used > 0)
printf(" %10s", "Used");
- if (stats->kmem_used > 0)
+ if (stats->mem.kmem_used > 0)
printf(" %10s", "Used");
printf("\n");
@@ -388,7 +493,7 @@ static void stats_print(const char *name, const struct stats *stats,
size_humanize(stats->io_service_bytes.total, iosb_total_str, sizeof(iosb_total_str));
size_humanize(stats->io_service_bytes.read, iosb_read_str, sizeof(iosb_read_str));
size_humanize(stats->io_service_bytes.write, iosb_write_str, sizeof(iosb_write_str));
- size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
+ size_humanize(stats->mem.used, mem_used_str, sizeof(mem_used_str));
ret = snprintf(iosb_str, sizeof(iosb_str), "%s(%s/%s)", iosb_total_str, iosb_read_str, iosb_write_str);
if (ret < 0 || (size_t)ret >= sizeof(iosb_str))
@@ -396,18 +501,18 @@ static void stats_print(const char *name, const struct stats *stats,
printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s",
name,
- (float)stats->cpu_use_nanos / 1000000000,
- (float)stats->cpu_use_sys / USER_HZ,
- (float)stats->cpu_use_user / USER_HZ,
+ (float)stats->cpu.use_nanos / 1000000000,
+ (float)stats->cpu.use_sys / user_hz,
+ (float)stats->cpu.use_user / user_hz,
iosb_str,
mem_used_str);
- if (total->memsw_used > 0) {
- size_humanize(stats->memsw_used, memsw_used_str, sizeof(memsw_used_str));
+ if (total->mem.swap_used > 0) {
+ size_humanize(stats->mem.swap_used, memsw_used_str, sizeof(memsw_used_str));
printf(" %10s", memsw_used_str);
}
- if (total->kmem_used > 0) {
- size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
+ if (total->mem.kmem_used > 0) {
+ size_humanize(stats->mem.kmem_used, kmem_used_str, sizeof(kmem_used_str));
printf(" %10s", kmem_used_str);
}
} else {
@@ -415,11 +520,11 @@ static void stats_print(const char *name, const struct stats *stats,
time_ms = (unsigned long long) (time_val.tv_sec) * 1000 + (unsigned long long) (time_val.tv_usec) / 1000;
printf("%" PRIu64 ",%s,%" PRIu64 ",%" PRIu64 ",%" PRIu64
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64,
- (uint64_t)time_ms, name, (uint64_t)stats->cpu_use_nanos,
- (uint64_t)stats->cpu_use_sys,
- (uint64_t)stats->cpu_use_user, (uint64_t)stats->io_service_bytes.total,
- (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem_used,
- (uint64_t)stats->memsw_used, (uint64_t)stats->kmem_used);
+ (uint64_t)time_ms, name, (uint64_t)stats->cpu.use_nanos,
+ (uint64_t)stats->cpu.use_sys,
+ (uint64_t)stats->cpu.use_user, (uint64_t)stats->io_service_bytes.total,
+ (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem.used,
+ (uint64_t)stats->mem.swap_used, (uint64_t)stats->mem.kmem_used);
}
}
@@ -441,9 +546,9 @@ static int cmp_cpuuse(const void *sct1, const void *sct2)
const struct container_stats *ct2 = sct2;
if (sort_reverse)
- return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
+ return ct2->stats->cpu.use_nanos < ct1->stats->cpu.use_nanos;
- return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
+ return ct1->stats->cpu.use_nanos < ct2->stats->cpu.use_nanos;
}
static int cmp_blkio(const void *sct1, const void *sct2)
@@ -463,9 +568,9 @@ static int cmp_memory(const void *sct1, const void *sct2)
const struct container_stats *ct2 = sct2;
if (sort_reverse)
- return ct2->stats->mem_used < ct1->stats->mem_used;
+ return ct2->stats->mem.used < ct1->stats->mem.used;
- return ct1->stats->mem_used < ct2->stats->mem_used;
+ return ct1->stats->mem.used < ct2->stats->mem.used;
}
static int cmp_memorysw(const void *sct1, const void *sct2)
@@ -474,9 +579,9 @@ static int cmp_memorysw(const void *sct1, const void *sct2)
const struct container_stats *ct2 = sct2;
if (sort_reverse)
- return ct2->stats->memsw_used < ct1->stats->memsw_used;
+ return ct2->stats->mem.swap_used < ct1->stats->mem.swap_used;
- return ct1->stats->memsw_used < ct2->stats->memsw_used;
+ return ct1->stats->mem.swap_used < ct2->stats->mem.swap_used;
}
static int cmp_kmemory(const void *sct1, const void *sct2)
@@ -485,9 +590,9 @@ static int cmp_kmemory(const void *sct1, const void *sct2)
const struct container_stats *ct2 = sct2;
if (sort_reverse)
- return ct2->stats->kmem_used < ct1->stats->kmem_used;
+ return ct2->stats->mem.kmem_used < ct1->stats->mem.kmem_used;
- return ct1->stats->kmem_used < ct2->stats->kmem_used;
+ return ct1->stats->mem.kmem_used < ct2->stats->mem.kmem_used;
}
static void ct_sort(int active)
@@ -589,6 +694,11 @@ int lxc_top_main(int argc, char *argv[])
signal(SIGINT, sig_handler);
signal(SIGQUIT, sig_handler);
+ user_hz = sysconf(_SC_CLK_TCK);
+ if (user_hz == 0) {
+ user_hz = 100;
+ }
+
if (lxc_mainloop_open(&descr)) {
fprintf(stderr, "Failed to create mainloop\n");
goto out;
@@ -631,7 +741,7 @@ int lxc_top_main(int argc, char *argv[])
stats_print_header(&total);
}
- for (i = 0; i < active_cnt && i < ct_print_cnt; i++) {
+ for (i = 0; i < active_cnt && ((i < ct_print_cnt) || batch); i++) {
stats_print(container_stats[i].c->name, container_stats[i].stats, &total);
printf("\n");
}
diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in
index 04fa96333b..78e4dccbbd 100755
--- a/templates/lxc-busybox.in
+++ b/templates/lxc-busybox.in
@@ -42,33 +42,33 @@ install_busybox()
rootfs="${1}"
name="${2}"
res=0
- fstree="\
-${rootfs}/selinux \
-${rootfs}/dev \
-${rootfs}/home \
-${rootfs}/root \
-${rootfs}/etc \
-${rootfs}/etc/init.d \
-${rootfs}/bin \
-${rootfs}/usr/bin \
-${rootfs}/sbin \
-${rootfs}/usr/sbin \
-${rootfs}/proc \
-${rootfs}/sys \
-${rootfs}/mnt \
-${rootfs}/tmp \
-${rootfs}/var/log \
-${rootfs}/var/run \
-${rootfs}/dev/pts \
-${rootfs}/lib \
-${rootfs}/usr/lib \
-${rootfs}/lib64 \
-${rootfs}/usr/lib64"
-
- # shellcheck disable=SC2086
- mkdir -p ${fstree} || return 1
- # shellcheck disable=SC2086
- chmod 755 ${fstree} || return 1
+
+ for d in \
+ selinux \
+ dev \
+ home \
+ root \
+ etc \
+ etc/init.d \
+ bin \
+ usr/bin \
+ sbin \
+ usr/sbin \
+ proc \
+ sys \
+ mnt \
+ tmp \
+ var/log \
+ var/run \
+ dev/pts \
+ lib \
+ usr/lib \
+ lib64 \
+ usr/lib64
+ do
+ mkdir -p "${rootfs}/$d" || return 1
+ chmod 755 "${rootfs}/$d" || return 1
+ done
# make /tmp accessible to any user (with sticky bit)
chmod 1777 "${rootfs}/tmp" || return 1
@@ -107,10 +107,10 @@ EOF
# Look for the pathname of "default.script" from the help of udhcpc
DEF_SCRIPT=$(${BUSYBOX_EXE} udhcpc --help 2>&1 | egrep -- '-s.*Run PROG' | cut -d'/' -f2- | cut -d')' -f1)
DEF_SCRIPT_DIR=$(dirname /${DEF_SCRIPT})
- mkdir -p ${rootfs}/${DEF_SCRIPT_DIR}
- chmod 644 ${rootfs}/${DEF_SCRIPT_DIR} || return 1
+ mkdir -p "${rootfs}/${DEF_SCRIPT_DIR}"
+ chmod 644 "${rootfs}/${DEF_SCRIPT_DIR}" || return 1
- cat <> ${rootfs}/${DEF_SCRIPT}
+ cat <> "${rootfs}/${DEF_SCRIPT}"
#!/bin/sh
case "\$1" in
deconfig)
@@ -147,7 +147,7 @@ esac
exit 0
EOF
- chmod 744 ${rootfs}/${DEF_SCRIPT}
+ chmod 744 "${rootfs}/${DEF_SCRIPT}"
return "${res}"
}
@@ -193,7 +193,7 @@ grep -q "^lxc.rootfs.path" "${path}/config" 2>/dev/null || echo "lxc.rootfs.path
cat <> "${path}/config"
lxc.signal.halt = SIGUSR1
lxc.signal.reboot = SIGTERM
-lxc.uts.name = "${name}"
+lxc.uts.name = ${name}
lxc.autodev = 1
lxc.tty.max = 5
lxc.pty.max = 1
@@ -270,12 +270,12 @@ while true
do
case "$1" in
-h|--help) usage && exit 0;;
- -n|--name) name=$2; shift 2;;
- -p|--path) path=$2; shift 2;;
- --rootfs) rootfs=$2; shift 2;;
- --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
- --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
- --busybox-path) BUSYBOX_EXE=$2; shift 2;;
+ -n|--name) name="$2"; shift 2;;
+ -p|--path) path="$2"; shift 2;;
+ --rootfs) rootfs="$2"; shift 2;;
+ --mapped-uid) LXC_MAPPED_UID="$2"; shift 2;;
+ --mapped-gid) LXC_MAPPED_GID="$2"; shift 2;;
+ --busybox-path) BUSYBOX_EXE="$2"; shift 2;;
--) shift 1; break ;;
*) break ;;
esac