xref: /netbsd-src/sbin/iscsictl/iscsic_parse.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: iscsic_parse.c,v 1.3 2015/05/30 15:57:32 joerg Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "iscsic_globals.h"
33 
34 #include <ctype.h>
35 #include <assert.h>
36 
37 /*
38  * get_address:
39  *    Get an address specification that may include port and group tag.
40  *
41  *    Parameter:
42  *       portal   The portal address
43  *       str      The parameter string to scan
44  *
45  *    Aborts app on error.
46  */
47 
48 STATIC void
49 get_address(iscsi_portal_address_t * portal, char *str, char *arg)
50 {
51 	char *sp, *sp2;
52 	int val;
53 
54 	if (!str || !*str)
55 		arg_error(arg, "Address is missing");
56 
57 	/* is there a port? don't check inside square brackets (IPv6 addr) */
58 	for (sp = str + 1, val = 0; *sp && (*sp != ':' || val); sp++) {
59 		if (*sp == '[')
60 			val = 1;
61 		else if (*sp == ']')
62 			val = 0;
63 	}
64 
65 	/* */
66 	if (*sp) {
67 		for (sp2 = sp + 1; *sp2 && *sp2 != ':'; sp2++);
68 		/* if there's a second colon, assume it's an unbracketed IPv6 address */
69 		if (!*sp2) {
70 			/* truncate source, that's the address */
71 			*sp++ = '\0';
72 			if (sscanf(sp, "%d", &val) != 1)
73 				arg_error(arg, "Bad address format: Expected port number");
74 			if (val < 0 || val > 0xffff)
75 				arg_error(arg, "Bad address format: Port number out of range");
76 			portal->port = (uint16_t) val;
77 		}
78 		/* is there a group tag? */
79 		for (; isdigit((unsigned char)*sp); sp++);
80 		if (*sp && *sp != ',')
81 			arg_error(arg, "Bad address format: Extra character(s) '%c'", *sp);
82 	} else
83 		for (sp = str + 1; *sp && *sp != ','; sp++);
84 
85 	if (*sp) {
86 		if (sscanf(sp + 1, "%d", &val) != 1)
87 			arg_error(arg, "Bad address format: Expected group tag");
88 		if (val < 0 || val > 0xffff)
89 			arg_error(arg, "Bad address format: Group tag out of range");
90 		portal->group_tag = (uint16_t) val;
91 		/* truncate source, that's the address */
92 		*sp = '\0';
93 	}
94 	/* only check length, don't verify correct format (too many possibilities) */
95 	if (strlen(str) >= sizeof(portal->address))
96 		arg_error(arg, "Bad address format: Address string too long");
97 
98 	strlcpy((char *)portal->address, str, sizeof(portal->address));
99 }
100 
101 
102 
103 /*
104  * get_short_int:
105  *    Get a short integer.
106  *
107  *    Parameter:
108  *       sp       The parameter string to scan
109  *       arg      The associated option argument (for error message)
110  *       name     The argument name
111  *
112  *    Returns given integer, aborts app on error.
113  */
114 
115 STATIC uint16_t
116 get_short_int(char *sp, char *arg, const char *name)
117 {
118 	int val;
119 
120 	if (!sp || !*sp)
121 		arg_error(arg, "%s is missing", name);
122 
123 	if (!sscanf(sp, "%d", &val))
124 		arg_error(arg, "Expected integer %s", name);
125 	if (val < 0 || val > 0xffff)
126 		arg_error(arg, "%s out of range", name);
127 
128 	return (uint16_t) val;
129 }
130 
131 
132 /*
133  * get_dsl:
134  *    Get MaxRecvDataSegmentLength
135  *
136  *    Parameter:
137  *       sp       The parameter string to scan
138  *       arg      The associated option argument (for error message)
139  *
140  *    Returns given integer, aborts app on error.
141  */
142 
143 STATIC uint32_t
144 get_dsl(char *sp, char *arg)
145 {
146 	int val;
147 
148 	if (!sp || !*sp)
149 		arg_error(arg, "Missing MaxRecvDataSegmentLength");
150 	if (!sscanf(sp, "%d", &val))
151 		arg_error(arg, "Integer MaxRecvDataSegmentLength expected");
152 	if (val < 512 || val > 0xffffff)
153 		arg_error(arg, "MaxRecvDataSegmentLength out of range");
154 
155 	return (uint32_t) val;
156 }
157 
158 
159 /*
160  * get_str:
161  *    Get a string.
162  *
163  *    Parameter:
164  *       dest     The destination string
165  *       sp       The parameter string to scan
166  *       arg      The associated option argument (for error message)
167  *       name     The argument name
168  *
169  *    Aborts app on error.
170  */
171 
172 STATIC void
173 get_str(char *dest, char *sp, char *arg, const char *name)
174 {
175 
176 	if (!sp || !*sp)
177 		arg_error(arg, "%s is missing", name);
178 	if (strlen(sp) >= ISCSI_STRING_LENGTH)
179 		arg_error(arg, "%s is too long", name);
180 
181 	strlcpy(dest, sp, ISCSI_STRING_LENGTH);
182 }
183 
184 /*
185  * cl_get_target:
186  *    Get a target address specification that may include name, address, port,
187  *    and group tag, with address/port/tag possibly repeated.
188  *
189  *    Parameter:
190  *       ptarg       pointer to hold the resulting add target request parameter
191  *       argc, argv  program parameters (shifted)
192  *       nreq        target name is required if TRUE
193  *
194  *    Returns:    0 if there is no target, else the size of the allocated
195  *                  request.
196  *                Aborts app on bad parameter or mem allocation error.
197  */
198 
199 int
200 cl_get_target(iscsid_add_target_req_t ** ptarg, int argc, char **argv, int nreq)
201 {
202 	iscsid_add_target_req_t *targ;
203 	char *sp;
204 	size_t num, len, name;
205 	int i, p;
206 
207 	/* count number of addreses first, so we know how much memory to allocate */
208 	for (i = (int)(num = name = 0); i < argc; i++) {
209 		if (!argv[i] || argv[i][0] != '-')
210 			continue;
211 		if (argv[i][1] == 'a')
212 			num++;
213 		if (argv[i][1] == 'n')
214 			name++;
215 	}
216 
217 	if (!name && nreq)
218 		return 0;
219 
220 	len = sizeof(iscsid_add_target_req_t) +
221 		num * sizeof(iscsi_portal_address_t);
222 
223 	if (NULL == (targ = calloc(1, len)))
224 		gen_error("Can't allocate %zu bytes of memory", len);
225 
226 	*ptarg = targ;
227 	p = -1;
228 
229 	for (i = 0; i < argc; i++) {
230 		if (!argv[i] || argv[i][0] != '-')
231 			continue;
232 
233 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
234 
235 		switch (argv[i][1]) {
236 		case 'n':				/* target name */
237 			get_str((char *)targ->TargetName, sp, argv[i], "Target name");
238 			break;
239 
240 		case 'a':				/* target address */
241 			get_address(&targ->portal[++p], sp, argv[i]);
242 			break;
243 
244 		case 'p':				/* port */
245 			assert(p >= 0);
246 			targ->portal[p].port = get_short_int(sp, argv[i], "Port");
247 			break;
248 
249 		case 'g':				/* group tag */
250 			assert(p >= 0);
251 			targ->portal[p].group_tag = get_short_int(sp, argv[i],
252 														"Group tag");
253 			break;
254 
255 		default:
256 			continue;
257 		}
258 		if (!argv[i][2])
259 			argv[i + 1] = NULL;
260 
261 		argv[i] = NULL;
262 	}
263 	targ->num_portals = p + 1;
264 
265 	return (int)len;
266 }
267 
268 
269 /*
270  * cl_get_isns:
271  *    Get an iSNS server address specification that may include name, address
272  *    and port.
273  *
274  *    Parameter:
275  *       srv         add_isns_server request parameter
276  *       argc, argv  program parameters (shifted)
277  *
278  *    Returns:    0 on error, 1 if OK.
279  */
280 
281 int
282 cl_get_isns(iscsid_add_isns_server_req_t * srv, int argc, char **argv)
283 {
284 	iscsi_portal_address_t addr;
285 	char *sp;
286 	int i, found;
287 
288 	(void) memset(&addr, 0x0, sizeof(addr));
289 	found = FALSE;
290 
291 	for (i = 0; i < argc; i++) {
292 		if (!argv[i] || argv[i][0] != '-')
293 			continue;
294 
295 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
296 
297 		switch (argv[i][1]) {
298 		case 'N':				/* symbolic name */
299 			get_str((char *)srv->name, sp, argv[i], "Server name");
300 			break;
301 
302 		case 'a':				/* target address */
303 			get_address(&addr, sp, argv[i]);
304 			found = TRUE;
305 			break;
306 
307 		case 'p':				/* port */
308 			addr.port = get_short_int(sp, argv[i], "Port");
309 			break;
310 
311 		default:
312 			continue;
313 		}
314 		if (!argv[i][2]) {
315 			argv[i + 1] = NULL;
316 		}
317 		argv[i] = NULL;
318 	}
319 
320 	strlcpy((char *)srv->address, (char *)addr.address, sizeof(srv->address));
321 	srv->port = addr.port;
322 
323 	return found;
324 }
325 
326 
327 /*
328  * cl_get_auth_opts:
329  *    Get authentication options.
330  *
331  *    Parameter:
332  *          auth        authentication parameters
333  *          argc, argv  program parameters (shifted)
334  *
335  *    Returns:    0 if there are no authorization options, 1 otherwise.
336  *                Aborts app on bad parameter.
337  */
338 
339 int
340 cl_get_auth_opts(iscsid_set_target_authentication_req_t *auth,
341 				 int argc, char **argv)
342 {
343 	int n, i, found;
344 	char *sp;
345 
346 	found = FALSE;
347 	memset(auth, 0, sizeof(*auth));
348 
349 	for (i = 0; i < argc; i++) {
350 		if (!argv[i] || argv[i][0] != '-') {
351 			continue;
352 		}
353 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
354 
355 		switch (argv[i][1]) {
356 		case 't':				/* authentication type */
357 			if (!sp || !*sp)
358 				arg_error(argv[i], "Missing authentication type");
359 			n = 0;
360 			while (*sp) {
361 				switch (*sp) {
362 				case 'n':		/* no authentication */
363 					auth->auth_info.auth_type[n] = ISCSI_AUTH_None;
364 					break;
365 				case 'c':		/* CHAP authentication */
366 					auth->auth_info.auth_type[n] = ISCSI_AUTH_CHAP;
367 					break;
368 				case 'C':		/* Mutual CHAP authentication */
369 					auth->auth_info.auth_type[n] = ISCSI_AUTH_CHAP;
370 					auth->auth_info.mutual_auth = 1;
371 					break;
372 				default:
373 					arg_error(argv[i], "Bad authentication type '%c'", *sp);
374 				}
375 				sp++;
376 				n++;
377 			}
378 			auth->auth_info.auth_number = n;
379 			break;
380 
381 		case 'u':				/* user name */
382 			get_str((char *)auth->user_name, sp, argv[i], "User name");
383 			break;
384 
385 		case 's':				/* secret */
386 			get_str((char *)auth->password, sp, argv[i], "Secret");
387 			break;
388 
389 		case 'S':				/* target secret */
390 			get_str((char *)auth->target_password, sp, argv[i], "Target secret");
391 			break;
392 
393 		default:
394 			continue;
395 		}
396 		if (!argv[i][2])
397 			argv[i + 1] = NULL;
398 
399 		argv[i] = NULL;
400 		found = TRUE;
401 	}
402 	return found;
403 }
404 
405 
406 /*
407  * cl_get_target_opts:
408  *    Get session/connection options.
409  *
410  *    Parameter:
411  *          opt         target options
412  *          argc, argv  program parameters (shifted)
413  *
414  *    Returns:    0 if there are no target options, 1 otherwise.
415  *                Aborts app on bad parameter.
416  */
417 
418 int
419 cl_get_target_opts(iscsid_get_set_target_options_t * opt, int argc, char **argv)
420 {
421 	int i, found;
422 	char *sp;
423 
424 	found = FALSE;
425 	memset(opt, 0, sizeof(*opt));
426 
427 	for (i = 0; i < argc; i++) {
428 		if (!argv[i] || argv[i][0] != '-')
429 			continue;
430 
431 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
432 
433 		switch (argv[i][1]) {
434 		case 'h':				/* Header Digest */
435 			opt->HeaderDigest = ISCSI_DIGEST_CRC32C;
436 			opt->is_present.HeaderDigest = 1;
437 			break;
438 
439 		case 'd':				/* Data Digest */
440 			opt->DataDigest = ISCSI_DIGEST_CRC32C;
441 			opt->is_present.DataDigest = 1;
442 			break;
443 
444 		case 'w':				/* Time 2 Wait */
445 			opt->DefaultTime2Wait = get_short_int(sp, argv[i], "Time to wait");
446 			opt->is_present.DefaultTime2Wait = 1;
447 			if (!argv[i][2])
448 				argv[i + 1] = NULL;
449 			break;
450 
451 		case 'r':				/* Time 2 Retain */
452 			opt->DefaultTime2Retain = get_short_int(sp, argv[i],
453 													"Time to retain");
454 			opt->is_present.DefaultTime2Retain = 1;
455 			if (!argv[i][2])
456 				argv[i + 1] = NULL;
457 			break;
458 
459 		case 'e':				/* Error Recovery Level */
460 			opt->ErrorRecoveryLevel = get_short_int(sp, argv[i],
461 													"ErrorRecoveryLevel");
462 			opt->is_present.ErrorRecoveryLevel = 1;
463 			if (!argv[i][2])
464 				argv[i + 1] = NULL;
465 			break;
466 
467 		case 'l':				/* Data Segment Length */
468 			opt->MaxRecvDataSegmentLength = get_dsl(sp, argv[i]);
469 			opt->is_present.MaxRecvDataSegmentLength = 1;
470 			if (!argv[i][2])
471 				argv[i + 1] = NULL;
472 			break;
473 
474 		default:
475 			continue;
476 		}
477 		argv[i] = NULL;
478 		found = TRUE;
479 	}
480 	return found;
481 }
482 
483 
484 /*
485  * cl_get_portal:
486  *    Get a portal address specification that may include address, port,
487  *    and group tag, plus portal options.
488  *
489  *    Parameter:
490  *          port        add portal request parameter
491  *          argc, argv  program parameters (shifted)
492  *
493  *    Returns:    FALSE if there is no portal, else TRUE.
494  *                Aborts app on bad parameter or mem allocation error.
495  */
496 
497 int
498 cl_get_portal(iscsid_add_portal_req_t * port, int argc, char **argv)
499 {
500 	char *sp;
501 	int i, found;
502 	iscsid_portal_options_t *opt = &port->options;
503 
504 	found = FALSE;
505 	memset(port, 0, sizeof(*port));
506 
507 	for (i = 0; i < argc; i++) {
508 		if (!argv[i] || argv[i][0] != '-')
509 			continue;
510 
511 		sp = (argv[i][2]) ? &argv[i][2] : ((i + 1 < argc) ? argv[i + 1] : NULL);
512 
513 		switch (argv[i][1]) {
514 		case 'a':				/* target address */
515 			get_address(&port->portal, sp, argv[i]);
516 			found = TRUE;
517 			break;
518 
519 		case 'p':				/* port */
520 			port->portal.port = get_short_int(sp, argv[i], "Port");
521 			break;
522 
523 		case 'g':				/* group tag */
524 			port->portal.group_tag = get_short_int(sp, argv[i], "Group tag");
525 			break;
526 
527 		case 'h':				/* Header Digest */
528 			opt->HeaderDigest = ISCSI_DIGEST_CRC32C;
529 			opt->is_present.HeaderDigest = 1;
530 			break;
531 
532 		case 'd':				/* Data Digest */
533 			opt->DataDigest = ISCSI_DIGEST_CRC32C;
534 			opt->is_present.DataDigest = 1;
535 			break;
536 
537 		case 'l':				/* Data Segment Length */
538 			opt->MaxRecvDataSegmentLength = get_dsl(sp, argv[i]);
539 			opt->is_present.MaxRecvDataSegmentLength = 1;
540 			if (!argv[i][2])
541 				argv[i + 1] = NULL;
542 			break;
543 
544 		default:
545 			continue;
546 		}
547 		if (!argv[i][2])
548 			argv[i + 1] = NULL;
549 
550 		argv[i] = NULL;
551 	}
552 	return found;
553 }
554 
555 
556 /*
557  * cl_get_id:
558  *    Get an identifier (symbolic or numeric)
559  *
560  *    Parameter:
561  *          ident       the parameter identifier character
562  *          sid         the ID
563  *          argc, argv  program parameters (shifted)
564  *
565  *    Returns:    0 if there is no ID, 1 otherwise.
566  *                Aborts app on bad parameter.
567  */
568 
569 int
570 cl_get_id(char ident, iscsid_sym_id_t * sid, int argc, char **argv)
571 {
572 	int i, found;
573 	char *sp;
574 
575 	found = FALSE;
576 	memset(sid, 0, sizeof(*sid));
577 
578 	for (i = 0; i < argc && !found; i++) {
579 		if (!argv[i] || argv[i][0] != '-')
580 			continue;
581 
582 		if (argv[i][1] == ident) {
583 			sp = (argv[i][2]) ? &argv[i][2] :
584 				((i + 1 < argc) ? argv[i + 1] : NULL);
585 
586 			if (!sp || !*sp)
587 				arg_error(argv[i], "Missing ID");
588 			if (strlen(sp) >= ISCSI_STRING_LENGTH)
589 				arg_error(argv[i], "ID String too long");
590 			if (!sscanf(sp, "%d", &sid->id))
591 				strlcpy((char *)sid->name, sp, sizeof(sid->name));
592 			else if (!sid->id)
593 				arg_error(argv[i], "Invalid ID");
594 
595 			if (!argv[i][2])
596 				argv[i + 1] = NULL;
597 
598 			argv[i] = NULL;
599 			found = TRUE;
600 		}
601 	}
602 	return found;
603 }
604 
605 
606 /*
607  * cl_get_symname:
608  *    Get a symbolic name
609  *
610  *    Parameter:
611  *          sn          the name
612  *          argc, argv  program parameters (shifted)
613  *
614  *    Returns:    0 if there is no symbolic name, 1 otherwise.
615  *                Aborts app on bad parameter.
616  */
617 
618 int
619 cl_get_symname(uint8_t * sn, int argc, char **argv)
620 {
621 	int i, found;
622 	char *sp;
623 
624 	found = FALSE;
625 	*sn = '\0';
626 
627 	for (i = 0; i < argc && !found; i++) {
628 		if (!argv[i] || argv[i][0] != '-')
629 			continue;
630 
631 		if (argv[i][1] == 'N') {
632 			sp = (argv[i][2]) ? &argv[i][2]
633 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
634 
635 			if (!sp || !*sp)
636 				arg_error(argv[i], "Symbolic name missing");
637 			if (isdigit((unsigned char)*sp))
638 				arg_error(argv[i], "Symbolic name must not be numeric");
639 			if (strlen(sp) >= ISCSI_STRING_LENGTH)
640 				arg_error(argv[i], "Symbolic name too long");
641 
642 			strlcpy((char *)sn, sp, ISCSI_STRING_LENGTH);
643 
644 			if (!argv[i][2])
645 				argv[i + 1] = NULL;
646 
647 			argv[i] = NULL;
648 			found = TRUE;
649 		}
650 	}
651 	return found;
652 }
653 
654 
655 /*
656  * cl_get_string:
657  *    Get a string value
658  *
659  *    Parameter:
660  *          ident       the parameter identifier character
661  *          pstr        the result string
662  *          argc, argv  program parameters (shifted)
663  *
664  *    Returns:    0 if there is no string, 1 otherwise.
665  *                Aborts app on bad parameter.
666  */
667 
668 int
669 cl_get_string(char ident, char *pstr, int argc, char **argv)
670 {
671 	int i, found;
672 	char *sp;
673 
674 	found = FALSE;
675 	*pstr = '\0';
676 
677 	for (i = 0; i < argc && !found; i++) {
678 		if (!argv[i] || argv[i][0] != '-')
679 			continue;
680 
681 		if (argv[i][1] == ident) {
682 			sp = (argv[i][2]) ? &argv[i][2]
683 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
684 
685 			get_str(pstr, sp, argv[i], "String");
686 
687 			if (!argv[i][2])
688 				argv[i + 1] = NULL;
689 
690 			argv[i] = NULL;
691 			found = TRUE;
692 		}
693 	}
694 	return found;
695 }
696 
697 
698 /*
699  * cl_get_opt:
700  *    Get an option with no value
701  *
702  *    Parameter:
703  *          ident       the parameter identifier character
704  *          argc, argv  program parameters (shifted)
705  *
706  *    Returns:    0 if the option was not found, 1 otherwise.
707  *                Aborts app on bad parameter.
708  */
709 
710 int
711 cl_get_opt(char ident, int argc, char **argv)
712 {
713 	int i, found;
714 
715 	found = FALSE;
716 
717 	for (i = 0; i < argc && !found; i++) {
718 		if (!argv[i] || argv[i][0] != '-')
719 			continue;
720 
721 		if (argv[i][1] == ident) {
722 			argv[i] = NULL;
723 			found = TRUE;
724 		}
725 	}
726 	return found;
727 }
728 
729 
730 /*
731  * cl_get_char:
732  *    Get an option with a character value
733  *
734  *    Parameter:
735  *          ident       the parameter identifier character
736  *          argc, argv  program parameters (shifted)
737  *
738  *    Returns:    The option character (0 if not found).
739  *                Aborts app on bad parameter.
740  */
741 
742 char
743 cl_get_char(char ident, int argc, char **argv)
744 {
745 	int i, found;
746 	char *sp;
747 	char ch = 0;
748 
749 	found = FALSE;
750 
751 	for (i = 0; i < argc && !found; i++) {
752 		if (!argv[i] || argv[i][0] != '-')
753 			continue;
754 
755 		if (argv[i][1] == ident) {
756 			sp = (argv[i][2]) ? &argv[i][2]
757 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
758 
759 			if (!sp || !*sp)
760 				arg_error(argv[i], "Option character missing");
761 			if (strlen(sp) > 1)
762 				arg_error(argv[i], "Option invalid");
763 			ch = *sp;
764 
765 			if (!argv[i][2])
766 				argv[i + 1] = NULL;
767 
768 			argv[i] = NULL;
769 			found = TRUE;
770 		}
771 	}
772 
773 	return ch;
774 }
775 
776 
777 /*
778  * cl_get_int:
779  *    Get an option with an integer value
780  *
781  *    Parameter:
782  *          ident       the parameter identifier character
783  *          argc, argv  program parameters (shifted)
784  *
785  *    Returns:    The option value (0 if not found).
786  *                Aborts app on bad parameter.
787  */
788 
789 int
790 cl_get_int(char ident, int argc, char **argv)
791 {
792 	int i, found;
793 	char *sp;
794 	int val = 0;
795 
796 	found = FALSE;
797 
798 	for (i = 0; i < argc && !found; i++) {
799 		if (!argv[i] || argv[i][0] != '-')
800 			continue;
801 
802 		if (argv[i][1] == ident) {
803 			sp = (argv[i][2]) ? &argv[i][2]
804 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
805 
806 			if (!sp || !*sp)
807 				arg_error(argv[i], "Option value missing");
808 			if (!sscanf(sp, "%i", &val))
809 				arg_error(argv[i], "Integer expected");
810 
811 			if (!argv[i][2])
812 				argv[i + 1] = NULL;
813 
814 			argv[i] = NULL;
815 			found = TRUE;
816 		}
817 	}
818 
819 	return val;
820 }
821 
822 
823 /*
824  * cl_get_uint:
825  *    Get an option with a positive integer value
826  *
827  *    Parameter:
828  *          ident       the parameter identifier character
829  *          argc, argv  program parameters (shifted)
830  *
831  *    Returns:    The option value (-1 if not found).
832  *                Aborts app on bad parameter.
833  */
834 
835 #if 0
836 int
837 cl_get_uint(char ident, int argc, char **argv)
838 {
839 	int i, found;
840 	char *sp;
841 	int val = -1;
842 
843 	found = FALSE;
844 
845 	for (i = 0; i < argc && !found; i++) {
846 		if (!argv[i] || argv[i][0] != '-')
847 			continue;
848 
849 		if (argv[i][1] == ident) {
850 			sp = (argv[i][2]) ? &argv[i][2]
851 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
852 
853 			if (!sp || !*sp)
854 				arg_error(argv[i], "Option value missing");
855 			if (!sscanf(sp, "%i", &val))
856 				arg_error(argv[i], "Positive integer expected");
857 
858 			if (!argv[i][2])
859 				argv[i + 1] = NULL;
860 
861 			argv[i] = NULL;
862 			found = TRUE;
863 		}
864 	}
865 
866 	return val;
867 }
868 #endif
869 
870 
871 /*
872  * cl_get_longlong:
873  *    Get an option with a 64-bit value
874  *
875  *    Parameter:
876  *          ident       the parameter identifier character
877  *          argc, argv  program parameters (shifted)
878  *
879  *    Returns:    The option value (0 if not found).
880  *                Aborts app on bad parameter.
881  */
882 
883 uint64_t
884 cl_get_longlong(char ident, int argc, char **argv)
885 {
886 	int i, found;
887 	char *sp;
888 	uint64_t val = 0;
889 
890 	found = FALSE;
891 
892 	for (i = 0; i < argc && !found; i++) {
893 		if (!argv[i] || argv[i][0] != '-')
894 			continue;
895 
896 		if (argv[i][1] == ident) {
897 			sp = (argv[i][2]) ? &argv[i][2]
898 				: ((i + 1 < argc) ? argv[i + 1] : NULL);
899 
900 			if (!sp || !*sp)
901 				arg_error(argv[i], "Option value missing");
902 			if (!sscanf(sp, "%qi", (long long *)(void *)&val))
903 				arg_error(argv[i], "Integer expected");
904 
905 			if (!argv[i][2])
906 				argv[i + 1] = NULL;
907 
908 			argv[i] = NULL;
909 			found = TRUE;
910 		}
911 	}
912 
913 	return val;
914 }
915