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 * Copyright 2012 Milan Jurik. All rights reserved. 25 */ 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> 41 42 #include <cmdparse.h> 43 #include <sys/stmf_defines.h> 44 #include <libstmf.h> 45 #include <sys/stmf_sbd_ioctl.h> 46 47 #define MAX_LU_LIST 8192 48 #define LU_LIST_MAX_RETRIES 3 49 #define GUID_INPUT 32 50 51 #define VERSION_STRING_MAJOR "1" 52 #define VERSION_STRING_MINOR "0" 53 #define VERSION_STRING_MAX_LEN 10 54 55 56 char *cmdName; 57 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(); 69 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 } 76 }; 77 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} 92 }; 93 94 /*ARGSUSED*/ 95 int 96 create_lu(int argc, char *operands[], cmdOptions_t *options, void *callData) 97 { 98 luResource hdl = NULL; 99 int ret = 0; 100 stmfGuid createdGuid; 101 102 ret = stmfCreateLuResource(STMF_DISK, &hdl); 103 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); 108 } 109 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); 121 } 122 break; 123 default: 124 (void) fprintf(stderr, "%s: %c: %s\n", 125 cmdName, options->optval, 126 gettext("unknown option")); 127 return (1); 128 } 129 } 130 131 ret = stmfSetLuProp(hdl, STMF_LU_PROP_FILENAME, operands[0]); 132 133 if (ret != STMF_STATUS_SUCCESS) { 134 (void) fprintf(stderr, "%s: %s\n", 135 cmdName, gettext("could not set filename")); 136 return (1); 137 } 138 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; 194 } 195 196 if (ret != STMF_STATUS_SUCCESS) { 197 goto done; 198 } 199 200 (void) printf("Created the following LU:\n"); 201 print_attr_header(); 202 ret = print_lu_attr(&createdGuid); 203 204 done: 205 (void) stmfFreeLuResource(hdl); 206 return (ret); 207 } 208 209 /*ARGSUSED*/ 210 int 211 import_lu(int argc, char *operands[], cmdOptions_t *options, void *callData) 212 { 213 int ret = 0; 214 stmfGuid createdGuid; 215 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; 266 } 267 268 if (ret != STMF_STATUS_SUCCESS) { 269 goto done; 270 } 271 272 (void) printf("Imported the following LU:\n"); 273 print_attr_header(); 274 ret = print_lu_attr(&createdGuid); 275 276 done: 277 return (ret); 278 } 279 280 /*ARGSUSED*/ 281 int 282 delete_lu(int operandLen, char *operands[], cmdOptions_t *options, 283 void *callData) 284 { 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; 297 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); 309 } 310 } 311 312 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; 318 } 319 sGuid[j] = tolower(operands[i][j]); 320 } 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; 330 } 331 332 sGuid[j] = 0; 333 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]); 340 341 for (j = 0; j < sizeof (stmfGuid); j++) { 342 delGuid.guid[j] = inGuid[j]; 343 } 344 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; 367 } 368 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); 376 } 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++; 385 } 386 } 387 if (keepViews) { 388 stmfRet = stmfGetViewEntryList(&delGuid, 389 &viewEntryList); 390 if (stmfRet == STMF_STATUS_SUCCESS) { 391 views = B_TRUE; 392 stmfFreeMemory(viewEntryList); 393 } 394 } 395 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++; 402 } 403 noLunFound = viewEntriesRemoved = views = B_FALSE; 404 } 405 return (ret); 406 } 407 408 /*ARGSUSED*/ 409 int 410 modify_lu(int operandLen, char *operands[], cmdOptions_t *options, 411 void *callData) 412 { 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; 420 421 if (operands[0][0] == '/') { 422 fnameUsed = B_TRUE; 423 fname = operands[0]; 424 } 425 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); 432 } 433 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]); 444 445 for (i = 0; i < sizeof (stmfGuid); i++) { 446 inGuid.guid[i] = guid[i]; 447 } 448 } 449 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); 457 } 458 break; 459 default: 460 (void) fprintf(stderr, "%s: %c: %s\n", 461 cmdName, options->optval, 462 gettext("unknown option")); 463 return (1); 464 } 465 } 466 return (ret); 467 } 468 469 static int 470 callModify(char *fname, stmfGuid *luGuid, uint32_t prop, const char *propVal, 471 const char *propString) 472 { 473 int ret = 0; 474 int stmfRet = 0; 475 476 if (!fname) { 477 stmfRet = stmfModifyLu(luGuid, prop, propVal); 478 } else { 479 stmfRet = stmfModifyLuByFname(STMF_DISK, fname, prop, 480 propVal); 481 } 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; 537 } 538 539 return (ret); 540 } 541 542 543 /*ARGSUSED*/ 544 int 545 list_lus(int argc, char *argv[], cmdOptions_t *options, void *callData) 546 { 547 int stmfRet; 548 stmfGuidList *luList; 549 stmfLogicalUnitProperties luProps; 550 int sbdLuCnt = 0; 551 int i; 552 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; 576 } 577 return (1); 578 } 579 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); 587 } 588 if (strcmp(luProps.providerName, "sbd") == 0) { 589 sbdLuCnt++; 590 } 591 } 592 593 594 if (sbdLuCnt == 0) 595 return (0); 596 597 (void) printf("\nFound %d LU(s)\n", sbdLuCnt); 598 print_attr_header(); 599 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); 607 } 608 if (strcmp(luProps.providerName, "sbd") == 0) { 609 (void) print_lu_attr(&luList->guid[i]); 610 } 611 } 612 return (0); 613 } 614 615 void 616 print_attr_header() 617 { 618 (void) printf("\n"); 619 (void) printf(" GUID DATA SIZE " 620 " SOURCE\n"); 621 (void) printf("-------------------------------- -------------------" 622 " ----------------\n"); 623 } 624 625 void 626 print_guid(uint8_t *g, FILE *f) 627 { 628 int i; 629 630 for (i = 0; i < 16; i++) { 631 (void) fprintf(f, "%02x", g[i]); 632 } 633 } 634 635 int 636 print_lu_attr(stmfGuid *guid) 637 { 638 luResource hdl = NULL; 639 int stmfRet = 0; 640 int ret = 0; 641 char propVal[MAXPATHLEN]; 642 size_t propValSize = sizeof (propVal); 643 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; 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