1 /*
2 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * BSD 3 Clause License
7 *
8 * Copyright (c) 2007, The Storage Networking Industry Association.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * - Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * - Neither the name of The Storage Networking Industry Association (SNIA)
22 * nor the names of its contributors may be used to endorse or promote
23 * products derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 #include <assert.h>
39 #include <ctype.h>
40 #include <libgen.h>
41 #include <libintl.h>
42 #include <locale.h>
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <strings.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #include <door.h>
51 #include <sys/mman.h>
52 #include <libndmp.h>
53 #include "ndmpadm.h"
54
55 typedef enum {
56 HELP_GET_CONFIG,
57 HELP_SET_CONFIG,
58 HELP_SHOW_DEVICES,
59 HELP_SHOW_SESSIONS,
60 HELP_KILL_SESSIONS,
61 HELP_ENABLE_AUTH,
62 HELP_DISABLE_AUTH
63 } ndmp_help_t;
64
65 typedef struct ndmp_command {
66 const char *nc_name;
67 int (*func)(int argc, char **argv,
68 struct ndmp_command *cur_cmd);
69 ndmp_help_t nc_usage;
70 } ndmp_command_t;
71
72 static int ndmp_get_config(int, char **, ndmp_command_t *);
73 static int ndmp_set_config(int, char **, ndmp_command_t *);
74 static int ndmp_show_devices(int, char **, ndmp_command_t *);
75 static int ndmp_show_sessions(int, char **, ndmp_command_t *);
76 static int ndmp_kill_sessions(int, char **, ndmp_command_t *);
77 static int ndmp_enable_auth(int, char **, ndmp_command_t *);
78 static int ndmp_disable_auth(int, char **, ndmp_command_t *);
79 static void ndmp_get_config_process(char *);
80 static void ndmp_set_config_process(char *arg);
81 static int ndmp_get_password(char **);
82
83 static ndmp_command_t command_table[] = {
84 { "get", ndmp_get_config, HELP_GET_CONFIG },
85 { "set", ndmp_set_config, HELP_SET_CONFIG },
86 { "show-devices", ndmp_show_devices, HELP_SHOW_DEVICES },
87 { "show-sessions", ndmp_show_sessions, HELP_SHOW_SESSIONS },
88 { "kill-sessions", ndmp_kill_sessions, HELP_KILL_SESSIONS },
89 { "enable", ndmp_enable_auth, HELP_ENABLE_AUTH },
90 { "disable", ndmp_disable_auth, HELP_DISABLE_AUTH }
91 };
92
93 #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
94
95 static char *prop_table[] = {
96 "debug-path",
97 "dump-pathnode",
98 "tar-pathnode",
99 "ignore-ctime",
100 "token-maxseq",
101 "version",
102 "dar-support",
103 "tcp-port",
104 "backup-quarantine",
105 "restore-quarantine",
106 "overwrite-quarantine",
107 "zfs-force-override",
108 "drive-type"
109 };
110
111 #define NDMPADM_NPROP (sizeof (prop_table) / sizeof (prop_table[0]))
112
113 typedef struct ndmp_auth {
114 const char *auth_type;
115 const char *username;
116 const char *password;
117 } ndmp_auth_t;
118
119 static ndmp_auth_t ndmp_auth_table[] = {
120 { "cram-md5", "cram-md5-username", "cram-md5-password" },
121 { "cleartext", "cleartext-username", "cleartext-password" }
122 };
123 #define NAUTH (sizeof (ndmp_auth_table) / sizeof (ndmp_auth_table[0]))
124 #define NDMP_PASSWORD_RETRIES 3
125
126 #if !defined(TEXT_DOMAIN)
127 #define TEXT_DOMAIN "SYS_TEST"
128 #endif
129
130 static const char *
get_usage(ndmp_help_t idx)131 get_usage(ndmp_help_t idx)
132 {
133 switch (idx) {
134 case HELP_SET_CONFIG:
135 return ("\tset [-p] <property=value> [[-p] property=value] "
136 "...\n");
137 case HELP_GET_CONFIG:
138 return ("\tget [-p] [property] [[-p] property] ...\n");
139 case HELP_SHOW_DEVICES:
140 return ("\tshow-devices\n");
141 case HELP_SHOW_SESSIONS:
142 return ("\tshow-sessions [-i tape,scsi,data,mover] [id] ...\n");
143 case HELP_KILL_SESSIONS:
144 return ("\tkill-sessions <id ...>\n");
145 case HELP_ENABLE_AUTH:
146 return ("\tenable <-a auth-type> <-u username>\n");
147 case HELP_DISABLE_AUTH:
148 return ("\tdisable <-a auth-type>\n");
149 }
150
151 return (NULL);
152 }
153
154 /*
155 * Display usage message. If we're inside a command, display only the usage for
156 * that command. Otherwise, iterate over the entire command table and display
157 * a complete usage message.
158 */
159 static void
usage(boolean_t requested,ndmp_command_t * current_command)160 usage(boolean_t requested, ndmp_command_t *current_command)
161 {
162 int i;
163 boolean_t show_properties = B_FALSE;
164 FILE *fp = requested ? stdout : stderr;
165
166 if (current_command == NULL) {
167 (void) fprintf(fp,
168 gettext("Usage: ndmpadm subcommand args ...\n"));
169 (void) fprintf(fp,
170 gettext("where 'command' is one of the following:\n\n"));
171
172 for (i = 0; i < NCOMMAND; i++) {
173 (void) fprintf(fp, "%s",
174 get_usage(command_table[i].nc_usage));
175 }
176 (void) fprintf(fp, gettext("\t\twhere %s can be either "
177 "%s or %s\n"), "'auth-type'", "'cram-md5'", "'cleartext'");
178 } else {
179 (void) fprintf(fp, gettext("Usage:\n"));
180 (void) fprintf(fp, "%s", get_usage(current_command->nc_usage));
181 if ((current_command->nc_usage == HELP_ENABLE_AUTH) ||
182 (current_command->nc_usage == HELP_DISABLE_AUTH))
183 (void) fprintf(fp, gettext("\t\twhere %s can be either "
184 "%s or %s\n"),
185 "'auth-type'", "'cram-md5'", "'cleartext'");
186 }
187
188 if (current_command != NULL &&
189 (strcmp(current_command->nc_name, "set") == 0))
190 show_properties = B_TRUE;
191
192 if (show_properties) {
193 (void) fprintf(fp,
194 gettext("\nThe following properties are supported:\n"));
195
196 (void) fprintf(fp, gettext("\n\tPROPERTY"));
197 (void) fprintf(fp, "\n\t%s", "-------------");
198 for (i = 0; i < NDMPADM_NPROP; i++)
199 (void) fprintf(fp, "\n\t%s", prop_table[i]);
200 (void) fprintf(fp, "\n");
201 }
202
203 exit(requested ? 0 : 2);
204 }
205
206 /*ARGSUSED*/
207 static int
ndmp_get_config(int argc,char ** argv,ndmp_command_t * cur_cmd)208 ndmp_get_config(int argc, char **argv, ndmp_command_t *cur_cmd)
209 {
210 char *propval;
211 int i, c;
212
213 if (argc == 1) {
214 /*
215 * Get all the properties and variables ndmpadm is allowed
216 * to see.
217 */
218 for (i = 0; i < NDMPADM_NPROP; i++) {
219 if (ndmp_get_prop(prop_table[i], &propval)) {
220 (void) fprintf(stdout, "\t%s=\n",
221 prop_table[i]);
222 } else {
223 (void) fprintf(stdout, "\t%s=%s\n",
224 prop_table[i], propval);
225 free(propval);
226 }
227 }
228 } else if (argc > 1) {
229 while ((c = getopt(argc, argv, ":p:")) != -1) {
230 switch (c) {
231 case 'p':
232 ndmp_get_config_process(optarg);
233 break;
234 case ':':
235 (void) fprintf(stderr, gettext("Option -%c "
236 "requires an operand\n"), optopt);
237 break;
238 case '?':
239 (void) fprintf(stderr, gettext("Unrecognized "
240 "option: -%c\n"), optopt);
241 }
242 }
243 /*
244 * optind is initialized to 1 if the -p option is not used,
245 * otherwise index to argv.
246 */
247 argc -= optind;
248 argv += optind;
249
250 for (i = 0; i < argc; i++) {
251 if (strncmp(argv[i], "-p", 2) == 0)
252 continue;
253
254 ndmp_get_config_process(argv[i]);
255 }
256 }
257 return (0);
258 }
259
260 static void
ndmp_get_config_process(char * arg)261 ndmp_get_config_process(char *arg)
262 {
263 int j;
264 char *propval;
265
266 for (j = 0; j < NDMPADM_NPROP; j++) {
267 if (strcmp(arg, prop_table[j]) == 0) {
268 if (ndmp_get_prop(arg, &propval)) {
269 (void) fprintf(stdout, "\t%s=\n", arg);
270 } else {
271 (void) fprintf(stdout, "\t%s=%s\n",
272 arg, propval);
273 free(propval);
274 }
275 break;
276 }
277 }
278 if (j == NDMPADM_NPROP) {
279 (void) fprintf(stdout, gettext("\t%s is invalid property "
280 "or variable\n"), arg);
281 }
282 }
283
284 /*ARGSUSED*/
285 static int
ndmp_set_config(int argc,char ** argv,ndmp_command_t * cur_cmd)286 ndmp_set_config(int argc, char **argv, ndmp_command_t *cur_cmd)
287 {
288 int c, i;
289
290 if (argc < 2) {
291 (void) fprintf(stderr, gettext("Missing property=value "
292 "argument\n"));
293 usage(B_FALSE, cur_cmd);
294 }
295 while ((c = getopt(argc, argv, ":p:")) != -1) {
296 switch (c) {
297 case 'p':
298 ndmp_set_config_process(optarg);
299 break;
300 case ':':
301 (void) fprintf(stderr, gettext("Option -%c "
302 "requires an operand\n"), optopt);
303 break;
304 case '?':
305 (void) fprintf(stderr, gettext("Unrecognized "
306 "option: -%c\n"), optopt);
307 }
308 }
309 /*
310 * optind is initialized to 1 if the -p option is not used,
311 * otherwise index to argv.
312 */
313 argc -= optind;
314 argv += optind;
315
316 for (i = 0; i < argc; i++) {
317 if (strncmp(argv[i], "-p", 2) == 0)
318 continue;
319
320 ndmp_set_config_process(argv[i]);
321 }
322 return (0);
323 }
324
325 static void
ndmp_set_config_process(char * propname)326 ndmp_set_config_process(char *propname)
327 {
328 char *propvalue;
329 int ret, j;
330
331 if ((propvalue = strchr(propname, '=')) == NULL) {
332 (void) fprintf(stderr, gettext("Missing value in "
333 "property=value argument for %s\n"), propname);
334 return;
335 }
336 *propvalue = '\0';
337 propvalue++;
338
339 if (*propname == '\0') {
340 (void) fprintf(stderr, gettext("Missing property in "
341 "property=value argument for %s\n"), propname);
342 return;
343 }
344 for (j = 0; j < NDMPADM_NPROP; j++) {
345 if (strcmp(propname, prop_table[j]) == 0)
346 break;
347 }
348 if (j == NDMPADM_NPROP) {
349 (void) fprintf(stdout, gettext("%s is invalid property or "
350 "variable\n"), propname);
351 return;
352 }
353 ret = ndmp_set_prop(propname, propvalue);
354 if (ret != -1) {
355 if (!ndmp_door_status()) {
356 if (ndmp_service_refresh() == -1)
357 (void) fprintf(stdout, gettext("Could not "
358 "refesh property of service ndmpd\n"));
359 }
360 } else {
361 (void) fprintf(stdout, gettext("Could not set property for "
362 "%s - %s\n"), propname, ndmp_strerror(ndmp_errno));
363 }
364 }
365
366 /*ARGSUSED*/
367 static int
ndmp_show_devices(int argc,char ** argv,ndmp_command_t * cur_cmd)368 ndmp_show_devices(int argc, char **argv, ndmp_command_t *cur_cmd)
369 {
370 int ret;
371 ndmp_devinfo_t *dip = NULL;
372 size_t size;
373
374 if (ndmp_door_status()) {
375 (void) fprintf(stdout,
376 gettext("Service ndmpd not running\n"));
377 return (-1);
378 }
379
380 ret = ndmp_get_devinfo(&dip, &size);
381
382 if (ret == -1)
383 (void) fprintf(stdout,
384 gettext("Could not get device information\n"));
385 else
386 ndmp_devinfo_print(dip, size);
387
388 ndmp_get_devinfo_free(dip, size);
389 return (0);
390 }
391
392 static int
ndmp_show_sessions(int argc,char ** argv,ndmp_command_t * cur_cmd)393 ndmp_show_sessions(int argc, char **argv, ndmp_command_t *cur_cmd)
394 {
395 ndmp_session_info_t *sinfo = NULL;
396 ndmp_session_info_t *sp = NULL;
397 uint_t num;
398 int c, ret, i, j;
399 int statarg = 0;
400 char *value;
401 char *type_subopts[] = { "tape", "scsi", "data", "mover", NULL };
402
403 if (ndmp_door_status()) {
404 (void) fprintf(stdout,
405 gettext("Service ndmpd not running\n"));
406 return (-1);
407 }
408
409 /* Detail output if no option is specified */
410 if (argc == 1) {
411 statarg = NDMP_CAT_ALL;
412 } else {
413 statarg = 0;
414 while ((c = getopt(argc, argv, ":i:")) != -1) {
415 switch (c) {
416 case 'i':
417 while (*optarg != '\0') {
418 switch (getsubopt(&optarg, type_subopts,
419 &value)) {
420 case 0:
421 statarg |= NDMP_CAT_TAPE;
422 break;
423 case 1:
424 statarg |= NDMP_CAT_SCSI;
425 break;
426 case 2:
427 statarg |= NDMP_CAT_DATA;
428 break;
429 case 3:
430 statarg |= NDMP_CAT_MOVER;
431 break;
432 default:
433 (void) fprintf(stderr,
434 gettext("Invalid object "
435 "type '%s'\n"), value);
436 usage(B_FALSE, cur_cmd);
437 }
438 }
439 break;
440 case ':':
441 (void) fprintf(stderr,
442 gettext("Missing argument for "
443 "'%c' option\n"), optopt);
444 usage(B_FALSE, cur_cmd);
445 break;
446 case '?':
447 (void) fprintf(stderr,
448 gettext("Invalid option '%c'\n"), optopt);
449 usage(B_FALSE, cur_cmd);
450 }
451 }
452 /* if -i and its argument are not specified, display all */
453 if (statarg == 0)
454 statarg = NDMP_CAT_ALL;
455 }
456 /*
457 * optind is initialized to 1 if the -i option is not used, otherwise
458 * index to argv.
459 */
460 argc -= optind;
461 argv += optind;
462
463 ret = ndmp_get_session_info(&sinfo, &num);
464 if (ret == -1) {
465 (void) fprintf(stdout,
466 gettext("Could not get session information\n"));
467 } else {
468 if (argc == 0) {
469 ndmp_session_all_print(statarg, sinfo, num);
470 } else {
471 for (i = 0; i < argc; i++) {
472 sp = sinfo;
473 for (j = 0; j < num; j++, sp++) {
474 if (sp->nsi_sid == atoi(argv[i])) {
475 ndmp_session_print(statarg, sp);
476 (void) fprintf(stdout, "\n");
477 break;
478 }
479 }
480 if (j == num) {
481 (void) fprintf(stdout,
482 gettext("Session %d not "
483 "found\n"), atoi(argv[i]));
484 }
485 }
486 }
487 ndmp_get_session_info_free(sinfo, num);
488 }
489 return (0);
490 }
491
492 /*ARGSUSED*/
493 static int
ndmp_kill_sessions(int argc,char ** argv,ndmp_command_t * cur_cmd)494 ndmp_kill_sessions(int argc, char **argv, ndmp_command_t *cur_cmd)
495 {
496 int ret, i;
497
498 if (ndmp_door_status()) {
499 (void) fprintf(stdout,
500 gettext("Service ndmpd not running.\n"));
501 return (-1);
502 }
503
504 /* If no arg is specified, print the usage and exit */
505 if (argc == 1)
506 usage(B_FALSE, cur_cmd);
507
508 for (i = 1; i < argc; i++) {
509 if (atoi(argv[i]) > 0) {
510 ret = ndmp_terminate_session(atoi(argv[i]));
511 } else {
512 (void) fprintf(stderr,
513 gettext("Invalid argument %s\n"), argv[i]);
514 continue;
515 }
516 if (ret == -1)
517 (void) fprintf(stdout,
518 gettext("Session id %d not found.\n"),
519 atoi(argv[i]));
520 }
521 return (0);
522 }
523
524 static int
ndmp_get_password(char ** password)525 ndmp_get_password(char **password)
526 {
527 char *pw1, pw2[257];
528 int i;
529
530 for (i = 0; i < NDMP_PASSWORD_RETRIES; i++) {
531 /*
532 * getpassphrase use the same buffer to return password, so
533 * copy the result in different buffer, before calling the
534 * getpassphrase again.
535 */
536 if ((pw1 =
537 getpassphrase(gettext("Enter new password: "))) != NULL) {
538 (void) strlcpy(pw2, pw1, sizeof (pw2));
539 if ((pw1 =
540 getpassphrase(gettext("Re-enter password: ")))
541 != NULL) {
542 if (strncmp(pw1, pw2, strlen(pw1)) == 0) {
543 *password = pw1;
544 return (0);
545 } else {
546 (void) fprintf(stderr,
547 gettext("Both password did not "
548 "match.\n"));
549 }
550 }
551 }
552 }
553 return (-1);
554 }
555
556 static int
ndmp_enable_auth(int argc,char ** argv,ndmp_command_t * cur_cmd)557 ndmp_enable_auth(int argc, char **argv, ndmp_command_t *cur_cmd)
558 {
559 char *auth_type, *username, *password;
560 int c, i, auth_type_flag = 0;
561 char *enc_password;
562
563 /* enable <-a auth-type> <-u username> */
564 if (argc != 5) {
565 usage(B_FALSE, cur_cmd);
566 }
567
568 while ((c = getopt(argc, argv, ":a:u:")) != -1) {
569 switch (c) {
570 case 'a':
571 auth_type = strdup(optarg);
572 break;
573 case 'u':
574 username = strdup(optarg);
575 break;
576 case ':':
577 (void) fprintf(stderr, gettext("Option -%c "
578 "requires an operand\n"), optopt);
579 usage(B_FALSE, cur_cmd);
580 break;
581 case '?':
582 (void) fprintf(stderr, gettext("Unrecognized "
583 "option: -%c\n"), optopt);
584 usage(B_FALSE, cur_cmd);
585 }
586 }
587
588 if ((auth_type) && (username)) {
589 if (ndmp_get_password(&password)) {
590 (void) fprintf(stderr, gettext("Could not get correct "
591 "password, exiting..."));
592 free(auth_type);
593 free(username);
594 exit(-1);
595 }
596 } else {
597 (void) fprintf(stderr, gettext("%s or %s can not be blank"),
598 "'auth-type'", "'username'");
599 free(auth_type);
600 free(username);
601 exit(-1);
602 }
603
604 if ((enc_password = ndmp_base64_encode(password)) == NULL) {
605 (void) fprintf(stdout,
606 gettext("Could not encode password - %s\n"),
607 ndmp_strerror(ndmp_errno));
608 free(auth_type);
609 free(username);
610 exit(-1);
611 }
612
613 for (i = 0; i < NAUTH; i++) {
614 if (strncmp(auth_type, ndmp_auth_table[i].auth_type,
615 strlen(ndmp_auth_table[i].auth_type)) == 0) {
616 auth_type_flag = 1;
617 if ((ndmp_set_prop((char *)ndmp_auth_table[i].username,
618 username)) == -1) {
619 (void) fprintf(stdout,
620 gettext("Could not set username - %s\n"),
621 ndmp_strerror(ndmp_errno));
622 continue;
623 }
624 if ((ndmp_set_prop((char *)ndmp_auth_table[i].password,
625 enc_password)) == -1) {
626 (void) fprintf(stdout,
627 gettext("Could not set password - %s\n"),
628 ndmp_strerror(ndmp_errno));
629 continue;
630 }
631 if (!ndmp_door_status() &&
632 (ndmp_service_refresh()) == -1) {
633 (void) fprintf(stdout,
634 gettext("Could not refesh ndmpd service "
635 "properties\n"));
636 }
637 }
638 }
639 free(auth_type);
640 free(username);
641 free(enc_password);
642
643 if (!auth_type_flag)
644 usage(B_FALSE, cur_cmd);
645
646 return (0);
647 }
648
649 static int
ndmp_disable_auth(int argc,char ** argv,ndmp_command_t * cur_cmd)650 ndmp_disable_auth(int argc, char **argv, ndmp_command_t *cur_cmd)
651 {
652 char *auth_type;
653 int c, i, auth_type_flag = 0;
654
655 /* disable <-a auth-type> */
656 if (argc != 3) {
657 usage(B_FALSE, cur_cmd);
658 }
659
660 while ((c = getopt(argc, argv, ":a:")) != -1) {
661 switch (c) {
662 case 'a':
663 auth_type = strdup(optarg);
664 break;
665 case ':':
666 (void) fprintf(stderr, gettext("Option -%c "
667 "requires an operand\n"), optopt);
668 break;
669 case '?':
670 (void) fprintf(stderr, gettext("Unrecognized "
671 "option: -%c\n"), optopt);
672 }
673 }
674 for (i = 0; i < NAUTH; i++) {
675 if (strncmp(auth_type, ndmp_auth_table[i].auth_type,
676 strlen(ndmp_auth_table[i].auth_type)) == 0) {
677 auth_type_flag = 1;
678 if ((ndmp_set_prop((char *)ndmp_auth_table[i].username,
679 "")) == -1) {
680 (void) fprintf(stdout,
681 gettext("Could not clear username - %s\n"),
682 ndmp_strerror(ndmp_errno));
683 continue;
684 }
685 if ((ndmp_set_prop((char *)ndmp_auth_table[i].password,
686 "")) == -1) {
687 (void) fprintf(stdout,
688 gettext("Could not clear password - %s\n"),
689 ndmp_strerror(ndmp_errno));
690 continue;
691 }
692 if (!ndmp_door_status() &&
693 (ndmp_service_refresh()) == -1) {
694 (void) fprintf(stdout, gettext("Could not "
695 "refesh ndmpd service properties\n"));
696 }
697 }
698 }
699 free(auth_type);
700
701 if (!auth_type_flag)
702 usage(B_FALSE, cur_cmd);
703
704 return (0);
705 }
706
707 int
main(int argc,char ** argv)708 main(int argc, char **argv)
709 {
710 int ret;
711 int i;
712 char *cmdname;
713 ndmp_command_t *current_command = NULL;
714
715 (void) setlocale(LC_ALL, "");
716 (void) textdomain(TEXT_DOMAIN);
717
718 opterr = 0;
719
720 /* Make sure the user has specified some command. */
721 if (argc < 2) {
722 (void) fprintf(stderr, gettext("Missing command.\n"));
723 usage(B_FALSE, current_command);
724 }
725
726 cmdname = argv[1];
727
728 /*
729 * Special case '-?'
730 */
731 if (strcmp(cmdname, "-?") == 0)
732 usage(B_TRUE, current_command);
733
734 /*
735 * Run the appropriate sub-command.
736 */
737 for (i = 0; i < NCOMMAND; i++) {
738 if (strcmp(cmdname, command_table[i].nc_name) == 0) {
739 current_command = &command_table[i];
740 ret = command_table[i].func(argc - 1, argv + 1,
741 current_command);
742 break;
743 }
744 }
745
746 if (i == NCOMMAND) {
747 (void) fprintf(stderr, gettext("Unrecognized "
748 "command '%s'\n"), cmdname);
749 usage(B_FALSE, current_command);
750 }
751
752 return (ret);
753 }
754