nbd-proxy: add configuration facility
Add a little configuration facility. We'll later add a method to
integrate this with UI-side configuration, so it's implmented as json.
We add a (temporarily required) argument to specify which configuration
to use.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/nbd-proxy.c b/nbd-proxy.c
index 13a1366..cb3c33c 100644
--- a/nbd-proxy.c
+++ b/nbd-proxy.c
@@ -34,22 +34,34 @@
#include <sys/un.h>
#include <sys/wait.h>
+#include <json.h>
+
#include "config.h"
+struct config {
+ char *name;
+ char *nbd_device;
+ struct json_object *metadata;
+};
+
struct ctx {
int sock;
int sock_client;
int signal_pipe[2];
char *sock_path;
+ char *dev_path;
pid_t nbd_client_pid;
+ int nbd_timeout;
uint8_t *buf;
size_t bufsize;
+ struct config *configs;
+ int n_configs;
};
+static const char *conf_path = SYSCONFDIR "/nbd-proxy/config.json";
static const char *sockpath_tmpl = RUNSTATEDIR "/nbd.%d.sock";
-static const char *dev_path = "/dev/nbd0";
static const size_t bufsize = 0x20000;
-static const int nbd_timeout = 30;
+static const int nbd_timeout_default = 30;
static int open_nbd_socket(struct ctx *ctx)
{
@@ -116,7 +128,8 @@
char timeout_str[10];
int fd;
- snprintf(timeout_str, sizeof(timeout_str), "%d", nbd_timeout);
+ snprintf(timeout_str, sizeof(timeout_str),
+ "%d", ctx->nbd_timeout);
fd = open("/dev/null", O_RDWR);
if (fd < 0)
@@ -132,7 +145,7 @@
"-u", ctx->sock_path,
"-n",
"-t", timeout_str,
- dev_path,
+ ctx->dev_path,
NULL);
err(EXIT_FAILURE, "can't start ndb client");
}
@@ -373,16 +386,163 @@
return rc ? -1 : 0;
}
-int main(void)
+static void config_free_one(struct config *config)
{
+ if (config->metadata)
+ json_object_put(config->metadata);
+ free(config->nbd_device);
+ free(config->name);
+}
+
+static int config_parse_one(struct config *config, const char *name,
+ json_object *obj)
+{
+ struct json_object *tmp, *meta;
+ json_bool jrc;
+
+ jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
+ if (!jrc) {
+ warnx("config %s doesn't specify a nbd-device", name);
+ return -1;
+ }
+
+ if (!json_object_is_type(tmp, json_type_string)) {
+ warnx("config %s has invalid nbd-device", name);
+ return -1;
+ }
+
+ config->nbd_device = strdup(json_object_get_string(tmp));
+ config->name = strdup(name);
+
+ jrc = json_object_object_get_ex(obj, "metadata", &meta);
+ if (jrc && json_object_is_type(meta, json_type_object))
+ config->metadata = json_object_get(meta);
+ else
+ config->metadata = NULL;
+
+ return 0;
+}
+
+static void config_free(struct ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->n_configs; i++)
+ config_free_one(&ctx->configs[i]);
+
+ free(ctx->configs);
+ ctx->n_configs = 0;
+}
+
+static int config_init(struct ctx *ctx)
+{
+ struct json_object *obj, *tmp;
+ json_bool jrc;
+ int i, rc;
+
+ /* apply defaults */
+ ctx->nbd_timeout = nbd_timeout_default;
+
+ obj = json_object_from_file(conf_path);
+ if (!obj) {
+ warnx("can't read configuration from %s\n", conf_path);
+ return -1;
+ }
+
+ /* global configuration */
+ jrc = json_object_object_get_ex(obj, "timeout", &tmp);
+ if (jrc) {
+ errno = 0;
+ ctx->nbd_timeout = json_object_get_int(tmp);
+ if (ctx->nbd_timeout == 0 && errno) {
+ warnx("can't parse timeout value");
+ goto err_free;
+ }
+ }
+
+ /* per-config configuration */
+ jrc = json_object_object_get_ex(obj, "configurations", &tmp);
+ if (!jrc) {
+ warnx("no configurations specified");
+ goto err_free;
+ }
+
+ if (!json_object_is_type(tmp, json_type_object)) {
+ warnx("invalid configurations format");
+ goto err_free;
+ }
+
+ ctx->n_configs = json_object_object_length(tmp);
+ ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
+
+ i = 0;
+ json_object_object_foreach(tmp, name, config) {
+ rc = config_parse_one(&ctx->configs[i], name, config);
+ if (rc)
+ goto err_free;
+ i++;
+ }
+
+ json_object_put(obj);
+
+ return 0;
+
+err_free:
+ warnx("failed to load config from %s", conf_path);
+ json_object_put(obj);
+ return -1;
+}
+
+static int config_select(struct ctx *ctx, const char *name)
+{
+ struct config *config;
+ int i;
+
+ config = NULL;
+
+ /* find a matching config... */
+ for (i = 0; i < ctx->n_configs; i++) {
+ if (!strcmp(ctx->configs[i].name, name)) {
+ config = &ctx->configs[i];
+ break;
+ }
+ }
+
+ if (!config) {
+ warnx("no such configuration '%s'", name);
+ return -1;
+ }
+
+ /* ... and apply it */
+ ctx->dev_path = config->nbd_device;
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *config_name;
struct ctx _ctx, *ctx;
int rc;
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <configuration>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ config_name = argv[1];
+
ctx = &_ctx;
+ memset(ctx, 0, sizeof(*ctx));
ctx->bufsize = bufsize;
ctx->buf = malloc(ctx->bufsize);
- ctx->sock_path = NULL;
- ctx->nbd_client_pid = 0;
+
+ rc = config_init(ctx);
+ if (rc)
+ goto out_free;
+
+ rc = config_select(ctx, config_name);
+ if (rc)
+ goto out_free;
rc = open_nbd_socket(ctx);
if (rc)
@@ -419,6 +579,7 @@
}
close(ctx->sock);
out_free:
+ config_free(ctx);
free(ctx->buf);
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
}