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 2010 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 boolean_t notValidHexNumber = B_FALSE; 294 char sGuid[GUID_INPUT + 1]; 295 stmfViewEntryList *viewEntryList = NULL; 296 297 for (; options->optval; options++) { 298 switch (options->optval) { 299 /* Keep views for logical unit */ 300 case 'k': 301 keepViews = B_TRUE; 302 break; 303 default: 304 (void) fprintf(stderr, "%s: %c: %s\n", 305 cmdName, options->optval, 306 gettext("unknown option")); 307 return (1); 308 } 309 } 310 311 312 for (i = 0; i < operandLen; i++) { 313 for (j = 0; j < GUID_INPUT; j++) { 314 if (!isxdigit(operands[i][j])) { 315 notValidHexNumber = B_TRUE; 316 break; 317 } 318 sGuid[j] = tolower(operands[i][j]); 319 } 320 if ((notValidHexNumber == B_TRUE) || 321 (strlen(operands[i]) != GUID_INPUT)) { 322 (void) fprintf(stderr, "%s: %s: %s%d%s\n", 323 cmdName, operands[i], gettext("must be "), 324 GUID_INPUT, 325 gettext(" hexadecimal digits long")); 326 notValidHexNumber = B_FALSE; 327 ret++; 328 continue; 329 } 330 331 sGuid[j] = 0; 332 333 (void) sscanf(sGuid, 334 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 335 &inGuid[0], &inGuid[1], &inGuid[2], &inGuid[3], 336 &inGuid[4], &inGuid[5], &inGuid[6], &inGuid[7], 337 &inGuid[8], &inGuid[9], &inGuid[10], &inGuid[11], 338 &inGuid[12], &inGuid[13], &inGuid[14], &inGuid[15]); 339 340 for (j = 0; j < sizeof (stmfGuid); j++) { 341 delGuid.guid[j] = inGuid[j]; 342 } 343 344 stmfRet = stmfDeleteLu(&delGuid); 345 switch (stmfRet) { 346 case STMF_STATUS_SUCCESS: 347 break; 348 case STMF_ERROR_NOT_FOUND: 349 noLunFound = B_TRUE; 350 break; 351 case STMF_ERROR_BUSY: 352 (void) fprintf(stderr, "%s: %s\n", cmdName, 353 gettext("resource busy")); 354 ret++; 355 break; 356 case STMF_ERROR_PERM: 357 (void) fprintf(stderr, "%s: %s\n", cmdName, 358 gettext("permission denied")); 359 ret++; 360 break; 361 default: 362 (void) fprintf(stderr, "%s: %s\n", cmdName, 363 gettext("unknown error")); 364 ret++; 365 break; 366 } 367 368 if (!keepViews) { 369 stmfRet = stmfGetViewEntryList(&delGuid, 370 &viewEntryList); 371 if (stmfRet == STMF_STATUS_SUCCESS) { 372 for (j = 0; j < viewEntryList->cnt; j++) { 373 (void) stmfRemoveViewEntry(&delGuid, 374 viewEntryList->ve[j].veIndex); 375 } 376 /* check if viewEntryList is empty */ 377 if (viewEntryList->cnt != 0) 378 viewEntriesRemoved = B_TRUE; 379 stmfFreeMemory(viewEntryList); 380 } else { 381 (void) fprintf(stderr, "%s: %s\n", cmdName, 382 gettext("unable to remove view entries\n")); 383 ret++; 384 } 385 } 386 if (keepViews) { 387 stmfRet = stmfGetViewEntryList(&delGuid, 388 &viewEntryList); 389 if (stmfRet == STMF_STATUS_SUCCESS) { 390 views = B_TRUE; 391 stmfFreeMemory(viewEntryList); 392 } 393 } 394 395 if ((!viewEntriesRemoved && noLunFound && !views) || 396 (!views && keepViews && noLunFound)) { 397 (void) fprintf(stderr, "%s: %s: %s\n", 398 cmdName, sGuid, 399 gettext("not found")); 400 ret++; 401 } 402 noLunFound = viewEntriesRemoved = views = B_FALSE; 403 } 404 return (ret); 405 } 406 407 /*ARGSUSED*/ 408 int 409 modify_lu(int operandLen, char *operands[], cmdOptions_t *options, 410 void *callData) 411 { 412 stmfGuid inGuid; 413 unsigned int guid[sizeof (stmfGuid)]; 414 int ret = 0; 415 int i; 416 char *fname = NULL; 417 char sGuid[GUID_INPUT + 1]; 418 boolean_t fnameUsed = B_FALSE; 419 420 if (operands[0][0] == '/') { 421 fnameUsed = B_TRUE; 422 fname = operands[0]; 423 } 424 425 /* check input length */ 426 if (!fnameUsed && strlen(operands[0]) != GUID_INPUT) { 427 (void) fprintf(stderr, "%s: %s: %s%d%s\n", cmdName, operands[0], 428 gettext("must be "), GUID_INPUT, 429 gettext(" hexadecimal digits")); 430 return (1); 431 } 432 433 if (!fnameUsed) { 434 /* convert to lower case for scan */ 435 for (i = 0; i < 32; i++) 436 sGuid[i] = tolower(operands[0][i]); 437 sGuid[i] = 0; 438 (void) sscanf(sGuid, 439 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 440 &guid[0], &guid[1], &guid[2], &guid[3], &guid[4], &guid[5], 441 &guid[6], &guid[7], &guid[8], &guid[9], &guid[10], 442 &guid[11], &guid[12], &guid[13], &guid[14], &guid[15]); 443 444 for (i = 0; i < sizeof (stmfGuid); i++) { 445 inGuid.guid[i] = guid[i]; 446 } 447 } 448 449 for (; options->optval; options++) { 450 switch (options->optval) { 451 case 's': 452 if (callModify(fname, &inGuid, 453 STMF_LU_PROP_SIZE, options->optarg, 454 "size") != 0) { 455 return (1); 456 } 457 break; 458 default: 459 (void) fprintf(stderr, "%s: %c: %s\n", 460 cmdName, options->optval, 461 gettext("unknown option")); 462 return (1); 463 } 464 } 465 return (ret); 466 } 467 468 static int 469 callModify(char *fname, stmfGuid *luGuid, uint32_t prop, const char *propVal, 470 const char *propString) 471 { 472 int ret = 0; 473 int stmfRet = 0; 474 475 if (!fname) { 476 stmfRet = stmfModifyLu(luGuid, prop, propVal); 477 } else { 478 stmfRet = stmfModifyLuByFname(STMF_DISK, fname, prop, 479 propVal); 480 } 481 switch (stmfRet) { 482 case STMF_STATUS_SUCCESS: 483 break; 484 case STMF_ERROR_BUSY: 485 case STMF_ERROR_LU_BUSY: 486 (void) fprintf(stderr, "%s: %s\n", cmdName, 487 gettext("resource busy")); 488 ret++; 489 break; 490 case STMF_ERROR_PERM: 491 (void) fprintf(stderr, "%s: %s\n", cmdName, 492 gettext("permission denied")); 493 ret++; 494 break; 495 case STMF_ERROR_INVALID_BLKSIZE: 496 (void) fprintf(stderr, "%s: %s\n", cmdName, 497 gettext("invalid block size")); 498 ret++; 499 break; 500 case STMF_ERROR_GUID_IN_USE: 501 (void) fprintf(stderr, "%s: %s\n", cmdName, 502 gettext("guid in use")); 503 ret++; 504 break; 505 case STMF_ERROR_META_FILE_NAME: 506 (void) fprintf(stderr, "%s: %s\n", cmdName, 507 gettext("meta file error")); 508 ret++; 509 break; 510 case STMF_ERROR_DATA_FILE_NAME: 511 (void) fprintf(stderr, "%s: %s\n", cmdName, 512 gettext("data file error")); 513 ret++; 514 break; 515 case STMF_ERROR_FILE_SIZE_INVALID: 516 (void) fprintf(stderr, "%s: %s\n", cmdName, 517 gettext("file size invalid")); 518 ret++; 519 break; 520 case STMF_ERROR_SIZE_OUT_OF_RANGE: 521 (void) fprintf(stderr, "%s: %s\n", cmdName, 522 gettext("invalid size")); 523 ret++; 524 break; 525 case STMF_ERROR_META_CREATION: 526 (void) fprintf(stderr, "%s: %s\n", cmdName, 527 gettext("could not create meta file")); 528 ret++; 529 break; 530 default: 531 (void) fprintf(stderr, "%s: %s: %s: %d\n", cmdName, 532 gettext("could not set property"), propString, 533 stmfRet); 534 ret++; 535 break; 536 } 537 538 return (ret); 539 } 540 541 542 /*ARGSUSED*/ 543 int 544 list_lus(int argc, char *argv[], cmdOptions_t *options, void *callData) 545 { 546 int stmfRet; 547 stmfGuidList *luList; 548 stmfLogicalUnitProperties luProps; 549 int sbdLuCnt = 0; 550 int i; 551 552 if ((stmfRet = stmfGetLogicalUnitList(&luList)) 553 != STMF_STATUS_SUCCESS) { 554 switch (stmfRet) { 555 case STMF_ERROR_SERVICE_NOT_FOUND: 556 (void) fprintf(stderr, "%s: %s\n", cmdName, 557 gettext("STMF service not found")); 558 break; 559 case STMF_ERROR_BUSY: 560 (void) fprintf(stderr, "%s: %s\n", cmdName, 561 gettext("resource busy")); 562 break; 563 case STMF_ERROR_PERM: 564 (void) fprintf(stderr, "%s: %s\n", cmdName, 565 gettext("permission denied")); 566 break; 567 case STMF_ERROR_SERVICE_DATA_VERSION: 568 (void) fprintf(stderr, "%s: %s\n", cmdName, 569 gettext("STMF service version incorrect")); 570 break; 571 default: 572 (void) fprintf(stderr, "%s: %s\n", cmdName, 573 gettext("list failed")); 574 break; 575 } 576 return (1); 577 } 578 579 for (i = 0; i < luList->cnt; i++) { 580 stmfRet = stmfGetLogicalUnitProperties(&luList->guid[i], 581 &luProps); 582 if (stmfRet != STMF_STATUS_SUCCESS) { 583 (void) fprintf(stderr, "%s: %s\n", cmdName, 584 gettext("list failed")); 585 return (1); 586 } 587 if (strcmp(luProps.providerName, "sbd") == 0) { 588 sbdLuCnt++; 589 } 590 } 591 592 593 if (sbdLuCnt == 0) 594 return (0); 595 596 (void) printf("\nFound %d LU(s)\n", sbdLuCnt); 597 print_attr_header(); 598 599 for (i = 0; i < luList->cnt; i++) { 600 stmfRet = stmfGetLogicalUnitProperties(&luList->guid[i], 601 &luProps); 602 if (stmfRet != STMF_STATUS_SUCCESS) { 603 (void) fprintf(stderr, "%s: %s\n", cmdName, 604 gettext("list failed")); 605 return (1); 606 } 607 if (strcmp(luProps.providerName, "sbd") == 0) { 608 (void) print_lu_attr(&luList->guid[i]); 609 } 610 } 611 return (0); 612 } 613 614 void 615 print_attr_header() 616 { 617 (void) printf("\n"); 618 (void) printf(" GUID DATA SIZE " 619 " SOURCE\n"); 620 (void) printf("-------------------------------- -------------------" 621 " ----------------\n"); 622 } 623 624 void 625 print_guid(uint8_t *g, FILE *f) 626 { 627 int i; 628 629 for (i = 0; i < 16; i++) { 630 (void) fprintf(f, "%02x", g[i]); 631 } 632 } 633 634 int 635 print_lu_attr(stmfGuid *guid) 636 { 637 luResource hdl = NULL; 638 int stmfRet = 0; 639 int ret = 0; 640 char propVal[MAXPATHLEN]; 641 size_t propValSize = sizeof (propVal); 642 643 if ((stmfRet = stmfGetLuResource(guid, &hdl)) != STMF_STATUS_SUCCESS) { 644 switch (stmfRet) { 645 case STMF_ERROR_BUSY: 646 (void) fprintf(stderr, "%s: %s\n", cmdName, 647 gettext("resource busy")); 648 break; 649 case STMF_ERROR_PERM: 650 (void) fprintf(stderr, "%s: %s\n", cmdName, 651 gettext("permission denied")); 652 break; 653 case STMF_ERROR_NOT_FOUND: 654 /* No error here */ 655 return (0); 656 break; 657 default: 658 (void) fprintf(stderr, "%s: %s\n", cmdName, 659 gettext("get extended properties failed")); 660 break; 661 } 662 return (1); 663 } 664 665 print_guid((uint8_t *)guid, stdout); 666 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++; 676 } 677 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++; 687 } 688 689 690 (void) stmfFreeLuResource(hdl); 691 return (ret); 692 } 693 694 /* 695 * input: 696 * execFullName - exec name of program (argv[0]) 697 * 698 * copied from usr/src/cmd/zoneadm/zoneadm.c in OS/Net 699 * (changed name to lowerCamelCase to keep consistent with this file) 700 * 701 * Returns: 702 * command name portion of execFullName 703 */ 704 static char * 705 getExecBasename(char *execFullname) 706 { 707 char *lastSlash, *execBasename; 708 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; 720 } 721 break; 722 } 723 } 724 return (execBasename); 725 } 726 int 727 main(int argc, char *argv[]) 728 { 729 synTables_t synTables; 730 char versionString[VERSION_STRING_MAX_LEN]; 731 int ret; 732 int funcRet; 733 void *subcommandArgs = NULL; 734 735 (void) setlocale(LC_ALL, ""); 736 (void) textdomain(TEXT_DOMAIN); 737 /* set global command name */ 738 cmdName = getExecBasename(argv[0]); 739 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; 745 746 ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); 747 if (ret != 0) { 748 return (ret); 749 } 750 751 return (funcRet); 752 } /* end main */ 753