1272343Sngie/* $NetBSD: director.c,v 1.10 2012/06/03 23:19:11 joerg Exp $ */ 2272343Sngie 3272343Sngie/*- 4272343Sngie * Copyright 2009 Brett Lymn <blymn@NetBSD.org> 5272343Sngie * 6272343Sngie * All rights reserved. 7272343Sngie * 8272343Sngie * This code has been donated to The NetBSD Foundation by the Author. 9272343Sngie * 10272343Sngie * Redistribution and use in source and binary forms, with or without 11272343Sngie * modification, are permitted provided that the following conditions 12272343Sngie * are met: 13272343Sngie * 1. Redistributions of source code must retain the above copyright 14272343Sngie * notice, this list of conditions and the following disclaimer. 15272343Sngie * 2. The name of the author may not be used to endorse or promote products 16272343Sngie * derived from this software withough specific prior written permission 17272343Sngie * 18272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19272343Sngie * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20272343Sngie * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21272343Sngie * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22272343Sngie * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23272343Sngie * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24272343Sngie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25272343Sngie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26272343Sngie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27272343Sngie * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28272343Sngie * 29272343Sngie * 30272343Sngie */ 31272343Sngie 32272343Sngie#include <sys/param.h> 33272343Sngie#include <sys/stat.h> 34272343Sngie#include <sys/mman.h> 35272343Sngie#include <fcntl.h> 36272343Sngie#include <unistd.h> 37272343Sngie#include <ctype.h> 38272343Sngie#include <termios.h> 39272343Sngie#include <signal.h> 40272343Sngie#include <stdio.h> 41272343Sngie#include <stdlib.h> 42272343Sngie#include <string.h> 43272343Sngie#include <util.h> 44272343Sngie#include <err.h> 45272343Sngie#include "returns.h" 46272343Sngie 47272343Sngievoid yyparse(void); 48272343Sngie#define DEF_TERMPATH "." 49272343Sngie#define DEF_TERM "atf" 50272343Sngie#define DEF_SLAVE "./slave" 51272343Sngie 52272343Sngieconst char *def_check_path = "./"; /* default check path */ 53272343Sngieconst char *def_include_path = "./"; /* default include path */ 54272343Sngie 55272343Sngieextern size_t nvars; /* In testlang_conf.y */ 56272343Sngiesaved_data_t saved_output; /* In testlang_conf.y */ 57272343Sngieint cmdpipe[2]; /* command pipe between director and slave */ 58272343Sngieint slvpipe[2]; /* reply pipe back from slave */ 59272343Sngieint master; /* pty to the slave */ 60272343Sngieint verbose; /* control verbosity of tests */ 61272343Sngieconst char *check_path; /* path to prepend to check files for output 62272343Sngie validation */ 63272343Sngieconst char *include_path; /* path to prepend to include files */ 64272343Sngiechar *cur_file; /* name of file currently being read */ 65272343Sngie 66272343Sngievoid init_parse_variables(int); /* in testlang_parse.y */ 67272343Sngie 68272343Sngie/* 69272343Sngie * Handle the slave exiting unexpectedly, try to recover the exit message 70272343Sngie * and print it out. 71272343Sngie */ 72272343Sngiestatic void 73272343Sngieslave_died(int param) 74272343Sngie{ 75272343Sngie char last_words[256]; 76272343Sngie size_t count; 77272343Sngie 78272343Sngie fprintf(stderr, "ERROR: Slave has exited\n"); 79272343Sngie if (saved_output.count > 0) { 80272343Sngie fprintf(stderr, "output from slave: "); 81272343Sngie for (count = 0; count < saved_output.count; count ++) { 82272343Sngie if (isprint((unsigned char)saved_output.data[count])) 83272343Sngie fprintf(stderr, "%c", saved_output.data[count]); 84272343Sngie } 85272343Sngie fprintf(stderr, "\n"); 86272343Sngie } 87272343Sngie 88272343Sngie if ((count = read(master, &last_words, 255)) > 0) { 89272343Sngie last_words[count] = '\0'; 90272343Sngie fprintf(stderr, "slave exited with message \"%s\"\n", 91272343Sngie last_words); 92272343Sngie } 93272343Sngie 94272343Sngie exit(2); 95272343Sngie} 96272343Sngie 97272343Sngie 98272343Sngiestatic void 99272343Sngieusage(void) 100272343Sngie{ 101272343Sngie fprintf(stderr, "Usage: %s [-v] [-I include-path] [-C check-path] " 102272343Sngie "[-T terminfo-file] [-s pathtoslave] [-t term] " 103272343Sngie "commandfile\n", getprogname()); 104272343Sngie fprintf(stderr, " where:\n"); 105272343Sngie fprintf(stderr, " -v enables verbose test output\n"); 106272343Sngie fprintf(stderr, " -T is a directory containing the terminfo.cdb " 107272343Sngie "file, or a file holding the terminfo description n"); 108272343Sngie fprintf(stderr, " -s is the path to the slave executable\n"); 109272343Sngie fprintf(stderr, " -t is value to set TERM to for the test\n"); 110272343Sngie fprintf(stderr, " -I is the directory to include files\n"); 111272343Sngie fprintf(stderr, " -C is the directory for config files\n"); 112272343Sngie fprintf(stderr, " commandfile is a file of test directives\n"); 113272343Sngie exit(1); 114272343Sngie} 115272343Sngie 116272343Sngie 117272343Sngieint 118272343Sngiemain(int argc, char *argv[]) 119272343Sngie{ 120272343Sngie extern char *optarg; 121272343Sngie extern int optind; 122272343Sngie const char *termpath, *term, *slave; 123272343Sngie int ch; 124272343Sngie pid_t slave_pid; 125272343Sngie extern FILE *yyin; 126272343Sngie char *arg1, *arg2, *arg3, *arg4; 127272343Sngie struct termios term_attr; 128272343Sngie struct stat st; 129272343Sngie 130272343Sngie termpath = term = slave = NULL; 131272343Sngie verbose = 0; 132272343Sngie 133272343Sngie while ((ch = getopt(argc, argv, "vC:I:p:s:t:T:")) != -1) { 134272343Sngie switch(ch) { 135272343Sngie case 'I': 136272343Sngie include_path = optarg; 137272343Sngie break; 138272343Sngie case 'C': 139272343Sngie check_path = optarg; 140272343Sngie break; 141272343Sngie case 'T': 142272343Sngie termpath = optarg; 143272343Sngie break; 144272343Sngie case 'p': 145272343Sngie termpath = optarg; 146272343Sngie break; 147272343Sngie case 's': 148272343Sngie slave = optarg; 149272343Sngie break; 150272343Sngie case 't': 151272343Sngie term = optarg; 152272343Sngie break; 153272343Sngie case 'v': 154272343Sngie verbose = 1; 155272343Sngie break; 156272343Sngie case '?': 157272343Sngie default: 158272343Sngie usage(); 159272343Sngie break; 160272343Sngie } 161272343Sngie } 162272343Sngie 163272343Sngie argc -= optind; 164272343Sngie argv += optind; 165272343Sngie if (argc < 1) 166272343Sngie usage(); 167272343Sngie 168272343Sngie if (termpath == NULL) 169272343Sngie termpath = DEF_TERMPATH; 170272343Sngie 171272343Sngie if (slave == NULL) 172272343Sngie slave = DEF_SLAVE; 173272343Sngie 174272343Sngie if (term == NULL) 175272343Sngie term = DEF_TERM; 176272343Sngie 177272343Sngie if (check_path == NULL) 178272343Sngie check_path = getenv("CHECK_PATH"); 179272343Sngie if ((check_path == NULL) || (check_path[0] == '\0')) { 180272343Sngie warn("$CHECK_PATH not set, defaulting to %s", def_check_path); 181272343Sngie check_path = def_check_path; 182272343Sngie } 183272343Sngie 184272343Sngie if (include_path == NULL) 185272343Sngie include_path = getenv("INCLUDE_PATH"); 186272343Sngie if ((include_path == NULL) || (include_path[0] == '\0')) { 187272343Sngie warn("$INCLUDE_PATH not set, defaulting to %s", 188272343Sngie def_include_path); 189272343Sngie include_path = def_include_path; 190272343Sngie } 191272343Sngie 192272343Sngie signal(SIGCHLD, slave_died); 193272343Sngie 194272343Sngie if (setenv("TERM", term, 1) != 0) 195272343Sngie err(2, "Failed to set TERM variable"); 196272343Sngie 197272343Sngie if (stat(termpath, &st) == -1) 198272343Sngie err(1, "Cannot stat %s", termpath); 199272343Sngie 200272343Sngie if (S_ISDIR(st.st_mode)) { 201272343Sngie char tinfo[MAXPATHLEN]; 202272343Sngie int l = snprintf(tinfo, sizeof(tinfo), "%s/%s", termpath, 203272343Sngie "terminfo.cdb"); 204272343Sngie if (stat(tinfo, &st) == -1) 205272343Sngie err(1, "Cannot stat `%s'", tinfo); 206272343Sngie if (l >= 4) 207272343Sngie tinfo[l - 4] = '\0'; 208272343Sngie if (setenv("TERMINFO", tinfo, 1) != 0) 209272343Sngie err(1, "Failed to set TERMINFO variable"); 210272343Sngie } else { 211272343Sngie int fd; 212272343Sngie char *tinfo; 213272343Sngie if ((fd = open(termpath, O_RDONLY)) == -1) 214272343Sngie err(1, "Cannot open `%s'", termpath); 215272343Sngie if ((tinfo = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_FILE, 216272343Sngie fd, 0)) == MAP_FAILED) 217272343Sngie err(1, "Cannot map `%s'", termpath); 218272343Sngie if (setenv("TERMINFO", tinfo, 1) != 0) 219272343Sngie err(1, "Failed to set TERMINFO variable"); 220272343Sngie close(fd); 221272343Sngie munmap(tinfo, (size_t)st.st_size); 222272343Sngie } 223272343Sngie 224272343Sngie if (pipe(cmdpipe) < 0) 225272343Sngie err(1, "Command pipe creation failed"); 226272343Sngie 227272343Sngie if (pipe(slvpipe) < 0) 228272343Sngie err(1, "Slave pipe creation failed"); 229272343Sngie 230272343Sngie /* 231272343Sngie * Create default termios settings for later use 232272343Sngie */ 233272343Sngie memset(&term_attr, 0, sizeof(term_attr)); 234272343Sngie term_attr.c_iflag = TTYDEF_IFLAG; 235272343Sngie term_attr.c_oflag = TTYDEF_OFLAG; 236272343Sngie term_attr.c_cflag = TTYDEF_CFLAG; 237272343Sngie term_attr.c_lflag = TTYDEF_LFLAG; 238272343Sngie cfsetspeed(&term_attr, TTYDEF_SPEED); 239272343Sngie term_attr.c_cc[VERASE] = '\b'; 240272343Sngie term_attr.c_cc[VKILL] = '\025'; /* ^U */ 241272343Sngie 242272343Sngie if ((slave_pid = forkpty(&master, NULL, &term_attr, NULL)) < 0) 243272343Sngie err(1, "Fork of pty for slave failed\n"); 244272343Sngie 245272343Sngie if (slave_pid == 0) { 246272343Sngie /* slave side, just exec the slave process */ 247272343Sngie if (asprintf(&arg1, "%d", cmdpipe[0]) < 0) 248272343Sngie err(1, "arg1 conversion failed"); 249272343Sngie 250272343Sngie if (asprintf(&arg2, "%d", cmdpipe[1]) < 0) 251272343Sngie err(1, "arg2 conversion failed"); 252272343Sngie 253272343Sngie if (asprintf(&arg3, "%d", slvpipe[0]) < 0) 254272343Sngie err(1, "arg3 conversion failed"); 255272343Sngie 256272343Sngie if (asprintf(&arg4, "%d", slvpipe[1]) < 0) 257272343Sngie err(1, "arg4 conversion failed"); 258272343Sngie 259272343Sngie if (execl(slave, slave, arg1, arg2, arg3, arg4, NULL) < 0) 260272343Sngie err(1, "Exec of slave %s failed", slave); 261272343Sngie 262272343Sngie /* NOT REACHED */ 263272343Sngie } 264272343Sngie 265272343Sngie fcntl(master, F_SETFL, O_NONBLOCK); 266272343Sngie 267272343Sngie if ((yyin = fopen(argv[0], "r")) == NULL) 268272343Sngie err(1, "Cannot open command file %s", argv[0]); 269272343Sngie 270272343Sngie if ((cur_file = strdup(argv[0])) == NULL) 271272343Sngie err(2, "Failed to alloc memory for test file name"); 272272343Sngie 273272343Sngie init_parse_variables(1); 274272343Sngie 275272343Sngie yyparse(); 276272343Sngie fclose(yyin); 277272343Sngie 278272343Sngie exit(0); 279272343Sngie} 280