OpenSolaris_b135/cmd/ndmpadm/ndmpadm_main.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * BSD 3 Clause License
 *
 * Copyright (c) 2007, The Storage Networking Industry Association.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 	- Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 *
 * 	- Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in
 *	  the documentation and/or other materials provided with the
 *	  distribution.
 *
 *	- Neither the name of The Storage Networking Industry Association (SNIA)
 *	  nor the names of its contributors may be used to endorse or promote
 *	  products derived from this software without specific prior written
 *	  permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <assert.h>
#include <ctype.h>
#include <libgen.h>
#include <libintl.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <door.h>
#include <sys/mman.h>
#include <libndmp.h>
#include "ndmpadm.h"

typedef enum {
	HELP_GET_CONFIG,
	HELP_SET_CONFIG,
	HELP_SHOW_DEVICES,
	HELP_SHOW_SESSIONS,
	HELP_KILL_SESSIONS,
	HELP_ENABLE_AUTH,
	HELP_DISABLE_AUTH
} ndmp_help_t;

typedef struct ndmp_command {
	const char	*nc_name;
	int		(*func)(int argc, char **argv,
			    struct ndmp_command *cur_cmd);
	ndmp_help_t	nc_usage;
} ndmp_command_t;

static int ndmp_get_config(int, char **, ndmp_command_t *);
static int ndmp_set_config(int, char **, ndmp_command_t *);
static int ndmp_show_devices(int, char **, ndmp_command_t *);
static int ndmp_show_sessions(int, char **, ndmp_command_t *);
static int ndmp_kill_sessions(int, char **, ndmp_command_t *);
static int ndmp_enable_auth(int, char **, ndmp_command_t *);
static int ndmp_disable_auth(int, char **, ndmp_command_t *);
static void ndmp_get_config_process(char *);
static void ndmp_set_config_process(char *arg);
static int ndmp_get_password(char **);

static ndmp_command_t command_table[] = {
	{ "get",		ndmp_get_config,	HELP_GET_CONFIG	},
	{ "set",		ndmp_set_config,	HELP_SET_CONFIG	},
	{ "show-devices",	ndmp_show_devices,	HELP_SHOW_DEVICES },
	{ "show-sessions",	ndmp_show_sessions,	HELP_SHOW_SESSIONS },
	{ "kill-sessions",	ndmp_kill_sessions,	HELP_KILL_SESSIONS },
	{ "enable",		ndmp_enable_auth,	HELP_ENABLE_AUTH },
	{ "disable",		ndmp_disable_auth,	HELP_DISABLE_AUTH }
};

#define	NCOMMAND	(sizeof (command_table) / sizeof (command_table[0]))

static char *prop_table[] = {
	"debug-path",
	"dump-pathnode",
	"tar-pathnode",
	"ignore-ctime",
	"token-maxseq",
	"version",
	"dar-support",
	"tcp-port",
	"backup-quarantine",
	"restore-quarantine",
	"overwrite-quarantine"
};

#define	NDMPADM_NPROP	(sizeof (prop_table) / sizeof (prop_table[0]))

typedef struct ndmp_auth {
	const char *auth_type;
	const char *username;
	const char *password;
} ndmp_auth_t;

static ndmp_auth_t ndmp_auth_table[] = {
	{ "cram-md5", "cram-md5-username", "cram-md5-password" },
	{ "cleartext", "cleartext-username", "cleartext-password" }
};
#define	NAUTH	(sizeof (ndmp_auth_table) / sizeof (ndmp_auth_table[0]))
#define	NDMP_PASSWORD_RETRIES	3

#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN	"SYS_TEST"
#endif

static const char *
get_usage(ndmp_help_t idx)
{
	switch (idx) {
	case HELP_SET_CONFIG:
		return ("\tset [-p] <property=value> [[-p] property=value] "
		    "...\n");
	case HELP_GET_CONFIG:
		return ("\tget [-p] [property] [[-p] property] ...\n");
	case HELP_SHOW_DEVICES:
		return ("\tshow-devices\n");
	case HELP_SHOW_SESSIONS:
		return ("\tshow-sessions [-i tape,scsi,data,mover] [id] ...\n");
	case HELP_KILL_SESSIONS:
		return ("\tkill-sessions <id ...>\n");
	case HELP_ENABLE_AUTH:
		return ("\tenable <-a auth-type> <-u username>\n");
	case HELP_DISABLE_AUTH:
		return ("\tdisable <-a auth-type>\n");
	}

	return (NULL);
}

