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 }