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