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