[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 &param->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 = &param->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