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