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