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