[netcf-devel] [PATCH] Reorganize code to making porting to other platforms easier

Laine Stump laine at laine.org
Tue Nov 16 20:52:59 UTC 2010


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



More information about the netcf-devel mailing list