/*
 * Display usage message.  If we're inside a command, display only the usage for
 * that command.  Otherwise, iterate over the entire command table and display
 * a complete usage message.
 */
static void
usage(boolean_t requested, ndmp_command_t *current_command)
{
	int i;
	boolean_t show_properties = B_FALSE;
	FILE *fp = requested ? stdout : stderr;

	if (current_command == NULL) {
		(void) fprintf(fp,
		    gettext("Usage: ndmpadm subcommand args ...\n"));
		(void) fprintf(fp,
		    gettext("where 'command' is one of the following:\n\n"));

		for (i = 0; i < NCOMMAND; i++) {
			(void) fprintf(fp, "%s",
			    get_usage(command_table[i].nc_usage));
		}
		(void) fprintf(fp, gettext("\t\twhere %s can be either "
		    "%s or %s\n"), "'auth-type'", "'cram-md5'", "'cleartext'");
	} else {
		(void) fprintf(fp, gettext("Usage:\n"));
		(void) fprintf(fp, "%s", get_usage(current_command->nc_usage));
		if ((current_command->nc_usage == HELP_ENABLE_AUTH) ||
		    (current_command->nc_usage == HELP_DISABLE_AUTH))
			(void) fprintf(fp, gettext("\t\twhere %s can be either "
			    "%s or %s\n"),
			    "'auth-type'", "'cram-md5'", "'cleartext'");
	}

	if (current_command != NULL &&
	    (strcmp(current_command->nc_name, "set") == 0))
		show_properties = B_TRUE;

	if (show_properties) {
		(void) fprintf(fp,
		    gettext("\nThe following properties are supported:\n"));

		(void) fprintf(fp, gettext("\n\tPROPERTY"));
		(void) fprintf(fp, "\n\t%s", "-------------");
		for (i = 0; i < NDMPADM_NPROP; i++)
			(void) fprintf(fp, "\n\t%s", prop_table[i]);
		(void) fprintf(fp, "\n");
	}

	exit(requested ? 0 : 2);
}

/*ARGSUSED*/
static int
ndmp_get_config(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	char *propval;
	int i, c;

	if (argc == 1) {
		/*
		 * Get all the properties and variables ndmpadm is allowed
		 * to see.
		 */
		for (i = 0; i < NDMPADM_NPROP; i++) {
			if (ndmp_get_prop(prop_table[i], &propval)) {
				(void) fprintf(stdout, "\t%s=\n",
				    prop_table[i]);
			} else {
				(void) fprintf(stdout, "\t%s=%s\n",
				    prop_table[i], propval);
				free(propval);
			}
		}
	} else if (argc > 1) {
		while ((c = getopt(argc, argv, ":p:")) != -1) {
			switch (c) {
			case 'p':
				ndmp_get_config_process(optarg);
				break;
			case ':':
				(void) fprintf(stderr, gettext("Option -%c "
				    "requires an operand\n"), optopt);
				break;
			case '?':
				(void) fprintf(stderr, gettext("Unrecognized "
				    "option: -%c\n"), optopt);
			}
		}
		/*
		 * optind is initialized to 1 if the -p option is not used,
		 * otherwise index to argv.
		 */
		argc -= optind;
		argv += optind;

		for (i = 0; i < argc; i++) {
			if (strncmp(argv[i], "-p", 2) == 0)
				continue;

			ndmp_get_config_process(argv[i]);
		}
	}
	return (0);
}

static void
ndmp_get_config_process(char *arg)
{
	int j;
	char *propval;

	for (j = 0; j < NDMPADM_NPROP; j++) {
		if (strcmp(arg, prop_table[j]) == 0) {
			if (ndmp_get_prop(arg, &propval)) {
				(void) fprintf(stdout, "\t%s=\n", arg);
			} else {
				(void) fprintf(stdout, "\t%s=%s\n",
				    arg, propval);
				free(propval);
			}
			break;
		}
	}
	if (j == NDMPADM_NPROP) {
		(void) fprintf(stdout, gettext("\t%s is invalid property "
		    "or variable\n"), arg);
	}
}

