Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c85a67a
Fix return from fn stack
hallyn May 9, 2026
432a17b
simplify cleanup in storage_copy
hallyn May 9, 2026
10b533a
Merge pull request #4682 from hallyn/2026-05-09/storagecopy
stgraber May 9, 2026
750da2a
tools: lxc-info: Print memory and cpu utilization on cgroup2 system
petris Dec 4, 2023
d80b6bf
tools: lxc-top: refactor lxc-top stat structs
ncopa Jul 24, 2024
46ad85a
tools: lxc-top: get memory stats from cgroups2
ncopa Jul 24, 2024
25b8560
lxc-top: CPU stats for cgroups2
ncopa Jul 24, 2024
54bebd3
tools: lxc-top: get the user HZ at runtime
ncopa Jul 24, 2024
1722580
tools: lxc-top: add cgroups2 IO stats
ncopa Jul 24, 2024
0ef8abe
lxc-top: added kernel memory usage for cgroup2
DevonSchwartz Jul 25, 2024
5aef4b9
tools: lxc-top: add batch as conditional because ct_print_cnt irrelev…
DevonSchwartz Jul 25, 2024
1f552e1
tools: lxc-top: print when batch or less than container count
DevonSchwartz Jul 26, 2024
9976835
tools: lxc-top: add union to cgroup1 memsw and cgroup2 swap stats
DevonSchwartz Jul 26, 2024
b0ff3f2
Merge pull request #4426 from petris/lxc-info-cg2
stgraber May 15, 2026
5aab9c5
Merge pull request #4439 from DevonSchwartz/fix-lxc-top-cgroup2
stgraber May 15, 2026
9c55205
templates/lxc-busybox: handle container names with spaces
mihalicyn May 18, 2026
ecae7d5
Merge pull request #4684 from mihalicyn/18-05-2026-busybox-fixes
stgraber May 18, 2026
e4e1fa4
lxc/caps: fix cap_to_text() memory cleanup
DreamConnected May 19, 2026
db3ff9c
Merge pull request #4685 from Container-On-Android/fix-cap-free
stgraber May 19, 2026
cb8e0ec
Merge pull request #4683 from hallyn/2026-05-09/storagecopy+cleanup
stgraber May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/lxc/caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
9 changes: 4 additions & 5 deletions src/lxc/lxccontainer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
20 changes: 11 additions & 9 deletions src/lxc/storage/storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions src/lxc/storage/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
172 changes: 153 additions & 19 deletions src/lxc/tools/lxc_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "arguments.h"
#include "log.h"
#include "utils.h"
#include "macro.h"

lxc_log_define(lxc_info, lxc);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down
Loading