mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-27 09:48:32 +00:00
define list container api (v2)
Two new commands are defined: list_defined_containers() and list_active_containers(). Both take an lxcpath (NULL means use the default lxcpath) and return the number of containers found. If a lxc_container ** is passed in, then an array of lxc_container's is returned, one for each container found. The caller must then lxc_container_put() each container and free the array, as shown in the new list testcase. If a char ** is passed in, then an array of container names is returned, after which the caller must free all the names and the name array, as showsn in the testcase. Changelog: Check for the container config file before trying to create an lxc_container *, to save some work. [ per stgraber comments] Add names ** argument to return only container names. Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com> Acked-by: Stéphane Graber <stgraber@ubuntu.com>
This commit is contained in:
parent
f3cef1cbe2
commit
a41f104bfb
1
.gitignore
vendored
1
.gitignore
vendored
@ -94,6 +94,7 @@ src/tests/lxc-test-startone
|
||||
src/tests/lxc-usernic-test
|
||||
src/tests/lxc-test-may-control
|
||||
src/tests/lxc-test-reboot
|
||||
src/tests/lxc-test-list
|
||||
|
||||
config/compile
|
||||
config/config.guess
|
||||
|
@ -69,6 +69,19 @@ static bool file_exists(char *f)
|
||||
return stat(f, &statbuf) == 0;
|
||||
}
|
||||
|
||||
static bool config_file_exists(const char *lxcpath, const char *cname)
|
||||
{
|
||||
/* $lxcpath + '/' + $cname + '/config' + \0 */
|
||||
int ret, len = strlen(lxcpath) + strlen(cname) + 9;
|
||||
char *fname = alloca(len);
|
||||
|
||||
ret = snprintf(fname, len, "%s/%s/config", lxcpath, cname);
|
||||
if (ret < 0 || ret >= len)
|
||||
return false;
|
||||
|
||||
return file_exists(fname);
|
||||
}
|
||||
|
||||
/*
|
||||
* A few functions to help detect when a container creation failed.
|
||||
* If a container creation was killed partway through, then trying
|
||||
@ -2744,3 +2757,225 @@ int lxc_get_wait_states(const char **states)
|
||||
states[i] = lxc_state2str(i);
|
||||
return MAX_STATE;
|
||||
}
|
||||
|
||||
|
||||
static bool add_to_names(char ***names, char *cname, int pos)
|
||||
{
|
||||
char **newnames = realloc(*names, (pos+1) * sizeof(char *));
|
||||
if (!newnames) {
|
||||
ERROR("Out of memory");
|
||||
return false;
|
||||
}
|
||||
*names = newnames;
|
||||
newnames[pos] = strdup(cname);
|
||||
if (!newnames[pos])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool add_to_clist(struct lxc_container ***list, struct lxc_container *c, int pos)
|
||||
{
|
||||
struct lxc_container **newlist = realloc(*list, (pos+1) * sizeof(struct lxc_container *));
|
||||
if (!newlist) {
|
||||
ERROR("Out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
*list = newlist;
|
||||
newlist[pos] = c;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* These next two could probably be done smarter with reusing a common function
|
||||
* with different iterators and tests...
|
||||
*/
|
||||
int list_defined_containers(const char *lxcpath, char ***names, struct lxc_container ***cret)
|
||||
{
|
||||
DIR *dir;
|
||||
int i, cfound = 0, nfound = 0;
|
||||
struct dirent dirent, *direntp;
|
||||
struct lxc_container *c;
|
||||
|
||||
if (!lxcpath)
|
||||
lxcpath = default_lxc_path();
|
||||
|
||||
process_lock();
|
||||
dir = opendir(lxcpath);
|
||||
process_unlock();
|
||||
|
||||
if (!dir) {
|
||||
SYSERROR("opendir on lxcpath");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cret)
|
||||
*cret = NULL;
|
||||
if (names)
|
||||
*names = NULL;
|
||||
|
||||
while (!readdir_r(dir, &dirent, &direntp)) {
|
||||
if (!direntp)
|
||||
break;
|
||||
if (!strcmp(direntp->d_name, "."))
|
||||
continue;
|
||||
if (!strcmp(direntp->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (!config_file_exists(lxcpath, direntp->d_name))
|
||||
continue;
|
||||
|
||||
if (names) {
|
||||
if (!add_to_names(names, direntp->d_name, cfound))
|
||||
goto free_bad;
|
||||
}
|
||||
cfound++;
|
||||
|
||||
if (!cret) {
|
||||
nfound++;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = lxc_container_new(direntp->d_name, lxcpath);
|
||||
if (!c) {
|
||||
INFO("Container %s:%s has a config but could not be loaded",
|
||||
lxcpath, direntp->d_name);
|
||||
if (names)
|
||||
free((*names)[cfound--]);
|
||||
continue;
|
||||
}
|
||||
if (!lxcapi_is_defined(c)) {
|
||||
INFO("Container %s:%s has a config but is not defined",
|
||||
lxcpath, direntp->d_name);
|
||||
if (names)
|
||||
free((*names)[cfound--]);
|
||||
lxc_container_put(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!add_to_clist(cret, c, nfound)) {
|
||||
lxc_container_put(c);
|
||||
goto free_bad;
|
||||
}
|
||||
nfound++;
|
||||
}
|
||||
|
||||
process_lock();
|
||||
closedir(dir);
|
||||
process_unlock();
|
||||
return nfound;
|
||||
|
||||
free_bad:
|
||||
if (names && *names) {
|
||||
for (i=0; i<cfound; i++)
|
||||
free((*names)[i]);
|
||||
free(*names);
|
||||
}
|
||||
if (cret && *cret) {
|
||||
for (i=0; i<nfound; i++)
|
||||
lxc_container_put((*cret)[i]);
|
||||
free(*cret);
|
||||
}
|
||||
process_lock();
|
||||
closedir(dir);
|
||||
process_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int list_active_containers(const char *lxcpath, char ***names, struct lxc_container ***cret)
|
||||
{
|
||||
int i, cfound = 0, nfound = 0;
|
||||
int lxcpath_len;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
struct lxc_container *c;
|
||||
|
||||
if (!lxcpath)
|
||||
lxcpath = default_lxc_path();
|
||||
lxcpath_len = strlen(lxcpath);
|
||||
|
||||
if (cret)
|
||||
*cret = NULL;
|
||||
if (names)
|
||||
*names = NULL;
|
||||
|
||||
process_lock();
|
||||
FILE *f = fopen("/proc/net/unix", "r");
|
||||
process_unlock();
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
while (getline(&line, &len, f) != -1) {
|
||||
char *p = rindex(line, ' '), *p2;
|
||||
if (!p)
|
||||
continue;
|
||||
p++;
|
||||
if (*p != 0x40)
|
||||
continue;
|
||||
p++;
|
||||
if (strncmp(p, lxcpath, lxcpath_len) != 0)
|
||||
continue;
|
||||
p += lxcpath_len;
|
||||
while (*p == '/')
|
||||
p++;
|
||||
|
||||
// Now p is the start of lxc_name
|
||||
p2 = index(p, '/');
|
||||
if (!p2 || strncmp(p2, "/command", 8) != 0)
|
||||
continue;
|
||||
*p2 = '\0';
|
||||
|
||||
if (names) {
|
||||
if (!add_to_names(names, p, nfound))
|
||||
goto free_bad;
|
||||
}
|
||||
cfound++;
|
||||
|
||||
if (!cret) {
|
||||
nfound++;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = lxc_container_new(p, lxcpath);
|
||||
if (!c) {
|
||||
INFO("Container %s:%s is running but could not be loaded",
|
||||
lxcpath, p);
|
||||
if (names)
|
||||
free((*names)[cfound--]);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is an anonymous container, then is_defined *can*
|
||||
* return false. So we don't do that check. Count on the
|
||||
* fact that the command socket exists.
|
||||
*/
|
||||
|
||||
if (!add_to_clist(cret, c, nfound)) {
|
||||
lxc_container_put(c);
|
||||
goto free_bad;
|
||||
}
|
||||
nfound++;
|
||||
}
|
||||
|
||||
process_lock();
|
||||
fclose(f);
|
||||
process_unlock();
|
||||
return nfound;
|
||||
|
||||
free_bad:
|
||||
if (names && *names) {
|
||||
for (i=0; i<cfound; i++)
|
||||
free((*names)[i]);
|
||||
free(*names);
|
||||
}
|
||||
if (cret && *cret) {
|
||||
for (i=0; i<nfound; i++)
|
||||
lxc_container_put((*cret)[i]);
|
||||
free(*cret);
|
||||
}
|
||||
process_lock();
|
||||
fclose(f);
|
||||
process_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
@ -248,6 +248,27 @@ const char *lxc_get_default_lvm_vg(void);
|
||||
const char *lxc_get_default_zfs_root(void);
|
||||
const char *lxc_get_version(void);
|
||||
|
||||
/*
|
||||
* Get a list of defined containers in a lxcpath.
|
||||
* @lxcpath: lxcpath under which to look.
|
||||
* @names: if not null, then a list of container names will be returned here.
|
||||
* @cret: if not null, then a list of lxc_containers will be returned here.
|
||||
*
|
||||
* Returns the number of containers found, or -1 on error.
|
||||
*/
|
||||
int list_defined_containers(const char *lxcpath, char ***names, struct lxc_container ***cret);
|
||||
|
||||
/*
|
||||
* Get a list of active containers in a lxcpath. Note that some of these
|
||||
* containers may not be "defined".
|
||||
* @lxcpath: lxcpath under which to look
|
||||
* @names: if not null, then a list of container names will be returned here.
|
||||
* @cret: if not null, then a list of lxc_containers will be returned here.
|
||||
*
|
||||
* Returns the number of containers found, or -1 on error.
|
||||
*/
|
||||
int list_active_containers(const char *lxcpath, char ***names, struct lxc_container ***cret);
|
||||
|
||||
#if 0
|
||||
char ** lxc_get_valid_keys();
|
||||
char ** lxc_get_valid_values(char *key);
|
||||
|
@ -21,6 +21,7 @@ lxc_test_snapshot_SOURCES = snapshot.c
|
||||
lxc_test_concurrent_SOURCES = concurrent.c
|
||||
lxc_test_may_control_SOURCES = may_control.c
|
||||
lxc_test_reboot_SOURCES = reboot.c
|
||||
lxc_test_list_SOURCES = list.c
|
||||
|
||||
AM_CFLAGS=-I$(top_srcdir)/src \
|
||||
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
|
||||
@ -34,7 +35,7 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
|
||||
lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys lxc-test-lxcpath \
|
||||
lxc-test-cgpath lxc-test-clonetest lxc-test-console lxc-usernic-test \
|
||||
lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
|
||||
lxc-test-reboot
|
||||
lxc-test-reboot lxc-test-list
|
||||
|
||||
bin_SCRIPTS = lxc-test-usernic
|
||||
|
||||
@ -62,4 +63,5 @@ EXTRA_DIST = \
|
||||
snapshot.c \
|
||||
concurrent.c \
|
||||
may_control.c \
|
||||
lxc-test-ubuntu
|
||||
lxc-test-ubuntu \
|
||||
list.c
|
||||
|
117
src/tests/list.c
Normal file
117
src/tests/list.c
Normal file
@ -0,0 +1,117 @@
|
||||
/* list.c
|
||||
*
|
||||
* Copyright © 2013 Canonical, Inc
|
||||
* Author: Serge Hallyn <serge.hallyn@ubuntu.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lxc/lxccontainer.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *lxcpath = NULL;
|
||||
struct lxc_container **clist;
|
||||
char **names;
|
||||
int i, n, n2;
|
||||
|
||||
if (argc > 1)
|
||||
lxcpath = argv[1];
|
||||
|
||||
printf("Counting defined containers only\n");
|
||||
n = list_defined_containers(lxcpath, NULL, NULL);
|
||||
printf("Found %d defined containers\n", n);
|
||||
printf("Looking for defined containers only\n");
|
||||
n2 = list_defined_containers(lxcpath, NULL, &clist);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
struct lxc_container *c = clist[i];
|
||||
printf("Found defined container %s\n", c->name);
|
||||
lxc_container_put(c);
|
||||
}
|
||||
if (n2 > 0)
|
||||
free(clist);
|
||||
|
||||
printf("Looking for defined names only\n");
|
||||
n2 = list_defined_containers(lxcpath, &names, NULL);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
printf("Found defined container %s\n", names[i]);
|
||||
free(names[i]);
|
||||
}
|
||||
if (n2 > 0)
|
||||
free(names);
|
||||
|
||||
printf("Looking for defined names and containers\n");
|
||||
n2 = list_defined_containers(lxcpath, &names, &clist);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
struct lxc_container *c = clist[i];
|
||||
printf("Found defined container %s, name was %s\n", c->name, names[i]);
|
||||
free(names[i]);
|
||||
lxc_container_put(c);
|
||||
}
|
||||
if (n2 > 0) {
|
||||
free(names);
|
||||
free(clist);
|
||||
}
|
||||
|
||||
|
||||
printf("Counting active containers only\n");
|
||||
n = list_active_containers(lxcpath, NULL, NULL);
|
||||
printf("Found %d active containers\n", n);
|
||||
printf("Looking for active containers only\n");
|
||||
n2 = list_active_containers(lxcpath, NULL, &clist);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
printf("Found active container %s\n", clist[i]->name);
|
||||
lxc_container_put(clist[i]);
|
||||
}
|
||||
if (n2 > 0)
|
||||
free(clist);
|
||||
|
||||
printf("Looking for active names only\n");
|
||||
n2 = list_active_containers(lxcpath, &names, NULL);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
printf("Found active container %s\n", names[i]);
|
||||
free(names[i]);
|
||||
}
|
||||
if (n2 > 0)
|
||||
free(names);
|
||||
|
||||
printf("Looking for active names and containers\n");
|
||||
n2 = list_active_containers(lxcpath, &names, &clist);
|
||||
if (n2 != n)
|
||||
printf("Warning: first call returned %d, second %d\n", n, n2);
|
||||
for (i=0; i<n2; i++) {
|
||||
struct lxc_container *c = clist[i];
|
||||
printf("Found active container %s, name was %s\n", c->name, names[i]);
|
||||
free(names[i]);
|
||||
lxc_container_put(c);
|
||||
}
|
||||
if (n2 > 0) {
|
||||
free(names);
|
||||
free(clist);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user