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