/*ARGSUSED*/
static int
ndmp_set_config(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	int c, i;

	if (argc < 2) {
		(void) fprintf(stderr, gettext("Missing property=value "
		    "argument\n"));
		usage(B_FALSE, cur_cmd);
	}
	while ((c = getopt(argc, argv, ":p:")) != -1) {
		switch (c) {
		case 'p':
			ndmp_set_config_process(optarg);
			break;
		case ':':
			(void) fprintf(stderr, gettext("Option -%c "
			    "requires an operand\n"), optopt);
			break;
		case '?':
			(void) fprintf(stderr, gettext("Unrecognized "
			    "option: -%c\n"), optopt);
		}
	}
	/*
	 * optind is initialized to 1 if the -p option is not used,
	 * otherwise index to argv.
	 */
	argc -= optind;
	argv += optind;

	for (i = 0; i < argc; i++) {
		if (strncmp(argv[i], "-p", 2) == 0)
			continue;

		ndmp_set_config_process(argv[i]);
	}
	return (0);
}

static void
ndmp_set_config_process(char *propname)
{
	char *propvalue;
	int ret, j;

	if ((propvalue = strchr(propname, '=')) == NULL) {
		(void) fprintf(stderr, gettext("Missing value in "
		    "property=value argument for %s\n"), propname);
			return;
	}
	*propvalue = '\0';
	propvalue++;

	if (*propname == '\0') {
		(void) fprintf(stderr, gettext("Missing property in "
		    "property=value argument for %s\n"), propname);
			return;
	}
	for (j = 0; j < NDMPADM_NPROP; j++) {
		if (strcmp(propname, prop_table[j]) == 0)
			break;
	}
	if (j == NDMPADM_NPROP) {
		(void) fprintf(stdout, gettext("%s is invalid property or "
		    "variable\n"), propname);
		return;
	}
	ret = ndmp_set_prop(propname, propvalue);
	if (ret != -1) {
		if (!ndmp_door_status()) {
			if (ndmp_service_refresh() == -1)
				(void) fprintf(stdout, gettext("Could not "
				    "refesh property of service ndmpd\n"));
		}
	} else {
		(void) fprintf(stdout, gettext("Could not set property for "
		    "%s - %s\n"), propname, ndmp_strerror(ndmp_errno));
	}
}

/*ARGSUSED*/
static int
ndmp_show_devices(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	int ret;
	ndmp_devinfo_t *dip = NULL;
	size_t size;

	if (ndmp_door_status()) {
		(void) fprintf(stdout,
		    gettext("Service ndmpd not running\n"));
		return (-1);
	}

	ret = ndmp_get_devinfo(&dip, &size);

	if (ret == -1)
		(void) fprintf(stdout,
		    gettext("Could not get device information\n"));
	else
		ndmp_devinfo_print(dip, size);

	ndmp_get_devinfo_free(dip, size);
	return (0);
}

static int
ndmp_show_sessions(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	ndmp_session_info_t *sinfo = NULL;
	ndmp_session_info_t *sp = NULL;
	uint_t num;
	int c, ret, i, j;
	int statarg = 0;
	char *value;
	char *type_subopts[] = { "tape", "scsi", "data", "mover", NULL };

	if (ndmp_door_status()) {
		(void) fprintf(stdout,
		    gettext("Service ndmpd not running\n"));
		return (-1);
	}

	/* Detail output if no option is specified */
	if (argc == 1) {
		statarg = NDMP_CAT_ALL;
	} else {
		statarg = 0;
		while ((c = getopt(argc, argv, ":i:")) != -1) {
			switch (c) {
			case 'i':
				while (*optarg != '\0') {
					switch (getsubopt(&optarg, type_subopts,
					    &value)) {
					case 0:
						statarg |= NDMP_CAT_TAPE;
						break;
					case 1:
						statarg |= NDMP_CAT_SCSI;
						break;
					case 2:
						statarg |= NDMP_CAT_DATA;
						break;
					case 3:
						statarg |= NDMP_CAT_MOVER;
						break;
					default:
						(void) fprintf(stderr,
						    gettext("Invalid object "
						    "type '%s'\n"), value);
						usage(B_FALSE, cur_cmd);
					}
				}
				break;
			case ':':
				(void) fprintf(stderr,
				    gettext("Missing argument for "
				    "'%c' option\n"), optopt);
				usage(B_FALSE, cur_cmd);
				break;
			case '?':
				(void) fprintf(stderr,
				    gettext("Invalid option '%c'\n"), optopt);
				usage(B_FALSE, cur_cmd);
			}
		}
		/* if -i and its argument are not specified, display all */
		if (statarg == 0)
			statarg = NDMP_CAT_ALL;
	}
	/*
	 * optind is initialized to 1 if the -i option is not used, otherwise
	 * index to argv.
	 */
	argc -= optind;
	argv += optind;

	ret = ndmp_get_session_info(&sinfo, &num);
	if (ret == -1) {
		(void) fprintf(stdout,
		    gettext("Could not get session information\n"));
	} else {
		if (argc == 0) {
			ndmp_session_all_print(statarg, sinfo, num);
		} else {
			for (i = 0; i < argc; i++) {
				sp = sinfo;
				for (j = 0; j < num; j++, sp++) {
					if (sp->nsi_sid == atoi(argv[i])) {
						ndmp_session_print(statarg, sp);
						(void) fprintf(stdout, "\n");
						break;
					}
				}
				if (j == num) {
					(void) fprintf(stdout,
					    gettext("Session %d not "
					    "found\n"), atoi(argv[i]));
				}
			}
		}
		ndmp_get_session_info_free(sinfo, num);
	}
	return (0);
}

