[Ocfs2-tools-devel] [PATCH 16/19] Ocfs2-tools: Add main.c to o2info.

Sunil Mushran sunil.mushran at oracle.com
Fri Apr 16 16:20:41 PDT 2010


So the outer shell of main.c should be earlier in the series.
As you add support for commands, like volinfo, you can add
the appropriate support here. That makes reviewing easier.

Tristan Ye wrote:
> It handles the parsing of options, links the operational
> tasks to list, then schedules to run task one bye one.
>
> To add a new operation task for o2info(such as --volinfo)
>
> 1. declare a new extern operation structure.
>    extern struct o2info_operation volinfo_op;
>
> 2. add a new option:
>    static struct o2info_option volinfo_option = {
>         .opt_option     = {
>                 .name           = "volinfo",
>                 .val            = 'V',
>                 .has_arg        = 0,
>                 .flag           = NULL,
>         },
>         .opt_help       =
>                 "-V|--volinfo",
>         .opt_handler    = NULL,
>         .opt_op         = &volinfo_op,
>         .opt_private    = NULL,
> };
>
> 3. Implement the real codes for volinfo_op in operation.c
>
> Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
> ---
>  o2info/main.c |  601 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 601 insertions(+), 0 deletions(-)
>  create mode 100644 o2info/main.c
>
> diff --git a/o2info/main.c b/o2info/main.c
> new file mode 100644
> index 0000000..174aa75
> --- /dev/null
> +++ b/o2info/main.c
> @@ -0,0 +1,601 @@
> +/* -*- mode: c; c-basic-offset: 8; -*-
> + * vim: noexpandtab sw=8 ts=8 sts=0:
> + *
> + * main.c
> + *
> + * Ocfs2 utility to gather and report fs information
> + *
> + * Copyright (C) 2010 Oracle.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +
> +#define _XOPEN_SOURCE 600
> +#define _LARGEFILE64_SOURCE
> +#define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */
> +
> +#include <unistd.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include <getopt.h>
> +#include <assert.h>
> +
> +#include "ocfs2/ocfs2.h"
> +#include "ocfs2-kernel/ocfs2_ioctl.h"
> +#include "ocfs2-kernel/kernel-list.h"
> +#include "tools-internal/verbose.h"
> +
> +#include "utils.h"
> +
> +extern struct o2info_operation volinfo_op;
> +extern struct o2info_operation fs_features_op;
> +extern struct o2info_operation usage_op;
> +extern struct o2info_operation filestat_op;
> +extern struct o2info_operation freefrag_op;
> +extern struct o2info_operation freeinode_op;
> +
> +static LIST_HEAD(o2info_op_task_list);
> +static int o2info_op_task_count;
> +int no_coherency;
> +
> +void print_usage(int rc);
> +static int help_handler(struct o2info_option *opt, char *arg)
> +{
> +	print_usage(0);
> +	exit(0);
> +}
> +
> +static int version_handler(struct o2info_option *opt, char *arg)
> +{
> +	tools_version();
> +	exit(0);
> +}
> +
> +static int coherency_handler(struct o2info_option *opt, char *arg)
> +{
> +	no_coherency = OCFS2_INFO_FL_NON_COHERENT;
> +
> +	return 0;
> +}
> +
> +static struct o2info_option help_option = {
> +	.opt_option	= {
> +		.name		= "help",
> +		.val		= 'h',
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= NULL,
> +	.opt_handler	= help_handler,
> +	.opt_op		= NULL,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option version_option = {
> +	.opt_option	= {
> +		.name		= "version",
> +		.val		= 'V',
> +		.has_arg 	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= NULL,
> +	.opt_handler	= version_handler,
> +	.opt_op		= NULL,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option coherency_option = {
> +	.opt_option	= {
> +		.name		= "no-coherency",
> +		.val		= 'F',
> +		.has_arg 	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	=
> +		"-F|--no-coherency",
> +	.opt_handler	= coherency_handler,
> +	.opt_op		= NULL,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option volinfo_option = {
> +	.opt_option	= {
> +		.name		= "volinfo",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --volinfo",
> +	.opt_handler	= NULL,
> +	.opt_op		= &volinfo_op,
> +	.opt_private	= NULL,
> +};
> +
> +static struct o2info_option fs_features_option = {
> +	.opt_option	= {
> +		.name		= "fs-features",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --fs-features",
> +	.opt_handler	= NULL,
> +	.opt_op		= &fs_features_op,
> +	.opt_private	= NULL,
> +};
> +
> +static struct o2info_option usage_option = {
> +	.opt_option	= {
> +		.name		= "space-usage",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --space-usage",
> +	.opt_handler	= NULL,
> +	.opt_op		= &usage_op,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option filestat_option = {
> +	.opt_option	= {
> +		.name		= "file-statistics",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --file-statistics",
> +	.opt_handler	= NULL,
> +	.opt_op		= &filestat_op,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option freefrag_option = {
> +	.opt_option	= {
> +		.name		= "freefrag",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 1,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --freefrag <chunksize in KB>",
> +	.opt_handler	= NULL,
> +	.opt_op		= &freefrag_op,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option freeinode_option = {
> +	.opt_option	= {
> +		.name		= "freeinode",
> +		.val		= CHAR_MAX,
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	= "   --freeinode",
> +	.opt_handler	= NULL,
> +	.opt_op		= &freeinode_op,
> +	.opt_private	= NULL,
> +};
> +
> +static struct o2info_option *options[] = {
> +	&help_option,
> +	&version_option,
> +	&volinfo_option,
> +	&fs_features_option,
> +	&usage_option,
> +	&filestat_option,
> +	&freefrag_option,
> +	&freeinode_option,
> +	&coherency_option,
> +	NULL,
> +};
> +
> +void print_usage(int rc)
> +{
> +	int i;
> +	enum tools_verbosity_level level = VL_ERR;
> +
> +	if (!rc)
> +		level = VL_OUT;
> +
> +	verbosef(level, "Usage: %s [options] <device or file>\n",
> +		 tools_progname());
> +	verbosef(level, "       %s -h|--help\n", tools_progname());
> +	verbosef(level, "       %s -V|--version\n", tools_progname());
> +	verbosef(level, "[options] can be followings:\n");
> +
> +	for (i = 0; options[i]; i++) {
> +		if (options[i]->opt_help)
> +			verbosef(level, "\t%s\n", options[i]->opt_help);
> +	}
> +
> +	exit(rc);
> +}
> +
> +static int build_options(char **optstring, struct option **longopts)
> +{
> +	errcode_t err;
> +	int i, num_opts, rc = 0;
> +	int unprintable_counter;
> +	size_t optstring_len;
> +	char *p, *str = NULL;
> +	struct option *lopts = NULL;
> +	struct o2info_option *opt;
> +
> +	unprintable_counter = 1;	/* Start unique at CHAR_MAX + 1*/
> +	optstring_len = 1;		/* For the leading ':' */
> +	for (i = 0; options[i]; i++) {
> +		opt = options[i];
> +
> +		/*
> +		 * Any option with a val of CHAR_MAX wants an unique but
> +		 * unreadable ->val.  Only readable characters go into
> +		 * optstring.
> +		 */
> +		if (opt->opt_option.val == CHAR_MAX) {
> +			opt->opt_option.val =
> +				CHAR_MAX + unprintable_counter;
> +			unprintable_counter++;
> +			continue;
> +		}
> +
> +		/*
> +		 * A given option has a single character in optstring.
> +		 * If it takes a mandatory argument, has_arg==1 and you add
> +		 * a ":" to optstring.  If it takes an optional argument,
> +		 * has_arg==2 and you add "::" to optstring.  Thus,
> +		 * 1 + has_arg is the total space needed in opstring.
> +		 */
> +		optstring_len += 1 + opt->opt_option.has_arg;
> +	}
> +
> +	num_opts = i;
> +
> +	err = ocfs2_malloc0(sizeof(char) * (optstring_len + 1), &str);
> +	if (!err)
> +		err = ocfs2_malloc(sizeof(struct option) * (num_opts + 1),
> +				   &lopts);
> +	if (err) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +
> +	p = str;
> +	*p++ = ':';
> +	for (i = 0; options[i]; i++) {
> +		assert(p < (str + optstring_len + 1));
> +		opt = options[i];
> +
> +		memcpy(&lopts[i], &opt->opt_option, sizeof(struct option));
> +
> +		if (opt->opt_option.val >= CHAR_MAX)
> +			continue;
> +
> +		*p = opt->opt_option.val;
> +		p++;
> +		if (opt->opt_option.has_arg > 0) {
> +			*p = ':';
> +			p++;
> +		}
> +		if (opt->opt_option.has_arg > 1) {
> +			*p = ':';
> +			p++;
> +		}
> +	}
> +
> +	/*
> +	 * Fill last entry of options with zeros.
> +	 */
> +	memset(&lopts[i], 0, sizeof(struct option));
> +
> +out:
> +	if (!rc) {
> +		*optstring = str;
> +		*longopts = lopts;
> +	} else {
> +		if (str)
> +			free(str);
> +		if (lopts)
> +			free(lopts);
> +	}
> +
> +	return rc;
> +}
> +
> +static struct o2info_option *find_option_by_val(int val)
> +{
> +	int i;
> +	struct o2info_option *opt = NULL;
> +
> +	for (i = 0; options[i]; i++) {
> +		if (options[i]->opt_option.val == val) {
> +			opt = options[i];
> +			break;
> +		}
> +	}
> +
> +	return opt;
> +}
> +
> +static errcode_t o2info_append_task(struct o2info_operation *o2p)
> +{
> +	errcode_t err;
> +	struct o2info_op_task *task;
> +
> +	err = ocfs2_malloc0(sizeof(struct o2info_op_task), &task);
> +	if (!err) {
> +		task->o2p_task = o2p;
> +		list_add_tail(&task->o2p_list, &o2info_op_task_list);
> +		o2info_op_task_count++;
> +	} else
> +		ocfs2_free(&task);
> +
> +	return err;
> +}
> +
> +static void o2info_free_op_task_list(void)
> +{
> +	struct o2info_op_task *task;
> +	struct list_head *pos, *next;
> +
> +	if (list_empty(&o2info_op_task_list))
> +		return;
> +
> +	list_for_each_safe(pos, next, &o2info_op_task_list) {
> +		task = list_entry(pos, struct o2info_op_task, o2p_list);
> +		list_del(pos);
> +		ocfs2_free(&task);
> +	}
> +}
> +
> +extern int optind, opterr, optopt;
> +extern char *optarg;
> +static errcode_t parse_options(int argc, char *argv[], char **device_or_file)
> +{
> +	int c, lopt_idx = 0;
> +	errcode_t err;
> +	struct option *long_options = NULL;
> +	char error[PATH_MAX];
> +	char *optstring = NULL;
> +	struct o2info_option *opt;
> +
> +	err = build_options(&optstring, &long_options);
> +	if (err)
> +		goto out;
> +
> +	opterr = 0;
> +	error[0] = '\0';
> +	while ((c = getopt_long(argc, argv, optstring,
> +				long_options, &lopt_idx)) != EOF) {
> +		opt = NULL;
> +		switch (c) {
> +		case '?':
> +			if (optopt)
> +				errorf("Invalid option: '-%c'\n", optopt);
> +			else
> +				errorf("Invalid option: '%s'\n",
> +				       argv[optind - 1]);
> +			print_usage(1);
> +			break;
> +
> +		case ':':
> +			if (optopt < CHAR_MAX)
> +				errorf("Option '-%c' requires an argument\n",
> +				       optopt);
> +			else
> +				errorf("Option '%s' requires an argument\n",
> +				       argv[optind - 1]);
> +			print_usage(1);
> +			break;
> +
> +		default:
> +			opt = find_option_by_val(c);
> +			if (!opt) {
> +				errorf("Shouldn't have gotten here: "
> +				       "option '-%c'\n", c);
> +				print_usage(1);
> +			}
> +
> +			if (optarg)
> +				opt->opt_private = (void *)optarg;
> +
> +			break;
> +		}
> +
> +		if (opt->opt_set) {
> +			errorf("Option '-%c' specified more than once\n",
> +			       c);
> +			print_usage(1);
> +		}
> +
> +		opt->opt_set = 1;
> +		/*
> +		 * Handlers for simple options such as showing version,
> +		 * printing the usage, or specify the coherency etc.
> +		 */
> +		if (opt->opt_handler) {
> +			if (opt->opt_handler(opt, optarg))
> +				print_usage(1);
> +		}
> +
> +		/*
> +		 * Real operation will be added to a list to run later.
> +		 */
> +		if (opt->opt_op) {
> +			opt->opt_op->to_private = opt->opt_private;
> +			err = o2info_append_task(opt->opt_op);
> +			if (err)
> +				goto out;
> +		}
> +	}
> +
> +	if (optind == 1)
> +		print_usage(1);
> +
> +	if (optind >= argc) {
> +		errorf("No device or file specified\n");
> +		print_usage(1);
> +	}
> +
> +	*device_or_file = strdup(argv[optind]);
> +	if (!*device_or_file) {
> +		errorf("No memory for allocation\n");
> +		goto out;
> +	}
> +
> +	optind++;
> +
> +	if (optind < argc) {
> +		errorf("Too many arguments\n");
> +		print_usage(1);
> +	}
> +
> +out:
> +	if (optstring)
> +		ocfs2_free(&optstring);
> +
> +	if (long_options)
> +		ocfs2_free(&long_options);
> +
> +	return err;
> +}
> +
> +static errcode_t o2info_run_task(struct o2info_method *om)
> +{
> +	struct list_head *p, *n;
> +	struct o2info_op_task *task;
> +
> +	list_for_each_safe(p, n, &o2info_op_task_list) {
> +		task = list_entry(p, struct o2info_op_task, o2p_list);
> +		task->o2p_task->to_run(task->o2p_task, om,
> +				       task->o2p_task->to_private);
> +	}
> +
> +	return 0;
> +}
> +
> +static void handle_signal(int caught_sig)
> +{
> +	int exitp = 0, abortp = 0;
> +	static int segv_already;
> +
> +	switch (caught_sig) {
> +	case SIGQUIT:
> +		abortp = 1;
> +		/* FALL THROUGH */
> +
> +	case SIGTERM:
> +	case SIGINT:
> +	case SIGHUP:
> +		errorf("Caught signal %d, exiting\n", caught_sig);
> +		exitp = 1;
> +		break;
> +
> +	case SIGSEGV:
> +		errorf("Segmentation fault, exiting\n");
> +		exitp = 1;
> +		if (segv_already) {
> +			errorf("Segmentation fault loop detected\n");
> +			abortp = 1;
> +		} else
> +			segv_already = 1;
> +		break;
> +
> +	default:
> +		errorf("Caught signal %d, ignoring\n", caught_sig);
> +		break;
> +	}
> +
> +	if (!exitp)
> +		return;
> +
> +	if (abortp)
> +		abort();
> +
> +	exit(1);
> +}
> +
> +static int setup_signals(void)
> +{
> +	int rc = 0;
> +	struct sigaction act;
> +
> +	act.sa_sigaction = NULL;
> +	sigemptyset(&act.sa_mask);
> +	act.sa_handler = handle_signal;
> +#ifdef SA_INTERRUPT
> +	act.sa_flags = SA_INTERRUPT;
> +#endif
> +	rc += sigaction(SIGTERM, &act, NULL);
> +	rc += sigaction(SIGINT, &act, NULL);
> +	rc += sigaction(SIGHUP, &act, NULL);
> +	rc += sigaction(SIGQUIT, &act, NULL);
> +	rc += sigaction(SIGSEGV, &act, NULL);
> +	act.sa_handler = SIG_IGN;
> +	rc += sigaction(SIGPIPE, &act, NULL);  /* Get EPIPE instead */
> +
> +	return rc;
> +}
> +
> +static void o2info_init(const char *argv0)
> +{
> +	initialize_ocfs_error_table();
> +
> +	tools_setup_argv0(argv0);
> +
> +	setbuf(stdout, NULL);
> +	setbuf(stderr, NULL);
> +
> +	if (setup_signals()) {
> +		errorf("Unable to setup signal handling \n");
> +		exit(1);
> +	}
> +
> +	/* Default should be cluster-coherency */
> +	no_coherency = 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int rc = 0;
> +
> +	char *device_or_file = NULL;
> +	static struct o2info_method om;
> +
> +	o2info_init(argv[0]);
> +	parse_options(argc, argv, &device_or_file);
> +
> +	rc = o2info_is_device(device_or_file);
> +	if (rc < 0)
> +		goto out;
> +	else
> +		if (rc)
> +			om.om_method = O2INFO_USE_LIBOCFS2;
> +		else
> +			om.om_method = O2INFO_USE_IOCTL;
> +
> +	strncpy(om.om_path, device_or_file, PATH_MAX);
> +
> +	rc = o2info_open(&om, 0);
> +	if (rc)
> +		goto out;
> +
> +	rc = o2info_run_task(&om);
> +	if (rc)
> +		goto out;
> +
> +	o2info_free_op_task_list();
> +
> +	rc = o2info_close(&om);
> +out:
> +	if (device_or_file)
> +		ocfs2_free(&device_or_file);
> +
> +	return rc;
> +}
>   




More information about the Ocfs2-tools-devel mailing list