Python-netcf
by Lars Sjöström
Hi all,
Thanks for an excellent project! I've have found it really useful!
I was missing a python binding so I decided to create one myself!
All the main functions are in place but it still need some work like
proper unit tests for example.
I would really appreciate if someone would like to have a look at it and
give me some feedback!
The project can be found on github at the moment:
http://github.com/lsjostro/python-netcf
Would it be make sense to commit it to the main netcf repo our should I
request separate fedorahosted project?
Best regards,
Lars Sjostrom
13 years, 3 months
[PATCH] Reorganize code to making porting to other platforms easier
by Laine Stump
netcf has always been intended to be ported to other platforms, but
was originally only targetted to Fedora/RHEL (which both use the
identical drv_initscripts.c driver). During development, there was a
bit of mixing of platform-specific code with code that should be able
to work / be useful on all netcf-supported platforms.
This patch attempts to remedy that problem by moving around some
functions, prototypes, and data definitions to better separate
platform-specific things from platform-independent. Hopefully that
will make upcoming ports to other platforms less painful.
Note that there is still work to do, since this has only separated
linux/initscripts functions from platform-independent. In the future
when there is a port to a linux distro that doesn't use
drv_initscripts.c some more refinement will need to take place.
"make check" ran successfully after all the changes, and both ncftool
and virsh iface-* appearred to continue working successfully.
Note that almost all changes involved moving entire
functions/prototypes/structures from one file to another. The few
exceptions are noted below.
Data structure changes:
The only data structure change was to move the "rng" pointer from the
driver object up one level into the netcf object (because this should
be useful on all platforms).
Functions moved into dutil.c:
free_netcf()
free_netcf_if()
argv_to_string()
report_error()
vreport_error()
xml_node()
xml_new_node()
These functions were deemed probably buildable, and potentially usable
on any platform, so they were moved into dutil.c, which should be
included in any build on any platform.
netlink/ioctl/exec/augeas related functions - These were all moved
into dutil_linux.c
dutil_get_aug/dutil_put_aug - since these functions (in dutil.c) were
called only once (from functions in drv_initscripts.c), their bodies
were moved into the calling functions.
Related to that, ncf_get_aug and ncf_put_aug, which are exported from
the library, but considered private (they are in netcf_private.syms)
have been moved from netcf.c to drv_initscripts.c. This should allow
netcf.c to build unmodified on all platforms. To make this work, the
API_ENTRY() macro, used by those two functions, has been moved from
netcf.c to internal.h.
All other changes are collateral #include changes.
One outstanding item - the prototypes for ncf_get_aug() and
ncf_put_aug() are still located in internal.h (which should be
platform-independent) even though they are only applicable to the
initscripts version of netcf. I left these in place because there is
currently no separate drv_initscripts.h file, and I didn't want to
create one just for this purpose.
---
src/drv_initscripts.c | 58 +++-
src/dutil.c | 931 ++++--------------------------------------------
src/dutil.h | 111 ++-----
src/dutil_linux.c | 948 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dutil_linux.h | 80 ++++-
src/internal.h | 45 +--
src/netcf.c | 244 +------------
src/xslt_ext.c | 1 +
8 files changed, 1210 insertions(+), 1208 deletions(-)
diff --git a/src/drv_initscripts.c b/src/drv_initscripts.c
index b2d96cb..b2f16d5 100644
--- a/src/drv_initscripts.c
+++ b/src/drv_initscripts.c
@@ -363,7 +363,6 @@ int drv_init(struct netcf *ncf) {
exsltStrRegister();
ncf->driver->get = parse_stylesheet(ncf, "initscripts-get.xsl");
ncf->driver->put = parse_stylesheet(ncf, "initscripts-put.xsl");
- ncf->driver->rng = rng_parse(ncf, "interface.rng");
ERR_BAIL(ncf);
/* open a socket for interface ioctls */
@@ -384,7 +383,6 @@ void drv_close(struct netcf *ncf) {
return;
xsltFreeStylesheet(ncf->driver->get);
xsltFreeStylesheet(ncf->driver->put);
- xmlRelaxNGFree(ncf->driver->rng);
netlink_close(ncf);
if (ncf->driver->ioctl_fd >= 0)
close(ncf->driver->ioctl_fd);
@@ -1075,15 +1073,59 @@ int drv_if_down(struct netcf_if *nif) {
/*
* Test interface
*/
-int drv_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
- /* Use utility implementation */
- return dutil_get_aug(ncf, ncf_xml, aug_xml);
+static int drv_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
+ xmlDocPtr ncf_doc = NULL, aug_doc = NULL;
+ int result = -1;
+
+ ncf_doc = parse_xml(ncf, ncf_xml);
+ ERR_BAIL(ncf);
+
+ rng_validate(ncf, ncf_doc);
+ ERR_BAIL(ncf);
+
+ *aug_xml = apply_stylesheet_to_string(ncf, ncf->driver->get, ncf_doc);
+ ERR_BAIL(ncf);
+
+ /* fallthrough intentional */
+ result = 0;
+ error:
+ xmlFreeDoc(ncf_doc);
+ xmlFreeDoc(aug_doc);
+ return result;
}
/* Transform the Augeas XML AUG_XML into interface XML NCF_XML */
-int drv_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
- /* Use utility implementation */
- return dutil_put_aug(ncf, aug_xml, ncf_xml);
+static int drv_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
+ xmlDocPtr ncf_doc = NULL, aug_doc = NULL;
+ int result = -1;
+
+ aug_doc = parse_xml(ncf, aug_xml);
+ ERR_BAIL(ncf);
+
+ *ncf_xml = apply_stylesheet_to_string(ncf, ncf->driver->put, aug_doc);
+ ERR_BAIL(ncf);
+
+ /* fallthrough intentional */
+ result = 0;
+ error:
+ xmlFreeDoc(ncf_doc);
+ xmlFreeDoc(aug_doc);
+ return result;
+}
+
+/*
+ * Test interface
+ */
+int ncf_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
+ API_ENTRY(ncf);
+
+ return drv_get_aug(ncf, ncf_xml, aug_xml);
+}
+
+int ncf_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
+ API_ENTRY(ncf);
+
+ return drv_put_aug(ncf, aug_xml, ncf_xml);
}
/*
diff --git a/src/dutil.c b/src/dutil.c
index fd37e16..f13f4fd 100644
--- a/src/dutil.c
+++ b/src/dutil.c
@@ -33,30 +33,12 @@
#include <ctype.h>
#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-
#include "safe-alloc.h"
#include "ref.h"
#include "list.h"
#include "netcf.h"
#include "dutil.h"
-#include <netlink/socket.h>
-#include <netlink/cache.h>
-#include <netlink/route/addr.h>
-#include <netlink/route/link.h>
-/* For some reason, the headers for libnl vlan functions aren't installed */
-extern int rtnl_link_vlan_get_id(struct rtnl_link *link);
-
-#include <dirent.h>
-
#include <libxml/parser.h>
#include <libxml/relaxng.h>
#include <libxml/tree.h>
@@ -65,6 +47,44 @@ extern int rtnl_link_vlan_get_id(struct rtnl_link *link);
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
+/* Create a new netcf if instance for interface NAME */
+struct netcf_if *make_netcf_if(struct netcf *ncf, char *name) {
+ int r;
+ struct netcf_if *result = NULL;
+
+ r = make_ref(result);
+ ERR_NOMEM(r < 0, ncf);
+ result->ncf = ref(ncf);
+ result->name = name;
+ return result;
+
+ error:
+ unref(result, netcf_if);
+ return result;
+}
+
+/* never call this directly. Only call it via "unref(nif, netcf_if)" */
+void free_netcf_if(struct netcf_if *nif) {
+ if (nif == NULL)
+ return;
+
+ assert(nif->ref == 0);
+ unref(nif->ncf, netcf);
+ free(nif->name);
+ free(nif->mac);
+ free(nif);
+}
+
+/* never call this directly. Only call it via "unref(ncf, netcf)" */
+void free_netcf(struct netcf *ncf) {
+ if (ncf == NULL)
+ return;
+
+ assert(ncf->ref == 0);
+ free(ncf->root);
+ free(ncf);
+}
+
/* Like asprintf, but set *STRP to NULL on error */
int xasprintf(char **strp, const char *format, ...) {
va_list args;
@@ -78,177 +98,59 @@ int xasprintf(char **strp, const char *format, ...) {
return result;
}
-int add_augeas_xfm_table(struct netcf *ncf,
- const struct augeas_xfm_table *xfm) {
- int slot, r;
- struct driver *d = ncf->driver;
-
- if (d->augeas_xfm_num_tables == 0) {
- slot = 0;
- r = ALLOC(d->augeas_xfm_tables);
- ERR_NOMEM(r < 0, ncf);
- d->augeas_xfm_num_tables = 1;
- } else {
- for (slot =0;
- slot < d->augeas_xfm_num_tables
- && d->augeas_xfm_tables[slot] != NULL;
- slot++);
- if (slot == d->augeas_xfm_num_tables) {
- r = REALLOC_N(ncf->driver->augeas_xfm_tables, slot + 1);
- ERR_NOMEM(r < 0, ncf);
- d->augeas_xfm_num_tables = slot + 1;
- }
- }
-
- ncf->driver->augeas_xfm_tables[slot] = xfm;
- ncf->driver->copy_augeas_xfm = 1;
- return 0;
- error:
- return -1;
-}
-
-int remove_augeas_xfm_table(struct netcf *ncf,
- const struct augeas_xfm_table *xfm) {
- int slot;
- const int last = ncf->driver->augeas_xfm_num_tables;
- const struct augeas_xfm_table **tables =
- ncf->driver->augeas_xfm_tables;
-
- for (slot = 0; slot < last && tables[slot] != xfm; slot++);
- if (tables[slot] == xfm) {
- tables[slot] = NULL;
- ncf->driver->copy_augeas_xfm = 1;
- }
- return 0;
-}
-
-/* Get the Augeas instance; if we already initialized it, just return
- * it. Otherwise, create a new one and return that.
+/*
+ * argv_to_string() is borrowed from libvirt's
+ * src/util.c:virArgvToString()
*/
-struct augeas *get_augeas(struct netcf *ncf) {
- int r;
+char *
+argv_to_string(const char *const *argv) {
+ int i;
+ size_t len;
+ char *ret, *p;
- if (ncf->driver->augeas == NULL) {
- struct augeas *aug;
- char *path;
+ for (len = 1, i = 0; argv[i]; i++)
+ len += strlen(argv[i]) + 1;
- r = xasprintf(&path, "%s/lenses", ncf->data_dir);
- ERR_NOMEM(r < 0, ncf);
+ if (ALLOC_N(ret, len) < 0)
+ return NULL;
+ p = ret;
- aug = aug_init(ncf->root, path, AUG_NO_MODL_AUTOLOAD);
- FREE(path);
- ERR_THROW(aug == NULL, ncf, EOTHER, "aug_init failed");
- ncf->driver->augeas = aug;
- ncf->driver->copy_augeas_xfm = 1;
- }
+ for (i = 0; argv[i]; i++) {
+ if (i != 0)
+ *(p++) = ' ';
- if (ncf->driver->copy_augeas_xfm) {
- struct augeas *aug = ncf->driver->augeas;
- /* Only look at a few config files */
- r = aug_rm(aug, "/augeas/load/*");
- ERR_THROW(r < 0, ncf, EOTHER, "aug_rm failed in get_augeas");
-
- for (int slot = 0; slot < ncf->driver->augeas_xfm_num_tables; slot++) {
- const struct augeas_xfm_table *t =
- ncf->driver->augeas_xfm_tables[slot];
- if (t == NULL)
- continue;
- for (int i=0; i < t->size; i++) {
- r = aug_set(aug, t->pv[i].path, t->pv[i].value);
- ERR_THROW(r < 0, ncf, EOTHER,
- "transform setup failed to set %s",
- t->pv[i].path);
- }
- }
- ncf->driver->copy_augeas_xfm = 0;
- ncf->driver->load_augeas = 1;
+ strcpy(p, argv[i]);
+ p += strlen(argv[i]);
}
- if (ncf->driver->load_augeas) {
- struct augeas *aug = ncf->driver->augeas;
-
- r = aug_load(aug);
- ERR_THROW(r < 0, ncf, EOTHER, "failed to load config files");
-
- /* FIXME: we need to produce _much_ better diagnostics here - need
- * to analyze what came back in /augeas//error; ultimately, we need
- * to understand whether this is harmless or a real error. For real
- * errors, we need to return an error.
- */
- r = aug_match(aug, "/augeas//error", NULL);
- if (r > 0 && NCF_DEBUG(ncf)) {
- fprintf(stderr, "warning: augeas initialization had errors\n");
- fprintf(stderr, "please file a bug with the following lines in the bug report:\n");
- aug_print(aug, stderr, "/augeas//error");
- }
- ERR_THROW(r > 0, ncf, EOTHER, "errors in loading some config files");
- ncf->driver->load_augeas = 0;
- }
- return ncf->driver->augeas;
- error:
- aug_close(ncf->driver->augeas);
- ncf->driver->augeas = NULL;
- return NULL;
+ *p = '\0';
+
+ return ret;
}
-ATTRIBUTE_FORMAT(printf, 4, 5)
-int defnode(struct netcf *ncf, const char *name, const char *value,
- const char *format, ...) {
- struct augeas *aug = get_augeas(ncf);
+void report_error(struct netcf *ncf, netcf_errcode_t errcode,
+ const char *format, ...) {
va_list ap;
- char *expr = NULL;
- int r, created;
va_start(ap, format);
- r = vasprintf (&expr, format, ap);
- va_end (ap);
- if (r < 0)
- expr = NULL;
- ERR_NOMEM(r < 0, ncf);
-
- r = aug_defnode(aug, name, expr, value, &created);
- ERR_THROW(r < 0, ncf, EOTHER, "failed to define node %s", name);
-
- /* Fallthrough intentional */
- error:
- free(expr);
- return (r < 0) ? -1 : created;
+ vreport_error(ncf, errcode, format, ap);
+ va_end(ap);
}
-int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...) {
- struct augeas *aug = NULL;
- char *path = NULL;
- va_list args;
- int r;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
+void vreport_error(struct netcf *ncf, netcf_errcode_t errcode,
+ const char *format, va_list ap) {
+ /* We only remember the first error */
+ if (ncf->errcode != NETCF_NOERROR)
+ return;
+ assert(ncf->errdetails == NULL);
- va_start(args, fmt);
- r = vasprintf(&path, fmt, args);
- va_end(args);
- if (r < 0) {
- path = NULL;
- ERR_NOMEM(1, ncf);
+ ncf->errcode = errcode;
+ if (format != NULL) {
+ if (vasprintf(&(ncf->errdetails), format, ap) < 0)
+ ncf->errdetails = NULL;
}
-
- r = aug_match(aug, path, matches);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
-
- free(path);
- return r;
- error:
- free(path);
- return -1;
}
-void free_matches(int nint, char ***intf) {
- if (*intf != NULL) {
- for (int i=0; i < nint; i++)
- FREE((*intf)[i]);
- FREE(*intf);
- }
-}
xsltStylesheetPtr parse_stylesheet(struct netcf *ncf,
const char *fname) {
@@ -374,7 +276,7 @@ void rng_validate(struct netcf *ncf, xmlDocPtr doc) {
xmlRelaxNGValidCtxtPtr ctxt;
int r;
- ctxt = xmlRelaxNGNewValidCtxt(ncf->driver->rng);
+ ctxt = xmlRelaxNGNewValidCtxt(ncf->rng);
xmlRelaxNGSetValidErrors(ctxt, rng_error, rng_error, ncf);
r = xmlRelaxNGValidateDoc(ctxt, doc);
@@ -437,8 +339,8 @@ char *xml_prop(xmlNodePtr node, const char *name) {
* same name already exists. A NULL return means there was a memory
* failure, and it needs to be reported by the caller.
*/
-static xmlNodePtr xml_new_node(xmlDocPtr doc,
- xmlNodePtr parent, const char *name) {
+xmlNodePtr xml_new_node(xmlDocPtr doc,
+ xmlNodePtr parent, const char *name) {
xmlNodePtr cur, ret = NULL;
ret = xmlNewDocNode(doc, NULL, BAD_CAST name, NULL);
@@ -455,8 +357,8 @@ static xmlNodePtr xml_new_node(xmlDocPtr doc,
/* Find existing node of given name within parent, or create and link
* in a new one if not found.
*/
-static xmlNodePtr xml_node(xmlDocPtr doc,
- xmlNodePtr parent, const char *name) {
+xmlNodePtr xml_node(xmlDocPtr doc,
+ xmlNodePtr parent, const char *name) {
xmlNodePtr cur, ret = NULL;
for (cur = parent->children; cur != NULL; cur = cur->next) {
@@ -473,683 +375,6 @@ static xmlNodePtr xml_node(xmlDocPtr doc,
return ret;
}
-int init_ioctl_fd(struct netcf *ncf) {
- int ioctl_fd;
- int flags;
-
- ioctl_fd = socket(AF_INET, SOCK_STREAM, 0);
- ERR_THROW(ioctl_fd < 0, ncf, EINTERNAL, "failed to open socket for interface ioctl");
-
- flags = fcntl(ioctl_fd, F_GETFD);
- ERR_THROW(flags < 0, ncf, EINTERNAL, "failed to get flags for ioctl socket");
-
- flags = fcntl(ioctl_fd, F_SETFD, flags | FD_CLOEXEC);
- ERR_THROW(flags < 0, ncf, EINTERNAL, "failed to set FD_CLOEXEC flag on ioctl socket");
- return ioctl_fd;
-
-error:
- if (ioctl_fd >= 0)
- close(ioctl_fd);
- return -1;
-}
-
-int netlink_init(struct netcf *ncf) {
-
- ncf->driver->nl_sock = nl_handle_alloc();
- if (ncf->driver->nl_sock == NULL)
- goto error;
- if (nl_connect(ncf->driver->nl_sock, NETLINK_ROUTE) < 0) {
- goto error;
- }
-
- ncf->driver->link_cache = rtnl_link_alloc_cache(ncf->driver->nl_sock);
- if (ncf->driver->link_cache == NULL) {
- goto error;
- }
- nl_cache_mngt_provide(ncf->driver->link_cache);
-
- ncf->driver->addr_cache = rtnl_addr_alloc_cache(ncf->driver->nl_sock);
- if (ncf->driver->addr_cache == NULL) {
- goto error;
- }
- nl_cache_mngt_provide(ncf->driver->addr_cache);
-
- int netlink_fd = nl_socket_get_fd(ncf->driver->nl_sock);
- if (netlink_fd >= 0)
- fcntl(netlink_fd, F_SETFD, FD_CLOEXEC);
- return 0;
-
-error:
- netlink_close(ncf);
- return -1;
-}
-
-int netlink_close(struct netcf *ncf) {
-
- if (ncf->driver->addr_cache) {
- nl_cache_free(ncf->driver->addr_cache);
- ncf->driver->addr_cache = NULL;
- }
- if (ncf->driver->link_cache) {
- nl_cache_free(ncf->driver->link_cache);
- ncf->driver->link_cache = NULL;
- }
- if (ncf->driver->nl_sock) {
- nl_close(ncf->driver->nl_sock);
- nl_handle_destroy(ncf->driver->nl_sock);
- ncf->driver->nl_sock = NULL;
- }
- return 0;
-}
-
-int if_is_active(struct netcf *ncf, const char *intf) {
- struct ifreq ifr;
-
- MEMZERO(&ifr, 1);
- strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
- ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
- if (ioctl(ncf->driver->ioctl_fd, SIOCGIFFLAGS, &ifr)) {
- return 0;
- }
- return ((ifr.ifr_flags & IFF_UP) == IFF_UP);
-}
-
-netcf_if_type_t if_type(struct netcf *ncf, const char *intf) {
- char *path;
- struct stat stats;
- netcf_if_type_t ret = NETCF_IFACE_TYPE_NONE;
-
- xasprintf(&path, "/proc/net/vlan/%s", intf);
- ERR_NOMEM(path == NULL, ncf);
- if ((stat (path, &stats) == 0) && S_ISREG (stats.st_mode)) {
- ret = NETCF_IFACE_TYPE_VLAN;
- }
- FREE(path);
-
- if (ret == NETCF_IFACE_TYPE_NONE) {
- xasprintf(&path, "/sys/class/net/%s/bridge", intf);
- ERR_NOMEM(path == NULL, ncf);
- if (stat (path, &stats) == 0 && S_ISDIR (stats.st_mode))
- ret = NETCF_IFACE_TYPE_BRIDGE;
- FREE(path);
- }
- if (ret == NETCF_IFACE_TYPE_NONE) {
- xasprintf(&path, "/sys/class/net/%s/bonding", intf);
- ERR_NOMEM(path == NULL, ncf);
- if (stat (path, &stats) == 0 && S_ISDIR (stats.st_mode))
- ret = NETCF_IFACE_TYPE_BOND;
- FREE(path);
- }
- if (ret == NETCF_IFACE_TYPE_NONE)
- ret = NETCF_IFACE_TYPE_ETHERNET;
-
-error:
- FREE(path);
- return ret;
-}
-
-/* Given a netcf_if_type_t, return a const char * representation */
-const char *if_type_str(netcf_if_type_t type) {
- switch (type) {
- case NETCF_IFACE_TYPE_ETHERNET:
- return "ethernet";
- case NETCF_IFACE_TYPE_BOND:
- return "bond";
- case NETCF_IFACE_TYPE_BRIDGE:
- return "bridge";
- case NETCF_IFACE_TYPE_VLAN:
- return "vlan";
- default:
- return NULL;
- }
-}
-
-static int if_bridge_phys_name(struct netcf *ncf,
- const char *intf, char ***phys_names) {
- /* We can learn the name of the physical interface associated with
- * this bridge by looking for the names of the links in
- * /sys/class/net/$ifname/brif.
- *
- * The caller of this function must free the array of strings that is
- * returned.
- *
- */
- int r, ii, ret = 0;
- char *dirpath = NULL;
- DIR *dir = NULL;
-
- *phys_names = NULL;
-
- xasprintf(&dirpath, "/sys/class/net/%s/brif", intf);
- ERR_NOMEM(dirpath == NULL, ncf);
-
- dir = opendir(dirpath);
- if (dir != NULL) {
- struct dirent *d;
-
- while ((d = readdir (dir)) != NULL) {
- if (STRNEQ(d->d_name, ".") && STRNEQ(d->d_name, "..")) {
- r = REALLOC_N(*phys_names, ret + 1);
- ERR_NOMEM(r < 0, ncf);
- ret++;
- xasprintf(&((*phys_names)[ret - 1]), "%s", d->d_name);
- ERR_NOMEM((*phys_names)[ret - 1] == NULL, ncf);
- }
- }
- }
- goto done;
-
-error:
- for (ii = 0; ii < ret; ii++)
- FREE((*phys_names)[ii]);
- FREE(*phys_names);
- *phys_names = NULL;
- ret = -1;
-
-done:
- if (dir)
- closedir (dir);
- FREE(dirpath);
- return ret;
-
-}
-
-/* Create a new netcf if instance for interface NAME */
-struct netcf_if *make_netcf_if(struct netcf *ncf, char *name) {
- int r;
- struct netcf_if *result = NULL;
-
- r = make_ref(result);
- ERR_NOMEM(r < 0, ncf);
- result->ncf = ref(ncf);
- result->name = name;
- return result;
-
- error:
- unref(result, netcf_if);
- return result;
-}
-
-/*
- * Test interface
- */
-int dutil_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
- xmlDocPtr ncf_doc = NULL, aug_doc = NULL;
- int result = -1;
-
- ncf_doc = parse_xml(ncf, ncf_xml);
- ERR_BAIL(ncf);
-
- rng_validate(ncf, ncf_doc);
- ERR_BAIL(ncf);
-
- *aug_xml = apply_stylesheet_to_string(ncf, ncf->driver->get, ncf_doc);
- ERR_BAIL(ncf);
-
- /* fallthrough intentional */
- result = 0;
- error:
- xmlFreeDoc(ncf_doc);
- xmlFreeDoc(aug_doc);
- return result;
-}
-
-/* Transform the Augeas XML AUG_XML into interface XML NCF_XML */
-int dutil_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
- xmlDocPtr ncf_doc = NULL, aug_doc = NULL;
- int result = -1;
-
- aug_doc = parse_xml(ncf, aug_xml);
- ERR_BAIL(ncf);
-
- *ncf_xml = apply_stylesheet_to_string(ncf, ncf->driver->put, aug_doc);
- ERR_BAIL(ncf);
-
- /* fallthrough intentional */
- result = 0;
- error:
- xmlFreeDoc(ncf_doc);
- xmlFreeDoc(aug_doc);
- return result;
-}
-
-
-static void add_type_specific_info(struct netcf *ncf,
- const char *ifname, int ifindex,
- xmlDocPtr doc, xmlNodePtr root);
-
-/* Data that needs to be preserved between calls to the libnl iterator
- * callback.
- */
-struct nl_ip_callback_data {
- xmlDocPtr doc;
- xmlNodePtr root;
- xmlNodePtr protov4;
- xmlNodePtr protov6;
- struct netcf *ncf;
-};
-
-/* add all ip addresses for the given interface to the xml document
-*/
-static void add_ip_info_cb(struct nl_object *obj, void *arg) {
- struct nl_ip_callback_data *cb_data = arg;
- struct rtnl_addr *addr = (struct rtnl_addr *)obj;
- struct netcf *ncf = cb_data->ncf;
-
- struct nl_addr *local_addr;
- int family, prefix;
- const char *family_str;
- char ip_str[48];
- char prefix_str[16];
- xmlNodePtr *proto, ip_node, cur;
- xmlAttrPtr prop = NULL;
-
- local_addr = rtnl_addr_get_local(addr);
- family = nl_addr_get_family(local_addr);
- switch (family) {
- case AF_INET:
- family_str = "ipv4";
- proto = &cb_data->protov4;
- break;
- case AF_INET6:
- family_str = "ipv6";
- proto = &cb_data->protov6;
- break;
-
- default:
- /* Nothing that interests us in this entry */
- return;
- }
-
- inet_ntop(family, nl_addr_get_binary_addr(local_addr),
- ip_str, sizeof(ip_str));
- prefix = nl_addr_get_prefixlen(local_addr);
-
- if (*proto == NULL) {
- /* We haven't dont anything with this proto yet. Search for an
- * existing node.
- */
- for (cur = cb_data->root->children; cur != NULL; cur = cur->next) {
- if ((cur->type == XML_ELEMENT_NODE) &&
- xmlStrEqual(cur->name, BAD_CAST "protocol")) {
- xmlChar *node_family = xmlGetProp(cur, BAD_CAST "family");
- if (node_family != NULL) {
- if (xmlStrEqual(node_family, BAD_CAST family_str))
- *proto = cur;
- xmlFree(node_family);
- if (*proto != NULL) {
- break;
- }
- }
- }
- }
- }
-
- if (*proto == NULL) {
- /* No node exists for this protocol family. Create one.
- */
- *proto = xml_new_node(cb_data->doc, cb_data->root, "protocol");
- ERR_NOMEM(*proto == NULL, ncf);
- prop = xmlSetProp(*proto, BAD_CAST "family", BAD_CAST family_str);
- ERR_NOMEM(prop == NULL, ncf);
-
- }
-
- /* Create a new ip node for this address/prefix, and set the
- * properties
- */
- ip_node = xml_new_node(cb_data->doc, *proto, "ip");
- ERR_NOMEM(ip_node == NULL, ncf);
- prop = xmlSetProp(ip_node, BAD_CAST "address", BAD_CAST ip_str);
- ERR_NOMEM(prop == NULL, ncf);
- snprintf(prefix_str, sizeof(prefix_str), "%d", prefix);
- prop = xmlSetProp(ip_node, BAD_CAST "prefix", BAD_CAST prefix_str);
- ERR_NOMEM(prop == NULL, ncf);
-
-error:
- return;
-}
-
-static void add_ip_info(struct netcf *ncf,
- const char *ifname ATTRIBUTE_UNUSED, int ifindex,
- xmlDocPtr doc, xmlNodePtr root) {
- struct nl_ip_callback_data cb_data
- = { doc, root, NULL, NULL, ncf };
- struct rtnl_addr *filter_addr = NULL;
-
- filter_addr = rtnl_addr_alloc();
- ERR_NOMEM(filter_addr == NULL, ncf);
-
- rtnl_addr_set_ifindex(filter_addr, ifindex);
- nl_cache_foreach_filter(ncf->driver->addr_cache,
- OBJ_CAST(filter_addr), add_ip_info_cb,
- &cb_data);
-error:
- if (filter_addr)
- rtnl_addr_put(filter_addr);
- return;
-}
-
-
-struct nl_ethernet_callback_data {
- xmlDocPtr doc;
- xmlNodePtr root;
- xmlNodePtr mac;
- struct netcf *ncf;
-};
-
-static void add_ethernet_info_cb(struct nl_object *obj, void *arg) {
- struct nl_ethernet_callback_data *cb_data = arg;
- struct rtnl_link *iflink = (struct rtnl_link *)obj;
- struct netcf *ncf = cb_data->ncf;
-
- struct nl_addr *addr;
- xmlAttrPtr prop = NULL;
-
- if ((cb_data->mac == NULL)
- && ((addr = rtnl_link_get_addr(iflink)) != NULL)
- && !nl_addr_iszero(addr)) {
-
- char mac_str[64];
-
- nl_addr2str(addr, mac_str, sizeof(mac_str));
- cb_data->mac = xml_node(cb_data->doc, cb_data->root, "mac");
- ERR_NOMEM(cb_data->mac == NULL, ncf);
- prop = xmlSetProp(cb_data->mac, BAD_CAST "address", BAD_CAST mac_str);
- ERR_NOMEM(prop == NULL, ncf);
- }
-error:
- return;
-}
-
-static void add_ethernet_info(struct netcf *ncf,
- const char *ifname ATTRIBUTE_UNUSED, int ifindex,
- xmlDocPtr doc, xmlNodePtr root) {
- struct nl_ethernet_callback_data cb_data
- = { doc, root, NULL, ncf };
- struct rtnl_link *filter_link = NULL;
-
- filter_link = rtnl_link_alloc();
- ERR_NOMEM(filter_link == NULL, ncf);
-
- rtnl_link_set_ifindex(filter_link, ifindex);
- nl_cache_foreach_filter(ncf->driver->link_cache,
- OBJ_CAST(filter_link), add_ethernet_info_cb,
- &cb_data);
-error:
- if (filter_link)
- rtnl_link_put(filter_link);
- return;
-}
-
-struct nl_vlan_callback_data {
- xmlDocPtr doc;
- xmlNodePtr root;
- xmlNodePtr vlan;
- struct netcf *ncf;
-};
-
-static void add_vlan_info_cb(struct nl_object *obj, void *arg) {
- struct nl_vlan_callback_data *cb_data = arg;
- struct rtnl_link *iflink = (struct rtnl_link *)obj;
- struct netcf *ncf = cb_data->ncf;
-
- struct rtnl_link *master_link;
- char *master_name = NULL;
- int l_link, vlan_id, master_ifindex;
- char vlan_id_str[16];
- char *link_type;
- xmlNodePtr interface_node;
- xmlAttrPtr prop = NULL;
-
- /* If this really is a vlan link, get the master interface and vlan id.
- */
- if (cb_data->vlan != NULL)
- return;
-
- link_type = rtnl_link_get_info_type(iflink);
- if ((link_type == NULL) || STRNEQ(link_type, "vlan"))
- return;
-
- l_link = rtnl_link_get_link(iflink);
- if (l_link == RTNL_LINK_NOT_FOUND)
- return;
-
- master_link = rtnl_link_get(nl_object_get_cache(obj), l_link);
- if (master_link == NULL)
- return;
-
- master_name = rtnl_link_get_name(master_link);
- if (master_name == NULL)
- return;
-
-
- cb_data->vlan = xml_node(cb_data->doc, cb_data->root, "vlan");
- ERR_NOMEM(cb_data->vlan == NULL, ncf);
-
- vlan_id = rtnl_link_vlan_get_id(iflink);
- snprintf(vlan_id_str, sizeof(vlan_id_str), "%d", vlan_id);
- prop = xmlSetProp(cb_data->vlan, BAD_CAST "tag", BAD_CAST vlan_id_str);
- ERR_NOMEM(prop == NULL, ncf);
-
- interface_node = xml_new_node(cb_data->doc, cb_data->vlan, "interface");
- ERR_NOMEM(interface_node == NULL, ncf);
-
- /* Add in type-specific info of master interface */
- master_ifindex = rtnl_link_name2i(ncf->driver->link_cache, master_name);
- ERR_THROW((master_ifindex == RTNL_LINK_NOT_FOUND), ncf, ENETLINK,
- "couldn't find ifindex for vlan master interface `%s`",
- master_name);
- add_type_specific_info(ncf, master_name, master_ifindex,
- cb_data->doc, interface_node);
-
-error:
- return;
-}
-
-static void add_vlan_info(struct netcf *ncf,
- const char *ifname ATTRIBUTE_UNUSED, int ifindex,
- xmlDocPtr doc, xmlNodePtr root) {
- struct nl_vlan_callback_data cb_data
- = { doc, root, NULL, ncf };
- struct rtnl_link *filter_link = NULL;
-
- filter_link = rtnl_link_alloc();
- ERR_NOMEM(filter_link == NULL, ncf);
-
- rtnl_link_set_ifindex(filter_link, ifindex);
- nl_cache_foreach_filter(ncf->driver->link_cache,
- OBJ_CAST(filter_link), add_vlan_info_cb,
- &cb_data);
- ERR_BAIL(ncf);
-error:
- if (filter_link)
- rtnl_link_put(filter_link);
- return;
-}
-
-static void add_bridge_info(struct netcf *ncf,
- const char *ifname, int ifindex ATTRIBUTE_UNUSED,
- xmlDocPtr doc, xmlNodePtr root) {
- char **phys_names;
- int nphys, ii;
- xmlNodePtr bridge_node = NULL, interface_node = NULL;
-
- nphys = if_bridge_phys_name(ncf, ifname, &phys_names);
- if (nphys <= 0)
- return;
-
- bridge_node = xml_node(doc, root, "bridge");
- ERR_NOMEM(bridge_node == NULL, ncf);
-
- for (ii = 0; ii < nphys; ii++) {
- int phys_ifindex;
-
- interface_node = xml_new_node(doc, bridge_node, "interface");
- ERR_NOMEM(interface_node == NULL, ncf);
-
- /* Add in type-specific info of physical interface */
- phys_ifindex =
- rtnl_link_name2i(ncf->driver->link_cache, phys_names[ii]);
- ERR_THROW((phys_ifindex == RTNL_LINK_NOT_FOUND), ncf, ENETLINK,
- "couldn't find ifindex for physical interface `%s` of bridge %s",
- phys_names[ii], ifname);
-
- add_type_specific_info(ncf, phys_names[ii], phys_ifindex, doc,
- interface_node);
- }
-
-error:
- for (ii = 0; ii < nphys; ii++)
- FREE(phys_names[ii]);
- FREE(phys_names);
-}
-
-
-struct nl_bond_callback_data {
- xmlDocPtr doc;
- xmlNodePtr root;
- xmlNodePtr bond;
- int master_ifindex;
- struct netcf *ncf;
-};
-
-static void add_bond_info_cb(struct nl_object *obj,
- void *arg ATTRIBUTE_UNUSED) {
- struct nl_bond_callback_data *cb_data = arg;
- struct rtnl_link *iflink = (struct rtnl_link *)obj;
- struct netcf *ncf = cb_data->ncf;
-
- xmlNodePtr interface_node;
-
- /* If this is a slave link, and the master is master_ifindex, add the
- * interface info to the bond.
- */
-
- if (!(rtnl_link_get_flags(iflink) & IFF_SLAVE)
- || rtnl_link_get_master(iflink) != cb_data->master_ifindex)
- return;
-
- cb_data->bond = xml_node(cb_data->doc, cb_data->root, "bond");
- ERR_NOMEM(cb_data->bond == NULL, ncf);
-
- /* XXX - if we learn where to get bridge "mode" property, set it here */
-
- /* XXX - need to add node like one of these:
- *
- * <miimon freq="100" updelay="10" carrier="ioctl"/>
- * or
- * <arpmode interval='something' target='something'>
- */
-
- /* add a new interface node */
- interface_node = xml_new_node(cb_data->doc, cb_data->bond, "interface");
- ERR_NOMEM(interface_node == NULL, ncf);
-
- /* Add in type-specific info of this slave interface */
- add_type_specific_info(ncf, rtnl_link_get_name(iflink),
- rtnl_link_get_ifindex(iflink),
- cb_data->doc, interface_node);
-error:
- return;
-}
-
-static void add_bond_info(struct netcf *ncf,
- const char *ifname ATTRIBUTE_UNUSED, int ifindex,
- xmlDocPtr doc, xmlNodePtr root) {
- struct nl_bond_callback_data cb_data
- = { doc, root, NULL, ifindex, ncf };
-
- nl_cache_foreach(ncf->driver->link_cache, add_bond_info_cb, &cb_data);
-}
-
-
-static void add_type_specific_info(struct netcf *ncf,
- const char *ifname, int ifindex,
- xmlDocPtr doc, xmlNodePtr root) {
- xmlAttrPtr prop;
- netcf_if_type_t iftype;
- const char *iftype_str;
-
- prop = xmlNewProp(root, BAD_CAST "name", BAD_CAST ifname);
- ERR_NOMEM(prop == NULL, ncf);
-
- iftype = if_type(ncf, ifname);
- ERR_BAIL(ncf);
- iftype_str = if_type_str(iftype);
-
- if (iftype_str) {
- prop = xmlSetProp(root, BAD_CAST "type", BAD_CAST if_type_str(iftype));
- ERR_NOMEM(prop == NULL, ncf);
- }
-
- switch (iftype) {
- case NETCF_IFACE_TYPE_ETHERNET:
- add_ethernet_info(ncf, ifname, ifindex, doc, root);
- break;
- case NETCF_IFACE_TYPE_BRIDGE:
- add_bridge_info(ncf, ifname, ifindex, doc, root);
- break;
- case NETCF_IFACE_TYPE_VLAN:
- add_vlan_info(ncf, ifname, ifindex, doc, root);
- break;
- case NETCF_IFACE_TYPE_BOND:
- add_bond_info(ncf, ifname, ifindex, doc, root);
- break;
- default:
- break;
- }
-error:
- return;
-}
-
-void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc) {
- xmlNodePtr root;
- int ifindex, code;
-
- root = xmlDocGetRootElement(doc);
- ERR_THROW((root == NULL), nif->ncf, EINTERNAL,
- "failed to get document root element");
- ERR_THROW(!xmlStrEqual(root->name, BAD_CAST "interface"),
- nif->ncf, EINTERNAL, "root document is not an interface");
-
- /* Update the caches with any recent changes */
- code = nl_cache_refill(nif->ncf->driver->nl_sock,
- nif->ncf->driver->link_cache);
- ERR_THROW((code < 0), nif->ncf, ENETLINK,
- "failed to refill interface index cache");
- code = nl_cache_refill(nif->ncf->driver->nl_sock,
- nif->ncf->driver->addr_cache);
- ERR_THROW((code < 0), nif->ncf, ENETLINK,
- "failed to refill interface address cache");
-
- ifindex = rtnl_link_name2i(nif->ncf->driver->link_cache, nif->name);
- ERR_THROW((ifindex == RTNL_LINK_NOT_FOUND), nif->ncf, ENETLINK,
- "couldn't find ifindex for interface `%s`", nif->name);
-
- add_type_specific_info(nif->ncf, nif->name, ifindex, doc, root);
- ERR_BAIL(nif->ncf);
-
- add_ip_info(nif->ncf, nif->name, ifindex, doc, root);
- ERR_BAIL(nif->ncf);
-
-error:
- return;
-}
-
-/*
- * Bringing interfaces up/down
- */
-
-/* Run the program PROG with the single argument ARG */
-void run1(struct netcf *ncf, const char *prog, const char *arg) {
- const char *const argv[] = {
- prog, arg, NULL
- };
-
- run_program(ncf, argv);
-}
-
/*
* Local variables:
* indent-tabs-mode: nil
diff --git a/src/dutil.h b/src/dutil.h
index 90dc59a..85d26b1 100644
--- a/src/dutil.h
+++ b/src/dutil.h
@@ -23,25 +23,6 @@
#ifndef DUTIL_H_
#define DUTIL_H_
-#include <libxml/relaxng.h>
-#include <libxslt/xsltInternals.h>
-#include <netlink/netlink.h>
-
-struct driver {
- struct augeas *augeas;
- xsltStylesheetPtr put;
- xsltStylesheetPtr get;
- xmlRelaxNGPtr rng;
- int ioctl_fd;
- struct nl_handle *nl_sock;
- struct nl_cache *link_cache;
- struct nl_cache *addr_cache;
- unsigned int load_augeas : 1;
- unsigned int copy_augeas_xfm : 1;
- unsigned int augeas_xfm_num_tables;
- const struct augeas_xfm_table **augeas_xfm_tables;
-};
-
struct augeas_pv {
const char *const path;
const char *const value;
@@ -52,33 +33,33 @@ struct augeas_xfm_table {
const struct augeas_pv *pv;
};
+/* never call these directly. Only call via, eg, "unref(ncf, netcf)" */
+void free_netcf(struct netcf *ncf);
+void free_netcf_if(struct netcf_if *nif);
+
/* Like asprintf, but set *STRP to NULL on error */
ATTRIBUTE_FORMAT(printf, 2, 3)
int xasprintf(char **strp, const char *format, ...);
-/* Add a table of transformations that the next GET_AUGEAS should run */
-int add_augeas_xfm_table(struct netcf *ncf,
- const struct augeas_xfm_table *table);
-
-/* Remove a table of transformations that the next GET_AUGEAS should run */
-int remove_augeas_xfm_table(struct netcf *ncf,
- const struct augeas_xfm_table *table);
-
-/* Get or create the augeas instance from NCF */
-struct augeas *get_augeas(struct netcf *ncf);
+/*
+ * Convert an array of char* into a single, newly allocated string
+ * with a space between each arg.
+ */
+char *argv_to_string(const char *const *argv);
-/* Define a node inside the augeas tree */
-ATTRIBUTE_FORMAT(printf, 4, 5)
-int defnode(struct netcf *ncf, const char *name, const char *value,
- const char *format, ...);
+/*
+ * Error reporting
+ */
+void report_error(struct netcf *ncf, netcf_errcode_t errcode,
+ const char *format, ...)
+ ATTRIBUTE_FORMAT(printf, 3, 4);
-/* Format a path by doing a printf of FMT and the var args, then call
- AUG_MATCH on that path. Sets NCF->ERRCODE on error */
-ATTRIBUTE_FORMAT(printf, 3, 4)
-int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...);
+void vreport_error(struct netcf *ncf, netcf_errcode_t errcode,
+ const char *format, va_list ap)
+ ATTRIBUTE_FORMAT(printf, 3, 0);
-/* Free matches from aug_match (or aug_submatch) */
-void free_matches(int nint, char ***intf);
+/* XSLT extension functions in xslt_ext.c */
+int xslt_register_exts(xsltTransformContextPtr ctxt);
/* Parse an XSLT stylesheet residing in the file NCF->data_dir/xml/FNAME */
xsltStylesheetPtr parse_stylesheet(struct netcf *ncf, const char *fname);
@@ -110,53 +91,21 @@ xmlDocPtr parse_xml(struct netcf *ncf, const char *xml_str);
/* Return the content the property NAME in NODE */
char *xml_prop(xmlNodePtr node, const char *name);
-/* Get a file descriptor to a ioctl socket */
-int init_ioctl_fd(struct netcf *ncf);
-
-/* setup the netlink socket */
-int netlink_init(struct netcf *ncf);
-
-/*shutdown the netlink socket and release its resources */
-int netlink_close(struct netcf *ncf);
+/* Create a new node (even if one of the same name already exists) and
+ * link it to the document. Return NULL on error.
+*/
+xmlNodePtr xml_new_node(xmlDocPtr doc,
+ xmlNodePtr parent, const char *name);
-/* Check if the interface INTF is up using an ioctl call */
-int if_is_active(struct netcf *ncf, const char *intf);
-
-/* Interface types recognized by netcf. */
-typedef enum {
- NETCF_IFACE_TYPE_NONE = 0, /* not yet determined */
- NETCF_IFACE_TYPE_ETHERNET, /* any physical device is "ethernet" */
- NETCF_IFACE_TYPE_BOND,
- NETCF_IFACE_TYPE_BRIDGE,
- NETCF_IFACE_TYPE_VLAN,
-} netcf_if_type_t;
-
-/* Return the type of the interface.
- */
-netcf_if_type_t if_type(struct netcf *ncf, const char *intf);
-
-/* Given a netcf_if_type_t enum value, return a const char *representation
- * This pointer has an indefinite life, and shouldn't be / can't be free'd.
- */
-const char *if_type_str(netcf_if_type_t type);
+/* Find an existing node, or create one if not found, and link it to
+ * the document. Return NULL on error.
+*/
+xmlNodePtr xml_node(xmlDocPtr doc,
+ xmlNodePtr parent, const char *name);
/* Create a new netcf if instance for interface NAME */
struct netcf_if *make_netcf_if(struct netcf *ncf, char *name);
-/* Transform the interface XML NCF_XML into Augeas XML AUG_XML */
-int dutil_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml);
-
-/* Transform the Augeas XML AUG_XML into interface XML NCF_XML */
-int dutil_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml);
-
-/* Add the state of the interface (currently all addresses + netmasks)
- * to its xml document.
- */
-void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc);
-
-/* Run the program PROG with the single argument ARG */
-void run1(struct netcf *ncf, const char *prog, const char *arg);
-
#endif
/*
diff --git a/src/dutil_linux.c b/src/dutil_linux.c
index 06a9650..9935431 100644
--- a/src/dutil_linux.c
+++ b/src/dutil_linux.c
@@ -31,8 +31,20 @@
#include <string.h>
#include <unistd.h>
#include <ctype.h>
+#include <errno.h>
+
+#include <dirent.h>
+#include <sys/wait.h>
+#include <signal.h>
#include <c-ctype.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include "safe-alloc.h"
#include "ref.h"
@@ -41,6 +53,332 @@
#include "dutil.h"
#include "dutil_linux.h"
+#include <net/if.h>
+#include <netlink/socket.h>
+#include <netlink/cache.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/link.h>
+
+/* For some reason, the headers for libnl vlan functions aren't installed */
+extern int rtnl_link_vlan_get_id(struct rtnl_link *link);
+
+/*
+ * Executing external programs
+ */
+
+static int
+exec_program(struct netcf *ncf,
+ const char *const*argv,
+ const char *commandline,
+ pid_t *pid)
+{
+ sigset_t oldmask, newmask;
+ struct sigaction sig_action;
+ char errbuf[128];
+
+ /* commandline is only used for error reporting */
+ if (commandline == NULL)
+ commandline = argv[0];
+ /*
+ * Need to block signals now, so that child process can safely
+ * kill off caller's signal handlers without a race.
+ */
+ sigfillset(&newmask);
+ if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
+ report_error(ncf, NETCF_EEXEC,
+ "failed to set signal mask while forking for '%s': %s",
+ commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
+ goto error;
+ }
+
+ *pid = fork();
+
+ ERR_THROW(*pid < 0, ncf, EEXEC, "failed to fork for '%s': %s",
+ commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
+
+ if (*pid) { /* parent */
+ /* Restore our original signal mask now that the child is
+ safely running */
+ ERR_THROW(pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0,
+ ncf, EEXEC,
+ "failed to restore signal mask while forking for '%s': %s",
+ commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
+ return 0;
+ }
+
+ /* child */
+
+ /* Clear out all signal handlers from parent so nothing unexpected
+ can happen in our child once we unblock signals */
+
+ sig_action.sa_handler = SIG_DFL;
+ sig_action.sa_flags = 0;
+ sigemptyset(&sig_action.sa_mask);
+
+ int i;
+ for (i = 1; i < NSIG; i++) {
+ /* Only possible errors are EFAULT or EINVAL
+ The former wont happen, the latter we
+ expect, so no need to check return value */
+
+ sigaction(i, &sig_action, NULL);
+ }
+
+ /* Unmask all signals in child, since we've no idea what the
+ caller's done with their signal mask and don't want to
+ propagate that to children */
+ sigemptyset(&newmask);
+ if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
+ /* don't report_error, as it will never be seen anyway */
+ _exit(1);
+ }
+
+ /* close all open file descriptors */
+ int openmax = sysconf (_SC_OPEN_MAX);
+ for (i = 3; i < openmax; i++)
+ close(i);
+
+ execvp(argv[0], (char **) argv);
+
+ /* if execvp() returns, it has failed */
+ /* don't report_error, as it will never be seen anyway */
+ _exit(1);
+
+error:
+ /* This is cleanup of parent process only - child
+ should never jump here on error */
+ return -1;
+}
+
+/**
+ * Run a command without using the shell.
+ *
+ * return 0 if the command run and exited with 0 status; Otherwise
+ * return -1
+ *
+ */
+int run_program(struct netcf *ncf, const char *const *argv) {
+
+ pid_t childpid;
+ int exitstatus, waitret;
+ char *argv_str;
+ int ret = -1;
+ char errbuf[128];
+
+ argv_str = argv_to_string(argv);
+ ERR_NOMEM(argv_str == NULL, ncf);
+
+ exec_program(ncf, argv, argv_str, &childpid);
+ ERR_BAIL(ncf);
+
+ while ((waitret = waitpid(childpid, &exitstatus, 0) == -1) &&
+ errno == EINTR) {
+ /* empty loop */
+ }
+
+ ERR_THROW(waitret == -1, ncf, EEXEC,
+ "Failed waiting for completion of '%s': %s",
+ argv_str, strerror_r(errno, errbuf, sizeof(errbuf)));
+ ERR_THROW(!WIFEXITED(exitstatus) && WIFSIGNALED(exitstatus), ncf, EEXEC,
+ "'%s' terminated by signal: %d",
+ argv_str, WTERMSIG(exitstatus));
+ ERR_THROW(!WIFEXITED(exitstatus), ncf, EEXEC,
+ "'%s' terminated improperly: %d",
+ argv_str, WEXITSTATUS(exitstatus));
+ ERR_THROW(WEXITSTATUS(exitstatus) != 0, ncf, EEXEC,
+ "Running '%s' failed with exit code %d",
+ argv_str, WEXITSTATUS(exitstatus));
+ ret = 0;
+
+error:
+ FREE(argv_str);
+ return ret;
+}
+
+/* Run the program PROG with the single argument ARG */
+void run1(struct netcf *ncf, const char *prog, const char *arg) {
+ const char *const argv[] = {
+ prog, arg, NULL
+ };
+
+ run_program(ncf, argv);
+}
+
+/*
+ * augeas-related utilities
+ */
+int add_augeas_xfm_table(struct netcf *ncf,
+ const struct augeas_xfm_table *xfm) {
+ int slot, r;
+ struct driver *d = ncf->driver;
+
+ if (d->augeas_xfm_num_tables == 0) {
+ slot = 0;
+ r = ALLOC(d->augeas_xfm_tables);
+ ERR_NOMEM(r < 0, ncf);
+ d->augeas_xfm_num_tables = 1;
+ } else {
+ for (slot =0;
+ slot < d->augeas_xfm_num_tables
+ && d->augeas_xfm_tables[slot] != NULL;
+ slot++);
+ if (slot == d->augeas_xfm_num_tables) {
+ r = REALLOC_N(ncf->driver->augeas_xfm_tables, slot + 1);
+ ERR_NOMEM(r < 0, ncf);
+ d->augeas_xfm_num_tables = slot + 1;
+ }
+ }
+
+ ncf->driver->augeas_xfm_tables[slot] = xfm;
+ ncf->driver->copy_augeas_xfm = 1;
+ return 0;
+ error:
+ return -1;
+}
+
+int remove_augeas_xfm_table(struct netcf *ncf,
+ const struct augeas_xfm_table *xfm) {
+ int slot;
+ const int last = ncf->driver->augeas_xfm_num_tables;
+ const struct augeas_xfm_table **tables =
+ ncf->driver->augeas_xfm_tables;
+
+ for (slot = 0; slot < last && tables[slot] != xfm; slot++);
+ if (tables[slot] == xfm) {
+ tables[slot] = NULL;
+ ncf->driver->copy_augeas_xfm = 1;
+ }
+ return 0;
+}
+
+/* Get the Augeas instance; if we already initialized it, just return
+ * it. Otherwise, create a new one and return that.
+ */
+struct augeas *get_augeas(struct netcf *ncf) {
+ int r;
+
+ if (ncf->driver->augeas == NULL) {
+ struct augeas *aug;
+ char *path;
+
+ r = xasprintf(&path, "%s/lenses", ncf->data_dir);
+ ERR_NOMEM(r < 0, ncf);
+
+ aug = aug_init(ncf->root, path, AUG_NO_MODL_AUTOLOAD);
+ FREE(path);
+ ERR_THROW(aug == NULL, ncf, EOTHER, "aug_init failed");
+ ncf->driver->augeas = aug;
+ ncf->driver->copy_augeas_xfm = 1;
+ }
+
+ if (ncf->driver->copy_augeas_xfm) {
+ struct augeas *aug = ncf->driver->augeas;
+ /* Only look at a few config files */
+ r = aug_rm(aug, "/augeas/load/*");
+ ERR_THROW(r < 0, ncf, EOTHER, "aug_rm failed in get_augeas");
+
+ for (int slot = 0; slot < ncf->driver->augeas_xfm_num_tables; slot++) {
+ const struct augeas_xfm_table *t =
+ ncf->driver->augeas_xfm_tables[slot];
+ if (t == NULL)
+ continue;
+ for (int i=0; i < t->size; i++) {
+ r = aug_set(aug, t->pv[i].path, t->pv[i].value);
+ ERR_THROW(r < 0, ncf, EOTHER,
+ "transform setup failed to set %s",
+ t->pv[i].path);
+ }
+ }
+ ncf->driver->copy_augeas_xfm = 0;
+ ncf->driver->load_augeas = 1;
+ }
+
+ if (ncf->driver->load_augeas) {
+ struct augeas *aug = ncf->driver->augeas;
+
+ r = aug_load(aug);
+ ERR_THROW(r < 0, ncf, EOTHER, "failed to load config files");
+
+ /* FIXME: we need to produce _much_ better diagnostics here - need
+ * to analyze what came back in /augeas//error; ultimately, we need
+ * to understand whether this is harmless or a real error. For real
+ * errors, we need to return an error.
+ */
+ r = aug_match(aug, "/augeas//error", NULL);
+ if (r > 0 && NCF_DEBUG(ncf)) {
+ fprintf(stderr, "warning: augeas initialization had errors\n");
+ fprintf(stderr, "please file a bug with the following lines in the bug report:\n");
+ aug_print(aug, stderr, "/augeas//error");
+ }
+ ERR_THROW(r > 0, ncf, EOTHER, "errors in loading some config files");
+ ncf->driver->load_augeas = 0;
+ }
+ return ncf->driver->augeas;
+ error:
+ aug_close(ncf->driver->augeas);
+ ncf->driver->augeas = NULL;
+ return NULL;
+}
+
+ATTRIBUTE_FORMAT(printf, 4, 5)
+int defnode(struct netcf *ncf, const char *name, const char *value,
+ const char *format, ...) {
+ struct augeas *aug = get_augeas(ncf);
+ va_list ap;
+ char *expr = NULL;
+ int r, created;
+
+ va_start(ap, format);
+ r = vasprintf (&expr, format, ap);
+ va_end (ap);
+ if (r < 0)
+ expr = NULL;
+ ERR_NOMEM(r < 0, ncf);
+
+ r = aug_defnode(aug, name, expr, value, &created);
+ ERR_THROW(r < 0, ncf, EOTHER, "failed to define node %s", name);
+
+ /* Fallthrough intentional */
+ error:
+ free(expr);
+ return (r < 0) ? -1 : created;
+}
+
+int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...) {
+ struct augeas *aug = NULL;
+ char *path = NULL;
+ va_list args;
+ int r;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ va_start(args, fmt);
+ r = vasprintf(&path, fmt, args);
+ va_end(args);
+ if (r < 0) {
+ path = NULL;
+ ERR_NOMEM(1, ncf);
+ }
+
+ r = aug_match(aug, path, matches);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ free(path);
+ return r;
+ error:
+ free(path);
+ return -1;
+}
+
+void free_matches(int nint, char ***intf) {
+ if (*intf != NULL) {
+ for (int i=0; i < nint; i++)
+ FREE((*intf)[i]);
+ FREE(*intf);
+ }
+}
+
/* Returns a list of all interfaces with MAC address MAC */
int aug_match_mac(struct netcf *ncf, const char *mac, char ***matches) {
int nmatches;
@@ -152,6 +490,616 @@ void modprobed_unalias_bond(struct netcf *ncf, const char *name) {
}
/*
+ * ioctl and netlink-related utilities
+ */
+
+int init_ioctl_fd(struct netcf *ncf) {
+ int ioctl_fd;
+ int flags;
+
+ ioctl_fd = socket(AF_INET, SOCK_STREAM, 0);
+ ERR_THROW(ioctl_fd < 0, ncf, EINTERNAL, "failed to open socket for interface ioctl");
+
+ flags = fcntl(ioctl_fd, F_GETFD);
+ ERR_THROW(flags < 0, ncf, EINTERNAL, "failed to get flags for ioctl socket");
+
+ flags = fcntl(ioctl_fd, F_SETFD, flags | FD_CLOEXEC);
+ ERR_THROW(flags < 0, ncf, EINTERNAL, "failed to set FD_CLOEXEC flag on ioctl socket");
+ return ioctl_fd;
+
+error:
+ if (ioctl_fd >= 0)
+ close(ioctl_fd);
+ return -1;
+}
+
+int if_is_active(struct netcf *ncf, const char *intf) {
+ struct ifreq ifr;
+
+ MEMZERO(&ifr, 1);
+ strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
+ if (ioctl(ncf->driver->ioctl_fd, SIOCGIFFLAGS, &ifr)) {
+ return 0;
+ }
+ return ((ifr.ifr_flags & IFF_UP) == IFF_UP);
+}
+
+netcf_if_type_t if_type(struct netcf *ncf, const char *intf) {
+ char *path;
+ struct stat stats;
+ netcf_if_type_t ret = NETCF_IFACE_TYPE_NONE;
+
+ xasprintf(&path, "/proc/net/vlan/%s", intf);
+ ERR_NOMEM(path == NULL, ncf);
+ if ((stat (path, &stats) == 0) && S_ISREG (stats.st_mode)) {
+ ret = NETCF_IFACE_TYPE_VLAN;
+ }
+ FREE(path);
+
+ if (ret == NETCF_IFACE_TYPE_NONE) {
+ xasprintf(&path, "/sys/class/net/%s/bridge", intf);
+ ERR_NOMEM(path == NULL, ncf);
+ if (stat (path, &stats) == 0 && S_ISDIR (stats.st_mode))
+ ret = NETCF_IFACE_TYPE_BRIDGE;
+ FREE(path);
+ }
+ if (ret == NETCF_IFACE_TYPE_NONE) {
+ xasprintf(&path, "/sys/class/net/%s/bonding", intf);
+ ERR_NOMEM(path == NULL, ncf);
+ if (stat (path, &stats) == 0 && S_ISDIR (stats.st_mode))
+ ret = NETCF_IFACE_TYPE_BOND;
+ FREE(path);
+ }
+ if (ret == NETCF_IFACE_TYPE_NONE)
+ ret = NETCF_IFACE_TYPE_ETHERNET;
+
+error:
+ FREE(path);
+ return ret;
+}
+
+/* Given a netcf_if_type_t, return a const char * representation */
+const char *if_type_str(netcf_if_type_t type) {
+ switch (type) {
+ case NETCF_IFACE_TYPE_ETHERNET:
+ return "ethernet";
+ case NETCF_IFACE_TYPE_BOND:
+ return "bond";
+ case NETCF_IFACE_TYPE_BRIDGE:
+ return "bridge";
+ case NETCF_IFACE_TYPE_VLAN:
+ return "vlan";
+ default:
+ return NULL;
+ }
+}
+
+static int if_bridge_phys_name(struct netcf *ncf,
+ const char *intf, char ***phys_names) {
+ /* We can learn the name of the physical interface associated with
+ * this bridge by looking for the names of the links in
+ * /sys/class/net/$ifname/brif.
+ *
+ * The caller of this function must free the array of strings that is
+ * returned.
+ *
+ */
+ int r, ii, ret = 0;
+ char *dirpath = NULL;
+ DIR *dir = NULL;
+
+ *phys_names = NULL;
+
+ xasprintf(&dirpath, "/sys/class/net/%s/brif", intf);
+ ERR_NOMEM(dirpath == NULL, ncf);
+
+ dir = opendir(dirpath);
+ if (dir != NULL) {
+ struct dirent *d;
+
+ while ((d = readdir (dir)) != NULL) {
+ if (STRNEQ(d->d_name, ".") && STRNEQ(d->d_name, "..")) {
+ r = REALLOC_N(*phys_names, ret + 1);
+ ERR_NOMEM(r < 0, ncf);
+ ret++;
+ xasprintf(&((*phys_names)[ret - 1]), "%s", d->d_name);
+ ERR_NOMEM((*phys_names)[ret - 1] == NULL, ncf);
+ }
+ }
+ }
+ goto done;
+
+error:
+ for (ii = 0; ii < ret; ii++)
+ FREE((*phys_names)[ii]);
+ FREE(*phys_names);
+ *phys_names = NULL;
+ ret = -1;
+
+done:
+ if (dir)
+ closedir (dir);
+ FREE(dirpath);
+ return ret;
+
+}
+
+
+int netlink_init(struct netcf *ncf) {
+
+ ncf->driver->nl_sock = nl_handle_alloc();
+ if (ncf->driver->nl_sock == NULL)
+ goto error;
+ if (nl_connect(ncf->driver->nl_sock, NETLINK_ROUTE) < 0) {
+ goto error;
+ }
+
+ ncf->driver->link_cache = rtnl_link_alloc_cache(ncf->driver->nl_sock);
+ if (ncf->driver->link_cache == NULL) {
+ goto error;
+ }
+ nl_cache_mngt_provide(ncf->driver->link_cache);
+
+ ncf->driver->addr_cache = rtnl_addr_alloc_cache(ncf->driver->nl_sock);
+ if (ncf->driver->addr_cache == NULL) {
+ goto error;
+ }
+ nl_cache_mngt_provide(ncf->driver->addr_cache);
+
+ int netlink_fd = nl_socket_get_fd(ncf->driver->nl_sock);
+ if (netlink_fd >= 0)
+ fcntl(netlink_fd, F_SETFD, FD_CLOEXEC);
+ return 0;
+
+error:
+ netlink_close(ncf);
+ return -1;
+}
+
+int netlink_close(struct netcf *ncf) {
+
+ if (ncf->driver->addr_cache) {
+ nl_cache_free(ncf->driver->addr_cache);
+ ncf->driver->addr_cache = NULL;
+ }
+ if (ncf->driver->link_cache) {
+ nl_cache_free(ncf->driver->link_cache);
+ ncf->driver->link_cache = NULL;
+ }
+ if (ncf->driver->nl_sock) {
+ nl_close(ncf->driver->nl_sock);
+ nl_handle_destroy(ncf->driver->nl_sock);
+ ncf->driver->nl_sock = NULL;
+ }
+ return 0;
+}
+
+
+static void add_type_specific_info(struct netcf *ncf,
+ const char *ifname, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root);
+
+/* Data that needs to be preserved between calls to the libnl iterator
+ * callback.
+ */
+struct nl_ip_callback_data {
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ xmlNodePtr protov4;
+ xmlNodePtr protov6;
+ struct netcf *ncf;
+};
+
+/* add all ip addresses for the given interface to the xml document
+*/
+static void add_ip_info_cb(struct nl_object *obj, void *arg) {
+ struct nl_ip_callback_data *cb_data = arg;
+ struct rtnl_addr *addr = (struct rtnl_addr *)obj;
+ struct netcf *ncf = cb_data->ncf;
+
+ struct nl_addr *local_addr;
+ int family, prefix;
+ const char *family_str;
+ char ip_str[48];
+ char prefix_str[16];
+ xmlNodePtr *proto, ip_node, cur;
+ xmlAttrPtr prop = NULL;
+
+ local_addr = rtnl_addr_get_local(addr);
+ family = nl_addr_get_family(local_addr);
+ switch (family) {
+ case AF_INET:
+ family_str = "ipv4";
+ proto = &cb_data->protov4;
+ break;
+ case AF_INET6:
+ family_str = "ipv6";
+ proto = &cb_data->protov6;
+ break;
+
+ default:
+ /* Nothing that interests us in this entry */
+ return;
+ }
+
+ inet_ntop(family, nl_addr_get_binary_addr(local_addr),
+ ip_str, sizeof(ip_str));
+ prefix = nl_addr_get_prefixlen(local_addr);
+
+ if (*proto == NULL) {
+ /* We haven't dont anything with this proto yet. Search for an
+ * existing node.
+ */
+ for (cur = cb_data->root->children; cur != NULL; cur = cur->next) {
+ if ((cur->type == XML_ELEMENT_NODE) &&
+ xmlStrEqual(cur->name, BAD_CAST "protocol")) {
+ xmlChar *node_family = xmlGetProp(cur, BAD_CAST "family");
+ if (node_family != NULL) {
+ if (xmlStrEqual(node_family, BAD_CAST family_str))
+ *proto = cur;
+ xmlFree(node_family);
+ if (*proto != NULL) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (*proto == NULL) {
+ /* No node exists for this protocol family. Create one.
+ */
+ *proto = xml_new_node(cb_data->doc, cb_data->root, "protocol");
+ ERR_NOMEM(*proto == NULL, ncf);
+ prop = xmlSetProp(*proto, BAD_CAST "family", BAD_CAST family_str);
+ ERR_NOMEM(prop == NULL, ncf);
+
+ }
+
+ /* Create a new ip node for this address/prefix, and set the
+ * properties
+ */
+ ip_node = xml_new_node(cb_data->doc, *proto, "ip");
+ ERR_NOMEM(ip_node == NULL, ncf);
+ prop = xmlSetProp(ip_node, BAD_CAST "address", BAD_CAST ip_str);
+ ERR_NOMEM(prop == NULL, ncf);
+ snprintf(prefix_str, sizeof(prefix_str), "%d", prefix);
+ prop = xmlSetProp(ip_node, BAD_CAST "prefix", BAD_CAST prefix_str);
+ ERR_NOMEM(prop == NULL, ncf);
+
+error:
+ return;
+}
+
+static void add_ip_info(struct netcf *ncf,
+ const char *ifname ATTRIBUTE_UNUSED, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root) {
+ struct nl_ip_callback_data cb_data
+ = { doc, root, NULL, NULL, ncf };
+ struct rtnl_addr *filter_addr = NULL;
+
+ filter_addr = rtnl_addr_alloc();
+ ERR_NOMEM(filter_addr == NULL, ncf);
+
+ rtnl_addr_set_ifindex(filter_addr, ifindex);
+ nl_cache_foreach_filter(ncf->driver->addr_cache,
+ OBJ_CAST(filter_addr), add_ip_info_cb,
+ &cb_data);
+error:
+ if (filter_addr)
+ rtnl_addr_put(filter_addr);
+ return;
+}
+
+
+struct nl_ethernet_callback_data {
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ xmlNodePtr mac;
+ struct netcf *ncf;
+};
+
+static void add_ethernet_info_cb(struct nl_object *obj, void *arg) {
+ struct nl_ethernet_callback_data *cb_data = arg;
+ struct rtnl_link *iflink = (struct rtnl_link *)obj;
+ struct netcf *ncf = cb_data->ncf;
+
+ struct nl_addr *addr;
+ xmlAttrPtr prop = NULL;
+
+ if ((cb_data->mac == NULL)
+ && ((addr = rtnl_link_get_addr(iflink)) != NULL)
+ && !nl_addr_iszero(addr)) {
+
+ char mac_str[64];
+
+ nl_addr2str(addr, mac_str, sizeof(mac_str));
+ cb_data->mac = xml_node(cb_data->doc, cb_data->root, "mac");
+ ERR_NOMEM(cb_data->mac == NULL, ncf);
+ prop = xmlSetProp(cb_data->mac, BAD_CAST "address", BAD_CAST mac_str);
+ ERR_NOMEM(prop == NULL, ncf);
+ }
+error:
+ return;
+}
+
+static void add_ethernet_info(struct netcf *ncf,
+ const char *ifname ATTRIBUTE_UNUSED, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root) {
+ struct nl_ethernet_callback_data cb_data
+ = { doc, root, NULL, ncf };
+ struct rtnl_link *filter_link = NULL;
+
+ filter_link = rtnl_link_alloc();
+ ERR_NOMEM(filter_link == NULL, ncf);
+
+ rtnl_link_set_ifindex(filter_link, ifindex);
+ nl_cache_foreach_filter(ncf->driver->link_cache,
+ OBJ_CAST(filter_link), add_ethernet_info_cb,
+ &cb_data);
+error:
+ if (filter_link)
+ rtnl_link_put(filter_link);
+ return;
+}
+
+struct nl_vlan_callback_data {
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ xmlNodePtr vlan;
+ struct netcf *ncf;
+};
+
+static void add_vlan_info_cb(struct nl_object *obj, void *arg) {
+ struct nl_vlan_callback_data *cb_data = arg;
+ struct rtnl_link *iflink = (struct rtnl_link *)obj;
+ struct netcf *ncf = cb_data->ncf;
+
+ struct rtnl_link *master_link;
+ char *master_name = NULL;
+ int l_link, vlan_id, master_ifindex;
+ char vlan_id_str[16];
+ char *link_type;
+ xmlNodePtr interface_node;
+ xmlAttrPtr prop = NULL;
+
+ /* If this really is a vlan link, get the master interface and vlan id.
+ */
+ if (cb_data->vlan != NULL)
+ return;
+
+ link_type = rtnl_link_get_info_type(iflink);
+ if ((link_type == NULL) || STRNEQ(link_type, "vlan"))
+ return;
+
+ l_link = rtnl_link_get_link(iflink);
+ if (l_link == RTNL_LINK_NOT_FOUND)
+ return;
+
+ master_link = rtnl_link_get(nl_object_get_cache(obj), l_link);
+ if (master_link == NULL)
+ return;
+
+ master_name = rtnl_link_get_name(master_link);
+ if (master_name == NULL)
+ return;
+
+
+ cb_data->vlan = xml_node(cb_data->doc, cb_data->root, "vlan");
+ ERR_NOMEM(cb_data->vlan == NULL, ncf);
+
+ vlan_id = rtnl_link_vlan_get_id(iflink);
+ snprintf(vlan_id_str, sizeof(vlan_id_str), "%d", vlan_id);
+ prop = xmlSetProp(cb_data->vlan, BAD_CAST "tag", BAD_CAST vlan_id_str);
+ ERR_NOMEM(prop == NULL, ncf);
+
+ interface_node = xml_new_node(cb_data->doc, cb_data->vlan, "interface");
+ ERR_NOMEM(interface_node == NULL, ncf);
+
+ /* Add in type-specific info of master interface */
+ master_ifindex = rtnl_link_name2i(ncf->driver->link_cache, master_name);
+ ERR_THROW((master_ifindex == RTNL_LINK_NOT_FOUND), ncf, ENETLINK,
+ "couldn't find ifindex for vlan master interface `%s`",
+ master_name);
+ add_type_specific_info(ncf, master_name, master_ifindex,
+ cb_data->doc, interface_node);
+
+error:
+ return;
+}
+
+static void add_vlan_info(struct netcf *ncf,
+ const char *ifname ATTRIBUTE_UNUSED, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root) {
+ struct nl_vlan_callback_data cb_data
+ = { doc, root, NULL, ncf };
+ struct rtnl_link *filter_link = NULL;
+
+ filter_link = rtnl_link_alloc();
+ ERR_NOMEM(filter_link == NULL, ncf);
+
+ rtnl_link_set_ifindex(filter_link, ifindex);
+ nl_cache_foreach_filter(ncf->driver->link_cache,
+ OBJ_CAST(filter_link), add_vlan_info_cb,
+ &cb_data);
+ ERR_BAIL(ncf);
+error:
+ if (filter_link)
+ rtnl_link_put(filter_link);
+ return;
+}
+
+static void add_bridge_info(struct netcf *ncf,
+ const char *ifname, int ifindex ATTRIBUTE_UNUSED,
+ xmlDocPtr doc, xmlNodePtr root) {
+ char **phys_names;
+ int nphys, ii;
+ xmlNodePtr bridge_node = NULL, interface_node = NULL;
+
+ nphys = if_bridge_phys_name(ncf, ifname, &phys_names);
+ if (nphys <= 0)
+ return;
+
+ bridge_node = xml_node(doc, root, "bridge");
+ ERR_NOMEM(bridge_node == NULL, ncf);
+
+ for (ii = 0; ii < nphys; ii++) {
+ int phys_ifindex;
+
+ interface_node = xml_new_node(doc, bridge_node, "interface");
+ ERR_NOMEM(interface_node == NULL, ncf);
+
+ /* Add in type-specific info of physical interface */
+ phys_ifindex =
+ rtnl_link_name2i(ncf->driver->link_cache, phys_names[ii]);
+ ERR_THROW((phys_ifindex == RTNL_LINK_NOT_FOUND), ncf, ENETLINK,
+ "couldn't find ifindex for physical interface `%s` of bridge %s",
+ phys_names[ii], ifname);
+
+ add_type_specific_info(ncf, phys_names[ii], phys_ifindex, doc,
+ interface_node);
+ }
+
+error:
+ for (ii = 0; ii < nphys; ii++)
+ FREE(phys_names[ii]);
+ FREE(phys_names);
+}
+
+
+struct nl_bond_callback_data {
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ xmlNodePtr bond;
+ int master_ifindex;
+ struct netcf *ncf;
+};
+
+static void add_bond_info_cb(struct nl_object *obj,
+ void *arg ATTRIBUTE_UNUSED) {
+ struct nl_bond_callback_data *cb_data = arg;
+ struct rtnl_link *iflink = (struct rtnl_link *)obj;
+ struct netcf *ncf = cb_data->ncf;
+
+ xmlNodePtr interface_node;
+
+ /* If this is a slave link, and the master is master_ifindex, add the
+ * interface info to the bond.
+ */
+
+ if (!(rtnl_link_get_flags(iflink) & IFF_SLAVE)
+ || rtnl_link_get_master(iflink) != cb_data->master_ifindex)
+ return;
+
+ cb_data->bond = xml_node(cb_data->doc, cb_data->root, "bond");
+ ERR_NOMEM(cb_data->bond == NULL, ncf);
+
+ /* XXX - if we learn where to get bridge "mode" property, set it here */
+
+ /* XXX - need to add node like one of these:
+ *
+ * <miimon freq="100" updelay="10" carrier="ioctl"/>
+ * or
+ * <arpmode interval='something' target='something'>
+ */
+
+ /* add a new interface node */
+ interface_node = xml_new_node(cb_data->doc, cb_data->bond, "interface");
+ ERR_NOMEM(interface_node == NULL, ncf);
+
+ /* Add in type-specific info of this slave interface */
+ add_type_specific_info(ncf, rtnl_link_get_name(iflink),
+ rtnl_link_get_ifindex(iflink),
+ cb_data->doc, interface_node);
+error:
+ return;
+}
+
+static void add_bond_info(struct netcf *ncf,
+ const char *ifname ATTRIBUTE_UNUSED, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root) {
+ struct nl_bond_callback_data cb_data
+ = { doc, root, NULL, ifindex, ncf };
+
+ nl_cache_foreach(ncf->driver->link_cache, add_bond_info_cb, &cb_data);
+}
+
+
+static void add_type_specific_info(struct netcf *ncf,
+ const char *ifname, int ifindex,
+ xmlDocPtr doc, xmlNodePtr root) {
+ xmlAttrPtr prop;
+ netcf_if_type_t iftype;
+ const char *iftype_str;
+
+ prop = xmlNewProp(root, BAD_CAST "name", BAD_CAST ifname);
+ ERR_NOMEM(prop == NULL, ncf);
+
+ iftype = if_type(ncf, ifname);
+ ERR_BAIL(ncf);
+ iftype_str = if_type_str(iftype);
+
+ if (iftype_str) {
+ prop = xmlSetProp(root, BAD_CAST "type", BAD_CAST if_type_str(iftype));
+ ERR_NOMEM(prop == NULL, ncf);
+ }
+
+ switch (iftype) {
+ case NETCF_IFACE_TYPE_ETHERNET:
+ add_ethernet_info(ncf, ifname, ifindex, doc, root);
+ break;
+ case NETCF_IFACE_TYPE_BRIDGE:
+ add_bridge_info(ncf, ifname, ifindex, doc, root);
+ break;
+ case NETCF_IFACE_TYPE_VLAN:
+ add_vlan_info(ncf, ifname, ifindex, doc, root);
+ break;
+ case NETCF_IFACE_TYPE_BOND:
+ add_bond_info(ncf, ifname, ifindex, doc, root);
+ break;
+ default:
+ break;
+ }
+error:
+ return;
+}
+
+void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc) {
+ xmlNodePtr root;
+ int ifindex, code;
+
+ root = xmlDocGetRootElement(doc);
+ ERR_THROW((root == NULL), nif->ncf, EINTERNAL,
+ "failed to get document root element");
+ ERR_THROW(!xmlStrEqual(root->name, BAD_CAST "interface"),
+ nif->ncf, EINTERNAL, "root document is not an interface");
+
+ /* Update the caches with any recent changes */
+ code = nl_cache_refill(nif->ncf->driver->nl_sock,
+ nif->ncf->driver->link_cache);
+ ERR_THROW((code < 0), nif->ncf, ENETLINK,
+ "failed to refill interface index cache");
+ code = nl_cache_refill(nif->ncf->driver->nl_sock,
+ nif->ncf->driver->addr_cache);
+ ERR_THROW((code < 0), nif->ncf, ENETLINK,
+ "failed to refill interface address cache");
+
+ ifindex = rtnl_link_name2i(nif->ncf->driver->link_cache, nif->name);
+ ERR_THROW((ifindex == RTNL_LINK_NOT_FOUND), nif->ncf, ENETLINK,
+ "couldn't find ifindex for interface `%s`", nif->name);
+
+ add_type_specific_info(nif->ncf, nif->name, ifindex, doc, root);
+ ERR_BAIL(nif->ncf);
+
+ add_ip_info(nif->ncf, nif->name, ifindex, doc, root);
+ ERR_BAIL(nif->ncf);
+
+error:
+ return;
+}
+
+/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
diff --git a/src/dutil_linux.h b/src/dutil_linux.h
index af76c3e..0f8b14a 100644
--- a/src/dutil_linux.h
+++ b/src/dutil_linux.h
@@ -23,6 +23,50 @@
#ifndef DUTIL_LINUX_H_
#define DUTIL_LINUX_H_
+#include <netlink/netlink.h>
+
+struct driver {
+ struct augeas *augeas;
+ xsltStylesheetPtr put;
+ xsltStylesheetPtr get;
+ int ioctl_fd;
+ struct nl_handle *nl_sock;
+ struct nl_cache *link_cache;
+ struct nl_cache *addr_cache;
+ unsigned int load_augeas : 1;
+ unsigned int copy_augeas_xfm : 1;
+ unsigned int augeas_xfm_num_tables;
+ const struct augeas_xfm_table **augeas_xfm_tables;
+};
+
+/* run an external program */
+int run_program(struct netcf *ncf, const char *const *argv);
+void run1(struct netcf *ncf, const char *prog, const char *arg);
+
+/* Add a table of transformations that the next GET_AUGEAS should run */
+int add_augeas_xfm_table(struct netcf *ncf,
+ const struct augeas_xfm_table *table);
+
+/* Remove a table of transformations that the next GET_AUGEAS should run */
+int remove_augeas_xfm_table(struct netcf *ncf,
+ const struct augeas_xfm_table *table);
+
+/* Get or create the augeas instance from NCF */
+struct augeas *get_augeas(struct netcf *ncf);
+
+/* Define a node inside the augeas tree */
+ATTRIBUTE_FORMAT(printf, 4, 5)
+int defnode(struct netcf *ncf, const char *name, const char *value,
+ const char *format, ...);
+
+/* Format a path by doing a printf of FMT and the var args, then call
+ AUG_MATCH on that path. Sets NCF->ERRCODE on error */
+ATTRIBUTE_FORMAT(printf, 3, 4)
+int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...);
+
+/* Free matches from aug_match (or aug_submatch) */
+void free_matches(int nint, char ***intf);
+
/* Returns a list of all interfaces with MAC address MAC */
int aug_match_mac(struct netcf *ncf, const char *mac, char ***matches);
@@ -35,7 +79,6 @@ int aug_match_mac(struct netcf *ncf, const char *mac, char ***matches);
*/
int aug_get_mac(struct netcf *ncf, const char *name, const char **mac);
-
/* Add an 'alias NAME bonding' to an appropriate file in /etc/modprobe.d,
* if none exists yet. If we need to create a new one, it goes into the
* file netcf.conf.
@@ -45,6 +88,41 @@ void modprobed_alias_bond(struct netcf *ncf, const char *name);
/* Remove an 'alias NAME bonding' as created by modprobed_alias_bond */
void modprobed_unalias_bond(struct netcf *ncf, const char *name);
+/* Get a file descriptor to a ioctl socket */
+int init_ioctl_fd(struct netcf *ncf);
+
+/* setup the netlink socket */
+int netlink_init(struct netcf *ncf);
+
+/*shutdown the netlink socket and release its resources */
+int netlink_close(struct netcf *ncf);
+
+/* Check if the interface INTF is up using an ioctl call */
+int if_is_active(struct netcf *ncf, const char *intf);
+
+/* Interface types recognized by netcf. */
+typedef enum {
+ NETCF_IFACE_TYPE_NONE = 0, /* not yet determined */
+ NETCF_IFACE_TYPE_ETHERNET, /* any physical device is "ethernet" */
+ NETCF_IFACE_TYPE_BOND,
+ NETCF_IFACE_TYPE_BRIDGE,
+ NETCF_IFACE_TYPE_VLAN,
+} netcf_if_type_t;
+
+/* Return the type of the interface.
+ */
+netcf_if_type_t if_type(struct netcf *ncf, const char *intf);
+
+/* Given a netcf_if_type_t enum value, return a const char *representation
+ * This pointer has an indefinite life, and shouldn't be / can't be free'd.
+ */
+const char *if_type_str(netcf_if_type_t type);
+
+/* Add the state of the interface (currently all addresses + netmasks)
+ * to its xml document.
+ */
+void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc);
+
#endif
/*
diff --git a/src/internal.h b/src/internal.h
index d06665d..b7fa5f2 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -31,6 +31,7 @@
#include <stdarg.h>
#include <libxslt/transform.h>
+#include <libxml/relaxng.h>
/*
* Macros for gcc's attributes
@@ -129,14 +130,26 @@
} \
} while(0)
+/* Clear error code and details */
+#define API_ENTRY(ncf) \
+ do { \
+ (ncf)->errcode = NETCF_NOERROR; \
+ FREE((ncf)->errdetails); \
+ if (ncf->driver != NULL) \
+ drv_entry(ncf); \
+ } while(0);
+
/*
* netcf structures and internal API's
*/
+struct driver;
+
struct netcf {
ref_t ref;
char *root; /* The filesystem root, always ends
* with '/' */
const char *data_dir; /* Where to find stylesheets etc. */
+ xmlRelaxNGPtr rng; /* RNG of <interface> elements */
netcf_errcode_t errcode;
char *errdetails; /* Error details */
struct driver *driver; /* Driver specific data */
@@ -151,23 +164,11 @@ struct netcf_if {
drv_mac_string */
};
-void free_netcf_if(struct netcf_if *nif);
-
#define NCF_DEBUG(ncf) ((ncf)->debug)
-/*
- * Error reporting
- */
-void report_error(struct netcf *ncf, netcf_errcode_t errcode,
- const char *format, ...)
- ATTRIBUTE_FORMAT(printf, 3, 4);
-
-void vreport_error(struct netcf *ncf, netcf_errcode_t errcode,
- const char *format, va_list ap)
- ATTRIBUTE_FORMAT(printf, 3, 0);
-
/* The interface to the driver (backend). The appropriate driver is
- * selected at build time from the available drivers in drv_*
+ * selected at build time from the available drivers in drv_*; each of
+ * these files should include definitions for all the drv_* functions.
*/
int drv_init(struct netcf *netcf);
void drv_close(struct netcf *netcf);
@@ -186,22 +187,10 @@ struct netcf_if *drv_define(struct netcf *ncf, const char *xml);
int drv_undefine(struct netcf_if *nif);
int drv_if_up(struct netcf_if *nif);
int drv_if_down(struct netcf_if *nif);
-int drv_get_aug(struct netcf *, const char *ncf_xml, char **aug_xml);
-int drv_put_aug(struct netcf *, const char *aug_xml, char **ncf_xml);
-
-/*
- * Internally used utilities
- */
-int run_program(struct netcf *ncf, const char *const *argv);
-char *argv_to_string(const char *const *argv);
-
-/*
- * XSLT extension functions in xslt_ext.c
- */
-int xslt_register_exts(xsltTransformContextPtr ctxt);
/*
- * Useful for debugging, used by ncftransform
+ * Useful for debugging, used by ncftransform (only needed for
+ * the initscripts version of netcf)
*/
/* Transform the NCF_XML into simple Augeas XML AUG_XML */
diff --git a/src/netcf.c b/src/netcf.c
index 1a95332..9440ed0 100644
--- a/src/netcf.c
+++ b/src/netcf.c
@@ -27,9 +27,6 @@
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
#include <errno.h>
#include "safe-alloc.h"
@@ -37,15 +34,6 @@
#include "netcf.h"
#include "dutil.h"
-/* Clear error code and details */
-#define API_ENTRY(ncf) \
- do { \
- (ncf)->errcode = NETCF_NOERROR; \
- FREE((ncf)->errdetails); \
- if (ncf->driver != NULL) \
- drv_entry(ncf); \
- } while(0);
-
/* Human-readable error messages. This array is indexed by NETCF_ERRCODE_T */
static const char *const errmsgs[] = {
"no error", /* NOERROR */
@@ -63,46 +51,28 @@ static const char *const errmsgs[] = {
"NETLINK socket operation failed" /* ENETLINK */
};
-static void free_netcf(struct netcf *ncf) {
- if (ncf == NULL)
- return;
-
- assert(ncf->ref == 0);
- free(ncf->root);
- free(ncf);
-}
-
-void free_netcf_if(struct netcf_if *nif) {
- if (nif == NULL)
- return;
-
- assert(nif->ref == 0);
- unref(nif->ncf, netcf);
- free(nif->name);
- free(nif->mac);
- free(nif);
-}
-
int ncf_init(struct netcf **ncf, const char *root) {
*ncf = NULL;
if (make_ref(*ncf) < 0)
- goto oom;
+ goto error;
if (root == NULL)
root = "/";
if (root[strlen(root)-1] == '/') {
(*ncf)->root = strdup(root);
} else {
if (xasprintf(&(*ncf)->root, "%s/", root) < 0)
- goto oom;
+ goto error;
}
if ((*ncf)->root == NULL)
- goto oom;
+ goto error;
(*ncf)->data_dir = getenv("NETCF_DATADIR");
if ((*ncf)->data_dir == NULL)
(*ncf)->data_dir = DATADIR "/netcf";
(*ncf)->debug = getenv("NETCF_DEBUG") != NULL;
+ (*ncf)->rng = rng_parse(*ncf, "interface.rng");
+ ERR_BAIL(*ncf);
return drv_init(*ncf);
- oom:
+error:
ncf_close(*ncf);
*ncf = NULL;
return -2;
@@ -117,6 +87,7 @@ int ncf_close(struct netcf *ncf) {
ERR_COND_BAIL(ncf->ref > 1, ncf, EINUSE);
drv_close(ncf);
+ xmlRelaxNGFree(ncf->rng);
unref(ncf, netcf);
return 0;
error:
@@ -248,207 +219,6 @@ int ncf_error(struct netcf *ncf, const char **errmsg, const char **details) {
}
/*
- * Test interface
- */
-int ncf_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
- API_ENTRY(ncf);
-
- return drv_get_aug(ncf, ncf_xml, aug_xml);
-}
-
-int ncf_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
- API_ENTRY(ncf);
-
- return drv_put_aug(ncf, aug_xml, ncf_xml);
-}
-
-/*
- * Internal helpers
- */
-
-static int
-exec_program(struct netcf *ncf,
- const char *const*argv,
- const char *commandline,
- pid_t *pid)
-{
- sigset_t oldmask, newmask;
- struct sigaction sig_action;
- char errbuf[128];
-
- /* commandline is only used for error reporting */
- if (commandline == NULL)
- commandline = argv[0];
- /*
- * Need to block signals now, so that child process can safely
- * kill off caller's signal handlers without a race.
- */
- sigfillset(&newmask);
- if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
- report_error(ncf, NETCF_EEXEC,
- "failed to set signal mask while forking for '%s': %s",
- commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
- goto error;
- }
-
- *pid = fork();
-
- ERR_THROW(*pid < 0, ncf, EEXEC, "failed to fork for '%s': %s",
- commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
-
- if (*pid) { /* parent */
- /* Restore our original signal mask now that the child is
- safely running */
- ERR_THROW(pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0,
- ncf, EEXEC,
- "failed to restore signal mask while forking for '%s': %s",
- commandline, strerror_r(errno, errbuf, sizeof(errbuf)));
- return 0;
- }
-
- /* child */
-
- /* Clear out all signal handlers from parent so nothing unexpected
- can happen in our child once we unblock signals */
-
- sig_action.sa_handler = SIG_DFL;
- sig_action.sa_flags = 0;
- sigemptyset(&sig_action.sa_mask);
-
- int i;
- for (i = 1; i < NSIG; i++) {
- /* Only possible errors are EFAULT or EINVAL
- The former wont happen, the latter we
- expect, so no need to check return value */
-
- sigaction(i, &sig_action, NULL);
- }
-
- /* Unmask all signals in child, since we've no idea what the
- caller's done with their signal mask and don't want to
- propagate that to children */
- sigemptyset(&newmask);
- if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
- /* don't report_error, as it will never be seen anyway */
- _exit(1);
- }
-
- /* close all open file descriptors */
- int openmax = sysconf (_SC_OPEN_MAX);
- for (i = 3; i < openmax; i++)
- close(i);
-
- execvp(argv[0], (char **) argv);
-
- /* if execvp() returns, it has failed */
- /* don't report_error, as it will never be seen anyway */
- _exit(1);
-
-error:
- /* This is cleanup of parent process only - child
- should never jump here on error */
- return -1;
-}
-
-/**
- * Run a command without using the shell.
- *
- * return 0 if the command run and exited with 0 status; Otherwise
- * return -1
- *
- */
-int run_program(struct netcf *ncf, const char *const *argv) {
-
- pid_t childpid;
- int exitstatus, waitret;
- char *argv_str;
- int ret = -1;
- char errbuf[128];
-
- argv_str = argv_to_string(argv);
- ERR_NOMEM(argv_str == NULL, ncf);
-
- exec_program(ncf, argv, argv_str, &childpid);
- ERR_BAIL(ncf);
-
- while ((waitret = waitpid(childpid, &exitstatus, 0) == -1) &&
- errno == EINTR) {
- /* empty loop */
- }
-
- ERR_THROW(waitret == -1, ncf, EEXEC,
- "Failed waiting for completion of '%s': %s",
- argv_str, strerror_r(errno, errbuf, sizeof(errbuf)));
- ERR_THROW(!WIFEXITED(exitstatus) && WIFSIGNALED(exitstatus), ncf, EEXEC,
- "'%s' terminated by signal: %d",
- argv_str, WTERMSIG(exitstatus));
- ERR_THROW(!WIFEXITED(exitstatus), ncf, EEXEC,
- "'%s' terminated improperly: %d",
- argv_str, WEXITSTATUS(exitstatus));
- ERR_THROW(WEXITSTATUS(exitstatus) != 0, ncf, EEXEC,
- "Running '%s' failed with exit code %d",
- argv_str, WEXITSTATUS(exitstatus));
- ret = 0;
-
-error:
- FREE(argv_str);
- return ret;
-}
-
-/*
- * argv_to_string() is borrowed from libvirt's
- * src/util.c:virArgvToString()
- */
-char *
-argv_to_string(const char *const *argv) {
- int i;
- size_t len;
- char *ret, *p;
-
- for (len = 1, i = 0; argv[i]; i++)
- len += strlen(argv[i]) + 1;
-
- if (ALLOC_N(ret, len) < 0)
- return NULL;
- p = ret;
-
- for (i = 0; argv[i]; i++) {
- if (i != 0)
- *(p++) = ' ';
-
- strcpy(p, argv[i]);
- p += strlen(argv[i]);
- }
-
- *p = '\0';
-
- return ret;
-}
-
-void report_error(struct netcf *ncf, netcf_errcode_t errcode,
- const char *format, ...) {
- va_list ap;
-
- va_start(ap, format);
- vreport_error(ncf, errcode, format, ap);
- va_end(ap);
-}
-
-void vreport_error(struct netcf *ncf, netcf_errcode_t errcode,
- const char *format, va_list ap) {
- /* We only remember the first error */
- if (ncf->errcode != NETCF_NOERROR)
- return;
- assert(ncf->errdetails == NULL);
-
- ncf->errcode = errcode;
- if (format != NULL) {
- if (vasprintf(&(ncf->errdetails), format, ap) < 0)
- ncf->errdetails = NULL;
- }
-}
-
-/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
diff --git a/src/xslt_ext.c b/src/xslt_ext.c
index 07e2b41..2bdc771 100644
--- a/src/xslt_ext.c
+++ b/src/xslt_ext.c
@@ -22,6 +22,7 @@
#include <config.h>
#include "internal.h"
+#include "dutil.h"
#include <errno.h>
#include <arpa/inet.h>
--
1.7.2.3
13 years, 4 months
Re: [netcf-devel] [Ovirt-devel] Updating oVirt Server to the latest Matahari 0.4.0 schema
by apevec@redhat.com
On 11/14/2010 01:46 PM, Nicolas Ochem wrote:
> Thanks for the heads-up. A few remarks/questions :
> 1. we should keep the augeas system for assigning network interfaces because it's the only way to reliably map the correct interfaces to the correct databases entries. Besides there is now support for vlan interfaces creation thru augeas, which is not supported in matahari, I assume. matahari returns all the vlan-tagged interfaces as if there were real interfaces and we have to filter them in host-register
yes, configuring network via augeas will stay. As it seems, netcf doesn't really satisfy requirements, so we'll not use it in Matahari.
> 2. in april there was a series of commits switching ovirt-server from ruby-qpid to ruby-qmf rpm. Those are two implementations of ruby bindings to qmf. (see http://mo.morsi.org/blog/node/288 for more details).
right, ruby-qmf is the way forward, so we should make it work
> I have found ruby-qmf to be unstable and buggy so I reverted to ruby-qpid in my local branch. I would like to know what is the plan in QPID regarding the ruby bindings. Is ruby-qpid ditched in profit of ruby-qmf, or the 2 are going to cohabit forever ? Because I'd really like to push the revert to ovirt-server next. When this is clarified, we can take care of migrating to new matahari.
please file bugs in Fedora/qpid-cpp component (that's SRPM for ruby-qmf)
https://bugzilla.redhat.com/buglist.cgi?component=qpid-cpp&product=Fedora
> 3.what version of matahari is in RHEL 6 ? what version will be in fedora 14 ? wouldn't it be good to maintain an ovirt version for rhel/centos 6 ?
Matahari is not yet in RHEL6, will be in 6.1. Fedora 14 would get latest version which is still being developed on Matahari 'next' branch and that's why I started looking at patching ovirt-server to adjust to it, so that Fedora update doesn't break it.
Alan
13 years, 4 months
Re: [netcf-devel] [Matahari] Updating oVirt Server to the latest Matahari 0.4.0 schema
by Adam Stokes
> > But there's more to it since, netcf can also configure interfaces
> > (not yet in Matahari schema, but it is planned). Currently,
> > ovirt-server provides network configuration which is downloaded by
> > the Node on boot and applied (via augeas). Question is should
> > ovirt-server switch fully to netcf (via Matahari)? I'm not sure how
> > does netcf handle unstable interface names, seems that it relies on
> > them being stable which is not the case in the stateless
> > environment like oVirt Node.
If the device is accessible via /files/sys/class/net/<interface> then netcf will capture anything made available through that file structure.
For example, the mac address can be pulled through reading:
/sys/class/net/<intername>/address
HTH
Adam
13 years, 4 months
[PATCH] Rename driver backend to not be so generic
by Adam Stokes
Rename drv_initscripts driver backend to indicate this being a Linux only set of routines.
---
src/Makefile.am | 2 +-
src/drv_initscripts.c | 1097 -------------------------------------------------
src/drv_linux.c | 1097 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1098 insertions(+), 1098 deletions(-)
delete mode 100644 src/drv_initscripts.c
create mode 100644 src/drv_linux.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9d8a3d7..0c16a22 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@ EXTRA_DIST = netcf_public.syms \
netcf_private.syms
if NETCF_DRIVER_INITSCRIPTS
-DRIVER_SOURCES = drv_initscripts.c dutil.h dutil.c \
+DRIVER_SOURCES = drv_linux.c dutil.h dutil.c \
dutil_linux.h dutil_linux.c
endif
diff --git a/src/drv_initscripts.c b/src/drv_initscripts.c
deleted file mode 100644
index b2d96cb..0000000
--- a/src/drv_initscripts.c
+++ /dev/null
@@ -1,1097 +0,0 @@
-/*
- * drv_initscripts.c: the initscripts backend for netcf
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Author: David Lutterkort <lutter(a)redhat.com>
- */
-
-#include <config.h>
-#include <internal.h>
-
-#include <augeas.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#include "safe-alloc.h"
-#include "ref.h"
-#include "list.h"
-#include "dutil.h"
-#include "dutil_linux.h"
-
-#include <libxml/parser.h>
-#include <libxml/relaxng.h>
-#include <libxml/tree.h>
-#include <libxslt/xslt.h>
-#include <libxslt/xsltInternals.h>
-#include <libxslt/transform.h>
-#include <libxslt/xsltutils.h>
-
-#include <libexslt/exslt.h>
-
-static const char *const network_scripts_path =
- "/files/etc/sysconfig/network-scripts";
-
-static const char *const ifcfg_path =
- "/files/etc/sysconfig/network-scripts/*";
-
-/* Augeas should only load the files we are interested in */
-static const struct augeas_pv augeas_xfm_common_pv[] = {
- /* Ifcfg files */
- { "/augeas/load/Ifcfg/lens", "Sysconfig.lns" },
- { "/augeas/load/Ifcfg/incl",
- "/etc/sysconfig/network-scripts/ifcfg-*" },
- { "/augeas/load/Ifcfg/excl[1]", "*~" },
- { "/augeas/load/Ifcfg/excl[2]", "*.bak" },
- { "/augeas/load/Ifcfg/excl[3]", "*.orig" },
- { "/augeas/load/Ifcfg/excl[4]", "*.rpmnew" },
- { "/augeas/load/Ifcfg/excl[5]", "*.rpmorig" },
- { "/augeas/load/Ifcfg/excl[6]", "*.rpmsave" },
- { "/augeas/load/Ifcfg/excl[7]", "*.augnew" },
- { "/augeas/load/Ifcfg/excl[8]", "*.augsave" },
- /* modprobe config */
- { "/augeas/load/Modprobe/lens", "Modprobe.lns" },
- { "/augeas/load/Modprobe/incl[1]", "/etc/modprobe.d/*" },
- { "/augeas/load/Modprobe/incl[2]", "/etc/modprobe.conf" },
- { "/augeas/load/Modprobe/excl[1]", "*.augnew" },
- { "/augeas/load/Modprobe/excl[2]", "*.augsave" },
- { "/augeas/load/Modprobe/excl[3]", "*.rpmsave" },
- { "/augeas/load/Modprobe/excl[4]", "*.rpmnew" },
- { "/augeas/load/Modprobe/excl[5]", "*~" },
- /* sysfs (choice entries from /class/net) */
- { "/augeas/load/Sysfs/lens", "Netcf.id" },
- { "/augeas/load/Sysfs/incl", "/sys/class/net/*/address" }
-};
-
-static const struct augeas_xfm_table augeas_xfm_common =
- { .size = ARRAY_CARDINALITY(augeas_xfm_common_pv),
- .pv = augeas_xfm_common_pv };
-
-/* Entries in a ifcfg file that tell us that the interface
- * is not a toplevel interface
- */
-static const char *const subif_paths[] = {
- "MASTER", "BRIDGE"
-};
-
-static int is_slave(struct netcf *ncf, const char *intf) {
- for (int s = 0; s < ARRAY_CARDINALITY(subif_paths); s++) {
- int r;
- r = aug_fmt_match(ncf, NULL, "%s/%s", intf, subif_paths[s]);
- if (r != 0)
- return r;
- }
- return 0;
-}
-
-static bool has_ifcfg_file(struct netcf *ncf, const char *name) {
- int nmatches;
-
- nmatches = aug_fmt_match(ncf, NULL,
- "%s[ DEVICE = '%s'"
- " or BRIDGE = '%s'"
- " or MASTER = '%s'"
- " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]/DEVICE",
- ifcfg_path, name, name, name, name);
- return nmatches > 0;
-}
-
-static int cmpstrp(const void *p1, const void *p2) {
- const char *s1 = * (const char **)p1;
- const char *s2 = * (const char **)p2;
- return strcmp(s1, s2);
-}
-
-/* Find the path to the ifcfg file that has the configuration for the
- * interface with MAC address MAC.
- */
-static char *find_ifcfg_path_by_hwaddr(struct netcf *ncf, const char *mac) {
- static const char *const hwaddr_str = "/HWADDR";
-
- int nhwaddr = 0, r;
- char **hwaddr = NULL;
- struct augeas *aug = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- // It would be nice if Augeas provided a way to do case-insensitive
- // matching. For now, we just write it out
- nhwaddr = aug_fmt_match(ncf, &hwaddr, "%s%s", ifcfg_path, hwaddr_str);
- ERR_COND_BAIL(nhwaddr < 0, ncf, EOTHER);
-
- /* Sort because need_config will return the last match in case
- * of multiple matches */
- qsort(hwaddr, nhwaddr, sizeof(*hwaddr), cmpstrp);
- int match = -1;
- for (int i=0; i < nhwaddr; i++) {
- const char *addr;
- r = aug_get(aug, hwaddr[i], &addr);
- ERR_COND_BAIL(r != 1, ncf, EOTHER);
- if (STRCASEEQ(addr, mac))
- match = i;
- }
- if (match != -1) {
- char *path = hwaddr[match];
- hwaddr[match] = NULL;
- path[strlen(path) - strlen(hwaddr_str)] = '\0';
- free_matches(nhwaddr, &hwaddr);
- return path;
- }
- error:
- free_matches(nhwaddr, &hwaddr);
- return NULL;
-}
-
-/* Find the path to the ifcfg file that has the configuration for the
- * interface by checking for an entry 'DEVICE=NAME'
- */
-static char *find_ifcfg_path_by_device(struct netcf *ncf, const char *name) {
- struct augeas *aug = NULL;
- int ndevs = 0;
- char **devs = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- ndevs = aug_fmt_match(ncf, &devs, "%s[DEVICE = '%s']",
- ifcfg_path, name);
- ERR_COND_BAIL(ndevs < 0, ncf, EOTHER);
-
- if (ndevs == 0)
- return NULL;
-
- qsort(devs, ndevs, sizeof(*devs), cmpstrp);
-
- char *path = devs[ndevs - 1];
- devs[ndevs - 1] = NULL;
-
- free_matches(ndevs, &devs);
-
- return path;
- error:
- free_matches(ndevs, &devs);
- return NULL;
-}
-
-/* Find the path to the ifcfg file that has the configuration for
- * the device NAME. The logic follows the need_config function
- * in /etc/sysconfig/network-scripts/network-functions
- */
-static char *find_ifcfg_path(struct netcf *ncf, const char *name) {
- struct augeas *aug = NULL;
- char *path = NULL;
- const char *mac = NULL;
- int r, nmatches;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- /* if ifcfg-NAME exists, use that */
- r = xasprintf(&path, "%s/ifcfg-%s", network_scripts_path, name);
- ERR_NOMEM(r < 0, ncf);
-
- nmatches = aug_match(aug, path, NULL);
- ERR_COND_BAIL(nmatches < 0, ncf, EOTHER);
-
- if (nmatches == 1)
- return path;
-
- FREE(path);
-
- /* Now find the config by MAC, matching on HWADDR */
- r = aug_get_mac(ncf, name, &mac);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
- if (r > 0) {
- path = find_ifcfg_path_by_hwaddr(ncf, mac);
- ERR_BAIL(ncf);
- if (path != NULL)
- return path;
- }
-
- path = find_ifcfg_path_by_device(ncf, name);
- ERR_BAIL(ncf);
-
- return path;
- error:
- FREE(path);
- return NULL;
-}
-
-/* Given NDEVS path to DEVICE entries which may contain duplicate devices,
- * produce a list of canonical paths to the interfaces in INTF and return
- * the number of entries. Return -1 on error
- */
-static int uniq_ifcfg_paths(struct netcf *ncf,
- int ndevs, char **devs,
- char ***intf) {
- struct augeas *aug;
- int r;
- int ndevnames = 0;
- const char **devnames = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- /* List unique device names */
- r = ALLOC_N(devnames, ndevs);
- ERR_NOMEM(r < 0, ncf);
-
- for (int i=0; i < ndevs; i++) {
- const char *name = NULL;
- r = aug_get(aug, devs[i], &name);
- ERR_COND_BAIL(r != 1, ncf, EOTHER);
- int exists = 0;
- for (int j = 0; j < ndevnames; j++)
- if (STREQ(name, devnames[j])) {
- exists = 1;
- break;
- }
- if (!exists)
- devnames[ndevnames++] = name;
- }
- qsort(devnames, ndevnames, sizeof(*devnames), cmpstrp);
-
- /* Find canonical config for each device name */
- r = ALLOC_N(*intf, ndevnames);
- ERR_NOMEM(r < 0, ncf);
- for (int i= 0; i < ndevnames; i++) {
- (*intf)[i] = find_ifcfg_path(ncf, devnames[i]);
- ERR_BAIL(ncf);
- }
-
- FREE(devnames);
- return ndevnames;
-
- error:
- FREE(devnames);
- free_matches(ndevnames, intf);
- return -1;
-}
-
-/* List all configured network devices; returns the number of devices or -1
- * on error. On success, the path to the config file for each interface
- * is returned in INTF
- */
-static int list_ifcfg_paths(struct netcf *ncf, char ***intf) {
- int result = 0, ndevs;
- char **devs = NULL;
-
- ndevs = aug_fmt_match(ncf, &devs, "%s/DEVICE", ifcfg_path);
- ERR_COND_BAIL(ndevs < 0, ncf, EOTHER);
-
- result = uniq_ifcfg_paths(ncf, ndevs, devs, intf);
- ERR_BAIL(ncf);
-
- free_matches(ndevs, &devs);
- return result;
-
- error:
- free_matches(ndevs, &devs);
- return -1;
-}
-
-static int list_interfaces(struct netcf *ncf, char ***intf) {
- int nint = 0, result = 0;
- struct augeas *aug = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- /* Look in augeas for all interfaces */
- nint = list_ifcfg_paths(ncf, intf);
- ERR_BAIL(ncf);
- result = nint;
-
- /* Filter out the interfaces that are slaves/subordinate */
- for (int i = 0; i < result;) {
- if (is_slave(ncf, (*intf)[i])) {
- FREE((*intf)[i]);
- memmove(*intf + i, *intf + i + 1,
- (nint - (i + 1))*sizeof((*intf)[0]));
- result -= 1;
- } else {
- i += 1;
- }
- }
- return result;
- error:
- free_matches(nint, intf);
- return -1;
-}
-
-int drv_init(struct netcf *ncf) {
- int r;
- struct stat stats;
-
- if (ALLOC(ncf->driver) < 0)
- return -1;
-
- ncf->driver->ioctl_fd = -1;
-
- r = add_augeas_xfm_table(ncf, &augeas_xfm_common);
- if (r < 0)
- goto error;
-
- if (stat(ncf->root, &stats) != 0 || !S_ISDIR(stats.st_mode)) {
- report_error(ncf, NETCF_EFILE,
- "invalid root '%s' is not a directory", ncf->root);
- return -1;
- }
-
- // FIXME: Check for errors
- xsltInit();
- exsltStrRegister();
- ncf->driver->get = parse_stylesheet(ncf, "initscripts-get.xsl");
- ncf->driver->put = parse_stylesheet(ncf, "initscripts-put.xsl");
- ncf->driver->rng = rng_parse(ncf, "interface.rng");
- ERR_BAIL(ncf);
-
- /* open a socket for interface ioctls */
- ncf->driver->ioctl_fd = init_ioctl_fd(ncf);
- if (ncf->driver->ioctl_fd < 0)
- goto error;
- if (netlink_init(ncf) < 0)
- goto error;
- return 0;
-
- error:
- drv_close(ncf);
- return -1;
-}
-
-void drv_close(struct netcf *ncf) {
- if (ncf == NULL || ncf->driver == NULL)
- return;
- xsltFreeStylesheet(ncf->driver->get);
- xsltFreeStylesheet(ncf->driver->put);
- xmlRelaxNGFree(ncf->driver->rng);
- netlink_close(ncf);
- if (ncf->driver->ioctl_fd >= 0)
- close(ncf->driver->ioctl_fd);
- aug_close(ncf->driver->augeas);
- FREE(ncf->driver->augeas_xfm_tables);
- FREE(ncf->driver);
-}
-
-void drv_entry(struct netcf *ncf) {
- ncf->driver->load_augeas = 1;
-}
-
-static int list_interface_ids(struct netcf *ncf,
- int maxnames, char **names,
- unsigned int flags,
- const char *id_attr) {
- struct augeas *aug = NULL;
- int nint = 0, nmatches = 0, nqualified = 0, result = 0, r;
- char **intf = NULL, **matches = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
- nint = list_interfaces(ncf, &intf);
- ERR_BAIL(ncf);
- if (!names) {
- maxnames = nint; /* if not returning list, ignore maxnames too */
- }
- for (result = 0; (result < nint) && (nqualified < maxnames); result++) {
- nmatches = aug_fmt_match(ncf, &matches,
- "%s/%s", intf[result], id_attr);
- ERR_BAIL(ncf);
- if (nmatches > 0) {
- const char *name;
- int is_qualified = ((flags & (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE))
- == (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE));
-
- r = aug_get(aug, matches[nmatches-1], &name);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
-
- if (!is_qualified) {
- int is_active = if_is_active(ncf, name);
- if ((is_active && (flags & NETCF_IFACE_ACTIVE))
- || ((!is_active) && (flags & NETCF_IFACE_INACTIVE))) {
-
- is_qualified = 1;
- }
- }
-
- if (is_qualified) {
- if (names) {
- names[nqualified] = strdup(name);
- ERR_NOMEM(names[nqualified] == NULL, ncf);
- }
- nqualified++;
- }
- }
- free_matches(nmatches, &matches);
- }
- free_matches(nint, &intf);
- return nqualified;
- error:
- free_matches(nmatches, &matches);
- free_matches(nint, &intf);
- return -1;
-}
-
-int drv_list_interfaces(struct netcf *ncf, int maxnames, char **names,
- unsigned int flags) {
- return list_interface_ids(ncf, maxnames, names, flags, "DEVICE");
-}
-
-int drv_num_of_interfaces(struct netcf *ncf, unsigned int flags) {
- return list_interface_ids(ncf, 0, NULL, flags, "DEVICE");
-}
-
-struct netcf_if *drv_lookup_by_name(struct netcf *ncf, const char *name) {
- struct netcf_if *nif = NULL;
- char *pathx = NULL;
- char *name_dup = NULL;
- struct augeas *aug;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- pathx = find_ifcfg_path(ncf, name);
- ERR_BAIL(ncf);
-
- if (pathx == NULL || is_slave(ncf, pathx))
- goto done;
-
- name_dup = strdup(name);
- ERR_NOMEM(name_dup == NULL, ncf);
-
- nif = make_netcf_if(ncf, name_dup);
- ERR_BAIL(ncf);
- goto done;
-
- error:
- unref(nif, netcf_if);
- FREE(name_dup);
- done:
- FREE(pathx);
- return nif;
-}
-
-/* Get an XML desription of the interfaces (just paths, really) in INTF.
- * The format is a very simple representation of the Augeas tree (see
- * xml/augeas.rng)
- */
-static xmlDocPtr aug_get_xml(struct netcf *ncf, int nint, char **intf) {
- struct augeas *aug;
- xmlDocPtr result = NULL;
- xmlNodePtr root = NULL, tree = NULL;
- char **matches = NULL;
- int nmatches, r;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- result = xmlNewDoc(BAD_CAST "1.0");
- root = xmlNewNode(NULL, BAD_CAST "forest");
- xmlDocSetRootElement(result, root);
-
- for (int i=0; i < nint; i++) {
- tree = xmlNewChild(root, NULL, BAD_CAST "tree", NULL);
- xmlNewProp(tree, BAD_CAST "path", BAD_CAST intf[i]);
- nmatches = aug_fmt_match(ncf, &matches, "%s/%s", intf[i], "*");
- ERR_COND_BAIL(nint < 0, ncf, EOTHER);
- for (int j = 0; j < nmatches; j++) {
- xmlNodePtr node = xmlNewChild(tree, NULL, BAD_CAST "node", NULL);
- const char *value;
- xmlNewProp(node, BAD_CAST "label",
- BAD_CAST matches[j] + strlen(intf[i]) + 1);
- r = aug_get(aug, matches[j], &value);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
- xmlNewProp(node, BAD_CAST "value", BAD_CAST value);
- }
- free_matches(nmatches, &matches);
- }
-
- return result;
-
- error:
- free_matches(nmatches, &matches);
- xmlFreeDoc(result);
- return NULL;
-}
-
-/* Write the XML doc in the simple Augeas format into the Augeas tree */
-static int aug_put_xml(struct netcf *ncf, xmlDocPtr xml) {
- xmlNodePtr forest;
- char *path = NULL, *lpath = NULL, *label = NULL, *value = NULL;
- struct augeas *aug = NULL;
- int result = -1;
- int r;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- forest = xmlDocGetRootElement(xml);
- ERR_THROW(forest == NULL, ncf, EINTERNAL, "missing root element");
- ERR_THROW(! xmlStrEqual(forest->name, BAD_CAST "forest"), ncf,
- EINTERNAL, "expected root node labeled 'forest', not '%s'",
- forest->name);
- list_for_each(tree, forest->children) {
- ERR_THROW(! xmlStrEqual(tree->name, BAD_CAST "tree"), ncf,
- EINTERNAL, "expected node labeled 'tree', not '%s'",
- tree->name);
- path = xml_prop(tree, "path");
- int toplevel = 1;
- /* This is a little drastic, since it clears out the file entirely */
- r = aug_rm(aug, path);
- ERR_THROW(r < 0, ncf, EINTERNAL, "aug_rm of '%s' failed", path);
- list_for_each(node, tree->children) {
- label = xml_prop(node, "label");
- value = xml_prop(node, "value");
- /* We should mark the toplevel interface from the XSLT */
- if (STREQ(label, "BRIDGE") || STREQ(label, "MASTER")) {
- toplevel = 0;
- }
- r = xasprintf(&lpath, "%s/%s", path, label);
- ERR_NOMEM(r < 0, ncf);
-
- r = aug_set(aug, lpath, value);
- ERR_THROW(r < 0, ncf, EOTHER,
- "aug_set of '%s' failed", lpath);
- FREE(lpath);
- xmlFree(label);
- xmlFree(value);
- label = value = NULL;
- }
- xmlFree(path);
- path = NULL;
- }
- result = 0;
- error:
- xmlFree(label);
- xmlFree(value);
- xmlFree(path);
- FREE(lpath);
- return result;
-}
-
-/* given a netcf_if, get the config for an interface in the simple
- * Augeas format
- */
-static xmlDocPtr aug_get_xml_for_nif(struct netcf_if *nif) {
- struct netcf *ncf;
- char **devs = NULL, **intf = NULL;
- xmlDocPtr aug_xml = NULL;
- int ndevs = 0, nint = 0;
-
- ncf = nif->ncf;
- ndevs = aug_fmt_match(ncf, &devs,
- "%s[ DEVICE = '%s' or BRIDGE = '%s' or MASTER = '%s'"
- " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]/DEVICE",
- ifcfg_path, nif->name, nif->name, nif->name, nif->name);
- ERR_BAIL(ncf);
-
- nint = uniq_ifcfg_paths(ncf, ndevs, devs, &intf);
- ERR_BAIL(ncf);
-
- aug_xml = aug_get_xml(ncf, nint, intf);
-
- error:
- free_matches(ndevs, &devs);
- free_matches(nint, &intf);
- return aug_xml;
-}
-
-/* return the current static configuration (as saved on disk) */
-char *drv_xml_desc(struct netcf_if *nif) {
- char *result = NULL;
- struct netcf *ncf;
- xmlDocPtr aug_xml = NULL;
-
- ncf = nif->ncf;
- aug_xml = aug_get_xml_for_nif(nif);
- ERR_BAIL(ncf);
-
- result = apply_stylesheet_to_string(ncf, ncf->driver->put, aug_xml);
-
- error:
- xmlFreeDoc(aug_xml);
- return result;
-}
-
-/* return the current live configuration state - a combination of
- * drv_xml_desc + results of querying the interface directly */
-
-char *drv_xml_state(struct netcf_if *nif) {
- char *result = NULL;
- int r, result_len;
- struct netcf *ncf;
- xmlDocPtr ncf_xml = NULL;
- xmlNodePtr root;
-
- ncf = nif->ncf;
-
- /* start out with an empty tree rather than the config tree. Just
- * put in the interface node and its name
- */
- ncf_xml = xmlNewDoc(BAD_CAST "1.0");
- ERR_NOMEM(ncf_xml == NULL, ncf);
- root = xmlNewNode(NULL, BAD_CAST "interface");
- ERR_NOMEM(root == NULL, ncf);
- xmlDocSetRootElement(ncf_xml, root);
-
- /* add all info we can gather from the kernel/sysfs/procfs */
- add_state_to_xml_doc(nif, ncf_xml);
- ERR_BAIL(ncf);
-
- r = xsltSaveResultToString((xmlChar **)&result, &result_len,
- ncf_xml, ncf->driver->put);
- ERR_NOMEM(r < 0, ncf);
-
- done:
- xmlFreeDoc(ncf_xml);
- return result;
- error:
- FREE(result);
- result = 0;
- goto done;
-}
-
-/* Report various status info about the interface as bits in
- * "flags". Returns 0 on success, -1 on failure
- */
-int drv_if_status(struct netcf_if *nif, unsigned int *flags) {
- int is_active;
-
- ERR_THROW(flags == NULL, nif->ncf, EOTHER, "NULL pointer for flags in ncf_if_status");
- *flags = 0;
- is_active = if_is_active(nif->ncf, nif->name);
- if (is_active)
- *flags |= NETCF_IFACE_ACTIVE;
- else
- *flags |= NETCF_IFACE_INACTIVE;
- return 0;
-error:
- return -1;
-}
-
-/* Get the content of /interface/@name. Result must be freed with xmlFree()
- *
- * The name on VLAN interfaces is optional; if there is no
- * /interface/@name, construct a name for the VLAN interface and set
- * /interface/@name in NCF_XML to it. This way, other code can assume we
- * always have a name on the interface.
- */
-static char *device_name_from_xml(struct netcf *ncf, xmlDocPtr ncf_xml) {
- xmlXPathContextPtr context = NULL;
- xmlXPathObjectPtr obj = NULL;
- char *result = NULL;
-
- context = xmlXPathNewContext(ncf_xml);
- ERR_NOMEM(context == NULL, ncf);
-
- obj = xmlXPathEvalExpression(BAD_CAST "string(/interface/@name)", context);
- ERR_NOMEM(obj == NULL, ncf);
- assert(obj->type == XPATH_STRING);
-
- if (xmlStrlen(obj->stringval) == 0) {
- xmlXPathFreeObject(obj);
- obj = xmlXPathEvalExpression(BAD_CAST
- "concat(/interface/vlan/interface/@name, '.', /interface/vlan/@tag)",
- context);
- ERR_NOMEM(obj == NULL, ncf);
- ERR_COND_BAIL(xmlStrlen(obj->stringval) == 0, ncf, EINTERNAL);
- assert(obj->type == XPATH_STRING);
-
- xmlNodePtr iface;
- iface = xmlDocGetRootElement(ncf_xml);
- ERR_COND_BAIL(iface == NULL, ncf, EINTERNAL);
- xmlSetProp(iface, BAD_CAST "name", BAD_CAST result);
- }
-
- result = (char *) xmlStrdup(obj->stringval);
- error:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(context);
- return result;
-}
-
-/* The device NAME is a bond if it is mentioned as the MASTER in sopme
- * other devices config file
- */
-static bool is_bond(struct netcf *ncf, const char *name) {
- int nmatches = 0;
-
- nmatches = aug_fmt_match(ncf, NULL,
- "%s[ MASTER = '%s']", ifcfg_path, name);
- return nmatches > 0;
-}
-
-/* The device NAME is a bridge if it has an entry TYPE=Bridge */
-static bool is_bridge(struct netcf *ncf, const char *name) {
- int nmatches = 0;
-
- nmatches = aug_fmt_match(ncf, NULL,
- "%s[ DEVICE = '%s' and TYPE = 'Bridge']",
- ifcfg_path, name);
- return nmatches > 0;
-}
-
-static int bridge_slaves(struct netcf *ncf, const char *name, char ***slaves) {
- struct augeas *aug = NULL;
- int r, nslaves = 0;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- nslaves = aug_fmt_match(ncf, slaves,
- "%s[ BRIDGE = '%s' ]/DEVICE", ifcfg_path, name);
- ERR_BAIL(ncf);
- for (int i=0; i < nslaves; i++) {
- char *p = (*slaves)[i];
- const char *dev;
- r = aug_get(aug, p, &dev);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
-
- (*slaves)[i] = strdup(dev);
- free(p);
- ERR_NOMEM(slaves[i] == NULL, ncf);
- }
- return nslaves;
- error:
- free_matches(nslaves, slaves);
- return -1;
-}
-
-
-/* For an interface NAME, remove the ifcfg-* files for that interface and
- * all its slaves. */
-static void rm_interface(struct netcf *ncf, const char *name) {
- int r;
- char *path = NULL;
- struct augeas *aug = NULL;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- /* The last or clause catches slaves of a bond that are enslaved to
- * a bridge NAME */
- r = xasprintf(&path,
- "%s[ DEVICE = '%s' or BRIDGE = '%s' or MASTER = '%s' "
- " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]",
- ifcfg_path, name, name, name, name);
- ERR_NOMEM(r < 0, ncf);
-
- r = aug_rm(aug, path);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
- error:
- FREE(path);
-}
-
-/* Remove all interfaces and their slaves mentioned in NCF_XML. We need to
- * remove interfaces one by one when we define an interface, since what
- * will become a subinterface may not be related to the new toplevel
- * interface, and calling RM_INTERFACE on the toplevel interface is
- * therefore not enough.
- */
-static void rm_all_interfaces(struct netcf *ncf, xmlDocPtr ncf_xml) {
- xmlXPathContextPtr context = NULL;
- xmlXPathObjectPtr obj = NULL;
-
- context = xmlXPathNewContext(ncf_xml);
- ERR_NOMEM(context == NULL, ncf);
-
- obj = xmlXPathEvalExpression(BAD_CAST
- "//interface[count(parent::vlan) = 0]",
- context);
- ERR_NOMEM(obj == NULL, ncf);
-
-
- xmlNodeSetPtr ns = obj->nodesetval;
- for (int i=0; i < ns->nodeNr; i++) {
- xmlChar *name = xmlGetProp(ns->nodeTab[i], BAD_CAST "name");
- ERR_NOMEM(name == NULL, ncf);
- rm_interface(ncf, (char *) name);
- xmlFree(name);
- ERR_BAIL(ncf);
- }
- error:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(context);
-}
-
-/* Dig through interface NAME and all its subinterfaces for bonds
- * and either add aliases in modprobe.conf for it (ALIAS == true), or
- * remove such aliases (ALIAS == false)
- */
-static void bond_setup(struct netcf *ncf, const char *name, bool alias) {
- void (*setup)(struct netcf *ncf, const char *name);
- int nslaves = 0;
- char **slaves = NULL;
-
- if (alias)
- setup = modprobed_alias_bond;
- else
- setup = modprobed_unalias_bond;
-
- if (is_bond(ncf, name)) {
- setup(ncf, name);
- ERR_BAIL(ncf);
- }
-
- if (is_bridge(ncf, name)) {
- nslaves = bridge_slaves(ncf, name, &slaves);
- ERR_BAIL(ncf);
- for (int i=0; i < nslaves; i++) {
- if (is_bond(ncf, slaves[i])) {
- setup(ncf, slaves[i]);
- ERR_BAIL(ncf);
- }
- }
- }
-
- error:
- free_matches(nslaves, &slaves);
- return;
-}
-
-struct netcf_if *drv_define(struct netcf *ncf, const char *xml_str) {
- xmlDocPtr ncf_xml = NULL, aug_xml = NULL;
- char *name = NULL;
- struct netcf_if *result = NULL;
- int r;
- struct augeas *aug = get_augeas(ncf);
-
- ncf_xml = parse_xml(ncf, xml_str);
- ERR_BAIL(ncf);
-
- rng_validate(ncf, ncf_xml);
- ERR_BAIL(ncf);
-
- name = device_name_from_xml(ncf, ncf_xml);
- ERR_COND_BAIL(name == NULL, ncf, EINTERNAL);
-
- rm_all_interfaces(ncf, ncf_xml);
- ERR_BAIL(ncf);
-
- aug_xml = apply_stylesheet(ncf, ncf->driver->get, ncf_xml);
- ERR_BAIL(ncf);
-
- aug_put_xml(ncf, aug_xml);
- ERR_BAIL(ncf);
-
- bond_setup(ncf, name, true);
- ERR_BAIL(ncf);
-
- r = aug_save(aug);
- if (r < 0 && NCF_DEBUG(ncf)) {
- fprintf(stderr, "Errors from aug_save:\n");
- aug_print(aug, stderr, "/augeas//error");
- }
- ERR_THROW(r < 0, ncf, EOTHER, "aug_save failed");
-
- result = make_netcf_if(ncf, name);
- ERR_BAIL(ncf);
-
- done:
- xmlFreeDoc(ncf_xml);
- xmlFreeDoc(aug_xml);
- return result;
- error:
- unref(result, netcf_if);
- goto done;
-}
-
-int drv_undefine(struct netcf_if *nif) {
- struct augeas *aug = NULL;
- struct netcf *ncf = nif->ncf;
- int r;
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- bond_setup(ncf, nif->name, false);
- ERR_BAIL(ncf);
-
- rm_interface(ncf, nif->name);
- ERR_BAIL(ncf);
-
- r = aug_save(aug);
- ERR_COND_BAIL(r < 0, ncf, EOTHER);
-
- return 0;
- error:
- return -1;
-}
-
-int drv_lookup_by_mac_string(struct netcf *ncf, const char *mac,
- int maxifaces, struct netcf_if **ifaces)
-{
- struct augeas *aug = NULL;
- char *path = NULL, *ifcfg = NULL;
- const char **names = NULL;
- int nmatches = 0;
- char **matches = NULL;
- int r;
- int result = -1;
-
- MEMZERO(ifaces, maxifaces);
-
- aug = get_augeas(ncf);
- ERR_BAIL(ncf);
-
- nmatches = aug_match_mac(ncf, mac, &matches);
- ERR_BAIL(ncf);
- if (nmatches == 0) {
- result = 0;
- goto done;
- }
-
- r = ALLOC_N(names, nmatches);
- ERR_NOMEM(r < 0, ncf);
-
- int cnt = 0;
- for (int i = 0; i < nmatches; i++) {
- if (!has_ifcfg_file(ncf, matches[i]))
- continue;
- r = xasprintf(&ifcfg, "%s[DEVICE = '%s']", ifcfg_path, matches[i]);
- ERR_NOMEM(r < 0, ncf);
-
- if (! is_slave(ncf, ifcfg))
- names[cnt++] = matches[i];
- FREE(ifcfg);
- }
- for (int i=0; i < cnt && i < maxifaces; i++) {
- char *name = strdup(names[i]);
- ERR_NOMEM(name == NULL, ncf);
- ifaces[i] = make_netcf_if(ncf, name);
- ERR_BAIL(ncf);
- }
- result = cnt;
- goto done;
-
- error:
- for (int i=0; i < maxifaces; i++)
- unref(ifaces[i], netcf_if);
- done:
- free(names);
- free(ifcfg);
- free(path);
- free_matches(nmatches, &matches);
- return result;
-}
-
-const char *drv_mac_string(struct netcf_if *nif) {
- struct netcf *ncf = nif->ncf;
- const char *mac;
- char *path = NULL;
- int r;
-
- r = aug_get_mac(ncf, nif->name, &mac);
- ERR_THROW(r < 0, ncf, EOTHER, "could not lookup MAC of %s", nif->name);
-
- if (mac != NULL) {
- if (nif->mac == NULL || STRNEQ(nif->mac, mac)) {
- FREE(nif->mac);
- nif->mac = strdup(mac);
- ERR_NOMEM(nif->mac == NULL, ncf);
- }
- } else {
- FREE(nif->mac);
- }
- /* fallthrough intentional */
- error:
- FREE(path);
- return nif->mac;
-}
-
-/*
- * Bringing interfaces up/down
- */
-
-int drv_if_up(struct netcf_if *nif) {
- static const char *const ifup = "ifup";
- struct netcf *ncf = nif->ncf;
- char **slaves = NULL;
- int nslaves = 0;
- int result = -1;
-
- if (is_bridge(ncf, nif->name)) {
- /* Bring up bridge slaves before the bridge */
- nslaves = bridge_slaves(ncf, nif->name, &slaves);
- ERR_BAIL(ncf);
-
- for (int i=0; i < nslaves; i++) {
- run1(ncf, ifup, slaves[i]);
- ERR_BAIL(ncf);
- }
- }
- run1(ncf, ifup, nif->name);
- ERR_BAIL(ncf);
- result = 0;
- error:
- free_matches(nslaves, &slaves);
- return result;
-}
-
-int drv_if_down(struct netcf_if *nif) {
- static const char *const ifdown = "ifdown";
- struct netcf *ncf = nif->ncf;
- char **slaves = NULL;
- int nslaves = 0;
- int result = -1;
-
- run1(ncf, ifdown, nif->name);
- ERR_BAIL(ncf);
- if (is_bridge(ncf, nif->name)) {
- /* Bring up bridge slaves after the bridge */
- nslaves = bridge_slaves(ncf, nif->name, &slaves);
- ERR_BAIL(ncf);
-
- for (int i=0; i < nslaves; i++) {
- run1(ncf, ifdown, slaves[i]);
- ERR_BAIL(ncf);
- }
- }
- result = 0;
- error:
- free_matches(nslaves, &slaves);
- return result;
-}
-
-/*
- * Test interface
- */
-int drv_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
- /* Use utility implementation */
- return dutil_get_aug(ncf, ncf_xml, aug_xml);
-}
-
-/* Transform the Augeas XML AUG_XML into interface XML NCF_XML */
-int drv_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
- /* Use utility implementation */
- return dutil_put_aug(ncf, aug_xml, ncf_xml);
-}
-
-/*
- * Local variables:
- * indent-tabs-mode: nil
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
-/* vim: set ts=4 sw=4 et: */
diff --git a/src/drv_linux.c b/src/drv_linux.c
new file mode 100644
index 0000000..b2d96cb
--- /dev/null
+++ b/src/drv_linux.c
@@ -0,0 +1,1097 @@
+/*
+ * drv_initscripts.c: the initscripts backend for netcf
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Lutterkort <lutter(a)redhat.com>
+ */
+
+#include <config.h>
+#include <internal.h>
+
+#include <augeas.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "safe-alloc.h"
+#include "ref.h"
+#include "list.h"
+#include "dutil.h"
+#include "dutil_linux.h"
+
+#include <libxml/parser.h>
+#include <libxml/relaxng.h>
+#include <libxml/tree.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+#include <libexslt/exslt.h>
+
+static const char *const network_scripts_path =
+ "/files/etc/sysconfig/network-scripts";
+
+static const char *const ifcfg_path =
+ "/files/etc/sysconfig/network-scripts/*";
+
+/* Augeas should only load the files we are interested in */
+static const struct augeas_pv augeas_xfm_common_pv[] = {
+ /* Ifcfg files */
+ { "/augeas/load/Ifcfg/lens", "Sysconfig.lns" },
+ { "/augeas/load/Ifcfg/incl",
+ "/etc/sysconfig/network-scripts/ifcfg-*" },
+ { "/augeas/load/Ifcfg/excl[1]", "*~" },
+ { "/augeas/load/Ifcfg/excl[2]", "*.bak" },
+ { "/augeas/load/Ifcfg/excl[3]", "*.orig" },
+ { "/augeas/load/Ifcfg/excl[4]", "*.rpmnew" },
+ { "/augeas/load/Ifcfg/excl[5]", "*.rpmorig" },
+ { "/augeas/load/Ifcfg/excl[6]", "*.rpmsave" },
+ { "/augeas/load/Ifcfg/excl[7]", "*.augnew" },
+ { "/augeas/load/Ifcfg/excl[8]", "*.augsave" },
+ /* modprobe config */
+ { "/augeas/load/Modprobe/lens", "Modprobe.lns" },
+ { "/augeas/load/Modprobe/incl[1]", "/etc/modprobe.d/*" },
+ { "/augeas/load/Modprobe/incl[2]", "/etc/modprobe.conf" },
+ { "/augeas/load/Modprobe/excl[1]", "*.augnew" },
+ { "/augeas/load/Modprobe/excl[2]", "*.augsave" },
+ { "/augeas/load/Modprobe/excl[3]", "*.rpmsave" },
+ { "/augeas/load/Modprobe/excl[4]", "*.rpmnew" },
+ { "/augeas/load/Modprobe/excl[5]", "*~" },
+ /* sysfs (choice entries from /class/net) */
+ { "/augeas/load/Sysfs/lens", "Netcf.id" },
+ { "/augeas/load/Sysfs/incl", "/sys/class/net/*/address" }
+};
+
+static const struct augeas_xfm_table augeas_xfm_common =
+ { .size = ARRAY_CARDINALITY(augeas_xfm_common_pv),
+ .pv = augeas_xfm_common_pv };
+
+/* Entries in a ifcfg file that tell us that the interface
+ * is not a toplevel interface
+ */
+static const char *const subif_paths[] = {
+ "MASTER", "BRIDGE"
+};
+
+static int is_slave(struct netcf *ncf, const char *intf) {
+ for (int s = 0; s < ARRAY_CARDINALITY(subif_paths); s++) {
+ int r;
+ r = aug_fmt_match(ncf, NULL, "%s/%s", intf, subif_paths[s]);
+ if (r != 0)
+ return r;
+ }
+ return 0;
+}
+
+static bool has_ifcfg_file(struct netcf *ncf, const char *name) {
+ int nmatches;
+
+ nmatches = aug_fmt_match(ncf, NULL,
+ "%s[ DEVICE = '%s'"
+ " or BRIDGE = '%s'"
+ " or MASTER = '%s'"
+ " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]/DEVICE",
+ ifcfg_path, name, name, name, name);
+ return nmatches > 0;
+}
+
+static int cmpstrp(const void *p1, const void *p2) {
+ const char *s1 = * (const char **)p1;
+ const char *s2 = * (const char **)p2;
+ return strcmp(s1, s2);
+}
+
+/* Find the path to the ifcfg file that has the configuration for the
+ * interface with MAC address MAC.
+ */
+static char *find_ifcfg_path_by_hwaddr(struct netcf *ncf, const char *mac) {
+ static const char *const hwaddr_str = "/HWADDR";
+
+ int nhwaddr = 0, r;
+ char **hwaddr = NULL;
+ struct augeas *aug = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ // It would be nice if Augeas provided a way to do case-insensitive
+ // matching. For now, we just write it out
+ nhwaddr = aug_fmt_match(ncf, &hwaddr, "%s%s", ifcfg_path, hwaddr_str);
+ ERR_COND_BAIL(nhwaddr < 0, ncf, EOTHER);
+
+ /* Sort because need_config will return the last match in case
+ * of multiple matches */
+ qsort(hwaddr, nhwaddr, sizeof(*hwaddr), cmpstrp);
+ int match = -1;
+ for (int i=0; i < nhwaddr; i++) {
+ const char *addr;
+ r = aug_get(aug, hwaddr[i], &addr);
+ ERR_COND_BAIL(r != 1, ncf, EOTHER);
+ if (STRCASEEQ(addr, mac))
+ match = i;
+ }
+ if (match != -1) {
+ char *path = hwaddr[match];
+ hwaddr[match] = NULL;
+ path[strlen(path) - strlen(hwaddr_str)] = '\0';
+ free_matches(nhwaddr, &hwaddr);
+ return path;
+ }
+ error:
+ free_matches(nhwaddr, &hwaddr);
+ return NULL;
+}
+
+/* Find the path to the ifcfg file that has the configuration for the
+ * interface by checking for an entry 'DEVICE=NAME'
+ */
+static char *find_ifcfg_path_by_device(struct netcf *ncf, const char *name) {
+ struct augeas *aug = NULL;
+ int ndevs = 0;
+ char **devs = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ ndevs = aug_fmt_match(ncf, &devs, "%s[DEVICE = '%s']",
+ ifcfg_path, name);
+ ERR_COND_BAIL(ndevs < 0, ncf, EOTHER);
+
+ if (ndevs == 0)
+ return NULL;
+
+ qsort(devs, ndevs, sizeof(*devs), cmpstrp);
+
+ char *path = devs[ndevs - 1];
+ devs[ndevs - 1] = NULL;
+
+ free_matches(ndevs, &devs);
+
+ return path;
+ error:
+ free_matches(ndevs, &devs);
+ return NULL;
+}
+
+/* Find the path to the ifcfg file that has the configuration for
+ * the device NAME. The logic follows the need_config function
+ * in /etc/sysconfig/network-scripts/network-functions
+ */
+static char *find_ifcfg_path(struct netcf *ncf, const char *name) {
+ struct augeas *aug = NULL;
+ char *path = NULL;
+ const char *mac = NULL;
+ int r, nmatches;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ /* if ifcfg-NAME exists, use that */
+ r = xasprintf(&path, "%s/ifcfg-%s", network_scripts_path, name);
+ ERR_NOMEM(r < 0, ncf);
+
+ nmatches = aug_match(aug, path, NULL);
+ ERR_COND_BAIL(nmatches < 0, ncf, EOTHER);
+
+ if (nmatches == 1)
+ return path;
+
+ FREE(path);
+
+ /* Now find the config by MAC, matching on HWADDR */
+ r = aug_get_mac(ncf, name, &mac);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+ if (r > 0) {
+ path = find_ifcfg_path_by_hwaddr(ncf, mac);
+ ERR_BAIL(ncf);
+ if (path != NULL)
+ return path;
+ }
+
+ path = find_ifcfg_path_by_device(ncf, name);
+ ERR_BAIL(ncf);
+
+ return path;
+ error:
+ FREE(path);
+ return NULL;
+}
+
+/* Given NDEVS path to DEVICE entries which may contain duplicate devices,
+ * produce a list of canonical paths to the interfaces in INTF and return
+ * the number of entries. Return -1 on error
+ */
+static int uniq_ifcfg_paths(struct netcf *ncf,
+ int ndevs, char **devs,
+ char ***intf) {
+ struct augeas *aug;
+ int r;
+ int ndevnames = 0;
+ const char **devnames = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ /* List unique device names */
+ r = ALLOC_N(devnames, ndevs);
+ ERR_NOMEM(r < 0, ncf);
+
+ for (int i=0; i < ndevs; i++) {
+ const char *name = NULL;
+ r = aug_get(aug, devs[i], &name);
+ ERR_COND_BAIL(r != 1, ncf, EOTHER);
+ int exists = 0;
+ for (int j = 0; j < ndevnames; j++)
+ if (STREQ(name, devnames[j])) {
+ exists = 1;
+ break;
+ }
+ if (!exists)
+ devnames[ndevnames++] = name;
+ }
+ qsort(devnames, ndevnames, sizeof(*devnames), cmpstrp);
+
+ /* Find canonical config for each device name */
+ r = ALLOC_N(*intf, ndevnames);
+ ERR_NOMEM(r < 0, ncf);
+ for (int i= 0; i < ndevnames; i++) {
+ (*intf)[i] = find_ifcfg_path(ncf, devnames[i]);
+ ERR_BAIL(ncf);
+ }
+
+ FREE(devnames);
+ return ndevnames;
+
+ error:
+ FREE(devnames);
+ free_matches(ndevnames, intf);
+ return -1;
+}
+
+/* List all configured network devices; returns the number of devices or -1
+ * on error. On success, the path to the config file for each interface
+ * is returned in INTF
+ */
+static int list_ifcfg_paths(struct netcf *ncf, char ***intf) {
+ int result = 0, ndevs;
+ char **devs = NULL;
+
+ ndevs = aug_fmt_match(ncf, &devs, "%s/DEVICE", ifcfg_path);
+ ERR_COND_BAIL(ndevs < 0, ncf, EOTHER);
+
+ result = uniq_ifcfg_paths(ncf, ndevs, devs, intf);
+ ERR_BAIL(ncf);
+
+ free_matches(ndevs, &devs);
+ return result;
+
+ error:
+ free_matches(ndevs, &devs);
+ return -1;
+}
+
+static int list_interfaces(struct netcf *ncf, char ***intf) {
+ int nint = 0, result = 0;
+ struct augeas *aug = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ /* Look in augeas for all interfaces */
+ nint = list_ifcfg_paths(ncf, intf);
+ ERR_BAIL(ncf);
+ result = nint;
+
+ /* Filter out the interfaces that are slaves/subordinate */
+ for (int i = 0; i < result;) {
+ if (is_slave(ncf, (*intf)[i])) {
+ FREE((*intf)[i]);
+ memmove(*intf + i, *intf + i + 1,
+ (nint - (i + 1))*sizeof((*intf)[0]));
+ result -= 1;
+ } else {
+ i += 1;
+ }
+ }
+ return result;
+ error:
+ free_matches(nint, intf);
+ return -1;
+}
+
+int drv_init(struct netcf *ncf) {
+ int r;
+ struct stat stats;
+
+ if (ALLOC(ncf->driver) < 0)
+ return -1;
+
+ ncf->driver->ioctl_fd = -1;
+
+ r = add_augeas_xfm_table(ncf, &augeas_xfm_common);
+ if (r < 0)
+ goto error;
+
+ if (stat(ncf->root, &stats) != 0 || !S_ISDIR(stats.st_mode)) {
+ report_error(ncf, NETCF_EFILE,
+ "invalid root '%s' is not a directory", ncf->root);
+ return -1;
+ }
+
+ // FIXME: Check for errors
+ xsltInit();
+ exsltStrRegister();
+ ncf->driver->get = parse_stylesheet(ncf, "initscripts-get.xsl");
+ ncf->driver->put = parse_stylesheet(ncf, "initscripts-put.xsl");
+ ncf->driver->rng = rng_parse(ncf, "interface.rng");
+ ERR_BAIL(ncf);
+
+ /* open a socket for interface ioctls */
+ ncf->driver->ioctl_fd = init_ioctl_fd(ncf);
+ if (ncf->driver->ioctl_fd < 0)
+ goto error;
+ if (netlink_init(ncf) < 0)
+ goto error;
+ return 0;
+
+ error:
+ drv_close(ncf);
+ return -1;
+}
+
+void drv_close(struct netcf *ncf) {
+ if (ncf == NULL || ncf->driver == NULL)
+ return;
+ xsltFreeStylesheet(ncf->driver->get);
+ xsltFreeStylesheet(ncf->driver->put);
+ xmlRelaxNGFree(ncf->driver->rng);
+ netlink_close(ncf);
+ if (ncf->driver->ioctl_fd >= 0)
+ close(ncf->driver->ioctl_fd);
+ aug_close(ncf->driver->augeas);
+ FREE(ncf->driver->augeas_xfm_tables);
+ FREE(ncf->driver);
+}
+
+void drv_entry(struct netcf *ncf) {
+ ncf->driver->load_augeas = 1;
+}
+
+static int list_interface_ids(struct netcf *ncf,
+ int maxnames, char **names,
+ unsigned int flags,
+ const char *id_attr) {
+ struct augeas *aug = NULL;
+ int nint = 0, nmatches = 0, nqualified = 0, result = 0, r;
+ char **intf = NULL, **matches = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+ nint = list_interfaces(ncf, &intf);
+ ERR_BAIL(ncf);
+ if (!names) {
+ maxnames = nint; /* if not returning list, ignore maxnames too */
+ }
+ for (result = 0; (result < nint) && (nqualified < maxnames); result++) {
+ nmatches = aug_fmt_match(ncf, &matches,
+ "%s/%s", intf[result], id_attr);
+ ERR_BAIL(ncf);
+ if (nmatches > 0) {
+ const char *name;
+ int is_qualified = ((flags & (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE))
+ == (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE));
+
+ r = aug_get(aug, matches[nmatches-1], &name);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ if (!is_qualified) {
+ int is_active = if_is_active(ncf, name);
+ if ((is_active && (flags & NETCF_IFACE_ACTIVE))
+ || ((!is_active) && (flags & NETCF_IFACE_INACTIVE))) {
+
+ is_qualified = 1;
+ }
+ }
+
+ if (is_qualified) {
+ if (names) {
+ names[nqualified] = strdup(name);
+ ERR_NOMEM(names[nqualified] == NULL, ncf);
+ }
+ nqualified++;
+ }
+ }
+ free_matches(nmatches, &matches);
+ }
+ free_matches(nint, &intf);
+ return nqualified;
+ error:
+ free_matches(nmatches, &matches);
+ free_matches(nint, &intf);
+ return -1;
+}
+
+int drv_list_interfaces(struct netcf *ncf, int maxnames, char **names,
+ unsigned int flags) {
+ return list_interface_ids(ncf, maxnames, names, flags, "DEVICE");
+}
+
+int drv_num_of_interfaces(struct netcf *ncf, unsigned int flags) {
+ return list_interface_ids(ncf, 0, NULL, flags, "DEVICE");
+}
+
+struct netcf_if *drv_lookup_by_name(struct netcf *ncf, const char *name) {
+ struct netcf_if *nif = NULL;
+ char *pathx = NULL;
+ char *name_dup = NULL;
+ struct augeas *aug;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ pathx = find_ifcfg_path(ncf, name);
+ ERR_BAIL(ncf);
+
+ if (pathx == NULL || is_slave(ncf, pathx))
+ goto done;
+
+ name_dup = strdup(name);
+ ERR_NOMEM(name_dup == NULL, ncf);
+
+ nif = make_netcf_if(ncf, name_dup);
+ ERR_BAIL(ncf);
+ goto done;
+
+ error:
+ unref(nif, netcf_if);
+ FREE(name_dup);
+ done:
+ FREE(pathx);
+ return nif;
+}
+
+/* Get an XML desription of the interfaces (just paths, really) in INTF.
+ * The format is a very simple representation of the Augeas tree (see
+ * xml/augeas.rng)
+ */
+static xmlDocPtr aug_get_xml(struct netcf *ncf, int nint, char **intf) {
+ struct augeas *aug;
+ xmlDocPtr result = NULL;
+ xmlNodePtr root = NULL, tree = NULL;
+ char **matches = NULL;
+ int nmatches, r;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ result = xmlNewDoc(BAD_CAST "1.0");
+ root = xmlNewNode(NULL, BAD_CAST "forest");
+ xmlDocSetRootElement(result, root);
+
+ for (int i=0; i < nint; i++) {
+ tree = xmlNewChild(root, NULL, BAD_CAST "tree", NULL);
+ xmlNewProp(tree, BAD_CAST "path", BAD_CAST intf[i]);
+ nmatches = aug_fmt_match(ncf, &matches, "%s/%s", intf[i], "*");
+ ERR_COND_BAIL(nint < 0, ncf, EOTHER);
+ for (int j = 0; j < nmatches; j++) {
+ xmlNodePtr node = xmlNewChild(tree, NULL, BAD_CAST "node", NULL);
+ const char *value;
+ xmlNewProp(node, BAD_CAST "label",
+ BAD_CAST matches[j] + strlen(intf[i]) + 1);
+ r = aug_get(aug, matches[j], &value);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+ xmlNewProp(node, BAD_CAST "value", BAD_CAST value);
+ }
+ free_matches(nmatches, &matches);
+ }
+
+ return result;
+
+ error:
+ free_matches(nmatches, &matches);
+ xmlFreeDoc(result);
+ return NULL;
+}
+
+/* Write the XML doc in the simple Augeas format into the Augeas tree */
+static int aug_put_xml(struct netcf *ncf, xmlDocPtr xml) {
+ xmlNodePtr forest;
+ char *path = NULL, *lpath = NULL, *label = NULL, *value = NULL;
+ struct augeas *aug = NULL;
+ int result = -1;
+ int r;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ forest = xmlDocGetRootElement(xml);
+ ERR_THROW(forest == NULL, ncf, EINTERNAL, "missing root element");
+ ERR_THROW(! xmlStrEqual(forest->name, BAD_CAST "forest"), ncf,
+ EINTERNAL, "expected root node labeled 'forest', not '%s'",
+ forest->name);
+ list_for_each(tree, forest->children) {
+ ERR_THROW(! xmlStrEqual(tree->name, BAD_CAST "tree"), ncf,
+ EINTERNAL, "expected node labeled 'tree', not '%s'",
+ tree->name);
+ path = xml_prop(tree, "path");
+ int toplevel = 1;
+ /* This is a little drastic, since it clears out the file entirely */
+ r = aug_rm(aug, path);
+ ERR_THROW(r < 0, ncf, EINTERNAL, "aug_rm of '%s' failed", path);
+ list_for_each(node, tree->children) {
+ label = xml_prop(node, "label");
+ value = xml_prop(node, "value");
+ /* We should mark the toplevel interface from the XSLT */
+ if (STREQ(label, "BRIDGE") || STREQ(label, "MASTER")) {
+ toplevel = 0;
+ }
+ r = xasprintf(&lpath, "%s/%s", path, label);
+ ERR_NOMEM(r < 0, ncf);
+
+ r = aug_set(aug, lpath, value);
+ ERR_THROW(r < 0, ncf, EOTHER,
+ "aug_set of '%s' failed", lpath);
+ FREE(lpath);
+ xmlFree(label);
+ xmlFree(value);
+ label = value = NULL;
+ }
+ xmlFree(path);
+ path = NULL;
+ }
+ result = 0;
+ error:
+ xmlFree(label);
+ xmlFree(value);
+ xmlFree(path);
+ FREE(lpath);
+ return result;
+}
+
+/* given a netcf_if, get the config for an interface in the simple
+ * Augeas format
+ */
+static xmlDocPtr aug_get_xml_for_nif(struct netcf_if *nif) {
+ struct netcf *ncf;
+ char **devs = NULL, **intf = NULL;
+ xmlDocPtr aug_xml = NULL;
+ int ndevs = 0, nint = 0;
+
+ ncf = nif->ncf;
+ ndevs = aug_fmt_match(ncf, &devs,
+ "%s[ DEVICE = '%s' or BRIDGE = '%s' or MASTER = '%s'"
+ " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]/DEVICE",
+ ifcfg_path, nif->name, nif->name, nif->name, nif->name);
+ ERR_BAIL(ncf);
+
+ nint = uniq_ifcfg_paths(ncf, ndevs, devs, &intf);
+ ERR_BAIL(ncf);
+
+ aug_xml = aug_get_xml(ncf, nint, intf);
+
+ error:
+ free_matches(ndevs, &devs);
+ free_matches(nint, &intf);
+ return aug_xml;
+}
+
+/* return the current static configuration (as saved on disk) */
+char *drv_xml_desc(struct netcf_if *nif) {
+ char *result = NULL;
+ struct netcf *ncf;
+ xmlDocPtr aug_xml = NULL;
+
+ ncf = nif->ncf;
+ aug_xml = aug_get_xml_for_nif(nif);
+ ERR_BAIL(ncf);
+
+ result = apply_stylesheet_to_string(ncf, ncf->driver->put, aug_xml);
+
+ error:
+ xmlFreeDoc(aug_xml);
+ return result;
+}
+
+/* return the current live configuration state - a combination of
+ * drv_xml_desc + results of querying the interface directly */
+
+char *drv_xml_state(struct netcf_if *nif) {
+ char *result = NULL;
+ int r, result_len;
+ struct netcf *ncf;
+ xmlDocPtr ncf_xml = NULL;
+ xmlNodePtr root;
+
+ ncf = nif->ncf;
+
+ /* start out with an empty tree rather than the config tree. Just
+ * put in the interface node and its name
+ */
+ ncf_xml = xmlNewDoc(BAD_CAST "1.0");
+ ERR_NOMEM(ncf_xml == NULL, ncf);
+ root = xmlNewNode(NULL, BAD_CAST "interface");
+ ERR_NOMEM(root == NULL, ncf);
+ xmlDocSetRootElement(ncf_xml, root);
+
+ /* add all info we can gather from the kernel/sysfs/procfs */
+ add_state_to_xml_doc(nif, ncf_xml);
+ ERR_BAIL(ncf);
+
+ r = xsltSaveResultToString((xmlChar **)&result, &result_len,
+ ncf_xml, ncf->driver->put);
+ ERR_NOMEM(r < 0, ncf);
+
+ done:
+ xmlFreeDoc(ncf_xml);
+ return result;
+ error:
+ FREE(result);
+ result = 0;
+ goto done;
+}
+
+/* Report various status info about the interface as bits in
+ * "flags". Returns 0 on success, -1 on failure
+ */
+int drv_if_status(struct netcf_if *nif, unsigned int *flags) {
+ int is_active;
+
+ ERR_THROW(flags == NULL, nif->ncf, EOTHER, "NULL pointer for flags in ncf_if_status");
+ *flags = 0;
+ is_active = if_is_active(nif->ncf, nif->name);
+ if (is_active)
+ *flags |= NETCF_IFACE_ACTIVE;
+ else
+ *flags |= NETCF_IFACE_INACTIVE;
+ return 0;
+error:
+ return -1;
+}
+
+/* Get the content of /interface/@name. Result must be freed with xmlFree()
+ *
+ * The name on VLAN interfaces is optional; if there is no
+ * /interface/@name, construct a name for the VLAN interface and set
+ * /interface/@name in NCF_XML to it. This way, other code can assume we
+ * always have a name on the interface.
+ */
+static char *device_name_from_xml(struct netcf *ncf, xmlDocPtr ncf_xml) {
+ xmlXPathContextPtr context = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ char *result = NULL;
+
+ context = xmlXPathNewContext(ncf_xml);
+ ERR_NOMEM(context == NULL, ncf);
+
+ obj = xmlXPathEvalExpression(BAD_CAST "string(/interface/@name)", context);
+ ERR_NOMEM(obj == NULL, ncf);
+ assert(obj->type == XPATH_STRING);
+
+ if (xmlStrlen(obj->stringval) == 0) {
+ xmlXPathFreeObject(obj);
+ obj = xmlXPathEvalExpression(BAD_CAST
+ "concat(/interface/vlan/interface/@name, '.', /interface/vlan/@tag)",
+ context);
+ ERR_NOMEM(obj == NULL, ncf);
+ ERR_COND_BAIL(xmlStrlen(obj->stringval) == 0, ncf, EINTERNAL);
+ assert(obj->type == XPATH_STRING);
+
+ xmlNodePtr iface;
+ iface = xmlDocGetRootElement(ncf_xml);
+ ERR_COND_BAIL(iface == NULL, ncf, EINTERNAL);
+ xmlSetProp(iface, BAD_CAST "name", BAD_CAST result);
+ }
+
+ result = (char *) xmlStrdup(obj->stringval);
+ error:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(context);
+ return result;
+}
+
+/* The device NAME is a bond if it is mentioned as the MASTER in sopme
+ * other devices config file
+ */
+static bool is_bond(struct netcf *ncf, const char *name) {
+ int nmatches = 0;
+
+ nmatches = aug_fmt_match(ncf, NULL,
+ "%s[ MASTER = '%s']", ifcfg_path, name);
+ return nmatches > 0;
+}
+
+/* The device NAME is a bridge if it has an entry TYPE=Bridge */
+static bool is_bridge(struct netcf *ncf, const char *name) {
+ int nmatches = 0;
+
+ nmatches = aug_fmt_match(ncf, NULL,
+ "%s[ DEVICE = '%s' and TYPE = 'Bridge']",
+ ifcfg_path, name);
+ return nmatches > 0;
+}
+
+static int bridge_slaves(struct netcf *ncf, const char *name, char ***slaves) {
+ struct augeas *aug = NULL;
+ int r, nslaves = 0;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ nslaves = aug_fmt_match(ncf, slaves,
+ "%s[ BRIDGE = '%s' ]/DEVICE", ifcfg_path, name);
+ ERR_BAIL(ncf);
+ for (int i=0; i < nslaves; i++) {
+ char *p = (*slaves)[i];
+ const char *dev;
+ r = aug_get(aug, p, &dev);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ (*slaves)[i] = strdup(dev);
+ free(p);
+ ERR_NOMEM(slaves[i] == NULL, ncf);
+ }
+ return nslaves;
+ error:
+ free_matches(nslaves, slaves);
+ return -1;
+}
+
+
+/* For an interface NAME, remove the ifcfg-* files for that interface and
+ * all its slaves. */
+static void rm_interface(struct netcf *ncf, const char *name) {
+ int r;
+ char *path = NULL;
+ struct augeas *aug = NULL;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ /* The last or clause catches slaves of a bond that are enslaved to
+ * a bridge NAME */
+ r = xasprintf(&path,
+ "%s[ DEVICE = '%s' or BRIDGE = '%s' or MASTER = '%s' "
+ " or MASTER = ../*[BRIDGE = '%s']/DEVICE ]",
+ ifcfg_path, name, name, name, name);
+ ERR_NOMEM(r < 0, ncf);
+
+ r = aug_rm(aug, path);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+ error:
+ FREE(path);
+}
+
+/* Remove all interfaces and their slaves mentioned in NCF_XML. We need to
+ * remove interfaces one by one when we define an interface, since what
+ * will become a subinterface may not be related to the new toplevel
+ * interface, and calling RM_INTERFACE on the toplevel interface is
+ * therefore not enough.
+ */
+static void rm_all_interfaces(struct netcf *ncf, xmlDocPtr ncf_xml) {
+ xmlXPathContextPtr context = NULL;
+ xmlXPathObjectPtr obj = NULL;
+
+ context = xmlXPathNewContext(ncf_xml);
+ ERR_NOMEM(context == NULL, ncf);
+
+ obj = xmlXPathEvalExpression(BAD_CAST
+ "//interface[count(parent::vlan) = 0]",
+ context);
+ ERR_NOMEM(obj == NULL, ncf);
+
+
+ xmlNodeSetPtr ns = obj->nodesetval;
+ for (int i=0; i < ns->nodeNr; i++) {
+ xmlChar *name = xmlGetProp(ns->nodeTab[i], BAD_CAST "name");
+ ERR_NOMEM(name == NULL, ncf);
+ rm_interface(ncf, (char *) name);
+ xmlFree(name);
+ ERR_BAIL(ncf);
+ }
+ error:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(context);
+}
+
+/* Dig through interface NAME and all its subinterfaces for bonds
+ * and either add aliases in modprobe.conf for it (ALIAS == true), or
+ * remove such aliases (ALIAS == false)
+ */
+static void bond_setup(struct netcf *ncf, const char *name, bool alias) {
+ void (*setup)(struct netcf *ncf, const char *name);
+ int nslaves = 0;
+ char **slaves = NULL;
+
+ if (alias)
+ setup = modprobed_alias_bond;
+ else
+ setup = modprobed_unalias_bond;
+
+ if (is_bond(ncf, name)) {
+ setup(ncf, name);
+ ERR_BAIL(ncf);
+ }
+
+ if (is_bridge(ncf, name)) {
+ nslaves = bridge_slaves(ncf, name, &slaves);
+ ERR_BAIL(ncf);
+ for (int i=0; i < nslaves; i++) {
+ if (is_bond(ncf, slaves[i])) {
+ setup(ncf, slaves[i]);
+ ERR_BAIL(ncf);
+ }
+ }
+ }
+
+ error:
+ free_matches(nslaves, &slaves);
+ return;
+}
+
+struct netcf_if *drv_define(struct netcf *ncf, const char *xml_str) {
+ xmlDocPtr ncf_xml = NULL, aug_xml = NULL;
+ char *name = NULL;
+ struct netcf_if *result = NULL;
+ int r;
+ struct augeas *aug = get_augeas(ncf);
+
+ ncf_xml = parse_xml(ncf, xml_str);
+ ERR_BAIL(ncf);
+
+ rng_validate(ncf, ncf_xml);
+ ERR_BAIL(ncf);
+
+ name = device_name_from_xml(ncf, ncf_xml);
+ ERR_COND_BAIL(name == NULL, ncf, EINTERNAL);
+
+ rm_all_interfaces(ncf, ncf_xml);
+ ERR_BAIL(ncf);
+
+ aug_xml = apply_stylesheet(ncf, ncf->driver->get, ncf_xml);
+ ERR_BAIL(ncf);
+
+ aug_put_xml(ncf, aug_xml);
+ ERR_BAIL(ncf);
+
+ bond_setup(ncf, name, true);
+ ERR_BAIL(ncf);
+
+ r = aug_save(aug);
+ if (r < 0 && NCF_DEBUG(ncf)) {
+ fprintf(stderr, "Errors from aug_save:\n");
+ aug_print(aug, stderr, "/augeas//error");
+ }
+ ERR_THROW(r < 0, ncf, EOTHER, "aug_save failed");
+
+ result = make_netcf_if(ncf, name);
+ ERR_BAIL(ncf);
+
+ done:
+ xmlFreeDoc(ncf_xml);
+ xmlFreeDoc(aug_xml);
+ return result;
+ error:
+ unref(result, netcf_if);
+ goto done;
+}
+
+int drv_undefine(struct netcf_if *nif) {
+ struct augeas *aug = NULL;
+ struct netcf *ncf = nif->ncf;
+ int r;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ bond_setup(ncf, nif->name, false);
+ ERR_BAIL(ncf);
+
+ rm_interface(ncf, nif->name);
+ ERR_BAIL(ncf);
+
+ r = aug_save(aug);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ return 0;
+ error:
+ return -1;
+}
+
+int drv_lookup_by_mac_string(struct netcf *ncf, const char *mac,
+ int maxifaces, struct netcf_if **ifaces)
+{
+ struct augeas *aug = NULL;
+ char *path = NULL, *ifcfg = NULL;
+ const char **names = NULL;
+ int nmatches = 0;
+ char **matches = NULL;
+ int r;
+ int result = -1;
+
+ MEMZERO(ifaces, maxifaces);
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+ nmatches = aug_match_mac(ncf, mac, &matches);
+ ERR_BAIL(ncf);
+ if (nmatches == 0) {
+ result = 0;
+ goto done;
+ }
+
+ r = ALLOC_N(names, nmatches);
+ ERR_NOMEM(r < 0, ncf);
+
+ int cnt = 0;
+ for (int i = 0; i < nmatches; i++) {
+ if (!has_ifcfg_file(ncf, matches[i]))
+ continue;
+ r = xasprintf(&ifcfg, "%s[DEVICE = '%s']", ifcfg_path, matches[i]);
+ ERR_NOMEM(r < 0, ncf);
+
+ if (! is_slave(ncf, ifcfg))
+ names[cnt++] = matches[i];
+ FREE(ifcfg);
+ }
+ for (int i=0; i < cnt && i < maxifaces; i++) {
+ char *name = strdup(names[i]);
+ ERR_NOMEM(name == NULL, ncf);
+ ifaces[i] = make_netcf_if(ncf, name);
+ ERR_BAIL(ncf);
+ }
+ result = cnt;
+ goto done;
+
+ error:
+ for (int i=0; i < maxifaces; i++)
+ unref(ifaces[i], netcf_if);
+ done:
+ free(names);
+ free(ifcfg);
+ free(path);
+ free_matches(nmatches, &matches);
+ return result;
+}
+
+const char *drv_mac_string(struct netcf_if *nif) {
+ struct netcf *ncf = nif->ncf;
+ const char *mac;
+ char *path = NULL;
+ int r;
+
+ r = aug_get_mac(ncf, nif->name, &mac);
+ ERR_THROW(r < 0, ncf, EOTHER, "could not lookup MAC of %s", nif->name);
+
+ if (mac != NULL) {
+ if (nif->mac == NULL || STRNEQ(nif->mac, mac)) {
+ FREE(nif->mac);
+ nif->mac = strdup(mac);
+ ERR_NOMEM(nif->mac == NULL, ncf);
+ }
+ } else {
+ FREE(nif->mac);
+ }
+ /* fallthrough intentional */
+ error:
+ FREE(path);
+ return nif->mac;
+}
+
+/*
+ * Bringing interfaces up/down
+ */
+
+int drv_if_up(struct netcf_if *nif) {
+ static const char *const ifup = "ifup";
+ struct netcf *ncf = nif->ncf;
+ char **slaves = NULL;
+ int nslaves = 0;
+ int result = -1;
+
+ if (is_bridge(ncf, nif->name)) {
+ /* Bring up bridge slaves before the bridge */
+ nslaves = bridge_slaves(ncf, nif->name, &slaves);
+ ERR_BAIL(ncf);
+
+ for (int i=0; i < nslaves; i++) {
+ run1(ncf, ifup, slaves[i]);
+ ERR_BAIL(ncf);
+ }
+ }
+ run1(ncf, ifup, nif->name);
+ ERR_BAIL(ncf);
+ result = 0;
+ error:
+ free_matches(nslaves, &slaves);
+ return result;
+}
+
+int drv_if_down(struct netcf_if *nif) {
+ static const char *const ifdown = "ifdown";
+ struct netcf *ncf = nif->ncf;
+ char **slaves = NULL;
+ int nslaves = 0;
+ int result = -1;
+
+ run1(ncf, ifdown, nif->name);
+ ERR_BAIL(ncf);
+ if (is_bridge(ncf, nif->name)) {
+ /* Bring up bridge slaves after the bridge */
+ nslaves = bridge_slaves(ncf, nif->name, &slaves);
+ ERR_BAIL(ncf);
+
+ for (int i=0; i < nslaves; i++) {
+ run1(ncf, ifdown, slaves[i]);
+ ERR_BAIL(ncf);
+ }
+ }
+ result = 0;
+ error:
+ free_matches(nslaves, &slaves);
+ return result;
+}
+
+/*
+ * Test interface
+ */
+int drv_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml) {
+ /* Use utility implementation */
+ return dutil_get_aug(ncf, ncf_xml, aug_xml);
+}
+
+/* Transform the Augeas XML AUG_XML into interface XML NCF_XML */
+int drv_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
+ /* Use utility implementation */
+ return dutil_put_aug(ncf, aug_xml, ncf_xml);
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
+/* vim: set ts=4 sw=4 et: */
--
1.7.2.3
13 years, 4 months
[PATCH] interface.rng: Fix ipv4-address regex
by Laine Stump
When the regex in ipv4-addr was copied for use in libvirt's
network.rng file, a reviewer pointed out that it allowed two digit
numbers with leading 0's. The new regex here doesn't allow that.
---
data/xml/interface.rng | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/data/xml/interface.rng b/data/xml/interface.rng
index 80d686e..4dd1bb4 100644
--- a/data/xml/interface.rng
+++ b/data/xml/interface.rng
@@ -442,7 +442,7 @@
<define name='ipv4-addr'>
<data type='string'>
- <param name="pattern">(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))\.){3}((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))</param>
+ <param name="pattern">(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([1-9][0-9])|([0-9]))</param>
</data>
</define>
--
1.7.2.3
13 years, 4 months
Re: [netcf-devel] [cloud-dept-status] Matahari scrum - Wed 15 November, 2010
by Adam Stokes
On Mon, 15 Nov 2010 15:23:47 +0000
"Daniel P. Berrange" <berrange(a)redhat.com> wrote:
> On Mon, Nov 15, 2010 at 04:16:46PM +0100, Andrew Beekhof wrote:
> > Attendees: Adam, Alan, Andrew
> >
> > ** Adam
> > + Tested and documented federation
> > + Reworked and committed Matahari patches
> > *** Todo
> > + Submit revised netcf patch by Wednesday
> > + Add cpu flags field to the host schema for oVirt
> > + Talk to netcf team to see if they're interested in supporting
> > hardware enumeration and MAC querying
>
> IMHO hardware enumeration doesn't really belong in netcf, since it is
> a completely separate task to that of network device configuration
> file. Hardware enumeration is something that you want outside the
> context of network management too, eg for storage mgmt, or VM device
> assignment etc.
>
Thanks for the input Daniel, I would like to CC netcf list to see if
anyone else could provide their thoughts as well?
Thanks,
Adam
13 years, 4 months
Updating oVirt Server to the latest Matahari 0.4.0 schema
by apevec@redhat.com
Hi all,
I looked at patching oVirt Server to work with the latest Matahari schema, from Matahari's 'next' branch, version 0.4.0.
ovirt-server depends on matahari version 0.0.5, which is currently latest in Fedora, and it is used during Node registration, to enumerate hardware information.
Usage extracted from src/host-browser/host-register.rb is as follows:
host_list = @qmfc.objects(:package => 'com.redhat.matahari.host', :class => 'Host')
host properties used:
object_id
object_id.agent_key
uuid
hostname
hypervisor
arch
memory
This is all good, those properties do exist in new Matahari 0.4.0 schema, patch for this part would be simply:
--- a/src/host-browser/host-register.rb
+++ b/src/host-browser/host-register.rb
@@ -143,7 +143,7 @@ class HostRegister < Qmf::ConsoleHandler
debugputs "Agent #{agent.key}.connected!"
agent_connected(agent)
- host_list = @qmfc.objects(:package => 'com.redhat.matahari', :class => 'host')
+ host_list = @qmfc.objects(:package => 'com.redhat.matahari.host', :class => 'Host')
puts "host_list length is #{host_list.length}"
host_list.each do |host|
if host.object_id.agent_key == agent.key
Next, old Matahari has 1:N relationship between host and cpu objects, ovirt-server uses it by retrieving a list of cpus for each host:
cpu_info = @qmfc.objects(:package => 'com.redhat.matahari', :class => 'cpu', 'host' => host.object_id)
cpu properties used:
cpunum
corenum
numcores
vendor
model
family
cpuid_lvl
speed
cache
flags[0..251]
New Matahari changed the model, here are CPU-related properties from 0.4.0 Host object, there isn't separate cpu class anymore:
processors uint8 ReadOnly The number of physical CPUs.
cores uint8 ReadOnly The total number of processor cores.
model long-string ReadOnly The processor(s) model description.
"model" is descriptive string, so it's not good enough to replace old CPU details.
On the other hand, oVirt does not really use all those CPU properties, they are just copied into ovirt database.
CPU information could be used to check migration compatibility, but this should be done through libvirt/libvirt-qpid anyway.
So I would propose ovirt-server to just remove cpus table, what does ovirt-devel say?
Last are network interfaces, also 1:N relationship:
nic_info = @qmfc.objects(:package => 'com.redhat.matahari', :class => 'nic', 'host' => host.object_id)
nic properties used:
macaddr
interface
seems to be bug in host-register.rb, it uses "interface_name" property which doesn't exist in matahari 0.0.5 QMF schema!
bandwidth
"nic" class is gone, there's new class 'com.redhat.matahari.net:network' with "hostname" property only and following methods:
'status' Check a network interface
'stop' Stop a network interface
'start' Start a network interface
'destroy' Remove network interface
'list' List IP addresses
'describe' Obtain XML description of a network interface status
Matahari now basically wraps netcf calls in its network agent, so properties like MAC are not directly available, it can be extracted from netcf XML, output of the 'describe' method.
But there's more to it since, netcf can also configure interfaces (not yet in Matahari schema, but it is planned).
Currently, ovirt-server provides network configuration which is downloaded by the Node on boot and applied (via augeas).
Question is should ovirt-server switch fully to netcf (via Matahari)?
I'm not sure how does netcf handle unstable interface names, seems that it relies on them being stable which is not the case in the stateless environment like oVirt Node.
Alan
13 years, 4 months
[PATCH 1/2] Windows port - routines patch
by Adam Stokes
In attempts to provide consistent api's with fedorahosted.org/matahari, netcf was chosen as
our network backend of choice.
As of this moment the matahari network api is limited to a few routines all of which are supported
through netcf. Netcf heavily relies on augeas, however, because of Windows lack of reliance on files
augeas had to be stripped out of the equation completely.
- Listing interfaces has been ported over through a few routines:
- drv_list_interfaces, drv_lookup_by_name
- Just like on Linux we are able to return "Friendly" interface names and an
interface count through drv_list_interfacs_ids.
- Fortunately there are several win32 routines that allow us to pull various interface information.
One requirement of Matahari is to list out the associated mac addresses which is accomplished through
our drv_mac_string routine.
- Lastly, to show off the administrative capability of matahari we needed to port over netcf's drv_if_up
and drv_if_down routines. These routines I was particularly hesistant on asking for review as there are
already routines to handle the execution of outside processes. However, I thought this may be the simplest
approach to the few administrative tasks we needed to do. If there is a need to do other administrative tasks
in Windows then this can be put in its own reusable routine.
I'd like to get this portion of the netcf port hashed out as the rest of the port is build specific changes which can be easily reviewed.
---
src/netcf_win.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/netcf_win.h | 54 ++++++++++++
2 files changed, 307 insertions(+), 0 deletions(-)
create mode 100644 src/netcf_win.c
create mode 100644 src/netcf_win.h
diff --git a/src/netcf_win.c b/src/netcf_win.c
new file mode 100644
index 0000000..e42f5b1
--- /dev/null
+++ b/src/netcf_win.c
@@ -0,0 +1,253 @@
+/*
+ * netcf-win.c: windows functions
+ *
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Adam Stokes <ajs(a)redhat.com>
+ */
+
+#include <config.h>
+#include <internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <spawn.h>
+#include "netcf_win.h"
+
+#define GAA_FLAGS ( GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST )
+#define BUFSIZE 1024
+
+char *strerror_r(int errnum, char *buf, size_t buflen) {
+ return strerror(errnum);
+}
+
+/* Like asprintf, but set *STRP to NULL on error */
+int xasprintf(char **strp, const char *format, ...) {
+ va_list args;
+ int result;
+
+ va_start (args, format);
+ result = vasprintf (strp, format, args);
+ va_end (args);
+ if (result < 0)
+ *strp = NULL;
+ return result;
+}
+
+/* Create a new netcf if instance for interface NAME */
+struct netcf_if *make_netcf_if(struct netcf *ncf, char *name) {
+ int r;
+ struct netcf_if *result = NULL;
+
+ r = make_ref(result);
+ ERR_NOMEM(r < 0, ncf);
+ result->ncf = ref(ncf);
+ result->name = strdup(name);
+ return result;
+
+ error:
+ unref(result, netcf_if);
+ free(result);
+ return result;
+}
+
+PIP_ADAPTER_ADDRESSES build_adapter_table(struct netcf *ncf) {
+ int r = 0;
+ DWORD tableSize = 0;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+
+ GetAdaptersAddresses(AF_UNSPEC, GAA_FLAGS, NULL, pAddresses, &tableSize);
+ pAddresses = malloc(tableSize);
+ ERR_NOMEM(pAddresses == NULL, ncf);
+ r = GetAdaptersAddresses(AF_INET, GAA_FLAGS, NULL, pAddresses, &tableSize);
+ ERR_COND_BAIL(r != NO_ERROR, ncf, EOTHER);
+ return pAddresses;
+error:
+ free(pAddresses);
+ return NULL;
+}
+
+static int list_interface_ids(struct netcf *ncf,
+ int maxnames,
+ char **names, unsigned int flags,
+ const char *id_attr) {
+ size_t nint = 0;
+ int r = 0;
+ DWORD tableSize = 0;
+ IP_ADAPTER_ADDRESSES *adapter;
+
+ adapter = build_adapter_table(ncf);
+ ERR_COND_BAIL(adapter == NULL, ncf, EOTHER);
+
+ while(adapter) {
+ if(names) {
+ char wName[8192];
+ r = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName,
+ -1, wName, sizeof(wName), NULL, NULL);
+ ERR_NOMEM(r == 0, ncf);
+ names[nint] = strdup(wName);
+ ERR_NOMEM(names[nint] == NULL, ncf);
+ }
+ nint++;
+ adapter = adapter->Next;
+ }
+ return nint;
+ error:
+ free(adapter);
+ while(nint > 0) {
+ free(names[nint]);
+ nint--;
+ }
+ return -1;
+}
+
+int drv_list_interfaces(struct netcf *ncf,
+ int maxnames, char **names,
+ unsigned int flags) {
+ return list_interface_ids(ncf, maxnames, names, 0, NULL);
+}
+
+
+int drv_num_of_interfaces(struct netcf *ncf, unsigned int flags) {
+ return list_interface_ids(ncf, 0, NULL, 0, NULL);
+}
+
+
+struct netcf_if *drv_lookup_by_name(struct netcf *ncf, const char *name) {
+ struct netcf_if *nif = NULL;
+ char *nameDup;
+ int r = 0;
+ IP_ADAPTER_ADDRESSES *adapter;
+
+ adapter = build_adapter_table(ncf);
+ ERR_COND_BAIL(adapter == NULL, ncf, EOTHER);
+
+ while(adapter) {
+ if(name) {
+ char wName[8192];
+ r = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName,
+ -1, wName, sizeof(wName), NULL, NULL);
+ ERR_NOMEM(r == 0, ncf);
+ if(strcmp(name,wName) == 0) {
+ nameDup = strdup(wName);
+ ERR_NOMEM(nameDup == NULL, ncf);
+ nif = make_netcf_if(ncf, nameDup);
+ ERR_BAIL(ncf);
+ return nif;
+ }
+ }
+ adapter = adapter->Next;
+ }
+ /* If we get here then the device wasn't found, however,
+ for cases where we know the device is disabled and
+ want to re-enable it we have to assume the device is
+ physically present
+ */
+ nameDup = strdup(name);
+ ERR_NOMEM(nameDup == NULL, ncf);
+ nif = make_netcf_if(ncf, nameDup);
+ ERR_BAIL(ncf);
+ return nif;
+ error:
+ free(adapter);
+ if(nameDup)
+ free(nameDup);
+ unref(nif, netcf_if);
+ return nif;
+}
+
+const char *drv_mac_string(struct netcf_if *nif) {
+ struct netcf *ncf = nif->ncf;
+ size_t i = 0;
+ int r = 0;
+ char mac[BUFSIZE], *buf;
+
+ IP_ADAPTER_ADDRESSES *adapter;
+
+ adapter = build_adapter_table(ncf);
+ ERR_COND_BAIL(adapter == NULL, ncf, EOTHER);
+
+ while(adapter) {
+ char wName[8192];
+ r = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName,
+ -1, wName, sizeof(wName), NULL, NULL);
+ ERR_NOMEM(r == 0, ncf);
+ if(strcmp(nif->name, wName) == 0) {
+ for(i = 0; i < adapter->PhysicalAddressLength; i++) {
+ if (i == 0) {
+ ERR_NOMEM(asprintf(&buf, "%.2X:", adapter->PhysicalAddress[i]) < 0, ncf);
+ strcpy(mac, buf);
+ }
+ if (i == (adapter->PhysicalAddressLength - 1)) {
+ ERR_NOMEM(asprintf(&buf, "%.2X", adapter->PhysicalAddress[i]) < 0, ncf);
+ strcat(mac, buf);
+ } else {
+ ERR_NOMEM(asprintf(&buf, "%.2X:", adapter->PhysicalAddress[i]) < 0, ncf);
+ strcat(mac, buf);
+ }
+ nif->mac = strdup(mac);
+ ERR_NOMEM(nif->mac == NULL, ncf);
+ }
+ return nif->mac;
+ }
+ adapter = adapter->Next;
+ }
+ error:
+ free(adapter);
+ free(buf);
+ free(mac);
+ return nif->mac;
+}
+
+int drv_if_down(struct netcf_if *nif) {
+ struct netcf *ncf = nif->ncf;
+ char *exe_path;
+ char *p;
+ int r = 0;
+
+ p = getenv("WINDIR");
+ r = asprintf(&exe_path, "%s\\system32\\netsh", p);
+ ERR_NOMEM(r < 0, ncf);
+
+ r = _spawnl(_P_WAIT, exe_path, exe_path, "interface",
+ "set", "interface", nif->name, "disabled", NULL);
+ ERR_COND_BAIL(r != 0, ncf, EEXEC);
+ return 0;
+ error:
+ free(exe_path);
+ return -1;
+}
+
+int drv_if_up(struct netcf_if *nif) {
+ struct netcf *ncf = nif->ncf;
+ char *exe_path;
+ char *p;
+ int r = 0;
+
+ p = getenv("WINDIR");
+ r = asprintf(&exe_path, "%s\\system32\\netsh", p);
+ ERR_NOMEM(r < 0, ncf);
+
+ r = _spawnl(_P_WAIT, exe_path, exe_path, "interface",
+ "set", "interface", nif->name, "enabled", NULL);
+ ERR_COND_BAIL(r != 0, ncf, EEXEC);
+ return 0;
+ error:
+ free(exe_path);
+ return -1;
+}
+
diff --git a/src/netcf_win.h b/src/netcf_win.h
new file mode 100644
index 0000000..659a1b7
--- /dev/null
+++ b/src/netcf_win.h
@@ -0,0 +1,54 @@
+/*
+ * netcf-win.h: windows functions
+ *
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Adam Stokes <ajs(a)redhat.com>
+ */
+
+#ifndef NETCF_WIN_H
+#define NETCF_WIN_H
+
+#ifndef WINVER
+# define WINVER 0x0501
+#endif
+
+#include "internal.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windns.h>
+#include <iphlpapi.h>
+#include <process.h>
+#include "safe-alloc.h"
+#include "ref.h"
+#include "list.h"
+
+char *strerror_r(int errnum, char *buf, size_t buflen);
+
+/* Like asprintf, but set *STRP to NULL on error */
+ATTRIBUTE_FORMAT(printf, 2, 3)
+int xasprintf(char **strp, const char *format, ...);
+
+/* Create a new netcf if instance for interface NAME */
+struct netcf_if *make_netcf_if(struct netcf *ncf, char *name);
+
+PIP_ADAPTER_ADDRESSES build_adapter_table(struct netcf *ncf);
+#endif /* NETCF_WIN_H */
--
1.7.2.3
13 years, 4 months