/*ARGSUSED*/
static int
ndmp_kill_sessions(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	int ret, i;

	if (ndmp_door_status()) {
		(void) fprintf(stdout,
		    gettext("Service ndmpd not running.\n"));
		return (-1);
	}

	/* If no arg is specified, print the usage and exit */
	if (argc == 1)
		usage(B_FALSE, cur_cmd);

	for (i = 1; i < argc; i++) {
		if (atoi(argv[i]) > 0) {
			ret = ndmp_terminate_session(atoi(argv[i]));
		} else {
			(void) fprintf(stderr,
			    gettext("Invalid argument %s\n"), argv[i]);
				continue;
		}
		if (ret == -1)
			(void) fprintf(stdout,
			    gettext("Session id %d not found.\n"),
			    atoi(argv[i]));
	}
	return (0);
}

static int
ndmp_get_password(char **password)
{
	char *pw1, pw2[257];
	int i;

	for (i = 0; i < NDMP_PASSWORD_RETRIES; i++) {
		/*
		 * getpassphrase use the same buffer to return password, so
		 * copy the result in different buffer, before calling the
		 * getpassphrase again.
		 */
		if ((pw1 =
		    getpassphrase(gettext("Enter new password: "))) != NULL) {
			(void) strlcpy(pw2, pw1, sizeof (pw2));
			if ((pw1 =
			    getpassphrase(gettext("Re-enter  password: ")))
			    != NULL) {
				if (strncmp(pw1, pw2, strlen(pw1)) == 0) {
					*password = pw1;
					return (0);
				} else {
					(void) fprintf(stderr,
					    gettext("Both password did not "
					    "match.\n"));
				}
			}
		}
	}
	return (-1);
}

