diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index d77ce37baf..e8dde91b81 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1179,25 +1179,30 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) return ret == 0; } -char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, int scope) -{ - int count = 0; - struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; - char addressOutputBuffer[INET6_ADDRSTRLEN]; - void *tempAddrPtr = NULL; - char **addresses = NULL, **temp; - char *address = NULL; +static inline void exit_from_ns(struct lxc_container *c, int *old_netns, int *new_netns) { + /* Switch back to original netns */ + if (*old_netns >= 0 && setns(*old_netns, CLONE_NEWNET)) + SYSERROR("failed to setns"); + process_lock(); + if (*new_netns >= 0) + close(*new_netns); + if (*old_netns >= 0) + close(*old_netns); + process_unlock(); +} + +static inline bool enter_to_ns(struct lxc_container *c, int *old_netns, int *new_netns) { + int ret = 0; char new_netns_path[MAXPATHLEN]; - int old_netns = -1, new_netns = -1, ret = 0; if (!c->is_running(c)) goto out; /* Save reference to old netns */ process_lock(); - old_netns = open("/proc/self/ns/net", O_RDONLY); + *old_netns = open("/proc/self/ns/net", O_RDONLY); process_unlock(); - if (old_netns < 0) { + if (*old_netns < 0) { SYSERROR("failed to open /proc/self/ns/net"); goto out; } @@ -1208,17 +1213,92 @@ char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, in goto out; process_lock(); - new_netns = open(new_netns_path, O_RDONLY); + *new_netns = open(new_netns_path, O_RDONLY); process_unlock(); - if (new_netns < 0) { + if (*new_netns < 0) { SYSERROR("failed to open %s", new_netns_path); goto out; } - if (setns(new_netns, CLONE_NEWNET)) { + if (setns(*new_netns, CLONE_NEWNET)) { SYSERROR("failed to setns"); goto out; } + return true; +out: + exit_from_ns(c, old_netns, new_netns); + return false; +} + +static char** lxcapi_get_interfaces(struct lxc_container *c) +{ + int count = 0, i; + bool found = false; + struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + char **interfaces = NULL, **temp; + int old_netns = -1, new_netns = -1; + + if (!enter_to_ns(c, &old_netns, &new_netns)) + goto out; + + /* Grab the list of interfaces */ + if (getifaddrs(&interfaceArray)) { + SYSERROR("failed to get interfaces list"); + goto out; + } + + /* Iterate through the interfaces */ + for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { + /* + * WARNING: Following "for" loop does a linear search over the interfaces array + * For the containers with lots of interfaces this may be problematic. + * I'm not expecting this to be the common usage but if it turns out to be + * than using binary search or a hash table could be more elegant solution. + */ + for (i = 0; i < count; i++) { + if (strcmp(interfaces[i], tempIfAddr->ifa_name) == 0) { + found = true; + break; + } + } + + if (!found) { + count += 1; + temp = realloc(interfaces, count * sizeof(*interfaces)); + if (!temp) { + count -= 1; + goto out; + } + interfaces = temp; + interfaces[count - 1] = strdup(tempIfAddr->ifa_name); + } + found = false; + } + +out: + if (interfaceArray) + freeifaddrs(interfaceArray); + + exit_from_ns(c, &old_netns, &new_netns); + + /* Append NULL to the array */ + interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count); + + return interfaces; +} + +static char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, int scope) +{ + int count = 0; + struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + char addressOutputBuffer[INET6_ADDRSTRLEN]; + void *tempAddrPtr = NULL; + char **addresses = NULL, **temp; + char *address = NULL; + int old_netns = -1, new_netns = -1; + + if (!enter_to_ns(c, &old_netns, &new_netns)) + goto out; /* Grab the list of interfaces */ if (getifaddrs(&interfaceArray)) { @@ -1269,30 +1349,10 @@ char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, in if(interfaceArray) freeifaddrs(interfaceArray); - /* Switch back to original netns */ - if (old_netns >= 0 && setns(old_netns, CLONE_NEWNET)) - SYSERROR("failed to setns"); - process_lock(); - if (new_netns >= 0) - close(new_netns); - if (old_netns >= 0) - close(old_netns); - process_unlock(); + exit_from_ns(c, &old_netns, &new_netns); /* Append NULL to the array */ - if (count) { - count++; - temp = realloc(addresses, count * sizeof(*addresses)); - if (!temp) { - int i; - for (i = 0; i < count-1; i++) - free(addresses[i]); - free(addresses); - return NULL; - } - addresses = temp; - addresses[count - 1] = NULL; - } + addresses = (char **)lxc_append_null_to_array((void **)addresses, count); return addresses; } @@ -2642,6 +2702,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; c->clone = lxcapi_clone; + c->get_interfaces = lxcapi_get_interfaces; c->get_ips = lxcapi_get_ips; c->attach = lxcapi_attach; c->attach_run_wait = lxcapi_attach_run_wait; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index f9ae43b7cd..89b55bd64e 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -90,6 +90,9 @@ struct lxc_container { * the length which was our would be printed. */ int (*get_config_item)(struct lxc_container *c, const char *key, char *retv, int inlen); int (*get_keys)(struct lxc_container *c, const char *key, char *retv, int inlen); + // Return interface names. The result is strdup()d, so free the result. + char** (*get_interfaces)(struct lxc_container *c); + // Return IP addresses. The result is strdup()d, so free the result. char** (*get_ips)(struct lxc_container *c, char* interface, char* family, int scope); /* * get_cgroup_item returns the number of bytes read, or an error (<0). diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 00e13d5952..a908b5cafb 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -880,7 +880,7 @@ int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool return -1; ret = lxc_write_nointr(fd, buf, count); if (ret < 0) - goto out_error; + goto out_error; if ((size_t)ret != count) goto out_error; if (add_newline) { @@ -935,3 +935,23 @@ int lxc_read_from_file(const char *filename, void* buf, size_t count) errno = saved_errno; return ret; } + +void **lxc_append_null_to_array(void **array, size_t count) +{ + void **temp; + + /* Append NULL to the array */ + if (count) { + temp = realloc(array, (count + 1) * sizeof(*array)); + if (!temp) { + int i; + for (i = 0; i < count; i++) + free(array[i]); + free(array); + return NULL; + } + array = temp; + array[count] = NULL; + } + return array; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index ba7cfb3efc..55f98fa875 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -236,4 +236,5 @@ extern void lxc_free_array(void **array, lxc_free_fn element_free_fn); extern size_t lxc_array_len(void **array); extern void **lxc_dup_array(void **array, lxc_dup_fn element_dup_fn, lxc_free_fn element_free_fn); +extern void **lxc_append_null_to_array(void **array, size_t count); #endif diff --git a/src/python-lxc/examples/api_test.py b/src/python-lxc/examples/api_test.py index e078d2bcd4..47543321d4 100644 --- a/src/python-lxc/examples/api_test.py +++ b/src/python-lxc/examples/api_test.py @@ -89,6 +89,11 @@ assert(container.running) assert(container.state == "RUNNING") + +## Checking IP address +print("Getting the interface names") +assert(container.get_interfaces() == ('lo', 'eth0')) + ## Checking IP address print("Getting the IP addresses") diff --git a/src/python-lxc/lxc.c b/src/python-lxc/lxc.c index bd053a7b23..b6e08047a1 100644 --- a/src/python-lxc/lxc.c +++ b/src/python-lxc/lxc.c @@ -410,6 +410,52 @@ Container_get_keys(Container *self, PyObject *args, PyObject *kwds) return ret; } +static PyObject * +Container_get_interfaces(Container *self) +{ + int i = 0; + char** interfaces = NULL; + + PyObject* ret; + + /* Get the interfaces */ + interfaces = self->container->get_interfaces(self->container); + if (!interfaces) + return PyTuple_New(0); + + /* Count the entries */ + while (interfaces[i]) + i++; + + /* Create the new tuple */ + ret = PyTuple_New(i); + if (!ret) + return NULL; + + /* Add the entries to the tuple and free the memory */ + i = 0; + while (interfaces[i]) { + PyObject *unicode = PyUnicode_FromString(interfaces[i]); + if (!unicode) { + Py_DECREF(ret); + ret = NULL; + break; + } + PyTuple_SET_ITEM(ret, i, unicode); + i++; + } + + /* Free the list of IPs */ + i = 0; + while (interfaces[i]) { + free(interfaces[i]); + i++; + } + free(interfaces); + + return ret; +} + static PyObject * Container_get_ips(Container *self, PyObject *args, PyObject *kwds) { @@ -898,15 +944,15 @@ LXC_arch_to_personality(PyObject *self, PyObject *arg) PyErr_SetString(PyExc_ValueError, "Expected a string"); return NULL; } - + pystr = PyUnicode_AsUTF8String(arg); if (!pystr) return NULL; - + str = PyBytes_AsString(pystr); if (!str) goto out; - + rv = lxc_config_parse_arch(str); if (rv == -1) PyErr_SetString(PyExc_KeyError, "Failed to lookup architecture."); @@ -1025,6 +1071,12 @@ static PyMethodDef Container_methods[] = { "\n" "Get a list of valid sub-keys for a key." }, + {"get_interfaces", (PyCFunction)Container_get_interfaces, + METH_NOARGS, + "get_interface() -> tuple\n" + "\n" + "Get a tuple of interfaces for the container." + }, {"get_ips", (PyCFunction)Container_get_ips, METH_VARARGS|METH_KEYWORDS, "get_ips(interface, family, scope) -> tuple\n" @@ -1205,7 +1257,7 @@ PyInit__lxc(void) /* add constants */ d = PyModule_GetDict(m); - + #define PYLXC_EXPORT_CONST(c) PyDict_SetItemString(d, #c, PyLong_FromLong(c)) /* environment variable handling */ diff --git a/src/python-lxc/lxc/__init__.py b/src/python-lxc/lxc/__init__.py index 3fb76dc021..6a29903d15 100644 --- a/src/python-lxc/lxc/__init__.py +++ b/src/python-lxc/lxc/__init__.py @@ -333,6 +333,14 @@ def get_keys(self, key=None): else: return value + def get_interfaces(self): + """ + Get a tuple of interfaces for the container. + """ + + return _lxc.Container.get_interfaces(self) + + def get_ips(self, interface=None, family=None, scope=None, timeout=0): """ Get a tuple of IPs for the container.