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