xref: /onnv-gate/usr/src/cmd/filebench/common/parser_gram.y (revision 5184:da60d2b4a9e2)
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 /* yacc externals */
79 extern FILE *yyin;
80 extern int yydebug;
81 extern void yyerror(char *s);
82 
83 /* utilities */
84 static void terminate(void);
85 static cmd_t *alloc_cmd(void);
86 static attr_t *alloc_attr(void);
87 static attr_t *get_attr(cmd_t *cmd, int64_t name);
88 static attr_t *get_attr_integer(cmd_t *cmd, int64_t name);
89 static attr_t *get_attr_bool(cmd_t *cmd, int64_t name);
90 static var_t *alloc_var(void);
91 static var_t *get_var(cmd_t *cmd, int64_t name);
92 static list_t *alloc_list();
93 
94 /* Info Commands */
95 static void parser_show(cmd_t *);
96 static void parser_list(cmd_t *);
97 
98 /* Define Commands */
99 static void parser_proc_define(cmd_t *);
100 static void parser_thread_define(cmd_t *, procflow_t *, int instances);
101 static void parser_flowop_define(cmd_t *, threadflow_t *);
102 static void parser_file_define(cmd_t *);
103 static void parser_fileset_define(cmd_t *);
104 
105 /* Create Commands */
106 static void parser_proc_create(cmd_t *);
107 static void parser_thread_create(cmd_t *);
108 static void parser_flowop_create(cmd_t *);
109 static void parser_file_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_FILE:
830 		$$->cmd = &parser_file_create;
831 		break;
832 	case FSE_FILESET:
833 		$$->cmd = &parser_fileset_create;
834 		break;
835 	default:
836 		filebench_log(LOG_ERROR, "unknown entity", $2);
837 		YYERROR;
838 	}
839 
840 };
841 
842 shutdown_command: FSC_SHUTDOWN entity
843 {
844 	if (($$ = alloc_cmd()) == NULL)
845 		YYERROR;
846 	switch ($2) {
847 	case FSE_PROC:
848 		$$->cmd = &parser_proc_shutdown;
849 		break;
850 	default:
851 		filebench_log(LOG_ERROR, "unknown entity", $2);
852 		YYERROR;
853 	}
854 
855 };
856 
857 sleep_command: FSC_SLEEP FSV_VAL_INT
858 {
859 	if (($$ = alloc_cmd()) == NULL)
860 		YYERROR;
861 	$$->cmd = parser_sleep;
862 	$$->cmd_qty = $2;
863 }
864 | FSC_SLEEP FSV_VARIABLE
865 {
866 	vinteger_t *integer;
867 
868 	if (($$ = alloc_cmd()) == NULL)
869 		YYERROR;
870 	$$->cmd = parser_sleep_variable;
871 	$$->cmd_tgt1 = fb_stralloc($2);
872 };
873 
874 run_command: FSC_RUN FSV_VAL_INT
875 {
876 	if (($$ = alloc_cmd()) == NULL)
877 		YYERROR;
878 	$$->cmd = parser_run;
879 	$$->cmd_qty = $2;
880 }
881 | FSC_RUN FSV_VARIABLE
882 {
883 	vinteger_t *integer;
884 
885 	if (($$ = alloc_cmd()) == NULL)
886 		YYERROR;
887 	$$->cmd = parser_run_variable;
888 	$$->cmd_tgt1 = fb_stralloc($2);
889 }
890 | FSC_RUN
891 {
892 	vinteger_t *integer;
893 
894 	if (($$ = alloc_cmd()) == NULL)
895 		YYERROR;
896 	$$->cmd = parser_run;
897 	$$->cmd_qty = 60UL;
898 };
899 
900 help_command: FSC_HELP
901 {
902 	if (($$ = alloc_cmd()) == NULL)
903 		YYERROR;
904 	$$->cmd = parser_help;
905 };
906 
907 flowop_command: FSC_FLOWOP name
908 {
909 	if (($$ = alloc_cmd()) == NULL)
910 		YYERROR;
911 	$$->cmd_name = fb_stralloc($2);
912 }
913 | flowop_command attr_ops
914 {
915 	$1->cmd_attr_list = $2;
916 };
917 
918 load_command: FSC_LOAD FSV_STRING
919 {
920 	FILE *newfile;
921 	char loadfile[128];
922 
923 	if (($$ = alloc_cmd()) == NULL)
924 		YYERROR;
925 
926 	(void) strcpy(loadfile, $2);
927 	(void) strcat(loadfile, ".f");
928 
929 	if ((newfile = fopen(loadfile, "r")) == NULL) {
930 		(void) strcpy(loadfile, FILEBENCHDIR);
931 		(void) strcat(loadfile, "/workloads/");
932 		(void) strcat(loadfile, $2);
933 		(void) strcat(loadfile, ".f");
934 		if ((newfile = fopen(loadfile, "r")) == NULL) {
935 			filebench_log(LOG_ERROR, "Cannot open %s", loadfile);
936 			YYERROR;
937 		}
938 	}
939 
940 	parentscript = yyin;
941 	yyin = newfile;
942 	yy_switchfileparent(yyin);
943 };
944 
945 entity: FSE_PROC {$$ = FSE_PROC;}
946 | FSE_THREAD {$$ = FSE_THREAD;}
947 | FSE_FILESET {$$ = FSE_FILESET;}
948 | FSE_FILE {$$ = FSE_FILE;};
949 
950 value: FSV_VAL_INT { $$.i = $1;}
951 | FSV_STRING { $$.s = $1;}
952 | FSV_VAL_BOOLEAN { $$.b = $1;};
953 
954 name: FSV_STRING;
955 
956 attr_ops: attr_op
957 {
958 	$$ = $1;
959 }
960 | attr_ops FSK_SEPLST attr_op
961 {
962 	attr_t *attr = NULL;
963 	attr_t *list_end = NULL;
964 
965 	for (attr = $1; attr != NULL;
966 	    attr = attr->attr_next)
967 		list_end = attr; /* Find end of list */
968 
969 	list_end->attr_next = $3;
970 
971 	$$ = $1;
972 };
973 
974 attr_op: attr_name FSK_ASSIGN attr_value
975 {
976 	$$ = $3;
977 	$$->attr_name = $1;
978 }
979 | attr_name
980 {
981 	if (($$ = alloc_attr()) == NULL)
982 		YYERROR;
983 	$$->attr_name = $1;
984 }
985 
986 attr_name: attrs_define_file
987 |attrs_define_fileset
988 |attrs_define_thread
989 |attrs_define_proc
990 |attrs_flowop
991 |attrs_eventgen;
992 
993 attrs_define_proc:
994 FSA_NICE { $$ = FSA_NICE;}
995 |FSA_INSTANCES { $$ = FSA_INSTANCES;};
996 
997 attrs_define_file:
998 FSA_SIZE { $$ = FSA_SIZE;}
999 | FSA_PATH { $$ = FSA_PATH;}
1000 | FSA_REUSE { $$ = FSA_REUSE;}
1001 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1002 | FSA_PARALLOC { $$ = FSA_PARALLOC;};
1003 
1004 attrs_define_fileset:
1005 FSA_SIZE { $$ = FSA_SIZE;}
1006 | FSA_PATH { $$ = FSA_PATH;}
1007 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;}
1008 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1009 | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;}
1010 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;}
1011 | FSA_CACHED { $$ = FSA_CACHED;}
1012 | FSA_ENTRIES { $$ = FSA_ENTRIES;};
1013 
1014 attrs_define_thread:
1015 FSA_PROCESS { $$ = FSA_PROCESS;}
1016 |FSA_MEMSIZE { $$ = FSA_MEMSIZE;}
1017 |FSA_USEISM { $$ = FSA_USEISM;}
1018 |FSA_INSTANCES { $$ = FSA_INSTANCES;};
1019 
1020 attrs_flowop:
1021 FSA_WSS { $$ = FSA_WSS;}
1022 |FSA_FILE { $$ = FSA_FILE;}
1023 |FSA_NAME { $$ = FSA_NAME;}
1024 |FSA_RANDOM { $$ = FSA_RANDOM;}
1025 |FSA_FD { $$ = FSA_FD;}
1026 |FSA_SRCFD { $$ = FSA_SRCFD;}
1027 |FSA_ROTATEFD { $$ = FSA_ROTATEFD;}
1028 |FSA_DSYNC { $$ = FSA_DSYNC;}
1029 |FSA_DIRECTIO { $$ = FSA_DIRECTIO;}
1030 |FSA_TARGET { $$ = FSA_TARGET;}
1031 |FSA_ITERS { $$ = FSA_ITERS;}
1032 |FSA_VALUE { $$ = FSA_VALUE;}
1033 |FSA_BLOCKING { $$ = FSA_BLOCKING;}
1034 |FSA_HIGHWATER { $$ = FSA_HIGHWATER;}
1035 |FSA_IOSIZE { $$ = FSA_IOSIZE;};
1036 
1037 attrs_eventgen:
1038 FSA_RATE { $$ = FSA_RATE;};
1039 
1040 attr_value: var_string_list {
1041 	if (($$ = alloc_attr()) == NULL)
1042 		YYERROR;
1043 	$$->attr_param_list = $1;
1044 } | FSV_STRING
1045 {
1046 	if (($$ = alloc_attr()) == NULL)
1047 		YYERROR;
1048 	$$->attr_string = string_alloc($1);
1049 } | FSV_VAL_INT {
1050 	if (($$ = alloc_attr()) == NULL)
1051 		YYERROR;
1052 	$$->attr_integer = integer_alloc($1);
1053 } | FSV_VARIABLE {
1054 	if (($$ = alloc_attr()) == NULL)
1055 		YYERROR;
1056 	$$->attr_integer = var_ref_integer($1);
1057 	$$->attr_string = var_ref_string($1);
1058 };
1059 
1060 %%
1061 
1062 /*
1063  *  The following 'c' routines implement the various commands defined in the
1064  * above yacc parser code. The yacc portion checks the syntax of the commands
1065  * found in a workload file, or typed on interactive command lines, parsing
1066  * the commands' parameters into lists. The lists are then passed in a cmd_t
1067  * struct for each command to its related routine in the following section
1068  * for actual execution. This section also includes a few utility routines
1069  * and the main entry point for the program.
1070  */
1071 
1072 /*
1073  * Entry point for filebench. Processes command line arguements. The -f
1074  * option will read in a workload file (the full name and extension must
1075  * must be given). The -a, -s, -m and -i options are used by worker process
1076  * to receive their name, the base address of shared memory, its path, and
1077  * the process' instance number, respectively. This information is supplied
1078  * by the master process when it execs worker processes under the process
1079  * model of execution. If the worker process arguments are passed then main
1080  * will call the procflow_exec routine which creates worker threadflows and
1081  * flowops and executes the procflow's portion of the workload model until
1082  * completion. If worker process arguments are not passed to the process,
1083  * then it becomes the master process for a filebench run. It initializes
1084  * the various filebench components and either executes the supplied workload
1085  * file, or enters interactive mode.
1086  */
1087 
1088 int
1089 main(int argc, char *argv[])
1090 {
1091 	int opt;
1092 	int docmd = FS_FALSE;
1093 	int instance;
1094 	char procname[128];
1095 	caddr_t shmaddr;
1096 	char dir[MAXPATHLEN];
1097 #ifdef HAVE_SETRLIMIT
1098 	struct rlimit rlp;
1099 #endif
1100 #ifdef HAVE_LIBTECLA
1101 	char *line;
1102 #else
1103 	char line[1024];
1104 #endif
1105 	char shmpathtmp[1024];
1106 
1107 #ifdef HAVE_SETRLIMIT
1108 	/* Set resource limits */
1109 	(void) getrlimit(RLIMIT_NOFILE, &rlp);
1110 	rlp.rlim_cur = rlp.rlim_max;
1111 	setrlimit(RLIMIT_NOFILE, &rlp);
1112 #endif
1113 
1114 	yydebug = 0;
1115 	execname = argv[0];
1116 	*procname = 0;
1117 	cwd = getcwd(dir, MAXPATHLEN);
1118 
1119 	while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) {
1120 
1121 		switch (opt) {
1122 		case 'h':
1123 			usage(2);
1124 			break;
1125 
1126 		case 'p':
1127 			noproc = 1;
1128 			break;
1129 
1130 		case 'f':
1131 			if (optarg == NULL)
1132 				usage(1);
1133 			if ((yyin = fopen(optarg, "r")) == NULL) {
1134 				(void) fprintf(stderr,
1135 				    "Cannot open file %s", optarg);
1136 				exit(1);
1137 			}
1138 			dofile = FS_TRUE;
1139 			fscriptname = optarg;
1140 
1141 			break;
1142 
1143 		case 'a':
1144 			if (optarg == NULL)
1145 				usage(1);
1146 			sscanf(optarg, "%s", &procname[0]);
1147 			break;
1148 
1149 		case 's':
1150 			if (optarg == NULL)
1151 				usage(1);
1152 #if defined(_LP64) || (__WORDSIZE == 64)
1153 			sscanf(optarg, "%llx", &shmaddr);
1154 #else
1155 			sscanf(optarg, "%x", &shmaddr);
1156 #endif
1157 			break;
1158 
1159 		case 'm':
1160 			if (optarg == NULL)
1161 				usage(1);
1162 			sscanf(optarg, "%s", shmpathtmp);
1163 			shmpath = shmpathtmp;
1164 			break;
1165 
1166 		case 'i':
1167 			if (optarg == NULL)
1168 				usage(1);
1169 			sscanf(optarg, "%d", &instance);
1170 			break;
1171 
1172 		case '?':
1173 		default:
1174 			usage(1);
1175 			break;
1176 		}
1177 	}
1178 
1179 #ifdef USE_PROCESS_MODEL
1180 	if (!(*procname))
1181 #endif
1182 	printf("FileBench Version %s\n", FILEBENCH_VERSION);
1183 	filebench_init();
1184 
1185 #ifdef USE_PROCESS_MODEL
1186 	if (*procname) {
1187 		pid = getpid();
1188 
1189 		if (ipc_attach(shmaddr) < 0) {
1190 			filebench_log(LOG_ERROR, "Cannot attach shm for %s",
1191 			    procname);
1192 			exit(1);
1193 		}
1194 
1195 		if (procflow_exec(procname, instance) < 0) {
1196 			filebench_log(LOG_ERROR, "Cannot startup process %s",
1197 			    procname);
1198 			exit(1);
1199 		}
1200 		exit(1);
1201 	}
1202 #endif
1203 
1204 	pid = getpid();
1205 	ipc_init();
1206 
1207 	if (fscriptname)
1208 		(void) strcpy(filebench_shm->fscriptname, fscriptname);
1209 
1210 	flowop_init();
1211 	stats_init();
1212 	eventgen_init();
1213 
1214 	signal(SIGINT, parser_abort);
1215 
1216 	if (dofile)
1217 		yyparse();
1218 	else {
1219 #ifdef HAVE_LIBTECLA
1220 		if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) {
1221 			filebench_log(LOG_ERROR,
1222 			    "Failed to create GetLine object");
1223 			filebench_shutdown(1);
1224 		}
1225 
1226 		if (gl_customize_completion(gl, NULL, command_complete)) {
1227 			filebench_log(LOG_ERROR,
1228 			    "Failed to register auto-completion function");
1229 			filebench_shutdown(1);
1230 		}
1231 
1232 		while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) {
1233 			arg_parse(line);
1234 			yyparse();
1235 		}
1236 
1237 		del_GetLine(gl);
1238 #else
1239 		while (!feof(stdin)) {
1240 			printf(FILEBENCH_PROMPT);
1241 			fflush(stdout);
1242 			if (fgets(line, sizeof (line), stdin) == NULL) {
1243 				if (errno == EINTR)
1244 					continue;
1245 				else
1246 					break;
1247 			}
1248 			arg_parse(line);
1249 			yyparse();
1250 		}
1251 		printf("\n");
1252 #endif	/* HAVE_LIBTECLA */
1253 	}
1254 
1255 	parser_filebench_shutdown((cmd_t *)0);
1256 
1257 	return (0);
1258 }
1259 
1260 /*
1261  * arg_parse() puts the parser into command parsing mode. Create a tmpfile
1262  * and instruct the parser to read instructions from this location by setting
1263  * yyin to the value returned by tmpfile. Write the command into the file.
1264  * Then seek back to to the start of the file so that the parser can read
1265  * the instructions.
1266  */
1267 static void
1268 arg_parse(const char *command)
1269 {
1270 	if ((yyin = tmpfile()) == NULL)
1271 		filebench_log(LOG_FATAL,
1272 		    "Cannot create tmpfile: %s", strerror(errno));
1273 
1274 	if (fwrite(command, strlen(command), 1, yyin) != 1)
1275 		filebench_log(LOG_FATAL,
1276 		    "Cannot write tmpfile: %s", strerror(errno));
1277 
1278 	if (fseek(yyin, 0, SEEK_SET) != 0)
1279 		filebench_log(LOG_FATAL,
1280 		    "Cannot seek tmpfile: %s", strerror(errno));
1281 }
1282 
1283 /*
1284  * Converts a list of var_strings or ordinary strings to a single ordinary
1285  * string. It returns a pointer to the string (in malloc'd memory) if found,
1286  * or NULL otherwise.
1287  */
1288 char *
1289 parser_list2string(list_t *list)
1290 {
1291 	list_t *l;
1292 	char *string;
1293 	char *tmp;
1294 	vinteger_t *integer;
1295 
1296 	if ((string = malloc(MAXPATHLEN)) == NULL) {
1297 		filebench_log(LOG_ERROR, "Failed to allocate memory");
1298 		return (NULL);
1299 	}
1300 
1301 	*string = 0;
1302 
1303 
1304 	/* Format args */
1305 	for (l = list; l != NULL;
1306 	    l = l->list_next) {
1307 		filebench_log(LOG_DEBUG_SCRIPT,
1308 		    "converting string '%s'", *l->list_string);
1309 
1310 		if ((tmp = var_to_string(*l->list_string)) != NULL) {
1311 			(void) strcat(string, tmp);
1312 			free(tmp);
1313 		} else {
1314 			(void) strcat(string, *l->list_string);
1315 		}
1316 	}
1317 	return (string);
1318 }
1319 
1320 /*
1321  * If the list just contains a single string starting with '$', then find
1322  * or create the named var and return the var's var_string component.
1323  * Otherwise, convert the list to a string, and allocate a var_string
1324  * containing a copy of that string. On failure either returns NULL
1325  * or shuts down the run.
1326  */
1327 var_string_t
1328 parser_list2varstring(list_t *list)
1329 {
1330 	/* Special case - variable name */
1331 	if ((list->list_next == NULL) && (*(*list->list_string) == '$'))
1332 		return (var_ref_string(*list->list_string));
1333 
1334 	return (string_alloc(parser_list2string(list)));
1335 }
1336 
1337 /*
1338  * Looks for the var named in list_string of the first element of the
1339  * supplied list. If found, returns the var_integer portion of the var.
1340  * If the var is not found, cannot be allocated, the supplied list is
1341  * NULL, or the list_string filed is empty, returns NULL.
1342  */
1343 var_integer_t
1344 parser_list2integer(list_t *list)
1345 {
1346 	var_integer_t v;
1347 
1348 	if (list && (*(list->list_string) != NULL)) {
1349 		v = var_ref_integer(*(list->list_string));
1350 		return (v);
1351 	}
1352 
1353 	return (NULL);
1354 }
1355 
1356 /*
1357  * Sets the event generator rate from the attribute supplied with the
1358  * command. If the attribute doesn't exist the routine does nothing.
1359  */
1360 static void
1361 parser_eventgen(cmd_t *cmd)
1362 {
1363 	attr_t *attr;
1364 	vinteger_t rate;
1365 
1366 	/* Get the rate from attribute */
1367 	if (attr = get_attr_integer(cmd, FSA_RATE)) {
1368 		if (attr->attr_integer) {
1369 			filebench_log(LOG_VERBOSE,
1370 			    "Eventgen: %lld per second",
1371 			    *attr->attr_integer);
1372 			eventgen_setrate(*attr->attr_integer);
1373 		}
1374 	}
1375 
1376 }
1377 
1378 /*
1379  * Assigns the designated integer variable successive values from the
1380  * supplied comma seperated integer list. After each successive integer
1381  * assignment, it executes the bracket enclosed list of commands. For
1382  * example, repeated runs of a workload with increasing io sizes can
1383  * be done using the following command line:
1384  * 	foreach $iosize in 2k, 4k, 8k {run 60}
1385  */
1386 static void
1387 parser_foreach_integer(cmd_t *cmd)
1388 {
1389 	list_t *list = cmd->cmd_param_list;
1390 	cmd_t *inner_cmd;
1391 
1392 	for (; list != NULL; list = list->list_next) {
1393 		var_assign_integer(cmd->cmd_tgt1, *list->list_integer);
1394 		filebench_log(LOG_VERBOSE, "Iterating %s=%lld",
1395 		    cmd->cmd_tgt1,
1396 		    *list->list_integer);
1397 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1398 		    inner_cmd = inner_cmd->cmd_next) {
1399 			inner_cmd->cmd(inner_cmd);
1400 		}
1401 	}
1402 }
1403 
1404 /*
1405  * Similar to parser_foreach_integer(), except takes a list of strings after
1406  * the "in" token. For example, to run twice using a different directory,
1407  * perhaps using a different filesystem, the following command line
1408  * could be used:
1409  * 	foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60)
1410  */
1411 static void
1412 parser_foreach_string(cmd_t *cmd)
1413 {
1414 	list_t *list = cmd->cmd_param_list;
1415 	cmd_t *inner_cmd;
1416 
1417 	for (; list != NULL; list = list->list_next) {
1418 		var_assign_string(cmd->cmd_tgt1, *list->list_string);
1419 		filebench_log(LOG_VERBOSE, "Iterating %s=%s",
1420 		    cmd->cmd_tgt1,
1421 		    *list->list_string);
1422 		for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1423 		    inner_cmd = inner_cmd->cmd_next) {
1424 			inner_cmd->cmd(inner_cmd);
1425 		}
1426 	}
1427 }
1428 
1429 /*
1430  * Lists the file name, path name and file size for all defined file objects.
1431  */
1432 static void
1433 parser_list(cmd_t *cmd)
1434 {
1435 	(void) fileobj_iter(fileobj_print);
1436 }
1437 
1438 /*
1439  * Calls procflow_define() to allocate "instances" number of  procflow(s)
1440  * (processes) with the supplied name. The default number of instances is
1441  * one. An optional priority level attribute can be supplied and is stored in
1442  * pf_nice. Finally the routine loops through the list of inner commands, if
1443  * any, which are defines for threadflows, and passes them one at a time to
1444  * parser_thread_define() to allocate threadflow entities for the process(es).
1445  */
1446 static void
1447 parser_proc_define(cmd_t *cmd)
1448 {
1449 	procflow_t *procflow, template;
1450 	char *name;
1451 	attr_t *attr;
1452 	var_integer_t instances = integer_alloc(1);
1453 	cmd_t *inner_cmd;
1454 
1455 	/* Get the name of the process */
1456 	if (attr = get_attr(cmd, FSA_NAME)) {
1457 		name = *attr->attr_string;
1458 	} else {
1459 		filebench_log(LOG_ERROR,
1460 		    "define proc: proc specifies no name");
1461 		filebench_shutdown(1);
1462 	}
1463 
1464 	/* Get the memory size from attribute */
1465 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
1466 		filebench_log(LOG_DEBUG_IMPL,
1467 		    "Setting instances = %lld",
1468 		    *attr->attr_integer);
1469 		instances = attr->attr_integer;
1470 	}
1471 
1472 	if ((procflow = procflow_define(name, NULL, instances)) == NULL) {
1473 		filebench_log(LOG_ERROR,
1474 		    "Failed to instantiate %d %s process(es)\n",
1475 		    instances, name);
1476 		filebench_shutdown(1);
1477 	}
1478 
1479 	/* Get the pri from attribute */
1480 	if (attr = get_attr_integer(cmd, FSA_NICE)) {
1481 		filebench_log(LOG_DEBUG_IMPL, "Setting pri = %lld",
1482 		    *attr->attr_integer);
1483 		procflow->pf_nice = attr->attr_integer;
1484 	} else
1485 		procflow->pf_nice = integer_alloc(0);
1486 
1487 
1488 	/* Create the list of threads for this process  */
1489 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1490 	    inner_cmd = inner_cmd->cmd_next) {
1491 		parser_thread_define(inner_cmd, procflow, *instances);
1492 	}
1493 }
1494 
1495 /*
1496  * Calls threadflow_define() to allocate "instances" number of  threadflow(s)
1497  * (threads) with the supplied name. The default number of instances is
1498  * one. Two other optional attributes may be supplied, one to set the memory
1499  * size, stored in tf_memsize, and to select the use of Interprocess Shared
1500  * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally
1501  * the routine loops through the list of inner commands, if any, which are
1502  * defines for flowops, and passes them one at a time to
1503  * parser_flowop_define() to allocate flowop entities for the threadflows.
1504  */
1505 static void
1506 parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances)
1507 {
1508 	threadflow_t *threadflow, template;
1509 	attr_t *attr;
1510 	var_integer_t instances = integer_alloc(1);
1511 	cmd_t *inner_cmd;
1512 	char *name;
1513 
1514 	memset(&template, 0, sizeof (threadflow_t));
1515 
1516 	/* Get the name of the thread */
1517 	if (attr = get_attr(cmd, FSA_NAME)) {
1518 		name = *attr->attr_string;
1519 	} else {
1520 		filebench_log(LOG_ERROR,
1521 		    "define thread: thread in process %s specifies no name",
1522 		    procflow->pf_name);
1523 		filebench_shutdown(1);
1524 	}
1525 
1526 	/* Get the number of instances from attribute */
1527 	if (attr = get_attr_integer(cmd, FSA_INSTANCES)) {
1528 		filebench_log(LOG_DEBUG_IMPL,
1529 		    "define thread: Setting instances = %lld",
1530 		    *attr->attr_integer);
1531 		instances = attr->attr_integer;
1532 	}
1533 
1534 	/* Get the memory size from attribute */
1535 	if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) {
1536 		filebench_log(LOG_DEBUG_IMPL,
1537 		    "define thread: Setting memsize = %lld",
1538 		    *attr->attr_integer);
1539 		template.tf_memsize = attr->attr_integer;
1540 	} else
1541 		template.tf_memsize = integer_alloc(0);
1542 
1543 	if ((threadflow = threadflow_define(procflow, name,
1544 	    &template, instances)) == NULL) {
1545 		filebench_log(LOG_ERROR,
1546 		    "define thread: Failed to instantiate thread\n");
1547 		filebench_shutdown(1);
1548 	}
1549 
1550 	/* Use ISM Memory? */
1551 	if (attr = get_attr(cmd, FSA_USEISM)) {
1552 		threadflow->tf_attrs |= THREADFLOW_USEISM;
1553 	}
1554 
1555 	/* Create the list of flowops */
1556 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
1557 	    inner_cmd = inner_cmd->cmd_next) {
1558 		parser_flowop_define(inner_cmd, threadflow);
1559 	}
1560 }
1561 
1562 /*
1563  * Calls flowop_define() to allocate a flowop with the supplied name.
1564  * The allocated flowop inherits attributes from a base flowop of the
1565  * same type.  If the new flowop has a file or fileset attribute specified,
1566  * it must specify a defined fileobj or fileset or an error will be logged.
1567  * The new flowop may  also have the following attributes set by
1568  * the program:
1569  *  - file size (fo_iosize)
1570  *  - working set size (fo_wss)
1571  *  - do random io (fo_random)
1572  *  - do synchronous io (fo_dsync)
1573  *  - perform each operation multiple times before advancing (fo_iter)
1574  *  - target name (fo_targetname)
1575  *  - An integer value (fo_value)
1576  *  - a file descriptor (fo_fd)
1577  *  - specify to rotate file descriptors (fo_rotatefd)
1578  *  - a source fd (fo_srcfdnumber)
1579  *  - specify a blocking operation (fo_blocking)
1580  *  - specify a highwater mark (fo_highwater)
1581  *
1582  * After all the supplied attributes are stored in their respective locations
1583  * in the flowop object, the flowop's init function is called. No errors are
1584  * returned, but the filebench run will be terminated if the flowtype is not
1585  * specified, a name for the new flowop is not supplied, the flowop_define
1586  * call fails, or a file or fileset name is supplied but the corresponding
1587  * fileobj or fileset cannot be located.
1588  */
1589 static void
1590 parser_flowop_define(cmd_t *cmd, threadflow_t *thread)
1591 {
1592 	flowop_t *flowop, *flowop_type;
1593 	fileobj_t *fileobj;
1594 	char *type = (char *)cmd->cmd_name;
1595 	char *name;
1596 	attr_t *attr;
1597 
1598 	/* Get the inherited flowop */
1599 	flowop_type = flowop_find(type);
1600 	if (flowop_type == NULL) {
1601 		filebench_log(LOG_ERROR,
1602 		    "define flowop: flowop type %s not found",
1603 		    type);
1604 		filebench_shutdown(1);
1605 	}
1606 
1607 	/* Get the name of the flowop */
1608 	if (attr = get_attr(cmd, FSA_NAME)) {
1609 		name = *attr->attr_string;
1610 	} else {
1611 		filebench_log(LOG_ERROR,
1612 		    "define flowop: flowop %s specifies no name",
1613 		    flowop_type->fo_name);
1614 		filebench_shutdown(1);
1615 	}
1616 
1617 	if ((flowop = flowop_define(thread, name,
1618 	    flowop_type, FLOW_MASTER, 0)) == NULL) {
1619 		filebench_log(LOG_ERROR,
1620 		    "define flowop: Failed to instantiate flowop %s\n",
1621 		    cmd->cmd_name);
1622 		filebench_shutdown(1);
1623 	}
1624 
1625 	/* Get the filename from attribute */
1626 	if (attr = get_attr(cmd, FSA_FILE)) {
1627 		flowop->fo_file = fileobj_find(*attr->attr_string);
1628 		flowop->fo_fileset = fileset_find(*attr->attr_string);
1629 
1630 		if ((flowop->fo_file == NULL) &&
1631 		    (flowop->fo_fileset == NULL)) {
1632 			filebench_log(LOG_ERROR,
1633 			    "define flowop: file %s not found",
1634 			    *attr->attr_string);
1635 			filebench_shutdown(1);
1636 		}
1637 	}
1638 
1639 	/* Get the iosize of the op */
1640 	if (attr = get_attr_integer(cmd, FSA_IOSIZE))
1641 		flowop->fo_iosize = attr->attr_integer;
1642 	else
1643 		flowop->fo_iosize = integer_alloc(0);
1644 
1645 	/* Get the working set size of the op */
1646 	if (attr = get_attr_integer(cmd, FSA_WSS))
1647 		flowop->fo_wss = attr->attr_integer;
1648 	else
1649 		flowop->fo_wss = integer_alloc(0);
1650 
1651 	/* Random I/O? */
1652 	if (attr = get_attr_bool(cmd, FSA_RANDOM))
1653 		flowop->fo_random = attr->attr_integer;
1654 	else
1655 		flowop->fo_random = integer_alloc(0);
1656 
1657 	/* Sync I/O? */
1658 	if (attr = get_attr_bool(cmd, FSA_DSYNC))
1659 		flowop->fo_dsync = attr->attr_integer;
1660 	else
1661 		flowop->fo_dsync = integer_alloc(0);
1662 
1663 	/* Iterations */
1664 	if (attr = get_attr_integer(cmd, FSA_ITERS))
1665 		flowop->fo_iters = attr->attr_integer;
1666 	else
1667 		flowop->fo_iters = integer_alloc(1);
1668 
1669 
1670 	/* Target, for wakeup etc */
1671 	if (attr = get_attr(cmd, FSA_TARGET))
1672 		(void) strcpy(flowop->fo_targetname, *attr->attr_string);
1673 
1674 	/* Value */
1675 	if (attr = get_attr_integer(cmd, FSA_VALUE))
1676 		flowop->fo_value = attr->attr_integer;
1677 	else
1678 		flowop->fo_value = integer_alloc(0);
1679 
1680 	/* FD */
1681 	if (attr = get_attr_integer(cmd, FSA_FD))
1682 		flowop->fo_fdnumber = *attr->attr_integer;
1683 
1684 	/* Rotatefd? */
1685 	if (attr = get_attr_bool(cmd, FSA_ROTATEFD))
1686 		flowop->fo_rotatefd = attr->attr_integer;
1687 	else
1688 		flowop->fo_rotatefd = integer_alloc(0);
1689 
1690 	/* SRC FD, for copies etc... */
1691 	if (attr = get_attr_integer(cmd, FSA_SRCFD))
1692 		flowop->fo_srcfdnumber = *attr->attr_integer;
1693 
1694 	/* Blocking operation? */
1695 	if (attr = get_attr_bool(cmd, FSA_BLOCKING))
1696 		flowop->fo_blocking = attr->attr_integer;
1697 	else
1698 		flowop->fo_blocking = integer_alloc(0);
1699 
1700 	/* Blocking operation? */
1701 	if (attr = get_attr_bool(cmd, FSA_DIRECTIO))
1702 		flowop->fo_directio = attr->attr_integer;
1703 	else
1704 		flowop->fo_directio = integer_alloc(0);
1705 
1706 	/* Highwater mark */
1707 	if (attr = get_attr_integer(cmd, FSA_HIGHWATER))
1708 		flowop->fo_highwater = attr->attr_integer;
1709 	else
1710 		flowop->fo_highwater = integer_alloc(1);
1711 }
1712 
1713 /*
1714  * Calls fileobj_define() to allocate a fileobj with the supplied name
1715  * and initializes the fileobj's pathname attribute, fo_path and fo_create,
1716  * and optionally the fo_prealloc, fo_paralloc, fo_reuse, fo_cached,
1717  * and fo_size attributes.
1718  */
1719 static void
1720 parser_file_define(cmd_t *cmd)
1721 {
1722 	fileobj_t *fileobj;
1723 	char *name;
1724 	attr_t *attr;
1725 	var_string_t pathname;
1726 
1727 	/* Get the name of the file */
1728 	if (attr = get_attr(cmd, FSA_NAME)) {
1729 		name = *attr->attr_string;
1730 	} else {
1731 		filebench_log(LOG_ERROR,
1732 		    "define file: file specifies no name");
1733 		return;
1734 	}
1735 
1736 	if ((fileobj = fileobj_define(name)) == NULL) {
1737 		filebench_log(LOG_ERROR,
1738 		    "define file: failed to instantiate file %s\n",
1739 		    cmd->cmd_name);
1740 		return;
1741 	}
1742 
1743 	/* Get the pathname from attribute */
1744 	if ((attr = get_attr(cmd, FSA_PATH)) == NULL) {
1745 		filebench_log(LOG_ERROR,
1746 		    "define file: no pathname specified");
1747 		return;
1748 	}
1749 
1750 	/* Expand variables in pathname */
1751 	if ((pathname = parser_list2varstring(attr->attr_param_list))
1752 	    == NULL) {
1753 		filebench_log(LOG_ERROR, "Cannot interpret path");
1754 		return;
1755 	}
1756 
1757 	fileobj->fo_path = pathname;
1758 
1759 	/* For now, all files are pre-created */
1760 	fileobj->fo_create = integer_alloc(1);
1761 
1762 	/* Should we preallocate? */
1763 	if (attr = get_attr_bool(cmd, FSA_PREALLOC)) {
1764 		fileobj->fo_prealloc = attr->attr_integer;
1765 	} else
1766 		fileobj->fo_prealloc = integer_alloc(0);
1767 
1768 	/* Should we prealloc in parallel? */
1769 	if (attr = get_attr_bool(cmd, FSA_PARALLOC)) {
1770 		fileobj->fo_paralloc = attr->attr_integer;
1771 	} else
1772 		fileobj->fo_paralloc = integer_alloc(0);
1773 
1774 	/* Should we reuse the existing file? */
1775 	if (attr = get_attr_bool(cmd, FSA_REUSE)) {
1776 		fileobj->fo_reuse = attr->attr_integer;
1777 	} else
1778 		fileobj->fo_reuse = integer_alloc(0);
1779 
1780 	/* Should we leave in cache? */
1781 	if (attr = get_attr_bool(cmd, FSA_CACHED)) {
1782 		fileobj->fo_cached = attr->attr_integer;
1783 	} else
1784 		fileobj->fo_cached = integer_alloc(0);
1785 
1786 	/* Get the size of the file */
1787 	if (attr = get_attr_integer(cmd, FSA_SIZE)) {
1788 		fileobj->fo_size = attr->attr_integer;
1789 	} else
1790 		fileobj->fo_size = integer_alloc(0);
1791 
1792 }
1793 
1794 /*
1795  * Calls fileset_define() to allocate a fileset with the supplied name and
1796  * initializes the fileset's pathname attribute, and optionally the fs_cached,
1797  * fs_reuse, fs_preallocpercent, fs_prealloc, fs_entries, fs_dirwidth,
1798  * fs_size, fs_dirgamma, and fs_sizegamma attributes.
1799  */
1800 static void
1801 parser_fileset_define(cmd_t *cmd)
1802 {
1803 	fileset_t *fileset;
1804 	char *name;
1805 	attr_t *attr;
1806 	var_string_t pathname;
1807 
1808 	/* Get the name of the file */
1809 	if (attr = get_attr(cmd, FSA_NAME)) {
1810 		name = *attr->attr_string;
1811 	} else {
1812 		filebench_log(LOG_ERROR,
1813 		    "define file: file specifies no name");
1814 		return;
1815 	}
1816 
1817 	if ((fileset = fileset_define(name)) == NULL) {
1818 		filebench_log(LOG_ERROR,
1819 		    "define file: failed to instantiate file %s\n",
1820 		    cmd->cmd_name);
1821 		return;
1822 	}
1823 
1824 	/* Get the pathname from attribute */
1825 	if ((attr = get_attr(cmd, FSA_PATH)) == NULL) {
1826 		filebench_log(LOG_ERROR, "define file: no pathname specified");
1827 		return;
1828 	}
1829 
1830 	/* Expand variables in pathname */
1831 	if ((pathname = parser_list2varstring(attr->attr_param_list)) == NULL) {
1832 		filebench_log(LOG_ERROR, "Cannot interpret path");
1833 		return;
1834 	}
1835 
1836 	fileset->fs_path = pathname;
1837 
1838 	/* Should we leave in cache? */
1839 	if (attr = get_attr_bool(cmd, FSA_CACHED)) {
1840 		fileset->fs_cached = attr->attr_integer;
1841 	} else
1842 		fileset->fs_cached = integer_alloc(0);
1843 
1844 	/* Should we reuse the existing file? */
1845 	if (attr = get_attr_bool(cmd, FSA_REUSE)) {
1846 		fileset->fs_reuse = attr->attr_integer;
1847 	} else
1848 		fileset->fs_reuse = integer_alloc(0);
1849 
1850 	/* How much should we prealloc */
1851 	if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) &&
1852 	    attr->attr_integer) {
1853 		fileset->fs_preallocpercent = attr->attr_integer;
1854 	} else if (attr && !attr->attr_integer) {
1855 		fileset->fs_preallocpercent = integer_alloc(100);
1856 	} else {
1857 		fileset->fs_preallocpercent = integer_alloc(0);
1858 	}
1859 
1860 	/* Should we preallocate? */
1861 	if (attr = get_attr_bool(cmd, FSA_PREALLOC)) {
1862 		fileset->fs_prealloc = attr->attr_integer;
1863 	} else
1864 		fileset->fs_prealloc = integer_alloc(0);
1865 
1866 	/* Get the size of the fileset */
1867 	if (attr = get_attr_integer(cmd, FSA_ENTRIES)) {
1868 		fileset->fs_entries = attr->attr_integer;
1869 	} else {
1870 		filebench_log(LOG_ERROR, "Fileset has zero entries");
1871 		fileset->fs_entries = integer_alloc(0);
1872 	}
1873 
1874 	/* Get the mean dir width of the fileset */
1875 	if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) {
1876 		fileset->fs_dirwidth = attr->attr_integer;
1877 	} else {
1878 		filebench_log(LOG_ERROR, "Fileset has zero directory width");
1879 		fileset->fs_dirwidth = integer_alloc(0);
1880 	}
1881 
1882 	/* Get the mean or absolute size of the file */
1883 	if (attr = get_attr_integer(cmd, FSA_SIZE)) {
1884 		fileset->fs_size = attr->attr_integer;
1885 	} else
1886 		fileset->fs_size = integer_alloc(0);
1887 
1888 	/* Get the gamma value for dir width distributions */
1889 	if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) {
1890 		fileset->fs_dirgamma = attr->attr_integer;
1891 	} else
1892 		fileset->fs_dirgamma = integer_alloc(1500);
1893 
1894 	/* Get the gamma value for dir width distributions */
1895 	if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) {
1896 		fileset->fs_sizegamma = attr->attr_integer;
1897 	} else
1898 		fileset->fs_sizegamma = integer_alloc(1500);
1899 }
1900 
1901 /*
1902  * Creates and starts all defined procflow processes. The call to
1903  * procflow_init() results in creation of the requested number of
1904  * process instances for each previously defined procflow. The
1905  * child processes exec() a new instance of filebench, passing it
1906  * the instance number and address of the shared memory region.
1907  * The child processes will then create their threads and flowops.
1908  * The routine then unlocks the run_lock to allow all the processes'
1909  * threads to start and  waits for all of them to begin execution.
1910  * Finally, it records the start time and resets the event generation
1911  * system.
1912  */
1913 static void
1914 parser_proc_create(cmd_t *cmd)
1915 {
1916 	if (procflow_init() != 0) {
1917 		filebench_log(LOG_ERROR, "Failed to create processes\n");
1918 		filebench_shutdown(1);
1919 	}
1920 
1921 	/* Release the read lock, allowing threads to start */
1922 	(void) pthread_rwlock_unlock(&filebench_shm->run_lock);
1923 
1924 	/* Wait for all threads to start */
1925 	if (procflow_allstarted() != 0) {
1926 		filebench_log(LOG_ERROR, "Could not start run");
1927 		return;
1928 	}
1929 
1930 
1931 	if (filebench_shm->shm_required &&
1932 	    (ipc_ismcreate(filebench_shm->shm_required) < 0)) {
1933 		filebench_log(LOG_ERROR, "Could not allocate shared memory");
1934 		return;
1935 	}
1936 
1937 	filebench_shm->starttime = gethrtime();
1938 	eventgen_reset();
1939 }
1940 
1941 /*
1942  * Calls fileobj_init() to create  and optionally pre fill files
1943  * for all fileobjs on the master list of fileobjs (filelist).
1944  * If errors are encountered, calls filebench_shutdown()
1945  * to exit filebench.
1946  */
1947 static void
1948 parser_file_create(cmd_t *cmd)
1949 {
1950 	fileobj_t *fileobj;
1951 
1952 	if (fileobj_init() != 0) {
1953 		filebench_log(LOG_ERROR, "Failed to create files");
1954 		filebench_shutdown(1);
1955 	}
1956 }
1957 
1958 /*
1959  * Calls fileset_createset() to populate all filesets and create all
1960  * associated, initially existant,  files and subdirectories.
1961  * If errors are encountered, calls filebench_shutdown()
1962  * to exit filebench.
1963  */
1964 static void
1965 parser_fileset_create(cmd_t *cmd)
1966 {
1967 	fileset_t *fileset;
1968 
1969 	if (fileset_createset(NULL) != 0) {
1970 		filebench_log(LOG_ERROR, "Failed to create filesets");
1971 		filebench_shutdown(1);
1972 	}
1973 }
1974 
1975 /*
1976  * Shuts down all processes and their associated threads. When finished
1977  * it deletes interprocess shared memory and resets the event generator.
1978  * It does not exit the filebench program though.
1979  */
1980 static void
1981 parser_proc_shutdown(cmd_t *cmd)
1982 {
1983 	filebench_log(LOG_INFO, "Shutting down processes");
1984 	procflow_shutdown();
1985 	if (filebench_shm->shm_required)
1986 		ipc_ismdelete();
1987 	eventgen_reset();
1988 }
1989 
1990 /*
1991  * Ends filebench run after first destoring any interprocess
1992  * shared memory. The call to filebench_shutdown()
1993  * also causes filebench to exit.
1994  */
1995 static void
1996 parser_filebench_shutdown(cmd_t *cmd)
1997 {
1998 	ipc_cleanup();
1999 	filebench_shutdown(1);
2000 }
2001 
2002 /*
2003  * Sleeps for cmd->cmd_qty seconds, one second at a time.
2004  */
2005 static void
2006 parser_sleep(cmd_t *cmd)
2007 {
2008 	int sleeptime;
2009 
2010 	/* check for startup errors */
2011 	if (filebench_shm->f_abort)
2012 		return;
2013 
2014 	sleeptime = cmd->cmd_qty;
2015 	filebench_log(LOG_INFO, "Running...");
2016 	while (sleeptime) {
2017 		(void) sleep(1);
2018 		sleeptime--;
2019 		if (filebench_shm->f_abort)
2020 			break;
2021 	}
2022 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2023 	    cmd->cmd_qty - sleeptime);
2024 }
2025 
2026 /*
2027  * Do a file bench run. Calls routines to create file sets, files, and
2028  * processes. It resets the statistics counters, then sleeps for the runtime
2029  * passed as an argument to it on the command line in 1 second increments.
2030  * When it is finished sleeping, it collects a snapshot of the statistics
2031  * and ends the run.
2032  */
2033 static void
2034 parser_run(cmd_t *cmd)
2035 {
2036 	int runtime;
2037 
2038 	runtime = cmd->cmd_qty;
2039 	parser_fileset_create(cmd);
2040 	parser_file_create(cmd);
2041 	parser_proc_create(cmd);
2042 
2043 	/* check for startup errors */
2044 	if (filebench_shm->f_abort)
2045 		return;
2046 
2047 	filebench_log(LOG_INFO, "Running...");
2048 	stats_clear();
2049 	while (runtime) {
2050 		(void) sleep(1);
2051 		runtime--;
2052 		if (filebench_shm->f_abort)
2053 			break;
2054 	}
2055 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2056 	    cmd->cmd_qty - runtime);
2057 	parser_statssnap(cmd);
2058 	parser_proc_shutdown(cmd);
2059 }
2060 
2061 /*
2062  * Similar to parser_run, but gets the sleep time from a variable
2063  * whose name is supplied as an argument to the command.
2064  */
2065 static void
2066 parser_run_variable(cmd_t *cmd)
2067 {
2068 	vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1);
2069 	int runtime;
2070 
2071 	if (integer == NULL) {
2072 		filebench_log(LOG_ERROR, "Unknown variable %s",
2073 		cmd->cmd_tgt1);
2074 		return;
2075 	}
2076 
2077 	runtime = *integer;
2078 
2079 	/* check for startup errors */
2080 	if (filebench_shm->f_abort)
2081 		return;
2082 
2083 	filebench_log(LOG_INFO, "Running...");
2084 	stats_clear();
2085 	while (runtime) {
2086 		(void) sleep(1);
2087 		runtime--;
2088 		if (filebench_shm->f_abort)
2089 			break;
2090 	}
2091 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2092 	    *integer - runtime);
2093 	parser_statssnap(cmd);
2094 }
2095 
2096 char *usagestr = NULL;
2097 
2098 /*
2099  * Prints usage string if defined, else just a message requesting load of a
2100  * personality.
2101  */
2102 static void
2103 parser_help(cmd_t *cmd)
2104 {
2105 	int runtime;
2106 
2107 	if (usagestr) {
2108 		filebench_log(LOG_INFO, "%s", usagestr);
2109 	} else {
2110 		filebench_log(LOG_INFO,
2111 		    "load <personality> (ls "
2112 		    "/usr/benchmarks/filebench/workloads for list)");
2113 	}
2114 }
2115 
2116 char *varstr = NULL;
2117 
2118 /*
2119  * Prints the string of all var definitions, if there is one.
2120  */
2121 static void
2122 parser_printvars(cmd_t *cmd)
2123 {
2124 	int runtime;
2125 	char *str, *c;
2126 
2127 	if (varstr) {
2128 		str = strdup(varstr);
2129 		for (c = str; *c != '\0'; c++) {
2130 			if ((char)*c == '$')
2131 				*c = ' ';
2132 		}
2133 		filebench_log(LOG_INFO, "%s", str);
2134 		free(str);
2135 	}
2136 }
2137 
2138 /*
2139  * Used by the SET command to add a var and default value string to the
2140  * varstr string. It allocates a new, larger varstr string, copies the
2141  * old contents of varstr into it, then adds the new var string on the end.
2142  */
2143 static void
2144 parser_vars(cmd_t *cmd)
2145 {
2146 	char *string = cmd->cmd_tgt1;
2147 	char *newvars;
2148 
2149 	if (string == NULL)
2150 		return;
2151 
2152 	if (dofile)
2153 		return;
2154 
2155 	if (varstr == NULL) {
2156 		newvars = malloc(strlen(string) + 2);
2157 		*newvars = 0;
2158 	} else {
2159 		newvars = malloc(strlen(varstr) + strlen(string) + 2);
2160 		(void) strcpy(newvars, varstr);
2161 	}
2162 	(void) strcat(newvars, string);
2163 	(void) strcat(newvars, " ");
2164 
2165 	if (varstr)
2166 		free(varstr);
2167 
2168 	varstr = newvars;
2169 }
2170 
2171 /*
2172  * Same as parser_sleep, except the sleep time is obtained from a variable
2173  * whose name is passed to it as an argument on the command line.
2174  */
2175 static void
2176 parser_sleep_variable(cmd_t *cmd)
2177 {
2178 	vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1);
2179 	int sleeptime;
2180 
2181 	if (integer == NULL) {
2182 		filebench_log(LOG_ERROR, "Unknown variable %s",
2183 		cmd->cmd_tgt1);
2184 		return;
2185 	}
2186 
2187 	sleeptime = *integer;
2188 
2189 	/* check for startup errors */
2190 	if (filebench_shm->f_abort)
2191 		return;
2192 
2193 	filebench_log(LOG_INFO, "Running...");
2194 	while (sleeptime) {
2195 		(void) sleep(1);
2196 		sleeptime--;
2197 		if (filebench_shm->f_abort)
2198 			break;
2199 	}
2200 	filebench_log(LOG_INFO, "Run took %lld seconds...",
2201 	    *integer - sleeptime);
2202 }
2203 
2204 /*
2205  * Parser log prints the values of a list of variables to the log file.
2206  * The list of variables is placed on the command line, separated
2207  * by comas and the entire list is enclosed in quotes.
2208  * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576,
2209  * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576
2210  */
2211 static void
2212 parser_log(cmd_t *cmd)
2213 {
2214 	char *string;
2215 
2216 	if (cmd->cmd_param_list == NULL)
2217 		return;
2218 
2219 	string = parser_list2string(cmd->cmd_param_list);
2220 
2221 	if (string == NULL)
2222 		return;
2223 
2224 	filebench_log(LOG_VERBOSE, "log %s", string);
2225 	filebench_log(LOG_LOG, "%s", string);
2226 }
2227 
2228 /*
2229  * Implements the stats directory command. changes the directory for
2230  * dumping statistics to supplied directory path. For example:
2231  * 	stats directory /tmp
2232  * changes the stats directory to "/tmp".
2233  */
2234 static void
2235 parser_directory(cmd_t *cmd)
2236 {
2237 	char newdir[MAXPATHLEN];
2238 	char *dir;
2239 
2240 	if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) {
2241 		filebench_log(LOG_ERROR, "Cannot interpret directory");
2242 		return;
2243 	}
2244 
2245 	*newdir = 0;
2246 	/* Change dir relative to cwd if path not fully qualified */
2247 	if (*dir != '/') {
2248 		(void) strcat(newdir, cwd);
2249 		(void) strcat(newdir, "/");
2250 	}
2251 	(void) strcat(newdir, dir);
2252 	(void) mkdir(newdir, 0755);
2253 	filebench_log(LOG_VERBOSE, "Change dir to %s", newdir);
2254 	chdir(newdir);
2255 	free(dir);
2256 }
2257 
2258 #define	PIPE_PARENT 1
2259 #define	PIPE_CHILD  0
2260 
2261 /*
2262  * Runs the quoted unix command as a background process. Intended for
2263  * running statistics gathering utilities such as mpstat while the filebench
2264  * workload is running. Also records the pid's of the background processes
2265  * so that parser_statssnap() can terminate them when the run completes.
2266  */
2267 static void
2268 parser_statscmd(cmd_t *cmd)
2269 {
2270 	char *string;
2271 	pid_t pid;
2272 	pidlist_t *pidlistent;
2273 	int pipe_fd[2];
2274 	int newstdout;
2275 
2276 	if (cmd->cmd_param_list == NULL)
2277 		return;
2278 
2279 	string = parser_list2string(cmd->cmd_param_list);
2280 
2281 	if (string == NULL)
2282 		return;
2283 
2284 	if ((pipe(pipe_fd)) < 0) {
2285 		filebench_log(LOG_ERROR, "statscmd pipe failed");
2286 		return;
2287 	}
2288 
2289 #ifdef HAVE_FORK1
2290 	if ((pid = fork1()) < 0) {
2291 		filebench_log(LOG_ERROR, "statscmd fork failed");
2292 		return;
2293 	}
2294 #elif HAVE_FORK
2295 	if ((pid = fork()) < 0) {
2296 		filebench_log(LOG_ERROR, "statscmd fork failed");
2297 		return;
2298 	}
2299 #else
2300 	Crash! - Need code to deal with no fork1!
2301 #endif /* HAVE_FORK1 */
2302 
2303 	if (pid == 0) {
2304 
2305 		setsid();
2306 
2307 		filebench_log(LOG_VERBOSE,
2308 		    "Backgrounding %s", string);
2309 		/*
2310 		 * Child
2311 		 * - close stdout
2312 		 * - dup to create new stdout
2313 		 * - close pipe fds
2314 		 */
2315 		(void) close(1);
2316 
2317 		if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) {
2318 			filebench_log(LOG_ERROR,
2319 			    "statscmd dup failed: %s",
2320 			    strerror(errno));
2321 		}
2322 
2323 		(void) close(pipe_fd[PIPE_PARENT]);
2324 		(void) close(pipe_fd[PIPE_CHILD]);
2325 
2326 		if (system(string) < 0) {
2327 			filebench_log(LOG_ERROR,
2328 			    "statscmd exec failed: %s",
2329 			    strerror(errno));
2330 		}
2331 		/* Failed! */
2332 		exit(1);
2333 
2334 	} else {
2335 
2336 		/* Record pid in pidlist for subsequent reaping by stats snap */
2337 		if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t)))
2338 		    == NULL) {
2339 			filebench_log(LOG_ERROR, "pidlistent malloc failed");
2340 			return;
2341 		}
2342 
2343 		pidlistent->pl_pid = pid;
2344 		pidlistent->pl_fd = pipe_fd[PIPE_PARENT];
2345 		(void) close(pipe_fd[PIPE_CHILD]);
2346 
2347 		/* Add fileobj to global list */
2348 		if (pidlist == NULL) {
2349 			pidlist = pidlistent;
2350 			pidlistent->pl_next = NULL;
2351 		} else {
2352 			pidlistent->pl_next = pidlist;
2353 			pidlist = pidlistent;
2354 		}
2355 	}
2356 }
2357 
2358 /*
2359  * Launches a shell to run the unix command supplied in the argument.
2360  * The command should be enclosed in quotes, as in:
2361  * 	system "rm xyz"
2362  * which would run the "rm" utility to delete the file "xyz".
2363  */
2364 static void
2365 parser_system(cmd_t *cmd)
2366 {
2367 	char *string;
2368 
2369 	if (cmd->cmd_param_list == NULL)
2370 		return;
2371 
2372 	string = parser_list2string(cmd->cmd_param_list);
2373 
2374 	if (string == NULL)
2375 		return;
2376 
2377 	filebench_log(LOG_VERBOSE,
2378 	    "Running '%s'", string);
2379 
2380 	if (system(string) < 0) {
2381 		filebench_log(LOG_ERROR,
2382 		    "system exec failed: %s",
2383 		    strerror(errno));
2384 	}
2385 	free(string);
2386 }
2387 
2388 /*
2389  * Echos string supplied with command to the log.
2390  */
2391 static void
2392 parser_echo(cmd_t *cmd)
2393 {
2394 	char *string;
2395 
2396 	if (cmd->cmd_param_list == NULL)
2397 		return;
2398 
2399 	string = parser_list2string(cmd->cmd_param_list);
2400 
2401 	if (string == NULL)
2402 		return;
2403 
2404 	filebench_log(LOG_INFO, "%s", string);
2405 }
2406 
2407 
2408 /*
2409  * Adds the string supplied as the argument to the usage command
2410  * to the end of the string printed by the help command.
2411  */
2412 static void
2413 parser_usage(cmd_t *cmd)
2414 {
2415 	char *string;
2416 	char *newusage;
2417 
2418 	if (cmd->cmd_param_list == NULL)
2419 		return;
2420 
2421 	string = parser_list2string(cmd->cmd_param_list);
2422 
2423 	if (string == NULL)
2424 		return;
2425 
2426 	if (dofile)
2427 		return;
2428 
2429 	if (usagestr == NULL) {
2430 		newusage = malloc(strlen(string) + 2);
2431 		*newusage = 0;
2432 	} else {
2433 		newusage = malloc(strlen(usagestr) + strlen(string) + 2);
2434 		(void) strcpy(newusage, usagestr);
2435 	}
2436 	(void) strcat(newusage, "\n");
2437 	(void) strcat(newusage, string);
2438 
2439 	if (usagestr)
2440 		free(usagestr);
2441 
2442 	usagestr = newusage;
2443 
2444 	filebench_log(LOG_INFO, "%s", string);
2445 }
2446 
2447 /*
2448  * Updates the global dump filename with the filename supplied
2449  * as the command's argument. Then dumps the statistics of each
2450  * worker flowop into the dump file, followed by a summary of
2451  * overall totals.
2452  */
2453 static void
2454 parser_statsdump(cmd_t *cmd)
2455 {
2456 	char *string;
2457 
2458 	if (cmd->cmd_param_list == NULL)
2459 		return;
2460 
2461 	string = parser_list2string(cmd->cmd_param_list);
2462 
2463 	if (string == NULL)
2464 		return;
2465 
2466 	filebench_log(LOG_VERBOSE,
2467 	    "Stats dump to file '%s'", string);
2468 
2469 	stats_dump(string);
2470 
2471 	free(string);
2472 }
2473 
2474 /*
2475  * Same as parser_statsdump, but in xml format.
2476  */
2477 static void
2478 parser_statsxmldump(cmd_t *cmd)
2479 {
2480 	char *string;
2481 
2482 	if (cmd->cmd_param_list == NULL)
2483 		return;
2484 
2485 	string = parser_list2string(cmd->cmd_param_list);
2486 
2487 	if (string == NULL)
2488 		return;
2489 
2490 	filebench_log(LOG_VERBOSE,
2491 	    "Stats dump to file '%s'", string);
2492 
2493 	stats_xmldump(string);
2494 
2495 	free(string);
2496 }
2497 
2498 /*
2499  * Kills off background statistics collection processes, then takes a snapshot
2500  * of the filebench run's collected statistics using stats_snap() from
2501  * stats.c.
2502  */
2503 static void
2504 parser_statssnap(cmd_t *cmd)
2505 {
2506 	pidlist_t *pidlistent;
2507 	int stat;
2508 	pid_t pid;
2509 
2510 	for (pidlistent = pidlist; pidlistent != NULL;
2511 	    pidlistent = pidlistent->pl_next) {
2512 		filebench_log(LOG_VERBOSE, "Killing session %d for pid %d",
2513 		    getsid(pidlistent->pl_pid),
2514 		    pidlistent->pl_pid);
2515 		if (pidlistent->pl_fd)
2516 			(void) close(pidlistent->pl_fd);
2517 #ifdef HAVE_SIGSEND
2518 		sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM);
2519 #else
2520 		(void) kill(-1, SIGTERM);
2521 #endif
2522 
2523 		/* Close pipe */
2524 		if (pidlistent->pl_fd)
2525 			(void) close(pidlistent->pl_fd);
2526 
2527 		/* Wait for cmd and all its children */
2528 		while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0)
2529 			filebench_log(LOG_DEBUG_IMPL,
2530 			"Waited for pid %lld", pid);
2531 	}
2532 
2533 	for (pidlistent = pidlist; pidlistent != NULL;
2534 	    pidlistent = pidlistent->pl_next) {
2535 		free(pidlistent);
2536 	}
2537 
2538 	pidlist = NULL;
2539 	stats_snap();
2540 }
2541 
2542 /*
2543  * Shutdown filebench.
2544  */
2545 static void
2546 parser_abort(int arg)
2547 {
2548 	(void) sigignore(SIGINT);
2549 	filebench_log(LOG_INFO, "Aborting...");
2550 	filebench_shutdown(1);
2551 }
2552 
2553 /*
2554  * alloc_cmd() allocates the required resources for a cmd_t. On failure, a
2555  * filebench_log is issued and NULL is returned.
2556  */
2557 static cmd_t *
2558 alloc_cmd(void)
2559 {
2560 	cmd_t *cmd;
2561 
2562 	if ((cmd = malloc(sizeof (cmd_t))) == NULL) {
2563 		filebench_log(LOG_ERROR, "Alloc cmd failed");
2564 		return (NULL);
2565 	}
2566 
2567 	(void) memset(cmd, 0, sizeof (cmd_t));
2568 
2569 	return (cmd);
2570 }
2571 
2572 /*
2573  * Frees the resources of a cmd_t and then the cmd_t "cmd" itself.
2574  */
2575 static void
2576 free_cmd(cmd_t *cmd)
2577 {
2578 	free((void *)cmd->cmd_tgt1);
2579 	free((void *)cmd->cmd_tgt2);
2580 	free(cmd);
2581 }
2582 
2583 /*
2584  * Allocates an attr_t structure and zeros it. Returns NULL on failure, or
2585  * a pointer to the attr_t.
2586  */
2587 static attr_t *
2588 alloc_attr()
2589 {
2590 	attr_t *attr;
2591 
2592 	if ((attr = malloc(sizeof (attr_t))) == NULL) {
2593 		return (NULL);
2594 	}
2595 
2596 	(void) memset(attr, 0, sizeof (attr_t));
2597 	return (attr);
2598 }
2599 
2600 /*
2601  * Searches the attribute list for the command for the named attribute type.
2602  * The attribute list is created by the parser from the list of attributes
2603  * supplied with certain commands, such as the define and flowop commands.
2604  * Returns a pointer to the attribute structure if the named attribute is
2605  * found, otherwise returns NULL. If the attribute includes a parameter list,
2606  * the list is converted to a string and stored in the attr_string field of
2607  * the returned attr_t struct.
2608  */
2609 static attr_t *
2610 get_attr(cmd_t *cmd, int64_t name)
2611 {
2612 	attr_t *attr;
2613 	attr_t *rtn = NULL;
2614 	char *string;
2615 
2616 	for (attr = cmd->cmd_attr_list; attr != NULL;
2617 	    attr = attr->attr_next) {
2618 		filebench_log(LOG_DEBUG_IMPL,
2619 		    "attr %d = %d %llx?",
2620 		    attr->attr_name,
2621 		    name,
2622 		    attr->attr_integer);
2623 
2624 		if (attr->attr_name == name)
2625 			rtn = attr;
2626 	}
2627 
2628 	if (rtn == NULL)
2629 		return (NULL);
2630 
2631 	if (rtn->attr_param_list) {
2632 		filebench_log(LOG_DEBUG_SCRIPT, "attr is param list");
2633 		string = parser_list2string(rtn->attr_param_list);
2634 		if (string != NULL) {
2635 			rtn->attr_string = string_alloc(string);
2636 			filebench_log(LOG_DEBUG_SCRIPT,
2637 			    "attr string %s", string);
2638 		}
2639 	}
2640 
2641 	return (rtn);
2642 }
2643 
2644 /*
2645  * Similar to get_attr, but converts the parameter string supplied with the
2646  * named attribute to an integer and stores the integer in the attr_integer
2647  * portion of the returned attr_t struct.
2648  */
2649 static attr_t *
2650 get_attr_integer(cmd_t *cmd, int64_t name)
2651 {
2652 	attr_t *attr;
2653 	attr_t *rtn = NULL;
2654 
2655 	for (attr = cmd->cmd_attr_list; attr != NULL;
2656 	    attr = attr->attr_next) {
2657 		if (attr->attr_name == name)
2658 			rtn = attr;
2659 	}
2660 
2661 	if (rtn == NULL)
2662 		return (NULL);
2663 
2664 	if (rtn->attr_param_list) {
2665 		rtn->attr_integer = parser_list2integer(rtn->attr_param_list);
2666 	}
2667 
2668 	return (rtn);
2669 }
2670 
2671 /*
2672  * Similar to get_attr, but converts the parameter string supplied with the
2673  * named attribute to an integer and stores the integer in the attr_integer
2674  * portion of the returned attr_t struct. If no parameter string is supplied
2675  * then it defaults to TRUE (1).
2676  */
2677 static attr_t *
2678 get_attr_bool(cmd_t *cmd, int64_t name)
2679 {
2680 	attr_t *attr;
2681 	attr_t *rtn = NULL;
2682 
2683 	for (attr = cmd->cmd_attr_list; attr != NULL;
2684 	    attr = attr->attr_next) {
2685 		if (attr->attr_name == name)
2686 			rtn = attr;
2687 	}
2688 
2689 	if (rtn == NULL)
2690 		return (NULL);
2691 
2692 	if (rtn->attr_param_list) {
2693 		rtn->attr_integer = parser_list2integer(rtn->attr_param_list);
2694 	} else if (rtn->attr_integer == 0) {
2695 		rtn->attr_integer = integer_alloc(1);
2696 	}
2697 
2698 	return (rtn);
2699 }
2700 
2701 /*
2702  * Allocates memory for a list_t structure, initializes it to zero, and
2703  * returns a pointer to it. On failure, returns NULL.
2704  */
2705 static list_t *
2706 alloc_list()
2707 {
2708 	list_t *list;
2709 
2710 	if ((list = malloc(sizeof (list_t))) == NULL) {
2711 		return (NULL);
2712 	}
2713 
2714 	(void) memset(list, 0, sizeof (list_t));
2715 	return (list);
2716 }
2717 
2718 
2719 #define	USAGE1	\
2720 "Usage:\n" \
2721 "%s: interpret f script and generate file workload\n" \
2722 "Options:\n" \
2723 "   [-h] Display verbose help\n" \
2724 "   [-p] Disable opening /proc to set uacct to enable truss\n"
2725 
2726 #define	PARSER_CMDS \
2727 "create [files|filesets|processes]\n" \
2728 "stats [clear|snap]\n" \
2729 "stats command \"shell command $var1,$var2...\"\n" \
2730 "stats directory <directory>\n" \
2731 "sleep <sleep-value>\n" \
2732 "quit\n\n" \
2733 "Variables:\n" \
2734 "set $var = value\n" \
2735 "    $var   - regular variables\n" \
2736 "    ${var} - internal special variables\n" \
2737 "    $(var) - environment variables\n\n"
2738 
2739 #define	PARSER_EXAMPLE \
2740 "Example:\n\n" \
2741 "#!/usr/bin/filebench -f\n" \
2742 "\n" \
2743 "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \
2744 "define process name=randomizer\n" \
2745 "{\n" \
2746 "  thread random-thread procname=randomizer\n"	\
2747 "  {\n" \
2748 "    flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \
2749 "  }\n" \
2750 "}\n" \
2751 "create files\n" \
2752 "create processes\n" \
2753 "stats clear\n" \
2754 "sleep 30\n" \
2755 "stats snap\n"
2756 
2757 /*
2758  * usage() display brief or verbose help for the filebench(1) command.
2759  */
2760 static void
2761 usage(int help)
2762 {
2763 	if (help >= 1)
2764 		(void) fprintf(stderr, USAGE1, cmdname);
2765 	if (help >= 2) {
2766 
2767 		(void) fprintf(stderr,
2768 		    "\n'f' language definition:\n\n");
2769 		fileobj_usage();
2770 		fileset_usage();
2771 		procflow_usage();
2772 		threadflow_usage();
2773 		flowoplib_usage();
2774 		eventgen_usage();
2775 		(void) fprintf(stderr, PARSER_CMDS);
2776 		(void) fprintf(stderr, PARSER_EXAMPLE);
2777 	}
2778 	exit(E_USAGE);
2779 }
2780 
2781 int
2782 yywrap()
2783 {
2784 	char buf[1024];
2785 
2786 	if (parentscript) {
2787 		yyin = parentscript;
2788 		yy_switchfilescript(yyin);
2789 		parentscript = NULL;
2790 		return (0);
2791 	} else
2792 		return (1);
2793 }
2794