1272343Sngie%{ 2309466Sngie/* $NetBSD: testlang_parse.y,v 1.14 2015/01/04 20:19:46 christos Exp $ */ 3272343Sngie 4272343Sngie/*- 5272343Sngie * Copyright 2009 Brett Lymn <blymn@NetBSD.org> 6272343Sngie * 7272343Sngie * All rights reserved. 8272343Sngie * 9272343Sngie * This code has been donated to The NetBSD Foundation by the Author. 10272343Sngie * 11272343Sngie * Redistribution and use in source and binary forms, with or without 12272343Sngie * modification, are permitted provided that the following conditions 13272343Sngie * are met: 14272343Sngie * 1. Redistributions of source code must retain the above copyright 15272343Sngie * notice, this list of conditions and the following disclaimer. 16272343Sngie * 2. The name of the author may not be used to endorse or promote products 17272343Sngie * derived from this software withough specific prior written permission 18272343Sngie * 19272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20272343Sngie * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21272343Sngie * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22272343Sngie * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23272343Sngie * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24272343Sngie * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25272343Sngie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26272343Sngie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27272343Sngie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28272343Sngie * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29272343Sngie * 30272343Sngie * 31272343Sngie */ 32272343Sngie#include <assert.h> 33272343Sngie#include <curses.h> 34272343Sngie#include <errno.h> 35272343Sngie#include <fcntl.h> 36272343Sngie#include <err.h> 37272343Sngie#include <unistd.h> 38272343Sngie#include <poll.h> 39272343Sngie#include <stdbool.h> 40272343Sngie#include <stdio.h> 41272343Sngie#include <string.h> 42309466Sngie#include <stdlib.h> 43309466Sngie#include <limits.h> 44272343Sngie#include <time.h> 45272343Sngie#include <vis.h> 46272343Sngie#include <stdint.h> 47272343Sngie#include "returns.h" 48272343Sngie 49272343Sngie#define YYDEBUG 1 50272343Sngie 51272343Sngieextern int verbose; 52272343Sngieextern int cmdpipe[2]; 53272343Sngieextern int slvpipe[2]; 54272343Sngieextern int master; 55272343Sngieextern struct pollfd readfd; 56272343Sngieextern char *check_path; 57272343Sngieextern char *cur_file; /* from director.c */ 58272343Sngie 59272343Sngieint yylex(void); 60272343Sngie 61272343Sngiesize_t line; 62272343Sngie 63272343Sngiestatic int input_delay; 64272343Sngie 65272343Sngie/* time delay between inputs chars - default to 0.1ms minimum to prevent 66272343Sngie * problems with input tests 67272343Sngie */ 68272343Sngie#define DELAY_MIN 0.1 69272343Sngie 70272343Sngie/* time delay after a function call - allows the slave time to 71272343Sngie * run the function and output data before we do other actions. 72272343Sngie * Set this to 50ms. 73272343Sngie */ 74272343Sngie#define POST_CALL_DELAY 50 75272343Sngie 76272343Sngiestatic struct timespec delay_spec = {0, 1000 * DELAY_MIN}; 77272343Sngiestatic struct timespec delay_post_call = {0, 1000 * POST_CALL_DELAY}; 78272343Sngie 79272343Sngiestatic char *input_str; /* string to feed in as input */ 80272343Sngiestatic bool no_input; /* don't need more input */ 81272343Sngie 82272343Sngie#define READ_PIPE 0 83272343Sngie#define WRITE_PIPE 1 84272343Sngie 85272343Sngieconst char *returns_enum_names[] = { 86272343Sngie "unused", "numeric", "string", "byte", "ERR", "OK", "NULL", "not NULL", 87272343Sngie "variable", "reference", "returns count", "slave error" 88272343Sngie}; 89272343Sngie 90272343Sngietypedef enum { 91272343Sngie arg_static, 92272343Sngie arg_byte, 93272343Sngie arg_var, 94272343Sngie arg_null 95272343Sngie} args_state_t; 96272343Sngie 97272343Sngiestatic const char *args_enum_names[] = { 98272343Sngie "static", "byte", "var", "NULL" 99272343Sngie}; 100272343Sngie 101272343Sngietypedef struct { 102272343Sngie args_state_t arg_type; 103272343Sngie size_t arg_len; 104272343Sngie char *arg_string; 105272343Sngie int var_index; 106272343Sngie} args_t; 107272343Sngie 108272343Sngietypedef struct { 109272343Sngie char *function; 110272343Sngie int nrets; /* number of returns */ 111272343Sngie returns_t *returns; /* array of expected returns */ 112272343Sngie int nargs; /* number of arguments */ 113272343Sngie args_t *args; /* arguments for the call */ 114272343Sngie} cmd_line_t; 115272343Sngie 116272343Sngiestatic cmd_line_t command; 117272343Sngie 118272343Sngietypedef struct { 119272343Sngie char *name; 120272343Sngie size_t len; 121272343Sngie returns_enum_t type; 122272343Sngie void *value; 123272343Sngie} var_t; 124272343Sngie 125272343Sngiestatic size_t nvars; /* Number of declared variables */ 126272343Sngiestatic var_t *vars; /* Variables defined during the test. */ 127272343Sngie 128272343Sngiestatic int check_function_table(char *, const char *[], int); 129272343Sngiestatic int find_var_index(const char *); 130272343Sngiestatic void assign_arg(args_state_t, void *); 131272343Sngiestatic int assign_var(char *); 132272343Sngievoid init_parse_variables(int); 133272343Sngiestatic void validate(int, void *); 134272343Sngiestatic void validate_return(const char *, const char *, int); 135272343Sngiestatic void validate_variable(int, returns_enum_t, const void *, int, int); 136272343Sngiestatic void validate_byte(returns_t *, returns_t *, int); 137272343Sngiestatic void write_cmd_pipe(char *); 138272343Sngiestatic void write_cmd_pipe_args(args_state_t, void *); 139272343Sngiestatic void read_cmd_pipe(returns_t *); 140272343Sngiestatic void write_func_and_args(void); 141272343Sngiestatic void compare_streams(char *, bool); 142272343Sngiestatic void do_function_call(size_t); 143272343Sngiestatic void save_slave_output(bool); 144272343Sngiestatic void validate_type(returns_enum_t, returns_t *, int); 145272343Sngiestatic void set_var(returns_enum_t, char *, void *); 146272343Sngiestatic void validate_reference(int, void *); 147272343Sngiestatic char *numeric_or(char *, char *); 148272343Sngiestatic char *get_numeric_var(const char *); 149272343Sngiestatic void perform_delay(struct timespec *); 150272343Sngie 151272343Sngiestatic const char *input_functions[] = { 152272343Sngie "getch", "getnstr", "getstr", "mvgetnstr", "mvgetstr", "mvgetnstr", 153272343Sngie "mvgetstr", "mvscanw", "mvwscanw", "scanw", "wgetch", "wgetnstr", 154272343Sngie "wgetstr" 155272343Sngie}; 156272343Sngie 157272343Sngiestatic const unsigned ninput_functions = 158272343Sngie sizeof(input_functions) / sizeof(char *); 159272343Sngie 160272343Sngiesaved_data_t saved_output; 161272343Sngie 162272343Sngie%} 163272343Sngie 164272343Sngie%union { 165272343Sngie char *string; 166272343Sngie returns_t *retval; 167272343Sngie} 168272343Sngie 169272343Sngie%token <string> PATH 170272343Sngie%token <string> STRING 171272343Sngie%token <retval> BYTE 172272343Sngie%token <string> VARNAME 173272343Sngie%token <string> FILENAME 174272343Sngie%token <string> VARIABLE 175272343Sngie%token <string> REFERENCE 176272343Sngie%token <string> NULL_RET 177272343Sngie%token <string> NON_NULL 178272343Sngie%token <string> ERR_RET 179272343Sngie%token <string> OK_RET 180272343Sngie%token <string> numeric 181272343Sngie%token <string> DELAY 182272343Sngie%token <string> INPUT 183272343Sngie%token <string> COMPARE 184272343Sngie%token <string> COMPAREND 185272343Sngie%token <string> ASSIGN 186272343Sngie%token EOL CALL CHECK NOINPUT OR LHB RHB 187272343Sngie%token CALL2 CALL3 CALL4 DRAIN 188272343Sngie 189272343Sngie%nonassoc OR 190272343Sngie 191272343Sngie%% 192272343Sngie 193272343Sngiestatement : /* empty */ 194272343Sngie | assign statement 195272343Sngie | call statement 196272343Sngie | call2 statement 197272343Sngie | call3 statement 198272343Sngie | call4 statement 199272343Sngie | check statement 200272343Sngie | delay statement 201272343Sngie | input statement 202272343Sngie | noinput statement 203272343Sngie | compare statement 204272343Sngie | comparend statement 205272343Sngie | eol statement 206272343Sngie ; 207272343Sngie 208272343Sngieassign : ASSIGN VARNAME numeric {set_var(ret_number, $2, $3);} eol 209272343Sngie | ASSIGN VARNAME LHB expr RHB {set_var(ret_number, $2, $<string>4);} eol 210272343Sngie | ASSIGN VARNAME STRING {set_var(ret_string, $2, $3);} eol 211272343Sngie | ASSIGN VARNAME BYTE {set_var(ret_byte, $2, $3);} eol 212272343Sngie ; 213272343Sngie 214272343Sngiecall : CALL result fn_name args eol { 215272343Sngie do_function_call(1); 216272343Sngie} 217272343Sngie ; 218272343Sngie 219272343Sngiecall2 : CALL2 result result fn_name args eol { 220272343Sngie do_function_call(2); 221272343Sngie} 222272343Sngie ; 223272343Sngie 224272343Sngiecall3 : CALL3 result result result fn_name args eol { 225272343Sngie do_function_call(3); 226272343Sngie} 227272343Sngie ; 228272343Sngie 229272343Sngiecall4 : CALL4 result result result result fn_name args eol { 230272343Sngie do_function_call(4); 231272343Sngie } 232272343Sngie ; 233272343Sngie 234272343Sngiecheck : CHECK var returns eol { 235272343Sngie returns_t retvar; 236272343Sngie var_t *vptr; 237272343Sngie if (command.returns[0].return_index == -1) 238272343Sngie err(1, "Undefined variable in check statement, line %zu" 239272343Sngie " of file %s", line, cur_file); 240272343Sngie 241272343Sngie if (verbose) { 242272343Sngie fprintf(stderr, "Checking contents of variable %s for %s\n", 243272343Sngie vars[command.returns[0].return_index].name, 244272343Sngie returns_enum_names[command.returns[1].return_type]); 245272343Sngie } 246272343Sngie 247272343Sngie if (((command.returns[1].return_type == ret_byte) && 248272343Sngie (vars[command.returns[0].return_index].type != ret_byte)) || 249272343Sngie vars[command.returns[0].return_index].type != ret_string) 250272343Sngie err(1, "Var type %s (%d) does not match return type %s (%d)", 251272343Sngie returns_enum_names[ 252272343Sngie vars[command.returns[0].return_index].type], 253272343Sngie vars[command.returns[0].return_index].type, 254272343Sngie returns_enum_names[command.returns[1].return_type], 255272343Sngie command.returns[1].return_type); 256272343Sngie 257272343Sngie switch (command.returns[1].return_type) { 258272343Sngie case ret_err: 259272343Sngie validate_variable(0, ret_string, "ERR", 260272343Sngie command.returns[0].return_index, 0); 261272343Sngie break; 262272343Sngie 263272343Sngie case ret_ok: 264272343Sngie validate_variable(0, ret_string, "OK", 265272343Sngie command.returns[0].return_index, 0); 266272343Sngie break; 267272343Sngie 268272343Sngie case ret_null: 269272343Sngie validate_variable(0, ret_string, "NULL", 270272343Sngie command.returns[0].return_index, 0); 271272343Sngie break; 272272343Sngie 273272343Sngie case ret_nonnull: 274272343Sngie validate_variable(0, ret_string, "NULL", 275272343Sngie command.returns[0].return_index, 1); 276272343Sngie break; 277272343Sngie 278272343Sngie case ret_string: 279272343Sngie case ret_number: 280272343Sngie if (verbose) { 281272343Sngie fprintf(stderr, " %s == returned %s\n", 282272343Sngie (const char *)command.returns[1].return_value, 283272343Sngie (const char *) 284272343Sngie vars[command.returns[0].return_index].value); 285272343Sngie } 286272343Sngie validate_variable(0, ret_string, 287272343Sngie command.returns[1].return_value, 288272343Sngie command.returns[0].return_index, 0); 289272343Sngie break; 290272343Sngie 291272343Sngie case ret_byte: 292272343Sngie vptr = &vars[command.returns[0].return_index]; 293272343Sngie retvar.return_len = vptr->len; 294272343Sngie retvar.return_type = vptr->type; 295272343Sngie retvar.return_value = vptr->value; 296272343Sngie validate_byte(&retvar, &command.returns[1], 0); 297272343Sngie break; 298272343Sngie 299272343Sngie default: 300272343Sngie err(1, "Malformed check statement at line %zu " 301272343Sngie "of file %s", line, cur_file); 302272343Sngie break; 303272343Sngie } 304272343Sngie 305272343Sngie init_parse_variables(0); 306272343Sngie } 307272343Sngie ; 308272343Sngie 309272343Sngiedelay : DELAY numeric eol { 310272343Sngie /* set the inter-character delay */ 311272343Sngie if (sscanf($2, "%d", &input_delay) == 0) 312272343Sngie err(1, "delay specification %s could not be converted to " 313272343Sngie "numeric at line %zu of file %s", $2, line, cur_file); 314272343Sngie if (verbose) { 315272343Sngie fprintf(stderr, "Set input delay to %d ms\n", input_delay); 316272343Sngie } 317272343Sngie 318272343Sngie if (input_delay < DELAY_MIN) 319272343Sngie input_delay = DELAY_MIN; 320272343Sngie /* 321272343Sngie * Fill in the timespec structure now ready for use later. 322272343Sngie * The delay is specified in milliseconds so convert to timespec 323272343Sngie * values 324272343Sngie */ 325272343Sngie delay_spec.tv_sec = input_delay / 1000; 326272343Sngie delay_spec.tv_nsec = (input_delay - 1000 * delay_spec.tv_sec) * 1000; 327272343Sngie if (verbose) { 328272343Sngie fprintf(stderr, "set delay to %jd.%jd\n", 329272343Sngie (intmax_t)delay_spec.tv_sec, 330272343Sngie (intmax_t)delay_spec.tv_nsec); 331272343Sngie } 332272343Sngie 333272343Sngie init_parse_variables(0); 334272343Sngie } 335272343Sngie ; 336272343Sngie 337272343Sngieinput : INPUT STRING eol { 338272343Sngie if (input_str != NULL) { 339272343Sngie warnx("%s, %zu: Discarding unused input string", 340272343Sngie cur_file, line); 341272343Sngie free(input_str); 342272343Sngie } 343272343Sngie 344272343Sngie if ((input_str = malloc(strlen($2) + 1)) == NULL) 345272343Sngie err(2, "Cannot allocate memory for input string"); 346272343Sngie 347272343Sngie strlcpy(input_str, $2, strlen($2) + 1); 348272343Sngie} 349272343Sngie ; 350272343Sngie 351272343Sngie 352272343Sngienoinput : NOINPUT eol { 353272343Sngie if (input_str != NULL) { 354272343Sngie warnx("%s, %zu: Discarding unused input string", 355272343Sngie cur_file, line); 356272343Sngie free(input_str); 357272343Sngie } 358272343Sngie 359272343Sngie no_input = true; 360272343Sngie } 361272343Sngie 362272343Sngiecompare : COMPARE PATH eol 363272343Sngie | COMPARE FILENAME eol 364272343Sngie{ 365272343Sngie compare_streams($2, true); 366272343Sngie} 367272343Sngie ; 368272343Sngie 369272343Sngie 370272343Sngiecomparend : COMPAREND PATH eol 371272343Sngie | COMPAREND FILENAME eol 372272343Sngie{ 373272343Sngie compare_streams($2, false); 374272343Sngie} 375272343Sngie ; 376272343Sngie 377272343Sngie 378272343Sngieresult : returns 379272343Sngie | var 380272343Sngie | reference 381272343Sngie ; 382272343Sngie 383272343Sngiereturns : numeric { assign_rets(ret_number, $1); } 384272343Sngie | LHB expr RHB { assign_rets(ret_number, $<string>2); } 385272343Sngie | STRING { assign_rets(ret_string, $1); } 386272343Sngie | BYTE { assign_rets(ret_byte, (void *) $1); } 387272343Sngie | ERR_RET { assign_rets(ret_err, NULL); } 388272343Sngie | OK_RET { assign_rets(ret_ok, NULL); } 389272343Sngie | NULL_RET { assign_rets(ret_null, NULL); } 390272343Sngie | NON_NULL { assign_rets(ret_nonnull, NULL); } 391272343Sngie ; 392272343Sngie 393272343Sngievar : VARNAME { 394272343Sngie assign_rets(ret_var, $1); 395272343Sngie } 396272343Sngie ; 397272343Sngie 398272343Sngiereference : VARIABLE { 399272343Sngie assign_rets(ret_ref, $1); 400272343Sngie } 401272343Sngie 402272343Sngiefn_name : VARNAME { 403272343Sngie if (command.function != NULL) 404272343Sngie free(command.function); 405272343Sngie 406272343Sngie command.function = malloc(strlen($1) + 1); 407272343Sngie if (command.function == NULL) 408272343Sngie err(1, "Could not allocate memory for function name"); 409272343Sngie strcpy(command.function, $1); 410272343Sngie } 411272343Sngie ; 412272343Sngie 413272343Sngieexpr : numeric 414272343Sngie | VARIABLE 415272343Sngie { $<string>$ = get_numeric_var($1); } 416272343Sngie | expr OR expr 417272343Sngie { $<string>$ = numeric_or($<string>1, $<string>3); } 418272343Sngie ; 419272343Sngie 420272343Sngieargs : /* empty */ 421272343Sngie | LHB expr RHB { assign_arg(arg_static, $<string>2); } args 422272343Sngie | numeric { assign_arg(arg_static, $1); } args 423272343Sngie | STRING { assign_arg(arg_static, $1); } args 424272343Sngie | BYTE { assign_arg(arg_byte, $1); } args 425272343Sngie | PATH { assign_arg(arg_static, $1); } args 426272343Sngie | FILENAME { assign_arg(arg_static, $1); } args 427272343Sngie | VARNAME { assign_arg(arg_static, $1); } args 428272343Sngie | VARIABLE { assign_arg(arg_var, $1); } args 429272343Sngie | NULL_RET { assign_arg(arg_null, $1); } args 430272343Sngie ; 431272343Sngie 432272343Sngieeol : EOL 433272343Sngie ; 434272343Sngie 435272343Sngie%% 436272343Sngie 437272343Sngiestatic void 438272343Sngieexcess(const char *fname, size_t lineno, const char *func, const char *comment, 439272343Sngie const void *data, size_t datalen) 440272343Sngie{ 441272343Sngie size_t dstlen = datalen * 4 + 1; 442272343Sngie char *dst = malloc(dstlen); 443272343Sngie 444272343Sngie if (dst == NULL) 445272343Sngie err(1, "malloc"); 446272343Sngie 447272343Sngie if (strnvisx(dst, dstlen, data, datalen, VIS_WHITE | VIS_OCTAL) == -1) 448272343Sngie err(1, "strnvisx"); 449272343Sngie 450272343Sngie warnx("%s, %zu: [%s] Excess %zu bytes%s [%s]", 451272343Sngie fname, lineno, func, datalen, comment, dst); 452272343Sngie free(dst); 453272343Sngie} 454272343Sngie 455272343Sngie/* 456272343Sngie * Get the value of a variable, error if the variable has not been set or 457272343Sngie * is not a numeric type. 458272343Sngie */ 459272343Sngiestatic char * 460272343Sngieget_numeric_var(const char *var) 461272343Sngie{ 462272343Sngie int i; 463272343Sngie 464272343Sngie if ((i = find_var_index(var)) < 0) 465272343Sngie err(1, "Variable %s is undefined", var); 466272343Sngie 467272343Sngie if (vars[i].type != ret_number) 468272343Sngie err(1, "Variable %s is not a numeric type", var); 469272343Sngie 470272343Sngie return vars[i].value; 471272343Sngie} 472272343Sngie 473272343Sngie/* 474272343Sngie * Perform a bitwise OR on two numbers and return the result. 475272343Sngie */ 476272343Sngiestatic char * 477272343Sngienumeric_or(char *n1, char *n2) 478272343Sngie{ 479272343Sngie unsigned long i1, i2, result; 480272343Sngie char *ret; 481272343Sngie 482272343Sngie i1 = strtoul(n1, NULL, 10); 483272343Sngie i2 = strtoul(n2, NULL, 10); 484272343Sngie 485272343Sngie result = i1 | i2; 486272343Sngie asprintf(&ret, "%lu", result); 487272343Sngie 488272343Sngie if (verbose) { 489272343Sngie fprintf(stderr, "numeric or of 0x%lx (%s) and 0x%lx (%s)" 490272343Sngie " results in 0x%lx (%s)\n", 491272343Sngie i1, n1, i2, n2, result, ret); 492272343Sngie } 493272343Sngie 494272343Sngie return ret; 495272343Sngie} 496272343Sngie 497272343Sngie/* 498272343Sngie * Sleep for the specified time, handle the sleep getting interrupted 499272343Sngie * by a signal. 500272343Sngie */ 501272343Sngiestatic void 502272343Sngieperform_delay(struct timespec *ts) 503272343Sngie{ 504272343Sngie struct timespec delay_copy, delay_remainder; 505272343Sngie 506272343Sngie delay_copy = *ts; 507272343Sngie while (nanosleep(&delay_copy, &delay_remainder) < 0) { 508272343Sngie if (errno != EINTR) 509272343Sngie err(2, "nanosleep returned error"); 510272343Sngie delay_copy = delay_remainder; 511272343Sngie } 512272343Sngie} 513272343Sngie 514272343Sngie/* 515272343Sngie * Assign the value given to the named variable. 516272343Sngie */ 517272343Sngiestatic void 518272343Sngieset_var(returns_enum_t type, char *name, void *value) 519272343Sngie{ 520272343Sngie int i; 521272343Sngie char *number; 522272343Sngie returns_t *ret; 523272343Sngie 524272343Sngie i = find_var_index(name); 525272343Sngie if (i < 0) 526272343Sngie i = assign_var(name); 527272343Sngie 528272343Sngie vars[i].type = type; 529272343Sngie if ((type == ret_number) || (type == ret_string)) { 530272343Sngie number = value; 531272343Sngie vars[i].len = strlen(number) + 1; 532272343Sngie vars[i].value = malloc(vars[i].len + 1); 533272343Sngie if (vars[i].value == NULL) 534272343Sngie err(1, "Could not malloc memory for assign string"); 535272343Sngie strcpy(vars[i].value, number); 536272343Sngie } else { 537272343Sngie /* can only be a byte value */ 538272343Sngie ret = value; 539272343Sngie vars[i].len = ret->return_len; 540272343Sngie vars[i].value = malloc(vars[i].len); 541272343Sngie if (vars[i].value == NULL) 542272343Sngie err(1, "Could not malloc memory to assign byte string"); 543272343Sngie memcpy(vars[i].value, ret->return_value, vars[i].len); 544272343Sngie } 545272343Sngie} 546272343Sngie 547272343Sngie/* 548272343Sngie * Add a new variable to the vars array, the value will be assigned later, 549272343Sngie * when a test function call returns. 550272343Sngie */ 551272343Sngiestatic int 552272343Sngieassign_var(char *varname) 553272343Sngie{ 554272343Sngie var_t *temp; 555272343Sngie char *name; 556272343Sngie 557272343Sngie if ((name = malloc(strlen(varname) + 1)) == NULL) 558272343Sngie err(1, "Alloc of varname failed"); 559272343Sngie 560272343Sngie if ((temp = realloc(vars, sizeof(*temp) * (nvars + 1))) == NULL) { 561272343Sngie free(name); 562272343Sngie err(1, "Realloc of vars array failed"); 563272343Sngie } 564272343Sngie 565272343Sngie strcpy(name, varname); 566272343Sngie vars = temp; 567272343Sngie vars[nvars].name = name; 568272343Sngie vars[nvars].len = 0; 569272343Sngie vars[nvars].value = NULL; 570272343Sngie nvars++; 571272343Sngie 572272343Sngie return (nvars - 1); 573272343Sngie} 574272343Sngie 575272343Sngie/* 576272343Sngie * Allocate and assign a new argument of the given type. 577272343Sngie */ 578272343Sngiestatic void 579272343Sngieassign_arg(args_state_t arg_type, void *arg) 580272343Sngie{ 581272343Sngie args_t *temp, cur; 582272343Sngie char *str = arg; 583272343Sngie returns_t *ret; 584272343Sngie 585272343Sngie if (verbose) { 586272343Sngie fprintf(stderr, "function is >%s<, adding arg >%s< type %s\n", 587272343Sngie command.function, str, args_enum_names[arg_type]); 588272343Sngie } 589272343Sngie 590272343Sngie cur.arg_type = arg_type; 591272343Sngie switch (arg_type) { 592272343Sngie case arg_var: 593272343Sngie cur.var_index = find_var_index(arg); 594272343Sngie if (cur.var_index < 0) 595272343Sngie err(1, "Invalid variable %s at line %zu of file %s", 596272343Sngie str, line, cur_file); 597272343Sngie cur.arg_type = ret_string; 598272343Sngie break; 599272343Sngie 600272343Sngie case arg_byte: 601272343Sngie ret = arg; 602272343Sngie cur.arg_len = ret->return_len; 603272343Sngie cur.arg_string = malloc(cur.arg_len); 604272343Sngie if (cur.arg_string == NULL) 605272343Sngie err(1, "Could not malloc memory for arg bytes"); 606272343Sngie memcpy(cur.arg_string, ret->return_value, cur.arg_len); 607272343Sngie break; 608272343Sngie 609272343Sngie case arg_null: 610272343Sngie cur.arg_len = 0; 611272343Sngie cur.arg_string = NULL; 612272343Sngie break; 613272343Sngie 614272343Sngie default: 615272343Sngie cur.arg_len = strlen(str); 616272343Sngie cur.arg_string = malloc(cur.arg_len + 1); 617272343Sngie if (cur.arg_string == NULL) 618272343Sngie err(1, "Could not malloc memory for arg string"); 619272343Sngie strcpy(cur.arg_string, arg); 620272343Sngie } 621272343Sngie 622272343Sngie temp = realloc(command.args, sizeof(*temp) * (command.nargs + 1)); 623272343Sngie if (temp == NULL) 624272343Sngie err(1, "Failed to reallocate args"); 625272343Sngie command.args = temp; 626272343Sngie memcpy(&command.args[command.nargs], &cur, sizeof(args_t)); 627272343Sngie command.nargs++; 628272343Sngie} 629272343Sngie 630272343Sngie/* 631272343Sngie * Allocate and assign a new return. 632272343Sngie */ 633272343Sngiestatic void 634272343Sngieassign_rets(returns_enum_t ret_type, void *ret) 635272343Sngie{ 636272343Sngie returns_t *temp, cur; 637272343Sngie char *ret_str; 638272343Sngie returns_t *ret_ret; 639272343Sngie 640272343Sngie cur.return_type = ret_type; 641272343Sngie if (ret_type != ret_var) { 642272343Sngie if ((ret_type == ret_number) || (ret_type == ret_string)) { 643272343Sngie ret_str = ret; 644272343Sngie cur.return_len = strlen(ret_str) + 1; 645272343Sngie cur.return_value = malloc(cur.return_len + 1); 646272343Sngie if (cur.return_value == NULL) 647272343Sngie err(1, 648272343Sngie "Could not malloc memory for arg string"); 649272343Sngie strcpy(cur.return_value, ret_str); 650272343Sngie } else if (ret_type == ret_byte) { 651272343Sngie ret_ret = ret; 652272343Sngie cur.return_len = ret_ret->return_len; 653272343Sngie cur.return_value = malloc(cur.return_len); 654272343Sngie if (cur.return_value == NULL) 655272343Sngie err(1, 656272343Sngie "Could not malloc memory for byte string"); 657272343Sngie memcpy(cur.return_value, ret_ret->return_value, 658272343Sngie cur.return_len); 659272343Sngie } else if (ret_type == ret_ref) { 660272343Sngie if ((cur.return_index = find_var_index(ret)) < 0) 661272343Sngie err(1, "Undefined variable reference"); 662272343Sngie } 663272343Sngie } else { 664272343Sngie cur.return_index = find_var_index(ret); 665272343Sngie if (cur.return_index < 0) 666272343Sngie cur.return_index = assign_var(ret); 667272343Sngie } 668272343Sngie 669272343Sngie temp = realloc(command.returns, sizeof(*temp) * (command.nrets + 1)); 670272343Sngie if (temp == NULL) 671272343Sngie err(1, "Failed to reallocate returns"); 672272343Sngie command.returns = temp; 673272343Sngie memcpy(&command.returns[command.nrets], &cur, sizeof(returns_t)); 674272343Sngie command.nrets++; 675272343Sngie} 676272343Sngie 677272343Sngie/* 678272343Sngie * Find the given variable name in the var array and return the i 679272343Sngie * return -1 if var is not found. 680272343Sngie */ 681272343Sngiestatic int 682272343Sngiefind_var_index(const char *var_name) 683272343Sngie{ 684272343Sngie int result; 685272343Sngie size_t i; 686272343Sngie 687272343Sngie result = -1; 688272343Sngie 689272343Sngie for (i = 0; i < nvars; i++) { 690272343Sngie if (strcmp(var_name, vars[i].name) == 0) { 691272343Sngie result = i; 692272343Sngie break; 693272343Sngie } 694272343Sngie } 695272343Sngie 696272343Sngie return result; 697272343Sngie} 698272343Sngie 699272343Sngie/* 700272343Sngie * Check the given function name in the given table of names, return 1 if 701272343Sngie * there is a match. 702272343Sngie */ 703272343Sngiestatic int check_function_table(char *function, const char *table[], 704272343Sngie int nfunctions) 705272343Sngie{ 706272343Sngie int i; 707272343Sngie 708272343Sngie for (i = 0; i < nfunctions; i++) { 709272343Sngie if ((strlen(function) == strlen(table[i])) && 710272343Sngie (strcmp(function, table[i]) == 0)) 711272343Sngie return 1; 712272343Sngie } 713272343Sngie 714272343Sngie return 0; 715272343Sngie} 716272343Sngie 717272343Sngie/* 718272343Sngie * Compare the output from the slave against the given file and report 719272343Sngie * any differences. 720272343Sngie */ 721272343Sngiestatic void 722272343Sngiecompare_streams(char *filename, bool discard) 723272343Sngie{ 724272343Sngie char check_file[PATH_MAX], drain[100], ref, data; 725272343Sngie struct pollfd fds[2]; 726272343Sngie int nfd, check_fd; 727272343Sngie ssize_t result; 728272343Sngie size_t offs; 729272343Sngie 730272343Sngie /* 731272343Sngie * Don't prepend check path iff check file has an absolute 732272343Sngie * path. 733272343Sngie */ 734272343Sngie if (filename[0] != '/') { 735272343Sngie if (strlcpy(check_file, check_path, sizeof(check_file)) 736272343Sngie >= sizeof(check_file)) 737272343Sngie err(2, "CHECK_PATH too long"); 738272343Sngie 739272343Sngie if (strlcat(check_file, "/", sizeof(check_file)) 740272343Sngie >= sizeof(check_file)) 741272343Sngie err(2, "Could not append / to check file path"); 742272343Sngie } else { 743272343Sngie check_file[0] = '\0'; 744272343Sngie } 745272343Sngie 746272343Sngie if (strlcat(check_file, filename, sizeof(check_file)) 747272343Sngie >= sizeof(check_file)) 748272343Sngie err(2, "Path to check file path overflowed"); 749272343Sngie 750272343Sngie if ((check_fd = open(check_file, O_RDONLY, 0)) < 0) 751272343Sngie err(2, "failed to open file %s line %zu of file %s", 752272343Sngie check_file, line, cur_file); 753272343Sngie 754272343Sngie fds[0].fd = check_fd; 755272343Sngie fds[0].events = POLLIN; 756272343Sngie fds[1].fd = master; 757272343Sngie fds[1].events = POLLIN; 758272343Sngie 759272343Sngie nfd = 2; 760272343Sngie /* 761272343Sngie * if we have saved output then only check for data in the 762272343Sngie * reference file since the slave data may already be drained. 763272343Sngie */ 764272343Sngie if (saved_output.count > 0) 765272343Sngie nfd = 1; 766272343Sngie 767272343Sngie offs = 0; 768272343Sngie while (poll(fds, nfd, 500) == nfd) { 769272343Sngie if (fds[0].revents & POLLIN) { 770272343Sngie if ((result = read(check_fd, &ref, 1)) < 1) { 771272343Sngie if (result != 0) { 772272343Sngie err(2, 773272343Sngie "Bad read on file %s", check_file); 774272343Sngie } else { 775272343Sngie break; 776272343Sngie } 777272343Sngie } 778272343Sngie } 779272343Sngie 780272343Sngie if (saved_output.count > 0) { 781272343Sngie data = saved_output.data[saved_output.readp]; 782272343Sngie saved_output.count--; 783272343Sngie saved_output.readp++; 784272343Sngie /* run out of saved data, switch to file */ 785272343Sngie if (saved_output.count == 0) 786272343Sngie nfd = 2; 787272343Sngie } else { 788272343Sngie if (fds[0].revents & POLLIN) { 789272343Sngie if (read(master, &data, 1) < 1) 790272343Sngie err(2, "Bad read on slave pty"); 791272343Sngie } else 792272343Sngie continue; 793272343Sngie } 794272343Sngie 795272343Sngie if (verbose) { 796272343Sngie fprintf(stderr, "Comparing reference byte 0x%x (%c)" 797272343Sngie " against slave byte 0x%x (%c)\n", 798272343Sngie ref, (ref >= ' ') ? ref : '-', 799272343Sngie data, (data >= ' ' )? data : '-'); 800272343Sngie } 801272343Sngie 802272343Sngie if (ref != data) { 803272343Sngie errx(2, "%s, %zu: refresh data from slave does " 804272343Sngie "not match expected from file %s offs %zu " 805272343Sngie "[reference 0x%x (%c) != slave 0x%x (%c)]", 806272343Sngie cur_file, line, check_file, offs, 807272343Sngie ref, (ref >= ' ') ? ref : '-', 808272343Sngie data, (data >= ' ') ? data : '-'); 809272343Sngie } 810272343Sngie 811272343Sngie offs++; 812272343Sngie } 813272343Sngie 814272343Sngie 815272343Sngie if (saved_output.count > 0) 816272343Sngie excess(cur_file, line, __func__, " from slave", 817272343Sngie &saved_output.data[saved_output.readp], saved_output.count); 818272343Sngie 819272343Sngie /* discard any excess saved output if required */ 820272343Sngie if (discard) { 821272343Sngie saved_output.count = 0; 822272343Sngie saved_output.readp = 0; 823272343Sngie } 824272343Sngie 825272343Sngie if ((result = poll(&fds[0], 2, 0)) != 0) { 826272343Sngie if (result == -1) 827272343Sngie err(2, "poll of file descriptors failed"); 828272343Sngie 829272343Sngie if ((fds[1].revents & POLLIN) == POLLIN) { 830272343Sngie save_slave_output(true); 831272343Sngie } else if ((fds[0].revents & POLLIN) == POLLIN) { 832272343Sngie /* 833272343Sngie * handle excess in file if it exists. Poll 834272343Sngie * says there is data until EOF is read. 835272343Sngie * Check next read is EOF, if it is not then 836272343Sngie * the file really has more data than the 837272343Sngie * slave produced so flag this as a warning. 838272343Sngie */ 839272343Sngie result = read(check_fd, drain, sizeof(drain)); 840272343Sngie if (result == -1) 841272343Sngie err(1, "read of data file failed"); 842272343Sngie 843272343Sngie if (result > 0) { 844272343Sngie excess(check_file, 0, __func__, "", drain, 845272343Sngie result); 846272343Sngie } 847272343Sngie } 848272343Sngie } 849272343Sngie 850272343Sngie close(check_fd); 851272343Sngie} 852272343Sngie 853272343Sngie/* 854272343Sngie * Pass a function call and arguments to the slave and wait for the 855272343Sngie * results. The variable nresults determines how many returns we expect 856272343Sngie * back from the slave. These results will be validated against the 857272343Sngie * expected returns or assigned to variables. 858272343Sngie */ 859272343Sngiestatic void 860272343Sngiedo_function_call(size_t nresults) 861272343Sngie{ 862272343Sngie#define MAX_RESULTS 4 863272343Sngie char *p; 864272343Sngie int do_input; 865272343Sngie size_t i; 866272343Sngie struct pollfd fds[3]; 867272343Sngie returns_t response[MAX_RESULTS], returns_count; 868272343Sngie assert(nresults <= MAX_RESULTS); 869272343Sngie 870272343Sngie do_input = check_function_table(command.function, input_functions, 871272343Sngie ninput_functions); 872272343Sngie 873272343Sngie write_func_and_args(); 874272343Sngie 875272343Sngie /* 876272343Sngie * We should get the number of returns back here, grab it before 877272343Sngie * doing input otherwise it will confuse the input poll 878272343Sngie */ 879272343Sngie read_cmd_pipe(&returns_count); 880272343Sngie if (returns_count.return_type != ret_count) 881272343Sngie err(2, "expected return type of ret_count but received %s", 882272343Sngie returns_enum_names[returns_count.return_type]); 883272343Sngie 884272343Sngie perform_delay(&delay_post_call); /* let slave catch up */ 885272343Sngie 886272343Sngie if (verbose) { 887272343Sngie fprintf(stderr, "Expect %zu results from slave, slave " 888272343Sngie "reported %zu\n", nresults, returns_count.return_len); 889272343Sngie } 890272343Sngie 891272343Sngie if ((no_input == false) && (do_input == 1)) { 892272343Sngie if (verbose) { 893272343Sngie fprintf(stderr, "doing input with inputstr >%s<\n", 894272343Sngie input_str); 895272343Sngie } 896272343Sngie 897272343Sngie if (input_str == NULL) 898272343Sngie errx(2, "%s, %zu: Call to input function " 899272343Sngie "but no input defined", cur_file, line); 900272343Sngie 901272343Sngie fds[0].fd = slvpipe[READ_PIPE]; 902272343Sngie fds[0].events = POLLIN; 903272343Sngie fds[1].fd = master; 904272343Sngie fds[1].events = POLLOUT; 905272343Sngie p = input_str; 906272343Sngie save_slave_output(false); 907272343Sngie while(*p != '\0') { 908272343Sngie perform_delay(&delay_spec); 909272343Sngie 910272343Sngie if (poll(fds, 2, 0) < 0) 911272343Sngie err(2, "poll failed"); 912272343Sngie if (fds[0].revents & POLLIN) { 913272343Sngie warnx("%s, %zu: Slave function " 914272343Sngie "returned before end of input string", 915272343Sngie cur_file, line); 916272343Sngie break; 917272343Sngie } 918272343Sngie if ((fds[1].revents & POLLOUT) == 0) 919272343Sngie continue; 920272343Sngie if (verbose) { 921272343Sngie fprintf(stderr, "Writing char >%c< to slave\n", 922272343Sngie *p); 923272343Sngie } 924272343Sngie if (write(master, p, 1) != 1) { 925272343Sngie warn("%s, %zu: Slave function write error", 926272343Sngie cur_file, line); 927272343Sngie break; 928272343Sngie } 929272343Sngie p++; 930272343Sngie 931272343Sngie } 932272343Sngie save_slave_output(false); 933272343Sngie 934272343Sngie if (verbose) { 935272343Sngie fprintf(stderr, "Input done.\n"); 936272343Sngie } 937272343Sngie 938272343Sngie /* done with the input string, free the resources */ 939272343Sngie free(input_str); 940272343Sngie input_str = NULL; 941272343Sngie } 942272343Sngie 943272343Sngie if (verbose) { 944272343Sngie fds[0].fd = slvpipe[READ_PIPE]; 945272343Sngie fds[0].events = POLLIN; 946272343Sngie 947272343Sngie fds[1].fd = slvpipe[WRITE_PIPE]; 948272343Sngie fds[1].events = POLLOUT; 949272343Sngie 950272343Sngie fds[2].fd = master; 951272343Sngie fds[2].events = POLLIN | POLLOUT; 952272343Sngie 953272343Sngie i = poll(&fds[0], 3, 1000); 954272343Sngie fprintf(stderr, "Poll returned %zu\n", i); 955272343Sngie for (i = 0; i < 3; i++) { 956272343Sngie fprintf(stderr, "revents for fd[%zu] = 0x%x\n", 957272343Sngie i, fds[i].revents); 958272343Sngie } 959272343Sngie } 960272343Sngie 961272343Sngie /* drain any trailing output */ 962272343Sngie save_slave_output(false); 963272343Sngie 964272343Sngie for (i = 0; i < returns_count.return_len; i++) { 965272343Sngie read_cmd_pipe(&response[i]); 966272343Sngie } 967272343Sngie 968272343Sngie /* 969272343Sngie * Check for a slave error in the first return slot, if the 970272343Sngie * slave errored then we may not have the number of returns we 971272343Sngie * expect but in this case we should report the slave error 972272343Sngie * instead of a return count mismatch. 973272343Sngie */ 974272343Sngie if ((returns_count.return_len > 0) && 975272343Sngie (response[0].return_type == ret_slave_error)) 976272343Sngie err(2, "Slave returned error: %s", 977272343Sngie (const char *)response[0].return_value); 978272343Sngie 979272343Sngie if (returns_count.return_len != nresults) 980272343Sngie err(2, "Incorrect number of returns from slave, expected %zu " 981272343Sngie "but received %zu", nresults, returns_count.return_len); 982272343Sngie 983272343Sngie if (verbose) { 984272343Sngie for (i = 0; i < nresults; i++) { 985272343Sngie if ((response[i].return_type != ret_byte) && 986272343Sngie (response[i].return_type != ret_err) && 987272343Sngie (response[i].return_type != ret_ok)) 988272343Sngie fprintf(stderr, 989272343Sngie "received response >%s< " 990272343Sngie "expected", 991272343Sngie (const char *)response[i].return_value); 992272343Sngie else 993272343Sngie fprintf(stderr, "received"); 994272343Sngie 995272343Sngie fprintf(stderr, " return_type %s\n", 996272343Sngie returns_enum_names[command.returns[i].return_type]); 997272343Sngie } 998272343Sngie } 999272343Sngie 1000272343Sngie for (i = 0; i < nresults; i++) { 1001272343Sngie if (command.returns[i].return_type != ret_var) { 1002272343Sngie validate(i, &response[i]); 1003272343Sngie } else { 1004272343Sngie vars[command.returns[i].return_index].len = 1005272343Sngie response[i].return_len; 1006272343Sngie vars[command.returns[i].return_index].value = 1007272343Sngie response[i].return_value; 1008272343Sngie vars[command.returns[i].return_index].type = 1009272343Sngie response[i].return_type; 1010272343Sngie } 1011272343Sngie } 1012272343Sngie 1013272343Sngie if (verbose && (saved_output.count > 0)) 1014272343Sngie excess(cur_file, line, __func__, " from slave", 1015272343Sngie &saved_output.data[saved_output.readp], saved_output.count); 1016272343Sngie 1017272343Sngie init_parse_variables(0); 1018272343Sngie} 1019272343Sngie 1020272343Sngie/* 1021272343Sngie * Write the function and command arguments to the command pipe. 1022272343Sngie */ 1023272343Sngiestatic void 1024272343Sngiewrite_func_and_args(void) 1025272343Sngie{ 1026272343Sngie int i; 1027272343Sngie 1028272343Sngie if (verbose) { 1029272343Sngie fprintf(stderr, "calling function >%s<\n", command.function); 1030272343Sngie } 1031272343Sngie 1032272343Sngie write_cmd_pipe(command.function); 1033272343Sngie for (i = 0; i < command.nargs; i++) { 1034272343Sngie if (command.args[i].arg_type == arg_var) 1035272343Sngie write_cmd_pipe_args(command.args[i].arg_type, 1036272343Sngie &vars[command.args[i].var_index]); 1037272343Sngie else 1038272343Sngie write_cmd_pipe_args(command.args[i].arg_type, 1039272343Sngie &command.args[i]); 1040272343Sngie } 1041272343Sngie 1042272343Sngie write_cmd_pipe(NULL); /* signal end of arguments */ 1043272343Sngie} 1044272343Sngie 1045272343Sngie/* 1046272343Sngie * Initialise the command structure - if initial is non-zero then just set 1047272343Sngie * everything to sane values otherwise free any memory that was allocated 1048272343Sngie * when building the structure. 1049272343Sngie */ 1050272343Sngievoid 1051272343Sngieinit_parse_variables(int initial) 1052272343Sngie{ 1053272343Sngie int i, result; 1054272343Sngie struct pollfd slave_pty; 1055272343Sngie 1056272343Sngie if (initial == 0) { 1057272343Sngie free(command.function); 1058272343Sngie for (i = 0; i < command.nrets; i++) { 1059272343Sngie if (command.returns[i].return_type == ret_number) 1060272343Sngie free(command.returns[i].return_value); 1061272343Sngie } 1062272343Sngie free(command.returns); 1063272343Sngie 1064272343Sngie for (i = 0; i < command.nargs; i++) { 1065272343Sngie if (command.args[i].arg_type != arg_var) 1066272343Sngie free(command.args[i].arg_string); 1067272343Sngie } 1068272343Sngie free(command.args); 1069272343Sngie } else { 1070272343Sngie line = 0; 1071272343Sngie input_delay = 0; 1072272343Sngie vars = NULL; 1073272343Sngie nvars = 0; 1074272343Sngie input_str = NULL; 1075272343Sngie saved_output.allocated = 0; 1076272343Sngie saved_output.count = 0; 1077272343Sngie saved_output.readp = 0; 1078272343Sngie saved_output.data = NULL; 1079272343Sngie } 1080272343Sngie 1081272343Sngie no_input = false; 1082272343Sngie command.function = NULL; 1083272343Sngie command.nargs = 0; 1084272343Sngie command.args = NULL; 1085272343Sngie command.nrets = 0; 1086272343Sngie command.returns = NULL; 1087272343Sngie 1088272343Sngie /* 1089272343Sngie * Check the slave pty for stray output from the slave, at this 1090272343Sngie * point we should not see any data as it should have been 1091272343Sngie * consumed by the test functions. If we see data then we have 1092272343Sngie * either a bug or are not handling an output generating function 1093272343Sngie * correctly. 1094272343Sngie */ 1095272343Sngie slave_pty.fd = master; 1096272343Sngie slave_pty.events = POLLIN; 1097272343Sngie result = poll(&slave_pty, 1, 0); 1098272343Sngie 1099272343Sngie if (result < 0) 1100272343Sngie err(2, "Poll of slave pty failed"); 1101272343Sngie else if (result > 0) 1102272343Sngie warnx("%s, %zu: Unexpected data from slave", cur_file, line); 1103272343Sngie} 1104272343Sngie 1105272343Sngie/* 1106272343Sngie * Validate the response against the expected return. The variable 1107272343Sngie * i is the i into the rets array in command. 1108272343Sngie */ 1109272343Sngiestatic void 1110272343Sngievalidate(int i, void *data) 1111272343Sngie{ 1112272343Sngie char *response; 1113272343Sngie returns_t *byte_response; 1114272343Sngie 1115272343Sngie byte_response = data; 1116272343Sngie if ((command.returns[i].return_type != ret_byte) && 1117272343Sngie (command.returns[i].return_type != ret_err) && 1118272343Sngie (command.returns[i].return_type != ret_ok)) { 1119272343Sngie if ((byte_response->return_type == ret_byte) || 1120272343Sngie (byte_response->return_type == ret_err) || 1121272343Sngie (byte_response->return_type == ret_ok)) 1122272343Sngie err(1, "%s: expecting type %s, received type %s" 1123272343Sngie " at line %zu of file %s", __func__, 1124272343Sngie returns_enum_names[command.returns[i].return_type], 1125272343Sngie returns_enum_names[byte_response->return_type], 1126272343Sngie line, cur_file); 1127272343Sngie 1128272343Sngie response = byte_response->return_value; 1129272343Sngie } 1130272343Sngie 1131272343Sngie switch (command.returns[i].return_type) { 1132272343Sngie case ret_err: 1133272343Sngie validate_type(ret_err, byte_response, 0); 1134272343Sngie break; 1135272343Sngie 1136272343Sngie case ret_ok: 1137272343Sngie validate_type(ret_ok, byte_response, 0); 1138272343Sngie break; 1139272343Sngie 1140272343Sngie case ret_null: 1141272343Sngie validate_return("NULL", response, 0); 1142272343Sngie break; 1143272343Sngie 1144272343Sngie case ret_nonnull: 1145272343Sngie validate_return("NULL", response, 1); 1146272343Sngie break; 1147272343Sngie 1148272343Sngie case ret_string: 1149272343Sngie case ret_number: 1150272343Sngie validate_return(command.returns[i].return_value, 1151272343Sngie response, 0); 1152272343Sngie break; 1153272343Sngie 1154272343Sngie case ret_ref: 1155272343Sngie validate_reference(i, response); 1156272343Sngie break; 1157272343Sngie 1158272343Sngie case ret_byte: 1159272343Sngie validate_byte(&command.returns[i], byte_response, 0); 1160272343Sngie break; 1161272343Sngie 1162272343Sngie default: 1163272343Sngie err(1, "Malformed statement at line %zu of file %s", 1164272343Sngie line, cur_file); 1165272343Sngie break; 1166272343Sngie } 1167272343Sngie} 1168272343Sngie 1169272343Sngie/* 1170272343Sngie * Validate the return against the contents of a variable. 1171272343Sngie */ 1172272343Sngiestatic void 1173272343Sngievalidate_reference(int i, void *data) 1174272343Sngie{ 1175272343Sngie char *response; 1176272343Sngie returns_t *byte_response; 1177272343Sngie var_t *varp; 1178272343Sngie 1179272343Sngie varp = &vars[command.returns[i].return_index]; 1180272343Sngie 1181272343Sngie byte_response = data; 1182272343Sngie if (command.returns[i].return_type != ret_byte) 1183272343Sngie response = data; 1184272343Sngie 1185272343Sngie if (verbose) { 1186272343Sngie fprintf(stderr, 1187272343Sngie "%s: return type of %s, value %s \n", __func__, 1188272343Sngie returns_enum_names[varp->type], 1189272343Sngie (const char *)varp->value); 1190272343Sngie } 1191272343Sngie 1192272343Sngie switch (varp->type) { 1193272343Sngie case ret_string: 1194272343Sngie case ret_number: 1195272343Sngie validate_return(varp->value, response, 0); 1196272343Sngie break; 1197272343Sngie 1198272343Sngie case ret_byte: 1199272343Sngie validate_byte(varp->value, byte_response, 0); 1200272343Sngie break; 1201272343Sngie 1202272343Sngie default: 1203272343Sngie err(1, 1204272343Sngie "Invalid return type for reference at line %zu of file %s", 1205272343Sngie line, cur_file); 1206272343Sngie break; 1207272343Sngie } 1208272343Sngie} 1209272343Sngie 1210272343Sngie/* 1211272343Sngie * Validate the return type against the expected type, throw an error 1212272343Sngie * if they don't match. 1213272343Sngie */ 1214272343Sngiestatic void 1215272343Sngievalidate_type(returns_enum_t expected, returns_t *value, int check) 1216272343Sngie{ 1217272343Sngie if (((check == 0) && (expected != value->return_type)) || 1218272343Sngie ((check == 1) && (expected == value->return_type))) 1219272343Sngie err(1, "Validate expected type %s %s %s line %zu of file %s", 1220272343Sngie returns_enum_names[expected], 1221272343Sngie (check == 0)? "matching" : "not matching", 1222272343Sngie returns_enum_names[value->return_type], line, cur_file); 1223272343Sngie 1224272343Sngie if (verbose) { 1225272343Sngie fprintf(stderr, "Validate expected type %s %s %s line %zu" 1226272343Sngie " of file %s\n", 1227272343Sngie returns_enum_names[expected], 1228272343Sngie (check == 0)? "matching" : "not matching", 1229272343Sngie returns_enum_names[value->return_type], line, cur_file); 1230272343Sngie } 1231272343Sngie} 1232272343Sngie 1233272343Sngie/* 1234272343Sngie * Validate the return value against the expected value, throw an error 1235272343Sngie * if they don't match. 1236272343Sngie */ 1237272343Sngiestatic void 1238272343Sngievalidate_return(const char *expected, const char *value, int check) 1239272343Sngie{ 1240272343Sngie if (((check == 0) && strcmp(expected, value) != 0) || 1241272343Sngie ((check == 1) && strcmp(expected, value) == 0)) 1242272343Sngie errx(1, "Validate expected %s %s %s line %zu of file %s", 1243272343Sngie expected, 1244272343Sngie (check == 0)? "matching" : "not matching", value, 1245272343Sngie line, cur_file); 1246272343Sngie if (verbose) { 1247272343Sngie fprintf(stderr, "Validated expected value %s %s %s " 1248272343Sngie "at line %zu of file %s\n", expected, 1249272343Sngie (check == 0)? "matches" : "does not match", 1250272343Sngie value, line, cur_file); 1251272343Sngie } 1252272343Sngie} 1253272343Sngie 1254272343Sngie/* 1255272343Sngie * Validate the return value against the expected value, throw an error 1256272343Sngie * if they don't match expectations. 1257272343Sngie */ 1258272343Sngiestatic void 1259272343Sngievalidate_byte(returns_t *expected, returns_t *value, int check) 1260272343Sngie{ 1261272343Sngie char *ch; 1262272343Sngie size_t i; 1263272343Sngie 1264272343Sngie if (verbose) { 1265272343Sngie ch = value->return_value; 1266272343Sngie fprintf(stderr, "checking returned byte stream: "); 1267272343Sngie for (i = 0; i < value->return_len; i++) 1268272343Sngie fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]); 1269272343Sngie fprintf(stderr, "\n"); 1270272343Sngie 1271272343Sngie fprintf(stderr, "%s byte stream: ", 1272272343Sngie (check == 0)? "matches" : "does not match"); 1273272343Sngie ch = (char *) expected->return_value; 1274272343Sngie for (i = 0; i < expected->return_len; i++) 1275272343Sngie fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]); 1276272343Sngie fprintf(stderr, "\n"); 1277272343Sngie } 1278272343Sngie 1279272343Sngie /* 1280272343Sngie * No chance of a match if lengths differ... 1281272343Sngie */ 1282272343Sngie if ((check == 0) && (expected->return_len != value->return_len)) 1283272343Sngie errx(1, "Byte validation failed, length mismatch, expected %zu," 1284272343Sngie "received %zu", expected->return_len, value->return_len); 1285272343Sngie 1286272343Sngie /* 1287272343Sngie * If check is 0 then we want to throw an error IFF the byte streams 1288272343Sngie * do not match, if check is 1 then throw an error if the byte 1289272343Sngie * streams match. 1290272343Sngie */ 1291272343Sngie if (((check == 0) && memcmp(expected->return_value, value->return_value, 1292272343Sngie value->return_len) != 0) || 1293272343Sngie ((check == 1) && (expected->return_len == value->return_len) && 1294272343Sngie memcmp(expected->return_value, value->return_value, 1295272343Sngie value->return_len) == 0)) 1296272343Sngie errx(1, "Validate expected %s byte stream at line %zu" 1297272343Sngie "of file %s", 1298272343Sngie (check == 0)? "matching" : "not matching", line, cur_file); 1299272343Sngie if (verbose) { 1300272343Sngie fprintf(stderr, "Validated expected %s byte stream " 1301272343Sngie "at line %zu of file %s\n", 1302272343Sngie (check == 0)? "matching" : "not matching", 1303272343Sngie line, cur_file); 1304272343Sngie } 1305272343Sngie} 1306272343Sngie 1307272343Sngie/* 1308272343Sngie * Validate the variable at i against the expected value, throw an 1309272343Sngie * error if they don't match, if check is non-zero then the match is 1310272343Sngie * negated. 1311272343Sngie */ 1312272343Sngiestatic void 1313272343Sngievalidate_variable(int ret, returns_enum_t type, const void *value, int i, 1314272343Sngie int check) 1315272343Sngie{ 1316272343Sngie returns_t *retval; 1317272343Sngie var_t *varptr; 1318272343Sngie 1319272343Sngie retval = &command.returns[ret]; 1320272343Sngie varptr = &vars[command.returns[ret].return_index]; 1321272343Sngie 1322272343Sngie if (varptr->value == NULL) 1323272343Sngie err(1, "Variable %s has no value assigned to it", varptr->name); 1324272343Sngie 1325272343Sngie 1326272343Sngie if (varptr->type != type) 1327272343Sngie err(1, "Variable %s is not the expected type", varptr->name); 1328272343Sngie 1329272343Sngie if (type != ret_byte) { 1330272343Sngie if ((((check == 0) && strcmp(value, varptr->value) != 0)) 1331272343Sngie || ((check == 1) && strcmp(value, varptr->value) == 0)) 1332272343Sngie err(1, "Variable %s contains %s instead of %s" 1333272343Sngie " value %s at line %zu of file %s", 1334272343Sngie varptr->name, (const char *)varptr->value, 1335272343Sngie (check == 0)? "expected" : "not matching", 1336272343Sngie (const char *)value, 1337272343Sngie line, cur_file); 1338272343Sngie if (verbose) { 1339272343Sngie fprintf(stderr, "Variable %s contains %s value " 1340272343Sngie "%s at line %zu of file %s\n", 1341272343Sngie varptr->name, 1342272343Sngie (check == 0)? "expected" : "not matching", 1343272343Sngie (const char *)varptr->value, line, cur_file); 1344272343Sngie } 1345272343Sngie } else { 1346272343Sngie if ((check == 0) && (retval->return_len != varptr->len)) 1347272343Sngie err(1, "Byte validation failed, length mismatch"); 1348272343Sngie 1349272343Sngie /* 1350272343Sngie * If check is 0 then we want to throw an error IFF 1351272343Sngie * the byte streams do not match, if check is 1 then 1352272343Sngie * throw an error if the byte streams match. 1353272343Sngie */ 1354272343Sngie if (((check == 0) && memcmp(retval->return_value, varptr->value, 1355272343Sngie varptr->len) != 0) || 1356272343Sngie ((check == 1) && (retval->return_len == varptr->len) && 1357272343Sngie memcmp(retval->return_value, varptr->value, 1358272343Sngie varptr->len) == 0)) 1359272343Sngie err(1, "Validate expected %s byte stream at line %zu" 1360272343Sngie " of file %s", 1361272343Sngie (check == 0)? "matching" : "not matching", 1362272343Sngie line, cur_file); 1363272343Sngie if (verbose) { 1364272343Sngie fprintf(stderr, "Validated expected %s byte stream " 1365272343Sngie "at line %zu of file %s\n", 1366272343Sngie (check == 0)? "matching" : "not matching", 1367272343Sngie line, cur_file); 1368272343Sngie } 1369272343Sngie } 1370272343Sngie} 1371272343Sngie 1372272343Sngie/* 1373272343Sngie * Write a string to the command pipe - we feed the number of bytes coming 1374272343Sngie * down first to allow storage allocation and then follow up with the data. 1375272343Sngie * If cmd is NULL then feed a -1 down the pipe to say the end of the args. 1376272343Sngie */ 1377272343Sngiestatic void 1378272343Sngiewrite_cmd_pipe(char *cmd) 1379272343Sngie{ 1380272343Sngie args_t arg; 1381272343Sngie size_t len; 1382272343Sngie 1383272343Sngie if (cmd == NULL) 1384272343Sngie len = 0; 1385272343Sngie else 1386272343Sngie len = strlen(cmd); 1387272343Sngie 1388272343Sngie arg.arg_type = arg_static; 1389272343Sngie arg.arg_len = len; 1390272343Sngie arg.arg_string = cmd; 1391272343Sngie write_cmd_pipe_args(arg.arg_type, &arg); 1392272343Sngie 1393272343Sngie} 1394272343Sngie 1395272343Sngiestatic void 1396272343Sngiewrite_cmd_pipe_args(args_state_t type, void *data) 1397272343Sngie{ 1398272343Sngie var_t *var_data; 1399272343Sngie args_t *arg_data; 1400272343Sngie int len, send_type; 1401272343Sngie void *cmd; 1402272343Sngie 1403272343Sngie arg_data = data; 1404272343Sngie switch (type) { 1405272343Sngie case arg_var: 1406272343Sngie var_data = data; 1407272343Sngie len = var_data->len; 1408272343Sngie cmd = var_data->value; 1409272343Sngie if (type == arg_byte) 1410272343Sngie send_type = ret_byte; 1411272343Sngie else 1412272343Sngie send_type = ret_string; 1413272343Sngie break; 1414272343Sngie 1415272343Sngie case arg_null: 1416272343Sngie send_type = ret_null; 1417272343Sngie len = 0; 1418272343Sngie break; 1419272343Sngie 1420272343Sngie default: 1421272343Sngie if ((arg_data->arg_len == 0) && (arg_data->arg_string == NULL)) 1422272343Sngie len = -1; 1423272343Sngie else 1424272343Sngie len = arg_data->arg_len; 1425272343Sngie cmd = arg_data->arg_string; 1426272343Sngie if (type == arg_byte) 1427272343Sngie send_type = ret_byte; 1428272343Sngie else 1429272343Sngie send_type = ret_string; 1430272343Sngie } 1431272343Sngie 1432272343Sngie if (verbose) { 1433272343Sngie fprintf(stderr, "Writing type %s to command pipe\n", 1434272343Sngie returns_enum_names[send_type]); 1435272343Sngie } 1436272343Sngie 1437272343Sngie if (write(cmdpipe[WRITE_PIPE], &send_type, sizeof(int)) < 0) 1438272343Sngie err(1, "command pipe write for type failed"); 1439272343Sngie 1440272343Sngie if (verbose) { 1441272343Sngie fprintf(stderr, "Writing length %d to command pipe\n", len); 1442272343Sngie } 1443272343Sngie 1444272343Sngie if (write(cmdpipe[WRITE_PIPE], &len, sizeof(int)) < 0) 1445272343Sngie err(1, "command pipe write for length failed"); 1446272343Sngie 1447272343Sngie if (len > 0) { 1448272343Sngie if (verbose) { 1449272343Sngie fprintf(stderr, "Writing data >%s< to command pipe\n", 1450272343Sngie (const char *)cmd); 1451272343Sngie } 1452272343Sngie if (write(cmdpipe[WRITE_PIPE], cmd, len) < 0) 1453272343Sngie err(1, "command pipe write of data failed"); 1454272343Sngie } 1455272343Sngie} 1456272343Sngie 1457272343Sngie/* 1458272343Sngie * Read a response from the command pipe, first we will receive the 1459272343Sngie * length of the response then the actual data. 1460272343Sngie */ 1461272343Sngiestatic void 1462272343Sngieread_cmd_pipe(returns_t *response) 1463272343Sngie{ 1464272343Sngie int len, type; 1465272343Sngie struct pollfd rfd[2]; 1466272343Sngie char *str; 1467272343Sngie 1468272343Sngie /* 1469272343Sngie * Check if there is data to read - just in case slave has died, we 1470272343Sngie * don't want to block on the read and just hang. We also check 1471272343Sngie * output from the slave because the slave may be blocked waiting 1472272343Sngie * for a flush on its stdout. 1473272343Sngie */ 1474272343Sngie rfd[0].fd = slvpipe[READ_PIPE]; 1475272343Sngie rfd[0].events = POLLIN; 1476272343Sngie rfd[1].fd = master; 1477272343Sngie rfd[1].events = POLLIN; 1478272343Sngie 1479272343Sngie do { 1480272343Sngie if (poll(rfd, 2, 4000) == 0) 1481272343Sngie errx(2, "%s, %zu: Command pipe read timeout", 1482272343Sngie cur_file, line); 1483272343Sngie 1484272343Sngie if ((rfd[1].revents & POLLIN) == POLLIN) { 1485272343Sngie if (verbose) { 1486272343Sngie fprintf(stderr, 1487272343Sngie "draining output from slave\n"); 1488272343Sngie } 1489272343Sngie save_slave_output(false); 1490272343Sngie } 1491272343Sngie } 1492272343Sngie while((rfd[1].revents & POLLIN) == POLLIN); 1493272343Sngie 1494272343Sngie if (read(slvpipe[READ_PIPE], &type, sizeof(int)) < 0) 1495272343Sngie err(1, "command pipe read for type failed"); 1496272343Sngie response->return_type = type; 1497272343Sngie 1498272343Sngie if ((type != ret_ok) && (type != ret_err) && (type != ret_count)) { 1499272343Sngie if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0) 1500272343Sngie err(1, "command pipe read for length failed"); 1501272343Sngie response->return_len = len; 1502272343Sngie 1503272343Sngie if (verbose) { 1504272343Sngie fprintf(stderr, 1505272343Sngie "Reading %d bytes from command pipe\n", len); 1506272343Sngie } 1507272343Sngie 1508272343Sngie if ((response->return_value = malloc(len + 1)) == NULL) 1509272343Sngie err(1, "Failed to alloc memory for cmd pipe read"); 1510272343Sngie 1511272343Sngie if (read(slvpipe[READ_PIPE], response->return_value, len) < 0) 1512272343Sngie err(1, "command pipe read of data failed"); 1513272343Sngie 1514272343Sngie if (response->return_type != ret_byte) { 1515272343Sngie str = response->return_value; 1516272343Sngie str[len] = '\0'; 1517272343Sngie 1518272343Sngie if (verbose) { 1519272343Sngie fprintf(stderr, "Read data >%s< from pipe\n", 1520272343Sngie (const char *)response->return_value); 1521272343Sngie } 1522272343Sngie } 1523272343Sngie } else { 1524272343Sngie response->return_value = NULL; 1525272343Sngie if (type == ret_count) { 1526272343Sngie if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0) 1527272343Sngie err(1, "command pipe read for number of " 1528272343Sngie "returns failed"); 1529272343Sngie response->return_len = len; 1530272343Sngie } 1531272343Sngie 1532272343Sngie if (verbose) { 1533272343Sngie fprintf(stderr, "Read type %s from pipe\n", 1534272343Sngie returns_enum_names[type]); 1535272343Sngie } 1536272343Sngie } 1537272343Sngie} 1538272343Sngie 1539272343Sngie/* 1540272343Sngie * Check for writes from the slave on the pty, save the output into a 1541272343Sngie * buffer for later checking if discard is false. 1542272343Sngie */ 1543272343Sngie#define MAX_DRAIN 256 1544272343Sngie 1545272343Sngiestatic void 1546272343Sngiesave_slave_output(bool discard) 1547272343Sngie{ 1548272343Sngie char *new_data, drain[MAX_DRAIN]; 1549272343Sngie size_t to_allocate; 1550272343Sngie ssize_t result; 1551272343Sngie size_t i; 1552272343Sngie 1553272343Sngie result = 0; 1554272343Sngie for (;;) { 1555272343Sngie if (result == -1) 1556272343Sngie err(2, "poll of slave pty failed"); 1557272343Sngie result = MAX_DRAIN; 1558272343Sngie if ((result = read(master, drain, result)) < 0) { 1559272343Sngie if (errno == EAGAIN) 1560272343Sngie break; 1561272343Sngie else 1562272343Sngie err(2, "draining slave pty failed"); 1563272343Sngie } 1564272343Sngie if (result == 0) 1565272343Sngie abort(); 1566272343Sngie 1567272343Sngie if (!discard) { 1568272343Sngie if ((size_t)result > 1569272343Sngie (saved_output.allocated - saved_output.count)) { 1570272343Sngie to_allocate = 1024 * ((result / 1024) + 1); 1571272343Sngie 1572272343Sngie if ((new_data = realloc(saved_output.data, 1573272343Sngie saved_output.allocated + to_allocate)) 1574272343Sngie == NULL) 1575272343Sngie err(2, "Realloc of saved_output failed"); 1576272343Sngie saved_output.data = new_data; 1577272343Sngie saved_output.allocated += to_allocate; 1578272343Sngie } 1579272343Sngie 1580272343Sngie if (verbose) { 1581272343Sngie fprintf(stderr, "count = %zu, " 1582272343Sngie "allocated = %zu\n", saved_output.count, 1583272343Sngie saved_output.allocated); 1584272343Sngie for (i = 0; i < (size_t)result; i++) { 1585272343Sngie fprintf(stderr, "Saving slave output " 1586272343Sngie "at %zu: 0x%x (%c)\n", 1587272343Sngie saved_output.count + i, drain[i], 1588272343Sngie (drain[i] >= ' ')? drain[i] : '-'); 1589272343Sngie } 1590272343Sngie } 1591272343Sngie 1592272343Sngie memcpy(&saved_output.data[saved_output.count], drain, 1593272343Sngie result); 1594272343Sngie saved_output.count += result; 1595272343Sngie 1596272343Sngie if (verbose) { 1597272343Sngie fprintf(stderr, "count = %zu, " 1598272343Sngie "allocated = %zu\n", saved_output.count, 1599272343Sngie saved_output.allocated); 1600272343Sngie } 1601272343Sngie } else { 1602272343Sngie if (verbose) { 1603272343Sngie for (i = 0; i < (size_t)result; i++) { 1604272343Sngie fprintf(stderr, "Discarding slave " 1605272343Sngie "output 0x%x (%c)\n", 1606272343Sngie drain[i], 1607272343Sngie (drain[i] >= ' ')? drain[i] : '-'); 1608272343Sngie } 1609272343Sngie } 1610272343Sngie } 1611272343Sngie } 1612272343Sngie} 1613272343Sngie 1614272343Sngiestatic void 1615272343Sngieyyerror(const char *msg) 1616272343Sngie{ 1617272343Sngie warnx("%s in line %zu of file %s", msg, line, cur_file); 1618272343Sngie} 1619