xref: /onnv-gate/usr/src/cmd/filebench/common/parser_gram.y (revision 5673:043503f0cca3)
1 
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 %{
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 %}
30 
31 %{
32 
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <locale.h>
40 #include <sys/utsname.h>
41 #ifdef HAVE_STDINT_H
42 #include <stdint.h>
43 #endif
44 #include <fcntl.h>
45 #include <sys/mman.h>
46 #include <sys/wait.h>
47 #ifdef HAVE_LIBTECLA
48 #include <libtecla.h>
49 #endif
50 #include "parsertypes.h"
51 #include "filebench.h"
52 #include "utils.h"
53 #include "stats.h"
54 #include "vars.h"
55 #include "eventgen.h"
56 #ifdef HAVE_LIBTECLA
57 #include "auto_comp.h"
58 #endif
59 
60 int dofile = FS_FALSE;
61 static const char cmdname[] = "filebench";
62 static const char cmd_options[] = "pa:f:hi:s:m:";
63 static void usage(int);
64 
65 static cmd_t *cmd = NULL;		/* Command being processed */
66 #ifdef HAVE_LIBTECLA
67 static GetLine *gl;			/* GetLine resource object */
68 #endif
69 
70 char *execname;
71 char *fscriptname;
72 int noproc = 0;
73 var_t *var_list = NULL;
74 pidlist_t *pidlist = NULL;
75 char *cwd = NULL;
76 FILE *parentscript = NULL;
77 
78 static int filecreate_done = 0;
79 
80 /* yacc externals */
81 extern FILE *yyin;
82 extern int yydebug;
83 extern void yyerror(char *s);
84 
85 /* utilities */
86 static void terminate(void);
87 static cmd_t *alloc_cmd(void);
88 static attr_t *alloc_attr(void);
89 static attr_t *get_attr(cmd_t *cmd, int64_t name);
90 static attr_t *get_attr_integer(cmd_t *cmd, int64_t name);
91 static attr_t *get_attr_bool(cmd_t *cmd, int64_t name);
92 static var_t *alloc_var(void);
93 static var_t *get_var(cmd_t *cmd, int64_t name);
94 static list_t *alloc_list();
95 
96 /* Info Commands */
97 static void parser_list(cmd_t *);
98 
99 /* Define Commands */
100 static void parser_proc_define(cmd_t *);
101 static void parser_thread_define(cmd_t *, procflow_t *, int instances);
102 static void parser_flowop_define(cmd_t *, threadflow_t *);
103 static void parser_file_define(cmd_t *);
104 static void parser_fileset_define(cmd_t *);
105 
106 /* Create Commands */
107 static void parser_proc_create(cmd_t *);
108 static void parser_thread_create(cmd_t *);
109 static void parser_flowop_create(cmd_t *);
110 static void parser_fileset_create(cmd_t *);
111 
112 /* Shutdown Commands */
113 static void parser_proc_shutdown(cmd_t *);
114 static void parser_filebench_shutdown(cmd_t *cmd);
115 
116 /* Other Commands */
117 static void parser_foreach_integer(cmd_t *cmd);
118 static void parser_foreach_string(cmd_t *cmd);
119 static void parser_sleep(cmd_t *cmd);
120 static void parser_sleep_variable(cmd_t *cmd);
121 static void parser_log(cmd_t *cmd);
122 static void parser_statscmd(cmd_t *cmd);
123 static void parser_statsdump(cmd_t *cmd);
124 static void parser_statsxmldump(cmd_t *cmd);
125 static void parser_echo(cmd_t *cmd);
126 static void parser_usage(cmd_t *cmd);
127 static void parser_vars(cmd_t *cmd);
128 static void parser_printvars(cmd_t *cmd);
129 static void parser_system(cmd_t *cmd);
130 static void parser_statssnap(cmd_t *cmd);
131 static void parser_directory(cmd_t *cmd);
132 static void parser_eventgen(cmd_t *cmd);
133 static void parser_run(cmd_t *cmd);
134 static void parser_run_variable(cmd_t *cmd);
135 static void parser_help(cmd_t *cmd);
136 static void arg_parse(const char *command);
137 static void parser_abort(int arg);
138 
139 %}
140 
141 %union {
142 	int64_t	 ival;
143 	uchar_t  bval;
144 	char *	 sval;
145 	fs_u	 val;
146 	cmd_t	 *cmd;
147 	attr_t	 *attr;
148 	list_t	 *list;
149 }
150 
151 %start commands
152 
153 %token FSC_LIST FSC_DEFINE FSC_EXEC FSC_QUIT FSC_DEBUG FSC_CREATE
154 %token FSC_SLEEP FSC_STATS FSC_FOREACH FSC_SET FSC_SHUTDOWN FSC_LOG
155 %token FSC_SYSTEM FSC_FLOWOP FSC_EVENTGEN FSC_ECHO FSC_LOAD FSC_RUN
156 %token FSC_USAGE FSC_HELP FSC_VARS
157 %token FSV_STRING FSV_VAL_INT FSV_VAL_BOOLEAN FSV_VARIABLE FSV_WHITESTRING
158 %token FST_INT FST_BOOLEAN
159 %token FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSE_ALL FSE_SNAP FSE_DUMP
160 %token FSE_DIRECTORY FSE_COMMAND FSE_FILESET FSE_XMLDUMP
161 %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_ASSIGN FSK_IN FSK_QUOTE
162 %token FSK_DIRSEPLST
163 %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE
164 %token FSA_PROCESS FSA_MEMSIZE FSA_RATE FSA_CACHED
165 %token FSA_IOSIZE FSA_FILE FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES
166 %token FSA_DSYNC FSA_TARGET FSA_ITERS FSA_NICE FSA_VALUE FSA_BLOCKING
167 %token FSA_HIGHWATER FSA_DIRECTIO FSA_DIRWIDTH FSA_FD FSA_SRCFD FSA_ROTATEFD
168 %token FSA_NAMELENGTH FSA_FILESIZE FSA_ENTRIES FSA_FILESIZEGAMMA
169 %token FSA_DIRGAMMA FSA_USEISM
170 
171 %type <ival> FSV_VAL_INT
172 %type <bval> FSV_VAL_BOOLEAN
173 %type <sval> FSV_STRING
174 %type <sval> FSV_WHITESTRING
175 %type <sval> FSV_VARIABLE
176 %type <sval> FSK_ASSIGN
177 
178 %type <ival> FSC_LIST FSC_DEFINE FSC_SET FSC_LOAD FSC_RUN
179 %type <ival> FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSC_HELP
180 
181 %type <sval> name
182 %type <ival> entity
183 %type <val>  value
184 
185 %type <cmd> command inner_commands load_command run_command
186 %type <cmd> list_command define_command debug_command create_command
187 %type <cmd> sleep_command stats_command set_command shutdown_command
188 %type <cmd> foreach_command log_command system_command flowop_command
189 %type <cmd> eventgen_command quit_command flowop_list thread_list
190 %type <cmd> thread echo_command usage_command help_command vars_command
191 
192 %type <attr> attr_op attr_ops
193 %type <attr> attr_value
194 %type <list> integer_seplist string_seplist string_list var_string_list var_string
195 %type <list> whitevar_string whitevar_string_list
196 %type <ival> attrs_define_file attrs_define_thread attrs_flowop attrs_define_fileset
197 %type <ival> attrs_define_proc attrs_eventgen
198 %type <ival> attr_name
199 
200 %%
201 
202 commands: commands command
203 {
204 	list_t *list = NULL;
205 	list_t *list_end = NULL;
206 
207 	if ($2->cmd != NULL)
208 		$2->cmd($2);
209 
210 	free($2);
211 }
212 | commands error
213 {
214 	if (dofile)
215 		YYABORT;
216 }
217 |;
218 
219 inner_commands: command
220 {
221 	filebench_log(LOG_DEBUG_IMPL, "inner_command %zx", $1);
222 	$$ = $1;
223 }
224 | inner_commands command
225 {
226 	cmd_t *list = NULL;
227 	cmd_t *list_end = NULL;
228 
229 	/* Find end of list */
230 	for (list = $1; list != NULL;
231 	    list = list->cmd_next)
232 		list_end = list;
233 
234 	list_end->cmd_next = $2;
235 
236 	filebench_log(LOG_DEBUG_IMPL,
237 	    "inner_commands adding cmd %zx to list %zx", $2, $1);
238 
239 	$$ = $1;
240 };
241 
242 command:
243   define_command
244 | debug_command
245 | eventgen_command
246 | create_command
247 | echo_command
248 | usage_command
249 | vars_command
250 | foreach_command
251 | help_command
252 | list_command
253 | load_command
254 | log_command
255 | run_command
256 | set_command
257 | shutdown_command
258 | sleep_command
259 | stats_command
260 | system_command
261 | quit_command;
262 
263 foreach_command: FSC_FOREACH
264 {
265 	if (($$ = alloc_cmd()) == NULL)
266 		YYERROR;
267 	filebench_log(LOG_DEBUG_IMPL, "foreach_command %zx", $$);
268 }
269 | foreach_command FSV_VARIABLE FSK_IN integer_seplist FSK_OPENLST inner_commands FSK_CLOSELST
270 {
271 	cmd_t *cmd, *inner_cmd;
272 	list_t *list;
273 
274 	$$ = $1;
275 	$$->cmd_list = $6;
276 	$$->cmd_tgt1 = $2;
277 	$$->cmd_param_list = $4;
278 	$$->cmd = parser_foreach_integer;
279 
280 	for (list = $$->cmd_param_list; list != NULL;
281 	    list = list->list_next) {
282 		for (inner_cmd = $$->cmd_list;
283 		    inner_cmd != NULL;
284 		    inner_cmd = inner_cmd->cmd_next) {
285 			filebench_log(LOG_DEBUG_IMPL,
286 			    "packing foreach: %zx %s=%lld, cmd %zx",
287 			    $$,
288 			    $$->cmd_tgt1,
289 			    *list->list_integer, inner_cmd);
290 		}
291 	}
292 }| foreach_command FSV_VARIABLE FSK_IN string_seplist FSK_OPENLST inner_commands FSK_CLOSELST
293 {
294 	cmd_t *cmd, *inner_cmd;
295 	list_t *list;
296 
297 	$$ = $1;
298 	$$->cmd_list = $6;
299 	$$->cmd_tgt1 = $2;
300 	$$->cmd_param_list = $4;
301 	$$->cmd = parser_foreach_string;
302 
303 	for (list = $$->cmd_param_list; list != NULL;
304 	    list = list->list_next) {
305 		for (inner_cmd = $$->cmd_list;
306 		    inner_cmd != NULL;
307 		    inner_cmd = inner_cmd->cmd_next) {
308 			filebench_log(LOG_DEBUG_IMPL,
309 			    "packing foreach: %zx %s=%s, cmd %zx",
310 			    $$,
311 			    $$->cmd_tgt1,
312 			    *list->list_string, inner_cmd);
313 		}
314 	}
315 };
316 
317 integer_seplist: FSV_VAL_INT
318 {
319 	if (($$ = alloc_list()) == NULL)
320 		YYERROR;
321 
322 	$$->list_integer = integer_alloc($1);
323 }
324 | integer_seplist FSK_SEPLST FSV_VAL_INT
325 {
326 	list_t *list = NULL;
327 	list_t *list_end = NULL;
328 
329 	if (($$ = alloc_list()) == NULL)
330 		YYERROR;
331 
332 	$$->list_integer = integer_alloc($3);
333 
334 	/* Find end of list */
335 	for (list = $1; list != NULL;
336 	    list = list->list_next)
337 		list_end = list;
338 	list_end->list_next = $$;
339 	$$ = $1;
340 };
341 
342 string_seplist: FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
343 {
344 	if (($$ = alloc_list()) == NULL)
345 		YYERROR;
346 
347 	$$->list_string = string_alloc($2);
348 }
349 | string_seplist FSK_SEPLST FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
350 {
351 	list_t *list = NULL;
352 	list_t *list_end = NULL;
353 
354 	if (($$ = alloc_list()) == NULL)
355 			YYERROR;
356 
357 	$$->list_string = string_alloc($4);
358 
359 	/* Find end of list */
360 	for (list = $1; list != NULL;
361 	    list = list->list_next)
362 		list_end = list;
363 	list_end->list_next = $$;
364 	$$ = $1;
365 };
366 
367 eventgen_command: FSC_EVENTGEN
368 {
369 	if (($$ = alloc_cmd()) == NULL)
370 		YYERROR;
371 	$$->cmd = &parser_eventgen;
372 }
373 | eventgen_command attr_ops
374 {
375 	$1->cmd_attr_list = $2;
376 };
377 
378 system_command: FSC_SYSTEM whitevar_string_list
379 {
380 	if (($$ = alloc_cmd()) == NULL)
381 		YYERROR;
382 
383 	$$->cmd_param_list = $2;
384 	$$->cmd = parser_system;
385 };
386 
387 echo_command: FSC_ECHO whitevar_string_list
388 {
389 	if (($$ = alloc_cmd()) == NULL)
390 		YYERROR;
391 
392 	$$->cmd_param_list = $2;
393 	$$->cmd = parser_echo;
394 };
395 
396 usage_command: FSC_USAGE whitevar_string_list
397 {
398 	if (($$ = alloc_cmd()) == NULL)
399 		YYERROR;
400 
401 	$$->cmd_param_list = $2;
402 	$$->cmd = parser_usage;
403 };
404 
405 vars_command: FSC_VARS
406 {
407 	if (($$ = alloc_cmd()) == NULL)
408 		YYERROR;
409 
410 	$$->cmd = parser_printvars;
411 };
412 
413 string_list: FSV_VARIABLE
414 {
415 	if (($$ = alloc_list()) == NULL)
416 			YYERROR;
417 
418 	$$->list_string = string_alloc($1);
419 }
420 | string_list FSK_SEPLST FSV_VARIABLE
421 {
422 	list_t *list = NULL;
423 	list_t *list_end = NULL;
424 
425 	if (($$ = alloc_list()) == NULL)
426 		YYERROR;
427 
428 	$$->list_string = string_alloc($3);
429 
430 	/* Find end of list */
431 	for (list = $1; list != NULL;
432 	    list = list->list_next)
433 		list_end = list;
434 	list_end->list_next = $$;
435 	$$ = $1;
436 };
437 
438 var_string: FSV_VARIABLE
439 {
440 	if (($$ = alloc_list()) == NULL)
441 			YYERROR;
442 
443 	$$->list_string = string_alloc($1);
444 }
445 | FSV_STRING
446 {
447 	if (($$ = alloc_list()) == NULL)
448 			YYERROR;
449 
450 	$$->list_string = string_alloc($1);
451 };
452 
453 var_string_list: var_string
454 {
455 	$$ = $1;
456 }| var_string FSV_STRING
457 {
458 	list_t *list = NULL;
459 	list_t *list_end = NULL;
460 
461 	/* Add string */
462 	if (($$ = alloc_list()) == NULL)
463 		YYERROR;
464 
465 	$$->list_string = string_alloc($2);
466 
467 	/* Find end of list */
468 	for (list = $1; list != NULL;
469 	    list = list->list_next)
470 		list_end = list;
471 	list_end->list_next = $$;
472 	$$ = $1;
473 
474 }| var_string FSV_VARIABLE
475 {
476 	list_t *list = NULL;
477 	list_t *list_end = NULL;
478 
479 	/* Add variable */
480 	if (($$ = alloc_list()) == NULL)
481 		YYERROR;
482 
483 	$$->list_string = string_alloc($2);
484 
485 	/* Find end of list */
486 	for (list = $1; list != NULL;
487 	    list = list->list_next)
488 		list_end = list;
489 	list_end->list_next = $$;
490 	$$ = $1;
491 } |var_string_list FSV_STRING
492 {
493 	list_t *list = NULL;
494 	list_t *list_end = NULL;
495 
496 	/* Add string */
497 	if (($$ = alloc_list()) == NULL)
498 		YYERROR;
499 
500 	$$->list_string = string_alloc($2);
501 
502 	/* Find end of list */
503 	for (list = $1; list != NULL;
504 	    list = list->list_next)
505 		list_end = list;
506 	list_end->list_next = $$;
507 	$$ = $1;
508 
509 }| var_string_list FSV_VARIABLE
510 {
511 	list_t *list = NULL;
512 	list_t *list_end = NULL;
513 
514 	/* Add variable */
515 	if (($$ = alloc_list()) == NULL)
516 		YYERROR;
517 
518 	$$->list_string = string_alloc($2);
519 
520 	/* Find end of list */
521 	for (list = $1; list != NULL;
522 	    list = list->list_next)
523 		list_end = list;
524 	list_end->list_next = $$;
525 	$$ = $1;
526 };
527 
528 whitevar_string: FSK_QUOTE FSV_VARIABLE
529 {
530 	if (($$ = alloc_list()) == NULL)
531 			YYERROR;
532 
533 	$$->list_string = string_alloc($2);
534 }
535 | FSK_QUOTE FSV_WHITESTRING
536 {
537 	if (($$ = alloc_list()) == NULL)
538 			YYERROR;
539 
540 	$$->list_string = string_alloc($2);
541 };
542 
543 whitevar_string_list: whitevar_string FSV_WHITESTRING
544 {
545 	list_t *list = NULL;
546 	list_t *list_end = NULL;
547 
548 	/* Add string */
549 	if (($$ = alloc_list()) == NULL)
550 		YYERROR;
551 
552 	$$->list_string = string_alloc($2);
553 
554 	/* Find end of list */
555 	for (list = $1; list != NULL;
556 	    list = list->list_next)
557 		list_end = list;
558 	list_end->list_next = $$;
559 	$$ = $1;
560 
561 }| whitevar_string FSV_VARIABLE
562 {
563 	list_t *list = NULL;
564 	list_t *list_end = NULL;
565 
566 	/* Add variable */
567 	if (($$ = alloc_list()) == NULL)
568 		YYERROR;
569 
570 	$$->list_string = string_alloc($2);
571 
572 	/* Find end of list */
573 	for (list = $1; list != NULL;
574 	    list = list->list_next)
575 		list_end = list;
576 	list_end->list_next = $$;
577 	$$ = $1;
578 } |whitevar_string_list FSV_WHITESTRING
579 {
580 	list_t *list = NULL;
581 	list_t *list_end = NULL;
582 
583 	/* Add string */
584 	if (($$ = alloc_list()) == NULL)
585 		YYERROR;
586 
587 	$$->list_string = string_alloc($2);
588 
589 	/* Find end of list */
590 	for (list = $1; list != NULL;
591 	    list = list->list_next)
592 		list_end = list;
593 	list_end->list_next = $$;
594 	$$ = $1;
595 
596 }| whitevar_string_list FSV_VARIABLE
597 {
598 	list_t *list = NULL;
599 	list_t *list_end = NULL;
600 
601 	/* Add variable */
602 	if (($$ = alloc_list()) == NULL)
603 		YYERROR;
604 
605 	$$->list_string = string_alloc($2);
606 
607 	/* Find end of list */
608 	for (list = $1; list != NULL;
609 	    list = list->list_next)
610 		list_end = list;
611 	list_end->list_next = $$;
612 	$$ = $1;
613 }| whitevar_string_list FSK_QUOTE
614 {
615 	$$ = $1;
616 }| whitevar_string FSK_QUOTE
617 {
618 	$$ = $1;
619 };
620 
621 list_command: FSC_LIST
622 {
623 	if (($$ = alloc_cmd()) == NULL)
624 		YYERROR;
625 	$$->cmd = &parser_list;
626 };
627 
628 log_command: FSC_LOG whitevar_string_list
629 {
630 	if (($$ = alloc_cmd()) == NULL)
631 		YYERROR;
632 	$$->cmd = &parser_log;
633 	$$->cmd_param_list = $2;
634 };
635 
636 debug_command: FSC_DEBUG FSV_VAL_INT
637 {
638 	if (($$ = alloc_cmd()) == NULL)
639 		YYERROR;
640 	$$->cmd = NULL;
641 	filebench_shm->debug_level = $2;
642 	if (filebench_shm->debug_level > 9)
643 		yydebug = 1;
644 };
645 
646 set_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT
647 {
648 	if (($$ = alloc_cmd()) == NULL)
649 		YYERROR;
650 	var_assign_integer($2, $4);
651 	if (parentscript) {
652 		$$->cmd_tgt1 = $2;
653 		parser_vars($$);
654 	}
655 	$$->cmd = NULL;
656 }
657 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
658 {
659 	if (($$ = alloc_cmd()) == NULL)
660 		YYERROR;
661 	var_assign_string($2, $5);
662 	if (parentscript) {
663 		$$->cmd_tgt1 = $2;
664 		parser_vars($$);
665 	}
666 	$$->cmd = NULL;
667 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_STRING
668 {
669 	if (($$ = alloc_cmd()) == NULL)
670 		YYERROR;
671 	var_assign_string($2, $4);
672 	if (parentscript) {
673 		$$->cmd_tgt1 = $2;
674 		parser_vars($$);
675 	}
676 	$$->cmd = NULL;
677 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
678 {
679 	if (($$ = alloc_cmd()) == NULL)
680 		YYERROR;
681 	var_assign_var($2, $4);
682 	if (parentscript) {
683 		$$->cmd_tgt1 = $2;
684 		parser_vars($$);
685 	}
686 	$$->cmd = NULL;
687 };
688 
689 stats_command: FSC_STATS FSE_SNAP
690 {
691 	if (($$ = alloc_cmd()) == NULL)
692 		YYERROR;
693 	$$->cmd = (void (*)(struct cmd *))&parser_statssnap;
694 	break;
695 
696 }
697 | FSC_STATS FSE_CLEAR
698 {
699 	if (($$ = alloc_cmd()) == NULL)
700 		YYERROR;
701 	$$->cmd = (void (*)(struct cmd *))&stats_clear;
702 
703 }
704 | FSC_STATS FSE_DIRECTORY var_string_list
705 {
706 	if (($$ = alloc_cmd()) == NULL)
707 		YYERROR;
708 	$$->cmd_param_list = $3;
709 	$$->cmd = (void (*)(struct cmd *))&parser_directory;
710 
711 }
712 | FSC_STATS FSE_COMMAND whitevar_string_list
713 {
714 	if (($$ = alloc_cmd()) == NULL)
715 		YYERROR;
716 
717 	$$->cmd_param_list = $3;
718 	$$->cmd = parser_statscmd;
719 
720 }| FSC_STATS FSE_DUMP whitevar_string_list
721 {
722 	if (($$ = alloc_cmd()) == NULL)
723 		YYERROR;
724 
725 	$$->cmd_param_list = $3;
726 	$$->cmd = parser_statsdump;
727 }| FSC_STATS FSE_XMLDUMP whitevar_string_list
728 {
729 	if (($$ = alloc_cmd()) == NULL)
730 		YYERROR;
731 
732 	$$->cmd_param_list = $3;
733 	$$->cmd = parser_statsxmldump;
734 };
735 
736 quit_command: FSC_QUIT
737 {
738 	if (($$ = alloc_cmd()) == NULL)
739 		YYERROR;
740 	$$->cmd = parser_filebench_shutdown;
741 };
742 
743 flowop_list: flowop_command
744 {
745 	$$ = $1;
746 }| flowop_list flowop_command
747 {
748 	cmd_t *list = NULL;
749 	cmd_t *list_end = NULL;
750 
751 	/* Find end of list */
752 	for (list = $1; list != NULL;
753 	    list = list->cmd_next)
754 		list_end = list;
755 
756 	list_end->cmd_next = $2;
757 
758 	filebench_log(LOG_DEBUG_IMPL,
759 	    "flowop_list adding cmd %zx to list %zx", $2, $1);
760 
761 	$$ = $1;
762 };
763 
764 thread: FSE_THREAD attr_ops FSK_OPENLST flowop_list FSK_CLOSELST
765 {
766 	/*
767 	 * Allocate a cmd node per thread, with a
768 	 * list of flowops attached to the cmd_list
769 	 */
770 	if (($$ = alloc_cmd()) == NULL)
771 		YYERROR;
772 	$$->cmd_list = $4;
773 	$$->cmd_attr_list = $2;
774 };
775 
776 thread_list: thread
777 {
778 	$$ = $1;
779 }| thread_list thread
780 {
781 	cmd_t *list = NULL;
782 	cmd_t *list_end = NULL;
783 
784 	/* Find end of list */
785 	for (list = $1; list != NULL;
786 	    list = list->cmd_next)
787 		list_end = list;
788 
789 	list_end->cmd_next = $2;
790 
791 	filebench_log(LOG_DEBUG_IMPL,
792 	    "thread_list adding cmd %zx to list %zx", $2, $1);
793 
794 	$$ = $1;
795 };
796 
797 define_command: FSC_DEFINE FSE_PROC attr_ops FSK_OPENLST thread_list FSK_CLOSELST
798 {
799 	if (($$ = alloc_cmd()) == NULL)
800 		YYERROR;
801 	$$->cmd = &parser_proc_define;
802 	$$->cmd_list = $5;
803 	$$->cmd_attr_list = $3;
804 
805 }| FSC_DEFINE FSE_FILE
806 {
807 	if (($$ = alloc_cmd()) == NULL)
808 		YYERROR;
809 	$$->cmd = &parser_file_define;
810 }| FSC_DEFINE FSE_FILESET
811 {
812 	if (($$ = alloc_cmd()) == NULL)
813 		YYERROR;
814 	$$->cmd = &parser_fileset_define;
815 }
816 | define_command attr_ops
817 {
818 	$1->cmd_attr_list = $2;
819 };
820 
821 create_command: FSC_CREATE entity
822 {
823 	if (($$ = alloc_cmd()) == NULL)
824 		YYERROR;
825 	switch ($2) {
826 	case FSE_PROC:
827 		$$->cmd = &parser_proc_create;
828 		break;
829 	case FSE_FILESET:
830 	case FSE_FILE:
831 		$$->cmd = &parser_fileset_create;
832 		break;
833 	default:
834 		filebench_log(LOG_ERROR, "unknown entity", $2);
835 		YYERROR;
836 	}
837 
838 };
839 
840 shutdown_command: FSC_SHUTDOWN entity
841 {
842 	if (($$ = alloc_cmd()) == NULL)
843 		YYERROR;
844 	switch ($2) {
845 	case FSE_PROC:
846 		$$->cmd = &parser_proc_shutdown;
847 		break;
848 	default:
849 		filebench_log(LOG_ERROR, "unknown entity", $2);
850 		YYERROR;
851 	}
852 
853 };
854 
855 sleep_command: FSC_SLEEP FSV_VAL_INT
856 {
857 	if (($$ = alloc_cmd()) == NULL)
858 		YYERROR;
859 	$$->cmd = parser_sleep;
860 	$$->cmd_qty = $2;
861 }
862 | FSC_SLEEP FSV_VARIABLE
863 {
864 	vinteger_t *integer;
865 
866 	if (($$ = alloc_cmd()) == NULL)
867 		YYERROR;
868 	$$->cmd = parser_sleep_variable;
869 	$$->cmd_tgt1 = fb_stralloc($2);
870 };
871 
872 run_command: FSC_RUN FSV_VAL_INT
873 {
874 	if (($$ = alloc_cmd()) == NULL)
875 		YYERROR;
876 	$$->cmd = parser_run;
877 	$$->cmd_qty = $2;
878 }
879 | FSC_RUN FSV_VARIABLE
880 {
881 	vinteger_t *integer;
882 
883 	if (($$ = alloc_cmd()) == NULL)
884 		YYERROR;
885 	$$->cmd = parser_run_variable;
886 	$$->cmd_tgt1 = fb_stralloc($2);
887 }
888 | FSC_RUN
889 {
890 	vinteger_t *integer;
891 
892 	if (($$ = alloc_cmd()) == NULL)
893 		YYERROR;
894 	$$->cmd = parser_run;
895 	$$->cmd_qty = 60UL;
896 };
897 
898 help_command: FSC_HELP
899 {
900 	if (($$ = alloc_cmd()) == NULL)
901 		YYERROR;
902 	$$->cmd = parser_help;
903 };
904 
905 flowop_command: FSC_FLOWOP name
906 {
907 	if (($$ = alloc_cmd()) == NULL)
908 		YYERROR;
909 	$$->cmd_name = fb_stralloc($2);
910 }
911 | flowop_command attr_ops
912 {
913 	$1->cmd_attr_list = $2;
914 };
915 
916 load_command: FSC_LOAD FSV_STRING
917 {
918 	FILE *newfile;
919 	char loadfile[128];
920 
921 	if (($$ = alloc_cmd()) == NULL)
922 		YYERROR;
923 
924 	(void) strcpy(loadfile, $2);
925 	(void) strcat(loadfile, ".f");
926 
927 	if ((newfile = fopen(loadfile, "r")) == NULL) {
928 		(void) strcpy(loadfile, FILEBENCHDIR);
929 		(void) strcat(loadfile, "/workloads/");
930 		(void) strcat(loadfile, $2);
931 		(void) strcat(loadfile, ".f");
932 		if ((newfile = fopen(loadfile, "r")) == NULL) {
933 			filebench_log(LOG_ERROR, "Cannot open %s", loadfile);
934 			YYERROR;
935 		}
936 	}
937 
938 	parentscript = yyin;
939 	yyin = newfile;
940 	yy_switchfileparent(yyin);
941 };
942 
943 entity: FSE_PROC {$$ = FSE_PROC;}
944 | FSE_THREAD {$$ = FSE_THREAD;}
945 | FSE_FILESET {$$ = FSE_FILESET;}
946 | FSE_FILE {$$ = FSE_FILE;};
947 
948 value: FSV_VAL_INT { $$.i = $1;}
949 | FSV_STRING { $$.s = $1;}
950 | FSV_VAL_BOOLEAN { $$.b = $1;};
951 
952 name: FSV_STRING;
953 
954 attr_ops: attr_op
955 {
956 	$$ = $1;
957 }
958 | attr_ops FSK_SEPLST attr_op
959 {
960 	attr_t *attr = NULL;
961 	attr_t *list_end = NULL;
962 
963 	for (attr = $1; attr != NULL;
964 	    attr = attr->attr_next)
965 		list_end = attr; /* Find end of list */
966 
967 	list_end->attr_next = $3;
968 
969 	$$ = $1;
970 };
971 
972 attr_op: attr_name FSK_ASSIGN attr_value
973 {
974 	$$ = $3;
975 	$$->attr_name = $1;
976 }
977 | attr_name
978 {
979 	if (($$ = alloc_attr()) == NULL)
980 		YYERROR;
981 	$$->attr_name = $1;
982 }
983 
984 attr_name: attrs_define_file
985 |attrs_define_fileset
986 |attrs_define_thread
987 |attrs_define_proc
988 |attrs_flowop
989 |attrs_eventgen;
990 
991 attrs_define_proc:
992 FSA_NICE { $$ = FSA_NICE;}
993 |FSA_INSTANCES { $$ = FSA_INSTANCES;};
994 
995 attrs_define_file:
996 FSA_SIZE { $$ = FSA_SIZE;}
997 | FSA_PATH { $$ = FSA_PATH;}
998 | FSA_REUSE { $$ = FSA_REUSE;}
999 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1000 | FSA_PARALLOC { $$ = FSA_PARALLOC;};
1001 
1002 attrs_define_fileset:
1003 FSA_SIZE { $$ = FSA_SIZE;}
1004 | FSA_PATH { $$ = FSA_PATH;}
1005 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;}
1006 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1007 | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;}
1008 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;}
1009 | FSA_CACHED { $$ = FSA_CACHED;}
1010 | FSA_ENTRIES { $$ = FSA_ENTRIES;};
1011 
1012 attrs_define_thread:
1013 FSA_PROCESS { $$ = FSA_PROCESS;}
1014 |FSA_MEMSIZE { $$ = FSA_MEMSIZE;}
1015 |FSA_USEISM { $$ = FSA_USEISM;}
1016 |FSA_INSTANCES { $$ = FSA_INSTANCES;};
1017 
1018 attrs_flowop:
1019 FSA_WSS { $$ = FSA_WSS;}
1020 |FSA_FILE { $$ = FSA_FILE;}
1021 |FSA_NAME { $$ = FSA_NAME;}
1022 |FSA_RANDOM { $$ = FSA_RANDOM;}
1023 |FSA_FD { $$ = FSA_FD;}
1024 |FSA_SRCFD { $$ = FSA_SRCFD;}
1025 |FSA_ROTATEFD { $$ = FSA_ROTATEFD;}
1026 |FSA_DSYNC { $$ = FSA_DSYNC;}
1027 |FSA_DIRECTIO { $$ = FSA_DIRECTIO;}
1028 |FSA_TARGET { $$ = FSA_TARGET;}
1029 |FSA_ITERS { $$ = FSA_ITERS;}
1030 |FSA_VALUE { $$ = FSA_VALUE;}
1031 |FSA_BLOCKING { $$ = FSA_BLOCKING;}
1032 |FSA_HIGHWATER { $$ = FSA_HIGHWATER;}
1033 |FSA_IOSIZE { $$ = FSA_IOSIZE;};
1034 
1035 attrs_eventgen:
1036 FSA_RATE { $$ = FSA_RATE;};
1037 
1038 attr_value: var_string_list {
1039 	if (($$ = alloc_attr()) == NULL)
1040 		YYERROR;
1041 	$$->attr_param_list = $1;
1042 } | FSV_STRING
1043 {
1044 	if (($$ = alloc_attr()) == NULL)
1045 		YYERROR;
1046 	$$->attr_string = string_alloc($1);
1047 } | FSV_VAL_INT {
1048 	if (($$ = alloc_attr()) == NULL)
1049 		YYERROR;
1050 	$$->attr_integer = integer_alloc($1);
1051 } | FSV_VARIABLE {
1052 	if (($$ = alloc_attr()) == NULL)
1053 		YYERROR;
1054 	$$->attr_integer = var_ref_integer($1);
1055 	$$->attr_string = var_ref_string($1);
1056 };
1057 
1058 %%
1059 
1060 /*
1061  *  The following 'c' routines implement the various commands defined in the
1062  * above yacc parser code. The yacc portion checks the syntax of the commands
1063  * found in a workload file, or typed on interactive command lines, parsing
1064  * the commands' parameters into lists. The lists are then passed in a cmd_t
1065  * struct for each command to its related routine in the following section
1066  * for actual execution. This section also includes a few utility routines
1067  * and the main entry point for the program.
1068  */
1069 
1070 /*
1071  * Entry point for filebench. Processes command line arguements. The -f
1072  * option will read in a workload file (the full name and extension must
1073  * must be given). The -a, -s, -m and -i options are used by worker process
1074  * to receive their name, the base address of shared memory, its path, and
1075  * the process' instance number, respectively. This information is supplied
1076  * by the master process when it execs worker processes under the process
1077  * model of execution. If the worker process arguments are passed then main
1078  * will call the procflow_exec routine which creates worker threadflows and
1079  * flowops and executes the procflow's portion of the workload model until
1080  * completion. If worker process arguments are not passed to the process,
1081  * then it becomes the master process for a filebench run. It initializes
1082  * the various filebench components and either executes the supplied workload
1083  * file, or enters interactive mode.
1084  */
1085 
1086 int
1087 main(int argc, char *argv[])
1088 {
1089 	int opt;
1090 	int docmd = FS_FALSE;
1091 	int instance;
1092 	char procname[128];
1093 	caddr_t shmaddr;
1094 	char dir[MAXPATHLEN];
1095 #ifdef HAVE_SETRLIMIT
1096 	struct rlimit rlp;
1097 #endif
1098 #ifdef HAVE_LIBTECLA
1099 	char *line;
1100 #else
1101 	char line[1024];
1102 #endif
1103 	char shmpathtmp[1024];
1104 
1105 #ifdef HAVE_SETRLIMIT
1106 	/* Set resource limits */
1107 	(void) getrlimit(RLIMIT_NOFILE, &rlp);
1108 	rlp.rlim_cur = rlp.rlim_max;
1109 	setrlimit(RLIMIT_NOFILE, &rlp);
1110 #endif
1111 
1112 	yydebug = 0;
1113 	execname = argv[0];
1114 	*procname = 0;
1115 	cwd = getcwd(dir, MAXPATHLEN);
1116 
1117 	while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) {
1118 
1119 		switch (opt) {
1120 		case 'h':
1121 			usage(2);
1122 			break;
1123 
1124 		case 'p':
1125 			noproc = 1;
1126 			break;
1127 
1128 		case 'f':
1129 			if (optarg == NULL)
1130 				usage(1);
1131 			if ((yyin = fopen(optarg, "r")) == NULL) {
1132 				(void) fprintf(stderr,
1133 				    "Cannot open file %s", optarg);
1134 				exit(1);
1135 			}
1136 			dofile = FS_TRUE;
1137 			fscriptname = optarg;
1138 
1139 			break;
1140 
1141 		case 'a':
1142 			if (optarg == NULL)
1143 				usage(1);
1144 			sscanf(optarg, "%s", &procname[0]);
1145 			break;
1146 
1147 		case 's':
1148 			if (optarg == NULL)
1149 				usage(1);
1150 #if defined(_LP64) || (__WORDSIZE == 64)
1151 			sscanf(optarg, "%llx", &shmaddr);
1152 #else
1153 			sscanf(optarg, "%x", &shmaddr);
1154 #endif
1155 			break;
1156 
1157 		case 'm':
1158 			if (optarg == NULL)
1159 				usage(1);
1160 			sscanf(optarg, "%s", shmpathtmp);
1161 			shmpath = shmpathtmp;
1162 			break;
1163 
1164 		case 'i':
1165 			if (optarg == NULL)
1166 				usage(1);
1167 			sscanf(optarg, "%d", &instance);
1168 			break;
1169 
1170 		case '?':
1171 		default:
1172 			usage(1);
1173 			break;
1174 		}
1175 	}
1176 
1177 #ifdef USE_PROCESS_MODEL
1178 	if (!(*procname))
1179 #endif
1180 	printf("FileBench Version %s\n", FILEBENCH_VERSION);
1181 	filebench_init();
1182 
1183 #ifdef USE_PROCESS_MODEL
1184 	if (*procname) {
1185 		pid = getpid();
1186 
1187 		if (ipc_attach(shmaddr) < 0) {
1188 			filebench_log(LOG_ERROR, "Cannot attach shm for %s",
1189 			    procname);
1190 			exit(1);
1191 		}
1192 
1193 		if (procflow_exec(procname, instance) < 0) {
1194 			filebench_log(LOG_ERROR, "Cannot startup process %s",
1195 			    procname);
1196 			exit(1);
1197 		}
1198 		exit(1);
1199 	}
1200 #endif
1201 
1202 	pid = getpid();
1203 	ipc_init();
1204 
1205 	if (fscriptname)
1206 		(void) strcpy(filebench_shm->fscriptname, fscriptname);
1207 
1208 	flowop_init();
1209 	stats_init();
1210 	eventgen_init();
1211 
1212 	signal(SIGINT, parser_abort);
1213 
1214 	if (dofile)
1215 		yyparse();
1216 	else {
1217 #ifdef HAVE_LIBTECLA
1218 		if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) {
1219 			filebench_log(LOG_ERROR,
1220 			    "Failed to create GetLine object");
1221 			filebench_shutdown(1);
1222 		}
1223 
1224 		if (gl_customize_completion(gl, NULL, command_complete)) {
1225 			filebench_log(LOG_ERROR,
1226 			    "Failed to register auto-completion function");
1227 			filebench_shutdown(1);
1228 		}
1229 
1230 		while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) {
1231 			arg_parse(line);
1232 			yyparse();
1233 		}
1234 
1235 		del_GetLine(gl);
1236 #else
1237 		while (!feof(stdin)) {
1238 			printf(FILEBENCH_PROMPT);
1239 			fflush(stdout);
1240 			if (fgets(line, sizeof (line), stdin) == NULL) {
1241 				if (errno == EINTR)
1242 					continue;
1243 				else
1244 					break;
1245 			}
1246 			arg_parse(line);
1247 			yyparse();
1248 		}
1249 		printf("\n");
1250 #endif	/* HAVE_LIBTECLA */
1251 	}
1252 
1253 	parser_filebench_shutdown((cmd_t *)0);
1254 
1255 	return (0);
1256 }
1257 
1258 /*
1259  * arg_parse() puts the parser into command parsing mode. Create a tmpfile
1260  * and instruct the parser to read instructions from this location by setting
1261  * yyin to the value returned by tmpfile. Write the command into the file.
1262  * Then seek back to to the start of the file so that the parser can read
1263  * the instructions.
1264  */
1265 static void
1266 arg_parse(const char *command)
1267 {
1268 	if ((yyin = tmpfile()) == NULL)
1269 		filebench_log(LOG_FATAL,
1270 		    "Cannot create tmpfile: %s", strerror(errno));
1271 
1272 	if (fwrite(command, strlen(command), 1, yyin) != 1)
1273 		filebench_log(LOG_FATAL,
1274 		    "Cannot write tmpfile: %s", strerror(errno));
1275 
1276 	if (fseek(yyin, 0, SEEK_SET) != 0)
1277 		filebench_log(LOG_FATAL,
1278 		    "Cannot seek tmpfile: %s", strerror(errno));
1279 }
1280 
1281 /*
1282  * Converts a list of var_strings or ordinary strings to a single ordinary
1283  * string. It returns a pointer to the string (in malloc'd memory) if found,
1284  * or NULL otherwise.
1285  */
1286 char *
1287 parser_list2string(list_t *list)
1288 {
1289 	list_t *l;
1290 	char *string;
1291 	char *tmp;
1292 	vinteger_t *integer;
1293 
1294 	if ((string = malloc(MAXPATHLEN)) == NULL) {
1295 		filebench_log(LOG_ERROR, "Failed to allocate memory");
1296 		return (NULL);
1297 	}
1298 
1299 	*string = 0;
1300 
1301 
1302 	/* Format args */
1303 	for (l = list; l != NULL;
1304 	    l = l->list_next) {
1305 		filebench_log(LOG_DEBUG_SCRIPT,
1306 		    "converting string '%s'", *l->list_string);
1307 
1308 		if ((tmp = var_to_string(*l->list_string)) != NULL) {
1309 			(void) strcat(string, tmp);
1310 			free(tmp);
1311 		} else {
1312 			(void) strcat(string, *l->list_string);
1313 		}
1314 	}
1315 	return (string);
1316 }
1317 
1318 /*
1319  * If the list just contains a single string starting with '$', then find
1320  * or create the named var and return the var's var_string component.
1321  * Otherwise, convert the list to a string, and allocate a var_string
1322  * containing a copy of that string. On failure either returns NULL
1323  * or shuts down the run.
1324  */
1325 var_string_t
1326 parser_list2varstring(list_t *list)
1327 {
1328 	/* Special case - variable name */
1329 	if ((list->list_next == NULL) && (*(*list->list_string) == '$'))
1330 		return (var_ref_string(*list->list_string));
1331 
1332 	return (string_alloc(parser_list2string(list)));
1333 }
1334 
1335 /*
1336  * Looks for the var named in list_string of the first element of the
1337  * supplied list. If found, returns the var_integer portion of the var.
1338  * If the var is not found, cannot be allocated, the supplied list is
1339  * NULL, or the list_string filed is empty, returns NULL.
1340  */
1341 var_integer_t
1342 parser_list2integer(list_t *list)
1343 {
1344 	var_integer_t v;
1345 
1346 	if (list && (*(list->list_string) != NULL)) {
1347 		v = var_ref_integer(*(list->list_string));
1348 		return (v);
1349 	}
1350 
1351 	return (NULL);
1352 }
1353 
1354 /*
1355  * Sets the event generator rate from the attribute supplied with the
1356  * command. If the attribute doesn't exist the routine does nothing.
1357  */
1358 static void
1359 parser_eventgen(cmd_t *cmd)
1360 {
1361 	attr_t *attr;
1362 	vinteger_t rate;
1363 
1364 	/* Get the rate from attribute */
1365 	if (attr = get_attr_integer(cmd, FSA_RATE)) {
1366 		if (attr->attr_integer) {
1367 			filebench_log(LOG_VERBOSE,
1368 			    "Eventgen: %lld per second",
1369 			    *attr->attr_integer);
1370 			eventgen_setrate(*attr->attr_integer);
1371 		}
1372 	}
1373 
1374 }
1375 
1376 /*
1377  * Assigns the designated integer variable successive values from the
1378  * supplied comma seperated integer list. After each successive integer
1379  * assignment, it executes the bracket enclosed list of commands. For
1380  * example, repeated runs of a workload with increasing io sizes can
1381  * be done using the following command line:
1382  * 	foreach $iosize in 2k, 4k, 8k {run 60}
1383  */
1384 static void
1385 parser_foreach_integer(cmd_t *cmd)
1386 {
1387 	list_t *list = cmd->cmd_param_list;
1388 	cmd_t *inner_cmd;
1389 
1390 	for (; list != NULL; list = list->list_next) {
1391 		var_assign_integer(cmd->cmd_tgt1, *list->list_integer);
1392 		filebench_log(LOG_VERBOSE, "Iterating %s=%lld",
1393 		    cmd->cmd_tgt1,
1394 		    *list->list_integer);
1395 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1396 		    inner_cmd = inner_cmd->cmd_next) {
1397 			inner_cmd->cmd(inner_cmd);
1398 		}
1399 	}
1400 }
1401 
1402 /*
1403  * Similar to parser_foreach_integer(), except takes a list of strings after
1404  * the "in" token. For example, to run twice using a different directory,
1405  * perhaps using a different filesystem, the following command line
1406  * could be used:
1407  * 	foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60)
1408  */
1409 static void
1410 parser_foreach_string(cmd_t *cmd)
1411 {
1412 	list_t *list = cmd->cmd_param_list;
1413 	cmd_t *inner_cmd;
1414 
1415 	for (; list != NULL; list = list->list_next) {
1416 		var_assign_string(cmd->cmd_tgt1, *list->list_string);
1417 		filebench_log(LOG_VERBOSE, "Iterating %s=%s",
1418 		    cmd->cmd_tgt1,
1419 		    *list->list_string);
1420 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1421 		    inner_cmd = inner_cmd->cmd_next) {
1422 			inner_cmd->cmd(inner_cmd);
1423 		}
1424 	}
1425 }
1426 
1427 /*
1428  * Lists the fileset name, path name and average size for all defined
1429  * filesets.
1430  */
1431 static void
1432 parser_list(cmd_t *cmd)
1433 {
1434 	(void) fileset_iter(fileset_print);
1435 }
1436 
1437 /*
1438  * Calls procflow_define() to allocate "instances" number of  procflow(s)
1439  * (processes) with the supplied name. The default number of instances is
1440  * one. An optional priority level attribute can be supplied and is stored in
1441  * pf_nice. Finally the routine loops through the list of inner commands, if
1442  * any, which are defines for threadflows, and passes them one at a time to
1443  * parser_thread_define() to allocate threadflow entities for the process(es).
1444  */
1445 static void
1446 parser_proc_define(cmd_t *cmd)
1447 {
1448 	procflow_t *procflow, template;
1449 	char *name;
1450 	attr_t *attr;
1451 	var_integer_t instances = integer_alloc(1);
1452 	cmd_t *inner_cmd;
1453 
1454 	/* Get the name of the process */
1455 	if (attr = get_attr(cmd, FSA_NAME)) {
1456 		name = *attr->attr_string;
1457 	} else {
1458 		filebench_log(LOG_ERROR,
1459 		    "define proc: proc specifies no name");
1460 		filebench_shutdown(1);
1461 	}
1462 
1463 	/* Get the memory size from attribute */
1464 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
1465 		filebench_log(LOG_DEBUG_IMPL,
1466 		    "Setting instances = %lld",
1467 		    *attr->attr_integer);
1468 		instances = attr->attr_integer;
1469 	}
1470 
1471 	if ((procflow = procflow_define(name, NULL, instances)) == NULL) {
1472 		filebench_log(LOG_ERROR,
1473 		    "Failed to instantiate %d %s process(es)\n",
1474 		    instances, name);
1475 		filebench_shutdown(1);
1476 	}
1477 
1478 	/* Get the pri from attribute */
1479 	if (attr = get_attr_integer(cmd, FSA_NICE)) {
1480 		filebench_log(LOG_DEBUG_IMPL, "Setting pri = %lld",
1481 		    *attr->attr_integer);
1482 		procflow->pf_nice = attr->attr_integer;
1483 	} else
1484 		procflow->pf_nice = integer_alloc(0);
1485 
1486 
1487 	/* Create the list of threads for this process  */
1488 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1489 	    inner_cmd = inner_cmd->cmd_next) {
1490 		parser_thread_define(inner_cmd, procflow, *instances);
1491 	}
1492 }
1493 
1494 /*
1495  * Calls threadflow_define() to allocate "instances" number of  threadflow(s)
1496  * (threads) with the supplied name. The default number of instances is
1497  * one. Two other optional attributes may be supplied, one to set the memory
1498  * size, stored in tf_memsize, and to select the use of Interprocess Shared
1499  * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally
1500  * the routine loops through the list of inner commands, if any, which are
1501  * defines for flowops, and passes them one at a time to
1502  * parser_flowop_define() to allocate flowop entities for the threadflows.
1503  */
1504 static void
1505 parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances)
1506 {
1507 	threadflow_t *threadflow, template;
1508 	attr_t *attr;
1509 	var_integer_t instances = integer_alloc(1);
1510 	cmd_t *inner_cmd;
1511 	char *name;
1512 
1513 	memset(&template, 0, sizeof (threadflow_t));
1514 
1515 	/* Get the name of the thread */
1516 	if (attr = get_attr(cmd, FSA_NAME)) {
1517 		name = *attr->attr_string;
1518 	} else {
1519 		filebench_log(LOG_ERROR,
1520 		    "define thread: thread in process %s specifies no name",
1521 		    procflow->pf_name);
1522 		filebench_shutdown(1);
1523 	}
1524 
1525 	/* Get the number of instances from attribute */
1526 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
1527 		filebench_log(LOG_DEBUG_IMPL,
1528 		    "define thread: Setting instances = %lld",
1529 		    *attr->attr_integer);
1530 		instances = attr->attr_integer;
1531 	}
1532 
1533 	/* Get the memory size from attribute */
1534 	if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) {
1535 		filebench_log(LOG_DEBUG_IMPL,
1536 		    "define thread: Setting memsize = %lld",
1537 		    *attr->attr_integer);
1538 		template.tf_memsize = attr->attr_integer;
1539 	} else
1540 		template.tf_memsize = integer_alloc(0);
1541 
1542 	if ((threadflow = threadflow_define(procflow, name,
1543 	    &template, instances)) == NULL) {
1544 		filebench_log(LOG_ERROR,
1545 		    "define thread: Failed to instantiate thread\n");
1546 		filebench_shutdown(1);
1547 	}
1548 
1549 	/* Use ISM Memory? */
1550 	if (attr = get_attr(cmd, FSA_USEISM)) {
1551 		threadflow->tf_attrs |= THREADFLOW_USEISM;
1552 	}
1553 
1554 	/* Create the list of flowops */
1555 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1556 	    inner_cmd = inner_cmd->cmd_next) {
1557 		parser_flowop_define(inner_cmd, threadflow);
1558 	}
1559 }
1560 
1561 /*
1562  * Calls flowop_define() to allocate a flowop with the supplied name.
1563  * The allocated flowop inherits attributes from a base flowop of the
1564  * same type.  If the new flowop has a file or fileset attribute specified,
1565  * it must specify a defined fileobj or fileset or an error will be logged.
1566  * The new flowop may  also have the following attributes set by
1567  * the program:
1568  *  - file size (fo_iosize)
1569  *  - working set size (fo_wss)
1570  *  - do random io (fo_random)
1571  *  - do synchronous io (fo_dsync)
1572  *  - perform each operation multiple times before advancing (fo_iter)
1573  *  - target name (fo_targetname)
1574  *  - An integer value (fo_value)
1575  *  - a file descriptor (fo_fd)
1576  *  - specify to rotate file descriptors (fo_rotatefd)
1577  *  - a source fd (fo_srcfdnumber)
1578  *  - specify a blocking operation (fo_blocking)
1579  *  - specify a highwater mark (fo_highwater)
1580  *
1581  * After all the supplied attributes are stored in their respective locations
1582  * in the flowop object, the flowop's init function is called. No errors are
1583  * returned, but the filebench run will be terminated if the flowtype is not
1584  * specified, a name for the new flowop is not supplied, the flowop_define
1585  * call fails, or a file or fileset name is supplied but the corresponding
1586  * fileobj or fileset cannot be located.
1587  */
1588 static void
1589 parser_flowop_define(cmd_t *cmd, threadflow_t *thread)
1590 {
1591 	flowop_t *flowop, *flowop_type;
1592 	char *type = (char *)cmd->cmd_name;
1593 	char *name;
1594 	attr_t *attr;
1595 
1596 	/* Get the inherited flowop */
1597 	flowop_type = flowop_find(type);
1598 	if (flowop_type == NULL) {
1599 		filebench_log(LOG_ERROR,
1600 		    "define flowop: flowop type %s not found",
1601 		    type);
1602 		filebench_shutdown(1);
1603 	}
1604 
1605 	/* Get the name of the flowop */
1606 	if (attr = get_attr(cmd, FSA_NAME)) {
1607 		name = *attr->attr_string;
1608 	} else {
1609 		filebench_log(LOG_ERROR,
1610 		    "define flowop: flowop %s specifies no name",
1611 		    flowop_type->fo_name);
1612 		filebench_shutdown(1);
1613 	}
1614 
1615 	if ((flowop = flowop_define(thread, name,
1616 	    flowop_type, FLOW_MASTER, 0)) == NULL) {
1617 		filebench_log(LOG_ERROR,
1618 		    "define flowop: Failed to instantiate flowop %s\n",
1619 		    cmd->cmd_name);
1620 		filebench_shutdown(1);
1621 	}
1622 
1623 	/* Get the filename from attribute */
1624 	if (attr = get_attr(cmd, FSA_FILE)) {
1625 		flowop->fo_fileset = fileset_find(*attr->attr_string);
1626 
1627 		if ((flowop->fo_fileset == NULL)) {
1628 			filebench_log(LOG_ERROR,
1629 			    "define flowop: file %s not found",
1630 			    *attr->attr_string);
1631 			filebench_shutdown(1);
1632 		}
1633 	}
1634 
1635 	/* Get the iosize of the op */
1636 	if (attr = get_attr_integer(cmd, FSA_IOSIZE))
1637 		flowop->fo_iosize = attr->attr_integer;
1638 	else
1639 		flowop->fo_iosize = integer_alloc(0);
1640 
1641 	/* Get the working set size of the op */
1642 	if (attr = get_attr_integer(cmd, FSA_WSS))
1643 		flowop->fo_wss = attr->attr_integer;
1644 	else
1645 		flowop->fo_wss = integer_alloc(0);
1646 
1647 	/* Random I/O? */
1648 	if (attr = get_attr_bool(cmd, FSA_RANDOM))
1649 		flowop->fo_random = attr->attr_integer;
1650 	else
1651 		flowop->fo_random = integer_alloc(0);
1652 
1653 	/* Sync I/O? */
1654 	if (attr = get_attr_bool(cmd, FSA_DSYNC))
1655 		flowop->fo_dsync = attr->attr_integer;
1656 	else
1657 		flowop->fo_dsync = integer_alloc(0);
1658 
1659 	/* Iterations */
1660 	if (attr = get_attr_integer(cmd, FSA_ITERS))
1661 		flowop->fo_iters = attr->attr_integer;
1662 	else
1663 		flowop->fo_iters = integer_alloc(1);
1664 
1665 
1666 	/* Target, for wakeup etc */
1667 	if (attr = get_attr(cmd, FSA_TARGET))
1668 		(void) strcpy(flowop->fo_targetname, *attr->attr_string);
1669 
1670 	/* Value */
1671 	if (attr = get_attr_integer(cmd, FSA_VALUE))
1672 		flowop->fo_value = attr->attr_integer;
1673 	else
1674 		flowop->fo_value = integer_alloc(0);
1675 
1676 	/* FD */
1677 	if (attr = get_attr_integer(cmd, FSA_FD))
1678 		flowop->fo_fdnumber = *attr->attr_integer;
1679 
1680 	/* Rotatefd? */
1681 	if (attr = get_attr_bool(cmd, FSA_ROTATEFD))
1682 		flowop->fo_rotatefd = attr->attr_integer;
1683 	else
1684 		flowop->fo_rotatefd = integer_alloc(0);
1685 
1686 	/* SRC FD, for copies etc... */
1687 	if (attr = get_attr_integer(cmd, FSA_SRCFD))
1688 		flowop->fo_srcfdnumber = *attr->attr_integer;
1689 
1690 	/* Blocking operation? */
1691 	if (attr = get_attr_bool(cmd, FSA_BLOCKING))
1692 		flowop->fo_blocking = attr->attr_integer;
1693 	else
1694 		flowop->fo_blocking = integer_alloc(0);
1695 
1696 	/* Blocking operation? */
1697 	if (attr = get_attr_bool(cmd, FSA_DIRECTIO))
1698 		flowop->fo_directio = attr->attr_integer;
1699 	else
1700 		flowop->fo_directio = integer_alloc(0);
1701 
1702 	/* Highwater mark */
1703 	if (attr = get_attr_integer(cmd, FSA_HIGHWATER))
1704 		flowop->fo_highwater = attr->attr_integer;
1705 	else
1706 		flowop->fo_highwater = integer_alloc(1);
1707 }
1708 
1709 /*
1710  * Calls fileset_define() to allocate a fileset with the supplied name and
1711  * initializes the fileset's pathname attribute, and optionally the fs_cached,
1712  * fs_reuse, fs_prealloc and fs_size attributes.
1713  *
1714  */
1715 static fileset_t *
1716 parser_fileset_define_common(cmd_t *cmd)
1717 {
1718 	fileset_t *fileset;
1719 	char *name;
1720 	attr_t *attr;
1721 	var_string_t pathname;
1722 
1723 	/* Get the name of the file */
1724 	if (attr = get_attr(cmd, FSA_NAME)) {
1725 		name = *attr->attr_string;
1726 	} else {
1727 		filebench_log(LOG_ERROR,
1728 		    "define fileset: file or fileset specifies no name");
1729 		return (NULL);
1730 	}
1731 
1732 	if ((fileset = fileset_define(name)) == NULL) {
1733 		filebench_log(LOG_ERROR,
1734 		    "define file: failed to instantiate file %s\n",
1735 		    name);
1736 		return (NULL);
1737 	}
1738 
1739 	/* Get the pathname from attribute */
1740 	if ((attr = get_attr(cmd, FSA_PATH)) == NULL) {
1741 		filebench_log(LOG_ERROR, "define file: no pathname specified");
1742 		return (NULL);
1743 	}
1744 
1745 	/* Expand variables in pathname */
1746 	if ((pathname = parser_list2varstring(attr->attr_param_list))
1747 	    == NULL) {
1748 		filebench_log(LOG_ERROR, "Cannot interpret path");
1749 		return (NULL);
1750 	}
1751 
1752 	fileset->fs_path = pathname;
1753 
1754 	/* Should we prealloc in parallel? */
1755 	if (attr = get_attr_bool(cmd, FSA_PARALLOC)) {
1756 		fileset->fs_paralloc = attr->attr_integer;
1757 	} else
1758 		fileset->fs_paralloc = integer_alloc(0);
1759 
1760 	/* Should we reuse the existing file? */
1761 	if (attr = get_attr_bool(cmd, FSA_REUSE)) {
1762 		fileset->fs_reuse = attr->attr_integer;
1763 	} else
1764 		fileset->fs_reuse = integer_alloc(0);
1765 
1766 	/* Should we leave in cache? */
1767 	if (attr = get_attr_bool(cmd, FSA_CACHED)) {
1768 		fileset->fs_cached = attr->attr_integer;
1769 	} else
1770 		fileset->fs_cached = integer_alloc(0);
1771 
1772 	/* Get the mean or absolute size of the file */
1773 	if (attr = get_attr_integer(cmd, FSA_SIZE)) {
1774 		fileset->fs_size = attr->attr_integer;
1775 	} else
1776 		fileset->fs_size = integer_alloc(0);
1777 
1778 	return (fileset);
1779 }
1780 
1781 /*
1782  * Calls parser_fileset_define_common() to allocate a fileset with
1783  * one entry and optionally the fs_prealloc. Sets the fs_preallocpercent,
1784  * fs_entries, fs_dirwidth, fs_dirgamma, and fs_sizegamma attributes
1785  * to appropriate values for emulating the old "fileobj" entity
1786  */
1787 static void
1788 parser_file_define(cmd_t *cmd)
1789 {
1790 	fileset_t *fileset;
1791 	attr_t *attr;
1792 
1793 	if ((fileset = parser_fileset_define_common(cmd)) == NULL) {
1794 		filebench_log(LOG_ERROR,
1795 		    "define file: failed to instantiate file");
1796 		filebench_shutdown(1);
1797 		return;
1798 	}
1799 
1800 	/* fileset is emulating a single file */
1801 	fileset->fs_attrs = FILESET_IS_FILE;
1802 
1803 	/* Set the size of the fileset to 1 */
1804 	fileset->fs_entries = integer_alloc(1);
1805 
1806 	/* Set the mean dir width to more than 1 */
1807 	fileset->fs_dirwidth = integer_alloc(10);
1808 
1809 	/* Set the dir and size gammas to 0 */
1810 	fileset->fs_dirgamma = integer_alloc(0);
1811 	fileset->fs_sizegamma = integer_alloc(0);
1812 
1813 	/* if a raw device, all done */
1814 	if (fileset_checkraw(fileset)) {
1815 		filebench_log(LOG_VERBOSE, "File %s/%s is RAW device",
1816 		    *fileset->fs_path, fileset->fs_name);
1817 		return;
1818 	}
1819 
1820 	/* Does file need to be preallocated? */
1821 	if (attr = get_attr_bool(cmd, FSA_PREALLOC)) {
1822 		/* yes */
1823 		fileset->fs_prealloc = attr->attr_integer;
1824 		fileset->fs_preallocpercent = integer_alloc(100);
1825 	} else {
1826 		/* no */
1827 		fileset->fs_prealloc = integer_alloc(0);
1828 		fileset->fs_preallocpercent = integer_alloc(0);
1829 	}
1830 }
1831 
1832 /*
1833  * Calls parser_fileset_define_common() to allocate a fileset with the
1834  * supplied name and initializes the fileset's fs_preallocpercent,
1835  * fs_prealloc, fs_entries, fs_dirwidth, fs_dirgamma, and fs_sizegamma
1836  * attributes.
1837  */
1838 static void
1839 parser_fileset_define(cmd_t *cmd)
1840 {
1841 	fileset_t *fileset;
1842 	attr_t *attr;
1843 
1844 	if ((fileset = parser_fileset_define_common(cmd)) == NULL) {
1845 		filebench_log(LOG_ERROR,
1846 		    "define fileset: failed to instantiate fileset");
1847 		filebench_shutdown(1);
1848 		return;
1849 	}
1850 
1851 	/* if a raw device, Error */
1852 	if (fileset_checkraw(fileset)) {
1853 		filebench_log(LOG_ERROR,
1854 		    "Fileset %s/%s: Cannot create a fileset on a RAW device",
1855 		    *fileset->fs_path, fileset->fs_name);
1856 		filebench_shutdown(0);
1857 		return;
1858 	}
1859 
1860 	/* How much should we preallocate? */
1861 	if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) &&
1862 	    attr->attr_integer) {
1863 		fileset->fs_preallocpercent = attr->attr_integer;
1864 	} else if (attr && !attr->attr_integer) {
1865 		fileset->fs_preallocpercent = integer_alloc(100);
1866 	} else {
1867 		fileset->fs_preallocpercent = integer_alloc(0);
1868 	}
1869 
1870 	/* Should we preallocate? */
1871 	if (attr = get_attr_bool(cmd, FSA_PREALLOC)) {
1872 		fileset->fs_prealloc = attr->attr_integer;
1873 	} else
1874 		fileset->fs_prealloc = integer_alloc(0);
1875 
1876 	/* Get the number of files in the fileset */
1877 	if (attr = get_attr_integer(cmd, FSA_ENTRIES)) {
1878 		fileset->fs_entries = attr->attr_integer;
1879 	} else {
1880 		filebench_log(LOG_ERROR, "Fileset has zero entries");
1881 		fileset->fs_entries = integer_alloc(0);
1882 	}
1883 
1884 	/* Get the mean dir width of the fileset */
1885 	if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) {
1886 		fileset->fs_dirwidth = attr->attr_integer;
1887 	} else {
1888 		filebench_log(LOG_ERROR, "Fileset has zero directory width");
1889 		fileset->fs_dirwidth = integer_alloc(0);
1890 	}
1891 
1892 	/* Get the gamma value for dir width distributions */
1893 	if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) {
1894 		fileset->fs_dirgamma = attr->attr_integer;
1895 	} else
1896 		fileset->fs_dirgamma = integer_alloc(1500);
1897 
1898 	/* Get the gamma value for dir width distributions */
1899 	if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) {
1900 		fileset->fs_sizegamma = attr->attr_integer;
1901 	} else
1902 		fileset->fs_sizegamma = integer_alloc(1500);
1903 }
1904 
1905 /*
1906  * Creates and starts all defined procflow processes. The call to
1907  * procflow_init() results in creation of the requested number of
1908  * process instances for each previously defined procflow. The
1909  * child processes exec() a new instance of filebench, passing it
1910  * the instance number and address of the shared memory region.
1911  * The child processes will then create their threads and flowops.
1912  * The routine then unlocks the run_lock to allow all the processes'
1913  * threads to start and  waits for all of them to begin execution.
1914  * Finally, it records the start time and resets the event generation
1915  * system.
1916  */
1917 static void
1918 parser_proc_create(cmd_t *cmd)
1919 {
1920 	if (procflow_init() != 0) {
1921 		filebench_log(LOG_ERROR, "Failed to create processes\n");
1922 		filebench_shutdown(1);
1923 	}
1924 
1925 	/* Release the read lock, allowing threads to start */
1926 	(void) pthread_rwlock_unlock(&filebench_shm->run_lock);
1927 
1928 	/* Wait for all threads to start */
1929 	if (procflow_allstarted() != 0) {
1930 		filebench_log(LOG_ERROR, "Could not start run");
1931 		return;
1932 	}
1933 
1934 
1935 	if (filebench_shm->shm_required &&
1936 	    (ipc_ismcreate(filebench_shm->shm_required) < 0)) {
1937 		filebench_log(LOG_ERROR, "Could not allocate shared memory");
1938 		return;
1939 	}
1940 
1941 	filebench_shm->starttime = gethrtime();
1942 	eventgen_reset();
1943 }
1944 
1945 /*
1946  * Calls fileset_createset() to populate all files and filesets and
1947  * create all associated, initially existant,  files and subdirectories.
1948  * If errors are encountered, calls filebench_shutdown()
1949  * to exit filebench.
1950  */
1951 static void
1952 parser_fileset_create(cmd_t *cmd)
1953 {
1954 	if (!filecreate_done) {
1955 		filecreate_done = 1;
1956 		if (fileset_createset(NULL) != 0) {
1957 			filebench_log(LOG_ERROR, "Failed to create filesets");
1958 			filebench_shutdown(1);
1959 		}
1960 	} else {
1961 		filebench_log(LOG_INFO,
1962 		    "Attempting to create fileset more than once, ignoring");
1963 	}
1964 
1965 }
1966 
1967 /*
1968  * Shuts down all processes and their associated threads. When finished
1969  * it deletes interprocess shared memory and resets the event generator.
1970  * It does not exit the filebench program though.
1971  */
1972 static void
1973 parser_proc_shutdown(cmd_t *cmd)
1974 {
1975 	filebench_log(LOG_INFO, "Shutting down processes");
1976 	filecreate_done = 0;
1977 	procflow_shutdown();
1978 	if (filebench_shm->shm_required)
1979 		ipc_ismdelete();
1980 	eventgen_reset();
1981 }
1982 
1983 /*
1984  * Ends filebench run after first destoring any interprocess
1985  * shared memory. The call to filebench_shutdown()
1986  * also causes filebench to exit.
1987  */
1988 static void
1989 parser_filebench_shutdown(cmd_t *cmd)
1990 {
1991 	ipc_cleanup();
1992 	filebench_shutdown(1);
1993 }
1994 
1995 /*
1996  * Sleeps for cmd->cmd_qty seconds, one second at a time.
1997  */
1998 static void
1999 parser_sleep(cmd_t *cmd)
2000 {
2001 	int sleeptime;
2002 
2003 	/* check for startup errors */
2004 	if (filebench_shm->f_abort)
2005 		return;
2006 
2007 	sleeptime = cmd->cmd_qty;
2008 	filebench_log(LOG_INFO, "Running...");
2009 	while (sleeptime) {
2010 		(void) sleep(1);
2011 		sleeptime--;
2012 		if (filebench_shm->f_abort)
2013 			break;
2014 	}
2015 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2016 	    cmd->cmd_qty - sleeptime);
2017 }
2018 
2019 /*
2020  * Do a file bench run. Calls routines to create file sets, files, and
2021  * processes. It resets the statistics counters, then sleeps for the runtime
2022  * passed as an argument to it on the command line in 1 second increments.
2023  * When it is finished sleeping, it collects a snapshot of the statistics
2024  * and ends the run.
2025  */
2026 static void
2027 parser_run(cmd_t *cmd)
2028 {
2029 	int runtime;
2030 
2031 	runtime = cmd->cmd_qty;
2032 	parser_fileset_create(cmd);
2033 	parser_proc_create(cmd);
2034 
2035 	/* check for startup errors */
2036 	if (filebench_shm->f_abort)
2037 		return;
2038 
2039 	filebench_log(LOG_INFO, "Running...");
2040 	stats_clear();
2041 	while (runtime) {
2042 		(void) sleep(1);
2043 		runtime--;
2044 		if (filebench_shm->f_abort)
2045 			break;
2046 	}
2047 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2048 	    cmd->cmd_qty - runtime);
2049 	parser_statssnap(cmd);
2050 	parser_proc_shutdown(cmd);
2051 }
2052 
2053 /*
2054  * Similar to parser_run, but gets the sleep time from a variable
2055  * whose name is supplied as an argument to the command.
2056  */
2057 static void
2058 parser_run_variable(cmd_t *cmd)
2059 {
2060 	vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1);
2061 	int runtime;
2062 
2063 	if (integer == NULL) {
2064 		filebench_log(LOG_ERROR, "Unknown variable %s",
2065 		cmd->cmd_tgt1);
2066 		return;
2067 	}
2068 
2069 	runtime = *integer;
2070 
2071 	/* check for startup errors */
2072 	if (filebench_shm->f_abort)
2073 		return;
2074 
2075 	filebench_log(LOG_INFO, "Running...");
2076 	stats_clear();
2077 	while (runtime) {
2078 		(void) sleep(1);
2079 		runtime--;
2080 		if (filebench_shm->f_abort)
2081 			break;
2082 	}
2083 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2084 	    *integer - runtime);
2085 	parser_statssnap(cmd);
2086 }
2087 
2088 char *usagestr = NULL;
2089 
2090 /*
2091  * Prints usage string if defined, else just a message requesting load of a
2092  * personality.
2093  */
2094 static void
2095 parser_help(cmd_t *cmd)
2096 {
2097 	int runtime;
2098 
2099 	if (usagestr) {
2100 		filebench_log(LOG_INFO, "%s", usagestr);
2101 	} else {
2102 		filebench_log(LOG_INFO,
2103 		    "load <personality> (ls "
2104 		    "/usr/benchmarks/filebench/workloads for list)");
2105 	}
2106 }
2107 
2108 char *varstr = NULL;
2109 
2110 /*
2111  * Prints the string of all var definitions, if there is one.
2112  */
2113 static void
2114 parser_printvars(cmd_t *cmd)
2115 {
2116 	int runtime;
2117 	char *str, *c;
2118 
2119 	if (varstr) {
2120 		str = strdup(varstr);
2121 		for (c = str; *c != '\0'; c++) {
2122 			if ((char)*c == '$')
2123 				*c = ' ';
2124 		}
2125 		filebench_log(LOG_INFO, "%s", str);
2126 		free(str);
2127 	}
2128 }
2129 
2130 /*
2131  * Used by the SET command to add a var and default value string to the
2132  * varstr string. It allocates a new, larger varstr string, copies the
2133  * old contents of varstr into it, then adds the new var string on the end.
2134  */
2135 static void
2136 parser_vars(cmd_t *cmd)
2137 {
2138 	char *string = cmd->cmd_tgt1;
2139 	char *newvars;
2140 
2141 	if (string == NULL)
2142 		return;
2143 
2144 	if (dofile)
2145 		return;
2146 
2147 	if (varstr == NULL) {
2148 		newvars = malloc(strlen(string) + 2);
2149 		*newvars = 0;
2150 	} else {
2151 		newvars = malloc(strlen(varstr) + strlen(string) + 2);
2152 		(void) strcpy(newvars, varstr);
2153 	}
2154 	(void) strcat(newvars, string);
2155 	(void) strcat(newvars, " ");
2156 
2157 	if (varstr)
2158 		free(varstr);
2159 
2160 	varstr = newvars;
2161 }
2162 
2163 /*
2164  * Same as parser_sleep, except the sleep time is obtained from a variable
2165  * whose name is passed to it as an argument on the command line.
2166  */
2167 static void
2168 parser_sleep_variable(cmd_t *cmd)
2169 {
2170 	vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1);
2171 	int sleeptime;
2172 
2173 	if (integer == NULL) {
2174 		filebench_log(LOG_ERROR, "Unknown variable %s",
2175 		cmd->cmd_tgt1);
2176 		return;
2177 	}
2178 
2179 	sleeptime = *integer;
2180 
2181 	/* check for startup errors */
2182 	if (filebench_shm->f_abort)
2183 		return;
2184 
2185 	filebench_log(LOG_INFO, "Running...");
2186 	while (sleeptime) {
2187 		(void) sleep(1);
2188 		sleeptime--;
2189 		if (filebench_shm->f_abort)
2190 			break;
2191 	}
2192 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2193 	    *integer - sleeptime);
2194 }
2195 
2196 /*
2197  * Parser log prints the values of a list of variables to the log file.
2198  * The list of variables is placed on the command line, separated
2199  * by comas and the entire list is enclosed in quotes.
2200  * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576,
2201  * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576
2202  */
2203 static void
2204 parser_log(cmd_t *cmd)
2205 {
2206 	char *string;
2207 
2208 	if (cmd->cmd_param_list == NULL)
2209 		return;
2210 
2211 	string = parser_list2string(cmd->cmd_param_list);
2212 
2213 	if (string == NULL)
2214 		return;
2215 
2216 	filebench_log(LOG_VERBOSE, "log %s", string);
2217 	filebench_log(LOG_LOG, "%s", string);
2218 }
2219 
2220 /*
2221  * Implements the stats directory command. changes the directory for
2222  * dumping statistics to supplied directory path. For example:
2223  * 	stats directory /tmp
2224  * changes the stats directory to "/tmp".
2225  */
2226 static void
2227 parser_directory(cmd_t *cmd)
2228 {
2229 	char newdir[MAXPATHLEN];
2230 	char *dir;
2231 
2232 	if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) {
2233 		filebench_log(LOG_ERROR, "Cannot interpret directory");
2234 		return;
2235 	}
2236 
2237 	*newdir = 0;
2238 	/* Change dir relative to cwd if path not fully qualified */
2239 	if (*dir != '/') {
2240 		(void) strcat(newdir, cwd);
2241 		(void) strcat(newdir, "/");
2242 	}
2243 	(void) strcat(newdir, dir);
2244 	(void) mkdir(newdir, 0755);
2245 	filebench_log(LOG_VERBOSE, "Change dir to %s", newdir);
2246 	chdir(newdir);
2247 	free(dir);
2248 }
2249 
2250 #define	PIPE_PARENT 1
2251 #define	PIPE_CHILD  0
2252 
2253 /*
2254  * Runs the quoted unix command as a background process. Intended for
2255  * running statistics gathering utilities such as mpstat while the filebench
2256  * workload is running. Also records the pid's of the background processes
2257  * so that parser_statssnap() can terminate them when the run completes.
2258  */
2259 static void
2260 parser_statscmd(cmd_t *cmd)
2261 {
2262 	char *string;
2263 	pid_t pid;
2264 	pidlist_t *pidlistent;
2265 	int pipe_fd[2];
2266 	int newstdout;
2267 
2268 	if (cmd->cmd_param_list == NULL)
2269 		return;
2270 
2271 	string = parser_list2string(cmd->cmd_param_list);
2272 
2273 	if (string == NULL)
2274 		return;
2275 
2276 	if ((pipe(pipe_fd)) < 0) {
2277 		filebench_log(LOG_ERROR, "statscmd pipe failed");
2278 		return;
2279 	}
2280 
2281 #ifdef HAVE_FORK1
2282 	if ((pid = fork1()) < 0) {
2283 		filebench_log(LOG_ERROR, "statscmd fork failed");
2284 		return;
2285 	}
2286 #elif HAVE_FORK
2287 	if ((pid = fork()) < 0) {
2288 		filebench_log(LOG_ERROR, "statscmd fork failed");
2289 		return;
2290 	}
2291 #else
2292 	Crash! - Need code to deal with no fork1!
2293 #endif /* HAVE_FORK1 */
2294 
2295 	if (pid == 0) {
2296 
2297 		setsid();
2298 
2299 		filebench_log(LOG_VERBOSE,
2300 		    "Backgrounding %s", string);
2301 		/*
2302 		 * Child
2303 		 * - close stdout
2304 		 * - dup to create new stdout
2305 		 * - close pipe fds
2306 		 */
2307 		(void) close(1);
2308 
2309 		if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) {
2310 			filebench_log(LOG_ERROR,
2311 			    "statscmd dup failed: %s",
2312 			    strerror(errno));
2313 		}
2314 
2315 		(void) close(pipe_fd[PIPE_PARENT]);
2316 		(void) close(pipe_fd[PIPE_CHILD]);
2317 
2318 		if (system(string) < 0) {
2319 			filebench_log(LOG_ERROR,
2320 			    "statscmd exec failed: %s",
2321 			    strerror(errno));
2322 		}
2323 		/* Failed! */
2324 		exit(1);
2325 
2326 	} else {
2327 
2328 		/* Record pid in pidlist for subsequent reaping by stats snap */
2329 		if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t)))
2330 		    == NULL) {
2331 			filebench_log(LOG_ERROR, "pidlistent malloc failed");
2332 			return;
2333 		}
2334 
2335 		pidlistent->pl_pid = pid;
2336 		pidlistent->pl_fd = pipe_fd[PIPE_PARENT];
2337 		(void) close(pipe_fd[PIPE_CHILD]);
2338 
2339 		/* Add fileobj to global list */
2340 		if (pidlist == NULL) {
2341 			pidlist = pidlistent;
2342 			pidlistent->pl_next = NULL;
2343 		} else {
2344 			pidlistent->pl_next = pidlist;
2345 			pidlist = pidlistent;
2346 		}
2347 	}
2348 }
2349 
2350 /*
2351  * Launches a shell to run the unix command supplied in the argument.
2352  * The command should be enclosed in quotes, as in:
2353  * 	system "rm xyz"
2354  * which would run the "rm" utility to delete the file "xyz".
2355  */
2356 static void
2357 parser_system(cmd_t *cmd)
2358 {
2359 	char *string;
2360 
2361 	if (cmd->cmd_param_list == NULL)
2362 		return;
2363 
2364 	string = parser_list2string(cmd->cmd_param_list);
2365 
2366 	if (string == NULL)
2367 		return;
2368 
2369 	filebench_log(LOG_VERBOSE,
2370 	    "Running '%s'", string);
2371 
2372 	if (system(string) < 0) {
2373 		filebench_log(LOG_ERROR,
2374 		    "system exec failed: %s",
2375 		    strerror(errno));
2376 	}
2377 	free(string);
2378 }
2379 
2380 /*
2381  * Echos string supplied with command to the log.
2382  */
2383 static void
2384 parser_echo(cmd_t *cmd)
2385 {
2386 	char *string;
2387 
2388 	if (cmd->cmd_param_list == NULL)
2389 		return;
2390 
2391 	string = parser_list2string(cmd->cmd_param_list);
2392 
2393 	if (string == NULL)
2394 		return;
2395 
2396 	filebench_log(LOG_INFO, "%s", string);
2397 }
2398 
2399 
2400 /*
2401  * Adds the string supplied as the argument to the usage command
2402  * to the end of the string printed by the help command.
2403  */
2404 static void
2405 parser_usage(cmd_t *cmd)
2406 {
2407 	char *string;
2408 	char *newusage;
2409 
2410 	if (cmd->cmd_param_list == NULL)
2411 		return;
2412 
2413 	string = parser_list2string(cmd->cmd_param_list);
2414 
2415 	if (string == NULL)
2416 		return;
2417 
2418 	if (dofile)
2419 		return;
2420 
2421 	if (usagestr == NULL) {
2422 		newusage = malloc(strlen(string) + 2);
2423 		*newusage = 0;
2424 	} else {
2425 		newusage = malloc(strlen(usagestr) + strlen(string) + 2);
2426 		(void) strcpy(newusage, usagestr);
2427 	}
2428 	(void) strcat(newusage, "\n");
2429 	(void) strcat(newusage, string);
2430 
2431 	if (usagestr)
2432 		free(usagestr);
2433 
2434 	usagestr = newusage;
2435 
2436 	filebench_log(LOG_INFO, "%s", string);
2437 }
2438 
2439 /*
2440  * Updates the global dump filename with the filename supplied
2441  * as the command's argument. Then dumps the statistics of each
2442  * worker flowop into the dump file, followed by a summary of
2443  * overall totals.
2444  */
2445 static void
2446 parser_statsdump(cmd_t *cmd)
2447 {
2448 	char *string;
2449 
2450 	if (cmd->cmd_param_list == NULL)
2451 		return;
2452 
2453 	string = parser_list2string(cmd->cmd_param_list);
2454 
2455 	if (string == NULL)
2456 		return;
2457 
2458 	filebench_log(LOG_VERBOSE,
2459 	    "Stats dump to file '%s'", string);
2460 
2461 	stats_dump(string);
2462 
2463 	free(string);
2464 }
2465 
2466 /*
2467  * Same as parser_statsdump, but in xml format.
2468  */
2469 static void
2470 parser_statsxmldump(cmd_t *cmd)
2471 {
2472 	char *string;
2473 
2474 	if (cmd->cmd_param_list == NULL)
2475 		return;
2476 
2477 	string = parser_list2string(cmd->cmd_param_list);
2478 
2479 	if (string == NULL)
2480 		return;
2481 
2482 	filebench_log(LOG_VERBOSE,
2483 	    "Stats dump to file '%s'", string);
2484 
2485 	stats_xmldump(string);
2486 
2487 	free(string);
2488 }
2489 
2490 /*
2491  * Kills off background statistics collection processes, then takes a snapshot
2492  * of the filebench run's collected statistics using stats_snap() from
2493  * stats.c.
2494  */
2495 static void
2496 parser_statssnap(cmd_t *cmd)
2497 {
2498 	pidlist_t *pidlistent;
2499 	int stat;
2500 	pid_t pid;
2501 
2502 	for (pidlistent = pidlist; pidlistent != NULL;
2503 	    pidlistent = pidlistent->pl_next) {
2504 		filebench_log(LOG_VERBOSE, "Killing session %d for pid %d",
2505 		    getsid(pidlistent->pl_pid),
2506 		    pidlistent->pl_pid);
2507 		if (pidlistent->pl_fd)
2508 			(void) close(pidlistent->pl_fd);
2509 #ifdef HAVE_SIGSEND
2510 		sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM);
2511 #else
2512 		(void) kill(-1, SIGTERM);
2513 #endif
2514 
2515 		/* Close pipe */
2516 		if (pidlistent->pl_fd)
2517 			(void) close(pidlistent->pl_fd);
2518 
2519 		/* Wait for cmd and all its children */
2520 		while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0)
2521 			filebench_log(LOG_DEBUG_IMPL,
2522 			"Waited for pid %lld", pid);
2523 	}
2524 
2525 	for (pidlistent = pidlist; pidlistent != NULL;
2526 	    pidlistent = pidlistent->pl_next) {
2527 		free(pidlistent);
2528 	}
2529 
2530 	pidlist = NULL;
2531 	stats_snap();
2532 }
2533 
2534 /*
2535  * Shutdown filebench.
2536  */
2537 static void
2538 parser_abort(int arg)
2539 {
2540 	(void) sigignore(SIGINT);
2541 	filebench_log(LOG_INFO, "Aborting...");
2542 	filebench_shutdown(1);
2543 }
2544 
2545 /*
2546  * alloc_cmd() allocates the required resources for a cmd_t. On failure, a
2547  * filebench_log is issued and NULL is returned.
2548  */
2549 static cmd_t *
2550 alloc_cmd(void)
2551 {
2552 	cmd_t *cmd;
2553 
2554 	if ((cmd = malloc(sizeof (cmd_t))) == NULL) {
2555 		filebench_log(LOG_ERROR, "Alloc cmd failed");
2556 		return (NULL);
2557 	}
2558 
2559 	(void) memset(cmd, 0, sizeof (cmd_t));
2560 
2561 	return (cmd);
2562 }
2563 
2564 /*
2565  * Frees the resources of a cmd_t and then the cmd_t "cmd" itself.
2566  */
2567 static void
2568 free_cmd(cmd_t *cmd)
2569 {
2570 	free((void *)cmd->cmd_tgt1);
2571 	free((void *)cmd->cmd_tgt2);
2572 	free(cmd);
2573 }
2574 
2575 /*
2576  * Allocates an attr_t structure and zeros it. Returns NULL on failure, or
2577  * a pointer to the attr_t.
2578  */
2579 static attr_t *
2580 alloc_attr()
2581 {
2582 	attr_t *attr;
2583 
2584 	if ((attr = malloc(sizeof (attr_t))) == NULL) {
2585 		return (NULL);
2586 	}
2587 
2588 	(void) memset(attr, 0, sizeof (attr_t));
2589 	return (attr);
2590 }
2591 
2592 /*
2593  * Searches the attribute list for the command for the named attribute type.
2594  * The attribute list is created by the parser from the list of attributes
2595  * supplied with certain commands, such as the define and flowop commands.
2596  * Returns a pointer to the attribute structure if the named attribute is
2597  * found, otherwise returns NULL. If the attribute includes a parameter list,
2598  * the list is converted to a string and stored in the attr_string field of
2599  * the returned attr_t struct.
2600  */
2601 static attr_t *
2602 get_attr(cmd_t *cmd, int64_t name)
2603 {
2604 	attr_t *attr;
2605 	attr_t *rtn = NULL;
2606 	char *string;
2607 
2608 	for (attr = cmd->cmd_attr_list; attr != NULL;
2609 	    attr = attr->attr_next) {
2610 		filebench_log(LOG_DEBUG_IMPL,
2611 		    "attr %d = %d %llx?",
2612 		    attr->attr_name,
2613 		    name,
2614 		    attr->attr_integer);
2615 
2616 		if (attr->attr_name == name)
2617 			rtn = attr;
2618 	}
2619 
2620 	if (rtn == NULL)
2621 		return (NULL);
2622 
2623 	if (rtn->attr_param_list) {
2624 		filebench_log(LOG_DEBUG_SCRIPT, "attr is param list");
2625 		string = parser_list2string(rtn->attr_param_list);
2626 		if (string != NULL) {
2627 			rtn->attr_string = string_alloc(string);
2628 			filebench_log(LOG_DEBUG_SCRIPT,
2629 			    "attr string %s", string);
2630 		}
2631 	}
2632 
2633 	return (rtn);
2634 }
2635 
2636 /*
2637  * Similar to get_attr, but converts the parameter string supplied with the
2638  * named attribute to an integer and stores the integer in the attr_integer
2639  * portion of the returned attr_t struct.
2640  */
2641 static attr_t *
2642 get_attr_integer(cmd_t *cmd, int64_t name)
2643 {
2644 	attr_t *attr;
2645 	attr_t *rtn = NULL;
2646 
2647 	for (attr = cmd->cmd_attr_list; attr != NULL;
2648 	    attr = attr->attr_next) {
2649 		if (attr->attr_name == name)
2650 			rtn = attr;
2651 	}
2652 
2653 	if (rtn == NULL)
2654 		return (NULL);
2655 
2656 	if (rtn->attr_param_list) {
2657 		rtn->attr_integer = parser_list2integer(rtn->attr_param_list);
2658 	}
2659 
2660 	return (rtn);
2661 }
2662 
2663 /*
2664  * Similar to get_attr, but converts the parameter string supplied with the
2665  * named attribute to an integer and stores the integer in the attr_integer
2666  * portion of the returned attr_t struct. If no parameter string is supplied
2667  * then it defaults to TRUE (1).
2668  */
2669 static attr_t *
2670 get_attr_bool(cmd_t *cmd, int64_t name)
2671 {
2672 	attr_t *attr;
2673 	attr_t *rtn = NULL;
2674 
2675 	for (attr = cmd->cmd_attr_list; attr != NULL;
2676 	    attr = attr->attr_next) {
2677 		if (attr->attr_name == name)
2678 			rtn = attr;
2679 	}
2680 
2681 	if (rtn == NULL)
2682 		return (NULL);
2683 
2684 	if (rtn->attr_param_list) {
2685 		rtn->attr_integer = parser_list2integer(rtn->attr_param_list);
2686 	} else if (rtn->attr_integer == 0) {
2687 		rtn->attr_integer = integer_alloc(1);
2688 	}
2689 
2690 	return (rtn);
2691 }
2692 
2693 /*
2694  * Allocates memory for a list_t structure, initializes it to zero, and
2695  * returns a pointer to it. On failure, returns NULL.
2696  */
2697 static list_t *
2698 alloc_list()
2699 {
2700 	list_t *list;
2701 
2702 	if ((list = malloc(sizeof (list_t))) == NULL) {
2703 		return (NULL);
2704 	}
2705 
2706 	(void) memset(list, 0, sizeof (list_t));
2707 	return (list);
2708 }
2709 
2710 
2711 #define	USAGE1	\
2712 "Usage:\n" \
2713 "%s: interpret f script and generate file workload\n" \
2714 "Options:\n" \
2715 "   [-h] Display verbose help\n" \
2716 "   [-p] Disable opening /proc to set uacct to enable truss\n"
2717 
2718 #define	PARSER_CMDS \
2719 "create [files|filesets|processes]\n" \
2720 "stats [clear|snap]\n" \
2721 "stats command \"shell command $var1,$var2...\"\n" \
2722 "stats directory <directory>\n" \
2723 "sleep <sleep-value>\n" \
2724 "quit\n\n" \
2725 "Variables:\n" \
2726 "set $var = value\n" \
2727 "    $var   - regular variables\n" \
2728 "    ${var} - internal special variables\n" \
2729 "    $(var) - environment variables\n\n"
2730 
2731 #define	PARSER_EXAMPLE \
2732 "Example:\n\n" \
2733 "#!/usr/bin/filebench -f\n" \
2734 "\n" \
2735 "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \
2736 "define process name=randomizer\n" \
2737 "{\n" \
2738 "  thread random-thread procname=randomizer\n"	\
2739 "  {\n" \
2740 "    flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \
2741 "  }\n" \
2742 "}\n" \
2743 "create files\n" \
2744 "create processes\n" \
2745 "stats clear\n" \
2746 "sleep 30\n" \
2747 "stats snap\n"
2748 
2749 /*
2750  * usage() display brief or verbose help for the filebench(1) command.
2751  */
2752 static void
2753 usage(int help)
2754 {
2755 	if (help >= 1)
2756 		(void) fprintf(stderr, USAGE1, cmdname);
2757 	if (help >= 2) {
2758 
2759 		(void) fprintf(stderr,
2760 		    "\n'f' language definition:\n\n");
2761 		fileset_usage();
2762 		procflow_usage();
2763 		threadflow_usage();
2764 		flowoplib_usage();
2765 		eventgen_usage();
2766 		(void) fprintf(stderr, PARSER_CMDS);
2767 		(void) fprintf(stderr, PARSER_EXAMPLE);
2768 	}
2769 	exit(E_USAGE);
2770 }
2771 
2772 int
2773 yywrap()
2774 {
2775 	char buf[1024];
2776 
2777 	if (parentscript) {
2778 		yyin = parentscript;
2779 		yy_switchfilescript(yyin);
2780 		parentscript = NULL;
2781 		return (0);
2782 	} else
2783 		return (1);
2784 }
2785