[prev in list] [next in list] [prev in thread] [next in thread]
List: netcf-devel
Subject: [netcf-devel] [PATCH 3/4] Support Debian network interface configuration
From: berrange () redhat ! com (Daniel P ! Berrange)
Date: 2011-09-16 14:18:06
Message-ID: 1316182687-18288-4-git-send-email-berrange () redhat ! com
[Download RAW message or body]
From: "Daniel P. Berrange" <berrange at redhat.com>
---
.gitignore | 1 +
configure.ac | 20 +-
data/xml/debian-get.xsl | 222 +++++++++
data/xml/debian-put.xsl | 284 ++++++++++++
doc/debian.txt | 195 ++++++++
src/Makefile.am | 8 +-
src/drv_debian.c | 1177 +++++++++++++++++++++++++++++++++++++++++++++++
src/drv_initscripts.c | 1 +
src/dutil.c | 1 -
src/dutil_linux.c | 57 +++
src/dutil_linux.h | 6 +
11 files changed, 1964 insertions(+), 8 deletions(-)
create mode 100644 data/xml/debian-get.xsl
create mode 100644 data/xml/debian-put.xsl
create mode 100644 doc/debian.txt
create mode 100644 src/drv_debian.c
diff --git a/.gitignore b/.gitignore
index 310a773..ee8f3ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ src/ncftransform
src/netcf.syms
src/netcf-transaction.init
tests/test-initscripts
+tests/test-debian
*.aux
*.dvi
*.log
diff --git a/configure.ac b/configure.ac
index 1ef302b..2a6543c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,19 +28,27 @@ AC_ARG_WITH([driver],
[],[with_driver=check])
AC_MSG_CHECKING([Network driver name])
+if test "x$with_driver" = "xcheck" && test -f /etc/redhat-release ; then
+ with_driver=initscripts
+fi
+if test "x$with_driver" = "xcheck" && test -f /etc/fedora-release ; then
+ with_driver=initscripts
+fi
+if test "x$with_driver" = "xcheck" && test -f /etc/debian_version ; then
+ with_driver=debian
+fi
+if test "x$with_driver" = "xcheck" && test -f /etc/ubuntu_version ; then
+ with_driver=debian
+fi
if test "x$with_driver" = "xcheck" ; then
- if test -f /etc/fedora-release || test -f /etc/redhat-release
- then
- with_driver=initscripts
- else
- AC_MSG_ERROR([Cannot detect network driver, use --with-driver=NAME to select \
implementation])
- fi
+ AC_MSG_ERROR([Cannot detect network driver, use --with-driver=NAME to select \
implementation]) fi
AC_MSG_RESULT([$with_driver])
NETCF_DRIVER=$with_driver
AC_SUBST([NETCF_DRIVER])
AC_DEFINE_UNQUOTED([NETCF_DRIVER], $NETCF_DRIVER, [The network driver])
AM_CONDITIONAL([NETCF_DRIVER_INITSCRIPTS], test "x$with_driver" = "xinitscripts")
+AM_CONDITIONAL([NETCF_DRIVER_DEBIAN], test "x$with_driver" = "xdebian")
dnl Need to test if pkg-config exists
PKG_PROG_PKG_CONFIG
diff --git a/data/xml/debian-get.xsl b/data/xml/debian-get.xsl
new file mode 100644
index 0000000..991eef8
--- /dev/null
+++ b/data/xml/debian-get.xsl
@@ -0,0 +1,222 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:ipcalc = "http://redhat.com/xslt/netcf/ipcalc/1.0"
+ extension-element-prefixes="ipcalc"
+ version="1.0">
+
+ <xsl:import href="util-get.xsl"/>
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:template match="/">
+ <forest>
+ <tree path="/files/etc/network/interfaces">
+ <xsl:apply-templates/>
+ </tree>
+ </forest>
+ </xsl:template>
+
+ <xsl:template match="interface">
+ <xsl:if test="./start/@mode = 'onboot'">
+ <array label="auto">
+ <element>
+ <node value="{@name}"/>
+ </element>
+ </array>
+ </xsl:if>
+ <array label="iface">
+ <xsl:for-each select="protocol">
+ <element key="{../@name}">
+ <xsl:apply-templates select="."/>
+ <xsl:if test="position() = 1">
+ <xsl:apply-templates select="../mtu|../mac"/>
+ </xsl:if>
+ <xsl:if test="position() = 1">
+ <xsl:apply-templates select="../bridge|../bond|../vlan"/>
+ <xsl:for-each select="../bridge/interface[count(bond|vlan)>0]">
+ <node label="pre-up" value="ifup {@name}"/>
+ <node label="post-down" value="ifdown {@name}"/>
+ </xsl:for-each>
+ </xsl:if>
+ </element>
+ </xsl:for-each>
+ <xsl:if test="count(protocol) = 0">
+ <element key="{@name}">
+ <node label="family" value='inet'/>
+ <node label='method' value='manual'/>
+ <xsl:apply-templates select="mtu|mac"/>
+ <xsl:apply-templates select="bridge|bond|vlan"/>
+ <xsl:for-each select="bridge/interface[count(bond)>0]">
+ <node label="pre-up" value="ifup {@name}"/>
+ <node label="post-down" value="ifdown {@name}"/>
+ </xsl:for-each>
+ </element>
+ </xsl:if>
+ <xsl:for-each select="bridge/interface[count(bond|vlan)>0]">
+ <element key="{@name}">
+ <node label="family" value='inet'/>
+ <node label="method" value='manual'/>
+ <xsl:apply-templates select="bond|vlan"/>
+ </element>
+ </xsl:for-each>
+ </array>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv4' and count(dhcp) > 0]">
+ <node label='family' value='inet'/>
+ <node label='method' value='dhcp'/>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv4' and count(ip) > 0]">
+ <node label='family' value='inet'/>
+ <node label='method' value='static'/>
+ <xsl:apply-templates select="ip[1]|route|address" mode='ipv4'/>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv4' and count(ip|dhcp) = 0]">
+ <node label='family' value='inet'/>
+ <node label='method' value='manual'/>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv6' and count(dhcp) > 0]">
+ <node label='family' value='inet6'/>
+ <node label='method' value='dhcp'/>
+ <xsl:apply-templates select="." mode="autoconf"/>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv6' and count(ip) > 0]">
+ <node label='family' value='inet6'/>
+ <node label='method' value='static'/>
+ <xsl:apply-templates select="ip[1]|route|address" mode='ipv6'/>
+ <xsl:apply-templates select="." mode="autoconf"/>
+ <xsl:for-each select="ip[position()>1]">
+ <node label="up" value="/sbin/ifconfig {../../@name} inet6 add \
{@address}/{@prefix}"/> + </xsl:for-each>
+ <xsl:for-each select="ip[position()>1]">
+ <node label="down" value="/sbin/ifconfig {../../@name} inet6 del \
{@address}/{@prefix}"/> + </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv6' and count(ip|dhcp) = 0]">
+ <node label='family' value='inet6'/>
+ <node label='method' value='manual'/>
+ <xsl:apply-templates select="." mode="autoconf"/>
+ </xsl:template>
+
+ <xsl:template match="protocol[@family='ipv6']" mode="autoconf">
+ <xsl:if test="count(autoconf) = 0">
+ <node label='pre-up' value='echo 0 > \
/proc/sys/net/ipv6/conf/{../@name}/autoconf'/> + <node label='post-down' \
value='echo 1 > /proc/sys/net/ipv6/conf/{../@name}/autoconf'/> + </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="ip" mode='ipv4'>
+ <node label="address" value="{@address}"/>
+ <xsl:if test="@prefix">
+ <node label="netmask" value="{ipcalc:netmask(@prefix)}"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="ip" mode='ipv6'>
+ <node label="address" value="{@address}"/>
+ <xsl:if test="@prefix">
+ <node label="netmask" value="{@prefix}"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="mtu">
+ <node label="mtu" value="{@size}"/>
+ </xsl:template>
+
+ <xsl:template match="mac">
+ <node label="hwaddress" value="ether {@address}"/>
+ </xsl:template>
+
+ <xsl:template match="bridge">
+ <xsl:choose>
+ <xsl:when test="count(interface) > 0">
+ <node label="bridge_ports">
+ <xsl:attribute name="value">
+ <xsl:for-each select="interface">
+ <xsl:if test="position() != 1">
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="@name"/>
+ </xsl:for-each>
+ </xsl:attribute>
+ </node>
+ </xsl:when>
+ <xsl:otherwise>
+ <node label="bridge_ports" value="none"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="@stp">
+ <node label="bridge_stp" value="{@stp}"/>
+ </xsl:if>
+ <xsl:if test="@delay">
+ <node label="bridge_fd" value="{@delay}"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="bond">
+ <xsl:choose>
+ <xsl:when test="count(interface) > 0">
+ <node label="bond_slaves">
+ <xsl:attribute name="value">
+ <xsl:for-each select="interface">
+ <xsl:if test="position() != 1">
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="@name"/>
+ </xsl:for-each>
+ </xsl:attribute>
+ </node>
+ <node label="bond_primary" value="{interface[1]/@name}"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <node label="bond_slaves" value="none"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="@mode">
+ <node label="bond_mode" value="{@mode}"/>
+ </xsl:if>
+ <xsl:apply-templates select="miimon|arpmon"/>
+ </xsl:template>
+
+ <xsl:template match="miimon">
+ <node label="bond_miimon" value="{@freq}"/>
+ <xsl:if test="@downdelay">
+ <node label="bond_downdelay" value="{@downdelay}"/>
+ </xsl:if>
+ <xsl:if test="@updelay">
+ <node label="bond_updelay" value="{@updelay}"/>
+ </xsl:if>
+ <xsl:if test="@carrier='ioctl'">
+ <node label="bond_use_carrier" value="0"/>
+ </xsl:if>
+ <xsl:if test="@carrier='netif'">
+ <node label="bond_use_carrier" value="1"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="arpmon">
+ <node label="bond_arp_interval" value="{@interval}"/>
+ <node label="bond_arp_ip_target" value="{@target}"/>
+ <xsl:if test="@validate">
+ <node label="bond_arp_validate" value="{@validate}"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="vlan">
+ <node label="vlan_raw_device" value="{interface/@name}"/>
+ </xsl:template>
+
+ <xsl:template match="route[@gateway]" mode='ipv4'>
+ <node label="gateway" value="{@gateway}"/>
+ </xsl:template>
+ <xsl:template match="route[@gateway]" mode='ipv6'>
+ <node label="gateway" value="{@gateway}"/>
+ </xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/data/xml/debian-put.xsl b/data/xml/debian-put.xsl
new file mode 100644
index 0000000..37413b5
--- /dev/null
+++ b/data/xml/debian-put.xsl
@@ -0,0 +1,284 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:ipcalc = "http://redhat.com/xslt/netcf/ipcalc/1.0"
+ xmlns:bond = "http://redhat.com/xslt/netcf/bond/1.0"
+ xmlns:str="http://exslt.org/strings"
+ extension-element-prefixes="bond ipcalc str"
+ version="1.0">
+
+ <xsl:import href="util-put.xsl"/>
+
+ <xsl:output method="xml" indent="yes"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="/">
+ <xsl:apply-templates \
select="forest/tree[@path='/files/etc/network/interfaces']/array[@label='iface']/element[1]" \
mode='iface'/> + </xsl:template>
+
+ <xsl:template match="element" mode="ifacetype">
+ <xsl:choose>
+ <xsl:when test="count(node[@label='bridge_ports']) > 0">
+ <xsl:text>bridge</xsl:text>
+ </xsl:when>
+ <xsl:when test="count(node[@label='bond_slaves']) > 0">
+ <xsl:text>bond</xsl:text>
+ </xsl:when>
+ <xsl:when test="count(node[@label='vlan_raw_device']) > 0">
+ <xsl:text>vlan</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>ethernet</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ifacestart">
+ <xsl:variable name='key' select="@key"/>
+ <xsl:if test="count(../../array[@label='auto']/element/node[@value=$key]) > 0">
+ <start mode="onboot"/>
+ </xsl:if>
+ <xsl:if test="count(../../array[@label='auto']/element/node[@value=$key]) = 0">
+ <start mode="none"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ifacemisc">
+ <xsl:if test="count(node[@label='hwaddress' and substring(@value, 1, 6) = 'ether \
']) > 0"> + <mac address="{substring(node[@label='hwaddress']/@value, 7)}"/>
+ </xsl:if>
+ <xsl:if test="count(node[@label='mtu']) > 0">
+ <mtu size="{node[@label='mtu']/@value}"/>
+ </xsl:if>
+ </xsl:template>
+ <!--
+ Ethernet adapter
+ -->
+ <xsl:template match="element" mode="iface">
+ <interface name="{@key}">
+ <xsl:attribute name='type'>
+ <xsl:apply-templates select="." mode="ifacetype"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="." mode="ifacestart"/>
+ <xsl:apply-templates select="." mode="ifacemisc"/>
+ <xsl:apply-templates select="." mode='protocol'/>
+ <xsl:apply-templates select="." mode='bridge'/>
+ <xsl:apply-templates select="." mode='bond'/>
+ <xsl:apply-templates select="." mode='vlan'/>
+ </interface>
+ </xsl:template>
+
+ <xsl:template match="element" mode="protocol">
+ <xsl:choose>
+ <xsl:when test="count(node[@label='family' and @value='inet']) > 0">
+ <xsl:apply-templates select="." mode="ipv4"/>
+ <xsl:apply-templates select="../element[count(node[@label='family' and \
@value='inet6'][1])>0]" mode='ipv6'/> + </xsl:when>
+ <xsl:when test="count(node[@label='family' and @value='inet6']) > 0">
+ <xsl:apply-templates select="." mode="ipv6"/>
+ <xsl:apply-templates select="../element[count(node[@label='family' and \
@value='inet4'][1])>0]" mode='ipv4'/> + </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv4">
+ <xsl:choose>
+ <xsl:when test="node[@label='method']/@value = 'static'">
+ <protocol family='ipv4'>
+ <xsl:apply-templates select="." mode="ipv4static"/>
+ </protocol>
+ </xsl:when>
+ <xsl:when test="node[@label='method']/@value = 'dhcp'">
+ <protocol family='ipv4'>
+ <xsl:apply-templates select="." mode="ipv4dhcp"/>
+ </protocol>
+ </xsl:when>
+ <xsl:when test="node[@label='method']/@value = 'loopback'">
+ <protocol family='ipv4'>
+ <xsl:apply-templates select="." mode="ipv4lo"/>
+ </protocol>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv4static">
+ <xsl:if test="count(node[@label='address']) > 0">
+ <ip address="{node[@label='address']/@value}">
+ <xsl:if test="count(node[@label='netmask']) > 0">
+ <xsl:attribute name="prefix">
+ <xsl:value-of select="ipcalc:prefix(node[@label='netmask']/@value)"/>
+ </xsl:attribute>
+ </xsl:if>
+ </ip>
+ </xsl:if>
+ <xsl:if test="count(node[@label='gateway']) > 0">
+ <route gateway="{node[@label='gateway']/@value}"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv4lo">
+ <ip address="127.0.0.1" prefix="8"/>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv4dhcp">
+ <dhcp/> <!-- peerdns='no'/>-->
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv6">
+ <protocol family='ipv6'>
+ <xsl:apply-templates select="." mode="ipv6static"/>
+ <xsl:apply-templates select="." mode="ipv6autoconf"/>
+ <xsl:apply-templates select="." mode="ipv6dhcp"/>
+ <xsl:apply-templates select="." mode="ipv6lo"/>
+ </protocol>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv6static">
+ <xsl:if test="count(node[@label='address']) > 0">
+ <ip address="{node[@label='address']/@value}" \
prefix="{node[@label='netmask']/@value}"/> + </xsl:if>
+ <xsl:if test="count(node[@label='gateway']) > 0">
+ <route gateway="{node[@label='gateway']/@value}"/>
+ </xsl:if>
+
+ <xsl:for-each select="node[@label='up']">
+ <xsl:variable name="ipaddcmd" select="concat('/sbin/ifconfig ', ../@key, ' \
inet6 add ')"/> +
+ <xsl:if test="starts-with(@value, $ipaddcmd)">
+ <xsl:variable name="ipaddrprefix" select="substring(@value, \
string-length($ipaddcmd)+1)"/> + <ip address="{substring-before($ipaddrprefix, \
'/')}" prefix="{substring-after($ipaddrprefix, '/')}"/> + </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv6lo">
+ <xsl:if test="node[@label='method']/@value = 'loopback'">
+ <ip address="::1" prefix="8"/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv6dhcp">
+ <xsl:if test="node[@label='method']/@value = 'dhcp'">
+ <dhcp/> <!-- peerdns='no'/>-->
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="ipv6autoconf">
+ <xsl:variable name="autoconfcmd" select="concat('echo 0 > \
/proc/sys/net/ipv6/conf/', @key, '/autoconf')"/> + <xsl:if \
test="count(node[@label='method' and @value='loopback']) = 0 and \
count(node[@label='pre-up' and @value = $autoconfcmd]) = 0"> + <autoconf/>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="bridge">
+ <xsl:if test="count(node[@label='bridge_ports']) > 0">
+ <bridge>
+ <xsl:if test="count(node[@label='bridge_stp']) > 0">
+ <xsl:attribute name='stp'>
+ <xsl:value-of select="node[@label='bridge_stp']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="count(node[@label='bridge_fd']) > 0">
+ <xsl:attribute name='delay'>
+ <xsl:value-of select="node[@label='bridge_fd']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:variable name="cur" select=".."/>
+ <xsl:if test="node[@label='bridge_ports']/@value != 'none'">
+ <xsl:for-each select="str:tokenize(node[@label='bridge_ports']/@value, ' \
')"> + <xsl:variable name="port" select="."/>
+ <interface name='{$port}'>
+ <xsl:attribute name="type">
+ <xsl:choose>
+ <xsl:when test="count($cur/element[@key=$port]) > 0">
+ <xsl:apply-templates select="$cur/element[@key=$port]" \
mode="ifacetype"/> + </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>ethernet</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:apply-templates select="$cur/element[@key=$port]" \
mode="ifacemisc"/> + <xsl:apply-templates \
select="$cur/element[@key=$port]" mode='bond'/> + <xsl:apply-templates \
select="$cur/element[@key=$port]" mode='vlan'/> + </interface>
+ </xsl:for-each>
+ </xsl:if>
+ </bridge>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="bond">
+ <xsl:if test="count(node[@label='bond_slaves']) > 0">
+ <bond>
+ <xsl:if test="count(node[@label='bond_mode']) > 0">
+ <xsl:attribute name='mode'>
+ <xsl:value-of select="node[@label='bond_mode']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="bondmiimon"/>
+ <xsl:apply-templates select="." mode="bondarpmon"/>
+ <xsl:for-each select="str:tokenize(node[@label='bond_slaves']/@value, ' ')">
+ <interface type='ethernet' name='{.}'/>
+ </xsl:for-each>
+ </bond>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="bondmiimon">
+ <xsl:if test="count(node[@label='bond_miimon']) > 0">
+ <miimon freq="{node[@label='bond_miimon']/@value}">
+ <xsl:if test="count(node[@label='bond_updelay']) > 0">
+ <xsl:attribute name='updelay'>
+ <xsl:value-of select="node[@label='bond_updelay']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="count(node[@label='bond_downdelay']) > 0">
+ <xsl:attribute name='downdelay'>
+ <xsl:value-of select="node[@label='bond_downdelay']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="count(node[@label='bond_use_carrier']) > 0">
+ <xsl:attribute name='carrier'>
+ <xsl:if test="node[@label='bond_use_carrier'] != 0">
+ <xsl:text>ioctl</xsl:text>
+ </xsl:if>
+ <xsl:if test="node[@label='bond_use_carrier'] = 0">
+ <xsl:text>netif</xsl:text>
+ </xsl:if>
+ </xsl:attribute>
+ </xsl:if>
+ </miimon>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="bondarpmon">
+ <xsl:if test="count(node[@label='bond_arp_ip_target']) > 0">
+ <arpmon target="{node[@label='bond_arp_ip_target']/@value}">
+ <xsl:if test="count(node[@label='bond_arp_interval']) > 0">
+ <xsl:attribute name='interval'>
+ <xsl:value-of select="node[@label='bond_arp_interval']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="count(node[@label='bond_arp_validate']) > 0">
+ <xsl:attribute name='validate'>
+ <xsl:value-of select="node[@label='bond_arp_validate']/@value"/>
+ </xsl:attribute>
+ </xsl:if>
+ </arpmon>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="element" mode="vlan">
+ <xsl:if test="count(node[@label='vlan_raw_device']) > 0">
+ <vlan tag="{substring(@key, \
string-length(node[@label='vlan_raw_device']/@value)+2)}"> + <interface \
name="{node[@label='vlan_raw_device']/@value}"/> + </vlan>
+ </xsl:if>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/doc/debian.txt b/doc/debian.txt
new file mode 100644
index 0000000..181628b
--- /dev/null
+++ b/doc/debian.txt
@@ -0,0 +1,195 @@
+ NetCF Debian Driver
+ ===================
+
+The Debian driver in NetCF is designed around the reading/writing of the
+primary configuration file /etc/network/interfaces.
+
+There are, unfortuntely, often many different ways to achieve the same
+type of network setup using /etc/network/interfaces. The NetCF driver
+does not attempt to understand all possible styles of configuration.
+The current driver impl has picked what is believed to be the "best
+practice" configuration style. It should be able to read back any
+configuration that it has written out, and any configuration the user
+may have created manually, provided they follow the best practice.
+
+Some of the configurations reqiure extra packages to be installed:
+
+ - ifenslave (required for any bonding config)
+ - bridge-utils (required for any bridging config)
+ - vlan (required for any vlan config)
+
+This document illustrates the types of configuration that the Debian
+NetCF driver supports:
+
+Ethernet Devices
+================
+
+Loopback:
+
+ auto lo
+ iface lo inet loopback
+
+DHCP:
+
+ auto eth0
+ iface eth0 inet dhcp
+
+Static config:
+
+ auto eth0
+ iface eth0 inet static
+ address 196.168.1.1
+ netmask 255.255.255.0
+ gateway 196.168.1.255
+
+No IP config
+
+
+ auto eth0
+ iface eth0 inet manual
+
+Config with MTU / MAC addres
+
+ auto eth0
+ iface eth0 inet dhcp
+ hwaddr ether 00:11:22:33:44:55
+ mtu 150
+
+Bonding
+=======
+
+With miimon
+
+ iface bond0 inet dhcp
+ bond_slaves eth1 eth2
+ bond_primary eth1
+ bond_mode active-backup
+ bond_miimon 100
+ bond_updelay 10
+ bond_use_carrier 0
+
+With arpmon
+
+ iface bond2 inet dhcp
+ bond_slaves eth6
+ bond_primary eth6
+ bond_mode active-backup
+ bond_arp_interval 100
+ bond_arp_ip_target 198.168.1.1
+
+
+VLANs
+=====
+
+ auto eth0.42
+ iface eth0.42 inet static
+ address 196.168.1.1
+ netmask 255.255.255.0
+ vlan_raw_device eth0
+
+
+Bridging
+========
+
+With single interface and IP addr
+
+ auto br0
+ iface br0 inet static
+ address 192.68.2.3
+ netmask 255.255.255.0
+ mtu 1500
+ bridge_ports eth3
+ bridge_stp off
+ bridge_fd 0.01
+
+With no IP addr
+
+ auto br0
+ iface br0 inet manual
+ bridge_ports eth3
+ bridge_stp off
+ bridge_fd 0.01
+
+With multiple interfaces
+
+ auto br0
+ iface br0 inet static
+ address 192.68.2.3
+ netmask 255.255.255.0
+ mtu 1500
+ bridge_ports eth3 eth4
+ bridge_stp off
+ bridge_fd 0.01
+
+With no interface or addr
+
+ auto br0
+ iface br0 inet manual
+ mtu 1500
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0.01
+
+
+Complex Bridging
+================
+
+Bridging a bond:
+
+ auto br1
+ iface br1 inet manual
+ mtu 1500
+ bridge_ports bond1
+ bridge_stp off
+ pre-up ifup bond1
+ post-down ifdown bond1
+ iface bond1 inet manual
+ bond_slaves eth4
+ bond_primary eth4
+ bond_mode active-backup
+ bond_miimon 100
+ bond_updelay 10
+ bond_use_carrier 0
+
+Bridging a VLAN:
+
+ auto br2
+ iface br2 inet manual
+ mtu 1500
+ bridge_ports eth0.42
+ bridge_stp off
+ iface eth0.42 inet manual
+ vlan_raw_device eth0
+
+
+IPv6
+====
+
+Static addressing, with multiple addresses:
+
+ auto eth5
+ iface eth5 inet6 static
+ address 3ffe:ffff:0:5::1
+ netmask 128
+ pre-up echo 0 > /proc/sys/net/ipv6/conf/eth5/autoconf
+ post-down echo 1 > /proc/sys/net/ipv6/conf/eth5/autoconf
+ up /sbin/ifconfig eth5 inet6 add 3ffe:ffff:0:5::5/128
+ down /sbin/ifconfig eth5 inet6 del 3ffe:ffff:0:5::5/128
+
+Stateless autoconf
+
+ auto eth5
+ iface eth5 inet6 manual
+
+DHCPv6 with autoconf
+
+ auto eth5
+ iface eth5 inet6 dhcp
+
+
+DHCPv6 without autoconf
+
+ auto eth5
+ iface eth5 inet6 dhcp
+ pre-up echo 0 > /proc/sys/net/ipv6/conf/eth5/autoconf
+ post-down echo 1 > /proc/sys/net/ipv6/conf/eth5/autoconf
diff --git a/src/Makefile.am b/src/Makefile.am
index eed7a22..8b4fec0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,15 +16,21 @@ noinst_PROGRAMS = ncftransform
DRIVER_SOURCES_LINUX = dutil.h dutil.c \
dutil_linux.h dutil_linux.c
DRIVER_SOURCES_INITSCRIPTS = drv_initscripts.c
+DRIVER_SOURCES_DEBIAN = drv_debian.c
EXTRA_DIST = netcf_public.syms \
netcf_private.syms \
netcf-transaction.init.sh \
- $(DRIVER_SOURCES_LINUX) $(DRIVER_SOURCES_INITSCRIPTS)
+ $(DRIVER_SOURCES_LINUX) \
+ $(DRIVER_SOURCES_INITSCRIPTS) \
+ $(DRIVER_SOURCES_DEBIAN)
if NETCF_DRIVER_INITSCRIPTS
DRIVER_SOURCES = $(DRIVER_SOURCES_LINUX) $(DRIVER_SOURCES_INITSCRIPTS)
endif
+if NETCF_DRIVER_DEBIAN
+DRIVER_SOURCES = $(DRIVER_SOURCES_LINUX) $(DRIVER_SOURCES_DEBIAN)
+endif
BUILT_SOURCES = datadir.h netcf.syms
diff --git a/src/drv_debian.c b/src/drv_debian.c
new file mode 100644
index 0000000..d28eda0
--- /dev/null
+++ b/src/drv_debian.c
@@ -0,0 +1,1177 @@
+/*
+ * 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 at 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_interfaces_path =
+ "/files/etc/network/interfaces";
+
+/* Augeas should only load the files we are interested in */
+static const struct augeas_pv augeas_xfm_common_pv[] = {
+ /* Interfaces files */
+ { "/augeas/load/Interfaces/lens", "Interfaces.lns" },
+ { "/augeas/load/Interfaces/incl",
+ "/etc/network/interfaces" },
+ { "/augeas/load/Interfaces/excl[1]", "*~" },
+ { "/augeas/load/Interfaces/excl[2]", "*.bak" },
+ { "/augeas/load/Interfaces/excl[3]", "*.orig" },
+ { "/augeas/load/Interfaces/excl[4]", "*.rpmnew" },
+ { "/augeas/load/Interfaces/excl[5]", "*.rpmorig" },
+ { "/augeas/load/Interfaces/excl[6]", "*.rpmsave" },
+ { "/augeas/load/Interfaces/excl[7]", "*.augnew" },
+ { "/augeas/load/Interfaces/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 };
+
+
+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);
+}
+
+/* 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/iface[. = '%s' and count(./bond_slaves)> 0]",
+ network_interfaces_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/iface[. = '%s' and count(./bridge_ports)> 0]",
+ network_interfaces_path,
+ name);
+ return nmatches > 0;
+}
+
+static int interface_deps(struct netcf *ncf, char ***slaves, const char *fmt, ...) {
+ struct augeas *aug = NULL;
+ char *path = NULL;
+ int r, nslaves = 0;
+ char **matches = NULL;
+ char **tmp;
+ int nmatches = 0;
+ const char *devs;
+ va_list args;
+
+ 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);
+ }
+
+ nmatches = aug_match(aug, path, &matches);
+ ERR_COND_BAIL(nmatches < 0, ncf, EOTHER);
+
+ if (!nmatches)
+ return 0;
+
+ for (int i = 0 ; i < nmatches ; i++) {
+ r = aug_get(aug, matches[i], &devs);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ if (strcmp(devs, "none") == 0)
+ continue;
+
+ do {
+ const char *skip;
+ tmp = realloc(*slaves, sizeof(char *) * (nslaves+1));
+ ERR_COND_BAIL(!tmp, ncf, ENOMEM);
+ *slaves = tmp;
+ nslaves++;
+
+ skip = strchr(devs, ' ');
+ if (skip) {
+ (*slaves)[nslaves-1] = strndup(devs, skip-devs);
+ skip++;
+ } else {
+ (*slaves)[nslaves-1] = strdup(devs);
+ }
+ ERR_NOMEM((*slaves)[nslaves-1] == NULL, ncf);
+ devs = skip;
+ } while (devs && *devs);
+ }
+
+ free_matches(nmatches, &matches);
+ return nslaves;
+ error:
+ free_matches(nslaves, slaves);
+ free_matches(nmatches, &matches);
+ return -1;
+}
+
+static int bridge_ports(struct netcf *ncf, const char *name, char ***slaves) {
+ return interface_deps(ncf, slaves, "%s/iface[. = '%s']/bridge_ports",
+ network_interfaces_path, name);
+}
+
+static int bond_slaves(struct netcf *ncf, const char *name, char ***slaves) {
+ return interface_deps(ncf, slaves, "%s/iface[. = '%s']/bond_slaves",
+ network_interfaces_path, name);
+}
+
+static int all_slaves(struct netcf *ncf, char ***slaves) {
+ int nbridge_ports = 0, nbond_slaves = 0;
+ char **abridge_ports = NULL, **abond_slaves = NULL;
+
+ nbridge_ports = interface_deps(ncf, &abridge_ports,
+ "%s/iface/bridge_ports",
+ network_interfaces_path);
+ ERR_BAIL(ncf);
+
+ nbond_slaves = interface_deps(ncf, &abond_slaves,
+ "%s/iface/bond_slaves",
+ network_interfaces_path);
+ ERR_BAIL(ncf);
+
+ *slaves = malloc(sizeof(char *) * (nbridge_ports + nbond_slaves));
+ ERR_COND_BAIL(*slaves == NULL, ncf, ENOMEM);
+
+ for (int i = 0 ; i < nbridge_ports ; i++) {
+ (*slaves)[i] = abridge_ports[i];
+ abridge_ports[i] = NULL;
+ }
+ for (int i = 0 ; i < nbond_slaves ; i++) {
+ (*slaves)[nbridge_ports+i] = abond_slaves[i];
+ abond_slaves[i] = NULL;
+ }
+ free(abridge_ports);
+ free(abond_slaves);
+
+ return nbond_slaves + nbridge_ports;
+
+ error:
+ free_matches(nbridge_ports, &abridge_ports);
+ free_matches(nbond_slaves, &abond_slaves);
+ return -1;
+}
+
+
+static bool is_slave(struct netcf *ncf, const char *intf) {
+ bool r = false;
+ char **slaves;
+ int nslaves = all_slaves(ncf, &slaves);
+ ERR_BAIL(ncf);
+
+ for (int i = 0 ; i < nslaves ; i++) {
+ if (strcmp(intf, slaves[i]) == 0) {
+ r = true;
+ break;
+ }
+ }
+ free_matches(nslaves, &slaves);
+ return r;
+
+ error:
+ return false;
+}
+
+static bool has_config(struct netcf *ncf, const char *name) {
+ int nmatches;
+
+ nmatches = aug_fmt_match(ncf, NULL,
+ "%s/iface[. = '%s']",
+ network_interfaces_path, name);
+ if (nmatches == 0)
+ return false;
+
+ return !is_slave(ncf, name);
+}
+
+
+
+/* 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_device_names(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] = strdup(devnames[i]);
+ ERR_NOMEM(!(*intf)[i], ncf);
+ }
+
+ FREE(devnames);
+ return ndevnames;
+
+ error:
+ FREE(devnames);
+ free_matches(ndevnames, intf);
+ return -1;
+}
+
+static int list_interfaces(struct netcf *ncf, char ***intf) {
+ int result = 0, ndevs;
+ char **devs = NULL;
+
+ ndevs = aug_fmt_match(ncf, &devs, "%s/iface", network_interfaces_path);
+ ERR_COND_BAIL(ndevs < 0, ncf, EOTHER);
+
+ result = uniq_device_names(ncf, ndevs, devs, intf);
+ ERR_BAIL(ncf);
+
+ /* 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,
+ (result - (i + 1))*sizeof((*intf)[0]));
+ result -= 1;
+ } else {
+ i += 1;
+ }
+ }
+
+ free_matches(ndevs, &devs);
+ return result;
+
+ error:
+ free_matches(ndevs, &devs);
+ 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);
+ goto error;
+ }
+
+ // FIXME: Check for errors
+ xsltInit();
+ exsltStrRegister();
+ ncf->driver->get = parse_stylesheet(ncf, "debian-get.xsl");
+ ncf->driver->put = parse_stylesheet(ncf, "debian-put.xsl");
+ 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);
+ 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) {
+ int nint = 0, nqualified = 0, result = 0;
+ char **intf = NULL;
+
+ 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++) {
+ int is_qualified = ((flags & (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE))
+ == (NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE));
+ if (!is_qualified) {
+ int is_active = if_is_active(ncf, intf[result]);
+ if ((is_active && (flags & NETCF_IFACE_ACTIVE))
+ || ((!is_active) && (flags & NETCF_IFACE_INACTIVE))) {
+
+ is_qualified = 1;
+ }
+ }
+ if (is_qualified) {
+ if (names) {
+ names[nqualified] = strdup(intf[result]);
+ ERR_NOMEM(names[nqualified] == NULL, ncf);
+ }
+ nqualified++;
+ }
+ }
+ free_matches(nint, &intf);
+ return nqualified;
+ error:
+ 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);
+}
+
+int drv_num_of_interfaces(struct netcf *ncf, unsigned int flags) {
+ return list_interface_ids(ncf, 0, NULL, flags);
+}
+
+struct netcf_if *drv_lookup_by_name(struct netcf *ncf, const char *name) {
+ struct netcf_if *nif = NULL;
+ char *name_dup = NULL;
+ int results;
+ char **names = NULL;
+ int maxnames = 0;
+ int found = 0;
+ int r;
+
+ results = list_interface_ids(ncf, 0, NULL, \
NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE); + ERR_BAIL(ncf);
+
+ maxnames = results;
+ r = ALLOC_N(names, maxnames);
+ ERR_NOMEM(r < 0, ncf);
+
+ results = list_interface_ids(ncf, maxnames, names, \
NETCF_IFACE_ACTIVE|NETCF_IFACE_INACTIVE); + ERR_BAIL(ncf);
+
+ for (int i = 0 ; i < results ; i++) {
+ if (strcmp(names[i], name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ ERR_COND_BAIL(found == 0, ncf, ENOENT);
+
+ 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_matches(maxnames, &names);
+ 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 int aug_get_xml_for_intf(struct netcf *ncf,
+ xmlNodePtr array,
+ const char *name) {
+ struct augeas *aug;
+ xmlNodePtr element = NULL, node = NULL;
+ char **matches = NULL;
+ char **intf = NULL;
+ int nmatches = 0, nintf = 0, r;
+
+ aug = get_augeas(ncf);
+ ERR_BAIL(ncf);
+
+
+ nintf = aug_fmt_match(ncf, &intf, "%s/%s[. = '%s']",
+ network_interfaces_path, "iface", name);
+ ERR_BAIL(ncf);
+ for (int i = 0 ; i < nintf ; i++) {
+ const char *value = NULL;
+ aug_get(aug, intf[i], &value);
+
+ element = xmlNewChild(array, NULL, BAD_CAST "element", NULL);
+ xmlNewProp(element, BAD_CAST "key", BAD_CAST name);
+
+ nmatches = aug_fmt_match(ncf, &matches, "%s/%s", intf[i], "*");
+ ERR_COND_BAIL(nmatches < 0, ncf, EOTHER);
+
+ for (int j = 0; j < nmatches; j++) {
+ node = xmlNewChild(element, NULL, BAD_CAST "node", NULL);
+ 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);
+ nmatches = 0;
+ }
+
+ free_matches(nintf, &intf);
+ return 0;
+
+ error:
+ free_matches(nintf, &intf);
+ return -1;
+}
+
+static xmlDocPtr aug_get_xml(struct netcf_if *nif) {
+ xmlDocPtr result = NULL;
+ xmlNodePtr root = NULL, tree = NULL, array = NULL, element = NULL, node = NULL;
+ char **matches = NULL;
+ int nmatches = 0;
+ struct netcf *ncf = nif->ncf;
+ char **slaves = NULL;
+ int nslaves = 0;
+ char **ports = NULL;
+ int nports = 0;
+
+ result = xmlNewDoc(BAD_CAST "1.0");
+ root = xmlNewNode(NULL, BAD_CAST "forest");
+ xmlDocSetRootElement(result, root);
+
+ tree = xmlNewChild(root, NULL, BAD_CAST "tree", NULL);
+ xmlNewProp(tree, BAD_CAST "path", BAD_CAST network_interfaces_path);
+
+ nmatches = aug_fmt_match(ncf, &matches, "%s/%s",
+ network_interfaces_path, "auto");
+ if (nmatches) {
+ free_matches(nmatches, &matches);
+ nmatches = 0;
+ array = xmlNewChild(tree, NULL, BAD_CAST "array", NULL);
+ xmlNewProp(array, BAD_CAST "label", BAD_CAST "auto");
+ element = xmlNewChild(array, NULL, BAD_CAST "element", NULL);
+
+ node = xmlNewChild(element, NULL, BAD_CAST "node", NULL);
+ xmlNewProp(node, BAD_CAST "value", BAD_CAST nif->name);
+ }
+
+
+ array = xmlNewChild(tree, NULL, BAD_CAST "array", NULL);
+ xmlNewProp(array, BAD_CAST "label", BAD_CAST "iface");
+
+ aug_get_xml_for_intf(ncf, array, nif->name);
+
+ if (is_bond(ncf, nif->name)) {
+ nslaves = bond_slaves(ncf, nif->name, &slaves);
+ ERR_BAIL(ncf);
+
+ for (int i = 0 ; i < nslaves ; i++) {
+ aug_get_xml_for_intf(ncf, array, slaves[i]);
+ ERR_BAIL(ncf);
+ }
+ free_matches(nslaves, &slaves);
+ } else if (is_bridge(ncf, nif->name)) {
+ nports = bridge_ports(ncf, nif->name, &ports);
+ ERR_BAIL(ncf);
+
+ for (int i = 0 ; i < nports ; i++) {
+ aug_get_xml_for_intf(ncf, array, ports[i]);
+ ERR_BAIL(ncf);
+
+ nslaves = bond_slaves(ncf, ports[i], &slaves);
+ ERR_BAIL(ncf);
+
+ for (int j = 0 ; j < nslaves ; j++) {
+ aug_get_xml_for_intf(ncf, array, slaves[j]);
+ ERR_BAIL(ncf);
+ }
+ free_matches(nslaves, &slaves);
+ }
+ free_matches(nports, &ports);
+ }
+
+ ERR_BAIL(ncf);
+
+ return result;
+
+ error:
+ free_matches(nmatches, &matches);
+ free_matches(nports, &ports);
+ free_matches(nslaves, &slaves);
+ 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, *key = NULL;
+ char *arraylabel = NULL;
+ char **matches = NULL;
+ int nmatches = 0;
+ int result = -1;
+ int r;
+ int n;
+
+ 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");
+
+ list_for_each(array, tree->children) {
+ ERR_THROW(! xmlStrEqual(array->name, BAD_CAST "array"), ncf,
+ EINTERNAL, "expected node labeled 'array', not '%s'",
+ array->name);
+
+ arraylabel = xml_prop(array, "label");
+
+ nmatches = aug_fmt_match(ncf, &matches, "%s/%s[last()]",
+ network_interfaces_path,
+ arraylabel);
+ ERR_COND_BAIL(nmatches < 0, ncf, EOTHER);
+ if (nmatches) {
+ char *start = strrchr(matches[nmatches-1], '[');
+ if (!start)
+ n = 1;
+ else
+ n = strtol(start+1, NULL, 10);
+ } else {
+ n = 0;
+ }
+ free_matches(nmatches, &matches);
+
+ /* Iterate over all array elements, inserting the new data */
+ list_for_each(element, array->children) {
+ ERR_THROW(! xmlStrEqual(element->name, BAD_CAST "element"), ncf,
+ EINTERNAL, "expected node labeled 'element', not '%s'",
+ element->name);
+
+ key = xml_prop(element, "key");
+
+ if (key) {
+ r = aug_fmt_set(ncf, key, "%s/%s[%d]",
+ network_interfaces_path,
+ arraylabel, ++n);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+ } else {
+ n++;
+ }
+
+ list_for_each(node, element->children) {
+ label = xml_prop(node, "label");
+ value = xml_prop(node, "value");
+
+ r = aug_fmt_set(ncf, value, "%s/%s[%d]/%s",
+ network_interfaces_path,
+ arraylabel, n, label ? label : "1");
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+ xmlFree(label);
+ xmlFree(value);
+ label = value = NULL;
+ }
+ }
+ }
+ xmlFree(path);
+ path = NULL;
+ }
+ result = 0;
+ error:
+ xmlFree(arraylabel);
+ xmlFree(label);
+ xmlFree(value);
+ xmlFree(path);
+ FREE(lpath);
+ free_matches(nmatches, &matches);
+ return result;
+}
+
+/* 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(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;
+}
+
+
+static void rm_interface(struct netcf *ncf, const char *name)
+{
+ int r;
+ char *path = NULL;
+
+ r = aug_fmt_rm(ncf, "%s/auto[./1 = '%s']",
+ network_interfaces_path, name);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ r = aug_fmt_rm(ncf, "%s/iface[. = '%s']",
+ network_interfaces_path, name);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ error:
+ FREE(path);
+}
+
+
+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_ports(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) {
+ struct netcf_if *result = NULL;
+ xmlDocPtr ncf_xml = NULL, aug_xml = NULL;
+ char *name = 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)
+{
+ char *path = NULL, *ifcfg = NULL;
+ const char **names = NULL;
+ int nmatches = 0;
+ char **matches = NULL;
+ int r;
+ int result = -1;
+
+ MEMZERO(ifaces, maxifaces);
+
+ 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_config(ncf, matches[i]))
+ names[cnt++] = matches[i];
+ }
+ 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;
+ int result = -1;
+
+ run1(ncf, ifup, nif->name);
+ ERR_BAIL(ncf);
+ result = 0;
+ error:
+ return result;
+}
+
+int drv_if_down(struct netcf_if *nif) {
+ static const char *const ifdown = "ifdown";
+ struct netcf *ncf = nif->ncf;
+ int result = -1;
+
+ run1(ncf, ifdown, nif->name);
+ ERR_BAIL(ncf);
+ result = 0;
+ error:
+ return result;
+}
+
+/* Functions to take a snapshot of network config (change_begin), and
+ * later either revert to that config (change_rollback), or make the
+ * new config permanent (change_commit).
+ */
+int
+drv_change_begin(struct netcf *ncf, unsigned int flags)
+{
+ int result = -1;
+
+ ERR_THROW(flags != 0, ncf, EOTHER, "unsupported flags value %d", flags);
+ run1(ncf, NETCF_TRANSACTION, "change-begin");
+ ERR_BAIL(ncf);
+ result = 0;
+error:
+ return result;
+}
+
+int
+drv_change_rollback(struct netcf *ncf, unsigned int flags)
+{
+ int result = -1;
+
+ ERR_THROW(flags != 0, ncf, EOTHER, "unsupported flags value %d", flags);
+ run1(ncf, NETCF_TRANSACTION, "change-rollback");
+ ERR_BAIL(ncf);
+ result = 0;
+error:
+ return result;
+}
+
+int
+drv_change_commit(struct netcf *ncf, unsigned int flags)
+{
+ int result = -1;
+
+ ERR_THROW(flags != 0, ncf, EOTHER, "unsupported flags value %d", flags);
+ run1(ncf, NETCF_TRANSACTION, "change-commit");
+ ERR_BAIL(ncf);
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * Test interface
+ */
+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 */
+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);
+}
+
+/*
+ * 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_initscripts.c b/src/drv_initscripts.c
index 7b2930d..3d3e88b 100644
--- a/src/drv_initscripts.c
+++ b/src/drv_initscripts.c
@@ -576,6 +576,7 @@ static int aug_put_xml(struct netcf *ncf, xmlDocPtr xml) {
}
xmlFree(path);
path = NULL;
+
}
result = 0;
error:
diff --git a/src/dutil.c b/src/dutil.c
index f13f4fd..abe604a 100644
--- a/src/dutil.c
+++ b/src/dutil.c
@@ -223,7 +223,6 @@ char *apply_stylesheet_to_string(struct netcf *ncf, \
xsltStylesheetPtr style,
doc_xfm = apply_stylesheet(ncf, style, doc);
ERR_BAIL(ncf);
-
r = xsltSaveResultToString((xmlChar **) &result, &result_len,
doc_xfm, style);
ERR_NOMEM(r < 0, ncf);
diff --git a/src/dutil_linux.c b/src/dutil_linux.c
index 75492ba..3d46da2 100644
--- a/src/dutil_linux.c
+++ b/src/dutil_linux.c
@@ -421,6 +421,63 @@ int defnode(struct netcf *ncf, const char *name, const char \
*value, return (r < 0) ? -1 : created;
}
+int aug_fmt_set(struct netcf *ncf, const char *value, 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_set(aug, path, value);
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ free(path);
+ return r;
+ error:
+ free(path);
+ return -1;
+}
+
+int aug_fmt_rm(struct netcf *ncf, 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_rm(aug, path);
+
+ ERR_COND_BAIL(r < 0, ncf, EOTHER);
+
+ free(path);
+ return r;
+ error:
+ free(path);
+ return -1;
+}
+
int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...) {
struct augeas *aug = NULL;
char *path = NULL;
diff --git a/src/dutil_linux.h b/src/dutil_linux.h
index a774bb9..d9f3fe9 100644
--- a/src/dutil_linux.h
+++ b/src/dutil_linux.h
@@ -79,6 +79,12 @@ int defnode(struct netcf *ncf, const char *name, const char \
*value, ATTRIBUTE_FORMAT(printf, 3, 4)
int aug_fmt_match(struct netcf *ncf, char ***matches, const char *fmt, ...);
+ATTRIBUTE_FORMAT(printf, 3, 4)
+int aug_fmt_set(struct netcf *ncf, const char *value, const char *fmt, ...);
+
+ATTRIBUTE_FORMAT(printf, 2, 3)
+int aug_fmt_rm(struct netcf *ncf, const char *fmt, ...);
+
/* Free matches from aug_match (or aug_submatch) */
void free_matches(int nint, char ***intf);
--
1.7.6
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic