xref: /illumos-gate/usr/src/cmd/sbdadm/sbdadm.c (revision 528737823843346cf95a4a701612f82089135554)
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