I had a need to edit /etc/network/interfaces file on Ubuntu (from my program). At first I found some awk script on the web, which claimed to do the job, but when I tried, it wasn't addressing all the CRUD cases that I am interested in (also, I didn't want to have system( ) calls from my C code).
So, I searched for a better utility, and found Augeas - this is fantastic! you have to try it to believe! It has a neat command line utility (augtool) as well as an easy to use C API (+ bindings for many scripting languages, including my fav Lua too :-) ).
The following commands show how easy it is to add/rm an interface (XPATH expressions)
So, I searched for a better utility, and found Augeas - this is fantastic! you have to try it to believe! It has a neat command line utility (augtool) as well as an easy to use C API (+ bindings for many scripting languages, including my fav Lua too :-) ).
The following commands show how easy it is to add/rm an interface (XPATH expressions)
$ sudo augtool # Add an interface at the end (last) augtool> set /files/etc/network/interfaces/iface[last()+1] eth1 augtool> set /files/etc/network/interfaces/iface[last()]/family inet augtool> set /files/etc/network/interfaces/iface[last()]/method static augtool> set /files/etc/network/interfaces/iface[last()]/address 10.1.1.1 augtool> save Saved 1 file(s) augtool> $ sudo augtool # Edit the added interface (by name, not position) augtool> set /files/etc/network/interfaces/iface[. = 'eth1']/netmask 255.255.255.0 augtool> save Saved 1 file(s) augtool> set /files/etc/network/interfaces/iface[. = 'eth1']/mtu 1500 augtool> save Saved 1 file(s) augtool> $ sudo cat /etc/network/interfaces auto lo iface lo inet loopback iface eth1 inet dhcp address 10.1.1.1 netmask 255.255.255.0 mtu 1500 $ sudo augtool # Lets just delete eth1 now augtool> rm /files/etc/network/interfaces/iface[. = 'eth1'] rm : /files/etc/network/interfaces/iface[. = 'eth1'] 6 <-- 6="" augtool="" fields="" removed=""> save Saved 1 file(s) augtool>Now, the same/similar exercise programmatically:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Test CRUD of an iface in /etc/network/interfaces ( on Ubuntu) | |
// using the Augeas lib | |
// Aniruddha. A (aniruddha.a@gmail.com) | |
// Nov 1, 2014 | |
#include <stdio.h> | |
#include <assert.h> | |
#include <stdarg.h> | |
#include <stddef.h> | |
#include <limits.h> | |
#include <string.h> | |
#include <augeas.h> | |
// using the .so installed from libaugeas-dev : | |
// gcc -I /usr/include/libxml2/ augparse.c -laugeas | |
// | |
// to run, just install libaugeas0 ( minus dev files ) | |
#define LENS_PATH "/usr/share/augeas/lenses/dist/" | |
void if_create (augeas *aug, const char *ifname, int nfields, ...) | |
{ | |
char key[128] = {0}, val[128] = {0}; | |
char *p = NULL; | |
char *kv = NULL; | |
va_list ap; | |
int ret, i; | |
char iffield_path[256]= {0}; | |
assert(aug && ifname && (nfields > 0)); | |
ret = aug_set(aug, "/files/etc/network/interfaces/iface[last()+1]", ifname); | |
assert(ret == 0); | |
va_start(ap, nfields); | |
for (i = 0; i < nfields; i++) { | |
kv = va_arg(ap,char*); | |
p = strchr(kv, '='); | |
if (!p) continue; | |
strncpy(key, kv, (ptrdiff_t)(p - kv)); | |
key[p - kv] = '\0'; | |
strcpy(val, p + 1); | |
sprintf(iffield_path, "/files/etc/network/interfaces/iface[last()]/%s", | |
key); | |
ret = aug_set(aug, iffield_path, val); | |
assert(ret == 0); | |
} | |
va_end(ap); | |
} | |
void if_update (augeas *aug, const char *ifname, int nfields, ...) | |
{ | |
char key[128] = {0}, val[128] = {0}; | |
char *p = NULL; | |
char *kv = NULL; | |
va_list ap; | |
int ret, i; | |
char iffield_path[256]= {0}; | |
assert(aug && ifname && (nfields > 0)); | |
va_start(ap, nfields); | |
for (i = 0; i < nfields; i++) { | |
kv = va_arg(ap,char*); | |
p = strchr(kv, '='); | |
if (!p) continue; | |
strncpy(key, kv, (ptrdiff_t)(p - kv)); | |
key[p - kv] = '\0'; | |
strcpy(val, p + 1); | |
sprintf(iffield_path, "/files/etc/network/interfaces/iface[. = '%s']/%s", | |
ifname, key); | |
ret = aug_set(aug, iffield_path, val); | |
assert(ret == 0); | |
} | |
va_end(ap); | |
} | |
void if_rm (augeas *aug, const char *ifname) | |
{ | |
int ret; | |
char ifpath[256]= {0}; | |
assert(aug && ifname); | |
sprintf(ifpath, "/files/etc/network/interfaces/iface[. = '%s']", ifname); | |
ret = aug_rm(aug, ifpath); | |
assert(ret != -1); | |
} | |
// TEST | |
int main (int argc, char *argv[]) | |
{ | |
augeas *aug = NULL; | |
aug = aug_init("/", LENS_PATH, AUG_SAVE_BACKUP|AUG_NO_ERR_CLOSE); | |
if (!aug || (aug_error(aug) != AUG_NOERROR)) { | |
fprintf(stderr, "Init failed: %s\n", aug_error_message(aug)); | |
exit(1); | |
} | |
if_create(aug, "eth2", 5, | |
"family=inet", "method=static", | |
"address=1.1.1.1", "netmask=255.255.0.0", | |
"mtu=1500" ); | |
aug_save(aug); | |
system("cat /etc/network/interfaces"); | |
if_update(aug, "eth2", 1, "address=2.2.2.2"); | |
aug_save(aug); | |
system("cat /etc/network/interfaces"); | |
if_rm(aug, "eth2"); | |
aug_save(aug); | |
system("cat /etc/network/interfaces"); | |
aug_close(aug); | |
return 0; | |
} |