[lxc-devel] [PATCH] define list container api (v2)
Stéphane Graber
stgraber at ubuntu.com
Mon Oct 14 16:54:13 UTC 2013
On Fri, Oct 11, 2013 at 10:44:39AM -0500, Serge Hallyn wrote:
> 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.
Sorry, missed that one in my mailbox...
I quite like the new implementation, I think it'll be flexible enough
for all our needs.
Acked-by: Stéphane Graber <stgraber at ubuntu.com>
>
> 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 at ubuntu.com>
> ---
> .gitignore | 1 +
> src/lxc/lxccontainer.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/lxc/lxccontainer.h | 21 +++++
> src/tests/Makefile.am | 6 +-
> src/tests/list.c | 117 ++++++++++++++++++++++++
> 5 files changed, 378 insertions(+), 2 deletions(-)
> create mode 100644 src/tests/list.c
>
> diff --git a/.gitignore b/.gitignore
> index 536423a..ab0105a 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -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
> diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
> index 13ed4d2..f3d9a93 100644
> --- a/src/lxc/lxccontainer.c
> +++ b/src/lxc/lxccontainer.c
> @@ -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;
> +}
> diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
> index 20ab8e8..5901066 100644
> --- a/src/lxc/lxccontainer.h
> +++ b/src/lxc/lxccontainer.h
> @@ -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);
> diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
> index e18693b..509e414 100644
> --- a/src/tests/Makefile.am
> +++ b/src/tests/Makefile.am
> @@ -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
> diff --git a/src/tests/list.c b/src/tests/list.c
> new file mode 100644
> index 0000000..f6704a4
> --- /dev/null
> +++ b/src/tests/list.c
> @@ -0,0 +1,117 @@
> +/* list.c
> + *
> + * Copyright © 2013 Canonical, Inc
> + * Author: Serge Hallyn <serge.hallyn at 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);
> +}
> --
> 1.8.1.2
>
--
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20131014/e378f5b0/attachment.pgp>
More information about the lxc-devel
mailing list