commit 9f9dc25f5ab60352e68544080c9144cf10c060f4
parent 876db202f8684ec30f015e8f83a190004a744a39
Author: finwo <finwo@pm.me>
Date: Sat, 5 Sep 2026 22:50:16 +0200
Config parser mostly wired
Diffstat:
17 files changed, 507 insertions(+), 231 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,11 +3,17 @@ TARGET:=linux-glibc-amd64
.PHONY: default
default: build/${TARGET}/pmlag
-build/${TARGET}/pmlag: build/${TARGET} $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h)
+FIND:=$(shell command -v gfind find)
+
+WATCH?=
+WATCH+=$(shell $(FIND) src -type f -name '*.c')
+WATCH+=$(shell $(FIND) src -type f -name '*.h')
+
+build/${TARGET}/pmlag: build/${TARGET} $(WATCH)
cd build/${TARGET} && dep install
$(MAKE) --directory build/${TARGET} TARGET=${TARGET}
-build/${TARGET}: $(wildcard src/*.c src/*.h src/*/*.c src/*/*.h)
+build/${TARGET}: $(WATCH)
mkdir -p build/${TARGET}
cp -rT manpage.1.md build/${TARGET}/manpage.1.md
cp -rT src/ build/${TARGET}/src
diff --git a/config/test.cnf b/config/test.cnf
@@ -0,0 +1 @@
+
diff --git a/example.conf b/example.conf
@@ -14,15 +14,18 @@ bond bon1
# Routed interface, L3 only
# Auto-announce routes
-routed rnic0
+router rnic0
+ interface eth1
+ interface eth2
address 192.0.0.1/32
- route auto
+ redistribute kernel
# Routed interface, L3 only
# Fixed routes
-routed rnic1
+router rnic1
address 192.0.0.1/32
address fe80::1/128
+ interface eth1
route 0.0.0.0/0
neighbour 9c:6b:00:60:05:98
ttl 3600
@@ -34,11 +37,11 @@ routed rnic1
# Routed interface, L3 only
# Mixed routes
-routed rnic1
+router rnic2
address 192.0.0.1/32
address fe80::1/128
# Propagate routes with metric +50
- route auto
+ redistribute kernel
ttl 300
metric 50
# Static routes
diff --git a/src/daemon/bond/config.c b/src/daemon/bond/config.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "util/config.h"
+
+#include "config.h"
+
+struct cnf_directive * cfg_parse_bond(FILE *fd, struct cnf_directive *dir, void *user) {
+ int i;
+
+ if (dir->argc != 1) {
+ fprintf(stderr, "`bond` directive accepts only 1 argument\n");
+ exit(1);
+ }
+
+ printf("Defining bond: %s\n", dir->argv[0]);
+
+ for(;;) {
+ if (dir) cnf_directive_free(dir);
+ dir = cnf_directive_read(fd);
+ if (!dir) break; // EOF = done
+
+ // Make following blocks identical in structure
+ if(0) {}
+
+ else if (!strcasecmp("interface", dir->name)) {
+ for( i = 0 ; i < dir->argc ; i++ ) {
+ printf(" add iface: %s\n", dir->argv[i]);
+ }
+ }
+
+ else if (!strcasecmp("address", dir->name)) {
+ for( i = 0 ; i < dir->argc ; i++ ) {
+ printf(" got addr: %s\n", dir->argv[i]);
+ }
+ }
+
+ else if (!strcasecmp("mac", dir->name)) {
+ if (dir->argc != 1) {
+ fprintf(stderr, "`mac` directive accepts only 1 argument\n");
+ exit(1);
+ }
+ printf(" set mac: %s\n", dir->argv[0]);
+ }
+
+ else {
+ // unknown directive
+ return dir;
+ }
+
+ }
+
+ return NULL;
+}
diff --git a/src/daemon/bond/config.h b/src/daemon/bond/config.h
@@ -0,0 +1,10 @@
+#ifndef __PMLAG_DAEMON_BOND_CONFIG_H__
+#define __PMLAG_DAEMON_BOND_CONFIG_H__
+
+#include <stdio.h>
+
+#include "finwo/cnfparse.h"
+
+struct cnf_directive * cfg_parse_bond(FILE *fd, struct cnf_directive *dir, void *user);
+
+#endif // __PMLAG_DAEMON_BOND_CONFIG_H__
diff --git a/src/daemon/bond/setup.c b/src/daemon/bond/setup.c
@@ -0,0 +1,8 @@
+#include "util/config.h"
+
+#include "setup.h"
+#include "config.h"
+
+void daemon_bond_setup() {
+ cfg_register_directive("bond", cfg_parse_bond);
+}
diff --git a/src/daemon/bond/setup.h b/src/daemon/bond/setup.h
@@ -0,0 +1,6 @@
+#ifndef __PMLAG_DAEMON_BOND_SETUP_H__
+#define __PMLAG_DAEMON_BOND_SETUP_H__
+
+void daemon_bond_setup();
+
+#endif // __PMLAG_DAEMON_BOND_SETUP_H__
diff --git a/src/daemon/router/config.c b/src/daemon/router/config.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "util/config.h"
+
+#include "config.h"
+
+struct cnf_directive * cfg_parse_redistribute(FILE *fd, struct cnf_directive *dir, void *user) {
+ int i;
+
+ if (dir->argc < 1) {
+ fprintf(stderr, "`redistribute` directive needs at least 1 argument\n");
+ exit(1);
+ }
+
+ // TODO: start building redistribute entry
+
+
+ for(;;) {
+ if (dir) cnf_directive_free(dir);
+ dir = cnf_directive_read(fd);
+ if (!dir) break; // EOF = done
+ // Make following blocks identical in structure
+ if(0) {}
+
+ else if (!strcasecmp("ttl", dir->name)) {
+ // TODO: route->ttl = atoi(argv[0])
+ }
+
+ else if (!strcasecmp("metric", dir->name)) {
+ // TODO: route->metric = atoi(argv[0])
+ }
+
+ else {
+ // unknown directive
+ break;
+ }
+ }
+
+ // Cleanup
+ // TODO: store redist in current router def
+
+ // Return the unparsed dir if set
+ return dir;
+}
+
+struct cnf_directive * cfg_parse_route(FILE *fd, struct cnf_directive *dir, void *user) {
+ int i;
+
+ if (dir->argc < 1) {
+ fprintf(stderr, "`route` directive needs at least 1\n");
+ exit(1);
+ }
+
+ // TODO: start building route
+
+ for(;;) {
+ if (dir) cnf_directive_free(dir);
+ dir = cnf_directive_read(fd);
+ if (!dir) break; // EOF = done
+cfg_parse_route_reparse:
+ // Make following blocks identical in structure
+ if(0) {}
+
+ else if (!strcasecmp("ttl", dir->name)) {
+ // TODO: route->ttl = atoi(argv[0])
+ }
+
+ else if (!strcasecmp("metric", dir->name)) {
+ // TODO: route->metric = atoi(argv[0])
+ }
+
+ else if (!strcasecmp("neighbour", dir->name)) {
+ // TODO: route->neighbour[] = parse_mac(argv[0])
+ }
+
+ else {
+ // unknown directive
+ break;
+ }
+ }
+
+ // Cleanup
+ // TODO: store route in current router def
+
+ // Return the unparsed dir if set
+ return dir;
+}
+
+struct cnf_directive * cfg_parse_router(FILE *fd, struct cnf_directive *dir, void *user) {
+ int i;
+
+ if (dir->argc != 1) {
+ fprintf(stderr, "`router` directive accepts only 1 argument\n");
+ exit(1);
+ }
+
+ printf("Defining router: %s\n", dir->argv[0]);
+
+ for(;;) {
+ if (dir) cnf_directive_free(dir);
+ dir = cnf_directive_read(fd);
+ if (!dir) break; // EOF = done
+
+cfg_parse_router_reparse:
+
+ // Make following blocks identical in structure
+ if(0) {}
+
+ else if (!strcasecmp("interface", dir->name)) {
+ for( i = 0 ; i < dir->argc ; i++ ) {
+ printf(" add iface: %s\n", dir->argv[i]);
+ }
+ }
+
+ else if (!strcasecmp("address", dir->name)) {
+ for( i = 0 ; i < dir->argc ; i++ ) {
+ printf(" got addr: %s\n", dir->argv[i]);
+ }
+ }
+
+ // else if (!strcasecmp("mac", dir->name)) {
+ // if (dir->argc != 1) {
+ // fprintf(stderr, "`mac` directive accepts only 1 argument\n");
+ // exit(1);
+ // }
+ // printf(" set mac: %s\n", dir->argv[0]);
+ // }
+
+ else if (!strcasecmp("route", dir->name)) {
+ dir = cfg_parse_route(fd, dir, user);
+ if (!dir) continue;
+ goto cfg_parse_router_reparse;
+ }
+
+ else if (!strcasecmp("redistribute", dir->name)) {
+ dir = cfg_parse_redistribute(fd, dir, user);
+ if (!dir) continue;
+ goto cfg_parse_router_reparse;
+ }
+
+
+ else {
+ // unknown directive
+ break;
+ }
+
+ }
+
+ // TODO: store router in actual config
+
+ return dir;
+}
diff --git a/src/daemon/router/config.h b/src/daemon/router/config.h
@@ -0,0 +1,10 @@
+#ifndef __PMLAG_DAEMON_ROUTER_CONFIG_H__
+#define __PMLAG_DAEMON_ROUTER_CONFIG_H__
+
+#include <stdio.h>
+
+#include "finwo/cnfparse.h"
+
+struct cnf_directive * cfg_parse_router(FILE *fd, struct cnf_directive *dir, void *user);
+
+#endif // __PMLAG_DAEMON_ROUTER_CONFIG_H__
diff --git a/src/daemon/router/setup.c b/src/daemon/router/setup.c
@@ -0,0 +1,8 @@
+#include "util/config.h"
+
+#include "setup.h"
+#include "config.h"
+
+void daemon_router_setup() {
+ cfg_register_directive("router", cfg_parse_router);
+}
diff --git a/src/daemon/router/setup.h b/src/daemon/router/setup.h
@@ -0,0 +1,6 @@
+#ifndef __PMLAG_DAEMON_ROUTER_SETUP_H__
+#define __PMLAG_DAEMON_ROUTER_SETUP_H__
+
+void daemon_router_setup();
+
+#endif // __PMLAG_DAEMON_ROUTER_SETUP_H__
diff --git a/src/main.c b/src/main.c
@@ -11,7 +11,10 @@ extern "C" {
#include "cofyc/argparse.h"
-#include "util/config-root.h"
+#include "util/config.h"
+
+#include "daemon/bond/setup.h"
+#include "daemon/router/setup.h"
static const char *const usage[] = {
__NAME " [options]",
@@ -37,6 +40,10 @@ int main(int argc, const char **argv) {
OPT_END(),
};
+ // Initialize components
+ daemon_bond_setup();
+ daemon_router_setup();
+
// Parse command line arguments
struct argparse argparse;
argparse_init(&argparse, options, usage, 0);
diff --git a/src/util/config-root.c b/src/util/config-root.c
@@ -1,208 +0,0 @@
-#include <glob.h>
-#include <libgen.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "config-root.h"
-
-#define CFG_MAX_REG 256
-
-static int handler_count = 0;
-static const char *handler_name[CFG_MAX_REG];
-static cfg_directive_fn handler_fn[CFG_MAX_REG];
-
-void cfg_register_directive(const char *name, cfg_directive_fn fn) {
- handler_name[handler_count] = name;
- handler_fn [handler_count] = fn;
- handler_count++;
-}
-
-int cfg_parse(const char *wd, FILE *fd, void *user) {
- struct cnf_directive *dir = NULL;
- glob_t globbuf;
- int globflags;
- int i;
-
- char *strtmp = NULL;
- FILE *nfd;
-
- for(;;) {
- if (dir) cnf_directive_free(dir);
- dir = cnf_directive_read(fd);
- if (!dir) break; // EOF = done
-
-cfg_parse_reparse:
-
- // Core handlers
- if (!strcasecmp("include", dir->name)) {
- globflags = GLOB_ERR;
- for ( i = 0 ; i < dir->argc ; i++ ) {
- strtmp = malloc(snprintf(NULL, 0, "%s/%s", wd, dir->argv[i])+1);
- sprintf(strtmp, "%s/%s", wd, dir->argv[i]);
- glob(strtmp, globflags, NULL, &globbuf);
- free(strtmp);
- globflags = GLOB_ERR | GLOB_APPEND;
- }
- for( i = 0 ; i < globbuf.gl_pathc ; i++ ) {
- strtmp = calloc(strlen(globbuf.gl_pathv[i])+1, 1);
- strcpy(strtmp, globbuf.gl_pathv[i]);
- dirname(strtmp);
- nfd = fopen(globbuf.gl_pathv[i], "r");
- if (!nfd) {
- perror("Could not open config file for reading");
- return CFG_RET_ERROR;
- }
- if (cfg_parse(strtmp, nfd, user) < 0) {
- fprintf(stderr, "Error during reading configuration from %s\n", globbuf.gl_pathv[i]);
- return CFG_RET_ERROR;
- }
- fclose(nfd);
- free(strtmp);
- }
- globfree(&globbuf);
- continue;
- }
-
- // Dynamic handlers
- for(i = 0 ; i < handler_count ; i++) {
- if (!strcasecmp(handler_name[i], dir->name)) {
- dir = handler_fn[i](fd, dir, user);
- if (dir) goto cfg_parse_reparse;
- goto cfg_parse_cleanup;
- }
- }
-
- // Here = not found
- fprintf(stderr, "Unknown directive: %s\n", dir->name);
- return CFG_RET_ERROR;
- }
-
- // Cleanup here
-cfg_parse_cleanup:
- if (dir) cnf_directive_free(dir);
-
- return CFG_RET_OK;
-}
-
-// #include <stdio.h>
-// #include <stdlib.h>
-// #include <string.h>
-
-// #include "config.h"
-
-// #include "benhoyt/inih.h"
-// #include "finwo/mindex.h"
-
-// int mindex_bond_cmp(const void *a, const void *b, void *udata) {
-// (void)udata;
-// const struct pmlag_configuration_bond *_a = a;
-// const struct pmlag_configuration_bond *_b = b;
-// return strcmp(_a->name, _b->name);
-// }
-
-// void mindex_bond_purge(void *item, void *udata) {
-// struct pmlag_configuration_bond *bond = item;
-// (void)udata;
-// if (!item) return;
-// if (bond->name) free(bond->name);
-// }
-
-// #ifndef MIN
-// #define MIN(a,b) ((a)<(b)?(a):(b))
-// #endif
-
-// static int config_load_handler(
-// void *user,
-// const char *section,
-// const char *name,
-// const char *value
-// ) {
-// // struct pmlag_bond *bond = NULL;
-// // struct pmlag_iface *iface = NULL;
-// // int bond_index = 0;
-// // int iface_index = 0;
-// struct pmlag_configuration* config = (struct pmlag_configuration *) user;
-
-// // Detect invalid entries
-// int i, l;
-// for( i = 0, l = strlen(name) ; i < l ; i++ ) {
-// if (name[i] == 0x5F ) continue; // '_'
-// if (name[i] >= 0x30 && name[i] <= 0x39) continue; // 0-9
-// if (name[i] >= 0x41 && name[i] <= 0x5A) continue; // A-Z
-// if (name[i] >= 0x61 && name[i] <= 0x7A) continue; // a-z
-// return 0; // Reject
-// }
-
-// // Strip # comments from values
-// char *found_comment = strstr(value, "#");
-// if (found_comment) {
-// for(; found_comment > (value + 1) ;) {
-// if ( *(found_comment) >= 0x21 && *(found_comment-1) <= 0x7E ) break;
-// found_comment--;
-// }
-// *found_comment = 0x00;
-// }
-
-// // Find or create bond
-// struct pmlag_configuration_bond *config_bond = mindex_get(config->bonds, (&(struct pmlag_configuration_bond){ .name = (char *)section }));
-// if (!config_bond) {
-// config_bond = calloc(1, sizeof(struct pmlag_configuration_bond));
-// config_bond->name = strdup(section);
-// mindex_set(config->bonds, config_bond);
-// }
-
-// // // Find the bond
-// // struct pmlag_configuration_bond *config_bond = NULL;
-// // for( i = 0 ; i < config->bond_len ; i++ ) {
-// // if (!strcmp(config->bonds[i].name, section)) {
-// // config_bond = &(config->bonds[i]);
-// // break;
-// // }
-// // }
-// // printf("%s:%d\n", __FILE__, __LINE__);
-// // // Build new bond if missing
-// // if (!config_bond) {
-// // // Expand array size if needed
-// // if ((config->bond_len+1) > config->bond_cap) {
-// // config->bond_cap = MIN(1, config->bond_cap*2);
-// // config->bonds = realloc(config->bonds, config->bond_cap * sizeof(struct pmlag_configuration_bond));
-// // }
-// // // Append new entry to list
-// // memset(&(config->bonds[config->bond_len]), 0, sizeof(struct pmlag_configuration_bond));
-// // config_bond = &(config->bonds[config->bond_len]);
-// // config->bond_len++;
-// // }
-
-// // Detect # comments and remove them
-
-// return 1;
-// }
-
-// struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config) {
-// // Load config, entry-by-entry
-// if (!config) {
-// config = calloc(1, sizeof(struct pmlag_configuration));
-// }
-// if (!config->bonds) {
-// config->bonds = mindex_init(mindex_bond_cmp, mindex_bond_purge, NULL);
-// }
-
-// if (ini_parse(filepath, config_load_handler, config) < 0) {
-// fprintf(stderr, "Can not load %s\n", filepath);
-// return NULL;
-// }
-
-// printf("bond_len: %ld\n", mindex_length(config->bonds));
-// printf("\n");
-// printf("bonds:\n");
-// int l = mindex_length(config->bonds);
-// for(int i = 0 ; i < l ; i++) {
-// struct pmlag_configuration_bond *bond = mindex_nth(config->bonds, i);
-// printf(" - %s\n", bond->name);
-// }
-// printf("\n");
-
-// return config;
-// }
diff --git a/src/util/config-root.h b/src/util/config-root.h
@@ -1,15 +0,0 @@
-#ifndef __PMLAG_UTIL_CONFIG_ROOT_H__
-#define __PMLAG_UTIL_CONFIG_ROOT_H__
-
-#include <stdio.h>
-
-#include "finwo/cnfparse.h"
-
-#define CFG_RET_ERROR -1
-#define CFG_RET_OK 0
-
-typedef struct cnf_directive * (*cfg_directive_fn)(FILE *fd, struct cnf_directive *dir, void *user);
-void cfg_register_directive(const char *name, cfg_directive_fn fn);
-int cfg_parse(const char *wd, FILE *fd, void *user);
-
-#endif // __PMLAG_UTIL_CONFIG_ROOT_H__
diff --git a/src/util/config.c b/src/util/config.c
@@ -0,0 +1,209 @@
+#include <glob.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "config.h"
+
+#define CFG_MAX_REG 256
+
+static int handler_count = 0;
+static const char *handler_name[CFG_MAX_REG];
+static cfg_directive_fn handler_fn[CFG_MAX_REG];
+
+void cfg_register_directive(const char *name, cfg_directive_fn fn) {
+ handler_name[handler_count] = name;
+ handler_fn [handler_count] = fn;
+ handler_count++;
+}
+
+int cfg_parse(const char *wd, FILE *fd, void *user) {
+ struct cnf_directive *dir = NULL;
+ glob_t globbuf;
+ int globflags;
+ int i;
+
+ char *strtmp = NULL;
+ FILE *nfd;
+
+ for(;;) {
+ if (dir) cnf_directive_free(dir);
+ dir = cnf_directive_read(fd);
+ if (!dir) break; // EOF = done
+
+cfg_parse_reparse:
+
+ // Core handlers
+ if (!strcasecmp("include", dir->name)) {
+ globflags = GLOB_ERR;
+ for ( i = 0 ; i < dir->argc ; i++ ) {
+ strtmp = malloc(snprintf(NULL, 0, "%s/%s", wd, dir->argv[i])+1);
+ sprintf(strtmp, "%s/%s", wd, dir->argv[i]);
+ glob(strtmp, globflags, NULL, &globbuf);
+ free(strtmp);
+ globflags = GLOB_ERR | GLOB_APPEND;
+ }
+ for( i = 0 ; i < globbuf.gl_pathc ; i++ ) {
+ strtmp = calloc(strlen(globbuf.gl_pathv[i])+1, 1);
+ strcpy(strtmp, globbuf.gl_pathv[i]);
+ dirname(strtmp);
+ nfd = fopen(globbuf.gl_pathv[i], "r");
+ if (!nfd) {
+ perror("Could not open config file for reading");
+ return CFG_RET_ERROR;
+ }
+ if (cfg_parse(strtmp, nfd, user) < 0) {
+ fprintf(stderr, "Error during reading configuration from %s\n", globbuf.gl_pathv[i]);
+ return CFG_RET_ERROR;
+ }
+ fclose(nfd);
+ free(strtmp);
+ }
+ globfree(&globbuf);
+ continue;
+ }
+
+ // Dynamic handlers
+ for(i = 0 ; i < handler_count ; i++) {
+ if (!strcasecmp(handler_name[i], dir->name)) {
+ dir = handler_fn[i](fd, dir, user);
+ if (dir) goto cfg_parse_reparse;
+ break;
+ }
+ }
+
+ if (i == handler_count) {
+ // Here = not found
+ fprintf(stderr, "Unknown directive: %s\n", dir->name);
+ return CFG_RET_ERROR;
+ }
+ }
+
+ // Cleanup here
+ if (dir) cnf_directive_free(dir);
+
+ return CFG_RET_OK;
+}
+
+// #include <stdio.h>
+// #include <stdlib.h>
+// #include <string.h>
+
+// #include "config.h"
+
+// #include "benhoyt/inih.h"
+// #include "finwo/mindex.h"
+
+// int mindex_bond_cmp(const void *a, const void *b, void *udata) {
+// (void)udata;
+// const struct pmlag_configuration_bond *_a = a;
+// const struct pmlag_configuration_bond *_b = b;
+// return strcmp(_a->name, _b->name);
+// }
+
+// void mindex_bond_purge(void *item, void *udata) {
+// struct pmlag_configuration_bond *bond = item;
+// (void)udata;
+// if (!item) return;
+// if (bond->name) free(bond->name);
+// }
+
+// #ifndef MIN
+// #define MIN(a,b) ((a)<(b)?(a):(b))
+// #endif
+
+// static int config_load_handler(
+// void *user,
+// const char *section,
+// const char *name,
+// const char *value
+// ) {
+// // struct pmlag_bond *bond = NULL;
+// // struct pmlag_iface *iface = NULL;
+// // int bond_index = 0;
+// // int iface_index = 0;
+// struct pmlag_configuration* config = (struct pmlag_configuration *) user;
+
+// // Detect invalid entries
+// int i, l;
+// for( i = 0, l = strlen(name) ; i < l ; i++ ) {
+// if (name[i] == 0x5F ) continue; // '_'
+// if (name[i] >= 0x30 && name[i] <= 0x39) continue; // 0-9
+// if (name[i] >= 0x41 && name[i] <= 0x5A) continue; // A-Z
+// if (name[i] >= 0x61 && name[i] <= 0x7A) continue; // a-z
+// return 0; // Reject
+// }
+
+// // Strip # comments from values
+// char *found_comment = strstr(value, "#");
+// if (found_comment) {
+// for(; found_comment > (value + 1) ;) {
+// if ( *(found_comment) >= 0x21 && *(found_comment-1) <= 0x7E ) break;
+// found_comment--;
+// }
+// *found_comment = 0x00;
+// }
+
+// // Find or create bond
+// struct pmlag_configuration_bond *config_bond = mindex_get(config->bonds, (&(struct pmlag_configuration_bond){ .name = (char *)section }));
+// if (!config_bond) {
+// config_bond = calloc(1, sizeof(struct pmlag_configuration_bond));
+// config_bond->name = strdup(section);
+// mindex_set(config->bonds, config_bond);
+// }
+
+// // // Find the bond
+// // struct pmlag_configuration_bond *config_bond = NULL;
+// // for( i = 0 ; i < config->bond_len ; i++ ) {
+// // if (!strcmp(config->bonds[i].name, section)) {
+// // config_bond = &(config->bonds[i]);
+// // break;
+// // }
+// // }
+// // printf("%s:%d\n", __FILE__, __LINE__);
+// // // Build new bond if missing
+// // if (!config_bond) {
+// // // Expand array size if needed
+// // if ((config->bond_len+1) > config->bond_cap) {
+// // config->bond_cap = MIN(1, config->bond_cap*2);
+// // config->bonds = realloc(config->bonds, config->bond_cap * sizeof(struct pmlag_configuration_bond));
+// // }
+// // // Append new entry to list
+// // memset(&(config->bonds[config->bond_len]), 0, sizeof(struct pmlag_configuration_bond));
+// // config_bond = &(config->bonds[config->bond_len]);
+// // config->bond_len++;
+// // }
+
+// // Detect # comments and remove them
+
+// return 1;
+// }
+
+// struct pmlag_configuration * config_load(char * filepath, struct pmlag_configuration *config) {
+// // Load config, entry-by-entry
+// if (!config) {
+// config = calloc(1, sizeof(struct pmlag_configuration));
+// }
+// if (!config->bonds) {
+// config->bonds = mindex_init(mindex_bond_cmp, mindex_bond_purge, NULL);
+// }
+
+// if (ini_parse(filepath, config_load_handler, config) < 0) {
+// fprintf(stderr, "Can not load %s\n", filepath);
+// return NULL;
+// }
+
+// printf("bond_len: %ld\n", mindex_length(config->bonds));
+// printf("\n");
+// printf("bonds:\n");
+// int l = mindex_length(config->bonds);
+// for(int i = 0 ; i < l ; i++) {
+// struct pmlag_configuration_bond *bond = mindex_nth(config->bonds, i);
+// printf(" - %s\n", bond->name);
+// }
+// printf("\n");
+
+// return config;
+// }
diff --git a/src/util/config.h b/src/util/config.h
@@ -0,0 +1,15 @@
+#ifndef __PMLAG_UTIL_CONFIG_H__
+#define __PMLAG_UTIL_CONFIG_H__
+
+#include <stdio.h>
+
+#include "finwo/cnfparse.h"
+
+#define CFG_RET_ERROR -1
+#define CFG_RET_OK 0
+
+typedef struct cnf_directive * (*cfg_directive_fn)(FILE *fd, struct cnf_directive *dir, void *user);
+void cfg_register_directive(const char *name, cfg_directive_fn fn);
+int cfg_parse(const char *wd, FILE *fd, void *user);
+
+#endif // __PMLAG_UTIL_CONFIG_H__
diff --git a/target/common/Makefile b/target/common/Makefile
@@ -6,6 +6,7 @@ TARGET?=
SRC:=
SRC+=$(wildcard src/*.c)
SRC+=$(wildcard src/*/*.c)
+SRC+=$(wildcard src/*/*/*.c)
CFLAGS?=-Wall -s -O2