static int
ndmp_enable_auth(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	char *auth_type, *username, *password;
	int c, i, auth_type_flag = 0;
	char *enc_password;

	/* enable <-a auth-type> <-u username> */
	if (argc != 5) {
		usage(B_FALSE, cur_cmd);
	}

	while ((c = getopt(argc, argv, ":a:u:")) != -1) {
		switch (c) {
		case 'a':
			auth_type = strdup(optarg);
			break;
		case 'u':
			username = strdup(optarg);
			break;
		case ':':
			(void) fprintf(stderr, gettext("Option -%c "
			    "requires an operand\n"), optopt);
			usage(B_FALSE, cur_cmd);
			break;
		case '?':
			(void) fprintf(stderr, gettext("Unrecognized "
			    "option: -%c\n"), optopt);
			usage(B_FALSE, cur_cmd);
		}
	}

	if ((auth_type) && (username)) {
		if (ndmp_get_password(&password)) {
			(void) fprintf(stderr, gettext("Could not get correct "
			    "password, exiting..."));
			free(auth_type);
			free(username);
			exit(-1);
		}
	} else {
		(void) fprintf(stderr, gettext("%s or %s can not be blank"),
		    "'auth-type'", "'username'");
		free(auth_type);
		free(username);
		exit(-1);
	}

	if ((enc_password = ndmp_base64_encode(password)) == NULL) {
		(void) fprintf(stdout,
		    gettext("Could not encode password - %s\n"),
		    ndmp_strerror(ndmp_errno));
		free(auth_type);
		free(username);
		exit(-1);
	}

	for (i = 0; i < NAUTH; i++) {
		if (strncmp(auth_type, ndmp_auth_table[i].auth_type,
		    strlen(ndmp_auth_table[i].auth_type)) == 0) {
			auth_type_flag = 1;
			if ((ndmp_set_prop((char *)ndmp_auth_table[i].username,
			    username)) == -1) {
				(void) fprintf(stdout,
				    gettext("Could not set username - %s\n"),
				    ndmp_strerror(ndmp_errno));
				continue;
			}
			if ((ndmp_set_prop((char *)ndmp_auth_table[i].password,
			    enc_password)) == -1) {
				(void) fprintf(stdout,
				    gettext("Could not set password - %s\n"),
				    ndmp_strerror(ndmp_errno));
				continue;
			}
			if (!ndmp_door_status() &&
			    (ndmp_service_refresh()) == -1) {
				(void) fprintf(stdout,
				    gettext("Could not refesh ndmpd service "
				    "properties\n"));
			}
		}
	}
	free(auth_type);
	free(username);
	free(enc_password);

	if (!auth_type_flag)
		usage(B_FALSE, cur_cmd);

	return (0);
}

static int
ndmp_disable_auth(int argc, char **argv, ndmp_command_t *cur_cmd)
{
	char *auth_type;
	int c, i, auth_type_flag = 0;

	/* disable <-a auth-type> */
	if (argc != 3) {
		usage(B_FALSE, cur_cmd);
	}

	while ((c = getopt(argc, argv, ":a:")) != -1) {
		switch (c) {
		case 'a':
			auth_type = strdup(optarg);
			break;
		case ':':
			(void) fprintf(stderr, gettext("Option -%c "
			    "requires an operand\n"), optopt);
			break;
		case '?':
			(void) fprintf(stderr, gettext("Unrecognized "
			    "option: -%c\n"), optopt);
		}
	}
	for (i = 0; i < NAUTH; i++) {
		if (strncmp(auth_type, ndmp_auth_table[i].auth_type,
		    strlen(ndmp_auth_table[i].auth_type)) == 0) {
			auth_type_flag = 1;
			if ((ndmp_set_prop((char *)ndmp_auth_table[i].username,
			    "")) == -1) {
				(void) fprintf(stdout,
				    gettext("Could not clear username - %s\n"),
				    ndmp_strerror(ndmp_errno));
				continue;
			}
			if ((ndmp_set_prop((char *)ndmp_auth_table[i].password,
			    "")) == -1) {
				(void) fprintf(stdout,
				    gettext("Could not clear password - %s\n"),
				    ndmp_strerror(ndmp_errno));
				continue;
			}
			if (!ndmp_door_status() &&
			    (ndmp_service_refresh()) == -1) {
				(void) fprintf(stdout, gettext("Could not "
				    "refesh ndmpd service properties\n"));
			}
		}
	}
	free(auth_type);

	if (!auth_type_flag)
		usage(B_FALSE, cur_cmd);

	return (0);
}

int
main(int argc, char **argv)
{
	int ret;
	int i;
	char *cmdname;
	ndmp_command_t	*current_command = NULL;

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	opterr = 0;

	/* Make sure the user has specified some command. */
	if (argc < 2) {
		(void) fprintf(stderr, gettext("Missing command.\n"));
		usage(B_FALSE, current_command);
	}

	cmdname = argv[1];

	/*
	 * Special case '-?'
	 */
	if (strcmp(cmdname, "-?") == 0)
		usage(B_TRUE, current_command);

	/*
	 * Run the appropriate sub-command.
	 */
	for (i = 0; i < NCOMMAND; i++) {
		if (strcmp(cmdname, command_table[i].nc_name) == 0) {
			current_command = &command_table[i];
			ret = command_table[i].func(argc - 1, argv + 1,
			    current_command);
			break;
		}
	}

	if (i == NCOMMAND) {
		(void) fprintf(stderr, gettext("Unrecognized "
		    "command '%s'\n"), cmdname);
		usage(B_FALSE, current_command);
	}

	return (ret);
}