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