[sheepdog] [PATCH 3/6] introduce sd_getopt as an alternative to getopt_long
morita.kazutaka at gmail.com
morita.kazutaka at gmail.com
Mon Nov 5 11:52:00 CET 2012
From: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
sd_getopt can handle the following syntax.
--<option> <arg>[:<key>=<value>[,<key>=<value>[,...]]]
This is useful to pass additional parameters of '--write-cache' and
'--cluster' options.
Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
---
collie/cluster.c | 16 ++---
collie/collie.c | 10 +--
collie/vdi.c | 18 ++---
include/option.h | 46 +++++++++++++
include/util.h | 1 +
lib/option.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/util.c | 10 +++
sheep/sheep.c | 34 +++++-----
8 files changed, 293 insertions(+), 39 deletions(-)
diff --git a/collie/cluster.c b/collie/cluster.c
index 065d2ac..8e97a8e 100644
--- a/collie/cluster.c
+++ b/collie/cluster.c
@@ -17,14 +17,14 @@
#include "collie.h"
static struct sd_option cluster_options[] = {
- {'b', "store", true, "specify backend store"},
- {'c', "copies", true, "specify the default data redundancy (number of copies)"},
- {'m', "mode", true, "mode (safe, quorum, unsafe)"},
- {'f', "force", false, "do not prompt for confirmation"},
- {'R', "restore", true, "restore the cluster"},
- {'l', "list", false, "list the user epoch information"},
-
- { 0, NULL, false, NULL },
+ {'b', "store", true, NULL, "specify backend store"},
+ {'c', "copies", true, NULL, "specify the default data redundancy (number of copies)"},
+ {'m', "mode", true, NULL, "mode (safe, quorum, unsafe)"},
+ {'f', "force", false, NULL, "do not prompt for confirmation"},
+ {'R', "restore", true, NULL, "restore the cluster"},
+ {'l', "list", false, NULL, "list the user epoch information"},
+
+ { 0, NULL, false, NULL, NULL },
};
static struct cluster_cmd_data {
diff --git a/collie/collie.c b/collie/collie.c
index a37e3a2..7b8e95f 100644
--- a/collie/collie.c
+++ b/collie/collie.c
@@ -25,13 +25,13 @@ bool raw_output;
static const struct sd_option collie_options[] = {
/* common options for all collie commands */
- {'a', "address", true, "specify the daemon address (default: localhost)"},
- {'p', "port", true, "specify the daemon port"},
- {'r', "raw", false, "raw output mode: omit headers, separate fields with\n\
+ {'a', "address", true, NULL, "specify the daemon address (default: localhost)"},
+ {'p', "port", true, NULL, "specify the daemon port"},
+ {'r', "raw", false, NULL, "raw output mode: omit headers, separate fields with\n\
single spaces and print all sizes in decimal bytes"},
- {'h', "help", false, "display this help and exit"},
+ {'h', "help", false, NULL, "display this help and exit"},
- { 0, NULL, false, NULL },
+ { 0, NULL, false, NULL, NULL },
};
static void usage(const struct command *commands, int status);
diff --git a/collie/vdi.c b/collie/vdi.c
index a3ad266..e8e3b2e 100644
--- a/collie/vdi.c
+++ b/collie/vdi.c
@@ -20,15 +20,15 @@
#include "treeview.h"
static struct sd_option vdi_options[] = {
- {'P', "prealloc", false, "preallocate all the data objects"},
- {'i', "index", true, "specify the index of data objects"},
- {'s', "snapshot", true, "specify a snapshot id or tag name"},
- {'x', "exclusive", false, "write in an exclusive mode"},
- {'d', "delete", false, "delete a key"},
- {'w', "writeback", false, "use writeback mode"},
- {'c', "copies", true, "specify the data redundancy (number of copies)"},
- {'F', "from", true, "create a differential backup from the snapshot"},
- { 0, NULL, false, NULL },
+ {'P', "prealloc", false, NULL, "preallocate all the data objects"},
+ {'i', "index", true, NULL, "specify the index of data objects"},
+ {'s', "snapshot", true, NULL, "specify a snapshot id or tag name"},
+ {'x', "exclusive", false, NULL, "write in an exclusive mode"},
+ {'d', "delete", false, NULL, "delete a key"},
+ {'w', "writeback", false, NULL, "use writeback mode"},
+ {'c', "copies", true, NULL, "specify the data redundancy (number of copies)"},
+ {'F', "from", true, NULL, "create a differential backup from the snapshot"},
+ { 0, NULL, false, NULL, NULL },
};
static struct vdi_cmd_data {
diff --git a/include/option.h b/include/option.h
index 14c1093..ccc5158 100644
--- a/include/option.h
+++ b/include/option.h
@@ -13,18 +13,64 @@
#include <stdbool.h>
#include <getopt.h>
+#include <stdint.h>
+
+/*
+ * Sheep command option syntax
+ *
+ * --<option> <arg>[:<key>=<value>[,<key>=<value>[,...]]]
+ *
+ * E.g.
+ * --cluster zookeeper:servers=10.0.0.1,10.0.0.2
+ * --writecache object:size=100M,directio=true
+ */
+
+struct sd_opt_value {
+ uint8_t type;
+
+ char *str;
+ int64_t num;
+ bool boolean;
+ uint64_t size;
+};
+
+struct sd_opt_param {
+ const char *arg;
+ const char *key;
+ const char *usage;
+ const char *desc;
+
+ struct sd_opt_value value;
+};
struct sd_option {
int ch;
const char *name;
bool has_arg;
+ struct sd_opt_param *params;
const char *desc;
+
+ struct sd_opt_value arg;
};
+bool sd_opt_is_number(const struct sd_opt_value *val);
+bool sd_opt_is_bool(const struct sd_opt_value *val);
+bool sd_opt_is_size(const struct sd_opt_value *val);
+bool sd_opt_is_valid_number(const struct sd_opt_value *val,
+ int64_t min_val, int64_t max_val);
+
char *build_short_options(const struct sd_option *opts);
struct option *build_long_options(const struct sd_option *opts);
+struct sd_opt_value *sd_opt_param_get(struct sd_opt_param *params,
+ const char *arg, const char *key);
+struct sd_option *sd_getopt(int argc, char * const argv[],
+ struct sd_option *opts);
+
#define sd_for_each_option(opt, opts) \
for (opt = (opts); opt->name; opt++)
+#define sd_for_each_opt_param(param, params) \
+ for (param = (params); param->arg; param++)
+
#endif /* __SD_OPTION_H__ */
diff --git a/include/util.h b/include/util.h
index 5fb19c2..7161945 100644
--- a/include/util.h
+++ b/include/util.h
@@ -79,6 +79,7 @@ extern ssize_t xread(int fd, void *buf, size_t len);
extern ssize_t xwrite(int fd, const void *buf, size_t len);
extern ssize_t xpread(int fd, void *buf, size_t count, off_t offset);
extern ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset);
+extern char *xstrdup(const char *s);
extern void pstrcpy(char *buf, int buf_size, const char *str);
extern int rmdir_r(char *dir_path);
diff --git a/lib/option.c b/lib/option.c
index 461d661..b8b969f 100644
--- a/lib/option.c
+++ b/lib/option.c
@@ -10,9 +10,41 @@
*/
#include <string.h>
+#include <getopt.h>
+#include "util.h"
#include "option.h"
+/* sheep options */
+
+#define SD_OPT_NONE 0x00
+#define SD_OPT_STRING 0x01
+#define SD_OPT_NUMBER 0x02
+#define SD_OPT_BOOL 0x04
+#define SD_OPT_SIZE 0x08
+
+bool sd_opt_is_number(const struct sd_opt_value *val)
+{
+ return !!(val->type & SD_OPT_NUMBER);
+}
+
+bool sd_opt_is_bool(const struct sd_opt_value *val)
+{
+ return !!(val->type & SD_OPT_BOOL);
+}
+
+bool sd_opt_is_size(const struct sd_opt_value *val)
+{
+ return !!(val->type & SD_OPT_SIZE);
+}
+
+bool sd_opt_is_valid_number(const struct sd_opt_value *val,
+ int64_t min_val, int64_t max_val)
+{
+ return sd_opt_is_number(val) &&
+ min_val <= val->num && val->num <= max_val;
+}
+
char *build_short_options(const struct sd_option *sd_opts)
{
static char sopts[256], *p;
@@ -46,3 +78,168 @@ struct option *build_long_options(const struct sd_option *sd_opts)
return lopts;
}
+
+static void parse_sd_opt_value(struct sd_opt_value *val, const char *str)
+{
+ char *p, *postfix;
+ double sizef;
+
+ val->str = xstrdup(str);
+ val->type = SD_OPT_STRING;
+
+ val->num = strtoll(str, &p, 10);
+ if (str < p)
+ val->type |= SD_OPT_NUMBER;
+
+ if (strcasecmp(str, "true") == 0 || strcasecmp(str, "t") == 0 ||
+ strcasecmp(str, "on") == 0 || strcmp(str, "1") == 0) {
+ val->boolean = true;
+ val->type |= SD_OPT_BOOL;
+ } else if (strcasecmp(str, "false") == 0 || strcasecmp(str, "f") == 0 ||
+ strcasecmp(str, "off") == 0 || strcmp(str, "0") == 0) {
+ val->boolean = false;
+ val->type |= SD_OPT_BOOL;
+ }
+
+ sizef = strtod(str, &postfix);
+ if (str < postfix) {
+ switch (*postfix) {
+ case 'T':
+ sizef *= 1024;
+ case 'G':
+ sizef *= 1024;
+ case 'M':
+ sizef *= 1024;
+ case 'K':
+ case 'k':
+ sizef *= 1024;
+ case 'b':
+ case '\0':
+ val->size = (uint64_t)sizef;
+ }
+ val->type |= SD_OPT_SIZE;
+ }
+}
+
+struct sd_opt_value *sd_opt_param_get(struct sd_opt_param *params,
+ const char *arg, const char *key)
+{
+ struct sd_opt_param *param;
+
+ sd_for_each_opt_param(param, params) {
+ if (strcmp(param->arg, arg) == 0 &&
+ strcmp(param->key, key) == 0) {
+ if (param->value.str == NULL)
+ return NULL;
+ return ¶m->value;
+ }
+ }
+
+ return NULL;
+}
+
+static int parse_sd_opt_param(struct sd_opt_param *params, const char *arg,
+ const char *key, const char *value)
+{
+ struct sd_opt_value *opt_val = NULL;
+ struct sd_opt_param *param;
+
+ sd_for_each_opt_param(param, params) {
+ if (strcmp(param->arg, arg) == 0 &&
+ strcmp(param->key, key) == 0) {
+ opt_val = ¶m->value;
+ parse_sd_opt_value(opt_val, value);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "%s: Invalid parameter '%s'\n", arg, key);
+ return -1;
+}
+
+/* parse key=value pairs */
+static int parse_sd_opt_params(struct sd_opt_param *params, const char *arg,
+ char *str)
+{
+ const char *key, *value;
+
+ key = strtok(str, "=");
+ if (!key)
+ return 0;
+
+ value = strtok(NULL, "=");
+ if (!value) {
+ fprintf(stderr, "%s: Invalid format '%s'\n", arg, key);
+ return -1;
+ }
+ while (key && value) {
+ int ret;
+ char *next_key = NULL, *next_value = NULL;
+
+ next_value = strtok(NULL, "=");
+ if (next_value) {
+ next_key = strrchr(value, ',');
+ if (next_key)
+ *next_key++ = '\0';
+ else
+ next_value = NULL;
+ }
+
+ ret = parse_sd_opt_param(params, arg, key, value);
+ if (ret < 0)
+ return ret;
+
+ key = next_key;
+ value = next_value;
+ }
+
+ return 0;
+}
+
+static void parse_sd_option(struct sd_option *opt, char *str)
+{
+ const char *arg;
+
+ if (str == NULL)
+ return;
+
+ arg = strtok(str, ":");
+ parse_sd_opt_value(&opt->arg, arg);
+
+ str = strtok(NULL, "");
+ if (str) {
+ int ret;
+
+ ret = parse_sd_opt_params(opt->params, arg, str);
+ if (ret < 0)
+ exit(1);
+ }
+}
+
+struct sd_option *sd_getopt(int argc, char * const argv[],
+ struct sd_option *opts)
+{
+ int ch;
+ static struct option *long_options;
+ static const char *short_options;
+ struct sd_option *opt;
+
+ if (long_options == NULL) {
+ /* initialize */
+ long_options = build_long_options(opts);
+ short_options = build_short_options(opts);
+ }
+
+ ch = getopt_long(argc, argv, short_options, long_options, NULL);
+ if (ch < 0)
+ return NULL;
+
+ sd_for_each_option(opt, opts) {
+ if (opt->ch == ch) {
+ parse_sd_option(opt, optarg);
+ return opt;
+ }
+ }
+
+ panic("internal error\n");
+}
diff --git a/lib/util.c b/lib/util.c
index ea00afd..8dc9acb 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -216,6 +216,16 @@ ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
return total;
}
+char *xstrdup(const char *s)
+{
+ char *ret = strdup(s);
+
+ if (ret == NULL)
+ panic("Out of memory");
+
+ return ret;
+}
+
/**
* Copy the string str to buf. If str length is bigger than buf_size -
* 1 then it is clamped to buf_size - 1.
diff --git a/sheep/sheep.c b/sheep/sheep.c
index c8ee7ff..576bc5a 100644
--- a/sheep/sheep.c
+++ b/sheep/sheep.c
@@ -40,23 +40,23 @@ LIST_HEAD(cluster_drivers);
static const char program_name[] = "sheep";
static struct sd_option sheep_options[] = {
- {'b', "bindaddr", true, "specify IP address of interface to listen on"},
- {'c', "cluster", true, "specify the cluster driver"},
- {'d', "debug", false, "include debug messages in the log"},
- {'f', "foreground", false, "make the program run in the foreground"},
- {'g', "gateway", false, "make the progam run as a gateway mode"},
- {'h', "help", false, "display this help and exit"},
- {'j', "journal", false, "use jouranl to update vdi objects"},
- {'l', "loglevel", true, "specify the level of logging detail"},
- {'o', "stdout", false, "log to stdout instead of shared logger"},
- {'p', "port", true, "specify the TCP port on which to listen"},
- {'P', "pidfile", true, "create a pid file"},
- {'s', "disk-space", true, "specify the free disk space in megabytes"},
- {'u', "upgrade", false, "upgrade to the latest data layout"},
- {'w', "write-cache", true, "specify the cache type"},
- {'y', "myaddr", true, "specify the address advertised to other sheep"},
- {'z', "zone", true, "specify the zone id"},
- { 0, NULL, false, NULL },
+ {'b', "bindaddr", true, NULL, "specify IP address of interface to listen on"},
+ {'c', "cluster", true, NULL, "specify the cluster driver"},
+ {'d', "debug", false, NULL, "include debug messages in the log"},
+ {'f', "foreground", false, NULL, "make the program run in the foreground"},
+ {'g', "gateway", false, NULL, "make the progam run as a gateway mode"},
+ {'h', "help", false, NULL, "display this help and exit"},
+ {'j', "journal", false, NULL, "use jouranl to update vdi objects"},
+ {'l', "loglevel", true, NULL, "specify the level of logging detail"},
+ {'o', "stdout", false, NULL, "log to stdout instead of shared logger"},
+ {'p', "port", true, NULL, "specify the TCP port on which to listen"},
+ {'P', "pidfile", true, NULL, "create a pid file"},
+ {'s', "disk-space", true, NULL, "specify the free disk space in megabytes"},
+ {'u', "upgrade", false, NULL, "upgrade to the latest data layout"},
+ {'w', "write-cache", true, NULL, "specify the cache type"},
+ {'y', "myaddr", true, NULL, "specify the address advertised to other sheep"},
+ {'z', "zone", true, NULL, "specify the zone id"},
+ { 0, NULL, false, NULL, NULL },
};
static void usage(int status)
--
1.7.9.5
More information about the sheepdog
mailing list