1 /*
2  * This file contains the ini and command liner parser main.
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <ctype.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <stdlib.h>
12 #include <math.h>
13 #include <float.h>
14 
15 #include "parse.h"
16 #include "debug.h"
17 #include "options.h"
18 #include "minmax.h"
19 #include "lib/ieee754.h"
20 
21 #ifdef CONFIG_ARITHMETIC
22 #include "y.tab.h"
23 #endif
24 
25 static struct fio_option *__fio_options;
26 
vp_cmp(const void * p1,const void * p2)27 static int vp_cmp(const void *p1, const void *p2)
28 {
29 	const struct value_pair *vp1 = p1;
30 	const struct value_pair *vp2 = p2;
31 
32 	return strlen(vp2->ival) - strlen(vp1->ival);
33 }
34 
posval_sort(struct fio_option * o,struct value_pair * vpmap)35 static void posval_sort(struct fio_option *o, struct value_pair *vpmap)
36 {
37 	const struct value_pair *vp;
38 	int entries;
39 
40 	memset(vpmap, 0, PARSE_MAX_VP * sizeof(struct value_pair));
41 
42 	for (entries = 0; entries < PARSE_MAX_VP; entries++) {
43 		vp = &o->posval[entries];
44 		if (!vp->ival || vp->ival[0] == '\0')
45 			break;
46 
47 		memcpy(&vpmap[entries], vp, sizeof(*vp));
48 	}
49 
50 	qsort(vpmap, entries, sizeof(struct value_pair), vp_cmp);
51 }
52 
show_option_range(struct fio_option * o,int (* logger)(const char * format,...))53 static void show_option_range(struct fio_option *o,
54 				int (*logger)(const char *format, ...))
55 {
56 	if (o->type == FIO_OPT_FLOAT_LIST) {
57 		if (o->minfp == DBL_MIN && o->maxfp == DBL_MAX)
58 			return;
59 
60 		logger("%20s: min=%f", "range", o->minfp);
61 		if (o->maxfp != DBL_MAX)
62 			logger(", max=%f", o->maxfp);
63 		logger("\n");
64 	} else if (!o->posval[0].ival) {
65 		if (!o->minval && !o->maxval)
66 			return;
67 
68 		logger("%20s: min=%d", "range", o->minval);
69 		if (o->maxval)
70 			logger(", max=%d", o->maxval);
71 		logger("\n");
72 	}
73 }
74 
show_option_values(struct fio_option * o)75 static void show_option_values(struct fio_option *o)
76 {
77 	int i;
78 
79 	for (i = 0; i < PARSE_MAX_VP; i++) {
80 		const struct value_pair *vp = &o->posval[i];
81 
82 		if (!vp->ival)
83 			continue;
84 
85 		log_info("%20s: %-10s", i == 0 ? "valid values" : "", vp->ival);
86 		if (vp->help)
87 			log_info(" %s", vp->help);
88 		log_info("\n");
89 	}
90 
91 	if (i)
92 		log_info("\n");
93 }
94 
show_option_help(struct fio_option * o,int is_err)95 static void show_option_help(struct fio_option *o, int is_err)
96 {
97 	const char *typehelp[] = {
98 		"invalid",
99 		"string (opt=bla)",
100 		"string (opt=bla)",
101 		"string with possible k/m/g postfix (opt=4k)",
102 		"string with time postfix (opt=10s)",
103 		"string (opt=bla)",
104 		"string with dual range (opt=1k-4k,4k-8k)",
105 		"integer value (opt=100)",
106 		"boolean value (opt=1)",
107 		"list of floating point values separated by ':' (opt=5.9:7.8)",
108 		"no argument (opt)",
109 		"deprecated",
110 	};
111 	int (*logger)(const char *format, ...);
112 
113 	if (is_err)
114 		logger = log_err;
115 	else
116 		logger = log_info;
117 
118 	if (o->alias)
119 		logger("%20s: %s\n", "alias", o->alias);
120 
121 	logger("%20s: %s\n", "type", typehelp[o->type]);
122 	logger("%20s: %s\n", "default", o->def ? o->def : "no default");
123 	if (o->prof_name)
124 		logger("%20s: only for profile '%s'\n", "valid", o->prof_name);
125 	show_option_range(o, logger);
126 	show_option_values(o);
127 }
128 
get_mult_time(const char * str,int len,int is_seconds)129 static unsigned long long get_mult_time(const char *str, int len,
130 					int is_seconds)
131 {
132 	const char *p = str;
133 	char *c;
134 	unsigned long long mult = 1;
135 
136 	/*
137          * Go forward until we hit a non-digit, or +/- sign
138          */
139 	while ((p - str) <= len) {
140 		if (!isdigit((int) *p) && (*p != '+') && (*p != '-'))
141 			break;
142 		p++;
143 	}
144 
145 	if (!isalpha((int) *p)) {
146 		if (is_seconds)
147 			return 1000000UL;
148 		else
149 			return 1;
150 	}
151 
152 	c = strdup(p);
153 	for (int i = 0; i < strlen(c); i++)
154 		c[i] = tolower(c[i]);
155 
156 	if (!strncmp("us", c, 2) || !strncmp("usec", c, 4))
157 		mult = 1;
158 	else if (!strncmp("ms", c, 2) || !strncmp("msec", c, 4))
159 		mult = 1000;
160 	else if (!strcmp("s", c))
161 		mult = 1000000;
162 	else if (!strcmp("m", c))
163 		mult = 60 * 1000000UL;
164 	else if (!strcmp("h", c))
165 		mult = 60 * 60 * 1000000UL;
166 	else if (!strcmp("d", c))
167 		mult = 24 * 60 * 60 * 1000000UL;
168 
169 	free(c);
170 	return mult;
171 }
172 
is_separator(char c)173 static int is_separator(char c)
174 {
175 	switch (c) {
176 	case ':':
177 	case '-':
178 	case ',':
179 	case '/':
180 		return 1;
181 	default:
182 		return 0;
183 	}
184 }
185 
__get_mult_bytes(const char * p,void * data,int * percent)186 static unsigned long long __get_mult_bytes(const char *p, void *data,
187 					   int *percent)
188 {
189 	unsigned int kb_base = fio_get_kb_base(data);
190 	unsigned long long ret = 1;
191 	unsigned int i, pow = 0, mult = kb_base;
192 	char *c;
193 
194 	if (!p)
195 		return 1;
196 
197 	c = strdup(p);
198 
199 	for (i = 0; i < strlen(c); i++) {
200 		c[i] = tolower(c[i]);
201 		if (is_separator(c[i])) {
202 			c[i] = '\0';
203 			break;
204 		}
205 	}
206 
207 	if (!strncmp("pib", c, 3)) {
208 		pow = 5;
209 		mult = 1000;
210 	} else if (!strncmp("tib", c, 3)) {
211 		pow = 4;
212 		mult = 1000;
213 	} else if (!strncmp("gib", c, 3)) {
214 		pow = 3;
215 		mult = 1000;
216 	} else if (!strncmp("mib", c, 3)) {
217 		pow = 2;
218 		mult = 1000;
219 	} else if (!strncmp("kib", c, 3)) {
220 		pow = 1;
221 		mult = 1000;
222 	} else if (!strncmp("p", c, 1) || !strncmp("pb", c, 2))
223 		pow = 5;
224 	else if (!strncmp("t", c, 1) || !strncmp("tb", c, 2))
225 		pow = 4;
226 	else if (!strncmp("g", c, 1) || !strncmp("gb", c, 2))
227 		pow = 3;
228 	else if (!strncmp("m", c, 1) || !strncmp("mb", c, 2))
229 		pow = 2;
230 	else if (!strncmp("k", c, 1) || !strncmp("kb", c, 2))
231 		pow = 1;
232 	else if (!strncmp("%", c, 1)) {
233 		*percent = 1;
234 		free(c);
235 		return ret;
236 	}
237 
238 	while (pow--)
239 		ret *= (unsigned long long) mult;
240 
241 	free(c);
242 	return ret;
243 }
244 
get_mult_bytes(const char * str,int len,void * data,int * percent)245 static unsigned long long get_mult_bytes(const char *str, int len, void *data,
246 					 int *percent)
247 {
248 	const char *p = str;
249 	int digit_seen = 0;
250 
251 	if (len < 2)
252 		return __get_mult_bytes(str, data, percent);
253 
254 	/*
255 	 * Go forward until we hit a non-digit, or +/- sign
256 	 */
257 	while ((p - str) <= len) {
258 		if (!isdigit((int) *p) &&
259 		    (((*p != '+') && (*p != '-')) || digit_seen))
260 			break;
261 		digit_seen |= isdigit((int) *p);
262 		p++;
263 	}
264 
265 	if (!isalpha((int) *p) && (*p != '%'))
266 		p = NULL;
267 
268 	return __get_mult_bytes(p, data, percent);
269 }
270 
271 extern int evaluate_arithmetic_expression(const char *buffer, long long *ival,
272 					  double *dval, double implied_units,
273 					  int is_time);
274 
275 /*
276  * Convert string into a floating number. Return 1 for success and 0 otherwise.
277  */
str_to_float(const char * str,double * val,int is_time)278 int str_to_float(const char *str, double *val, int is_time)
279 {
280 #ifdef CONFIG_ARITHMETIC
281 	int rc;
282 	long long ival;
283 	double dval;
284 
285 	if (str[0] == '(') {
286 		rc = evaluate_arithmetic_expression(str, &ival, &dval, 1.0, is_time);
287 		if (!rc) {
288 			*val = dval;
289 			return 1;
290 		}
291 	}
292 #endif
293 	return 1 == sscanf(str, "%lf", val);
294 }
295 
296 /*
297  * convert string into decimal value, noting any size suffix
298  */
str_to_decimal(const char * str,long long * val,int kilo,void * data,int is_seconds,int is_time)299 int str_to_decimal(const char *str, long long *val, int kilo, void *data,
300 		   int is_seconds, int is_time)
301 {
302 	int len, base;
303 	int rc = 1;
304 #ifdef CONFIG_ARITHMETIC
305 	long long ival;
306 	double dval;
307 	double implied_units = 1.0;
308 #endif
309 
310 	len = strlen(str);
311 	if (!len)
312 		return 1;
313 
314 #ifdef CONFIG_ARITHMETIC
315 	if (is_seconds)
316 		implied_units = 1000000.0;
317 	if (str[0] == '(')
318 		rc = evaluate_arithmetic_expression(str, &ival, &dval, implied_units, is_time);
319 	if (str[0] == '(' && !rc) {
320 		if (!kilo && is_seconds)
321 			*val = ival / 1000000LL;
322 		else
323 			*val = ival;
324 	}
325 #endif
326 
327 	if (rc == 1) {
328 		if (strstr(str, "0x") || strstr(str, "0X"))
329 			base = 16;
330 		else
331 			base = 10;
332 
333 		*val = strtoll(str, NULL, base);
334 		if (*val == LONG_MAX && errno == ERANGE)
335 			return 1;
336 	}
337 
338 	if (kilo) {
339 		unsigned long long mult;
340 		int perc = 0;
341 
342 		mult = get_mult_bytes(str, len, data, &perc);
343 		if (perc)
344 			*val = -1ULL - *val;
345 		else
346 			*val *= mult;
347 	} else
348 		*val *= get_mult_time(str, len, is_seconds);
349 
350 	return 0;
351 }
352 
check_str_bytes(const char * p,long long * val,void * data)353 int check_str_bytes(const char *p, long long *val, void *data)
354 {
355 	return str_to_decimal(p, val, 1, data, 0, 0);
356 }
357 
check_str_time(const char * p,long long * val,int is_seconds)358 int check_str_time(const char *p, long long *val, int is_seconds)
359 {
360 	return str_to_decimal(p, val, 0, NULL, is_seconds, 1);
361 }
362 
strip_blank_front(char ** p)363 void strip_blank_front(char **p)
364 {
365 	char *s = *p;
366 
367 	if (!strlen(s))
368 		return;
369 	while (isspace((int) *s))
370 		s++;
371 
372 	*p = s;
373 }
374 
strip_blank_end(char * p)375 void strip_blank_end(char *p)
376 {
377 	char *start = p, *s;
378 
379 	if (!strlen(p))
380 		return;
381 
382 	s = strchr(p, ';');
383 	if (s)
384 		*s = '\0';
385 	s = strchr(p, '#');
386 	if (s)
387 		*s = '\0';
388 	if (s)
389 		p = s;
390 
391 	s = p + strlen(p);
392 	while ((isspace((int) *s) || iscntrl((int) *s)) && (s > start))
393 		s--;
394 
395 	*(s + 1) = '\0';
396 }
397 
check_range_bytes(const char * str,long * val,void * data)398 static int check_range_bytes(const char *str, long *val, void *data)
399 {
400 	long long __val;
401 
402 	if (!str_to_decimal(str, &__val, 1, data, 0, 0)) {
403 		*val = __val;
404 		return 0;
405 	}
406 
407 	return 1;
408 }
409 
check_int(const char * p,int * val)410 static int check_int(const char *p, int *val)
411 {
412 	if (!strlen(p))
413 		return 1;
414 	if (strstr(p, "0x") || strstr(p, "0X")) {
415 		if (sscanf(p, "%x", val) == 1)
416 			return 0;
417 	} else {
418 		if (sscanf(p, "%u", val) == 1)
419 			return 0;
420 	}
421 
422 	return 1;
423 }
424 
opt_len(const char * str)425 static size_t opt_len(const char *str)
426 {
427 	char *postfix;
428 
429 	postfix = strchr(str, ':');
430 	if (!postfix)
431 		return strlen(str);
432 
433 	return (int)(postfix - str);
434 }
435 
str_match_len(const struct value_pair * vp,const char * str)436 static int str_match_len(const struct value_pair *vp, const char *str)
437 {
438 	return max(strlen(vp->ival), opt_len(str));
439 }
440 
441 #define val_store(ptr, val, off, or, data, o)		\
442 	do {						\
443 		ptr = td_var((data), (o), (off));	\
444 		if ((or))				\
445 			*ptr |= (val);			\
446 		else					\
447 			*ptr = (val);			\
448 	} while (0)
449 
__handle_option(struct fio_option * o,const char * ptr,void * data,int first,int more,int curr)450 static int __handle_option(struct fio_option *o, const char *ptr, void *data,
451 			   int first, int more, int curr)
452 {
453 	int il=0, *ilp;
454 	fio_fp64_t *flp;
455 	long long ull, *ullp;
456 	long ul1, ul2;
457 	double uf;
458 	char **cp = NULL;
459 	int ret = 0, is_time = 0;
460 	const struct value_pair *vp;
461 	struct value_pair posval[PARSE_MAX_VP];
462 	int i, all_skipped = 1;
463 
464 	dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name,
465 							o->type, ptr);
466 
467 	if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) {
468 		log_err("Option %s requires an argument\n", o->name);
469 		return 1;
470 	}
471 
472 	switch (o->type) {
473 	case FIO_OPT_STR:
474 	case FIO_OPT_STR_MULTI: {
475 		fio_opt_str_fn *fn = o->cb;
476 
477 		posval_sort(o, posval);
478 
479 		ret = 1;
480 		for (i = 0; i < PARSE_MAX_VP; i++) {
481 			vp = &posval[i];
482 			if (!vp->ival || vp->ival[0] == '\0')
483 				continue;
484 			all_skipped = 0;
485 			if (!strncmp(vp->ival, ptr, str_match_len(vp, ptr))) {
486 				ret = 0;
487 				if (o->off1)
488 					val_store(ilp, vp->oval, o->off1, vp->orval, data, o);
489 				continue;
490 			}
491 		}
492 
493 		if (ret && !all_skipped)
494 			show_option_values(o);
495 		else if (fn)
496 			ret = fn(data, ptr);
497 		break;
498 	}
499 	case FIO_OPT_STR_VAL_TIME:
500 		is_time = 1;
501 	case FIO_OPT_INT:
502 	case FIO_OPT_STR_VAL: {
503 		fio_opt_str_val_fn *fn = o->cb;
504 		char tmp[128], *p;
505 
506 		if (!is_time && o->is_time)
507 			is_time = o->is_time;
508 
509 		tmp[sizeof(tmp) - 1] = '\0';
510 		strncpy(tmp, ptr, sizeof(tmp) - 1);
511 		p = strchr(tmp, ',');
512 		if (p)
513 			*p = '\0';
514 
515 		if (is_time)
516 			ret = check_str_time(tmp, &ull, o->is_seconds);
517 		else
518 			ret = check_str_bytes(tmp, &ull, data);
519 
520 		dprint(FD_PARSE, "  ret=%d, out=%llu\n", ret, ull);
521 
522 		if (ret)
523 			break;
524 
525 		if (o->maxval && ull > o->maxval) {
526 			log_err("max value out of range: %llu"
527 					" (%u max)\n", ull, o->maxval);
528 			return 1;
529 		}
530 		if (o->minval && ull < o->minval) {
531 			log_err("min value out of range: %llu"
532 					" (%u min)\n", ull, o->minval);
533 			return 1;
534 		}
535 		if (o->posval[0].ival) {
536 			posval_sort(o, posval);
537 
538 			ret = 1;
539 			for (i = 0; i < PARSE_MAX_VP; i++) {
540 				vp = &posval[i];
541 				if (!vp->ival || vp->ival[0] == '\0')
542 					continue;
543 				if (vp->oval == ull) {
544 					ret = 0;
545 					break;
546 				}
547 			}
548 			if (ret) {
549 				log_err("fio: value %llu not allowed:\n", ull);
550 				show_option_values(o);
551 				return 1;
552 			}
553 		}
554 
555 		if (fn)
556 			ret = fn(data, &ull);
557 		else {
558 			if (o->type == FIO_OPT_INT) {
559 				if (first)
560 					val_store(ilp, ull, o->off1, 0, data, o);
561 				if (curr == 1) {
562 					if (o->off2)
563 						val_store(ilp, ull, o->off2, 0, data, o);
564 				}
565 				if (curr == 2) {
566 					if (o->off3)
567 						val_store(ilp, ull, o->off3, 0, data, o);
568 				}
569 				if (!more) {
570 					if (curr < 1) {
571 						if (o->off2)
572 							val_store(ilp, ull, o->off2, 0, data, o);
573 					}
574 					if (curr < 2) {
575 						if (o->off3)
576 							val_store(ilp, ull, o->off3, 0, data, o);
577 					}
578 				}
579 			} else {
580 				if (first)
581 					val_store(ullp, ull, o->off1, 0, data, o);
582 				if (!more) {
583 					if (o->off2)
584 						val_store(ullp, ull, o->off2, 0, data, o);
585 				}
586 			}
587 		}
588 		break;
589 	}
590 	case FIO_OPT_FLOAT_LIST: {
591 		char *cp2;
592 
593 		if (first) {
594 			/*
595 			** Initialize precision to 0 and zero out list
596 			** in case specified list is shorter than default
597 			*/
598 			if (o->off2) {
599 				ul2 = 0;
600 				ilp = td_var(data, o, o->off2);
601 				*ilp = ul2;
602 			}
603 
604 			flp = td_var(data, o, o->off1);
605 			for(i = 0; i < o->maxlen; i++)
606 				flp[i].u.f = 0.0;
607 		}
608 		if (curr >= o->maxlen) {
609 			log_err("the list exceeding max length %d\n",
610 					o->maxlen);
611 			return 1;
612 		}
613 		if (!str_to_float(ptr, &uf, 0)) { /* this breaks if we ever have lists of times */
614 			log_err("not a floating point value: %s\n", ptr);
615 			return 1;
616 		}
617 		if (uf > o->maxfp) {
618 			log_err("value out of range: %f"
619 				" (range max: %f)\n", uf, o->maxfp);
620 			return 1;
621 		}
622 		if (uf < o->minfp) {
623 			log_err("value out of range: %f"
624 				" (range min: %f)\n", uf, o->minfp);
625 			return 1;
626 		}
627 
628 		flp = td_var(data, o, o->off1);
629 		flp[curr].u.f = uf;
630 
631 		dprint(FD_PARSE, "  out=%f\n", uf);
632 
633 		/*
634 		** Calculate precision for output by counting
635 		** number of digits after period. Find first
636 		** period in entire remaining list each time
637 		*/
638 		cp2 = strchr(ptr, '.');
639 		if (cp2 != NULL) {
640 			int len = 0;
641 
642 			while (*++cp2 != '\0' && *cp2 >= '0' && *cp2 <= '9')
643 				len++;
644 
645 			if (o->off2) {
646 				ilp = td_var(data, o, o->off2);
647 				if (len > *ilp)
648 					*ilp = len;
649 			}
650 		}
651 
652 		break;
653 	}
654 	case FIO_OPT_STR_STORE: {
655 		fio_opt_str_fn *fn = o->cb;
656 
657 		if (!strlen(ptr))
658 			return 1;
659 
660 		if (o->off1) {
661 			cp = td_var(data, o, o->off1);
662 			*cp = strdup(ptr);
663 		}
664 
665 		if (fn)
666 			ret = fn(data, ptr);
667 		else if (o->posval[0].ival) {
668 			posval_sort(o, posval);
669 
670 			ret = 1;
671 			for (i = 0; i < PARSE_MAX_VP; i++) {
672 				vp = &posval[i];
673 				if (!vp->ival || vp->ival[0] == '\0' || !cp)
674 					continue;
675 				all_skipped = 0;
676 				if (!strncmp(vp->ival, ptr, str_match_len(vp, ptr))) {
677 					char *rest;
678 
679 					ret = 0;
680 					if (vp->cb)
681 						fn = vp->cb;
682 					rest = strstr(*cp ?: ptr, ":");
683 					if (rest) {
684 						if (*cp)
685 							*rest = '\0';
686 						ptr = rest + 1;
687 					} else
688 						ptr = NULL;
689 					break;
690 				}
691 			}
692 		}
693 
694 		if (!all_skipped) {
695 			if (ret && !*cp)
696 				show_option_values(o);
697 			else if (ret && *cp)
698 				ret = 0;
699 			else if (fn && ptr)
700 				ret = fn(data, ptr);
701 		}
702 
703 		break;
704 	}
705 	case FIO_OPT_RANGE: {
706 		char tmp[128];
707 		char *p1, *p2;
708 
709 		tmp[sizeof(tmp) - 1] = '\0';
710 		strncpy(tmp, ptr, sizeof(tmp) - 1);
711 
712 		/* Handle bsrange with separate read,write values: */
713 		p1 = strchr(tmp, ',');
714 		if (p1)
715 			*p1 = '\0';
716 
717 		p1 = strchr(tmp, '-');
718 		if (!p1) {
719 			p1 = strchr(tmp, ':');
720 			if (!p1) {
721 				ret = 1;
722 				break;
723 			}
724 		}
725 
726 		p2 = p1 + 1;
727 		*p1 = '\0';
728 		p1 = tmp;
729 
730 		ret = 1;
731 		if (!check_range_bytes(p1, &ul1, data) &&
732 		    !check_range_bytes(p2, &ul2, data)) {
733 			ret = 0;
734 			if (ul1 > ul2) {
735 				unsigned long foo = ul1;
736 
737 				ul1 = ul2;
738 				ul2 = foo;
739 			}
740 
741 			if (first) {
742 				val_store(ilp, ul1, o->off1, 0, data, o);
743 				val_store(ilp, ul2, o->off2, 0, data, o);
744 			}
745 			if (curr == 1) {
746 				if (o->off3 && o->off4) {
747 					val_store(ilp, ul1, o->off3, 0, data, o);
748 					val_store(ilp, ul2, o->off4, 0, data, o);
749 				}
750 			}
751 			if (curr == 2) {
752 				if (o->off5 && o->off6) {
753 					val_store(ilp, ul1, o->off5, 0, data, o);
754 					val_store(ilp, ul2, o->off6, 0, data, o);
755 				}
756 			}
757 			if (!more) {
758 				if (curr < 1) {
759 					if (o->off3 && o->off4) {
760 						val_store(ilp, ul1, o->off3, 0, data, o);
761 						val_store(ilp, ul2, o->off4, 0, data, o);
762 					}
763 				}
764 				if (curr < 2) {
765 					if (o->off5 && o->off6) {
766 						val_store(ilp, ul1, o->off5, 0, data, o);
767 						val_store(ilp, ul2, o->off6, 0, data, o);
768 					}
769 				}
770 			}
771 		}
772 
773 		break;
774 	}
775 	case FIO_OPT_BOOL:
776 	case FIO_OPT_STR_SET: {
777 		fio_opt_int_fn *fn = o->cb;
778 
779 		if (ptr)
780 			ret = check_int(ptr, &il);
781 		else if (o->type == FIO_OPT_BOOL)
782 			ret = 1;
783 		else
784 			il = 1;
785 
786 		dprint(FD_PARSE, "  ret=%d, out=%d\n", ret, il);
787 
788 		if (ret)
789 			break;
790 
791 		if (o->maxval && il > (int) o->maxval) {
792 			log_err("max value out of range: %d (%d max)\n",
793 								il, o->maxval);
794 			return 1;
795 		}
796 		if (o->minval && il < o->minval) {
797 			log_err("min value out of range: %d (%d min)\n",
798 								il, o->minval);
799 			return 1;
800 		}
801 
802 		if (o->neg)
803 			il = !il;
804 
805 		if (fn)
806 			ret = fn(data, &il);
807 		else {
808 			if (first)
809 				val_store(ilp, il, o->off1, 0, data, o);
810 			if (!more) {
811 				if (o->off2)
812 					val_store(ilp, il, o->off2, 0, data, o);
813 			}
814 		}
815 		break;
816 	}
817 	case FIO_OPT_DEPRECATED:
818 		log_info("Option %s is deprecated\n", o->name);
819 		ret = 1;
820 		break;
821 	default:
822 		log_err("Bad option type %u\n", o->type);
823 		ret = 1;
824 	}
825 
826 	if (ret)
827 		return ret;
828 
829 	if (o->verify) {
830 		ret = o->verify(o, data);
831 		if (ret) {
832 			log_err("Correct format for offending option\n");
833 			log_err("%20s: %s\n", o->name, o->help);
834 			show_option_help(o, 1);
835 		}
836 	}
837 
838 	return ret;
839 }
840 
handle_option(struct fio_option * o,const char * __ptr,void * data)841 static int handle_option(struct fio_option *o, const char *__ptr, void *data)
842 {
843 	char *o_ptr, *ptr, *ptr2;
844 	int ret, done;
845 
846 	dprint(FD_PARSE, "handle_option=%s, ptr=%s\n", o->name, __ptr);
847 
848 	o_ptr = ptr = NULL;
849 	if (__ptr)
850 		o_ptr = ptr = strdup(__ptr);
851 
852 	/*
853 	 * See if we have another set of parameters, hidden after a comma.
854 	 * Do this before parsing this round, to check if we should
855 	 * copy set 1 options to set 2.
856 	 */
857 	done = 0;
858 	ret = 1;
859 	do {
860 		int __ret;
861 
862 		ptr2 = NULL;
863 		if (ptr &&
864 		    (o->type != FIO_OPT_STR_STORE) &&
865 		    (o->type != FIO_OPT_STR) &&
866 		    (o->type != FIO_OPT_FLOAT_LIST)) {
867 			ptr2 = strchr(ptr, ',');
868 			if (ptr2 && *(ptr2 + 1) == '\0')
869 				*ptr2 = '\0';
870 			if (o->type != FIO_OPT_STR_MULTI && o->type != FIO_OPT_RANGE) {
871 				if (!ptr2)
872 					ptr2 = strchr(ptr, ':');
873 				if (!ptr2)
874 					ptr2 = strchr(ptr, '-');
875 			}
876 		} else if (ptr && o->type == FIO_OPT_FLOAT_LIST) {
877 			ptr2 = strchr(ptr, ':');
878 		}
879 
880 		/*
881 		 * Don't return early if parsing the first option fails - if
882 		 * we are doing multiple arguments, we can allow the first one
883 		 * being empty.
884 		 */
885 		__ret = __handle_option(o, ptr, data, !done, !!ptr2, done);
886 		if (ret)
887 			ret = __ret;
888 
889 		if (!ptr2)
890 			break;
891 
892 		ptr = ptr2 + 1;
893 		done++;
894 	} while (1);
895 
896 	if (o_ptr)
897 		free(o_ptr);
898 	return ret;
899 }
900 
get_option(char * opt,struct fio_option * options,char ** post)901 static struct fio_option *get_option(char *opt,
902 				     struct fio_option *options, char **post)
903 {
904 	struct fio_option *o;
905 	char *ret;
906 
907 	ret = strchr(opt, '=');
908 	if (ret) {
909 		*post = ret;
910 		*ret = '\0';
911 		ret = opt;
912 		(*post)++;
913 		strip_blank_end(ret);
914 		o = find_option(options, ret);
915 	} else {
916 		o = find_option(options, opt);
917 		*post = NULL;
918 	}
919 
920 	return o;
921 }
922 
opt_cmp(const void * p1,const void * p2)923 static int opt_cmp(const void *p1, const void *p2)
924 {
925 	struct fio_option *o;
926 	char *s, *foo;
927 	int prio1, prio2;
928 
929 	prio1 = prio2 = 0;
930 
931 	if (*(char **)p1) {
932 		s = strdup(*((char **) p1));
933 		o = get_option(s, __fio_options, &foo);
934 		if (o)
935 			prio1 = o->prio;
936 		free(s);
937 	}
938 	if (*(char **)p2) {
939 		s = strdup(*((char **) p2));
940 		o = get_option(s, __fio_options, &foo);
941 		if (o)
942 			prio2 = o->prio;
943 		free(s);
944 	}
945 
946 	return prio2 - prio1;
947 }
948 
sort_options(char ** opts,struct fio_option * options,int num_opts)949 void sort_options(char **opts, struct fio_option *options, int num_opts)
950 {
951 	__fio_options = options;
952 	qsort(opts, num_opts, sizeof(char *), opt_cmp);
953 	__fio_options = NULL;
954 }
955 
parse_cmd_option(const char * opt,const char * val,struct fio_option * options,void * data)956 int parse_cmd_option(const char *opt, const char *val,
957 		     struct fio_option *options, void *data)
958 {
959 	struct fio_option *o;
960 
961 	o = find_option(options, opt);
962 	if (!o) {
963 		log_err("Bad option <%s>\n", opt);
964 		return 1;
965 	}
966 
967 	if (!handle_option(o, val, data))
968 		return 0;
969 
970 	log_err("fio: failed parsing %s=%s\n", opt, val);
971 	return 1;
972 }
973 
parse_option(char * opt,const char * input,struct fio_option * options,struct fio_option ** o,void * data,int dump_cmdline)974 int parse_option(char *opt, const char *input,
975 		 struct fio_option *options, struct fio_option **o, void *data,
976 		 int dump_cmdline)
977 {
978 	char *post;
979 
980 	if (!opt) {
981 		log_err("fio: failed parsing %s\n", input);
982 		*o = NULL;
983 		return 1;
984 	}
985 
986 	*o = get_option(opt, options, &post);
987 	if (!*o) {
988 		if (post) {
989 			int len = strlen(opt);
990 			if (opt + len + 1 != post)
991 				memmove(opt + len + 1, post, strlen(post));
992 			opt[len] = '=';
993 		}
994 		return 1;
995 	}
996 
997 	if (handle_option(*o, post, data)) {
998 		log_err("fio: failed parsing %s\n", input);
999 		return 1;
1000 	}
1001 
1002 	if (dump_cmdline) {
1003 		const char *delim;
1004 
1005 		if (!strcmp("description", (*o)->name))
1006 			delim = "\"";
1007 		else
1008 			delim = "";
1009 
1010 		log_info("--%s%s", (*o)->name, post ? "" : " ");
1011 		if (post)
1012 			log_info("=%s%s%s ", delim, post, delim);
1013 	}
1014 
1015 	return 0;
1016 }
1017 
1018 /*
1019  * Option match, levenshtein distance. Handy for not quite remembering what
1020  * the option name is.
1021  */
string_distance(const char * s1,const char * s2)1022 int string_distance(const char *s1, const char *s2)
1023 {
1024 	unsigned int s1_len = strlen(s1);
1025 	unsigned int s2_len = strlen(s2);
1026 	unsigned int *p, *q, *r;
1027 	unsigned int i, j;
1028 
1029 	p = malloc(sizeof(unsigned int) * (s2_len + 1));
1030 	q = malloc(sizeof(unsigned int) * (s2_len + 1));
1031 
1032 	p[0] = 0;
1033 	for (i = 1; i <= s2_len; i++)
1034 		p[i] = p[i - 1] + 1;
1035 
1036 	for (i = 1; i <= s1_len; i++) {
1037 		q[0] = p[0] + 1;
1038 		for (j = 1; j <= s2_len; j++) {
1039 			unsigned int sub = p[j - 1];
1040 			unsigned int pmin;
1041 
1042 			if (s1[i - 1] != s2[j - 1])
1043 				sub++;
1044 
1045 			pmin = min(q[j - 1] + 1, sub);
1046 			q[j] = min(p[j] + 1, pmin);
1047 		}
1048 		r = p;
1049 		p = q;
1050 		q = r;
1051 	}
1052 
1053 	i = p[s2_len];
1054 	free(p);
1055 	free(q);
1056 	return i;
1057 }
1058 
find_child(struct fio_option * options,struct fio_option * o)1059 static struct fio_option *find_child(struct fio_option *options,
1060 				     struct fio_option *o)
1061 {
1062 	struct fio_option *__o;
1063 
1064 	for (__o = options + 1; __o->name; __o++)
1065 		if (__o->parent && !strcmp(__o->parent, o->name))
1066 			return __o;
1067 
1068 	return NULL;
1069 }
1070 
__print_option(struct fio_option * o,struct fio_option * org,int level)1071 static void __print_option(struct fio_option *o, struct fio_option *org,
1072 			   int level)
1073 {
1074 	char name[256], *p;
1075 	int depth;
1076 
1077 	if (!o)
1078 		return;
1079 	if (!org)
1080 		org = o;
1081 
1082 	p = name;
1083 	depth = level;
1084 	while (depth--)
1085 		p += sprintf(p, "%s", "  ");
1086 
1087 	sprintf(p, "%s", o->name);
1088 
1089 	log_info("%-24s: %s\n", name, o->help);
1090 }
1091 
print_option(struct fio_option * o)1092 static void print_option(struct fio_option *o)
1093 {
1094 	struct fio_option *parent;
1095 	struct fio_option *__o;
1096 	unsigned int printed;
1097 	unsigned int level;
1098 
1099 	__print_option(o, NULL, 0);
1100 	parent = o;
1101 	level = 0;
1102 	do {
1103 		level++;
1104 		printed = 0;
1105 
1106 		while ((__o = find_child(o, parent)) != NULL) {
1107 			__print_option(__o, o, level);
1108 			o = __o;
1109 			printed++;
1110 		}
1111 
1112 		parent = o;
1113 	} while (printed);
1114 }
1115 
show_cmd_help(struct fio_option * options,const char * name)1116 int show_cmd_help(struct fio_option *options, const char *name)
1117 {
1118 	struct fio_option *o, *closest;
1119 	unsigned int best_dist = -1U;
1120 	int found = 0;
1121 	int show_all = 0;
1122 
1123 	if (!name || !strcmp(name, "all"))
1124 		show_all = 1;
1125 
1126 	closest = NULL;
1127 	best_dist = -1;
1128 	for (o = &options[0]; o->name; o++) {
1129 		int match = 0;
1130 
1131 		if (o->type == FIO_OPT_DEPRECATED)
1132 			continue;
1133 		if (!exec_profile && o->prof_name)
1134 			continue;
1135 		if (exec_profile && !(o->prof_name && !strcmp(exec_profile, o->prof_name)))
1136 			continue;
1137 
1138 		if (name) {
1139 			if (!strcmp(name, o->name) ||
1140 			    (o->alias && !strcmp(name, o->alias)))
1141 				match = 1;
1142 			else {
1143 				unsigned int dist;
1144 
1145 				dist = string_distance(name, o->name);
1146 				if (dist < best_dist) {
1147 					best_dist = dist;
1148 					closest = o;
1149 				}
1150 			}
1151 		}
1152 
1153 		if (show_all || match) {
1154 			found = 1;
1155 			if (match)
1156 				log_info("%20s: %s\n", o->name, o->help);
1157 			if (show_all) {
1158 				if (!o->parent)
1159 					print_option(o);
1160 				continue;
1161 			}
1162 		}
1163 
1164 		if (!match)
1165 			continue;
1166 
1167 		show_option_help(o, 0);
1168 	}
1169 
1170 	if (found)
1171 		return 0;
1172 
1173 	log_err("No such command: %s", name);
1174 
1175 	/*
1176 	 * Only print an appropriately close option, one where the edit
1177 	 * distance isn't too big. Otherwise we get crazy matches.
1178 	 */
1179 	if (closest && best_dist < 3) {
1180 		log_info(" - showing closest match\n");
1181 		log_info("%20s: %s\n", closest->name, closest->help);
1182 		show_option_help(closest, 0);
1183 	} else
1184 		log_info("\n");
1185 
1186 	return 1;
1187 }
1188 
1189 /*
1190  * Handle parsing of default parameters.
1191  */
fill_default_options(void * data,struct fio_option * options)1192 void fill_default_options(void *data, struct fio_option *options)
1193 {
1194 	struct fio_option *o;
1195 
1196 	dprint(FD_PARSE, "filling default options\n");
1197 
1198 	for (o = &options[0]; o->name; o++)
1199 		if (o->def)
1200 			handle_option(o, o->def, data);
1201 }
1202 
option_init(struct fio_option * o)1203 void option_init(struct fio_option *o)
1204 {
1205 	if (o->type == FIO_OPT_DEPRECATED)
1206 		return;
1207 	if (o->type == FIO_OPT_BOOL) {
1208 		o->minval = 0;
1209 		o->maxval = 1;
1210 	}
1211 	if (o->type == FIO_OPT_INT) {
1212 		if (!o->maxval)
1213 			o->maxval = UINT_MAX;
1214 	}
1215 	if (o->type == FIO_OPT_FLOAT_LIST) {
1216 		o->minfp = DBL_MIN;
1217 		o->maxfp = DBL_MAX;
1218 	}
1219 	if (o->type == FIO_OPT_STR_SET && o->def && !o->no_warn_def) {
1220 		log_err("Option %s: string set option with"
1221 				" default will always be true\n", o->name);
1222 	}
1223 	if (!o->cb && !o->off1)
1224 		log_err("Option %s: neither cb nor offset given\n", o->name);
1225 	if (!o->category) {
1226 		log_info("Option %s: no category defined. Setting to misc\n", o->name);
1227 		o->category = FIO_OPT_C_GENERAL;
1228 		o->group = FIO_OPT_G_INVALID;
1229 	}
1230 	if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE ||
1231 	    o->type == FIO_OPT_STR_MULTI)
1232 		return;
1233 }
1234 
1235 /*
1236  * Sanitize the options structure. For now it just sets min/max for bool
1237  * values and whether both callback and offsets are given.
1238  */
options_init(struct fio_option * options)1239 void options_init(struct fio_option *options)
1240 {
1241 	struct fio_option *o;
1242 
1243 	dprint(FD_PARSE, "init options\n");
1244 
1245 	for (o = &options[0]; o->name; o++) {
1246 		option_init(o);
1247 		if (o->inverse)
1248 			o->inv_opt = find_option(options, o->inverse);
1249 	}
1250 }
1251 
options_free(struct fio_option * options,void * data)1252 void options_free(struct fio_option *options, void *data)
1253 {
1254 	struct fio_option *o;
1255 	char **ptr;
1256 
1257 	dprint(FD_PARSE, "free options\n");
1258 
1259 	for (o = &options[0]; o->name; o++) {
1260 		if (o->type != FIO_OPT_STR_STORE || !o->off1)
1261 			continue;
1262 
1263 		ptr = td_var(data, o, o->off1);
1264 		if (*ptr) {
1265 			free(*ptr);
1266 			*ptr = NULL;
1267 		}
1268 	}
1269 }
1270