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