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