Home | History | Annotate | Download | only in common
      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
   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 *
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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 *
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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 *
   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
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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 *
   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
   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
   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 *
   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
   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
   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