libbe: replace comment in be_has_grub()
[unleashed.git] / usr / src / cmd / sbdadm / sbdadm.c
blobc2323e11b19f84d425e0ac93c16f478ede5d7108
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <libintl.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <getopt.h>
37 #include <strings.h>
38 #include <ctype.h>
39 #include <libnvpair.h>
40 #include <locale.h>
42 #include <cmdparse.h>
43 #include <sys/stmf_defines.h>
44 #include <libstmf.h>
45 #include <sys/stmf_sbd_ioctl.h>
47 #define MAX_LU_LIST 8192
48 #define LU_LIST_MAX_RETRIES 3
49 #define GUID_INPUT 32
51 #define VERSION_STRING_MAJOR "1"
52 #define VERSION_STRING_MINOR "0"
53 #define VERSION_STRING_MAX_LEN 10
56 char *cmdName;
58 static char *getExecBasename(char *);
59 int delete_lu(int argc, char *argv[], cmdOptions_t *options,
60 void *callData);
61 int create_lu(int argc, char *argv[], cmdOptions_t *options, void *callData);
62 int list_lus(int argc, char *argv[], cmdOptions_t *options, void *callData);
63 int modify_lu(int argc, char *argv[], cmdOptions_t *options, void *callData);
64 int import_lu(int argc, char *argv[], cmdOptions_t *options, void *callData);
65 static int callModify(char *, stmfGuid *, uint32_t, const char *, const char *);
66 int print_lu_attr(stmfGuid *);
67 void print_guid(uint8_t *g, FILE *f);
68 void print_attr_header();
70 optionTbl_t options[] = {
71 { "disk-size", required_argument, 's',
72 "Size with <none>/k/m/g/t/p/e modifier" },
73 { "keep-views", no_arg, 'k',
74 "Dont delete view entries related to the LU" },
75 { NULL, 0, 0 }
78 subCommandProps_t subCommands[] = {
79 { "create-lu", create_lu, "s", NULL, NULL,
80 OPERAND_MANDATORY_SINGLE,
81 "Full path of the file to initialize" },
82 { "delete-lu", delete_lu, "k", NULL, NULL,
83 OPERAND_MANDATORY_SINGLE, "GUID of the LU to deregister" },
84 { "import-lu", import_lu, NULL, NULL, NULL,
85 OPERAND_MANDATORY_SINGLE, "filename of the LU to import" },
86 { "list-lu", list_lus, NULL, NULL, NULL,
87 OPERAND_NONE, "List all the exported LUs" },
88 { "modify-lu", modify_lu, "s", "s", NULL,
89 OPERAND_MANDATORY_SINGLE,
90 "Full path of the LU or GUID of a registered LU" },
91 { NULL, 0, 0, NULL, 0, NULL}
94 /*ARGSUSED*/
95 int
96 create_lu(int argc, char *operands[], cmdOptions_t *options, void *callData)
98 luResource hdl = NULL;
99 int ret = 0;
100 stmfGuid createdGuid;
102 ret = stmfCreateLuResource(STMF_DISK, &hdl);
104 if (ret != STMF_STATUS_SUCCESS) {
105 (void) fprintf(stderr, "%s: %s\n",
106 cmdName, gettext("Failure to create lu resource\n"));
107 return (1);
110 for (; options->optval; options++) {
111 switch (options->optval) {
112 case 's':
113 ret = stmfSetLuProp(hdl, STMF_LU_PROP_SIZE,
114 options->optarg);
115 if (ret != STMF_STATUS_SUCCESS) {
116 (void) fprintf(stderr, "%s: %c: %s\n",
117 cmdName, options->optval,
118 gettext("size param invalid"));
119 (void) stmfFreeLuResource(hdl);
120 return (1);
122 break;
123 default:
124 (void) fprintf(stderr, "%s: %c: %s\n",
125 cmdName, options->optval,
126 gettext("unknown option"));
127 return (1);
131 ret = stmfSetLuProp(hdl, STMF_LU_PROP_FILENAME, operands[0]);
133 if (ret != STMF_STATUS_SUCCESS) {
134 (void) fprintf(stderr, "%s: %s\n",
135 cmdName, gettext("could not set filename"));
136 return (1);
139 ret = stmfCreateLu(hdl, &createdGuid);
140 switch (ret) {
141 case STMF_STATUS_SUCCESS:
142 break;
143 case STMF_ERROR_BUSY:
144 case STMF_ERROR_LU_BUSY:
145 (void) fprintf(stderr, "%s: %s\n", cmdName,
146 gettext("resource busy"));
147 ret++;
148 break;
149 case STMF_ERROR_PERM:
150 (void) fprintf(stderr, "%s: %s\n", cmdName,
151 gettext("permission denied"));
152 ret++;
153 break;
154 case STMF_ERROR_FILE_IN_USE:
155 (void) fprintf(stderr, "%s: filename %s: %s\n", cmdName,
156 operands[0], gettext("in use"));
157 ret++;
158 break;
159 case STMF_ERROR_INVALID_BLKSIZE:
160 (void) fprintf(stderr, "%s: %s\n", cmdName,
161 gettext("invalid block size"));
162 ret++;
163 break;
164 case STMF_ERROR_GUID_IN_USE:
165 (void) fprintf(stderr, "%s: %s\n", cmdName,
166 gettext("guid in use"));
167 ret++;
168 break;
169 case STMF_ERROR_META_FILE_NAME:
170 (void) fprintf(stderr, "%s: %s\n", cmdName,
171 gettext("meta file error"));
172 ret++;
173 break;
174 case STMF_ERROR_DATA_FILE_NAME:
175 (void) fprintf(stderr, "%s: %s\n", cmdName,
176 gettext("data file error"));
177 ret++;
178 break;
179 case STMF_ERROR_SIZE_OUT_OF_RANGE:
180 (void) fprintf(stderr, "%s: %s\n", cmdName,
181 gettext("invalid size"));
182 ret++;
183 break;
184 case STMF_ERROR_META_CREATION:
185 (void) fprintf(stderr, "%s: %s\n", cmdName,
186 gettext("could not create meta file"));
187 ret++;
188 break;
189 default:
190 (void) fprintf(stderr, "%s: %s\n", cmdName,
191 gettext("unknown error"));
192 ret++;
193 break;
196 if (ret != STMF_STATUS_SUCCESS) {
197 goto done;
200 (void) printf("Created the following LU:\n");
201 print_attr_header();
202 ret = print_lu_attr(&createdGuid);
204 done:
205 (void) stmfFreeLuResource(hdl);
206 return (ret);
209 /*ARGSUSED*/
211 import_lu(int argc, char *operands[], cmdOptions_t *options, void *callData)
213 int ret = 0;
214 stmfGuid createdGuid;
216 ret = stmfImportLu(STMF_DISK, operands[0], &createdGuid);
217 switch (ret) {
218 case STMF_STATUS_SUCCESS:
219 break;
220 case STMF_ERROR_BUSY:
221 case STMF_ERROR_LU_BUSY:
222 (void) fprintf(stderr, "%s: %s\n", cmdName,
223 gettext("resource busy"));
224 ret++;
225 break;
226 case STMF_ERROR_PERM:
227 (void) fprintf(stderr, "%s: %s\n", cmdName,
228 gettext("permission denied"));
229 ret++;
230 break;
231 case STMF_ERROR_FILE_IN_USE:
232 (void) fprintf(stderr, "%s: filename %s: %s\n", cmdName,
233 operands[0], gettext("in use"));
234 ret++;
235 break;
236 case STMF_ERROR_GUID_IN_USE:
237 (void) fprintf(stderr, "%s: %s\n", cmdName,
238 gettext("guid in use"));
239 ret++;
240 break;
241 case STMF_ERROR_META_FILE_NAME:
242 (void) fprintf(stderr, "%s: %s\n", cmdName,
243 gettext("meta file error"));
244 ret++;
245 break;
246 case STMF_ERROR_DATA_FILE_NAME:
247 (void) fprintf(stderr, "%s: %s\n", cmdName,
248 gettext("data file error"));
249 ret++;
250 break;
251 case STMF_ERROR_SIZE_OUT_OF_RANGE:
252 (void) fprintf(stderr, "%s: %s\n", cmdName,
253 gettext("invalid size"));
254 ret++;
255 break;
256 case STMF_ERROR_META_CREATION:
257 (void) fprintf(stderr, "%s: %s\n", cmdName,
258 gettext("could not create meta file"));
259 ret++;
260 break;
261 default:
262 (void) fprintf(stderr, "%s: %s\n", cmdName,
263 gettext("unknown error"));
264 ret++;
265 break;
268 if (ret != STMF_STATUS_SUCCESS) {
269 goto done;
272 (void) printf("Imported the following LU:\n");
273 print_attr_header();
274 ret = print_lu_attr(&createdGuid);
276 done:
277 return (ret);
280 /*ARGSUSED*/
282 delete_lu(int operandLen, char *operands[], cmdOptions_t *options,
283 void *callData)
285 int i, j;
286 int ret = 0;
287 int stmfRet;
288 unsigned int inGuid[sizeof (stmfGuid)];
289 stmfGuid delGuid;
290 boolean_t keepViews = B_FALSE;
291 boolean_t viewEntriesRemoved = B_FALSE;
292 boolean_t noLunFound = B_FALSE;
293 boolean_t views = B_FALSE;
294 boolean_t notValidHexNumber = B_FALSE;
295 char sGuid[GUID_INPUT + 1];
296 stmfViewEntryList *viewEntryList = NULL;
298 for (; options->optval; options++) {
299 switch (options->optval) {
300 /* Keep views for logical unit */
301 case 'k':
302 keepViews = B_TRUE;
303 break;
304 default:
305 (void) fprintf(stderr, "%s: %c: %s\n",
306 cmdName, options->optval,
307 gettext("unknown option"));
308 return (1);
313 for (i = 0; i < operandLen; i++) {
314 for (j = 0; j < GUID_INPUT; j++) {
315 if (!isxdigit(operands[i][j])) {
316 notValidHexNumber = B_TRUE;
317 break;
319 sGuid[j] = tolower(operands[i][j]);
321 if ((notValidHexNumber == B_TRUE) ||
322 (strlen(operands[i]) != GUID_INPUT)) {
323 (void) fprintf(stderr, "%s: %s: %s%d%s\n",
324 cmdName, operands[i], gettext("must be "),
325 GUID_INPUT,
326 gettext(" hexadecimal digits long"));
327 notValidHexNumber = B_FALSE;
328 ret++;
329 continue;
332 sGuid[j] = 0;
334 (void) sscanf(sGuid,
335 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
336 &inGuid[0], &inGuid[1], &inGuid[2], &inGuid[3],
337 &inGuid[4], &inGuid[5], &inGuid[6], &inGuid[7],
338 &inGuid[8], &inGuid[9], &inGuid[10], &inGuid[11],
339 &inGuid[12], &inGuid[13], &inGuid[14], &inGuid[15]);
341 for (j = 0; j < sizeof (stmfGuid); j++) {
342 delGuid.guid[j] = inGuid[j];
345 stmfRet = stmfDeleteLu(&delGuid);
346 switch (stmfRet) {
347 case STMF_STATUS_SUCCESS:
348 break;
349 case STMF_ERROR_NOT_FOUND:
350 noLunFound = B_TRUE;
351 break;
352 case STMF_ERROR_BUSY:
353 (void) fprintf(stderr, "%s: %s\n", cmdName,
354 gettext("resource busy"));
355 ret++;
356 break;
357 case STMF_ERROR_PERM:
358 (void) fprintf(stderr, "%s: %s\n", cmdName,
359 gettext("permission denied"));
360 ret++;
361 break;
362 default:
363 (void) fprintf(stderr, "%s: %s\n", cmdName,
364 gettext("unknown error"));
365 ret++;
366 break;
369 if (!keepViews) {
370 stmfRet = stmfGetViewEntryList(&delGuid,
371 &viewEntryList);
372 if (stmfRet == STMF_STATUS_SUCCESS) {
373 for (j = 0; j < viewEntryList->cnt; j++) {
374 (void) stmfRemoveViewEntry(&delGuid,
375 viewEntryList->ve[j].veIndex);
377 /* check if viewEntryList is empty */
378 if (viewEntryList->cnt != 0)
379 viewEntriesRemoved = B_TRUE;
380 stmfFreeMemory(viewEntryList);
381 } else {
382 (void) fprintf(stderr, "%s: %s\n", cmdName,
383 gettext("unable to remove view entries\n"));
384 ret++;
387 if (keepViews) {
388 stmfRet = stmfGetViewEntryList(&delGuid,
389 &viewEntryList);
390 if (stmfRet == STMF_STATUS_SUCCESS) {
391 views = B_TRUE;
392 stmfFreeMemory(viewEntryList);
396 if ((!viewEntriesRemoved && noLunFound && !views) ||
397 (!views && keepViews && noLunFound)) {
398 (void) fprintf(stderr, "%s: %s: %s\n",
399 cmdName, sGuid,
400 gettext("not found"));
401 ret++;
403 noLunFound = viewEntriesRemoved = views = B_FALSE;
405 return (ret);
408 /*ARGSUSED*/
410 modify_lu(int operandLen, char *operands[], cmdOptions_t *options,
411 void *callData)
413 stmfGuid inGuid;
414 unsigned int guid[sizeof (stmfGuid)];
415 int ret = 0;
416 int i;
417 char *fname = NULL;
418 char sGuid[GUID_INPUT + 1];
419 boolean_t fnameUsed = B_FALSE;
421 if (operands[0][0] == '/') {
422 fnameUsed = B_TRUE;
423 fname = operands[0];
426 /* check input length */
427 if (!fnameUsed && strlen(operands[0]) != GUID_INPUT) {
428 (void) fprintf(stderr, "%s: %s: %s%d%s\n", cmdName, operands[0],
429 gettext("must be "), GUID_INPUT,
430 gettext(" hexadecimal digits"));
431 return (1);
434 if (!fnameUsed) {
435 /* convert to lower case for scan */
436 for (i = 0; i < 32; i++)
437 sGuid[i] = tolower(operands[0][i]);
438 sGuid[i] = 0;
439 (void) sscanf(sGuid,
440 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
441 &guid[0], &guid[1], &guid[2], &guid[3], &guid[4], &guid[5],
442 &guid[6], &guid[7], &guid[8], &guid[9], &guid[10],
443 &guid[11], &guid[12], &guid[13], &guid[14], &guid[15]);
445 for (i = 0; i < sizeof (stmfGuid); i++) {
446 inGuid.guid[i] = guid[i];
450 for (; options->optval; options++) {
451 switch (options->optval) {
452 case 's':
453 if (callModify(fname, &inGuid,
454 STMF_LU_PROP_SIZE, options->optarg,
455 "size") != 0) {
456 return (1);
458 break;
459 default:
460 (void) fprintf(stderr, "%s: %c: %s\n",
461 cmdName, options->optval,
462 gettext("unknown option"));
463 return (1);
466 return (ret);
469 static int
470 callModify(char *fname, stmfGuid *luGuid, uint32_t prop, const char *propVal,
471 const char *propString)
473 int ret = 0;
474 int stmfRet = 0;
476 if (!fname) {
477 stmfRet = stmfModifyLu(luGuid, prop, propVal);
478 } else {
479 stmfRet = stmfModifyLuByFname(STMF_DISK, fname, prop,
480 propVal);
482 switch (stmfRet) {
483 case STMF_STATUS_SUCCESS:
484 break;
485 case STMF_ERROR_BUSY:
486 case STMF_ERROR_LU_BUSY:
487 (void) fprintf(stderr, "%s: %s\n", cmdName,
488 gettext("resource busy"));
489 ret++;
490 break;
491 case STMF_ERROR_PERM:
492 (void) fprintf(stderr, "%s: %s\n", cmdName,
493 gettext("permission denied"));
494 ret++;
495 break;
496 case STMF_ERROR_INVALID_BLKSIZE:
497 (void) fprintf(stderr, "%s: %s\n", cmdName,
498 gettext("invalid block size"));
499 ret++;
500 break;
501 case STMF_ERROR_GUID_IN_USE:
502 (void) fprintf(stderr, "%s: %s\n", cmdName,
503 gettext("guid in use"));
504 ret++;
505 break;
506 case STMF_ERROR_META_FILE_NAME:
507 (void) fprintf(stderr, "%s: %s\n", cmdName,
508 gettext("meta file error"));
509 ret++;
510 break;
511 case STMF_ERROR_DATA_FILE_NAME:
512 (void) fprintf(stderr, "%s: %s\n", cmdName,
513 gettext("data file error"));
514 ret++;
515 break;
516 case STMF_ERROR_FILE_SIZE_INVALID:
517 (void) fprintf(stderr, "%s: %s\n", cmdName,
518 gettext("file size invalid"));
519 ret++;
520 break;
521 case STMF_ERROR_SIZE_OUT_OF_RANGE:
522 (void) fprintf(stderr, "%s: %s\n", cmdName,
523 gettext("invalid size"));
524 ret++;
525 break;
526 case STMF_ERROR_META_CREATION:
527 (void) fprintf(stderr, "%s: %s\n", cmdName,
528 gettext("could not create meta file"));
529 ret++;
530 break;
531 default:
532 (void) fprintf(stderr, "%s: %s: %s: %d\n", cmdName,
533 gettext("could not set property"), propString,
534 stmfRet);
535 ret++;
536 break;
539 return (ret);
543 /*ARGSUSED*/
545 list_lus(int argc, char *argv[], cmdOptions_t *options, void *callData)
547 int stmfRet;
548 stmfGuidList *luList;
549 stmfLogicalUnitProperties luProps;
550 int sbdLuCnt = 0;
551 int i;
553 if ((stmfRet = stmfGetLogicalUnitList(&luList))
554 != STMF_STATUS_SUCCESS) {
555 switch (stmfRet) {
556 case STMF_ERROR_SERVICE_NOT_FOUND:
557 (void) fprintf(stderr, "%s: %s\n", cmdName,
558 gettext("STMF service not found"));
559 break;
560 case STMF_ERROR_BUSY:
561 (void) fprintf(stderr, "%s: %s\n", cmdName,
562 gettext("resource busy"));
563 break;
564 case STMF_ERROR_PERM:
565 (void) fprintf(stderr, "%s: %s\n", cmdName,
566 gettext("permission denied"));
567 break;
568 case STMF_ERROR_SERVICE_DATA_VERSION:
569 (void) fprintf(stderr, "%s: %s\n", cmdName,
570 gettext("STMF service version incorrect"));
571 break;
572 default:
573 (void) fprintf(stderr, "%s: %s\n", cmdName,
574 gettext("list failed"));
575 break;
577 return (1);
580 for (i = 0; i < luList->cnt; i++) {
581 stmfRet = stmfGetLogicalUnitProperties(&luList->guid[i],
582 &luProps);
583 if (stmfRet != STMF_STATUS_SUCCESS) {
584 (void) fprintf(stderr, "%s: %s\n", cmdName,
585 gettext("list failed"));
586 return (1);
588 if (strcmp(luProps.providerName, "sbd") == 0) {
589 sbdLuCnt++;
594 if (sbdLuCnt == 0)
595 return (0);
597 (void) printf("\nFound %d LU(s)\n", sbdLuCnt);
598 print_attr_header();
600 for (i = 0; i < luList->cnt; i++) {
601 stmfRet = stmfGetLogicalUnitProperties(&luList->guid[i],
602 &luProps);
603 if (stmfRet != STMF_STATUS_SUCCESS) {
604 (void) fprintf(stderr, "%s: %s\n", cmdName,
605 gettext("list failed"));
606 return (1);
608 if (strcmp(luProps.providerName, "sbd") == 0) {
609 (void) print_lu_attr(&luList->guid[i]);
612 return (0);
615 void
616 print_attr_header()
618 (void) printf("\n");
619 (void) printf(" GUID DATA SIZE "
620 " SOURCE\n");
621 (void) printf("-------------------------------- -------------------"
622 " ----------------\n");
625 void
626 print_guid(uint8_t *g, FILE *f)
628 int i;
630 for (i = 0; i < 16; i++) {
631 (void) fprintf(f, "%02x", g[i]);
636 print_lu_attr(stmfGuid *guid)
638 luResource hdl = NULL;
639 int stmfRet = 0;
640 int ret = 0;
641 char propVal[MAXPATHLEN];
642 size_t propValSize = sizeof (propVal);
644 if ((stmfRet = stmfGetLuResource(guid, &hdl)) != STMF_STATUS_SUCCESS) {
645 switch (stmfRet) {
646 case STMF_ERROR_BUSY:
647 (void) fprintf(stderr, "%s: %s\n", cmdName,
648 gettext("resource busy"));
649 break;
650 case STMF_ERROR_PERM:
651 (void) fprintf(stderr, "%s: %s\n", cmdName,
652 gettext("permission denied"));
653 break;
654 case STMF_ERROR_NOT_FOUND:
655 /* No error here */
656 return (0);
657 default:
658 (void) fprintf(stderr, "%s: %s\n", cmdName,
659 gettext("get extended properties failed"));
660 break;
662 return (1);
665 print_guid((uint8_t *)guid, stdout);
667 stmfRet = stmfGetLuProp(hdl, STMF_LU_PROP_SIZE, propVal,
668 &propValSize);
669 if (stmfRet == STMF_STATUS_SUCCESS) {
670 (void) printf(" %-19s ", propVal);
671 } else if (stmfRet == STMF_ERROR_NO_PROP) {
672 (void) printf("not set\n");
673 } else {
674 (void) printf("<error retrieving property>\n");
675 ret++;
678 stmfRet = stmfGetLuProp(hdl, STMF_LU_PROP_FILENAME, propVal,
679 &propValSize);
680 if (stmfRet == STMF_STATUS_SUCCESS) {
681 (void) printf("%s\n", propVal);
682 } else if (stmfRet == STMF_ERROR_NO_PROP) {
683 (void) printf("not set\n");
684 } else {
685 (void) printf("<error retrieving property>\n");
686 ret++;
690 (void) stmfFreeLuResource(hdl);
691 return (ret);
695 * input:
696 * execFullName - exec name of program (argv[0])
698 * copied from usr/src/cmd/zoneadm/zoneadm.c in OS/Net
699 * (changed name to lowerCamelCase to keep consistent with this file)
701 * Returns:
702 * command name portion of execFullName
704 static char *
705 getExecBasename(char *execFullname)
707 char *lastSlash, *execBasename;
709 /* guard against '/' at end of command invocation */
710 for (;;) {
711 lastSlash = strrchr(execFullname, '/');
712 if (lastSlash == NULL) {
713 execBasename = execFullname;
714 break;
715 } else {
716 execBasename = lastSlash + 1;
717 if (*execBasename == '\0') {
718 *lastSlash = '\0';
719 continue;
721 break;
724 return (execBasename);
727 main(int argc, char *argv[])
729 synTables_t synTables;
730 char versionString[VERSION_STRING_MAX_LEN];
731 int ret;
732 int funcRet;
733 void *subcommandArgs = NULL;
735 (void) setlocale(LC_ALL, "");
736 (void) textdomain(TEXT_DOMAIN);
737 /* set global command name */
738 cmdName = getExecBasename(argv[0]);
740 (void) snprintf(versionString, VERSION_STRING_MAX_LEN, "%s.%s",
741 VERSION_STRING_MAJOR, VERSION_STRING_MINOR);
742 synTables.versionString = versionString;
743 synTables.longOptionTbl = options;
744 synTables.subCommandPropsTbl = subCommands;
746 ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet);
747 if (ret != 0) {
748 return (ret);
751 return (funcRet);
752 } /* end main */