xref: /onnv-gate/usr/src/cmd/filebench/common/parser_gram.y (revision 8404:b96b8ad1c3e9)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Portions Copyright 2008 Denis Cheng
26  */
27 
28 %{
29 
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <locale.h>
37 #include <sys/utsname.h>
38 #ifdef HAVE_STDINT_H
39 #include <stdint.h>
40 #endif
41 #include <fcntl.h>
42 #include <sys/mman.h>
43 #include <sys/wait.h>
44 #ifdef HAVE_LIBTECLA
45 #include <libtecla.h>
46 #endif
47 #include "parsertypes.h"
48 #include "filebench.h"
49 #include "utils.h"
50 #include "stats.h"
51 #include "vars.h"
52 #include "eventgen.h"
53 #ifdef HAVE_LIBTECLA
54 #include "auto_comp.h"
55 #endif
56 #include "multi_client_sync.h"
57 
58 int dofile = FS_FALSE;
59 static const char cmdname[] = "filebench";
60 static const char cmd_options[] = "pa:f:hi:s:m:";
61 static void usage(int);
62 
63 static cmd_t *cmd = NULL;		/* Command being processed */
64 #ifdef HAVE_LIBTECLA
65 static GetLine *gl;			/* GetLine resource object */
66 #endif
67 
68 char *execname;
69 char *fscriptname;
70 int noproc = 0;
71 var_t *var_list = NULL;
72 pidlist_t *pidlist = NULL;
73 char *cwd = NULL;
74 FILE *parentscript = NULL;
75 
76 static int filecreate_done = 0;
77 
78 /* yacc externals */
79 extern FILE *yyin;
80 extern int yydebug;
81 extern void yyerror(char *s);
82 
83 /* utilities */
84 static void terminate(void);
85 static cmd_t *alloc_cmd(void);
86 static attr_t *alloc_attr(void);
87 static attr_t *alloc_lvar_attr(var_t *var);
88 static attr_t *get_attr(cmd_t *cmd, int64_t name);
89 static attr_t *get_attr_fileset(cmd_t *cmd, int64_t name);
90 static attr_t *get_attr_integer(cmd_t *cmd, int64_t name);
91 static attr_t *get_attr_bool(cmd_t *cmd, int64_t name);
92 static void get_attr_lvars(cmd_t *cmd, flowop_t *flowop);
93 static var_t *alloc_var(void);
94 static var_t *get_var(cmd_t *cmd, int64_t name);
95 static list_t *alloc_list();
96 static probtabent_t *alloc_probtabent(void);
97 static void add_lvar_to_list(var_t *newlvar, var_t **lvar_list);
98 
99 /* Info Commands */
100 static void parser_list(cmd_t *);
101 static void parser_flowop_list(cmd_t *);
102 
103 /* Define Commands */
104 static void parser_proc_define(cmd_t *);
105 static void parser_thread_define(cmd_t *, procflow_t *, int instances);
106 static void parser_flowop_define(cmd_t *, threadflow_t *, flowop_t **, int);
107 static void parser_file_define(cmd_t *);
108 static void parser_fileset_define(cmd_t *);
109 static void parser_randvar_define(cmd_t *);
110 static void parser_randvar_set(cmd_t *);
111 static void parser_composite_flowop_define(cmd_t *);
112 
113 /* Create Commands */
114 static void parser_proc_create(cmd_t *);
115 static void parser_thread_create(cmd_t *);
116 static void parser_flowop_create(cmd_t *);
117 static void parser_fileset_create(cmd_t *);
118 
119 /* set commands */
120 static void parser_set_integer(char *, fbint_t);
121 static void parser_set_var(char *, char *);
122 
123 /* Shutdown Commands */
124 static void parser_proc_shutdown(cmd_t *);
125 static void parser_filebench_shutdown(cmd_t *cmd);
126 
127 /* Other Commands */
128 static void parser_foreach_integer(cmd_t *cmd);
129 static void parser_foreach_string(cmd_t *cmd);
130 static void parser_sleep(cmd_t *cmd);
131 static void parser_sleep_variable(cmd_t *cmd);
132 static void parser_log(cmd_t *cmd);
133 static void parser_statscmd(cmd_t *cmd);
134 static void parser_statsdump(cmd_t *cmd);
135 static void parser_statsxmldump(cmd_t *cmd);
136 static void parser_statsmultidump(cmd_t *cmd);
137 static void parser_echo(cmd_t *cmd);
138 static void parser_usage(cmd_t *cmd);
139 static void parser_vars(cmd_t *cmd);
140 static void parser_printvars(cmd_t *cmd);
141 static void parser_system(cmd_t *cmd);
142 static void parser_statssnap(cmd_t *cmd);
143 static void parser_directory(cmd_t *cmd);
144 static void parser_eventgen(cmd_t *cmd);
145 static void parser_enable_mc(cmd_t *cmd);
146 static void parser_domultisync(cmd_t *cmd);
147 static void parser_run(cmd_t *cmd);
148 static void parser_run_variable(cmd_t *cmd);
149 static void parser_help(cmd_t *cmd);
150 static void arg_parse(const char *command);
151 static void parser_abort(int arg);
152 static void parser_version(cmd_t *cmd);
153 
154 %}
155 
156 %union {
157 	int64_t		 ival;
158 	uchar_t		 bval;
159 	char *		 sval;
160 	fs_u		 val;
161 	avd_t		 avd;
162 	cmd_t		*cmd;
163 	attr_t		*attr;
164 	list_t		*list;
165 	probtabent_t	*rndtb;
166 }
167 
168 %start commands
169 
170 %token FSC_LIST FSC_DEFINE FSC_EXEC FSC_QUIT FSC_DEBUG FSC_CREATE
171 %token FSC_SLEEP FSC_STATS FSC_FOREACH FSC_SET FSC_SHUTDOWN FSC_LOG
172 %token FSC_SYSTEM FSC_FLOWOP FSC_EVENTGEN FSC_ECHO FSC_LOAD FSC_RUN
173 %token FSC_USAGE FSC_HELP FSC_VARS FSC_VERSION FSC_ENABLE FSC_DOMULTISYNC
174 %token FSV_STRING FSV_VAL_INT FSV_VAL_BOOLEAN FSV_VARIABLE FSV_WHITESTRING
175 %token FSV_RANDUNI FSV_RANDTAB FSV_RANDVAR FSV_URAND FSV_RAND48
176 %token FST_INT FST_BOOLEAN
177 %token FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSE_ALL FSE_SNAP FSE_DUMP
178 %token FSE_DIRECTORY FSE_COMMAND FSE_FILESET FSE_XMLDUMP FSE_RAND FSE_MODE
179 %token FSE_MULTI FSE_MULTIDUMP
180 %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_ASSIGN FSK_IN FSK_QUOTE
181 %token FSK_DIRSEPLST
182 %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE
183 %token FSA_PROCESS FSA_MEMSIZE FSA_RATE FSA_CACHED
184 %token FSA_IOSIZE FSA_FILE FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES
185 %token FSA_DSYNC FSA_TARGET FSA_ITERS FSA_NICE FSA_VALUE FSA_BLOCKING
186 %token FSA_HIGHWATER FSA_DIRECTIO FSA_DIRWIDTH FSA_FD FSA_SRCFD FSA_ROTATEFD
187 %token FSA_NAMELENGTH FSA_FILESIZE FSA_ENTRIES FSA_FILESIZEGAMMA FSA_DIRDEPTHRV
188 %token FSA_DIRGAMMA FSA_USEISM FSA_TYPE FSA_RANDTABLE FSA_RANDSRC FSA_RANDROUND
189 %token FSA_LEAFDIRS FSA_INDEXED
190 %token FSA_RANDSEED FSA_RANDGAMMA FSA_RANDMEAN FSA_RANDMIN FSA_MASTER
191 %token FSA_CLIENT
192 %token FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC FSS_ROUND
193 %token FSV_SET_LOCAL_VAR FSA_LVAR_ASSIGN
194 %token FSA_ALLDONE FSA_FIRSTDONE FSA_TIMEOUT
195 
196 %type <ival> FSV_VAL_INT
197 %type <bval> FSV_VAL_BOOLEAN
198 %type <sval> FSV_STRING
199 %type <sval> FSV_WHITESTRING
200 %type <sval> FSV_VARIABLE
201 %type <sval> FSV_RANDVAR
202 %type <sval> FSK_ASSIGN
203 %type <sval> FSV_SET_LOCAL_VAR
204 
205 %type <ival> FSC_LIST FSC_DEFINE FSC_SET FSC_LOAD FSC_RUN FSC_ENABLE
206 %type <ival> FSC_DOMULTISYNC
207 %type <ival> FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSC_HELP FSC_VERSION
208 
209 %type <sval> name
210 %type <ival> entity
211 %type <val>  value
212 
213 %type <cmd> command inner_commands load_command run_command list_command
214 %type <cmd> proc_define_command files_define_command randvar_define_command
215 %type <cmd> fo_define_command debug_command create_command
216 %type <cmd> sleep_command stats_command set_command shutdown_command
217 %type <cmd> foreach_command log_command system_command flowop_command
218 %type <cmd> eventgen_command quit_command flowop_list thread_list
219 %type <cmd> thread echo_command usage_command help_command vars_command
220 %type <cmd> version_command enable_command multisync_command
221 
222 %type <attr> files_attr_op files_attr_ops pt_attr_op pt_attr_ops
223 %type <attr> fo_attr_op fo_attr_ops ev_attr_op ev_attr_ops
224 %type <attr> randvar_attr_op randvar_attr_ops randvar_attr_typop
225 %type <attr> randvar_attr_srcop attr_value attr_list_value
226 %type <attr> comp_lvar_def comp_attr_op comp_attr_ops
227 %type <attr> enable_multi_ops enable_multi_op multisync_op
228 %type <list> integer_seplist string_seplist string_list var_string_list
229 %type <list> var_string whitevar_string whitevar_string_list
230 %type <ival> attrs_define_file attrs_define_thread attrs_flowop
231 %type <ival> attrs_define_fileset attrs_define_proc attrs_eventgen attrs_define_comp
232 %type <ival> files_attr_name pt_attr_name fo_attr_name ev_attr_name
233 %type <ival> randvar_attr_name FSA_TYPE randtype_name randvar_attr_param
234 %type <ival> randsrc_name FSA_RANDSRC randvar_attr_tsp em_attr_name
235 %type <ival> FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC
236 
237 %type <rndtb>  probtabentry_list probtabentry
238 %type <avd> var_int_val
239 %%
240 
241 commands: commands command
242 {
243 	list_t *list = NULL;
244 	list_t *list_end = NULL;
245 
246 	if ($2->cmd != NULL)
247 		$2->cmd($2);
248 
249 	free($2);
250 }
251 | commands error
252 {
253 	if (dofile)
254 		YYABORT;
255 }
256 |;
257 
258 inner_commands: command
259 {
260 	filebench_log(LOG_DEBUG_IMPL, "inner_command %zx", $1);
261 	$$ = $1;
262 }
263 | inner_commands command
264 {
265 	cmd_t *list = NULL;
266 	cmd_t *list_end = NULL;
267 
268 	/* Find end of list */
269 	for (list = $1; list != NULL;
270 	    list = list->cmd_next)
271 		list_end = list;
272 
273 	list_end->cmd_next = $2;
274 
275 	filebench_log(LOG_DEBUG_IMPL,
276 	    "inner_commands adding cmd %zx to list %zx", $2, $1);
277 
278 	$$ = $1;
279 };
280 
281 command:
282   proc_define_command
283 | files_define_command
284 | randvar_define_command
285 | fo_define_command
286 | debug_command
287 | eventgen_command
288 | create_command
289 | echo_command
290 | usage_command
291 | vars_command
292 | foreach_command
293 | help_command
294 | list_command
295 | load_command
296 | log_command
297 | run_command
298 | set_command
299 | shutdown_command
300 | sleep_command
301 | stats_command
302 | system_command
303 | version_command
304 | enable_command
305 | multisync_command
306 | quit_command;
307 
308 foreach_command: FSC_FOREACH
309 {
310 	if (($$ = alloc_cmd()) == NULL)
311 		YYERROR;
312 	filebench_log(LOG_DEBUG_IMPL, "foreach_command %zx", $$);
313 }
314 | foreach_command FSV_VARIABLE FSK_IN integer_seplist FSK_OPENLST inner_commands FSK_CLOSELST
315 {
316 	cmd_t *cmd, *inner_cmd;
317 	list_t *list;
318 
319 	$$ = $1;
320 	$$->cmd_list = $6;
321 	$$->cmd_tgt1 = $2;
322 	$$->cmd_param_list = $4;
323 	$$->cmd = parser_foreach_integer;
324 
325 	for (list = $$->cmd_param_list; list != NULL;
326 	    list = list->list_next) {
327 		for (inner_cmd = $$->cmd_list;
328 		    inner_cmd != NULL;
329 		    inner_cmd = inner_cmd->cmd_next) {
330 			filebench_log(LOG_DEBUG_IMPL,
331 			    "packing foreach: %zx %s=%llu, cmd %zx",
332 			    $$, $$->cmd_tgt1,
333 			    (u_longlong_t)avd_get_int(list->list_integer),
334 			    inner_cmd);
335 		}
336 	}
337 }| foreach_command FSV_VARIABLE FSK_IN string_seplist FSK_OPENLST inner_commands FSK_CLOSELST
338 {
339 	cmd_t *cmd, *inner_cmd;
340 	list_t *list;
341 
342 	$$ = $1;
343 	$$->cmd_list = $6;
344 	$$->cmd_tgt1 = $2;
345 	$$->cmd_param_list = $4;
346 	$$->cmd = parser_foreach_string;
347 
348 	for (list = $$->cmd_param_list; list != NULL;
349 	    list = list->list_next) {
350 		for (inner_cmd = $$->cmd_list;
351 		    inner_cmd != NULL;
352 		    inner_cmd = inner_cmd->cmd_next) {
353 			filebench_log(LOG_DEBUG_IMPL,
354 			    "packing foreach: %zx %s=%s, cmd %zx",
355 			    $$,
356 			    $$->cmd_tgt1,
357 			    *list->list_string, inner_cmd);
358 		}
359 	}
360 };
361 
362 integer_seplist: FSV_VAL_INT
363 {
364 	if (($$ = alloc_list()) == NULL)
365 		YYERROR;
366 
367 	$$->list_integer = avd_int_alloc($1);
368 }
369 | integer_seplist FSK_SEPLST FSV_VAL_INT
370 {
371 	list_t *list = NULL;
372 	list_t *list_end = NULL;
373 
374 	if (($$ = alloc_list()) == NULL)
375 		YYERROR;
376 
377 	$$->list_integer = avd_int_alloc($3);
378 
379 	/* Find end of list */
380 	for (list = $1; list != NULL;
381 	    list = list->list_next)
382 		list_end = list;
383 	list_end->list_next = $$;
384 	$$ = $1;
385 };
386 
387 string_seplist: FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
388 {
389 	if (($$ = alloc_list()) == NULL)
390 		YYERROR;
391 
392 	$$->list_string = avd_str_alloc($2);
393 }
394 | string_seplist FSK_SEPLST FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
395 {
396 	list_t *list = NULL;
397 	list_t *list_end = NULL;
398 
399 	if (($$ = alloc_list()) == NULL)
400 			YYERROR;
401 
402 	$$->list_string = avd_str_alloc($4);
403 
404 	/* Find end of list */
405 	for (list = $1; list != NULL;
406 	    list = list->list_next)
407 		list_end = list;
408 	list_end->list_next = $$;
409 	$$ = $1;
410 };
411 
412 eventgen_command: FSC_EVENTGEN
413 {
414 	if (($$ = alloc_cmd()) == NULL)
415 		YYERROR;
416 	$$->cmd = &parser_eventgen;
417 }
418 | eventgen_command ev_attr_ops
419 {
420 	$1->cmd_attr_list = $2;
421 };
422 
423 system_command: FSC_SYSTEM whitevar_string_list
424 {
425 	if (($$ = alloc_cmd()) == NULL)
426 		YYERROR;
427 
428 	$$->cmd_param_list = $2;
429 	$$->cmd = parser_system;
430 };
431 
432 echo_command: FSC_ECHO whitevar_string_list
433 {
434 	if (($$ = alloc_cmd()) == NULL)
435 		YYERROR;
436 
437 	$$->cmd_param_list = $2;
438 	$$->cmd = parser_echo;
439 };
440 
441 version_command: FSC_VERSION
442 {
443 	if (($$ = alloc_cmd()) == NULL)
444 		YYERROR;
445 	$$->cmd = parser_version;
446 };
447 
448 usage_command: FSC_USAGE whitevar_string_list
449 {
450 	if (($$ = alloc_cmd()) == NULL)
451 		YYERROR;
452 
453 	$$->cmd_param_list = $2;
454 	$$->cmd = parser_usage;
455 };
456 
457 vars_command: FSC_VARS
458 {
459 	if (($$ = alloc_cmd()) == NULL)
460 		YYERROR;
461 
462 	$$->cmd = parser_printvars;
463 };
464 
465 enable_command: FSC_ENABLE FSE_MULTI
466 {
467 	if (($$ = alloc_cmd()) == NULL)
468 		YYERROR;
469 
470 	$$->cmd = parser_enable_mc;
471 }
472 | enable_command  enable_multi_ops
473 {
474 	$1->cmd_attr_list = $2;
475 };
476 
477 multisync_command: FSC_DOMULTISYNC multisync_op
478 {
479 	if (($$ = alloc_cmd()) == NULL)
480 		YYERROR;
481 
482 	$$->cmd = parser_domultisync;
483 	$$->cmd_attr_list = $2;
484 }
485 
486 string_list: FSV_VARIABLE
487 {
488 	if (($$ = alloc_list()) == NULL)
489 			YYERROR;
490 	$$->list_string = avd_str_alloc($1);
491 }
492 | string_list FSK_SEPLST FSV_VARIABLE
493 {
494 	list_t *list = NULL;
495 	list_t *list_end = NULL;
496 
497 	if (($$ = alloc_list()) == NULL)
498 		YYERROR;
499 
500 	$$->list_string = avd_str_alloc($3);
501 
502 	/* Find end of list */
503 	for (list = $1; list != NULL;
504 	    list = list->list_next)
505 		list_end = list;
506 	list_end->list_next = $$;
507 	$$ = $1;
508 };
509 
510 var_string: FSV_VARIABLE
511 {
512 	if (($$ = alloc_list()) == NULL)
513 			YYERROR;
514 
515 	$$->list_string = avd_str_alloc($1);
516 }
517 | FSV_STRING
518 {
519 	if (($$ = alloc_list()) == NULL)
520 			YYERROR;
521 
522 	$$->list_string = avd_str_alloc($1);
523 };
524 
525 var_string_list: var_string
526 {
527 	$$ = $1;
528 }| var_string FSV_STRING
529 {
530 	list_t *list = NULL;
531 	list_t *list_end = NULL;
532 
533 	/* Add string */
534 	if (($$ = alloc_list()) == NULL)
535 		YYERROR;
536 
537 	$$->list_string = avd_str_alloc($2);
538 
539 	/* Find end of list */
540 	for (list = $1; list != NULL;
541 	    list = list->list_next)
542 		list_end = list;
543 	list_end->list_next = $$;
544 	$$ = $1;
545 
546 }| var_string FSV_VARIABLE
547 {
548 	list_t *list = NULL;
549 	list_t *list_end = NULL;
550 
551 	/* Add variable */
552 	if (($$ = alloc_list()) == NULL)
553 		YYERROR;
554 
555 	$$->list_string = avd_str_alloc($2);
556 
557 	/* Find end of list */
558 	for (list = $1; list != NULL;
559 	    list = list->list_next)
560 		list_end = list;
561 	list_end->list_next = $$;
562 	$$ = $1;
563 } |var_string_list FSV_STRING
564 {
565 	list_t *list = NULL;
566 	list_t *list_end = NULL;
567 
568 	/* Add string */
569 	if (($$ = alloc_list()) == NULL)
570 		YYERROR;
571 
572 	$$->list_string = avd_str_alloc($2);
573 
574 	/* Find end of list */
575 	for (list = $1; list != NULL;
576 	    list = list->list_next)
577 		list_end = list;
578 	list_end->list_next = $$;
579 	$$ = $1;
580 
581 }| var_string_list FSV_VARIABLE
582 {
583 	list_t *list = NULL;
584 	list_t *list_end = NULL;
585 
586 	/* Add variable */
587 	if (($$ = alloc_list()) == NULL)
588 		YYERROR;
589 
590 	$$->list_string = avd_str_alloc($2);
591 
592 	/* Find end of list */
593 	for (list = $1; list != NULL;
594 	    list = list->list_next)
595 		list_end = list;
596 	list_end->list_next = $$;
597 	$$ = $1;
598 };
599 
600 whitevar_string: FSK_QUOTE FSV_VARIABLE
601 {
602 	if (($$ = alloc_list()) == NULL)
603 			YYERROR;
604 
605 	$$->list_string = avd_str_alloc($2);
606 }
607 | FSK_QUOTE FSV_WHITESTRING
608 {
609 	if (($$ = alloc_list()) == NULL)
610 			YYERROR;
611 
612 	$$->list_string = avd_str_alloc($2);
613 };
614 
615 whitevar_string_list: whitevar_string FSV_WHITESTRING
616 {
617 	list_t *list = NULL;
618 	list_t *list_end = NULL;
619 
620 	/* Add string */
621 	if (($$ = alloc_list()) == NULL)
622 		YYERROR;
623 
624 	$$->list_string = avd_str_alloc($2);
625 
626 	/* Find end of list */
627 	for (list = $1; list != NULL;
628 	    list = list->list_next)
629 		list_end = list;
630 	list_end->list_next = $$;
631 	$$ = $1;
632 
633 }| whitevar_string FSV_VARIABLE
634 {
635 	list_t *list = NULL;
636 	list_t *list_end = NULL;
637 
638 	/* Add variable */
639 	if (($$ = alloc_list()) == NULL)
640 		YYERROR;
641 
642 	$$->list_string = avd_str_alloc($2);
643 
644 	/* Find end of list */
645 	for (list = $1; list != NULL;
646 	    list = list->list_next)
647 		list_end = list;
648 	list_end->list_next = $$;
649 	$$ = $1;
650 }| whitevar_string FSV_RANDVAR randvar_attr_tsp
651 {
652 	list_t *list = NULL;
653 	list_t *list_end = NULL;
654 
655 	/* Add variable */
656 	if (($$ = alloc_list()) == NULL)
657 		YYERROR;
658 
659 	$$->list_string = avd_str_alloc($2);
660 	$$->list_integer = avd_int_alloc($3);
661 
662 	/* Find end of list */
663 	for (list = $1; list != NULL;
664 	    list = list->list_next)
665 		list_end = list;
666 	list_end->list_next = $$;
667 	$$ = $1;
668 }| whitevar_string_list FSV_WHITESTRING
669 {
670 	list_t *list = NULL;
671 	list_t *list_end = NULL;
672 
673 	/* Add string */
674 	if (($$ = alloc_list()) == NULL)
675 		YYERROR;
676 
677 	$$->list_string = avd_str_alloc($2);
678 
679 	/* Find end of list */
680 	for (list = $1; list != NULL;
681 	    list = list->list_next)
682 		list_end = list;
683 	list_end->list_next = $$;
684 	$$ = $1;
685 
686 }| whitevar_string_list FSV_VARIABLE
687 {
688 	list_t *list = NULL;
689 	list_t *list_end = NULL;
690 
691 	/* Add variable */
692 	if (($$ = alloc_list()) == NULL)
693 		YYERROR;
694 
695 	$$->list_string = avd_str_alloc($2);
696 
697 	/* Find end of list */
698 	for (list = $1; list != NULL;
699 	    list = list->list_next)
700 		list_end = list;
701 	list_end->list_next = $$;
702 	$$ = $1;
703 }| whitevar_string_list FSV_RANDVAR randvar_attr_tsp
704 {
705 	list_t *list = NULL;
706 	list_t *list_end = NULL;
707 
708 	/* Add variable */
709 	if (($$ = alloc_list()) == NULL)
710 		YYERROR;
711 
712 	$$->list_string = avd_str_alloc($2);
713 	$$->list_integer = avd_int_alloc($3);
714 
715 	/* Find end of list */
716 	for (list = $1; list != NULL;
717 	    list = list->list_next)
718 		list_end = list;
719 	list_end->list_next = $$;
720 	$$ = $1;
721 }| whitevar_string_list FSK_QUOTE
722 {
723 	$$ = $1;
724 }| whitevar_string FSK_QUOTE
725 {
726 	$$ = $1;
727 };
728 
729 list_command: FSC_LIST
730 {
731 	if (($$ = alloc_cmd()) == NULL)
732 		YYERROR;
733 	$$->cmd = &parser_list;
734 }
735 | list_command FSC_FLOWOP
736 {
737 	$1->cmd = &parser_flowop_list;
738 };
739 
740 log_command: FSC_LOG whitevar_string_list
741 {
742 	if (($$ = alloc_cmd()) == NULL)
743 		YYERROR;
744 	$$->cmd = &parser_log;
745 	$$->cmd_param_list = $2;
746 };
747 
748 debug_command: FSC_DEBUG FSV_VAL_INT
749 {
750 	if (($$ = alloc_cmd()) == NULL)
751 		YYERROR;
752 	$$->cmd = NULL;
753 	filebench_shm->shm_debug_level = $2;
754 	if (filebench_shm->shm_debug_level > 9)
755 		yydebug = 1;
756 };
757 
758 set_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT
759 {
760 	if (($$ = alloc_cmd()) == NULL)
761 		YYERROR;
762 	var_assign_integer($2, $4);
763 	if (parentscript) {
764 		$$->cmd_tgt1 = $2;
765 		parser_vars($$);
766 	}
767 	$$->cmd = NULL;
768 }
769 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
770 {
771 	if (($$ = alloc_cmd()) == NULL)
772 		YYERROR;
773 	var_assign_boolean($2, $4);
774 	if (parentscript) {
775 		$$->cmd_tgt1 = $2;
776 		parser_vars($$);
777 	}
778 	$$->cmd = NULL;
779 }
780 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
781 {
782 	if (($$ = alloc_cmd()) == NULL)
783 		YYERROR;
784 	var_assign_string($2, $5);
785 	if (parentscript) {
786 		$$->cmd_tgt1 = $2;
787 		parser_vars($$);
788 	}
789 	$$->cmd = NULL;
790 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_STRING
791 {
792 	if (($$ = alloc_cmd()) == NULL)
793 		YYERROR;
794 	var_assign_string($2, $4);
795 	if (parentscript) {
796 		$$->cmd_tgt1 = $2;
797 		parser_vars($$);
798 	}
799 	$$->cmd = NULL;
800 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
801 {
802 	if (($$ = alloc_cmd()) == NULL)
803 		YYERROR;
804 	var_assign_var($2, $4);
805 	if (parentscript) {
806 		$$->cmd_tgt1 = $2;
807 		parser_vars($$);
808 	}
809 	$$->cmd = NULL;
810 } | FSC_SET FSE_MODE FSC_QUIT FSA_TIMEOUT
811 {
812 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
813 	if (($$ = alloc_cmd()) == NULL)
814 		YYERROR;
815 	$$->cmd = NULL;
816 } | FSC_SET FSE_MODE FSC_QUIT FSA_ALLDONE
817 {
818 	filebench_shm->shm_rmode = FILEBENCH_MODE_QALLDONE;
819 	if (($$ = alloc_cmd()) == NULL)
820 		YYERROR;
821 	$$->cmd = NULL;
822 } | FSC_SET FSE_MODE FSC_QUIT FSA_FIRSTDONE
823 {
824 	filebench_shm->shm_rmode = FILEBENCH_MODE_Q1STDONE;
825 	if (($$ = alloc_cmd()) == NULL)
826 		YYERROR;
827 	$$->cmd = NULL;
828 }| FSC_SET FSV_RANDVAR FSS_TYPE FSK_ASSIGN randvar_attr_typop
829 {
830 	if (($$ = alloc_cmd()) == NULL)
831 		YYERROR;
832 	$$->cmd = &parser_randvar_set;
833 	$$->cmd_tgt1 = $2;
834 	$$->cmd_qty = FSS_TYPE;
835 	$$->cmd_attr_list = $5;
836 
837 }| FSC_SET FSV_RANDVAR FSS_SRC FSK_ASSIGN randvar_attr_srcop
838 {
839 	if (($$ = alloc_cmd()) == NULL)
840 		YYERROR;
841 	$$->cmd = &parser_randvar_set;
842 	$$->cmd_tgt1 = $2;
843 	$$->cmd_qty = FSS_SRC;
844 	$$->cmd_attr_list = $5;
845 
846 }| FSC_SET FSV_RANDVAR randvar_attr_param FSK_ASSIGN attr_value
847 {
848 	if (($$ = alloc_cmd()) == NULL)
849 		YYERROR;
850 	$$->cmd = &parser_randvar_set;
851 	$$->cmd_tgt1 = $2;
852 	$$->cmd_qty = $3;
853 	$$->cmd_attr_list = $5;
854 
855 };
856 
857 stats_command: FSC_STATS FSE_SNAP
858 {
859 	if (($$ = alloc_cmd()) == NULL)
860 		YYERROR;
861 	$$->cmd = (void (*)(struct cmd *))&parser_statssnap;
862 	break;
863 
864 }
865 | FSC_STATS FSE_CLEAR
866 {
867 	if (($$ = alloc_cmd()) == NULL)
868 		YYERROR;
869 	$$->cmd = (void (*)(struct cmd *))&stats_clear;
870 
871 }
872 | FSC_STATS FSE_DIRECTORY var_string_list
873 {
874 	if (($$ = alloc_cmd()) == NULL)
875 		YYERROR;
876 	$$->cmd_param_list = $3;
877 	$$->cmd = (void (*)(struct cmd *))&parser_directory;
878 
879 }
880 | FSC_STATS FSE_COMMAND whitevar_string_list
881 {
882 	if (($$ = alloc_cmd()) == NULL)
883 		YYERROR;
884 
885 	$$->cmd_param_list = $3;
886 	$$->cmd = parser_statscmd;
887 
888 }| FSC_STATS FSE_DUMP whitevar_string_list
889 {
890 	if (($$ = alloc_cmd()) == NULL)
891 		YYERROR;
892 
893 	$$->cmd_param_list = $3;
894 	$$->cmd = parser_statsdump;
895 }| FSC_STATS FSE_XMLDUMP whitevar_string_list
896 {
897 	if (($$ = alloc_cmd()) == NULL)
898 		YYERROR;
899 
900 	$$->cmd_param_list = $3;
901 	$$->cmd = parser_statsxmldump;
902 }| FSC_STATS FSE_MULTIDUMP whitevar_string_list
903 {
904 	if (($$ = alloc_cmd()) == NULL)
905 		YYERROR;
906 
907 	$$->cmd_param_list = $3;
908 	$$->cmd = parser_statsmultidump;
909 };
910 
911 quit_command: FSC_QUIT
912 {
913 	if (($$ = alloc_cmd()) == NULL)
914 		YYERROR;
915 	$$->cmd = parser_filebench_shutdown;
916 };
917 
918 flowop_list: flowop_command
919 {
920 	$$ = $1;
921 }| flowop_list flowop_command
922 {
923 	cmd_t *list = NULL;
924 	cmd_t *list_end = NULL;
925 
926 	/* Find end of list */
927 	for (list = $1; list != NULL;
928 	    list = list->cmd_next)
929 		list_end = list;
930 
931 	list_end->cmd_next = $2;
932 
933 	filebench_log(LOG_DEBUG_IMPL,
934 	    "flowop_list adding cmd %zx to list %zx", $2, $1);
935 
936 	$$ = $1;
937 };
938 
939 thread: FSE_THREAD pt_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST
940 {
941 	/*
942 	 * Allocate a cmd node per thread, with a
943 	 * list of flowops attached to the cmd_list
944 	 */
945 	if (($$ = alloc_cmd()) == NULL)
946 		YYERROR;
947 	$$->cmd_list = $4;
948 	$$->cmd_attr_list = $2;
949 };
950 
951 thread_list: thread
952 {
953 	$$ = $1;
954 }| thread_list thread
955 {
956 	cmd_t *list = NULL;
957 	cmd_t *list_end = NULL;
958 
959 	/* Find end of list */
960 	for (list = $1; list != NULL;
961 	    list = list->cmd_next)
962 		list_end = list;
963 
964 	list_end->cmd_next = $2;
965 
966 	filebench_log(LOG_DEBUG_IMPL,
967 	    "thread_list adding cmd %zx to list %zx", $2, $1);
968 
969 	$$ = $1;
970 };
971 
972 proc_define_command: FSC_DEFINE FSE_PROC pt_attr_ops FSK_OPENLST thread_list FSK_CLOSELST
973 {
974 	if (($$ = alloc_cmd()) == NULL)
975 		YYERROR;
976 	$$->cmd = &parser_proc_define;
977 	$$->cmd_list = $5;
978 	$$->cmd_attr_list = $3;
979 
980 }
981 | proc_define_command pt_attr_ops
982 {
983 	$1->cmd_attr_list = $2;
984 };
985 
986 files_define_command: FSC_DEFINE FSE_FILE
987 {
988 	if (($$ = alloc_cmd()) == NULL)
989 		YYERROR;
990 	$$->cmd = &parser_file_define;
991 }| FSC_DEFINE FSE_FILESET
992 {
993 	if (($$ = alloc_cmd()) == NULL)
994 		YYERROR;
995 	$$->cmd = &parser_fileset_define;
996 }
997 | files_define_command files_attr_ops
998 {
999 	$1->cmd_attr_list = $2;
1000 };
1001 
1002 randvar_define_command: FSC_DEFINE FSE_RAND randvar_attr_ops
1003 {
1004 	if (($$ = alloc_cmd()) == NULL)
1005 		YYERROR;
1006 	$$->cmd = &parser_randvar_define;
1007 	$$->cmd_attr_list = $3;
1008 };
1009 
1010 fo_define_command: FSC_DEFINE FSC_FLOWOP comp_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST
1011 {
1012 	if (($$ = alloc_cmd()) == NULL)
1013 		YYERROR;
1014 	$$->cmd = &parser_composite_flowop_define;
1015 	$$->cmd_list = $5;
1016 	$$->cmd_attr_list = $3;
1017 }
1018 | fo_define_command comp_attr_ops
1019 {
1020 	$1->cmd_attr_list = $2;
1021 };
1022 
1023 create_command: FSC_CREATE entity
1024 {
1025 	if (($$ = alloc_cmd()) == NULL)
1026 		YYERROR;
1027 	switch ($2) {
1028 	case FSE_PROC:
1029 		$$->cmd = &parser_proc_create;
1030 		break;
1031 	case FSE_FILESET:
1032 	case FSE_FILE:
1033 		$$->cmd = &parser_fileset_create;
1034 		break;
1035 	default:
1036 		filebench_log(LOG_ERROR, "unknown entity", $2);
1037 		YYERROR;
1038 	}
1039 
1040 };
1041 
1042 shutdown_command: FSC_SHUTDOWN entity
1043 {
1044 	if (($$ = alloc_cmd()) == NULL)
1045 		YYERROR;
1046 	switch ($2) {
1047 	case FSE_PROC:
1048 		$$->cmd = &parser_proc_shutdown;
1049 		break;
1050 	default:
1051 		filebench_log(LOG_ERROR, "unknown entity", $2);
1052 		YYERROR;
1053 	}
1054 
1055 };
1056 
1057 sleep_command: FSC_SLEEP FSV_VAL_INT
1058 {
1059 	if (($$ = alloc_cmd()) == NULL)
1060 		YYERROR;
1061 	$$->cmd = parser_sleep;
1062 	$$->cmd_qty = $2;
1063 }
1064 | FSC_SLEEP FSV_VARIABLE
1065 {
1066 	fbint_t *integer;
1067 
1068 	if (($$ = alloc_cmd()) == NULL)
1069 		YYERROR;
1070 	$$->cmd = parser_sleep_variable;
1071 	$$->cmd_tgt1 = fb_stralloc($2);
1072 };
1073 
1074 run_command: FSC_RUN FSV_VAL_INT
1075 {
1076 	if (($$ = alloc_cmd()) == NULL)
1077 		YYERROR;
1078 	$$->cmd = parser_run;
1079 	$$->cmd_qty = $2;
1080 }
1081 | FSC_RUN FSV_VARIABLE
1082 {
1083 	fbint_t *integer;
1084 
1085 	if (($$ = alloc_cmd()) == NULL)
1086 		YYERROR;
1087 	$$->cmd = parser_run_variable;
1088 	$$->cmd_tgt1 = fb_stralloc($2);
1089 }
1090 | FSC_RUN
1091 {
1092 	fbint_t *integer;
1093 
1094 	if (($$ = alloc_cmd()) == NULL)
1095 		YYERROR;
1096 	$$->cmd = parser_run;
1097 	$$->cmd_qty = 60UL;
1098 };
1099 
1100 help_command: FSC_HELP
1101 {
1102 	if (($$ = alloc_cmd()) == NULL)
1103 		YYERROR;
1104 	$$->cmd = parser_help;
1105 };
1106 
1107 flowop_command: FSC_FLOWOP name
1108 {
1109 	if (($$ = alloc_cmd()) == NULL)
1110 		YYERROR;
1111 	$$->cmd_name = fb_stralloc($2);
1112 }
1113 | flowop_command fo_attr_ops
1114 {
1115 	$1->cmd_attr_list = $2;
1116 };
1117 
1118 load_command: FSC_LOAD FSV_STRING
1119 {
1120 	FILE *newfile;
1121 	char loadfile[128];
1122 
1123 	if (($$ = alloc_cmd()) == NULL)
1124 		YYERROR;
1125 
1126 	(void) strcpy(loadfile, $2);
1127 	(void) strcat(loadfile, ".f");
1128 
1129 	if ((newfile = fopen(loadfile, "r")) == NULL) {
1130 		(void) strcpy(loadfile, FILEBENCHDIR);
1131 		(void) strcat(loadfile, "/workloads/");
1132 		(void) strcat(loadfile, $2);
1133 		(void) strcat(loadfile, ".f");
1134 		if ((newfile = fopen(loadfile, "r")) == NULL) {
1135 			filebench_log(LOG_ERROR, "Cannot open %s", loadfile);
1136 			YYERROR;
1137 		}
1138 	}
1139 
1140 	parentscript = yyin;
1141 	yyin = newfile;
1142 	yy_switchfileparent(yyin);
1143 };
1144 
1145 
1146 entity: FSE_PROC {$$ = FSE_PROC;}
1147 | FSE_THREAD {$$ = FSE_THREAD;}
1148 | FSE_FILESET {$$ = FSE_FILESET;}
1149 | FSE_FILE {$$ = FSE_FILE;};
1150 
1151 value: FSV_VAL_INT { $$.i = $1;}
1152 | FSV_STRING { $$.s = $1;}
1153 | FSV_VAL_BOOLEAN { $$.b = $1;};
1154 
1155 name: FSV_STRING;
1156 
1157 /* attribute parsing for define file and define fileset */
1158 files_attr_ops: files_attr_op
1159 {
1160 	$$ = $1;
1161 }
1162 | files_attr_ops FSK_SEPLST files_attr_op
1163 {
1164 	attr_t *attr = NULL;
1165 	attr_t *list_end = NULL;
1166 
1167 	for (attr = $1; attr != NULL;
1168 	    attr = attr->attr_next)
1169 		list_end = attr; /* Find end of list */
1170 
1171 	list_end->attr_next = $3;
1172 
1173 	$$ = $1;
1174 };
1175 
1176 files_attr_op: files_attr_name FSK_ASSIGN attr_list_value
1177 {
1178 	$$ = $3;
1179 	$$->attr_name = $1;
1180 }
1181 | files_attr_name
1182 {
1183 	if (($$ = alloc_attr()) == NULL)
1184 		YYERROR;
1185 	$$->attr_name = $1;
1186 };
1187 
1188 /* attribute parsing for random variables */
1189 randvar_attr_ops: randvar_attr_op
1190 {
1191 	$$ = $1;
1192 }
1193 | randvar_attr_ops FSK_SEPLST randvar_attr_op
1194 {
1195 	attr_t *attr = NULL;
1196 	attr_t *list_end = NULL;
1197 
1198 	for (attr = $1; attr != NULL;
1199 	    attr = attr->attr_next)
1200 		list_end = attr; /* Find end of list */
1201 
1202 	list_end->attr_next = $3;
1203 
1204 	$$ = $1;
1205 }
1206 | randvar_attr_ops FSK_SEPLST FSA_RANDTABLE FSK_ASSIGN FSK_OPENLST probtabentry_list FSK_CLOSELST
1207 {
1208 	attr_t *attr = NULL;
1209 	attr_t *list_end = NULL;
1210 
1211 	for (attr = $1; attr != NULL;
1212 	    attr = attr->attr_next)
1213 		list_end = attr; /* Find end of list */
1214 
1215 
1216 	if ((attr = alloc_attr()) == NULL)
1217 		YYERROR;
1218 
1219 	attr->attr_name = FSA_RANDTABLE;
1220 	attr->attr_obj = (void *)$6;
1221 	list_end->attr_next = attr;
1222 	$$ = $1;
1223 };
1224 
1225 randvar_attr_op: randvar_attr_name FSK_ASSIGN attr_list_value
1226 {
1227 	$$ = $3;
1228 	$$->attr_name = $1;
1229 }
1230 | randvar_attr_name
1231 {
1232 	if (($$ = alloc_attr()) == NULL)
1233 		YYERROR;
1234 	$$->attr_name = $1;
1235 }
1236 | FSA_TYPE FSK_ASSIGN randvar_attr_typop
1237 {
1238 	$$ = $3;
1239 	$$->attr_name = FSA_TYPE;
1240 }
1241 | FSA_RANDSRC FSK_ASSIGN randvar_attr_srcop
1242 {
1243 	$$ = $3;
1244 	$$->attr_name = FSA_RANDSRC;
1245 };
1246 
1247 probtabentry: FSK_OPENLST var_int_val FSK_SEPLST var_int_val FSK_SEPLST var_int_val FSK_CLOSELST
1248 {
1249 	if (($$ = alloc_probtabent()) == NULL)
1250 		YYERROR;
1251 	$$->pte_percent = $2;
1252 	$$->pte_segmin  = $4;
1253 	$$->pte_segmax  = $6;
1254 };
1255 
1256 /* attribute parsing for prob density function table */
1257 probtabentry_list: probtabentry
1258 {
1259 	$$ = $1;
1260 }
1261 | probtabentry_list FSK_SEPLST probtabentry
1262 {
1263 	probtabent_t *pte = NULL;
1264 	probtabent_t *ptelist_end = NULL;
1265 
1266 	for (pte = $1; pte != NULL;
1267 	    pte = pte->pte_next)
1268 		ptelist_end = pte; /* Find end of prob table entry list */
1269 
1270 	ptelist_end->pte_next = $3;
1271 
1272 	$$ = $1;
1273 };
1274 
1275 /* attribute parsing for define thread and process */
1276 pt_attr_ops: pt_attr_op
1277 {
1278 	$$ = $1;
1279 }
1280 | pt_attr_ops FSK_SEPLST pt_attr_op
1281 {
1282 	attr_t *attr = NULL;
1283 	attr_t *list_end = NULL;
1284 
1285 	for (attr = $1; attr != NULL;
1286 	    attr = attr->attr_next)
1287 		list_end = attr; /* Find end of list */
1288 
1289 	list_end->attr_next = $3;
1290 
1291 	$$ = $1;
1292 };
1293 
1294 pt_attr_op: pt_attr_name FSK_ASSIGN attr_value
1295 {
1296 	$$ = $3;
1297 	$$->attr_name = $1;
1298 }
1299 | pt_attr_name
1300 {
1301 	if (($$ = alloc_attr()) == NULL)
1302 		YYERROR;
1303 	$$->attr_name = $1;
1304 };
1305 
1306 /* attribute parsing for flowops */
1307 fo_attr_ops: fo_attr_op
1308 {
1309 	$$ = $1;
1310 }
1311 | fo_attr_ops FSK_SEPLST fo_attr_op
1312 {
1313 	attr_t *attr = NULL;
1314 	attr_t *list_end = NULL;
1315 
1316 	for (attr = $1; attr != NULL;
1317 	    attr = attr->attr_next)
1318 		list_end = attr; /* Find end of list */
1319 
1320 	list_end->attr_next = $3;
1321 
1322 	$$ = $1;
1323 }
1324 | fo_attr_ops FSK_SEPLST comp_lvar_def
1325 {
1326 	attr_t *attr = NULL;
1327 	attr_t *list_end = NULL;
1328 
1329 	for (attr = $1; attr != NULL;
1330 	    attr = attr->attr_next)
1331 		list_end = attr; /* Find end of list */
1332 
1333 	list_end->attr_next = $3;
1334 
1335 	$$ = $1;
1336 };
1337 
1338 fo_attr_op: fo_attr_name FSK_ASSIGN attr_value
1339 {
1340 	$$ = $3;
1341 	$$->attr_name = $1;
1342 }
1343 | fo_attr_name
1344 {
1345 	if (($$ = alloc_attr()) == NULL)
1346 		YYERROR;
1347 	$$->attr_name = $1;
1348 };
1349 
1350 /* attribute parsing for Event Generator */
1351 ev_attr_ops: ev_attr_op
1352 {
1353 	$$ = $1;
1354 }
1355 | ev_attr_ops FSK_SEPLST ev_attr_op
1356 {
1357 	attr_t *attr = NULL;
1358 	attr_t *list_end = NULL;
1359 
1360 	for (attr = $1; attr != NULL;
1361 	    attr = attr->attr_next)
1362 		list_end = attr; /* Find end of list */
1363 
1364 	list_end->attr_next = $3;
1365 
1366 	$$ = $1;
1367 };
1368 
1369 ev_attr_op: ev_attr_name FSK_ASSIGN attr_value
1370 {
1371 	$$ = $3;
1372 	$$->attr_name = $1;
1373 }
1374 | ev_attr_name
1375 {
1376 	if (($$ = alloc_attr()) == NULL)
1377 		YYERROR;
1378 	$$->attr_name = $1;
1379 };
1380 
1381 /* attribute parsing for enable multiple client command */
1382 enable_multi_ops: enable_multi_op
1383 {
1384 	$$ = $1;
1385 }
1386 | enable_multi_ops FSK_SEPLST enable_multi_op
1387 {
1388 	attr_t *attr = NULL;
1389 	attr_t *list_end = NULL;
1390 
1391 	for (attr = $1; attr != NULL;
1392 	    attr = attr->attr_next)
1393 		list_end = attr; /* Find end of list */
1394 
1395 	list_end->attr_next = $3;
1396 
1397 	$$ = $1;
1398 };
1399 
1400 enable_multi_op: em_attr_name FSK_ASSIGN attr_value
1401 {
1402 	$$ = $3;
1403 	$$->attr_name = $1;
1404 }
1405 
1406 multisync_op: FSA_VALUE FSK_ASSIGN attr_value
1407 {
1408 	$$ = $3;
1409 	$$->attr_name = FSA_VALUE;
1410 }
1411 
1412 files_attr_name: attrs_define_file
1413 |attrs_define_fileset;
1414 
1415 pt_attr_name: attrs_define_thread
1416 |attrs_define_proc;
1417 
1418 fo_attr_name: attrs_flowop;
1419 
1420 ev_attr_name: attrs_eventgen;
1421 
1422 attrs_define_proc:
1423   FSA_NICE { $$ = FSA_NICE;}
1424 | FSA_NAME { $$ = FSA_NAME;}
1425 | FSA_INSTANCES { $$ = FSA_INSTANCES;};
1426 
1427 attrs_define_file:
1428   FSA_SIZE { $$ = FSA_SIZE;}
1429 | FSA_NAME { $$ = FSA_NAME;}
1430 | FSA_PATH { $$ = FSA_PATH;}
1431 | FSA_REUSE { $$ = FSA_REUSE;}
1432 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1433 | FSA_PARALLOC { $$ = FSA_PARALLOC;};
1434 
1435 attrs_define_fileset:
1436   FSA_SIZE { $$ = FSA_SIZE;}
1437 | FSA_NAME { $$ = FSA_NAME;}
1438 | FSA_PATH { $$ = FSA_PATH;}
1439 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;}
1440 | FSA_DIRDEPTHRV { $$ = FSA_DIRDEPTHRV;}
1441 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1442 | FSA_PARALLOC { $$ = FSA_PARALLOC;}
1443 | FSA_REUSE { $$ = FSA_REUSE;}
1444 | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;}
1445 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;}
1446 | FSA_CACHED { $$ = FSA_CACHED;}
1447 | FSA_ENTRIES { $$ = FSA_ENTRIES;};
1448 | FSA_LEAFDIRS { $$ = FSA_LEAFDIRS;};
1449 
1450 randvar_attr_name:
1451   FSA_NAME { $$ = FSA_NAME;}
1452 | FSA_RANDSEED { $$ = FSA_RANDSEED;}
1453 | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;}
1454 | FSA_RANDMEAN { $$ = FSA_RANDMEAN;}
1455 | FSA_RANDMIN { $$ = FSA_RANDMIN;}
1456 | FSA_RANDROUND { $$ = FSA_RANDROUND;};
1457 
1458 randvar_attr_tsp:
1459   FSS_TYPE { $$ = FSS_TYPE;}
1460 | FSS_SRC { $$ = FSS_SRC;}
1461 | FSS_SEED { $$ = FSS_SEED;}
1462 | FSS_GAMMA { $$ = FSS_GAMMA;}
1463 | FSS_MEAN { $$ = FSS_MEAN;}
1464 | FSS_MIN { $$ = FSS_MIN;}
1465 | FSS_ROUND { $$ = FSS_ROUND;};
1466 
1467 
1468 randvar_attr_param:
1469   FSS_SEED { $$ = FSS_SEED;}
1470 | FSS_GAMMA { $$ = FSS_GAMMA;}
1471 | FSS_MEAN { $$ = FSS_MEAN;}
1472 | FSS_MIN { $$ = FSS_MIN;}
1473 | FSS_ROUND { $$ = FSS_ROUND;};
1474 
1475 randvar_attr_typop: randtype_name
1476 {
1477 	if (($$ = alloc_attr()) == NULL)
1478 		YYERROR;
1479 	$$->attr_avd = avd_int_alloc($1);
1480 };
1481 
1482 randtype_name:
1483   FSV_RANDUNI { $$ = FSV_RANDUNI;}
1484 | FSV_RANDTAB { $$ = FSV_RANDTAB;}
1485 | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;};
1486 
1487 randvar_attr_srcop: randsrc_name
1488 {
1489 	if (($$ = alloc_attr()) == NULL)
1490 		YYERROR;
1491 	$$->attr_avd = avd_int_alloc($1);
1492 };
1493 
1494 randsrc_name:
1495   FSV_URAND { $$ = FSV_URAND;}
1496 | FSV_RAND48 { $$ = FSV_RAND48;};
1497 
1498 attrs_define_thread:
1499   FSA_PROCESS { $$ = FSA_PROCESS;}
1500 | FSA_NAME { $$ = FSA_NAME;}
1501 | FSA_MEMSIZE { $$ = FSA_MEMSIZE;}
1502 | FSA_USEISM { $$ = FSA_USEISM;}
1503 | FSA_INSTANCES { $$ = FSA_INSTANCES;};
1504 
1505 attrs_flowop:
1506   FSA_WSS { $$ = FSA_WSS;}
1507 | FSA_FILE { $$ = FSA_FILE;}
1508 | FSA_NAME { $$ = FSA_NAME;}
1509 | FSA_RANDOM { $$ = FSA_RANDOM;}
1510 | FSA_FD { $$ = FSA_FD;}
1511 | FSA_SRCFD { $$ = FSA_SRCFD;}
1512 | FSA_ROTATEFD { $$ = FSA_ROTATEFD;}
1513 | FSA_DSYNC { $$ = FSA_DSYNC;}
1514 | FSA_DIRECTIO { $$ = FSA_DIRECTIO;}
1515 | FSA_INDEXED { $$ = FSA_INDEXED;}
1516 | FSA_TARGET { $$ = FSA_TARGET;}
1517 | FSA_ITERS { $$ = FSA_ITERS;}
1518 | FSA_VALUE { $$ = FSA_VALUE;}
1519 | FSA_BLOCKING { $$ = FSA_BLOCKING;}
1520 | FSA_HIGHWATER { $$ = FSA_HIGHWATER;}
1521 | FSA_IOSIZE { $$ = FSA_IOSIZE;};
1522 
1523 attrs_eventgen:
1524   FSA_RATE { $$ = FSA_RATE;};
1525 
1526 em_attr_name:
1527   FSA_MASTER { $$ = FSA_MASTER;};
1528 | FSA_CLIENT { $$ = FSA_CLIENT;};
1529 
1530 comp_attr_ops: comp_attr_op
1531 {
1532 	$$ = $1;
1533 }
1534 | comp_attr_ops FSK_SEPLST comp_attr_op
1535 {
1536 	attr_t *attr = NULL;
1537 	attr_t *list_end = NULL;
1538 
1539 	for (attr = $1; attr != NULL;
1540 	    attr = attr->attr_next)
1541 		list_end = attr; /* Find end of list */
1542 
1543 	list_end->attr_next = $3;
1544 
1545 	$$ = $1;
1546 }
1547 | comp_attr_ops FSK_SEPLST comp_lvar_def
1548 {
1549 	attr_t *attr = NULL;
1550 	attr_t *list_end = NULL;
1551 
1552 	for (attr = $1; attr != NULL;
1553 	    attr = attr->attr_next)
1554 		list_end = attr; /* Find end of list */
1555 
1556 	list_end->attr_next = $3;
1557 
1558 	$$ = $1;
1559 };
1560 
1561 comp_attr_op: attrs_define_comp FSK_ASSIGN attr_value
1562 {
1563 	$$ = $3;
1564 	$$->attr_name = $1;
1565 };
1566 
1567 comp_lvar_def: FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
1568 {
1569 	if (($$ = alloc_lvar_attr(var_lvar_assign_boolean($1, $3))) == NULL)
1570 		YYERROR;
1571 }
1572 | FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT
1573 {
1574 	if (($$ = alloc_lvar_attr(var_lvar_assign_integer($1, $3))) == NULL)
1575 		YYERROR;
1576 }
1577 | FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
1578 {
1579 	if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $4))) == NULL)
1580 		YYERROR;
1581 }
1582 | FSV_VARIABLE FSK_ASSIGN FSV_STRING
1583 {
1584 	if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $3))) == NULL)
1585 		YYERROR;
1586 }
1587 | FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
1588 {
1589 	if (($$ = alloc_lvar_attr(var_lvar_assign_var($1, $3))) == NULL)
1590 		YYERROR;
1591 }
1592 | FSV_VARIABLE
1593 {
1594 	if (($$ = alloc_lvar_attr(var_lvar_alloc_local($1))) == NULL)
1595 		YYERROR;
1596 };
1597 
1598 
1599 attrs_define_comp:
1600   FSA_NAME { $$ = FSA_NAME;}
1601 | FSA_ITERS { $$ = FSA_ITERS;};
1602 
1603 attr_value: FSV_STRING
1604 {
1605 	if (($$ = alloc_attr()) == NULL)
1606 		YYERROR;
1607 	$$->attr_avd = avd_str_alloc($1);
1608 } | FSV_VAL_INT {
1609 	if (($$ = alloc_attr()) == NULL)
1610 		YYERROR;
1611 	$$->attr_avd = avd_int_alloc($1);
1612 } | FSV_VAL_BOOLEAN {
1613 	if (($$ = alloc_attr()) == NULL)
1614 		YYERROR;
1615 	$$->attr_avd = avd_bool_alloc($1);
1616 } | FSV_VARIABLE {
1617 	if (($$ = alloc_attr()) == NULL)
1618 		YYERROR;
1619 	$$->attr_avd = var_ref_attr($1);
1620 };
1621 
1622 attr_list_value: var_string_list {
1623 	if (($$ = alloc_attr()) == NULL)
1624 		YYERROR;
1625 	$$->attr_param_list = $1;
1626 } | FSV_STRING {
1627 	if (($$ = alloc_attr()) == NULL)
1628 		YYERROR;
1629 	$$->attr_avd = avd_str_alloc($1);
1630 } | FSV_VAL_INT {
1631 	if (($$ = alloc_attr()) == NULL)
1632 		YYERROR;
1633 	$$->attr_avd = avd_int_alloc($1);
1634 } | FSV_VAL_BOOLEAN {
1635 	if (($$ = alloc_attr()) == NULL)
1636 		YYERROR;
1637 	$$->attr_avd = avd_bool_alloc($1);
1638 } | FSV_VARIABLE {
1639 	if (($$ = alloc_attr()) == NULL)
1640 		YYERROR;
1641 	$$->attr_avd = var_ref_attr($1);
1642 };
1643 
1644 var_int_val: FSV_VAL_INT
1645 {
1646 	$$ = avd_int_alloc($1);
1647 } | FSV_VARIABLE
1648 {
1649 	$$ = var_ref_attr($1);
1650 };
1651 
1652 %%
1653 
1654 /*
1655  *  The following 'c' routines implement the various commands defined in the
1656  * above yacc parser code. The yacc portion checks the syntax of the commands
1657  * found in a workload file, or typed on interactive command lines, parsing
1658  * the commands' parameters into lists. The lists are then passed in a cmd_t
1659  * struct for each command to its related routine in the following section
1660  * for actual execution. This section also includes a few utility routines
1661  * and the main entry point for the program.
1662  */
1663 
1664 /*
1665  * Entry point for filebench. Processes command line arguements. The -f
1666  * option will read in a workload file (the full name and extension must
1667  * must be given). The -a, -s, -m and -i options are used by worker process
1668  * to receive their name, the base address of shared memory, its path, and
1669  * the process' instance number, respectively. This information is supplied
1670  * by the master process when it execs worker processes under the process
1671  * model of execution. If the worker process arguments are passed then main
1672  * will call the procflow_exec routine which creates worker threadflows and
1673  * flowops and executes the procflow's portion of the workload model until
1674  * completion. If worker process arguments are not passed to the process,
1675  * then it becomes the master process for a filebench run. It initializes
1676  * the various filebench components and either executes the supplied workload
1677  * file, or enters interactive mode.
1678  */
1679 
1680 int
1681 main(int argc, char *argv[])
1682 {
1683 	int opt;
1684 	int docmd = FS_FALSE;
1685 	int instance;
1686 	char procname[128];
1687 	caddr_t shmaddr;
1688 	char dir[MAXPATHLEN];
1689 #ifdef HAVE_SETRLIMIT
1690 	struct rlimit rlp;
1691 #endif
1692 #ifdef HAVE_LIBTECLA
1693 	char *line;
1694 #else
1695 	char line[1024];
1696 #endif
1697 	char shmpathtmp[1024];
1698 
1699 #ifdef HAVE_SETRLIMIT
1700 	/* Set resource limits */
1701 	(void) getrlimit(RLIMIT_NOFILE, &rlp);
1702 	rlp.rlim_cur = rlp.rlim_max;
1703 	setrlimit(RLIMIT_NOFILE, &rlp);
1704 #endif
1705 
1706 	yydebug = 0;
1707 	execname = argv[0];
1708 	*procname = 0;
1709 	cwd = getcwd(dir, MAXPATHLEN);
1710 
1711 	while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) {
1712 
1713 		switch (opt) {
1714 		case 'h':
1715 			usage(2);
1716 			break;
1717 
1718 		case 'p':
1719 			noproc = 1;
1720 			break;
1721 
1722 		case 'f':
1723 			if (optarg == NULL)
1724 				usage(1);
1725 			if ((yyin = fopen(optarg, "r")) == NULL) {
1726 				(void) fprintf(stderr,
1727 				    "Cannot open file %s", optarg);
1728 				exit(1);
1729 			}
1730 			dofile = FS_TRUE;
1731 			fscriptname = optarg;
1732 
1733 			break;
1734 
1735 		case 'a':
1736 			if (optarg == NULL)
1737 				usage(1);
1738 			sscanf(optarg, "%s", &procname[0]);
1739 			break;
1740 
1741 		case 's':
1742 			if (optarg == NULL)
1743 				usage(1);
1744 #if defined(_LP64) || (__WORDSIZE == 64)
1745 			sscanf(optarg, "%llx", &shmaddr);
1746 #else
1747 			sscanf(optarg, "%x", &shmaddr);
1748 #endif
1749 			break;
1750 
1751 		case 'm':
1752 			if (optarg == NULL)
1753 				usage(1);
1754 			sscanf(optarg, "%s", shmpathtmp);
1755 			shmpath = shmpathtmp;
1756 			break;
1757 
1758 		case 'i':
1759 			if (optarg == NULL)
1760 				usage(1);
1761 			sscanf(optarg, "%d", &instance);
1762 			break;
1763 
1764 		case '?':
1765 		default:
1766 			usage(1);
1767 			break;
1768 		}
1769 	}
1770 
1771 #ifdef USE_PROCESS_MODEL
1772 	if (!(*procname))
1773 #endif
1774 	printf("FileBench Version %s\n", FILEBENCH_VERSION);
1775 	filebench_init();
1776 
1777 	/* get process pid for use with message logging */
1778 	my_pid = getpid();
1779 
1780 #ifdef USE_PROCESS_MODEL
1781 	if (*procname) {
1782 		/* A child FileBench instance */
1783 		if (ipc_attach(shmaddr) < 0) {
1784 			filebench_log(LOG_ERROR, "Cannot attach shm for %s",
1785 			    procname);
1786 			exit(1);
1787 		}
1788 
1789 		if (procflow_exec(procname, instance) < 0) {
1790 			filebench_log(LOG_ERROR, "Cannot startup process %s",
1791 			    procname);
1792 			exit(1);
1793 		}
1794 
1795 		exit(0);
1796 	}
1797 #endif
1798 
1799 	/* master (or only) process */
1800 	ipc_init();
1801 
1802 	if (fscriptname)
1803 		(void) strcpy(filebench_shm->shm_fscriptname, fscriptname);
1804 
1805 	flowop_init();
1806 	stats_init();
1807 	eventgen_init();
1808 
1809 	signal(SIGINT, parser_abort);
1810 
1811 	if (dofile)
1812 		yyparse();
1813 	else {
1814 #ifdef HAVE_LIBTECLA
1815 		if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) {
1816 			filebench_log(LOG_ERROR,
1817 			    "Failed to create GetLine object");
1818 			filebench_shutdown(1);
1819 		}
1820 
1821 		if (gl_customize_completion(gl, NULL, command_complete)) {
1822 			filebench_log(LOG_ERROR,
1823 			    "Failed to register auto-completion function");
1824 			filebench_shutdown(1);
1825 		}
1826 
1827 		while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) {
1828 			arg_parse(line);
1829 			yyparse();
1830 		}
1831 
1832 		del_GetLine(gl);
1833 #else
1834 		while (!feof(stdin)) {
1835 			printf(FILEBENCH_PROMPT);
1836 			fflush(stdout);
1837 			if (fgets(line, sizeof (line), stdin) == NULL) {
1838 				if (errno == EINTR)
1839 					continue;
1840 				else
1841 					break;
1842 			}
1843 			arg_parse(line);
1844 			yyparse();
1845 		}
1846 		printf("\n");
1847 #endif	/* HAVE_LIBTECLA */
1848 	}
1849 
1850 	parser_filebench_shutdown((cmd_t *)0);
1851 
1852 	return (0);
1853 }
1854 
1855 /*
1856  * arg_parse() puts the parser into command parsing mode. Create a tmpfile
1857  * and instruct the parser to read instructions from this location by setting
1858  * yyin to the value returned by tmpfile. Write the command into the file.
1859  * Then seek back to to the start of the file so that the parser can read
1860  * the instructions.
1861  */
1862 static void
1863 arg_parse(const char *command)
1864 {
1865 	if ((yyin = tmpfile()) == NULL) {
1866 		filebench_log(LOG_FATAL,
1867 		    "Exiting: Cannot create tmpfile: %s", strerror(errno));
1868 		exit(1);
1869 	}
1870 
1871 	if (fwrite(command, strlen(command), 1, yyin) != 1)
1872 		filebench_log(LOG_FATAL,
1873 		    "Cannot write tmpfile: %s", strerror(errno));
1874 
1875 	if (fseek(yyin, 0, SEEK_SET) != 0)
1876 		filebench_log(LOG_FATAL,
1877 		    "Cannot seek tmpfile: %s", strerror(errno));
1878 }
1879 
1880 /*
1881  * Converts a list of var_strings or ordinary strings to a single ordinary
1882  * string. It returns a pointer to the string (in malloc'd memory) if found,
1883  * or NULL otherwise.
1884  */
1885 char *
1886 parser_list2string(list_t *list)
1887 {
1888 	list_t *l;
1889 	char *string;
1890 	char *tmp;
1891 	fbint_t *integer;
1892 	if ((string = malloc(MAXPATHLEN)) == NULL) {
1893 		filebench_log(LOG_ERROR, "Failed to allocate memory");
1894 		return (NULL);
1895 	}
1896 
1897 	*string = 0;
1898 
1899 	/*	printf("parser_list2string: called\n"); */
1900 	/* Format args */
1901 	for (l = list; l != NULL; l = l->list_next) {
1902 		char *lstr = avd_get_str(l->list_string);
1903 
1904 		filebench_log(LOG_DEBUG_SCRIPT,
1905 		    "converting string '%s'", lstr);
1906 
1907 		/* see if it is a random variable */
1908 		if (l->list_integer) {
1909 			fbint_t param_name;
1910 
1911 			tmp = NULL;
1912 			param_name = avd_get_int(l->list_integer);
1913 			switch (param_name) {
1914 			case FSS_TYPE:
1915 				tmp = var_randvar_to_string(lstr,
1916 				    RAND_PARAM_TYPE);
1917 				break;
1918 
1919 			case FSS_SRC:
1920 				tmp = var_randvar_to_string(lstr,
1921 				    RAND_PARAM_SRC);
1922 				break;
1923 
1924 			case FSS_SEED:
1925 				tmp = var_randvar_to_string(lstr,
1926 				    RAND_PARAM_SEED);
1927 				break;
1928 
1929 			case FSS_MIN:
1930 				tmp = var_randvar_to_string(lstr,
1931 				    RAND_PARAM_MIN);
1932 				break;
1933 
1934 			case FSS_MEAN:
1935 				tmp = var_randvar_to_string(lstr,
1936 				    RAND_PARAM_MEAN);
1937 				break;
1938 
1939 			case FSS_GAMMA:
1940 				tmp = var_randvar_to_string(lstr,
1941 				    RAND_PARAM_GAMMA);
1942 				break;
1943 
1944 			case FSS_ROUND:
1945 				tmp = var_randvar_to_string(lstr,
1946 				    RAND_PARAM_ROUND);
1947 				break;
1948 			}
1949 
1950 			if (tmp) {
1951 				(void) strcat(string, tmp);
1952 				free(tmp);
1953 			} else {
1954 				(void) strcat(string, lstr);
1955 			}
1956 		} else {
1957 			/* perhaps a normal variable? */
1958 			if ((tmp = var_to_string(lstr)) != NULL) {
1959 				(void) strcat(string, tmp);
1960 				free(tmp);
1961 			} else {
1962 				(void) strcat(string, lstr);
1963 			}
1964 		}
1965 	}
1966 	return (string);
1967 }
1968 
1969 /*
1970  * If the list just contains a single string starting with '$', then find
1971  * or create the named var and return the var's var_string component.
1972  * Otherwise, convert the list to a string, and allocate a var_string
1973  * containing a copy of that string. On failure either returns NULL
1974  * or shuts down the run.
1975  */
1976 avd_t
1977 parser_list2varstring(list_t *list)
1978 {
1979 	char *lstr = avd_get_str(list->list_string);
1980 
1981 	/*	printf("parser_list2varstring: Called\n"); */
1982 	/* Special case - variable name */
1983 	if ((list->list_next == NULL) && (*lstr == '$'))
1984 		return (var_ref_attr(lstr));
1985 
1986 	return (avd_str_alloc(parser_list2string(list)));
1987 }
1988 
1989 /*
1990  * Looks for the var named in list_string of the first element of the
1991  * supplied list. If found, returns the var_val portion of the var in
1992  * an attribute value descriptor. If the var is not found, cannot be
1993  * allocated, the supplied list is NULL, or the list_string filed is
1994  * empty, returns NULL.
1995  */
1996 avd_t
1997 parser_list2avd(list_t *list)
1998 {
1999 	avd_t avd;
2000 	char *lstr;
2001 
2002 	if (list && ((lstr = avd_get_str(list->list_string)) != NULL)) {
2003 		avd = var_ref_attr(lstr);
2004 		return (avd);
2005 	}
2006 
2007 	return (NULL);
2008 }
2009 
2010 /*
2011  * Sets the event generator rate from the attribute supplied with the
2012  * command. If the attribute doesn't exist the routine does nothing.
2013  */
2014 static void
2015 parser_eventgen(cmd_t *cmd)
2016 {
2017 	attr_t *attr;
2018 
2019 	/* Get the rate from attribute */
2020 	if (attr = get_attr_integer(cmd, FSA_RATE)) {
2021 		if (attr->attr_avd) {
2022 			eventgen_setrate(attr->attr_avd);
2023 		}
2024 	}
2025 }
2026 
2027 /*
2028  * Assigns the designated integer variable successive values from the
2029  * supplied comma seperated integer list. After each successive integer
2030  * assignment, it executes the bracket enclosed list of commands. For
2031  * example, repeated runs of a workload with increasing io sizes can
2032  * be done using the following command line:
2033  * 	foreach $iosize in 2k, 4k, 8k {run 60}
2034  */
2035 static void
2036 parser_foreach_integer(cmd_t *cmd)
2037 {
2038 	list_t *list = cmd->cmd_param_list;
2039 	cmd_t *inner_cmd;
2040 
2041 	for (; list != NULL; list = list->list_next) {
2042 		fbint_t list_int = avd_get_int(list->list_integer);
2043 
2044 		var_assign_integer(cmd->cmd_tgt1, list_int);
2045 		filebench_log(LOG_VERBOSE, "Iterating %s=%llu",
2046 		    cmd->cmd_tgt1, (u_longlong_t)list_int);
2047 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2048 		    inner_cmd = inner_cmd->cmd_next) {
2049 			inner_cmd->cmd(inner_cmd);
2050 		}
2051 	}
2052 }
2053 
2054 /*
2055  * Similar to parser_foreach_integer(), except takes a list of strings after
2056  * the "in" token. For example, to run twice using a different directory,
2057  * perhaps using a different filesystem, the following command line
2058  * could be used:
2059  * 	foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60)
2060  */
2061 static void
2062 parser_foreach_string(cmd_t *cmd)
2063 {
2064 	list_t *list = cmd->cmd_param_list;
2065 
2066 	for (; list != NULL; list = list->list_next) {
2067 		cmd_t *inner_cmd;
2068 		char *lstr = avd_get_str(list->list_string);
2069 		var_assign_string(cmd->cmd_tgt1, lstr);
2070 		filebench_log(LOG_VERBOSE, "Iterating %s=%s",
2071 		    cmd->cmd_tgt1, lstr);
2072 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2073 		    inner_cmd = inner_cmd->cmd_next) {
2074 			inner_cmd->cmd(inner_cmd);
2075 		}
2076 	}
2077 }
2078 
2079 /*
2080  * Lists the fileset name, path name and average size for all defined
2081  * filesets.
2082  */
2083 static void
2084 parser_list(cmd_t *cmd)
2085 {
2086 	(void) fileset_iter(fileset_print);
2087 }
2088 
2089 /*
2090  * Lists the flowop name and instance number for all flowops.
2091  */
2092 static void
2093 parser_flowop_list(cmd_t *cmd)
2094 {
2095 	flowop_printall();
2096 }
2097 
2098 /*
2099  * Calls procflow_define() to allocate "instances" number of  procflow(s)
2100  * (processes) with the supplied name. The default number of instances is
2101  * one. An optional priority level attribute can be supplied and is stored in
2102  * pf_nice. Finally the routine loops through the list of inner commands, if
2103  * any, which are defines for threadflows, and passes them one at a time to
2104  * parser_thread_define() to allocate threadflow entities for the process(es).
2105  */
2106 static void
2107 parser_proc_define(cmd_t *cmd)
2108 {
2109 	procflow_t *procflow, template;
2110 	char *name;
2111 	attr_t *attr;
2112 	avd_t var_instances;
2113 	fbint_t instances;
2114 	cmd_t *inner_cmd;
2115 
2116 	/* Get the name of the process */
2117 	if (attr = get_attr(cmd, FSA_NAME)) {
2118 		name = avd_get_str(attr->attr_avd);
2119 	} else {
2120 		filebench_log(LOG_ERROR,
2121 		    "define proc: proc specifies no name");
2122 		filebench_shutdown(1);
2123 	}
2124 
2125 	/* Get the memory size from attribute */
2126 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
2127 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2128 			filebench_log(LOG_ERROR,
2129 			    "proc_define: Instances attr cannot be random");
2130 			filebench_shutdown(1);
2131 		}
2132 		var_instances = attr->attr_avd;
2133 		instances = avd_get_int(var_instances);
2134 		filebench_log(LOG_DEBUG_IMPL,
2135 		    "Setting instances = %llu", (u_longlong_t)instances);
2136 	} else {
2137 		filebench_log(LOG_DEBUG_IMPL,
2138 		    "Defaulting to instances = 1");
2139 		var_instances = avd_int_alloc(1);
2140 		instances = 1;
2141 	}
2142 
2143 	if ((procflow = procflow_define(name, NULL, var_instances)) == NULL) {
2144 		filebench_log(LOG_ERROR,
2145 		    "Failed to instantiate %d %s process(es)\n",
2146 		    instances, name);
2147 		filebench_shutdown(1);
2148 	}
2149 
2150 	/* Get the pri from attribute */
2151 	if (attr = get_attr_integer(cmd, FSA_NICE)) {
2152 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2153 			filebench_log(LOG_ERROR,
2154 			    "proc_define: priority cannot be random");
2155 			filebench_shutdown(1);
2156 		}
2157 		filebench_log(LOG_DEBUG_IMPL, "Setting pri = %llu",
2158 		    (u_longlong_t)avd_get_int(attr->attr_avd));
2159 		procflow->pf_nice = attr->attr_avd;
2160 	} else
2161 		procflow->pf_nice = avd_int_alloc(0);
2162 
2163 
2164 	/* Create the list of threads for this process  */
2165 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2166 	    inner_cmd = inner_cmd->cmd_next) {
2167 		parser_thread_define(inner_cmd, procflow, instances);
2168 	}
2169 }
2170 
2171 /*
2172  * Calls threadflow_define() to allocate "instances" number of  threadflow(s)
2173  * (threads) with the supplied name. The default number of instances is
2174  * one. Two other optional attributes may be supplied, one to set the memory
2175  * size, stored in tf_memsize, and to select the use of Interprocess Shared
2176  * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally
2177  * the routine loops through the list of inner commands, if any, which are
2178  * defines for flowops, and passes them one at a time to
2179  * parser_flowop_define() to allocate flowop entities for the threadflows.
2180  */
2181 static void
2182 parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances)
2183 {
2184 	threadflow_t *threadflow, template;
2185 	attr_t *attr;
2186 	avd_t instances;
2187 	cmd_t *inner_cmd;
2188 	char *name;
2189 
2190 	memset(&template, 0, sizeof (threadflow_t));
2191 
2192 	/* Get the name of the thread */
2193 	if (attr = get_attr(cmd, FSA_NAME)) {
2194 		name = avd_get_str(attr->attr_avd);
2195 	} else {
2196 		filebench_log(LOG_ERROR,
2197 		    "define thread: thread in process %s specifies no name",
2198 		    procflow->pf_name);
2199 		filebench_shutdown(1);
2200 	}
2201 
2202 	/* Get the number of instances from attribute */
2203 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
2204 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2205 			filebench_log(LOG_ERROR,
2206 			    "define thread: Instances attr cannot be random");
2207 			filebench_shutdown(1);
2208 		}
2209 		filebench_log(LOG_DEBUG_IMPL,
2210 		    "define thread: Setting instances = %llu",
2211 		    (u_longlong_t)avd_get_int(attr->attr_avd));
2212 		instances = attr->attr_avd;
2213 	} else
2214 		instances = avd_int_alloc(1);
2215 
2216 	/* Get the memory size from attribute */
2217 	if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) {
2218 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2219 			filebench_log(LOG_ERROR,
2220 			    "define thread: Memory size cannot be random");
2221 			filebench_shutdown(1);
2222 		}
2223 		filebench_log(LOG_DEBUG_IMPL,
2224 		    "define thread: Setting memsize = %llu",
2225 		    (u_longlong_t)avd_get_int(attr->attr_avd));
2226 		template.tf_memsize = attr->attr_avd;
2227 	} else
2228 		template.tf_memsize = avd_int_alloc(0);
2229 
2230 	if ((threadflow = threadflow_define(procflow, name,
2231 	    &template, instances)) == NULL) {
2232 		filebench_log(LOG_ERROR,
2233 		    "define thread: Failed to instantiate thread\n");
2234 		filebench_shutdown(1);
2235 	}
2236 
2237 	/* Use ISM Memory? */
2238 	if (attr = get_attr(cmd, FSA_USEISM)) {
2239 		threadflow->tf_attrs |= THREADFLOW_USEISM;
2240 	}
2241 
2242 	/* Create the list of flowops */
2243 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2244 	    inner_cmd = inner_cmd->cmd_next) {
2245 		parser_flowop_define(inner_cmd, threadflow,
2246 		    &threadflow->tf_thrd_fops, FLOW_MASTER);
2247 	}
2248 }
2249 
2250 /*
2251  * Fills in the attributes for a newly allocated flowop
2252  */
2253 static void
2254 parser_flowop_get_attrs(cmd_t *cmd, flowop_t *flowop)
2255 {
2256 	attr_t *attr;
2257 
2258 	/* Get the filename from attribute */
2259 	if (attr = get_attr(cmd, FSA_FILE)) {
2260 		flowop->fo_filename = attr->attr_avd;
2261 		if (flowop->fo_filename == NULL) {
2262 			filebench_log(LOG_ERROR,
2263 			    "define flowop: no filename specfied");
2264 			filebench_shutdown(1);
2265 		}
2266 	}
2267 
2268 	/* Get the iosize of the op */
2269 	if (attr = get_attr_integer(cmd, FSA_IOSIZE))
2270 		flowop->fo_iosize = attr->attr_avd;
2271 	else
2272 		flowop->fo_iosize = avd_int_alloc(0);
2273 
2274 	/* Get the working set size of the op */
2275 	if (attr = get_attr_integer(cmd, FSA_WSS))
2276 		flowop->fo_wss = attr->attr_avd;
2277 	else
2278 		flowop->fo_wss = avd_int_alloc(0);
2279 
2280 	/* Random I/O? */
2281 	if (attr = get_attr_bool(cmd, FSA_RANDOM))
2282 		flowop->fo_random = attr->attr_avd;
2283 	else
2284 		flowop->fo_random = avd_bool_alloc(FALSE);
2285 
2286 	/* Sync I/O? */
2287 	if (attr = get_attr_bool(cmd, FSA_DSYNC))
2288 		flowop->fo_dsync = attr->attr_avd;
2289 	else
2290 		flowop->fo_dsync = avd_bool_alloc(FALSE);
2291 
2292 	/* Target, for wakeup etc */
2293 	if (attr = get_attr(cmd, FSA_TARGET))
2294 		(void) strcpy(flowop->fo_targetname,
2295 		    avd_get_str(attr->attr_avd));
2296 
2297 	/* Value */
2298 	if (attr = get_attr_integer(cmd, FSA_VALUE))
2299 		flowop->fo_value = attr->attr_avd;
2300 	else
2301 		flowop->fo_value = avd_int_alloc(0);
2302 
2303 	/* FD */
2304 	if (attr = get_attr_integer(cmd, FSA_FD))
2305 		flowop->fo_fdnumber = avd_get_int(attr->attr_avd);
2306 
2307 	/* Rotatefd? */
2308 	if (attr = get_attr_bool(cmd, FSA_ROTATEFD))
2309 		flowop->fo_rotatefd = attr->attr_avd;
2310 	else
2311 		flowop->fo_rotatefd = avd_bool_alloc(FALSE);
2312 
2313 	/* SRC FD, for copies etc... */
2314 	if (attr = get_attr_integer(cmd, FSA_SRCFD))
2315 		flowop->fo_srcfdnumber = avd_get_int(attr->attr_avd);
2316 
2317 	/* Blocking operation? */
2318 	if (attr = get_attr_bool(cmd, FSA_BLOCKING))
2319 		flowop->fo_blocking = attr->attr_avd;
2320 	else
2321 		flowop->fo_blocking = avd_bool_alloc(FALSE);
2322 
2323 	/* Direct I/O Operation */
2324 	if (attr = get_attr_bool(cmd, FSA_DIRECTIO))
2325 		flowop->fo_directio = attr->attr_avd;
2326 	else
2327 		flowop->fo_directio = avd_bool_alloc(FALSE);
2328 
2329 	/* Highwater mark */
2330 	if (attr = get_attr_integer(cmd, FSA_HIGHWATER)) {
2331 		flowop->fo_highwater = attr->attr_avd;
2332 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2333 			filebench_log(LOG_ERROR,
2334 			    "define flowop: Highwater attr cannot be random");
2335 			filebench_shutdown(1);
2336 		}
2337 	} else {
2338 		flowop->fo_highwater = avd_int_alloc(1);
2339 	}
2340 
2341 	/* find file or leaf directory by index number */
2342 	if (attr = get_attr_integer(cmd, FSA_INDEXED))
2343 		flowop->fo_fileindex = attr->attr_avd;
2344 	else
2345 		flowop->fo_fileindex = NULL;
2346 }
2347 
2348 /*
2349  * defines the FLOW_MASTER flowops within a FLOW_MASTER instance of
2350  * a composit flowop. Default attributes from the FLOW_INNER_DEF instances
2351  * of the composit flowop's inner flowops are used if set. Otherwise
2352  * default attributes from the FLOW_MASTER instance of the composit flowop
2353  * are used, which may include defaults from the original FLOW_DEFINITION
2354  * of the composit flowop.
2355  */
2356 static void
2357 parser_inner_flowop_define(threadflow_t *thread, flowop_t *comp0_flow,
2358 			   flowop_t *comp_mstr_flow)
2359 {
2360 	flowop_t *inner_flowtype, *inner_flowop;
2361 
2362 	/* follow flowop list, creating composit names */
2363 	inner_flowtype = comp0_flow->fo_comp_fops;
2364 	comp_mstr_flow->fo_comp_fops = NULL;
2365 
2366 	while (inner_flowtype) {
2367 		char fullname[MAXPATHLEN];
2368 
2369 		/* create composite_name.name for new flowop */
2370 		snprintf(fullname, MAXPATHLEN, "%s.%s",
2371 		    comp_mstr_flow->fo_name, inner_flowtype->fo_name);
2372 
2373 		if ((inner_flowop = flowop_define(thread, fullname,
2374 		    inner_flowtype, &comp_mstr_flow->fo_comp_fops,
2375 		    FLOW_MASTER, 0)) == NULL) {
2376 			filebench_log(LOG_ERROR,
2377 			    "define flowop: Failed to instantiate flowop %s\n",
2378 			    fullname);
2379 			filebench_shutdown(1);
2380 		}
2381 
2382 		/* if applicable, update filename attribute */
2383 		if (inner_flowop->fo_filename) {
2384 			char *name;
2385 
2386 			/* fix up avd_t */
2387 			avd_update(&inner_flowop->fo_filename,
2388 			    comp_mstr_flow->fo_lvar_list);
2389 
2390 			/* see if ready to get the file or fileset */
2391 			name = avd_get_str(inner_flowop->fo_filename);
2392 			if (name) {
2393 
2394 				inner_flowop->fo_fileset = fileset_find(name);
2395 
2396 				if (inner_flowop->fo_fileset == NULL) {
2397 					filebench_log(LOG_ERROR,
2398 					    "inr flowop %s: file %s not found",
2399 					    inner_flowop->fo_name, name);
2400 					filebench_shutdown(1);
2401 				}
2402 			}
2403 		}
2404 
2405 		/* update attributes from local variables */
2406 		avd_update(&inner_flowop->fo_iters,
2407 		    comp_mstr_flow->fo_lvar_list);
2408 
2409 		/* if the inner flowop is a composit flowop, recurse */
2410 		if (inner_flowtype->fo_type == FLOW_TYPE_COMPOSITE) {
2411 			var_t *newlvar, *proto_lvars, *lvar_ptr;
2412 
2413 			proto_lvars = inner_flowop->fo_lvar_list;
2414 			inner_flowop->fo_lvar_list = 0;
2415 
2416 			for (lvar_ptr = inner_flowtype->fo_lvar_list; lvar_ptr;
2417 			    lvar_ptr = lvar_ptr->var_next) {
2418 
2419 				if ((newlvar = var_lvar_alloc_local(
2420 				    lvar_ptr->var_name)) != NULL) {
2421 
2422 					add_lvar_to_list(newlvar,
2423 					    &inner_flowop->fo_lvar_list);
2424 
2425 					var_update_comp_lvars(newlvar,
2426 					    proto_lvars,
2427 					    comp_mstr_flow->fo_lvar_list);
2428 				}
2429 			}
2430 
2431 			parser_inner_flowop_define(thread,
2432 			    inner_flowtype,
2433 			    inner_flowop);
2434 
2435 			inner_flowtype = inner_flowtype->fo_exec_next;
2436 			continue;
2437 		}
2438 
2439 		avd_update(&inner_flowop->fo_iosize,
2440 		    comp_mstr_flow->fo_lvar_list);
2441 		avd_update(&inner_flowop->fo_wss,
2442 		    comp_mstr_flow->fo_lvar_list);
2443 		avd_update(&inner_flowop->fo_iters,
2444 		    comp_mstr_flow->fo_lvar_list);
2445 		avd_update(&inner_flowop->fo_value,
2446 		    comp_mstr_flow->fo_lvar_list);
2447 		avd_update(&inner_flowop->fo_random,
2448 		    comp_mstr_flow->fo_lvar_list);
2449 		avd_update(&inner_flowop->fo_dsync,
2450 		    comp_mstr_flow->fo_lvar_list);
2451 		avd_update(&inner_flowop->fo_rotatefd,
2452 		    comp_mstr_flow->fo_lvar_list);
2453 		avd_update(&inner_flowop->fo_blocking,
2454 		    comp_mstr_flow->fo_lvar_list);
2455 		avd_update(&inner_flowop->fo_directio,
2456 		    comp_mstr_flow->fo_lvar_list);
2457 		avd_update(&inner_flowop->fo_highwater,
2458 		    comp_mstr_flow->fo_lvar_list);
2459 
2460 		inner_flowtype = inner_flowtype->fo_exec_next;
2461 	}
2462 }
2463 
2464 /*
2465  * Calls flowop_define() to allocate a flowop with the supplied name.
2466  * The allocated flowop inherits attributes from a base flowop of the
2467  * same type.  If the new flowop has a file or fileset attribute specified,
2468  * it must specify a defined fileobj or fileset or an error will be logged.
2469  * The new flowop may  also have the following attributes set by
2470  * the program:
2471  *  - file size (fo_iosize)
2472  *  - working set size (fo_wss)
2473  *  - do random io (fo_random)
2474  *  - do synchronous io (fo_dsync)
2475  *  - perform each operation multiple times before advancing (fo_iter)
2476  *  - target name (fo_targetname)
2477  *  - An integer value (fo_value)
2478  *  - a file descriptor (fo_fd)
2479  *  - specify to rotate file descriptors (fo_rotatefd)
2480  *  - a source fd (fo_srcfdnumber)
2481  *  - specify a blocking operation (fo_blocking)
2482  *  - specify a highwater mark (fo_highwater)
2483  *
2484  * After all the supplied attributes are stored in their respective locations
2485  * in the flowop object, the flowop's init function is called. No errors are
2486  * returned, but the filebench run will be terminated if the flowtype is not
2487  * specified, a name for the new flowop is not supplied, the flowop_define
2488  * call fails, or a file or fileset name is supplied but the corresponding
2489  * fileobj or fileset cannot be located.
2490  */
2491 static void
2492 parser_flowop_define(cmd_t *cmd, threadflow_t *thread,
2493     flowop_t **flowoplist_hdp, int category)
2494 {
2495 	flowop_t *flowop, *flowop_type;
2496 	char *type = (char *)cmd->cmd_name;
2497 	char *name;
2498 	attr_t *attr;
2499 
2500 	/* Get the inherited flowop */
2501 	flowop_type = flowop_find(type);
2502 	if (flowop_type == NULL) {
2503 		filebench_log(LOG_ERROR,
2504 		    "define flowop: flowop type %s not found",
2505 		    type);
2506 		filebench_shutdown(1);
2507 	}
2508 
2509 	/* Get the name of the flowop */
2510 	if (attr = get_attr(cmd, FSA_NAME)) {
2511 		name = avd_get_str(attr->attr_avd);
2512 	} else {
2513 		filebench_log(LOG_ERROR,
2514 		    "define flowop: flowop %s specifies no name",
2515 		    flowop_type->fo_name);
2516 		filebench_shutdown(1);
2517 	}
2518 
2519 	if ((flowop = flowop_define(thread, name,
2520 	    flowop_type, flowoplist_hdp, category, 0)) == NULL) {
2521 		filebench_log(LOG_ERROR,
2522 		    "define flowop: Failed to instantiate flowop %s\n",
2523 		    cmd->cmd_name);
2524 		filebench_shutdown(1);
2525 	}
2526 
2527 	/* Iterations */
2528 	if (attr = get_attr_integer(cmd, FSA_ITERS))
2529 		flowop->fo_iters = attr->attr_avd;
2530 	else
2531 		flowop->fo_iters = avd_int_alloc(1);
2532 
2533 
2534 	/* if this is a use of a composit flowop, create inner FLOW MASTERS */
2535 	if (flowop_type->fo_type == FLOW_TYPE_COMPOSITE) {
2536 		get_attr_lvars(cmd, flowop);
2537 		if (category == FLOW_MASTER)
2538 			parser_inner_flowop_define(thread,
2539 			    flowop_type, flowop);
2540 	}
2541 	else {
2542 		parser_flowop_get_attrs(cmd, flowop);
2543 	}
2544 }
2545 
2546 static void
2547 parser_composite_flowop_define(cmd_t *cmd)
2548 {
2549 	flowop_t *flowop;
2550 	cmd_t *inner_cmd;
2551 	char *name;
2552 	attr_t *attr;
2553 
2554 	/* Get the name of the flowop */
2555 	if (attr = get_attr(cmd, FSA_NAME)) {
2556 		name = avd_get_str(attr->attr_avd);
2557 	} else {
2558 		filebench_log(LOG_ERROR,
2559 		    "define flowop: Composit flowop specifies no name");
2560 
2561 		filebench_shutdown(1);
2562 	}
2563 
2564 	if ((flowop = flowop_new_composite_define(name)) == NULL) {
2565 		filebench_log(LOG_ERROR,
2566 		    "define flowop: Failed to instantiate flowop %s\n",
2567 		    cmd->cmd_name);
2568 		filebench_shutdown(1);
2569 	}
2570 
2571 	/* place any local var_t variables on the flowop's local list */
2572 	get_attr_lvars(cmd, flowop);
2573 
2574 	/* Iterations */
2575 	if (attr = get_attr_integer(cmd, FSA_ITERS))
2576 		flowop->fo_iters = attr->attr_avd;
2577 	else
2578 		flowop->fo_iters = avd_int_alloc(1);
2579 
2580 	/* define inner flowops */
2581 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2582 	    inner_cmd = inner_cmd->cmd_next) {
2583 		parser_flowop_define(inner_cmd, NULL,
2584 		    &flowop->fo_comp_fops, FLOW_INNER_DEF);
2585 	}
2586 }
2587 
2588 
2589 /*
2590  * Calls fileset_define() to allocate a fileset with the supplied name and
2591  * initializes the fileset's pathname attribute, and optionally the
2592  * fileset_cached, fileset_reuse, fileset_prealloc and fileset_size attributes.
2593  *
2594  */
2595 static fileset_t *
2596 parser_fileset_define_common(cmd_t *cmd)
2597 {
2598 	fileset_t *fileset;
2599 	avd_t name;
2600 	attr_t *attr;
2601 	avd_t pathname;
2602 
2603 	/* Get the name of the file */
2604 	if (attr = get_attr_fileset(cmd, FSA_NAME)) {
2605 		name = attr->attr_avd;
2606 	} else {
2607 		filebench_log(LOG_ERROR,
2608 		    "define fileset: file or fileset specifies no name");
2609 		return (NULL);
2610 	}
2611 
2612 	if ((fileset = fileset_define(name)) == NULL) {
2613 		filebench_log(LOG_ERROR,
2614 		    "define file: failed to instantiate file %s\n",
2615 		    avd_get_str(name));
2616 		return (NULL);
2617 	}
2618 
2619 	/* Get the pathname from attribute */
2620 	if ((attr = get_attr(cmd, FSA_PATH)) == NULL) {
2621 		filebench_log(LOG_ERROR, "define file: no pathname specified");
2622 		return (NULL);
2623 	}
2624 
2625 	/* Expand variables in pathname */
2626 	if ((pathname = parser_list2varstring(attr->attr_param_list))
2627 	    == NULL) {
2628 		filebench_log(LOG_ERROR, "Cannot interpret path");
2629 		return (NULL);
2630 	}
2631 
2632 	fileset->fs_path = pathname;
2633 
2634 	/* How much should we preallocate? */
2635 	if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) &&
2636 	    attr->attr_avd) {
2637 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2638 			filebench_log(LOG_ERROR,
2639 			    "define fileset: Prealloc attr cannot be random");
2640 			filebench_shutdown(1);
2641 		}
2642 		fileset->fs_preallocpercent = attr->attr_avd;
2643 	} else if (attr && !attr->attr_avd) {
2644 		fileset->fs_preallocpercent = avd_int_alloc(100);
2645 	} else {
2646 		fileset->fs_preallocpercent = avd_int_alloc(0);
2647 	}
2648 
2649 	/* Should we preallocate? */
2650 	if (attr = get_attr_bool(cmd, FSA_PREALLOC))
2651 		fileset->fs_prealloc = attr->attr_avd;
2652 	else
2653 		fileset->fs_prealloc = avd_bool_alloc(FALSE);
2654 
2655 	/* Should we prealloc in parallel? */
2656 	if (attr = get_attr_bool(cmd, FSA_PARALLOC))
2657 		fileset->fs_paralloc = attr->attr_avd;
2658 	else
2659 		fileset->fs_paralloc = avd_bool_alloc(FALSE);
2660 
2661 	/* Should we reuse the existing file? */
2662 	if (attr = get_attr_bool(cmd, FSA_REUSE))
2663 		fileset->fs_reuse = attr->attr_avd;
2664 	else
2665 		fileset->fs_reuse = avd_bool_alloc(FALSE);
2666 
2667 	/* Should we leave in cache? */
2668 	if (attr = get_attr_bool(cmd, FSA_CACHED))
2669 		fileset->fs_cached = attr->attr_avd;
2670 	else
2671 		fileset->fs_cached = avd_bool_alloc(FALSE);
2672 
2673 	/* Get the mean or absolute size of the file */
2674 	if (attr = get_attr_integer(cmd, FSA_SIZE))
2675 		fileset->fs_size = attr->attr_avd;
2676 	else
2677 		fileset->fs_size = avd_int_alloc(0);
2678 
2679 	return (fileset);
2680 }
2681 
2682 /*
2683  * Calls parser_fileset_define_common() to allocate a fileset with
2684  * one entry and optionally the fileset_prealloc. sets the fileset_entries,
2685  * fileset_dirwidth, fileset_dirgamma, and fileset_sizegamma attributes
2686  * to appropriate values for emulating the old "fileobj" entity
2687  */
2688 static void
2689 parser_file_define(cmd_t *cmd)
2690 {
2691 	fileset_t *fileset;
2692 	attr_t *attr;
2693 
2694 	if ((fileset = parser_fileset_define_common(cmd)) == NULL) {
2695 		filebench_log(LOG_ERROR,
2696 		    "define file: failed to instantiate file");
2697 		filebench_shutdown(1);
2698 		return;
2699 	}
2700 
2701 	/* fileset is emulating a single file */
2702 	fileset->fs_attrs = FILESET_IS_FILE;
2703 
2704 	/* Set the size of the fileset to 1 */
2705 	fileset->fs_entries = avd_int_alloc(1);
2706 
2707 	/* Set the mean dir width to more than 1 */
2708 	fileset->fs_dirwidth = avd_int_alloc(10);
2709 
2710 	/* Set the dir and size gammas to 0 */
2711 	fileset->fs_dirgamma = avd_int_alloc(0);
2712 	fileset->fs_sizegamma = avd_int_alloc(0);
2713 }
2714 
2715 /*
2716  * Calls parser_fileset_define_common() to allocate a fileset with the
2717  * supplied name and initializes the fileset's fileset_preallocpercent,
2718  * fileset_prealloc, fileset_entries, fileset_dirwidth, fileset_dirgamma,
2719  * and fileset_sizegamma attributes.
2720  */
2721 static void
2722 parser_fileset_define(cmd_t *cmd)
2723 {
2724 	fileset_t *fileset;
2725 	attr_t *attr;
2726 
2727 	if ((fileset = parser_fileset_define_common(cmd)) == NULL) {
2728 		filebench_log(LOG_ERROR,
2729 		    "define fileset: failed to instantiate fileset");
2730 		filebench_shutdown(1);
2731 		return;
2732 	}
2733 	/* Get the number of files in the fileset */
2734 	if (attr = get_attr_integer(cmd, FSA_ENTRIES)) {
2735 		fileset->fs_entries = attr->attr_avd;
2736 	} else {
2737 		fileset->fs_entries = avd_int_alloc(0);
2738 	}
2739 
2740 	/* Get the number of leafdirs in the fileset */
2741 	if (attr = get_attr_integer(cmd, FSA_LEAFDIRS)) {
2742 		fileset->fs_leafdirs = attr->attr_avd;
2743 	} else {
2744 		fileset->fs_leafdirs = avd_int_alloc(0);
2745 	}
2746 
2747 	if ((avd_get_int(fileset->fs_entries) == 0) &&
2748 	    (avd_get_int(fileset->fs_leafdirs) == 0)) {
2749 		filebench_log(LOG_ERROR, "Fileset has no files or leafdirs");
2750 	}
2751 
2752 	/* Get the mean dir width of the fileset */
2753 	if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) {
2754 		fileset->fs_dirwidth = attr->attr_avd;
2755 	} else {
2756 		filebench_log(LOG_ERROR, "Fileset has zero directory width");
2757 		fileset->fs_dirwidth = avd_int_alloc(0);
2758 	}
2759 
2760 	/* Get the random variable for dir depth, if supplied */
2761 	if (attr = get_attr_integer(cmd, FSA_DIRDEPTHRV)) {
2762 		if (!AVD_IS_RANDOM(attr->attr_avd)) {
2763 			filebench_log(LOG_ERROR,
2764 			    "Define fileset: dirdepthrv must be random var");
2765 			filebench_shutdown(1);
2766 		}
2767 		fileset->fs_dirdepthrv = attr->attr_avd;
2768 	} else {
2769 		fileset->fs_dirdepthrv = NULL;
2770 	}
2771 
2772 	/* Get the gamma value for dir depth distributions */
2773 	if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) {
2774 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2775 			filebench_log(LOG_ERROR,
2776 			    "Define fileset: dirgamma attr cannot be random");
2777 			filebench_shutdown(1);
2778 		}
2779 		fileset->fs_dirgamma = attr->attr_avd;
2780 	} else
2781 		fileset->fs_dirgamma = avd_int_alloc(1500);
2782 
2783 	/* Get the gamma value for dir width distributions */
2784 	if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) {
2785 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2786 			filebench_log(LOG_ERROR,
2787 			    "Define fileset: filesizegamma cannot be random");
2788 			filebench_shutdown(1);
2789 		}
2790 		fileset->fs_sizegamma = attr->attr_avd;
2791 	} else
2792 		fileset->fs_sizegamma = avd_int_alloc(1500);
2793 }
2794 
2795 /*
2796  * Creates and starts all defined procflow processes. The call to
2797  * procflow_init() results in creation of the requested number of
2798  * process instances for each previously defined procflow. The
2799  * child processes exec() a new instance of filebench, passing it
2800  * the instance number and address of the shared memory region.
2801  * The child processes will then create their threads and flowops.
2802  * The routine then unlocks the run_lock to allow all the processes'
2803  * threads to start and  waits for all of them to begin execution.
2804  * Finally, it records the start time and resets the event generation
2805  * system.
2806  */
2807 static void
2808 parser_proc_create(cmd_t *cmd)
2809 {
2810 	filebench_shm->shm_1st_err = 0;
2811 	if (procflow_init() != 0) {
2812 		filebench_log(LOG_ERROR, "Failed to create processes\n");
2813 		filebench_shutdown(1);
2814 	}
2815 
2816 	/* Release the read lock, allowing threads to start */
2817 	(void) pthread_rwlock_unlock(&filebench_shm->shm_run_lock);
2818 
2819 	/* Wait for all threads to start */
2820 	if (procflow_allstarted() != 0) {
2821 		filebench_log(LOG_ERROR, "Could not start run");
2822 		return;
2823 	}
2824 
2825 
2826 	if (filebench_shm->shm_required &&
2827 	    (ipc_ismcreate(filebench_shm->shm_required) < 0)) {
2828 		filebench_log(LOG_ERROR, "Could not allocate shared memory");
2829 		return;
2830 	}
2831 
2832 	filebench_shm->shm_starttime = gethrtime();
2833 	eventgen_reset();
2834 }
2835 
2836 /*
2837  * Calls fileset_createset() to populate all files and filesets and
2838  * create all associated, initially existant,  files and subdirectories.
2839  * If errors are encountered, calls filebench_shutdown()
2840  * to exit filebench.
2841  */
2842 static void
2843 parser_fileset_create(cmd_t *cmd)
2844 {
2845 	if (!filecreate_done) {
2846 		filecreate_done = 1;
2847 
2848 		/* initialize the random number system first */
2849 		randdist_init();
2850 
2851 		/* create all the filesets */
2852 		if (fileset_createset(NULL) != 0) {
2853 			filebench_log(LOG_ERROR, "Failed to create filesets");
2854 			filebench_shutdown(1);
2855 		}
2856 	} else {
2857 		filebench_log(LOG_INFO,
2858 		    "Attempting to create fileset more than once, ignoring");
2859 	}
2860 
2861 }
2862 
2863 /*
2864  * Shuts down all processes and their associated threads. When finished
2865  * it deletes interprocess shared memory and resets the event generator.
2866  * It does not exit the filebench program though.
2867  */
2868 static void
2869 parser_proc_shutdown(cmd_t *cmd)
2870 {
2871 	filebench_log(LOG_INFO, "Shutting down processes");
2872 	filecreate_done = 0;
2873 	procflow_shutdown();
2874 	if (filebench_shm->shm_required)
2875 		ipc_ismdelete();
2876 	eventgen_reset();
2877 }
2878 
2879 /*
2880  * Ends filebench run after first destoring any interprocess
2881  * shared memory. The call to filebench_shutdown()
2882  * also causes filebench to exit.
2883  */
2884 static void
2885 parser_filebench_shutdown(cmd_t *cmd)
2886 {
2887 	int f_abort = filebench_shm->shm_f_abort;
2888 
2889 	ipc_fini();
2890 
2891 	if (f_abort == FILEBENCH_ABORT_ERROR)
2892 		filebench_shutdown(1);
2893 	else
2894 		filebench_shutdown(0);
2895 }
2896 
2897 /*
2898  * This is Used for timing runs.Pauses the master thread in one second
2899  * intervals until the supplied ptime runs out or the f_abort flag
2900  * is raised. If given a time of zero or less, or the mode is stop on
2901  * lack of resources, it will pause until f_abort is raised.
2902  */
2903 static void
2904 parser_pause(int ptime)
2905 {
2906 	int timeslept = 0;
2907 
2908 	if ((filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT) &&
2909 	    (ptime > 0)) {
2910 		while (timeslept < ptime) {
2911 			(void) sleep(1);
2912 			timeslept++;
2913 			if (filebench_shm->shm_f_abort)
2914 				break;
2915 		}
2916 	} else {
2917 		/* initial runtime of 0 means run till abort */
2918 		/* CONSTCOND */
2919 		while (1) {
2920 			(void) sleep(1);
2921 			timeslept++;
2922 			if (filebench_shm->shm_f_abort)
2923 				break;
2924 		}
2925 	}
2926 
2927 	filebench_log(LOG_INFO, "Run took %d seconds...", timeslept);
2928 }
2929 
2930 /*
2931  * Do a file bench run. Calls routines to create file sets, files, and
2932  * processes. It resets the statistics counters, then sleeps for the runtime
2933  * passed as an argument to it on the command line in 1 second increments.
2934  * When it is finished sleeping, it collects a snapshot of the statistics
2935  * and ends the run.
2936  */
2937 static void
2938 parser_run(cmd_t *cmd)
2939 {
2940 	int runtime;
2941 
2942 	runtime = cmd->cmd_qty;
2943 
2944 	parser_fileset_create(cmd);
2945 	parser_proc_create(cmd);
2946 
2947 	/* check for startup errors */
2948 	if (filebench_shm->shm_f_abort)
2949 		return;
2950 
2951 	filebench_log(LOG_INFO, "Running...");
2952 	stats_clear();
2953 
2954 	parser_pause(runtime);
2955 
2956 	parser_statssnap(cmd);
2957 	parser_proc_shutdown(cmd);
2958 }
2959 
2960 /*
2961  * Similar to parser_run, but gets the sleep time from a variable
2962  * whose name is supplied as an argument to the command.
2963  */
2964 static void
2965 parser_run_variable(cmd_t *cmd)
2966 {
2967 	avd_t integer = var_ref_attr(cmd->cmd_tgt1);
2968 	int runtime;
2969 
2970 	if (integer == NULL) {
2971 		filebench_log(LOG_ERROR, "Unknown variable %s",
2972 		cmd->cmd_tgt1);
2973 		return;
2974 	}
2975 
2976 	runtime = avd_get_int(integer);
2977 
2978 	/* check for startup errors */
2979 	if (filebench_shm->shm_f_abort)
2980 		return;
2981 
2982 	filebench_log(LOG_INFO, "Running...");
2983 	stats_clear();
2984 
2985 	parser_pause(runtime);
2986 
2987 	parser_statssnap(cmd);
2988 	parser_proc_shutdown(cmd);
2989 }
2990 
2991 char *usagestr = NULL;
2992 
2993 /*
2994  * Prints usage string if defined, else just a message requesting load of a
2995  * personality.
2996  */
2997 static void
2998 parser_help(cmd_t *cmd)
2999 {
3000 	if (usagestr) {
3001 		filebench_log(LOG_INFO, "%s", usagestr);
3002 	} else {
3003 		filebench_log(LOG_INFO,
3004 		    "load <personality> (ls "
3005 		    FILEBENCHDIR "/workloads for list)");
3006 	}
3007 }
3008 
3009 char *varstr = NULL;
3010 
3011 /*
3012  * Prints the string of all var definitions, if there is one.
3013  */
3014 static void
3015 parser_printvars(cmd_t *cmd)
3016 {
3017 	char *str, *c;
3018 
3019 	if (varstr) {
3020 		str = strdup(varstr);
3021 		for (c = str; *c != '\0'; c++) {
3022 			if ((char)*c == '$')
3023 				*c = ' ';
3024 		}
3025 		filebench_log(LOG_INFO, "%s", str);
3026 		free(str);
3027 	}
3028 }
3029 
3030 /*
3031  * Establishes multi-client synchronization socket with synch server.
3032  */
3033 static void
3034 parser_enable_mc(cmd_t *cmd)
3035 {
3036 	attr_t *attr;
3037 	char *master;
3038 	char *client;
3039 
3040 	if (attr= get_attr(cmd, FSA_MASTER)) {
3041 		master = avd_get_str(attr->attr_avd);
3042 	} else {
3043 		filebench_log(LOG_ERROR,
3044 		    "enable multi: no master specified");
3045 		return;
3046 	}
3047 
3048 	if (attr= get_attr(cmd, FSA_CLIENT)) {
3049 		client = avd_get_str(attr->attr_avd);
3050 	} else {
3051 		filebench_log(LOG_ERROR,
3052 		    "enable multi: no client specified");
3053 		return;
3054 	}
3055 
3056 	mc_sync_open_sock(master, 8001, client);
3057 }
3058 
3059 /*
3060  * Exchanges multi-client synchronization message with synch server.
3061  */
3062 static void
3063 parser_domultisync(cmd_t *cmd)
3064 {
3065 	attr_t *attr;
3066 	fbint_t value;
3067 
3068 	if (attr = get_attr(cmd, FSA_VALUE))
3069 		value = avd_get_int(attr->attr_avd);
3070 	else
3071 		value = 1;
3072 
3073 	mc_sync_synchronize((int)value);
3074 }
3075 
3076 /*
3077  * Used by the SET command to add a var and default value string to the
3078  * varstr string. It allocates a new, larger varstr string, copies the
3079  * old contents of varstr into it, then adds the new var string on the end.
3080  */
3081 static void
3082 parser_vars(cmd_t *cmd)
3083 {
3084 	char *string = cmd->cmd_tgt1;
3085 	char *newvars;
3086 
3087 	if (string == NULL)
3088 		return;
3089 
3090 	if (dofile)
3091 		return;
3092 
3093 	if (varstr == NULL) {
3094 		newvars = malloc(strlen(string) + 2);
3095 		*newvars = 0;
3096 	} else {
3097 		newvars = malloc(strlen(varstr) + strlen(string) + 2);
3098 		(void) strcpy(newvars, varstr);
3099 	}
3100 	(void) strcat(newvars, string);
3101 	(void) strcat(newvars, " ");
3102 
3103 	if (varstr)
3104 		free(varstr);
3105 
3106 	varstr = newvars;
3107 }
3108 
3109 /*
3110  * Sleeps for cmd->cmd_qty seconds, one second at a time.
3111  */
3112 static void
3113 parser_sleep(cmd_t *cmd)
3114 {
3115 	int sleeptime;
3116 
3117 	/* check for startup errors */
3118 	if (filebench_shm->shm_f_abort)
3119 		return;
3120 
3121 	sleeptime = cmd->cmd_qty;
3122 	filebench_log(LOG_INFO, "Running...");
3123 
3124 	parser_pause(sleeptime);
3125 }
3126 
3127 /*
3128  * used by the set command to set the integer part of a regular
3129  * variable, or the appropriate field of a random variable
3130  */
3131 static void
3132 parser_set_integer(char *name, fbint_t integer)
3133 {
3134 	var_assign_integer(name, integer);
3135 }
3136 
3137 /*
3138  * used by the set command to set the integer part of a regular
3139  * variable from another variable, or the appropriate field of a
3140  * random variable from another variable
3141  */
3142 static void
3143 parser_set_var(char *dst_name, char *src_name)
3144 {
3145 	var_assign_var(dst_name, src_name);
3146 }
3147 
3148 
3149 /*
3150  * Same as parser_sleep, except the sleep time is obtained from a variable
3151  * whose name is passed to it as an argument on the command line.
3152  */
3153 static void
3154 parser_sleep_variable(cmd_t *cmd)
3155 {
3156 	avd_t integer = var_ref_attr(cmd->cmd_tgt1);
3157 	int sleeptime;
3158 
3159 	if (integer == NULL) {
3160 		filebench_log(LOG_ERROR, "Unknown variable %s",
3161 		cmd->cmd_tgt1);
3162 		return;
3163 	}
3164 
3165 	sleeptime = avd_get_int(integer);
3166 
3167 	/* check for startup errors */
3168 	if (filebench_shm->shm_f_abort)
3169 		return;
3170 
3171 	filebench_log(LOG_INFO, "Running...");
3172 
3173 	parser_pause(sleeptime);
3174 }
3175 
3176 /*
3177  * Parser log prints the values of a list of variables to the log file.
3178  * The list of variables is placed on the command line, separated
3179  * by comas and the entire list is enclosed in quotes.
3180  * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576,
3181  * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576
3182  */
3183 static void
3184 parser_log(cmd_t *cmd)
3185 {
3186 	char *string;
3187 
3188 	if (cmd->cmd_param_list == NULL)
3189 		return;
3190 
3191 	string = parser_list2string(cmd->cmd_param_list);
3192 
3193 	if (string == NULL)
3194 		return;
3195 
3196 	filebench_log(LOG_VERBOSE, "log %s", string);
3197 	filebench_log(LOG_LOG, "%s", string);
3198 }
3199 
3200 /*
3201  * Implements the stats directory command. changes the directory for
3202  * dumping statistics to supplied directory path. For example:
3203  * 	stats directory /tmp
3204  * changes the stats directory to "/tmp".
3205  */
3206 static void
3207 parser_directory(cmd_t *cmd)
3208 {
3209 	char newdir[MAXPATHLEN];
3210 	char *dir;
3211 
3212 	if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) {
3213 		filebench_log(LOG_ERROR, "Cannot interpret directory");
3214 		return;
3215 	}
3216 
3217 	*newdir = 0;
3218 	/* Change dir relative to cwd if path not fully qualified */
3219 	if (*dir != '/') {
3220 		(void) strcat(newdir, cwd);
3221 		(void) strcat(newdir, "/");
3222 	}
3223 	(void) strcat(newdir, dir);
3224 	(void) mkdir(newdir, 0755);
3225 	filebench_log(LOG_VERBOSE, "Change dir to %s", newdir);
3226 	chdir(newdir);
3227 	free(dir);
3228 }
3229 
3230 #define	PIPE_PARENT 1
3231 #define	PIPE_CHILD  0
3232 
3233 /*
3234  * Runs the quoted unix command as a background process. Intended for
3235  * running statistics gathering utilities such as mpstat while the filebench
3236  * workload is running. Also records the pid's of the background processes
3237  * so that parser_statssnap() can terminate them when the run completes.
3238  */
3239 static void
3240 parser_statscmd(cmd_t *cmd)
3241 {
3242 	char *string;
3243 	pid_t pid;
3244 	pidlist_t *pidlistent;
3245 	int pipe_fd[2];
3246 	int newstdout;
3247 
3248 	if (cmd->cmd_param_list == NULL)
3249 		return;
3250 
3251 	string = parser_list2string(cmd->cmd_param_list);
3252 
3253 	if (string == NULL)
3254 		return;
3255 
3256 	if ((pipe(pipe_fd)) < 0) {
3257 		filebench_log(LOG_ERROR, "statscmd pipe failed");
3258 		return;
3259 	}
3260 
3261 #ifdef HAVE_FORK1
3262 	if ((pid = fork1()) < 0) {
3263 		filebench_log(LOG_ERROR, "statscmd fork failed");
3264 		return;
3265 	}
3266 #elif HAVE_FORK
3267 	if ((pid = fork()) < 0) {
3268 		filebench_log(LOG_ERROR, "statscmd fork failed");
3269 		return;
3270 	}
3271 #else
3272 	Crash! - Need code to deal with no fork1!
3273 #endif /* HAVE_FORK1 */
3274 
3275 	if (pid == 0) {
3276 
3277 		setsid();
3278 
3279 		filebench_log(LOG_VERBOSE,
3280 		    "Backgrounding %s", string);
3281 		/*
3282 		 * Child
3283 		 * - close stdout
3284 		 * - dup to create new stdout
3285 		 * - close pipe fds
3286 		 */
3287 		(void) close(1);
3288 
3289 		if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) {
3290 			filebench_log(LOG_ERROR,
3291 			    "statscmd dup failed: %s",
3292 			    strerror(errno));
3293 		}
3294 
3295 		(void) close(pipe_fd[PIPE_PARENT]);
3296 		(void) close(pipe_fd[PIPE_CHILD]);
3297 
3298 		if (system(string) < 0) {
3299 			filebench_log(LOG_ERROR,
3300 			    "statscmd exec failed: %s",
3301 			    strerror(errno));
3302 		}
3303 		/* Failed! */
3304 		exit(1);
3305 
3306 	} else {
3307 
3308 		/* Record pid in pidlist for subsequent reaping by stats snap */
3309 		if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t)))
3310 		    == NULL) {
3311 			filebench_log(LOG_ERROR, "pidlistent malloc failed");
3312 			return;
3313 		}
3314 
3315 		pidlistent->pl_pid = pid;
3316 		pidlistent->pl_fd = pipe_fd[PIPE_PARENT];
3317 		(void) close(pipe_fd[PIPE_CHILD]);
3318 
3319 		/* Add fileobj to global list */
3320 		if (pidlist == NULL) {
3321 			pidlist = pidlistent;
3322 			pidlistent->pl_next = NULL;
3323 		} else {
3324 			pidlistent->pl_next = pidlist;
3325 			pidlist = pidlistent;
3326 		}
3327 	}
3328 }
3329 
3330 /*
3331  * Launches a shell to run the unix command supplied in the argument.
3332  * The command should be enclosed in quotes, as in:
3333  * 	system "rm xyz"
3334  * which would run the "rm" utility to delete the file "xyz".
3335  */
3336 static void
3337 parser_system(cmd_t *cmd)
3338 {
3339 	char *string;
3340 
3341 	if (cmd->cmd_param_list == NULL)
3342 		return;
3343 
3344 	string = parser_list2string(cmd->cmd_param_list);
3345 
3346 	if (string == NULL)
3347 		return;
3348 
3349 	filebench_log(LOG_VERBOSE,
3350 	    "Running '%s'", string);
3351 
3352 	if (system(string) < 0) {
3353 		filebench_log(LOG_ERROR,
3354 		    "system exec failed: %s",
3355 		    strerror(errno));
3356 	}
3357 	free(string);
3358 }
3359 
3360 /*
3361  * Echos string supplied with command to the log.
3362  */
3363 static void
3364 parser_echo(cmd_t *cmd)
3365 {
3366 	char *string;
3367 
3368 	if (cmd->cmd_param_list == NULL)
3369 		return;
3370 
3371 	string = parser_list2string(cmd->cmd_param_list);
3372 
3373 	if (string == NULL)
3374 		return;
3375 
3376 	filebench_log(LOG_INFO, "%s", string);
3377 }
3378 
3379 /*
3380  * Prints out the version of FileBench.
3381  */
3382 static void
3383 parser_version(cmd_t *cmd)
3384 {
3385 	filebench_log(LOG_INFO, "FileBench Version: %s", FILEBENCH_VERSION);
3386 }
3387 
3388 /*
3389  * Adds the string supplied as the argument to the usage command
3390  * to the end of the string printed by the help command.
3391  */
3392 static void
3393 parser_usage(cmd_t *cmd)
3394 {
3395 	char *string;
3396 	char *newusage;
3397 
3398 	if (cmd->cmd_param_list == NULL)
3399 		return;
3400 
3401 	string = parser_list2string(cmd->cmd_param_list);
3402 
3403 	if (string == NULL)
3404 		return;
3405 
3406 	if (dofile)
3407 		return;
3408 
3409 	if (usagestr == NULL) {
3410 		newusage = malloc(strlen(string) + 2);
3411 		*newusage = 0;
3412 	} else {
3413 		newusage = malloc(strlen(usagestr) + strlen(string) + 2);
3414 		(void) strcpy(newusage, usagestr);
3415 	}
3416 	(void) strcat(newusage, "\n");
3417 	(void) strcat(newusage, string);
3418 
3419 	if (usagestr)
3420 		free(usagestr);
3421 
3422 	usagestr = newusage;
3423 
3424 	filebench_log(LOG_INFO, "%s", string);
3425 }
3426 
3427 /*
3428  * Updates the global dump filename with the filename supplied
3429  * as the command's argument. Then dumps the statistics of each
3430  * worker flowop into the dump file, followed by a summary of
3431  * overall totals.
3432  */
3433 static void
3434 parser_statsdump(cmd_t *cmd)
3435 {
3436 	char *string;
3437 
3438 	if (cmd->cmd_param_list == NULL)
3439 		return;
3440 
3441 	string = parser_list2string(cmd->cmd_param_list);
3442 
3443 	if (string == NULL)
3444 		return;
3445 
3446 	filebench_log(LOG_VERBOSE,
3447 	    "Stats dump to file '%s'", string);
3448 
3449 	stats_dump(string);
3450 
3451 	free(string);
3452 }
3453 
3454 /*
3455  * Same as statsdump, but outputs in a computer friendly format.
3456  */
3457 static void
3458 parser_statsmultidump(cmd_t *cmd)
3459 {
3460 	char *string;
3461 
3462 	if (cmd->cmd_param_list == NULL)
3463 		return;
3464 
3465 	string = parser_list2string(cmd->cmd_param_list);
3466 
3467 	if (string == NULL)
3468 		return;
3469 
3470 	filebench_log(LOG_VERBOSE,
3471 	    "Stats dump to file '%s'", string);
3472 
3473 	stats_multidump(string);
3474 
3475 	free(string);
3476 }
3477 
3478 /*
3479  * Same as parser_statsdump, but in xml format.
3480  */
3481 static void
3482 parser_statsxmldump(cmd_t *cmd)
3483 {
3484 	char *string;
3485 
3486 	if (cmd->cmd_param_list == NULL)
3487 		return;
3488 
3489 	string = parser_list2string(cmd->cmd_param_list);
3490 
3491 	if (string == NULL)
3492 		return;
3493 
3494 	filebench_log(LOG_VERBOSE,
3495 	    "Stats dump to file '%s'", string);
3496 
3497 	stats_xmldump(string);
3498 
3499 	free(string);
3500 }
3501 
3502 /*
3503  * Kills off background statistics collection processes, then takes a snapshot
3504  * of the filebench run's collected statistics using stats_snap() from
3505  * stats.c.
3506  */
3507 static void
3508 parser_statssnap(cmd_t *cmd)
3509 {
3510 	pidlist_t *pidlistent;
3511 	int stat;
3512 	pid_t pid;
3513 
3514 	for (pidlistent = pidlist; pidlistent != NULL;
3515 	    pidlistent = pidlistent->pl_next) {
3516 		filebench_log(LOG_VERBOSE, "Killing session %d for pid %d",
3517 		    getsid(pidlistent->pl_pid),
3518 		    pidlistent->pl_pid);
3519 		if (pidlistent->pl_fd)
3520 			(void) close(pidlistent->pl_fd);
3521 #ifdef HAVE_SIGSEND
3522 		sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM);
3523 #else
3524 		(void) kill(-1, SIGTERM);
3525 #endif
3526 
3527 		/* Close pipe */
3528 		if (pidlistent->pl_fd)
3529 			(void) close(pidlistent->pl_fd);
3530 
3531 		/* Wait for cmd and all its children */
3532 		while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0)
3533 			filebench_log(LOG_DEBUG_IMPL,
3534 			"Waited for pid %d", (int)pid);
3535 	}
3536 
3537 	for (pidlistent = pidlist; pidlistent != NULL;
3538 	    pidlistent = pidlistent->pl_next) {
3539 		free(pidlistent);
3540 	}
3541 
3542 	pidlist = NULL;
3543 	stats_snap();
3544 }
3545 
3546 /*
3547  * Shutdown filebench.
3548  */
3549 static void
3550 parser_abort(int arg)
3551 {
3552 	(void) sigignore(SIGINT);
3553 	filebench_log(LOG_INFO, "Aborting...");
3554 	filebench_shutdown(1);
3555 }
3556 
3557 /*
3558  * define a random variable and initialize the distribution parameters
3559  */
3560 static void
3561 parser_randvar_define(cmd_t *cmd)
3562 {
3563 	var_t		*var;
3564 	randdist_t	*rndp;
3565 	attr_t		*attr;
3566 	char		*name;
3567 
3568 	/* Get the name for the random variable */
3569 	if (attr = get_attr(cmd, FSA_NAME)) {
3570 		name = avd_get_str(attr->attr_avd);
3571 	} else {
3572 		filebench_log(LOG_ERROR,
3573 		    "define randvar: no name specified");
3574 		return;
3575 	}
3576 
3577 	if ((var = var_define_randvar(name)) == NULL) {
3578 		filebench_log(LOG_ERROR,
3579 		    "define randvar: failed for random variable %s",
3580 		    name);
3581 		return;
3582 	}
3583 
3584 	rndp = var->var_val.randptr;
3585 	rndp->rnd_type = 0;
3586 
3587 	/* Get the source of the random numbers */
3588 	if (attr = get_attr_integer(cmd, FSA_RANDSRC)) {
3589 		int randsrc = (int)avd_get_int(attr->attr_avd);
3590 
3591 		switch (randsrc) {
3592 		case FSV_URAND:
3593 			rndp->rnd_type |= RAND_SRC_URANDOM;
3594 			break;
3595 		case FSV_RAND48:
3596 			rndp->rnd_type |= RAND_SRC_GENERATOR;
3597 			break;
3598 		}
3599 	} else {
3600 		/* default to rand48 random number generator */
3601 		rndp->rnd_type |= RAND_SRC_GENERATOR;
3602 	}
3603 
3604 	/* Get the min value of the random distribution */
3605 	if (attr = get_attr_integer(cmd, FSA_RANDMIN))
3606 		rndp->rnd_min = attr->attr_avd;
3607 	else
3608 		rndp->rnd_min = avd_int_alloc(0);
3609 
3610 	/* Get the roundoff value for the random distribution */
3611 	if (attr = get_attr_integer(cmd, FSA_RANDROUND))
3612 		rndp->rnd_round = attr->attr_avd;
3613 	else
3614 		rndp->rnd_round = avd_int_alloc(0);
3615 
3616 	/* Get a tablular probablility distribution if there is one */
3617 	if (attr = get_attr(cmd, FSA_RANDTABLE)) {
3618 		rndp->rnd_probtabs = (probtabent_t *)(attr->attr_obj);
3619 		rndp->rnd_type |= RAND_TYPE_TABLE;
3620 
3621 		/* no need for the rest of the attributes */
3622 		return;
3623 	} else {
3624 		rndp->rnd_probtabs = NULL;
3625 	}
3626 
3627 	/* Get the type for the random variable */
3628 	if (attr = get_attr(cmd, FSA_TYPE)) {
3629 		int disttype = (int)avd_get_int(attr->attr_avd);
3630 
3631 		switch (disttype) {
3632 		case FSV_RANDUNI:
3633 			rndp->rnd_type |= RAND_TYPE_UNIFORM;
3634 			break;
3635 		case FSA_RANDGAMMA:
3636 			rndp->rnd_type |= RAND_TYPE_GAMMA;
3637 			break;
3638 		case FSV_RANDTAB:
3639 			filebench_log(LOG_ERROR,
3640 			    "Table distribution type without prob table");
3641 			break;
3642 		}
3643 	} else {
3644 		/* default to gamma distribution type */
3645 		rndp->rnd_type |= RAND_TYPE_GAMMA;
3646 	}
3647 
3648 	/* Get the seed for the random variable */
3649 	if (attr = get_attr_integer(cmd, FSA_RANDSEED))
3650 		rndp->rnd_seed = attr->attr_avd;
3651 	else
3652 		rndp->rnd_seed = avd_int_alloc(0);
3653 
3654 	/* Get the gamma value of the random distribution */
3655 	if (attr = get_attr_integer(cmd, FSA_RANDGAMMA))
3656 		rndp->rnd_gamma = attr->attr_avd;
3657 	else
3658 		rndp->rnd_gamma = avd_int_alloc(1500);
3659 
3660 	/* Get the mean value of the random distribution */
3661 	if (attr = get_attr_integer(cmd, FSA_RANDMEAN)) {
3662 		rndp->rnd_mean = attr->attr_avd;
3663 	} else if ((rndp->rnd_type & RAND_TYPE_MASK) == RAND_TYPE_GAMMA) {
3664 		rndp->rnd_mean = NULL;
3665 	} else {
3666 		rndp->rnd_mean = avd_int_alloc(0);
3667 	}
3668 }
3669 
3670 /*
3671  * Set a specified random distribution parameter in a random variable.
3672  */
3673 static void
3674 parser_randvar_set(cmd_t *cmd)
3675 {
3676 	var_t		*src_var, *randvar;
3677 	randdist_t	*rndp;
3678 	avd_t	value;
3679 
3680 	if ((randvar = var_find_randvar(cmd->cmd_tgt1)) == NULL) {
3681 		filebench_log(LOG_ERROR,
3682 		    "set randvar: failed",
3683 		    cmd->cmd_tgt1);
3684 		return;
3685 	}
3686 
3687 	rndp = randvar->var_val.randptr;
3688 	value = cmd->cmd_attr_list->attr_avd;
3689 
3690 	switch (cmd->cmd_qty) {
3691 	case FSS_TYPE:
3692 		{
3693 			int disttype = (int)avd_get_int(value);
3694 
3695 			rndp->rnd_type &= (~RAND_TYPE_MASK);
3696 
3697 			switch (disttype) {
3698 			case FSV_RANDUNI:
3699 				rndp->rnd_type |= RAND_TYPE_UNIFORM;
3700 				break;
3701 			case FSA_RANDGAMMA:
3702 				rndp->rnd_type |= RAND_TYPE_GAMMA;
3703 				break;
3704 			case FSV_RANDTAB:
3705 				rndp->rnd_type |= RAND_TYPE_TABLE;
3706 				break;
3707 			}
3708 			break;
3709 		}
3710 
3711 	case FSS_SRC:
3712 		{
3713 			int randsrc = (int)avd_get_int(value);
3714 
3715 			rndp->rnd_type &=
3716 			    (~(RAND_SRC_URANDOM | RAND_SRC_GENERATOR));
3717 
3718 			switch (randsrc) {
3719 			case FSV_URAND:
3720 				rndp->rnd_type |= RAND_SRC_URANDOM;
3721 				break;
3722 			case FSV_RAND48:
3723 				rndp->rnd_type |= RAND_SRC_GENERATOR;
3724 				break;
3725 			}
3726 			break;
3727 		}
3728 
3729 	case FSS_SEED:
3730 		rndp->rnd_seed = value;
3731 		break;
3732 
3733 	case FSS_GAMMA:
3734 		rndp->rnd_gamma = value;
3735 		break;
3736 
3737 	case FSS_MEAN:
3738 		rndp->rnd_mean = value;
3739 		break;
3740 
3741 	case FSS_MIN:
3742 		rndp->rnd_min = value;
3743 		break;
3744 
3745 	case FSS_ROUND:
3746 		rndp->rnd_round = value;
3747 		break;
3748 
3749 	default:
3750 		filebench_log(LOG_ERROR, "setrandvar: undefined attribute");
3751 	}
3752 }
3753 
3754 /*
3755  * alloc_cmd() allocates the required resources for a cmd_t. On failure, a
3756  * filebench_log is issued and NULL is returned.
3757  */
3758 static cmd_t *
3759 alloc_cmd(void)
3760 {
3761 	cmd_t *cmd;
3762 
3763 	if ((cmd = malloc(sizeof (cmd_t))) == NULL) {
3764 		filebench_log(LOG_ERROR, "Alloc cmd failed");
3765 		return (NULL);
3766 	}
3767 
3768 	(void) memset(cmd, 0, sizeof (cmd_t));
3769 
3770 	return (cmd);
3771 }
3772 
3773 /*
3774  * Frees the resources of a cmd_t and then the cmd_t "cmd" itself.
3775  */
3776 static void
3777 free_cmd(cmd_t *cmd)
3778 {
3779 	free((void *)cmd->cmd_tgt1);
3780 	free((void *)cmd->cmd_tgt2);
3781 	free(cmd);
3782 }
3783 
3784 /*
3785  * Allocates an attr_t structure and zeros it. Returns NULL on failure, or
3786  * a pointer to the attr_t.
3787  */
3788 static attr_t *
3789 alloc_attr(void)
3790 {
3791 	attr_t *attr;
3792 
3793 	if ((attr = malloc(sizeof (attr_t))) == NULL) {
3794 		return (NULL);
3795 	}
3796 
3797 	(void) memset(attr, 0, sizeof (attr_t));
3798 	return (attr);
3799 }
3800 
3801 /*
3802  * Allocates a probtabent_t structure and zeros it. Returns NULL on failure, or
3803  * a pointer to the probtabent_t.
3804  */
3805 static probtabent_t *
3806 alloc_probtabent(void)
3807 {
3808 	probtabent_t *rte;
3809 
3810 	if ((rte = malloc(sizeof (probtabent_t))) == NULL) {
3811 		return (NULL);
3812 	}
3813 
3814 	(void) memset(rte, 0, sizeof (probtabent_t));
3815 	return (rte);
3816 }
3817 
3818 /*
3819  * Allocates an attr_t structure and puts the supplied var_t into
3820  * its attr_avd location, and sets its name to FSA_LVAR_ASSIGN
3821  */
3822 static attr_t *
3823 alloc_lvar_attr(var_t *var)
3824 {
3825 	attr_t *attr;
3826 
3827 	if ((attr = alloc_attr()) == NULL)
3828 		return (NULL);
3829 
3830 	attr->attr_name = FSA_LVAR_ASSIGN;
3831 	attr->attr_avd = (avd_t)var;
3832 
3833 	return (attr);
3834 }
3835 
3836 
3837 /*
3838  * Searches the attribute list for the command for the named attribute type.
3839  * The attribute list is created by the parser from the list of attributes
3840  * supplied with certain commands, such as the define and flowop commands.
3841  * Returns a pointer to the attribute structure if the named attribute is
3842  * found, otherwise returns NULL. If the attribute includes a parameter list,
3843  * the list is converted to a string and stored in the attr_avd field of
3844  * the returned attr_t struct.
3845  */
3846 static attr_t *
3847 get_attr_fileset(cmd_t *cmd, int64_t name)
3848 {
3849 	attr_t *attr;
3850 	attr_t *rtn = NULL;
3851 	char *string;
3852 
3853 	for (attr = cmd->cmd_attr_list; attr != NULL;
3854 	    attr = attr->attr_next) {
3855 		filebench_log(LOG_DEBUG_IMPL,
3856 		    "attr %d = %d %llx?",
3857 		    attr->attr_name,
3858 		    name,
3859 		    attr->attr_avd);
3860 
3861 		if (attr->attr_name == name)
3862 			rtn = attr;
3863 	}
3864 
3865 	if (rtn == NULL)
3866 		return (NULL);
3867 
3868 	if (rtn->attr_param_list) {
3869 		filebench_log(LOG_DEBUG_SCRIPT, "attr is param list");
3870 		rtn->attr_avd = parser_list2varstring(rtn->attr_param_list);
3871 	}
3872 
3873 	return (rtn);
3874 }
3875 
3876 
3877 /*
3878  * Searches the attribute list for the command for the named attribute type.
3879  * The attribute list is created by the parser from the list of attributes
3880  * supplied with certain commands, such as the define and flowop commands.
3881  * Returns a pointer to the attribute structure if the named attribute is
3882  * found, otherwise returns NULL. If the attribute includes a parameter list,
3883  * the list is converted to a string and stored in the attr_avd field of
3884  * the returned attr_t struct.
3885  */
3886 static attr_t *
3887 get_attr(cmd_t *cmd, int64_t name)
3888 {
3889 	attr_t *attr;
3890 	attr_t *rtn = NULL;
3891 	char *string;
3892 
3893 	for (attr = cmd->cmd_attr_list; attr != NULL;
3894 	    attr = attr->attr_next) {
3895 		filebench_log(LOG_DEBUG_IMPL,
3896 		    "attr %d = %d %llx?",
3897 		    attr->attr_name,
3898 		    name,
3899 		    attr->attr_avd);
3900 
3901 		if (attr->attr_name == name)
3902 			rtn = attr;
3903 	}
3904 
3905 	if (rtn == NULL)
3906 		return (NULL);
3907 
3908 	if (rtn->attr_param_list) {
3909 		filebench_log(LOG_DEBUG_SCRIPT, "attr is param list");
3910 		string = parser_list2string(rtn->attr_param_list);
3911 		if (string != NULL) {
3912 			rtn->attr_avd = avd_str_alloc(string);
3913 			filebench_log(LOG_DEBUG_SCRIPT,
3914 			    "attr string %s", string);
3915 		}
3916 	}
3917 
3918 	return (rtn);
3919 }
3920 
3921 /*
3922  * Similar to get_attr, but converts the parameter string supplied with the
3923  * named attribute to an integer and stores the integer in the attr_avd
3924  * portion of the returned attr_t struct.
3925  */
3926 static attr_t *
3927 get_attr_integer(cmd_t *cmd, int64_t name)
3928 {
3929 	attr_t *attr;
3930 	attr_t *rtn = NULL;
3931 
3932 	for (attr = cmd->cmd_attr_list; attr != NULL;
3933 	    attr = attr->attr_next) {
3934 		if (attr->attr_name == name)
3935 			rtn = attr;
3936 	}
3937 
3938 	if (rtn == NULL)
3939 		return (NULL);
3940 
3941 	if (rtn->attr_param_list)
3942 		rtn->attr_avd = parser_list2avd(rtn->attr_param_list);
3943 
3944 	return (rtn);
3945 }
3946 
3947 /*
3948  * Similar to get_attr, but converts the parameter string supplied with the
3949  * named attribute to an integer and stores the integer in the attr_avd
3950  * portion of the returned attr_t struct. If no parameter string is supplied
3951  * then it defaults to TRUE (1).
3952  */
3953 static attr_t *
3954 get_attr_bool(cmd_t *cmd, int64_t name)
3955 {
3956 	attr_t *attr;
3957 	attr_t *rtn = NULL;
3958 
3959 	for (attr = cmd->cmd_attr_list; attr != NULL;
3960 	    attr = attr->attr_next) {
3961 		if (attr->attr_name == name)
3962 			rtn = attr;
3963 	}
3964 
3965 	if (rtn == NULL)
3966 		return (NULL);
3967 
3968 	if (rtn->attr_param_list) {
3969 		rtn->attr_avd = parser_list2avd(rtn->attr_param_list);
3970 
3971 	} else if (rtn->attr_avd == NULL) {
3972 		rtn->attr_avd = avd_bool_alloc(TRUE);
3973 	}
3974 
3975 	/* boolean attributes cannot point to random variables */
3976 	if (AVD_IS_RANDOM(rtn->attr_avd)) {
3977 		filebench_log(LOG_ERROR,
3978 		    "define flowop: Boolean attr %s cannot be random", name);
3979 		filebench_shutdown(1);
3980 		return (NULL);
3981 	}
3982 
3983 	return (rtn);
3984 }
3985 
3986 /*
3987  * removes the newly allocated local var from the shared local var
3988  * list, then puts it at the head of the private local var list
3989  * supplied as the second argument.
3990  */
3991 static void
3992 add_lvar_to_list(var_t *newlvar, var_t **lvar_list)
3993 {
3994 	var_t *prev;
3995 
3996 	/* remove from shared local list, if there */
3997 	if (newlvar == filebench_shm->shm_var_loc_list) {
3998 		/* on top of list, just grap */
3999 		filebench_shm->shm_var_loc_list = newlvar->var_next;
4000 	} else {
4001 		/* find newvar on list and remove */
4002 		for (prev = filebench_shm->shm_var_loc_list; prev;
4003 		    prev = prev->var_next) {
4004 			if (prev->var_next == newlvar)
4005 				prev->var_next = newlvar->var_next;
4006 		}
4007 	}
4008 	newlvar->var_next = NULL;
4009 
4010 	/* add to flowop private local list at head */
4011 	newlvar->var_next = *lvar_list;
4012 	*lvar_list = newlvar;
4013 }
4014 
4015 /*
4016  * Searches the attribute list for the command for any allocated local
4017  * variables. The attribute list is created by the parser from the list of
4018  * attributes supplied with certain commands, such as the define and flowop
4019  * commands. Places all found local vars onto the flowop's local variable
4020  * list.
4021  */
4022 static void
4023 get_attr_lvars(cmd_t *cmd, flowop_t *flowop)
4024 {
4025 	attr_t *attr;
4026 	var_t *list_tail, *orig_lvar_list;
4027 
4028 	/* save the local var list */
4029 	orig_lvar_list = flowop->fo_lvar_list;
4030 
4031 	for (attr = cmd->cmd_attr_list; attr != NULL;
4032 	    attr = attr->attr_next) {
4033 
4034 		if (attr->attr_name == FSA_LVAR_ASSIGN) {
4035 			var_t *newvar, *prev;
4036 
4037 			if ((newvar = (var_t *)attr->attr_avd) == NULL)
4038 				continue;
4039 
4040 			add_lvar_to_list(newvar, &flowop->fo_lvar_list);
4041 			var_update_comp_lvars(newvar, orig_lvar_list, NULL);
4042 		}
4043 	}
4044 }
4045 
4046 /*
4047  * Allocates memory for a list_t structure, initializes it to zero, and
4048  * returns a pointer to it. On failure, returns NULL.
4049  */
4050 static list_t *
4051 alloc_list()
4052 {
4053 	list_t *list;
4054 
4055 	if ((list = malloc(sizeof (list_t))) == NULL) {
4056 		return (NULL);
4057 	}
4058 
4059 	(void) memset(list, 0, sizeof (list_t));
4060 	return (list);
4061 }
4062 
4063 
4064 #define	USAGE1	\
4065 "Usage:\n" \
4066 "go_filebench: interpret f script and generate file workload\n" \
4067 "Options:\n" \
4068 "   [-h] Display verbose help\n" \
4069 "   [-p] Disable opening /proc to set uacct to enable truss\n"
4070 
4071 #define	PARSER_CMDS \
4072 "create [files|filesets|processes]\n" \
4073 "stats [clear|snap]\n" \
4074 "stats command \"shell command $var1,$var2...\"\n" \
4075 "stats directory <directory>\n" \
4076 "sleep <sleep-value>\n" \
4077 "quit\n\n" \
4078 "Variables:\n" \
4079 "set $var = value\n" \
4080 "    $var   - regular variables\n" \
4081 "    ${var} - internal special variables\n" \
4082 "    $(var) - environment variables\n\n"
4083 
4084 #define	PARSER_EXAMPLE \
4085 "Example:\n\n" \
4086 "#!" FILEBENCHDIR "/bin/go_filebench -f\n" \
4087 "\n" \
4088 "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \
4089 "define process name=randomizer\n" \
4090 "{\n" \
4091 "  thread random-thread procname=randomizer\n"	\
4092 "  {\n" \
4093 "    flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \
4094 "  }\n" \
4095 "}\n" \
4096 "create files\n" \
4097 "create processes\n" \
4098 "stats clear\n" \
4099 "sleep 30\n" \
4100 "stats snap\n"
4101 
4102 /*
4103  * usage() display brief or verbose help for the filebench(1) command.
4104  */
4105 static void
4106 usage(int help)
4107 {
4108 	if (help >= 1)
4109 		(void) fprintf(stderr, USAGE1, cmdname);
4110 	if (help >= 2) {
4111 
4112 		(void) fprintf(stderr,
4113 		    "\n'f' language definition:\n\n");
4114 		fileset_usage();
4115 		procflow_usage();
4116 		threadflow_usage();
4117 		flowoplib_usage();
4118 		eventgen_usage();
4119 		(void) fprintf(stderr, PARSER_CMDS);
4120 		(void) fprintf(stderr, PARSER_EXAMPLE);
4121 	}
4122 	exit(E_USAGE);
4123 }
4124 
4125 int
4126 yywrap()
4127 {
4128 	char buf[1024];
4129 
4130 	if (parentscript) {
4131 		yyin = parentscript;
4132 		yy_switchfilescript(yyin);
4133 		parentscript = NULL;
4134 		return (0);
4135 	} else
4136 		return (1);
4137 }
4138