/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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", "zfs-force-override", "drive-type", "debug-mode" }; #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] [[-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 \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() != 0) (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(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(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()) != 0) { (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(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(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()) != 0) { (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); }