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:
Serge Hallyn 2013-10-11 10:44:39 -05:00
parent f3cef1cbe2
commit a41f104bfb
5 changed files with 378 additions and 2 deletions

1
.gitignore vendored
View File

@ -94,6 +94,7 @@ src/tests/lxc-test-startone
src/tests/lxc-usernic-test src/tests/lxc-usernic-test
src/tests/lxc-test-may-control src/tests/lxc-test-may-control
src/tests/lxc-test-reboot src/tests/lxc-test-reboot
src/tests/lxc-test-list
config/compile config/compile
config/config.guess config/config.guess

View File

@ -69,6 +69,19 @@ static bool file_exists(char *f)
return stat(f, &statbuf) == 0; 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. * A few functions to help detect when a container creation failed.
* If a container creation was killed partway through, then trying * 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); states[i] = lxc_state2str(i);
return MAX_STATE; 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;
}

View File

@ -248,6 +248,27 @@ const char *lxc_get_default_lvm_vg(void);
const char *lxc_get_default_zfs_root(void); const char *lxc_get_default_zfs_root(void);
const char *lxc_get_version(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 #if 0
char ** lxc_get_valid_keys(); char ** lxc_get_valid_keys();
char ** lxc_get_valid_values(char *key); char ** lxc_get_valid_values(char *key);

View File

@ -21,6 +21,7 @@ lxc_test_snapshot_SOURCES = snapshot.c
lxc_test_concurrent_SOURCES = concurrent.c lxc_test_concurrent_SOURCES = concurrent.c
lxc_test_may_control_SOURCES = may_control.c lxc_test_may_control_SOURCES = may_control.c
lxc_test_reboot_SOURCES = reboot.c lxc_test_reboot_SOURCES = reboot.c
lxc_test_list_SOURCES = list.c
AM_CFLAGS=-I$(top_srcdir)/src \ AM_CFLAGS=-I$(top_srcdir)/src \
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -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-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-cgpath lxc-test-clonetest lxc-test-console lxc-usernic-test \
lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \ lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
lxc-test-reboot lxc-test-reboot lxc-test-list
bin_SCRIPTS = lxc-test-usernic bin_SCRIPTS = lxc-test-usernic
@ -62,4 +63,5 @@ EXTRA_DIST = \
snapshot.c \ snapshot.c \
concurrent.c \ concurrent.c \
may_control.c \ may_control.c \
lxc-test-ubuntu lxc-test-ubuntu \
list.c

117
src/tests/list.c Normal file
View 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);
}