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

Sunil Mushran sunil.mushran at oracle.com
Mon Nov 23 17:51:23 PST 2009


So I was hoping you'd do one task first. Currently you have handled
the raw device bit. What is equally important is the non-privileged
user. Actually, that is one of the main goal of this utility... a user that
does not have read permission on the device can get the same info.

Hence ioctl(). And when we have ioctl(), we have to be very careful
because we want the utility/fs to work across all combinations.

Anycase, we'll discuss this tomorrow in the concall.

Sunil

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 code for volinfo_op in o2info_operation.c
>
> Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
> ---
>  o2info/main.c |  588 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 588 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..90ea676
> --- /dev/null
> +++ b/o2info/main.c
> @@ -0,0 +1,588 @@
> +/* -*- 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) 2008 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.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +
> +#include "o2info.h"
> +
> +extern struct o2info_operation freefrag_op;
> +extern struct o2info_operation freeinode_op;
> +extern struct o2info_operation filestat_op;
> +extern struct o2info_operation usage_op;
> +extern struct o2info_operation volinfo_op;
> +extern struct o2info_operation fs_features_op;
> +
> +static LIST_HEAD(o2info_op_task_list);
> +
> +static int o2info_op_task_count;
> +
> +static 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 struct o2info_option help_option = {
> +	.opt_option	= {
> +		.name		= "help",
> +		.val		= 'h',
> +		.has_arg	= 0,	
> +		.flag		= 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_handler	= version_handler,
> +	.opt_op		= NULL,
> +	.opt_private = NULL,
> +};
> +
> +static struct o2info_option freefrag_option = {
> +	.opt_option	= {
> +		.name		= "freefrag",
> +		.val		= 'F',
> +		.has_arg	= 1,
> +		.flag		= NULL,
> +	},
> +	.opt_help	=
> +		"-F|--freefrag <chunksize>",
> +	.opt_handler	= NULL,
> +	.opt_op		= &freefrag_op,
> +};
> +
> +static struct o2info_option freeinode_option = {
> +	.opt_option	= {
> +		.name		= "freeinode",
> +		.val		= 'I',
> +		.has_arg	= 0,
> +		.flag		= NULL,
> +	},
> +	.opt_help	=
> +		"-I|--freeinode",
> +	.opt_handler	= NULL,
> +	.opt_op		= &freeinode_op,
> +};
> +
> +static struct o2info_option filestat_option = {
> +	.opt_option	= {
> +		.name		= "filestat",
> +		.val		= 'S',
> +		.has_arg	= 1,
> +		.flag		= NULL,
> +	},
> +	.opt_help	=
> +		"-S|--filestat <file>",
> +	.opt_handler	= NULL,
> +	.opt_op		= &filestat_op,
> +	.opt_private	= NULL,
> +};
> +
> +static struct o2info_option usage_option = {
> +	.opt_option	= {
> +		.name		= "usage",
> +		.val		= 'U',
> +		.has_arg	= 1,
> +		.flag		= NULL,
> +	},
> +	.opt_help	=
> +		"-U|--usage <file>",
> +	.opt_handler	= NULL,
> +	.opt_op		= &usage_op,
> +};
> +
> +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,
> +};
> +
> +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,
> +};
> +
> +static struct o2info_option *options[] = {
> +	&help_option,
> +	&version_option,
> +	&freefrag_option,
> +	&freeinode_option,
> +	&filestat_option,
> +	&usage_option,
> +	&volinfo_option,
> +	&fs_features_option,
> +	NULL,
> +};
> +
> +static 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>\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 errcode_t o2info_free_op_task_list(void)
> +{
> +	struct o2info_op_task *task;
> +	struct list_head *pos, *next;
> +
> +	if (list_empty(&o2info_op_task_list))
> +		goto out;
> +
> +	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);
> +        }
> +	
> +out:
> +	return 0;
> +}
> +
> +extern int optind, opterr, optopt;
> +extern char *optarg;
> +static errcode_t parse_options(int argc, char *argv[], char **device)
> +{
> +	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;
> +		/* simple handler for option such as showing version
> +		 * and printing the usage.
> +		 */
> +		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 >= argc) {
> +		errorf("No device specified\n");
> +		print_usage(1);
> +	}
> +
> +	*device = strdup(argv[optind]);
> +        if (!*device) {
> +		errorf("No memory to 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(ocfs2_filesys *fs)
> +{
> +	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, fs,
> +				       task->o2p_task->to_private);
> +	}
> +	return 0;
> +}
> +
> +static void handle_signal(int caught_sig)
> +{
> +	int exitp = 0, abortp = 0;
> +	static int segv_already = 0;
> +
> +	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);
> +	}
> +}
> +
> +static errcode_t o2info_open(const char *device, int flags, ocfs2_filesys **ret_fs)
> +{
> +	errcode_t err;
> +	int open_flags;
> +	ocfs2_filesys *fs = NULL;
> +
> +	open_flags = OCFS2_FLAG_HEARTBEAT_DEV_OK | OCFS2_FLAG_RO;
> +
> +	err = ocfs2_open(device, open_flags, 0, 0, &fs);
> +	if (err) {
> +		tcom_err(err, "while opening device %s", device);
> +		goto out;
> +	} else
> +		*ret_fs = fs;
> +
> +out:
> +	return err;
> +}
> +static errcode_t o2info_close(ocfs2_filesys *fs)
> +{
> +	errcode_t err;
> +
> +	if (fs) {
> +		err = ocfs2_close(fs);
> +		if (err)
> +			tcom_err(err, "while closing device");
> +	}
> +	
> +	return err;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int rc = 0;
> +
> +	char *device;
> +	ocfs2_filesys *fs = NULL;
> +
> +	o2info_init(argv[0]);
> +
> +	parse_options(argc, argv, &device);
> +
> +	rc = o2info_open(device, 0, &fs);
> +	if (rc)
> +		goto out;
> +
> +	rc = o2info_run_task(fs);
> +	if (rc)
> +		goto out;
> +
> +	o2info_free_op_task_list();
> +
> +	rc = o2info_close(fs);
> +out:
> +	if (device)
> +		ocfs2_free(&device);
> +
> +	return rc;
> +}
>   




More information about the Ocfs2-tools-devel mailing list