summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--settings.c174
-rw-r--r--settings.h52
3 files changed, 227 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index ef194c7..49a2f08 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ LIBMINOR = 0
LIBNAME = libdnsldap.so.$(LIBMAJOR).$(LIBMINOR).0
LIBSONAME = libdnsldap.so.$(LIBMAJOR)
-OBJS = ldap_driver.o semaphore.o log.o str.o
+OBJS = ldap_driver.o semaphore.o log.o settings.o str.o
CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -fPIC $(CFLAGS)
diff --git a/settings.c b/settings.c
new file mode 100644
index 0000000..6f1aa45
--- /dev/null
+++ b/settings.c
@@ -0,0 +1,174 @@
+#include <isc/util.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "settings.h"
+#include "str.h"
+#include "util.h"
+
+/* Forward declarations. */
+static int args_are_equal(const char *setting_argument,
+ const char *argv_argument);
+static isc_result_t set_value(isc_mem_t *mctx, setting_t *setting, void *target,
+ const char *arg);
+static isc_result_t set_default_value(isc_mem_t *mctx, setting_t *setting,
+ void *target);
+static const char * get_value_str(const char *arg);
+
+isc_result_t
+set_settings(isc_mem_t *mctx, void *target, setting_t settings[],
+ const char * const* argv)
+{
+ isc_result_t result;
+ int i, j;
+ const char *value_ptr;
+ void *target_member;
+
+ for (i = 0; argv[i] != NULL; i++) {
+ for (j = 0; settings[j].name != NULL; j++) {
+ if (!args_are_equal(settings[j].name, argv[i]))
+ continue;
+
+ target_member = (char *)target + settings[j].offset;
+ value_ptr = get_value_str(argv[i]);
+ CHECK(set_value(mctx, &settings[j], target, value_ptr));
+ }
+ }
+
+ /* When all is done, check that all the required settings are set. */
+ for (j = 0; settings[j].name != NULL; j++) {
+ if (settings[j].set != 0)
+ continue;
+ if (settings[j].type == ST_NO_DEFAULT) {
+ log_error("argument %s must be set");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ target_member = (char *)target + settings[j].offset;
+ CHECK(set_default_value(mctx, &settings[j], target_member));
+ }
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ /* TODO: Free memory in case of error. */
+ return result;
+}
+
+/*
+ * Return 1 if the argument names are equal. The argv_argument also needs to
+ * contain an additional space at the end.
+ */
+static int
+args_are_equal(const char *setting_argument, const char *argv_argument)
+{
+ if (setting_argument == argv_argument)
+ return 1;
+ if (setting_argument == NULL || argv_argument == NULL)
+ return 0;
+
+ while (1) {
+ if (*argv_argument == '\0')
+ return 0;
+ if (*setting_argument == '\0')
+ break;
+ if (*setting_argument != *argv_argument)
+ return 0;
+ setting_argument++;
+ argv_argument++;
+ }
+
+ /* Now make sure we also found a space at the end of argv_argument. */
+ if (*argv_argument != ' ')
+ return 0;
+
+ return 1;
+}
+
+static isc_result_t
+set_value(isc_mem_t *mctx, setting_t *setting, void *target, const char *arg)
+{
+ isc_result_t result;
+ int numeric_value;
+ const char *value;
+ ld_string_t **ld_string_ptr;
+
+ REQUIRE(target != NULL);
+
+ value = get_value_str(arg);
+
+ if (setting->type == ST_LD_STRING) {
+ ld_string_ptr = (ld_string_t **)target;
+
+ if (setting->set)
+ str_destroy(ld_string_ptr);
+
+ CHECK(str_new(mctx, ld_string_ptr));
+ CHECK(str_init_char(*ld_string_ptr, value));
+ } else if (setting->type == ST_SIGNED_INTEGER ||
+ setting->type == ST_UNSIGNED_INTEGER) {
+ if (*value == '\0') {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ /* TODO: better type checking. */
+ numeric_value = atoi(value);
+ if (setting->type == ST_SIGNED_INTEGER) {
+ (*(signed int *)target) = (signed int)numeric_value;
+ } else {
+ if (numeric_value < 0) {
+ log_error("argument %s must be an unsigned integer", setting->name);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ (*(unsigned int *)target) = (unsigned int)numeric_value;
+ }
+ } else {
+ fatal_error("unknown type in function set_value()");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ setting->set = 1;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+static isc_result_t
+set_default_value(isc_mem_t *mctx, setting_t *setting, void *target)
+{
+ switch (setting->type) {
+ case ST_LD_STRING:
+ return set_value(mctx, setting, target, setting->value_char);
+ break;
+ case ST_SIGNED_INTEGER:
+ (*(signed int *)target) = (signed int)setting->value_sint;
+ break;
+ case ST_UNSIGNED_INTEGER:
+ (*(unsigned int *)target) = (unsigned int)setting->value_uint;
+ break;
+ default:
+ fatal_error("unknown type in function set_default_value()");
+ return ISC_R_FAILURE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+static const char *
+get_value_str(const char *arg)
+{
+ while (*arg != '\0' && !isblank(*arg))
+ arg++;
+ while (*arg != '\0' && isblank(*arg))
+ arg++;
+
+ return arg;
+}
diff --git a/settings.h b/settings.h
new file mode 100644
index 0000000..9819cd3
--- /dev/null
+++ b/settings.h
@@ -0,0 +1,52 @@
+#ifndef _LD_SETTINGS_H_
+#define _LD_SETTINGS_H_
+
+typedef struct setting setting_t;
+
+typedef enum {
+ ST_LD_STRING,
+ ST_SIGNED_INTEGER,
+ ST_UNSIGNED_INTEGER,
+ ST_NO_DEFAULT,
+} setting_type_t;
+
+struct setting {
+ const char *name;
+ int set;
+ setting_type_t type;
+ union {
+ const char *value_char;
+ signed int value_sint;
+ unsigned int value_uint;
+ } default_value;
+ size_t offset;
+};
+
+/*
+ * These defines are used as initializers for setting_t, for example:
+ *
+ * const setting_t my_setting = {
+ * "name", default_string("this is the default"),
+ * offsetof(some_struct, some_member
+ * }
+ */
+#define default_string(value) 0, ST_LD_STRING, { .value_char = (value) }
+#define default_sint(value) 0, ST_SIGNED_INTEGER, { .value_sint = (value) }
+#define default_uint(value) 0, ST_UNSIGNED_INTEGER, { .value_uint = (value) }
+#define default_nothing() 0, ST_NO_DEFAULT, { .value_uint = 0 }
+
+/* This is used in the end of setting_t arrays. */
+#define end_of_settings { NULL, default_sint(0), 0 }
+
+#define value_char default_value.value_char
+#define value_sint default_value.value_sint
+#define value_uint default_value.value_uint
+
+/*
+ * Prototypes.
+ */
+isc_result_t
+set_settings(isc_mem_t *mctx, void *target, setting_t settings[],
+ const char * const* argv);
+
+#endif /* !_LD_SETTINGS_H_ */