@@@ tvec setvar
[mLib] / test / tvec-remote.c
CommitLineData
e63124bc
MW
1/* -*-c-*-
2 *
3 * Remote testing
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <errno.h>
c91413e6 31#include <signal.h>
e63124bc
MW
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
c91413e6 38#include <sys/wait.h>
e63124bc
MW
39#include <fcntl.h>
40#include <unistd.h>
41
42#include "alloc.h"
c91413e6 43#include "bench.h"
e63124bc 44#include "buf.h"
c91413e6
MW
45#include "compiler.h"
46#include "fdflags.h"
47#include "lbuf.h"
48#include "mdup.h"
49#include "quis.h"
e63124bc
MW
50#include "tvec.h"
51
31d0247c
MW
52/*----- Preliminaries -----------------------------------------------------*/
53
54/* The control macros I'm using below provoke `dangling-else' warnings from
55 * compilers. Suppress them. I generally don't care.
56 */
57
c91413e6
MW
58#if GCC_VERSION_P(7, 1)
59# pragma GCC diagnostic ignored "-Wdangling-else"
60#elif GCC_VERSION_P(4, 2)
61# pragma GCC diagnostic ignored "-Wparentheses"
62#endif
e63124bc 63
c91413e6
MW
64#if CLANG_VERSION_P(3, 1)
65# pragma clang diagnostic ignored "-Wdangling-else"
66#endif
e63124bc 67
c91413e6 68/*----- Basic I/O ---------------------------------------------------------*/
e63124bc 69
31d0247c
MW
70/* --- @init_comms@ --- *
71 *
72 * Arguments: @struct tvec_remotecomms *rc@ = communication state
73 *
74 * Returns: ---
75 *
76 * Use: Initialize a communication state. This doesn't allocate any
77 * resurces: it just ensures that everything is set up so that
78 * subsequent operations -- in particular @release_comms@ --
79 * behave sensibly.
80 */
81
c91413e6
MW
82static void init_comms(struct tvec_remotecomms *rc)
83{
31d0247c 84 rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout);
c91413e6
MW
85 rc->infd = rc->outfd = -1; rc->f = 0;
86}
e63124bc 87
31d0247c
MW
88/* --- @close_comms@ --- *
89 *
90 * Arguments: @struct tvec_remotecomms *rc@ = communication state
91 *
92 * Returns: ---
93 *
94 * Use: Close the input and output descriptors.
95 *
96 * If the descriptors are already closed -- or were never opened
97 * -- then nothing happens.
98 */
99
c91413e6
MW
100static void close_comms(struct tvec_remotecomms *rc)
101{
c81c35df
MW
102 if (rc->infd >= 0) {
103 if (rc->infd != rc->outfd) close(rc->infd);
104 rc->infd = -1;
105 }
106 if (rc->outfd >= 0)
107 { close(rc->outfd); rc->outfd = -1; }
108 rc->f |= TVRF_BROKEN;
c91413e6
MW
109}
110
31d0247c
MW
111/* --- @release_comms@ --- *
112 *
113 * Arguments: @struct tvec_remotecomms *rc@ = communication state
114 *
115 * Returns: ---
116 *
117 * Use: Releases the resources -- most notably the input and output
118 * buffers -- held by the communication state. Also calls
119 * @close_comms@.
120 */
121
c91413e6 122static void release_comms(struct tvec_remotecomms *rc)
31d0247c
MW
123 { close_comms(rc); xfree(rc->bin); dbuf_destroy(&rc->bout); }
124
125/* --- @setup_comms@ --- *
126 *
127 * Arguments: @struct tvec_remotecomms *rc@ = communication state
128 * @int infd, outfd@ = input and output file descriptors
129 *
130 * Returns: ---
131 *
132 * Use: Use the given descriptors for communication.
133 *
134 * Clears the private flags.
135 */
c91413e6
MW
136
137static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
138{
31d0247c
MW
139 rc->infd = infd; rc->outfd = outfd;
140 rc->binoff = rc->binlen = 0;
141 rc->f &= ~0xffu;
c91413e6 142}
e63124bc 143
31d0247c
MW
144/* --- @ioerr@ --- *
145 *
146 * Arguments: @struct tvec_state *tv@ = test-vector state
147 * @struct tvec_remotecomms *rc@ = communication state
148 * @const char *msg, ...@ = format string and arguments
149 *
150 * Returns: %$-1$%.
151 *
152 * Use: Reports the message as an error, closes communications and
153 * marks them as broken.
154 */
155
156static PRINTF_LIKE(3, 4)
157 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
158 const char *msg, ...)
e63124bc
MW
159{
160 va_list ap;
161
162 va_start(ap, msg);
c91413e6
MW
163 close_comms(rc); rc->f |= TVRF_BROKEN;
164 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
e63124bc
MW
165 va_end(ap);
166 return (-1);
167}
168
31d0247c
MW
169/* --- @send_all@ --- *
170 *
171 * Arguments: @struct tvec_state *tv@ = test-vector state
172 * @struct tvec_remotecomms *rc@ = communication state
173 * @const unsigned char *p@, @size_t sz@ = output buffer
174 *
175 * Returns: Zero on success, %$-1$% on error.
176 *
177 * Use: Send the output buffer over the communication state's output
178 * descriptor, even if it has to be written in multiple pieces.
179 */
180
c91413e6 181static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
e63124bc
MW
182 const unsigned char *p, size_t sz)
183{
184 ssize_t n;
c91413e6 185 int ret;
e63124bc
MW
186
187 while (sz) {
c91413e6 188 n = write(rc->outfd, p, sz);
e63124bc
MW
189 if (n > 0)
190 { p += n; sz -= n; }
c91413e6
MW
191 else {
192 ret = ioerr(tv, rc, "failed to send: %s",
193 n ? strerror(errno) : "empty write");
194 goto end;
195 }
e63124bc 196 }
c91413e6
MW
197 ret = 0;
198end:
c91413e6 199 return (ret);
e63124bc
MW
200}
201
31d0247c
MW
202/* --- @recv_all@ --- *
203 *
204 * Arguments: @struct tvec_state *tv@ = test-vector state
205 * @struct tvec_remotecomms *rc@ = communication state
206 * @unsigned f@ = flags (@RCVF_...@)
207 * @unsigned char *p@, @size_t sz@ = input buffer
208 * @size_t min@ = minimum acceptable size to read
209 * @size_t *n_out@ = size read
210 *
c81c35df 211 * Returns: A @RECV_...@ code.
31d0247c
MW
212 *
213 * Use: Receive data on the communication state's input descriptor to
214 * read at least @min@ bytes into the input buffer, even if it
215 * has to be done in multiple pieces. If more data is readily
216 * available, then up to @sz@ bytes will be read in total.
217 *
218 * If the descriptor immediately reports end-of-file, and
219 * @RCVF_ALLOWEOF@ is set in @f@, then return @RECV_EOF@.
220 * Otherwise, EOF is treated as an I/O error, resulting in a
221 * call to @ioerr@ and a return code of @RECV_FAIL@. If the
222 * read succeeded, then set @*n_out@ to the number of bytes read
223 * and return @RECV_OK@.
224 */
225
e63124bc 226#define RCVF_ALLOWEOF 1u
31d0247c 227
c91413e6
MW
228enum {
229 RECV_FAIL = -1,
230 RECV_OK = 0,
231 RECV_EOF = 1
232};
31d0247c 233
c91413e6 234static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
31d0247c
MW
235 unsigned f, unsigned char *p, size_t sz,
236 size_t min, size_t *n_out)
e63124bc 237{
31d0247c 238 size_t tot = 0;
e63124bc 239 ssize_t n;
e63124bc
MW
240
241 while (sz) {
c91413e6 242 n = read(rc->infd, p, sz);
31d0247c
MW
243 if (n > 0) {
244 p += n; sz -= n; tot += n;
245 if (tot >= min) break;
246 } else if (!n && !tot && (f&RCVF_ALLOWEOF))
c81c35df 247 { rc->f |= TVRF_BROKEN; return (RECV_EOF); }
e63124bc 248 else
c91413e6 249 return (ioerr(tv, rc, "failed to receive: %s",
e63124bc
MW
250 n ? strerror(errno) : "unexpected end-of-file"));
251 }
31d0247c 252 *n_out = tot; return (RECV_OK);
e63124bc
MW
253
254#undef f_any
255}
256
31d0247c
MW
257/* --- @buferr@ --- *
258 *
259 * Arguments: @struct tvec_state *tv@ = test-vector state
260 * @struct tvec_remotecomms *rc@ = communication state
261 *
262 * Returns: %$-$%.
263 *
264 * Use: Report a problem preparing the output buffer.
265 */
266
267static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
268 { return (ioerr(tv, rc, "failed to build output packet")); }
269
270/* --- @malformed@ --- *
271 *
272 * Arguments: @struct tvec_state *tv@ = test-vector state
273 * @struct tvec_remotecomms *rc@ = communication state
274 *
275 * Returns: %$-$%.
276 *
277 * Use: Report an I/O error that the incoming packet is malformed.
278 */
279
280static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
281 { return (ioerr(tv, rc, "received malformed packet")); }
282
283/* --- @remote_send@ --- *
284 *
285 * Arguments: @struct tvec_state *tv@ = test-vector state
286 * @struct tvec_remotecomms *rc@ = communication state
287 *
288 * Returns: Zero on success, %$-1$% on error.
289 *
290 * Use: Send the accuulated contents of the output buffer @rc->bout@.
291 *
292 * The function arranges to convert @SIGPIPE@ into an error.
293 *
294 * If the output buffer is broken, report this as an I/O error.
295 */
296
297#define SENDBUFSZ 4096
298
c91413e6 299static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
e63124bc 300{
31d0247c
MW
301 void (*opipe)(int) = SIG_ERR;
302 int ret;
303
304 /* Various preflight checks. */
305 if (rc->f&TVRF_BROKEN) { ret = -1; goto end; }
306 if (DBBAD(&rc->bout)) { ret = buferr(tv, rc); goto end; }
e63124bc 307
31d0247c
MW
308 /* Arrange to trap broken-pipe errors. */
309 opipe = signal(SIGPIPE, SIG_IGN);
310 if (opipe == SIG_ERR) {
311 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
312 goto end;
313 }
e63124bc 314
31d0247c
MW
315 /* Transmit the packet. */
316 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
317 { ret = -1; goto end; }
e63124bc 318
31d0247c
MW
319 /* Done. Put things back the way we found them. */
320 ret = 0;
321end:
322 DBRESET(&rc->bout);
323 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
324 return (ret);
e63124bc
MW
325}
326
31d0247c
MW
327/* --- @receive_buffered@ --- *
328 *
329 * Arguments: @struct tvec_state *tv@ = test-vector state
330 * @struct tvec_remotecomms *rc@ = communication state
331 * @unsigned f@ = flags (@RCVF_...@)
332 * @size_t want@ = data block size required
333 *
c81c35df 334 * Returns: A @RECV_...@ code.
31d0247c
MW
335 *
336 * Use: Reads a block of data from the input descriptor into the
337 * input buffer.
338 *
339 * This is the main machinery for manipulating the input buffer.
340 * The buffer has three regions:
341 *
342 * * from the buffer start to @rc->binoff@ is `consumed';
343 * * from @rc->binoff@ to @rc->binlen@ is `available'; and
344 * * from @rc->binlen@ to @rc->binsz@ is `free'.
345 *
346 * Data is read into the start of the `free' region, and the
347 * `available' region is extended to include it. Data in the
348 * `consumed' region is periodically discarded by moving the
349 * data from the `available' region to the start of the buffer
350 * and decreasing @rc->binoff@ and @rc->binlen@.
351 *
352 * This function ensures that the `available' region contains at
353 * least @want@ bytes, by (a) extending the buffer, if
354 * necessary, so that @rc->binsz >= rc->binoff + want@, and (b)
355 * reading fresh data from the input descriptor to extend the
356 * `available' region.
357 *
358 * If absolutely no data is available, and @RCVF_ALLOWEOF@ is
359 * set in @f@, then return @RECV_EOF@. On I/O errors, including
360 * a short read or end-of-file if @RCVF_ALLOWEOF@ is clear,
361 * return @RECV_FAIL@. On success, return @RECV_OK@. The
362 * amount of data read is indicated by updating the input buffer
363 * variables as described above.
364 */
365
366#define RECVBUFSZ 4096u
367
368static int receive_buffered(struct tvec_state *tv,
369 struct tvec_remotecomms *rc,
370 unsigned f, size_t want)
371{
372 size_t sz;
373 int ret;
374
375 /* If we can supply the caller's requirement from the buffer then do
376 * that.
377 */
378 if (rc->binlen - rc->binoff >= want) return (RECV_OK);
379
380 /* If the buffer is too small then we must grow it. */
381 if (want > rc->binsz) {
382 sz = rc->binsz; if (!sz) sz = RECVBUFSZ;
383 while (sz < want) { assert(sz < (size_t)-1/2); sz *= 2; }
384 if (!rc->bin) rc->bin = xmalloc(sz);
385 else rc->bin = xrealloc(rc->bin, sz, rc->binsz);
386 rc->binsz = sz;
387 }
388
389 /* Shunt the unused existing material to the start of the buffer. */
390 memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff);
391 rc->binlen -= rc->binoff; rc->binoff = 0;
392
393 /* Satisfy the caller from the input stream, and try to fill up as much of
394 * the rest of the buffer as we can.
395 */
396 ret = recv_all(tv, rc, rc->binlen ? 0 : f,
397 rc->bin + rc->binlen, rc->binsz - rc->binlen,
398 want - rc->binlen, &sz);
399 if (ret) return (ret);
400
401 /* Note how much material we have and return. */
402 rc->binlen += sz; return (RECV_OK);
403}
404
405/* --- @remote_recv@ --- *
406 *
407 * Arguments: @struct tvec_state *tv@ = test-vector state
408 * @unsigned f@ = flags (@RCVF_...@)
409 * @buf *b_out@ = buffer to establish around the packet contents
410 *
c81c35df 411 * Returns: A @RECV_...@ code.
31d0247c
MW
412 *
413 * Use: Receive a packet into the input buffer @rc->bin@ and
414 * establish @*b_out@ to read from it.
415 */
416
c91413e6
MW
417static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
418 unsigned f, buf *b_out)
e63124bc 419{
31d0247c
MW
420 kludge64 k, szmax;
421 size_t want;
c91413e6 422 int ret;
e63124bc 423
c91413e6 424 ASSIGN64(szmax, (size_t)-1);
31d0247c
MW
425
426 /* Preflight checks. */
427 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
428
429 /* See if we can read the next packet length from what we already have. */
430 ret = receive_buffered(tv, rc, f, 8); if (ret) return (ret);
431 LOAD64_L_(k, rc->bin + rc->binoff); rc->binoff += 8;
e63124bc 432 if (CMP64(k, >, szmax))
c91413e6 433 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
e63124bc 434 (unsigned long)HI64(k), (unsigned long)LO64(k)));
31d0247c 435 want = GET64(size_t, k);
e63124bc 436
31d0247c
MW
437 /* Read the next packet payload. */
438 ret = receive_buffered(tv, rc, 0, want); if (ret) return (ret);
439 buf_init(b_out, rc->bin + rc->binoff, want); rc->binoff += want;
440 return (RECV_OK);
e63124bc
MW
441}
442
31d0247c
MW
443/* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
444 *
445 * Arguments: @tag@ = control structure tag
446 * @struct tvec_state *tv@ = test-vector state
447 * @struct tvec_remotecomms *rc@ = communication state
448 * @unsigned fl@ = flags (@QF_...@)
449 * @unsigned pk@ = packet type
450 *
451 * Use: This is syntactically a statement head: the syntax is
452 * @QUEUEPK(tv, rc, f) body [else alt]@. The @body@ should
453 * write material to the output buffer @rc->bout@. The macro
454 * applies appropriate framing. If enough material has been
455 * collected, or if @QF_FORCE@ is set in @fl@, then
456 * @remote_send@ is invoked to transmit the buffered packets.
457 * If there is an error of any kind, then the @alt@ statement,
458 * if any, is executed.
459 */
e63124bc 460
31d0247c
MW
461#define QF_FORCE 1u
462#define QUEUEPK_TAG(tag, tv, rc, fl, pk) \
463 if ((rc)->f&TVRF_BROKEN) MC_GOELSE(tag##__else); else \
464 MC_ALLOWELSE(tag##__else) \
465 MC_AFTER(tag##__send, { \
466 if ((DBBAD(&(rc)->bout) && (buferr((tv), (rc)), 1)) || \
467 ((((fl)&QF_FORCE) || DBLEN(&(rc)->bout) >= SENDBUFSZ) && \
468 remote_send(tv, rc))) \
469 MC_GOELSE(tag##__else); \
470 }) \
471 DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L) \
472 MC_BEFORE(tag##__pkty, { \
473 dbuf_putu16l(&(rc)->bout, (pk)); \
474 })
475
476#define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
e63124bc
MW
477
478/*----- Packet types ------------------------------------------------------*/
479
c91413e6 480#define TVPF_ACK 0x0001u
e63124bc 481
31d0247c
MW
482#define TVPK_VER 0x0000u /* --> min, max: u16 *
483 * <-- ver: u16 */
484#define TVPK_BGROUP 0x0002u /* --> name: str16
485 * <-- --- */
814e42ff
MW
486#define TVPK_SETVAR 0x0004u /* --> name: str16, rv: value
487 * <-- rc: u8 */
488#define TVPK_TEST 0x0006u /* --> in: regs
31d0247c 489 * <-- --- */
814e42ff 490#define TVPK_EGROUP 0x0008u /* --> --- *
31d0247c 491 * <-- --- */
e63124bc 492
c91413e6
MW
493#define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
494#define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
e63124bc 495
31d0247c
MW
496#define TVPK_SKIPGRP 0x0104u /* <-- excuse: str16 */
497#define TVPK_SKIP 0x0106u /* <-- excuse: str16 */
498#define TVPK_FAIL 0x0108u /* <-- flag: u8, detail: str16 */
499#define TVPK_DUMPREG 0x010au /* <-- ri: u16; disp: u16;
c91413e6 500 * flag: u8, rv: value */
31d0247c
MW
501#define TVPK_BBENCH 0x010cu /* <-- ident: str32; unit: u16 */
502#define TVPK_EBENCH 0x010eu /* <-- ident: str32; unit: u16;
c91413e6 503 * flags: u16; n, t, cy: f64 */
e63124bc 504
c91413e6
MW
505/*----- Server ------------------------------------------------------------*/
506
31d0247c 507/* Forward declaration of output operations. */
c91413e6
MW
508static const struct tvec_outops remote_ops;
509
31d0247c
MW
510static struct tvec_state srvtv; /* server's test-vector state */
511static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
512static struct tvec_output srvout = { &remote_ops }; /* output state */
e63124bc 513
31d0247c
MW
514/* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
515 *
516 * Arguments: @const char *status@ = progress status token format
517 * @va_list ap@ = argument tail
518 *
519 * Returns: ---
520 *
521 * Use: Reports the progress of a test execution to the client.
522 *
523 * The framework makes use of tokens beginning with %|%|%:
524 *
525 * * %|%IDLE|%: during the top-level server code;
526 *
527 * * %|%SETUP|%: during the enclosing environment's @before@
528 * function;
529 *
530 * * %|%RUN|%: during the environment's @run@ function, or the
531 * test function; and
532 *
533 * * %|%DONE|%: during the enclosing environment's @after@
534 * function.
535 *
536 * The intent is that a test can use the progress token to check
537 * that a function which is expected to crash does so at the
538 * correct point, so it's expected that more complex test
539 * functions and/or environments will set their own progress
540 * tokens to reflect what's going on.
541 */
542
543int tvec_setprogress(const char *status, ...)
544{
545 va_list ap;
546 int rc;
547
548 va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
549 return (rc);
550}
551
552int tvec_setprogress_v(const char *status, va_list *ap)
c91413e6 553{
31d0247c
MW
554 /* Force immediate output in case we crash before the buffer is output
555 * organically.
556 */
557 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
558 dbuf_vputstrf16l(&srvrc.bout, status, ap);
c91413e6
MW
559 else return (-1);
560 return (0);
e63124bc
MW
561}
562
31d0247c
MW
563/* --- @tvec_remoteserver@ --- *
564 *
565 * Arguments: @int infd@, @int outfd@ = input and output file descriptors
566 * @const struct tvec_config *config@ = test configuration
567 *
568 * Returns: Suggested exit code.
569 *
570 * Use: Run a test server, reading packets from @infd@ and writing
571 * responses and notifications to @outfd@, and invoking tests as
572 * described by @config@.
573 *
574 * This function is not particularly general purpose. It
575 * expects to `take over' the process, and makes use of private
576 * global variables.
577 */
578
c91413e6 579int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
e63124bc 580{
c91413e6
MW
581 uint16 pk, u, v;
582 unsigned i;
583 buf b;
814e42ff 584 dstr d = DSTR_INIT;
c91413e6
MW
585 const struct tvec_test *t;
586 void *p; size_t sz;
587 const struct tvec_env *env = 0;
814e42ff
MW
588 const struct tvec_vardef *vd = 0; void *varctx;
589 struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
c91413e6
MW
590 void *ctx = 0;
591 int rc;
592
31d0247c 593 /* Initialize the communication machinery. */
c91413e6 594 setup_comms(&srvrc, infd, outfd);
31d0247c
MW
595
596 /* Begin a test session using our custom output driver. */
c91413e6
MW
597 tvec_begin(&srvtv, config, &srvout);
598
31d0247c
MW
599 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
600 * there's only version zero, so we return that.
601 */
c91413e6
MW
602 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
603 if (buf_getu16l(&b, &pk)) goto bad;
604 if (pk != TVPK_VER) {
605 rc = ioerr(&srvtv, &srvrc,
606 "unexpected packet type 0x%04x instead of client version",
607 pk);
608 goto end;
e63124bc 609 }
c91413e6 610 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
31d0247c
MW
611 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
612 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 613 else { rc = -1; goto end; }
e63124bc 614
31d0247c
MW
615 /* Handle packets until the server closes the connection.
616 *
617 * The protocol looks much simpler from our point of view than from the
618 * client.
619 *
620 * * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
621 *
622 * * Receive zero or more @TVPK_BGROUP@. Open a test group, producing
623 * output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
624 *
625 * -- Receive zero or more @TVPK_TEST@. Run a test, producing output
626 * packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
627 *
628 * -- Receive @TVPK_EGROUP@. Maybe produce output packets, and
629 * answer with @TVPK_EGROUP | TVPF_ACK@.
630 *
631 * * Read EOF. Stop.
632 */
c91413e6 633 for (;;) {
31d0247c
MW
634
635 /* Read a packet. End-of-file is expected here (and pretty much nowhere
636 * else). Otherwise, we expect to see @TVPK_BGROUP@.
637 */
c91413e6
MW
638 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
639 if (rc == RECV_EOF) break;
640 else if (rc == RECV_FAIL) goto end;
641 if (buf_getu16l(&b, &pk)) goto bad;
e63124bc 642
c91413e6
MW
643 switch (pk) {
644
645 case TVPK_BGROUP:
31d0247c
MW
646 /* Start a group. */
647
648 /* Parse the packet payload. */
c91413e6
MW
649 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
650 if (BLEFT(&b)) goto bad;
31d0247c
MW
651
652 /* Find the group given its name. */
c91413e6
MW
653 for (t = srvtv.tests; t->name; t++)
654 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
655 goto found_group;
656 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
657 (int)sz, (char *)p);
658 goto end;
659
660 found_group:
31d0247c 661 /* Set up the test environment. */
c91413e6
MW
662 srvtv.test = t; env = t->env;
663 if (env && env->setup == tvec_remotesetup)
664 env = ((struct tvec_remoteenv *)env)->r.env;
665 if (!env || !env->ctxsz) ctx = 0;
666 else ctx = xmalloc(env->ctxsz);
667 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
668
31d0247c
MW
669 /* Initialize the registers. */
670 tvec_initregs(&srvtv);
671
672 /* Report that the group has been opened and that we're ready to run
673 * tests.
674 */
675 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
c91413e6
MW
676 else { rc = -1; goto end; }
677
31d0247c 678 /* Handle packets until we're told to end the group. */
c91413e6 679 for (;;) {
31d0247c
MW
680
681 /* Read a packet. We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
c91413e6
MW
682 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
683 if (buf_getu16l(&b, &pk)) goto bad;
31d0247c 684
c91413e6
MW
685 switch (pk) {
686
687 case TVPK_EGROUP:
31d0247c
MW
688 /* End the group. */
689
690 /* Check the payload. */
c91413e6 691 if (BLEFT(&b)) goto bad;
31d0247c
MW
692
693 /* Leave the group loop. */
c91413e6
MW
694 goto endgroup;
695
814e42ff
MW
696 case TVPK_SETVAR:
697 /* Set a subenvironment variable. */
698
699 /* Get the variable name. */
700 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
701 DRESET(&d); DPUTM(&d, p, sz); DPUTZ(&d);
702
703 /* Look up the variable definition. */
704 if (env && env->findvar) {
705 vd = env->findvar(&srvtv, d.buf, &varctx, ctx);
706 if (vd) goto found_var;
707 }
708 rc = tvec_unkreg(&srvtv, d.buf); goto setvar_end;
709 found_var:
710
711 /* Set up the register. */
712 if (vd->regsz <= sizeof(rbuf))
713 r = &rbuf;
714 else {
715 if (rsz < vd->regsz) {
716 xfree(r_alloc);
717 if (!rsz) rsz = 8*sizeof(void *);
718 while (rsz < vd->regsz) rsz *= 2;
719 r_alloc = xmalloc(rsz);
720 }
721 r = r_alloc;
722 }
723
724 /* Collect and set the value. */
725 vd->def.ty->init(&r->v, &vd->def);
726 if (vd->def.ty->frombuf(&b, &r->v, &vd->def)) goto bad;
727 if (BLEFT(&b)) goto bad;
728 rc = vd->setvar(&srvtv, d.buf, &r->v, varctx);
729
730 /* Send the reply. */
731 setvar_end:
732 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_SETVAR | TVPF_ACK)
733 dbuf_putbyte(&srvrc.bout, rc ? 0xff : 0);
734 else { rc = -1; goto end; }
735 if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
736 break;
737
c91413e6 738 case TVPK_TEST:
31d0247c
MW
739 /* Run a test. */
740
741 /* Parse the packet payload. */
c91413e6
MW
742 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
743 srvtv.nreg, srvtv.regsz))
744 goto bad;
745 if (BLEFT(&b)) goto bad;
746
31d0247c
MW
747 /* If we're not skipping the test group, then actually try to
748 * run the test.
749 */
c91413e6 750 if (!(srvtv.f&TVSF_SKIP)) {
31d0247c
MW
751
752 /* Prepare the output registers and reset the test outcome.
753 * (The environment may force a skip.)
754 */
755 for (i = 0; i < srvtv.nrout; i++)
756 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
757 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
c91413e6 758 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
31d0247c
MW
759
760 /* Invoke the environment @before@ function. */
761 tvec_setprogress("%%SETUP");
c91413e6 762 if (env && env->before) env->before(&srvtv, ctx);
31d0247c
MW
763
764 /* Run the actual test. */
c91413e6
MW
765 if (!(srvtv.f&TVSF_ACTIVE))
766 /* setup forced a skip */;
767 else {
31d0247c 768 tvec_setprogress("%%RUN");
c91413e6
MW
769 if (env && env->run)
770 env->run(&srvtv, t->fn, ctx);
771 else {
772 t->fn(srvtv.in, srvtv.out, ctx);
773 tvec_check(&srvtv, 0);
774 }
775 }
31d0247c
MW
776
777 /* Conclude the test. */
778 tvec_setprogress("%%DONE");
c91413e6
MW
779 if (env && env->after) env->after(&srvtv, ctx);
780 tvec_endtest(&srvtv);
781 }
31d0247c
MW
782
783 /* Reset the input registers and report completion. */
784 tvec_releaseregs(&srvtv); tvec_initregs(&srvtv);
785 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_TEST | TVPF_ACK);
c91413e6 786 else { rc = -1; goto end; }
c91413e6
MW
787 break;
788
789 default:
31d0247c
MW
790 /* Some other kind of packet. Complain. */
791
c91413e6 792 rc = ioerr(&srvtv, &srvrc,
31d0247c
MW
793 "unexpected packet type 0x%04x during test group",
794 pk);
c91413e6
MW
795 goto end;
796
797 }
798 }
799
800 endgroup:
31d0247c
MW
801 /* The test group completed. */
802
803 /* Tear down the environment and release other resources. */
c91413e6 804 if (env && env->teardown) env->teardown(&srvtv, ctx);
31d0247c
MW
805 tvec_releaseregs(&srvtv);
806 xfree(ctx); srvtv.test = 0; env = 0; ctx = 0;
807
808 /* Report completion. */
809 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
810 else { rc = -1; goto end; }
c91413e6
MW
811 break;
812
813 default:
31d0247c
MW
814 rc = ioerr(&srvtv, &srvrc,
815 "unexpected packet type 0x%04x at top level", pk);
c91413e6
MW
816 }
817 }
818 rc = 0;
819
820end:
31d0247c 821 /* Clean up and return. */
c91413e6 822 if (env && env->teardown) env->teardown(&srvtv, ctx);
814e42ff
MW
823 if (vd) vd->def.ty->release(&r->v, &vd->def);
824 xfree(ctx); xfree(r_alloc);
31d0247c
MW
825 if (srvtv.test) tvec_releaseregs(&srvtv);
826 release_comms(&srvrc); tvec_end(&srvtv);
c91413e6
MW
827 return (rc ? 2 : 0);
828
829bad:
31d0247c 830 /* Miscellaneous malformed packet. */
c91413e6 831 rc = malformed(&srvtv, &srvrc); goto end;
e63124bc
MW
832}
833
c91413e6
MW
834/*----- Server output driver ----------------------------------------------*/
835
c81c35df
MW
836/* --- @remote_bsession@ --- *
837 *
838 * Arguments: @struct tvec_output *o@ = output sink (ignored)
839 * @struct tvec_state *tv@ = the test state producing output
840 *
841 * Returns: ---
842 *
843 * Use: Begin a test session.
844 *
845 * The remote driver does nothing at all.
846 */
847
848static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
849 { ; }
850
851/* --- @remote_esession@ --- *
852 *
853 * Arguments: @struct tvec_output *o@ = output sink (ignored)
854 *
855 * Returns: Suggested exit code.
856 *
857 * Use: End a test session.
858 *
859 * The remote driver returns a suitable exit code without
860 * printing anything.
861 */
862
863static int remote_esession(struct tvec_output *o)
864 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
865
866/* --- @remote_bgroup@ --- *
867 *
868 * Arguments: @struct tvec_output *o@ = output sink (ignored)
869 *
870 * Returns: ---
871 *
872 * Use: Begin a test group.
873 *
874 * This is a stub which should never be called.
875 */
876
877static void remote_bgroup(struct tvec_output *o)
878 { assert(!"remote_bgroup"); }
879
880/* --- @remote_skipgroup@ --- *
881 *
882 * Arguments: @struct tvec_output *o@ = output sink (ignored)
883 * @const char *excuse@, @va_list *ap@ = reason for skipping the
884 * group, or null
885 *
886 * Returns: ---
887 *
888 * Use: Report that a test group is being skipped.
889 *
890 * The remote driver sends a @TVPK_SKIP@ packet to its client.
891 */
892
e63124bc
MW
893static void remote_skipgroup(struct tvec_output *o,
894 const char *excuse, va_list *ap)
c91413e6 895{
31d0247c
MW
896 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
897 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 898}
e63124bc 899
c81c35df
MW
900/* --- @remote_egroup@ --- *
901 *
902 * Arguments: @struct tvec_output *o@ = output sink (ignored)
903 *
904 * Returns: ---
905 *
906 * Use: Report that a test group has finished.
907 *
908 * This is a stub which should never be called.
909 */
910
911static void remote_egroup(struct tvec_output *o)
912 { assert(!"remote_egroup"); }
913
914/* --- @remote_btest@ --- *
915 *
916 * Arguments: @struct tvec_output *o@ = output sink (ignored)
917 *
918 * Returns: ---
919 *
920 * Use: Report that a test is starting.
921 *
922 * This is a stub which should never be called.
923 */
924
925static void remote_btest(struct tvec_output *o)
926 { assert(!"remote_btest"); }
927
928/* --- @remote_skip@, @remote_fail@ --- *
929 *
930 * Arguments: @struct tvec_output *o@ = output sink (ignored)
931 * @unsigned attr@ = attribute to apply to the outcome
932 * @const char *outcome@ = outcome string to report
933 * @const char *detail@, @va_list *ap@ = a detail message
934 * @const char *excuse@, @va_list *ap@ = reason for skipping the
935 * test
936 *
937 * Returns: ---
938 *
939 * Use: Report that a test has been skipped or failed.
940 *
941 * The remote driver sends a @TVPK_SKIP@ or @TVPK_FAIL@ packet
942 * to its client as appropriate.
943 */
944
e63124bc
MW
945static void remote_skip(struct tvec_output *o,
946 const char *excuse, va_list *ap)
c91413e6 947{
31d0247c
MW
948 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
949 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 950}
e63124bc
MW
951
952static void remote_fail(struct tvec_output *o,
953 const char *detail, va_list *ap)
c91413e6 954{
31d0247c 955 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
c91413e6 956 if (!detail)
31d0247c 957 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 958 else {
31d0247c
MW
959 dbuf_putbyte(&srvrc.bout, 1);
960 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
c91413e6
MW
961 }
962}
e63124bc 963
c81c35df
MW
964/* --- @remote_dumpreg@ --- *
965 *
966 * Arguments: @struct tvec_output *o@ = output sink (ignored)
967 * @unsigned disp@ = register disposition
968 * @const union tvec_regval *rv@ = register value
969 * @const struct tvec_regdef *rd@ = register definition
970 *
971 * Returns: ---
972 *
973 * Use: Dump a register.
974 *
975 * The remote driver sends a @TVPK_DUMPREG@ packet to its
976 * client. This will only work if the register definition is
977 * one of those listed in the current test definition.
978 */
979
c91413e6
MW
980static void remote_dumpreg(struct tvec_output *o,
981 unsigned disp, const union tvec_regval *rv,
982 const struct tvec_regdef *rd)
e63124bc 983{
c91413e6
MW
984 const struct tvec_regdef *reg;
985 unsigned r;
e63124bc 986
c91413e6
MW
987 /* Find the register definition. */
988 for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
989 if (reg == rd) goto found;
990 assert(!"unexpected register definition");
991
992found:
31d0247c
MW
993 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
994 dbuf_putu16l(&srvrc.bout, r);
995 dbuf_putu16l(&srvrc.bout, disp);
c91413e6 996 if (!rv)
31d0247c 997 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 998 else {
31d0247c
MW
999 dbuf_putbyte(&srvrc.bout, 1);
1000 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
c91413e6 1001 }
e63124bc
MW
1002 }
1003}
1004
c81c35df
MW
1005/* --- @remote_etest@ --- *
1006 *
1007 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1008 * @unsigned outcome@ = the test outcome
1009 *
1010 * Returns: ---
1011 *
1012 * Use: Report that a test has finished.
1013 *
1014 * The remote driver does nothing at all.
1015 */
1016
1017static void remote_etest(struct tvec_output *o, unsigned outcome)
1018 { ; }
1019
1020/* --- @remote_bbench@ --- *
1021 *
1022 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1023 * @const char *ident@ = identifying register values
1024 * @unsigned unit@ = measurement unit (@TVBU_...@)
1025 *
1026 * Returns: ---
1027 *
1028 * Use: Report that a benchmark has started.
1029 *
1030 * The remote driver sends a @TVPK_BBENCH@ packet to its client.
1031 */
1032
c91413e6
MW
1033static void remote_bbench(struct tvec_output *o,
1034 const char *ident, unsigned unit)
e63124bc 1035{
31d0247c
MW
1036 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
1037 dbuf_putstr32l(&srvrc.bout, ident);
1038 dbuf_putu16l(&srvrc.bout, unit);
c91413e6 1039 }
e63124bc
MW
1040}
1041
c81c35df
MW
1042/* --- @remote_ebench@ --- *
1043 *
1044 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1045 * @const char *ident@ = identifying register values
1046 * @unsigned unit@ = measurement unit (@TVBU_...@)
1047 * @const struct bench_timing *tm@ = measurement
1048 *
1049 * Returns: ---
1050 *
1051 * Use: Report a benchmark's results
1052 *
1053 * The remote driver sends a @TVPK_EBENCH@ packet to its client.
1054 */
1055
e63124bc 1056static void remote_ebench(struct tvec_output *o,
c91413e6 1057 const char *ident, unsigned unit,
e63124bc
MW
1058 const struct bench_timing *t)
1059{
31d0247c
MW
1060 QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
1061 dbuf_putstr32l(&srvrc.bout, ident);
1062 dbuf_putu16l(&srvrc.bout, unit);
c91413e6 1063 if (!t || !(t->f&BTF_ANY))
31d0247c 1064 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 1065 else {
31d0247c
MW
1066 dbuf_putu16l(&srvrc.bout, t->f);
1067 dbuf_putf64l(&srvrc.bout, t->n);
1068 if (t->f&BTF_TIMEOK) dbuf_putf64l(&srvrc.bout, t->t);
1069 if (t->f&BTF_CYOK) dbuf_putf64l(&srvrc.bout, t->cy);
c91413e6
MW
1070 }
1071 }
1072}
1073
c81c35df
MW
1074/* --- @remote_report@ --- *
1075 *
1076 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1077 * @unsigned level@ = message level (@TVLEV_...@)
1078 * @const char *msg@, @va_list *ap@ = format string and
1079 * arguments
1080 *
1081 * Returns: ---
1082 *
1083 * Use: Report a message to the user.
1084 *
1085 * The remote driver sends a @TVPK_REPORT@ packet to its
1086 * client. If its attempt to transmit the packet fails, then
1087 * the message is written to the standard error stream instead,
1088 * in the hope that this will help it be noticed.
1089 */
1090
c91413e6
MW
1091static void remote_report(struct tvec_output *o, unsigned level,
1092 const char *msg, va_list *ap)
1093{
31d0247c
MW
1094 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
1095 dbuf_putu16l(&srvrc.bout, level);
1096 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
c91413e6 1097 } else {
31d0247c 1098 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
c91413e6
MW
1099 vfprintf(stderr, msg, *ap);
1100 fputc('\n', stderr);
e63124bc
MW
1101 }
1102}
1103
c81c35df
MW
1104/* --- @remote_destroy@ --- *
1105 *
1106 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1107 *
1108 * Returns: ---
1109 *
1110 * Use: Release the resources held by the output driver.
1111 *
1112 * The remote driver does nothing at all.
1113 */
1114
c91413e6
MW
1115static void remote_destroy(struct tvec_output *o)
1116 { ; }
e63124bc
MW
1117
1118static const struct tvec_outops remote_ops = {
e63124bc 1119 remote_bsession, remote_esession,
c91413e6
MW
1120 remote_bgroup, remote_skipgroup, remote_egroup,
1121 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
e63124bc 1122 remote_bbench, remote_ebench,
c91413e6 1123 remote_report,
e63124bc 1124 remote_destroy
c91413e6
MW
1125};
1126
c81c35df 1127/*----- Pseudoregister definitions ----------------------------------------*/
c91413e6 1128
814e42ff
MW
1129static tvec_setvarfn setvar_local, setvar_remote;
1130
c91413e6 1131static const struct tvec_flag exit_flags[] = {
c81c35df
MW
1132
1133 /* Cause codes. */
1134 { "running", TVXF_CAUSEMASK, TVXST_RUN },
1135 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
1136 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
1137 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
1138 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
1139 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
1140 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
1141 { "error", TVXF_CAUSEMASK, TVXST_ERR },
1142
c91413e6
MW
1143 /*
1144 ;;; The signal name table is very boring to type. To make life less
1145 ;;; awful, put the signal names in this list and evaluate the code to
1146 ;;; get Emacs to regenerate it.
1147
1148 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
1149 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
1150 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
1151 STKFLT INFO PWR THR LWP LIBRT LOST)))
1152 (save-excursion
1153 (goto-char (point-min))
1154 (search-forward (concat "***" "BEGIN siglist" "***"))
1155 (beginning-of-line 2)
1156 (delete-region (point)
1157 (progn
1158 (search-forward "***END***")
1159 (beginning-of-line)
1160 (point)))
1161 (dolist (sig signals)
1162 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
1163 sig sig sig)))))
1164 */
1165
1166 /***BEGIN siglist***/
1167#ifdef SIGHUP
1168 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
1169#endif
1170#ifdef SIGINT
1171 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
1172#endif
1173#ifdef SIGQUIT
1174 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
1175#endif
1176#ifdef SIGILL
1177 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
1178#endif
1179#ifdef SIGTRAP
1180 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
1181#endif
1182#ifdef SIGABRT
1183 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
1184#endif
1185#ifdef SIGIOT
1186 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
1187#endif
1188#ifdef SIGEMT
1189 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
1190#endif
1191#ifdef SIGFPE
1192 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
1193#endif
1194#ifdef SIGKILL
1195 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
1196#endif
1197#ifdef SIGBUS
1198 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
1199#endif
1200#ifdef SIGSEGV
1201 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
1202#endif
1203#ifdef SIGSYS
1204 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
1205#endif
1206#ifdef SIGPIPE
1207 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
1208#endif
1209#ifdef SIGALRM
1210 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
1211#endif
1212#ifdef SIGTERM
1213 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
1214#endif
1215#ifdef SIGURG
1216 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
1217#endif
1218#ifdef SIGSTOP
1219 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
1220#endif
1221#ifdef SIGTSTP
1222 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
1223#endif
1224#ifdef SIGCONT
1225 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
1226#endif
1227#ifdef SIGCHLD
1228 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1229#endif
1230#ifdef SIGCLD
1231 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1232#endif
1233#ifdef SIGTTIN
1234 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1235#endif
1236#ifdef SIGTTOU
1237 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1238#endif
1239#ifdef SIGPOLL
1240 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1241#endif
1242#ifdef SIGIO
1243 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1244#endif
1245#ifdef SIGTIN
1246 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1247#endif
1248#ifdef SIGXCPU
1249 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1250#endif
1251#ifdef SIGXFSZ
1252 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1253#endif
1254#ifdef SIGVTALRM
1255 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1256#endif
1257#ifdef SIGPROF
1258 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1259#endif
1260#ifdef SIGWINCH
1261 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1262#endif
1263#ifdef SIGUSR1
1264 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1265#endif
1266#ifdef SIGUSR2
1267 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1268#endif
1269#ifdef SIGSTKFLT
1270 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1271#endif
1272#ifdef SIGINFO
1273 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1274#endif
1275#ifdef SIGPWR
1276 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1277#endif
1278#ifdef SIGTHR
1279 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1280#endif
1281#ifdef SIGLWP
1282 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1283#endif
1284#ifdef SIGLIBRT
1285 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1286#endif
1287#ifdef SIGLOST
1288 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1289#endif
1290 /***END***/
1291
c81c35df 1292 /* This should be folded into the signal entries above. */
c91413e6
MW
1293 { "signal", TVXF_SIG, TVXF_SIG },
1294
c91413e6
MW
1295 TVEC_ENDFLAGS
1296};
c91413e6
MW
1297static const struct tvec_flaginfo exit_flaginfo =
1298 { "exit-status", exit_flags, &tvrange_uint };
814e42ff
MW
1299static const struct tvec_vardef exit_var =
1300 { sizeof(struct tvec_reg), setvar_local,
1301 { "@exit", -1, &tvty_flags, 0, { &exit_flaginfo } } };
c91413e6 1302
c81c35df
MW
1303/* Progress. */
1304
814e42ff
MW
1305static const struct tvec_vardef progress_var =
1306 { sizeof(struct tvec_reg), setvar_local,
1307 { "@progress", -1, &tvty_text, 0 } };
c81c35df
MW
1308
1309/* Reconnection. */
c91413e6
MW
1310
1311static const struct tvec_uassoc reconn_assocs[] = {
1312 { "on-demand", TVRCN_DEMAND },
1313 { "force", TVRCN_FORCE },
1314 { "skip", TVRCN_SKIP },
1315 TVEC_ENDENUM
1316};
c81c35df
MW
1317static const struct tvec_uenuminfo reconn_enuminfo =
1318 { "remote-reconnection", reconn_assocs, &tvrange_uint };
814e42ff
MW
1319static const struct tvec_vardef reconn_var =
1320 { sizeof(struct tvec_reg), setvar_local,
1321 { "@reconnect", -1, &tvty_uenum, 0, { &reconn_enuminfo } } };
c81c35df
MW
1322
1323/*----- Client ------------------------------------------------------------*/
1324
1325/* Connection state. */
c91413e6
MW
1326enum {
1327 CONN_BROKEN = -2, /* previously broken */
1328 CONN_FAILED = -1, /* attempt freshly failed */
1329 CONN_ESTABLISHED = 0, /* previously established */
1330 CONN_FRESH = 1 /* freshly connected */
1331};
1332
c81c35df
MW
1333/* --- @handle_packets@ --- *
1334 *
1335 * Arguments: @struct tvec_state *tv@ = test-vector state
1336 * @struct tvec_remotectx *r@ = remote client context
1337 * @unsigned f@ = receive flags (@RCVF_...@)
1338 * @uint16 end@ = expected end packet type
1339 * @buf *b_out@ = buffer in which to return end packet payload
1340 *
1341 * Returns: A @RECV_...@ code.
1342 *
1343 * Use: Handles notification packets from the server until a final
1344 * termination packet is received.
1345 *
1346 * The client/server protocol consists of a number of flows,
1347 * beginning with a request from the client, followed by a
1348 * number of notifications from the server, and terminated by an
1349 * acknowledgement to the original request indicating that the
1350 * server has completed acting on the original request.
1351 *
1352 * This function handles the notifications issued by the server,
1353 * returning when one of the following occurs: (a) a packet of
1354 * type @end@ is received, in which case the function returns
1355 * @RECV_OK@ and the remainder of the packet payload is left in
1356 * @b_out@; (b) the flag @RCVF_ALLOWEOF@ was set in @f@ on entry
1357 * and end-of-file is received at a packet boundary, in which
1358 * case the function returns @RECV_EOF@; or (c) an I/O error
1359 * occurs, in which case @ioerr@ is called and the function
1360 * returns @RECV_FAIL@.
1361 */
c91413e6
MW
1362
1363static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1364 unsigned f, uint16 end, buf *b_out)
1365{
1366 struct tvec_output *o = tv->output;
1367 uint16 pk, u, v;
1368 const char *p; size_t n;
1369 dstr d = DSTR_INIT;
1370 buf *b = b_out;
1371 const struct tvec_regdef *rd;
1372 struct bench_timing bt;
1373 struct tvec_reg *reg = 0;
1374 unsigned i;
1375 int rc;
1376
1377 for (;;) {
c81c35df
MW
1378
1379 /* Read the next packet. If we didn't receive one then end the loop.
1380 * Otherwise, retrieve the packet type and check it against @end@: quit
1381 * the loop if we get a match.
1382 */
1383 rc = remote_recv(tv, &r->rc, f, b); if (rc) break;
c91413e6 1384 if (buf_getu16l(b, &pk)) goto bad;
c81c35df 1385 if (pk == end) { rc = 0; break; }
c91413e6 1386
c81c35df 1387 /* Dispatch based on the packet type. */
c91413e6
MW
1388 switch (pk) {
1389
1390 case TVPK_PROGRESS:
c81c35df
MW
1391 /* A progress report. Update the saved progress. */
1392
c91413e6
MW
1393 p = buf_getmem16l(b, &n); if (!p) goto bad;
1394 if (BLEFT(b)) goto bad;
1395
1396 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1397 break;
1398
1399 case TVPK_REPORT:
c81c35df
MW
1400 /* A report. Recover the message and pass it along. */
1401
c91413e6
MW
1402 if (buf_getu16l(b, &u)) goto bad;
1403 p = buf_getmem16l(b, &n); if (!p) goto bad;
1404 if (BLEFT(b)) goto bad;
1405
1406 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1407 tvec_report(tv, u, "%s", d.buf);
1408 break;
1409
1410 case TVPK_SKIPGRP:
c81c35df
MW
1411 /* A request to skip the group. Recover the excuse message and pass
1412 * it along.
1413 */
1414
c91413e6 1415 p = buf_getmem16l(b, &n); if (!p) goto bad;
c91413e6
MW
1416 if (BLEFT(b)) goto bad;
1417
c81c35df 1418 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
c91413e6
MW
1419 tvec_skipgroup(tv, "%s", d.buf);
1420 break;
1421
1422 case TVPK_SKIP:
c81c35df
MW
1423 /* A request to skip the test. Recover the excuse message and pass
1424 * it along, if it's not unreasonable.
1425 */
1426
c91413e6
MW
1427 if (!(tv->f&TVSF_ACTIVE)) {
1428 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1429 goto end;
1430 }
1431
1432 p = buf_getmem16l(b, &n); if (!p) goto bad;
1433 if (BLEFT(b)) goto bad;
1434
1435 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1436 tvec_skip(tv, "%s", d.buf);
1437 break;
1438
1439 case TVPK_FAIL:
c81c35df
MW
1440 /* A report that the test failed. Recover the detail message, if
1441 * any, and pass it along, if it's not unreasonable.
1442 */
1443
c91413e6
MW
1444 if (!(tv->f&TVSF_ACTIVE) &&
1445 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
1446 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
1447 tv->test->name);
1448 goto end;
1449 }
1450
1451 rc = buf_getbyte(b); if (rc < 0) goto bad;
1452 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1453 else p = 0;
1454 if (BLEFT(b)) goto bad;
e63124bc 1455
c91413e6
MW
1456 if (!p)
1457 tvec_fail(tv, 0);
1458 else {
1459 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1460 tvec_fail(tv, "%s", d.buf);
1461 }
1462 break;
e63124bc 1463
c91413e6 1464 case TVPK_DUMPREG:
c81c35df
MW
1465 /* A request to dump a register. */
1466
1467 /* Find the register definition. */
c91413e6
MW
1468 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1469 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
1470 if (i == u) goto found_reg;
1471 rc = ioerr(tv, &r->rc,
1472 "register definition %u out of range for test `%s'",
1473 u, tv->test->name);
1474 goto end;
1475 found_reg:
1476 if (v >= TVRD_LIMIT) {
1477 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1478 goto end;
1479 }
e63124bc 1480
c81c35df
MW
1481 /* Read the flag. If there's no register value, then `dump' its
1482 * absence. Otherwise retrieve the register value and dump it.
1483 */
c91413e6
MW
1484 rc = buf_getbyte(b); if (rc < 0) goto bad;
1485 if (!rc)
1486 tvec_dumpreg(tv, v, 0, rd);
1487 else {
1488 if (!reg) reg = xmalloc(tv->regsz);
1489 rd->ty->init(&reg->v, rd);
1490 rc = rd->ty->frombuf(b, &reg->v, rd);
1491 if (!rc) tvec_dumpreg(tv, v, &reg->v, rd);
1492 rd->ty->release(&reg->v, rd);
1493 if (rc) goto bad;
1494 }
1495 if (BLEFT(b)) goto bad;
1496 break;
1497
1498 case TVPK_BBENCH:
c81c35df
MW
1499 /* A report that we're starting a benchmark. Pass this along. */
1500
c91413e6
MW
1501 p = buf_getmem32l(b, &n); if (!p) goto bad;
1502 if (buf_getu16l(b, &u)) goto bad;
1503 if (BLEFT(b)) goto bad;
1504 if (u >= TVBU_LIMIT) {
1505 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1506 goto end;
1507 }
1508
1509 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1510 o->ops->bbench(o, d.buf, u);
1511 break;
1512
1513 case TVPK_EBENCH:
c81c35df
MW
1514 /* A report that a benchmark completed. Pass this along. */
1515
c91413e6
MW
1516 p = buf_getmem32l(b, &n); if (!p) goto bad;
1517 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
31d0247c
MW
1518 if (u >= TVBU_LIMIT) {
1519 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1520 goto end;
1521 }
c91413e6
MW
1522 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
1523 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
1524 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
1525 if (BLEFT(b)) goto bad;
1526
1527 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1528 o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
1529 break;
1530
1531 default:
c81c35df
MW
1532 /* Something else. This is unexpected. */
1533
c91413e6
MW
1534 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1535 goto end;
1536 }
1537 }
1538
1539end:
1540 DDESTROY(&d);
1541 xfree(reg);
1542 return (rc);
1543bad:
1544 rc = malformed(tv, &r->rc); goto end;
1545}
1546
c81c35df
MW
1547/* --- @reap_kid@ --- *
1548 *
1549 * Arguments: @struct tvec_state *tv@ = test-vector state
1550 * @struct tvec_remotectx *r@ = remote client context
1551 *
1552 * Returns: ---
1553 *
1554 * Use: Determine the exit status of a broken connection, setting
1555 * @r->exit@ appropriately.
1556 *
1557 * If @r->kid@ is negative, the exit status has already been
1558 * set, and nothing further happens; this is not an error.
1559 *
1560 * If @r->kid@ is zero, then there is no real child process
1561 * (e.g., because the remote connection is a network connection
1562 * or similar), so @r->exit@ is set equal to @RVXST_DISCONN@.
1563 *
1564 * If @r->kid@ is positive, then it holds a child process id;
1565 * the function waits for it to end and collects its exit status
1566 *
1567 * It is an error to call this function if the connection is not
1568 * broken.
1569 */
1570
c91413e6
MW
1571static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1572{
1573 pid_t kid;
1574 int st;
1575
c81c35df 1576 assert(r->rc.f&TVRF_BROKEN);
c91413e6
MW
1577 if (!r->kid)
1578 { r->exit = TVXST_DISCONN; r->kid = -1; }
1579 else if (r->kid > 0) {
1580 kid = waitpid(r->kid, &st, 0);
1581 if (kid < 0) {
1582 tvec_notice(tv, "failed to wait for remote child: %s",
1583 strerror(errno));
1584 r->exit = TVXST_ERR;
1585 } else if (!kid) {
1586 tvec_notice(tv, "remote child vanished without a trace");
1587 r->exit = TVXST_ERR;
1588 } else if (WIFCONTINUED(st))
1589 r->exit = TVXST_CONT;
1590 else if (WIFSIGNALED(st))
1591 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
1592 else if (WIFSTOPPED(st))
1593 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
1594 else if (WIFEXITED(st))
1595 r->exit = TVXST_EXIT | WEXITSTATUS(st);
1596 else {
1597 tvec_notice(tv, "remote child died with unknown status 0x%04x",
1598 (unsigned)st);
1599 r->exit = TVXST_UNK;
1600 }
1601 r->kid = -1;
1602 }
1603}
1604
c81c35df
MW
1605/* --- @report_errline@ --- *
1606 *
1607 * Arguments: @char *p@ = pointer to the line
1608 * @size_t n@ = length in characters
1609 * @void *ctx@ = context, secretly a @struct tvec_remotectx@
1610 *
1611 * Returns: ---
1612 *
1613 * Use: Print a line of stderr output from the child. If
1614 * @TVRF_MUFFLE@ is set, then discard the line silently.
1615 *
1616 * This is an @lbuf_func@, invoked via @drain_errfd@.
1617 */
1618
c91413e6
MW
1619static void report_errline(char *p, size_t n, void *ctx)
1620{
1621 struct tvec_remotectx *r = ctx;
1622 struct tvec_state *tv = r->tv;
1623
1624 if (p && !(r->rc.f&TVRF_MUFFLE))
1625 tvec_notice(tv, "child process stderr: %s", p);
1626}
1627
c81c35df
MW
1628/* --- @drain_errfd@ --- *
1629 *
1630 * Arguments: @struct tvec_state *tv@ = test-vector state
1631 * @struct tvec_remotectx *r@ = remote client context
1632 * @unsigned f@ = receive flags (@ERF_...@)
1633 *
1634 * Returns: Zero on success, %$-1$% on error.
1635 *
1636 * Use: Collect material written by the child to its stderr stream
1637 * and report it.
1638 *
1639 * If @f@ has @ERF_SILENT@ set, then discard the stderr material
1640 * without reporting it. Otherwise it is reported as
1641 * @TVLEV_NOTE@.
1642 *
1643 * if @f@ has @ERF_CLOSE@ set, then continue reading until
1644 * end-of-file is received; also, report any final partial line,
1645 * and close @r->errfd@.
1646 *
1647 * If @r->errfd@ is already closed, or never established, then
1648 * do nothing and return successfully.
1649 */
1650
c91413e6
MW
1651#define ERF_SILENT 0x0001u
1652#define ERF_CLOSE 0x0002u
1653static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
1654 unsigned f)
1655{
1656 char *p; size_t sz;
1657 ssize_t n;
1658 int rc;
1659
c81c35df
MW
1660 /* Preliminaries. Bail if there is no error stream to fetch. Arrange
1661 * (rather clumsily) to muffle the output if we're supposed to be client.
1662 * And set the nonblocking state on @errfd@ appropriately.
1663 */
31d0247c 1664 if (r->errfd < 0) { rc = 0; goto end; }
c91413e6
MW
1665 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
1666 else r->rc.f &= ~TVRF_MUFFLE;
1667 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
1668 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
1669 f&ERF_CLOSE ? "clear" : "set");
1670 goto end;
1671 }
1672
c81c35df 1673 /* Read pieces of error output and feed them into the line buffer. */
c91413e6
MW
1674 for (;;) {
1675 sz = lbuf_free(&r->errbuf, &p);
1676 n = read(r->errfd, p, sz);
1677 if (!n) break;
1678 if (n < 0) {
1679 if (errno == EINTR) continue;
1680 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1681 break;
1682 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1683 strerror(errno));
1684 goto end;
1685 }
1686 lbuf_flush(&r->errbuf, p, n);
1687 }
c81c35df
MW
1688
1689 /* Done. */
c91413e6
MW
1690 rc = 0;
1691end:
1692 if (f&ERF_CLOSE) {
1693 lbuf_close(&r->errbuf);
31d0247c 1694 close(r->errfd); r->errfd = -1;
c91413e6
MW
1695 }
1696 return (rc);
1697}
1698
c81c35df
MW
1699/* --- @disconnect_remote@ --- *
1700 *
1701 * Arguments: @struct tvec_state *tv@ = test-vector state
1702 * @struct tvec_remotectx *r@ = remote client context
1703 * @unsigned f@ = receive flags (@DCF_...@)
1704 *
1705 * Returns: ---
1706 *
1707 * Use: Disconnect and shut down all of the remote client state.
1708 *
1709 * If @f@ has @DCF_KILL@ set then send the child process (if
1710 * any) @SIGTERM@ to make sure it shuts down in a timely manner.
1711 *
1712 * In detail: this function closes the @infd@ and @outfd@
1713 * descriptors, drains and closes @errfd@, and collects the exit
1714 * status (if any).
1715 */
1716
c91413e6
MW
1717#define DCF_KILL 0x0100u
1718static void disconnect_remote(struct tvec_state *tv,
1719 struct tvec_remotectx *r, unsigned f)
1720{
c91413e6
MW
1721 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1722 close_comms(&r->rc);
c91413e6
MW
1723 drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
1724}
1725
c81c35df
MW
1726/* --- @connect_remote@ --- *
1727 *
1728 * Arguments: @struct tvec_state *tv@ = test-vector state
1729 * @struct tvec_remotectx *r@ = remote client context
1730 *
1731 * Returns: Zero on success, %$-1$% on error.
1732 *
1733 * Use: Connect to the test server.
1734 */
1735
c91413e6
MW
1736static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1737{
1738 const struct tvec_remoteenv *re = r->re;
1739 pid_t kid = 0;
1740 buf b;
1741 uint16 v;
1742 int infd = -1, outfd = -1, errfd = -1, rc;
1743
c81c35df 1744 /* If we're already connected, then there's nothing to do. */
c91413e6 1745 if (r->kid >= 0) { rc = 0; goto end; }
c81c35df
MW
1746
1747 /* Set the preliminary progress indication. */
1748 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1749
1750 /* Call the connection function to establish descriptors. */
c91413e6
MW
1751 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1752 { rc = -1; goto end; }
c81c35df
MW
1753
1754 /* Establish communications state. */
c91413e6
MW
1755 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
1756 lbuf_init(&r->errbuf, report_errline, r);
1757 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
1758
c81c35df 1759 /* Do version negotiation. */
31d0247c
MW
1760 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1761 dbuf_putu16l(&r->rc.bout, 0);
1762 dbuf_putu16l(&r->rc.bout, 0);
c91413e6 1763 } else { rc = -1; goto end; }
c91413e6
MW
1764 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
1765 { rc = -1; goto end; }
1766 if (buf_getu16l(&b, &v)) goto bad;
1767 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1768 if (v) {
1769 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
1770 goto end;
1771 }
c81c35df 1772 r->ver = v;
c91413e6 1773
c81c35df 1774 /* Begin the test group at the server. */
31d0247c
MW
1775 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1776 dbuf_putstr16l(&r->rc.bout, tv->test->name);
c91413e6
MW
1777 else { rc = -1; goto end; }
1778 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
1779 { rc = -1; goto end; }
1780 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
c81c35df
MW
1781
1782 /* Done. */
1783 rc = 0;
c91413e6
MW
1784end:
1785 if (rc) disconnect_remote(tv, r, DCF_KILL);
1786 return (rc);
1787bad:
1788 rc = malformed(tv, &r->rc); goto end;
1789}
1790
c81c35df
MW
1791/* --- @check_comms@ --- *
1792 *
1793 * Arguments: @struct tvec_state *tv@ = test-vector state
1794 * @struct tvec_remotectx *r@ = remote client context
1795 *
1796 * Returns: A @CONN_...@ code reflecting the current communication
1797 * state.
1798 *
1799 * Use: Determine the current connection state. If the connection
1800 * has recently broken (i.e., @TVRF_BROKEN@ is set in @r->rc.f@)
1801 * since the last time we checked then disconnect.
1802 */
1803
c91413e6
MW
1804static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1805{
1806 if (r->kid < 0)
1807 return (CONN_BROKEN);
1808 else if (r->rc.f&TVRF_BROKEN)
1809 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1810 else
1811 return (CONN_ESTABLISHED);
1812}
1813
c81c35df
MW
1814/* --- @try_reconnect@ --- *
1815 *
1816 * Arguments: @struct tvec_state *tv@ = test-vector state
1817 * @struct tvec_remotectx *r@ = remote client context
1818 *
1819 * Returns: A @CONN_...@ code reflecting the new communication state.
1820 *
1821 * Use: Reconnects to the server according to the configured
1822 * @TVRCN_...@ policy.
1823 */
1824
c91413e6
MW
1825static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1826{
1827 int rc;
1828
1829 switch (r->rc.f&TVRF_RCNMASK) {
1830 case TVRCN_DEMAND:
1831 rc = check_comms(tv, r);
1832 if (rc < CONN_ESTABLISHED) {
1833 close_comms(&r->rc);
1834 if (connect_remote(tv, r)) rc = CONN_FAILED;
1835 else rc = CONN_FRESH;
1836 }
1837 break;
1838 case TVRCN_FORCE:
1839 disconnect_remote(tv, r, DCF_KILL);
1840 if (connect_remote(tv, r)) rc = CONN_FAILED;
1841 else rc = CONN_FRESH;
1842 break;
1843 case TVRCN_SKIP:
1844 rc = check_comms(tv, r);
1845 break;
1846 default:
1847 abort();
1848 }
1849 return (rc);
1850}
1851
c81c35df
MW
1852/*----- Remote environment ------------------------------------------------*/
1853
1854/* --- @reset_vars@ --- *
1855 *
1856 * Arguments: @struct tvec_remotectx *r@ = remote client context
1857 *
1858 * Returns: ---
1859 *
1860 * Use: Reset the pseudoregisters set through @tvec_remoteset@.
1861 */
1862
c91413e6
MW
1863static void reset_vars(struct tvec_remotectx *r)
1864{
814e42ff
MW
1865 const struct tvec_remoteenv *re = r->re;
1866
31d0247c 1867 r->exwant = TVXST_RUN;
814e42ff
MW
1868 r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) |
1869 (re->r.dflt_reconn&TVRF_RCNMASK);
c91413e6
MW
1870 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1871}
1872
c81c35df
MW
1873/* --- @tvec_remotesetup@ --- *
1874 *
1875 * Arguments: @struct tvec_state *tv@ = test vector state
1876 * @const struct tvec_env *env@ = environment description
1877 * @void *pctx@ = parent context (ignored)
1878 * @void *ctx@ = context pointer to initialize
1879 *
1880 * Returns: ---
1881 *
1882 * Use: Initialize a timeout environment context.
1883 *
1884 * The environment description should be a @struct
1885 * tvec_remoteenv@ subclass suitable for use by the @connect@
1886 * function.
1887 */
1888
c91413e6
MW
1889void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1890 void *pctx, void *ctx)
1891{
1892 struct tvec_remotectx *r = ctx;
1893 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
814e42ff 1894 const struct tvec_env *subenv = re->r.env;
c91413e6
MW
1895
1896 r->tv = tv;
1897 init_comms(&r->rc);
1898 r->re = re; r->kid = -1;
1899 DCREATE(&r->prgwant); DCREATE(&r->progress);
1900 if (connect_remote(tv, r))
1901 tvec_skipgroup(tv, "failed to connect to test backend");
1902 reset_vars(r);
814e42ff
MW
1903 if (subenv && subenv->ctxsz) r->subctx = xmalloc(subenv->ctxsz);
1904 else r->subctx = 0;
1905 if (subenv && subenv->setup) subenv->setup(tv, subenv, r, r->subctx);
c91413e6
MW
1906}
1907
814e42ff 1908/* --- @tvec_remotefindvar@, @setvar_local@, @setvar_remote@ --- *
c81c35df
MW
1909 *
1910 * Arguments: @struct tvec_state *tv@ = test vector state
1911 * @const char *var@ = variable name to set
814e42ff
MW
1912 * @const union tvec_regval *rv@ = register value
1913 * @void **ctx_out@ = where to put the @setvar@ context
c81c35df
MW
1914 * @void *ctx@ = context pointer
1915 *
814e42ff
MW
1916 * Returns: @tvec_remotefindvar@ returns a pointer to the variable
1917 * definition, or null; @remote_setvar@ returns zero on success
1918 * or %$-1$% on error.
c81c35df
MW
1919 *
1920 * Use: Set a special variable. The following special variables are
1921 * supported.
1922 *
1923 * * %|@exit|% is the expected exit status; see @TVXF_...@ and
1924 * @TVXST_...@.
1925 *
1926 * * %|progress|% is the expected progress token when the test
1927 * completes. On successful completion, this will be
1928 * %|%DONE|%; it's %|%RUN|% on entry to the test function,
1929 * but that can call @tvec_setprogress@ to change it.
1930 *
1931 * * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
1932 */
1933
814e42ff
MW
1934static int setvar_local(struct tvec_state *tv, const char *var,
1935 const union tvec_regval *rv, void *ctx)
c91413e6
MW
1936{
1937 struct tvec_remotectx *r = ctx;
c91413e6
MW
1938
1939 if (STRCMP(var, ==, "@exit")) {
814e42ff
MW
1940 if (r->rc.f&TVRF_SETEXIT) return (tvec_dupreg(tv, var));
1941 r->exwant = rv->u; r->rc.f |= TVRF_SETEXIT; return (0);
c91413e6 1942 } else if (STRCMP(var, ==, "@progress")) {
814e42ff
MW
1943 if (r->rc.f&TVRF_SETPRG) return (tvec_dupreg(tv, var));
1944 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv->text.p, rv->text.sz);
1945 DPUTZ(&r->prgwant);
1946 r->rc.f |= TVRF_SETPRG; return (0);
c91413e6 1947 } else if (STRCMP(var, ==, "@reconnect")) {
814e42ff
MW
1948 if (r->rc.f&TVRF_SETRCN) return (tvec_dupreg(tv, var));
1949 r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv->u&TVRF_RCNMASK) | TVRF_SETRCN;
1950 return (0);
1951 } else assert(!"unknown var");
1952}
1953
1954static int setvar_remote(struct tvec_state *tv, const char *var,
1955 const union tvec_regval *rv, void *ctx)
1956{
1957 struct tvec_remotectx *r = ctx;
1958 buf b;
1959 int ch, rc;
c91413e6 1960
814e42ff
MW
1961 if (try_reconnect(tv, r) < 0) { rc = 0; goto end; }
1962
1963 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_SETVAR) {
1964 dbuf_putstr16l(&r->rc.bout, var);
1965 r->vd.def.ty->tobuf(DBUF_BUF(&r->rc.bout), rv, &r->vd.def);
1966 } else { rc = -1; goto end; }
1967
1968 rc = handle_packets(tv, r, 0, TVPK_SETVAR | TVPF_ACK, &b);
1969 if (rc) goto end;
1970 ch = buf_getbyte(&b);
1971 if (ch < 0) { rc = malformed(tv, &r->rc); goto end; }
1972 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1973
1974 rc = ch ? -1 : 0;
c91413e6
MW
1975end:
1976 return (rc);
1977}
1978
814e42ff
MW
1979const struct tvec_vardef *tvec_remotefindvar
1980 (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
1981{
1982 struct tvec_remotectx *r = ctx;
1983 const struct tvec_remoteenv *re = r->re;
1984 const struct tvec_env *subenv = re->r.env;
1985 const struct tvec_vardef *vd; void *varctx;
1986
1987 if (STRCMP(var, ==, "@exit"))
1988 { *ctx_out = r; return (&exit_var); }
1989 else if (STRCMP(var, ==, "@progress"))
1990 { *ctx_out = r; return (&progress_var); }
1991 else if (STRCMP(var, ==, "@reconnect"))
1992 { *ctx_out = r; return (&reconn_var); }
1993 else if (subenv && subenv->findvar) {
1994 vd = subenv->findvar(tv, var, &varctx, r->subctx);
1995 if (!vd) return (0);
1996 r->vd.regsz = vd->regsz; r->vd.setvar = setvar_remote;
1997 r->vd.def = vd->def;
1998 *ctx_out = r; return (&r->vd);
1999 } else
2000 return (0);
2001}
2002
2003/* --- @tvec_remotebefore@ --- *
2004 *
2005 * Arguments: @struct tvec_state *tv@ = test vector state
2006 * @void *ctx@ = context pointer
2007 *
2008 * Returns: ---
2009 *
2010 * Use: Invoke the subordinate environment's @before@ function.
2011 */
2012
2013void tvec_remotebefore(struct tvec_state *tv, void *ctx)
2014{
2015 struct tvec_remotectx *r = ctx;
2016 const struct tvec_remoteenv *re = r->re;
2017 const struct tvec_env *subenv = re->r.env;
2018
2019 if (subenv && subenv->before) subenv->before(tv, r->subctx);
2020}
2021
c81c35df
MW
2022/* --- @tvec_remoterun@ --- *
2023 *
2024 * Arguments: @struct tvec_state *tv@ = test vector state
2025 * @tvec_testfn *fn@ = test function to run
2026 * @void *ctx@ = context pointer for the test function
2027 *
2028 * Returns: ---
2029 *
2030 * Use: Run a test on a remote server.
2031 */
c91413e6
MW
2032
2033void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
2034{
2035 struct tvec_remotectx *r = ctx;
2036 union tvec_regval rv;
2037 unsigned f = 0;
2038#define f_exit 1u
2039#define f_progress 2u
2040#define f_fail 4u
2041 buf b;
2042 int rc;
2043
c81c35df 2044 /* Reconnect to the server according to policy. */
c91413e6
MW
2045 switch (try_reconnect(tv, r)) {
2046 case CONN_FAILED:
2047 tvec_skip(tv, "failed to connect to test backend"); return;
2048 case CONN_BROKEN:
2049 tvec_skip(tv, "no connection"); return;
2050 }
2051
c81c35df 2052 /* Set initial progress state. */
31d0247c 2053 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
c81c35df
MW
2054
2055 /* Send the command to the server and handle output. */
31d0247c
MW
2056 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
2057 tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
c91413e6 2058 tv->test->regs, tv->nreg, tv->regsz);
c81c35df 2059 else { goto fail; }
c91413e6 2060 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
c81c35df
MW
2061
2062 /* Deal with the outcome. */
c91413e6 2063 switch (rc) {
c81c35df 2064
c91413e6 2065 case RECV_FAIL:
c81c35df
MW
2066 /* Some kind of error. Abandon ship. */
2067
2068 fail:
2069 tvec_skip(tv, "remote test runner communications failed");
2070 disconnect_remote(tv, r, 0);
2071 break;
2072
c91413e6 2073 case RECV_EOF:
c81c35df
MW
2074 /* End-of-file at a packet boundary. The server crashed trying to run
2075 * our test. Collect the exit status and continue.
2076 */
c91413e6
MW
2077 reap_kid(tv, r);
2078 /* fall through */
c81c35df 2079
c91413e6 2080 case RECV_OK:
c81c35df
MW
2081 /* Successful completion (or EOF). */
2082
2083 /* Notice if the exit status isn't right. */
c91413e6 2084 if (r->exit != r->exwant) f |= f_exit;
c81c35df
MW
2085
2086 /* Notice if the progress token isn't right. */
c91413e6
MW
2087 if (r->progress.len != r->prgwant.len ||
2088 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
2089 f |= f_progress;
c81c35df
MW
2090
2091 /* If we found something wrong but the test is passing so far, then
2092 * report the failure and dump the input registers.
2093 */
c91413e6
MW
2094 if (f && (tv->f&TVSF_ACTIVE))
2095 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
c81c35df
MW
2096
2097 /* If the test failed, then report the exit and progress states
2098 * relative to their expectations.
2099 */
c91413e6
MW
2100 if (!(tv->f&TVSF_ACTIVE) &&
2101 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
c81c35df
MW
2102
2103 /* Note here that the test failed. */
c91413e6
MW
2104 f |= f_fail;
2105
c81c35df 2106 /* Report exit status. */
c91413e6
MW
2107 rv.u = r->exit;
2108 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
814e42ff 2109 &rv, &exit_var.def);
c91413e6
MW
2110 if (f&f_exit) {
2111 rv.u = r->exwant;
814e42ff 2112 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_var.def);
c91413e6
MW
2113 }
2114
c81c35df
MW
2115 /* Report progress token. */
2116 rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
c91413e6 2117 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
814e42ff 2118 &rv, &progress_var.def);
c91413e6 2119 if (f&f_progress) {
c81c35df 2120 rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
814e42ff 2121 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_var.def);
c91413e6
MW
2122 }
2123 }
2124
c81c35df
MW
2125 /* If we received end-of-file, then close the connection. Suppress
2126 * error output if the test passed: it was presumably expected.
2127 */
c91413e6
MW
2128 if (rc == RECV_EOF)
2129 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2130 break;
2131 }
2132
c91413e6
MW
2133#undef f_exit
2134#undef f_progress
2135#undef f_fail
2136}
2137
c81c35df
MW
2138/* --- @tvec_remoteafter@ --- *
2139 *
2140 * Arguments: @struct tvec_state *tv@ = test vector state
2141 * @void *ctx@ = context pointer
2142 *
2143 * Returns: ---
2144 *
2145 * Use: Reset variables to their default values.
2146 */
2147
2148void tvec_remoteafter(struct tvec_state *tv, void *ctx)
2149{
2150 struct tvec_remotectx *r = ctx;
814e42ff
MW
2151 const struct tvec_remoteenv *re = r->re;
2152 const struct tvec_env *subenv = re->r.env;
c81c35df
MW
2153
2154 reset_vars(r);
814e42ff 2155 if (subenv && subenv->after) subenv->after(tv, r->subctx);
c81c35df
MW
2156}
2157
2158/* --- @tvec_remoteteardown@ --- *
2159 *
2160 * Arguments: @struct tvec_state *tv@ = test vector state
2161 * @void *ctx@ = context pointer
2162 *
2163 * Returns: ---
2164 *
2165 * Use: Tear down the remote environment.
2166 */
2167
c91413e6
MW
2168void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2169{
2170 struct tvec_remotectx *r = ctx;
814e42ff
MW
2171 const struct tvec_remoteenv *re = r->re;
2172 const struct tvec_env *subenv = re->r.env;
31d0247c 2173 buf b;
c91413e6 2174
814e42ff
MW
2175 if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
2176 xfree(r->subctx);
31d0247c
MW
2177 if (r->rc.outfd >= 0) {
2178 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
2179 if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
2180 if (BLEFT(&b)) malformed(tv, &r->rc);
c91413e6 2181 }
31d0247c
MW
2182 disconnect_remote(tv, r, 0); release_comms(&r->rc);
2183 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
c91413e6
MW
2184}
2185
2186/*----- Connectors --------------------------------------------------------*/
2187
c81c35df
MW
2188/* --- @fork_common@ --- *
2189 *
2190 * Arguments: @pid_t *kid_out@ = where to put child process-id
2191 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2192 * descriptors
2193 * @struct tvec_state *tv@ = test vector state
2194 *
2195 * Returns: Zero on success, %$-1$% on failure.
2196 *
2197 * Use: Common @fork@ machinery for the connectors. Create a
2198 * subprocess. On successful return, in the subprocess,
2199 * @*kid_out@ is zero, and the error descriptor replaces the
2200 * standard-error descriptor; in the parent, @*kid_out@ is the
2201 * child process-id, and @*errfd_out@ is a descriptor on which
2202 * the child's standard-error output can be read; in both
2203 * @*infd_out@ and @*outfd_out@ are descriptors for input and
2204 * output respectively -- they're opposite ends of pipes, but
2205 * obviously they're crossed over so that the parent's output
2206 * matches the child's input and %%\emph{vice versa}%%.
2207 */
2208
c91413e6
MW
2209static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
2210 int *errfd_out, struct tvec_state *tv)
2211{
2212 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
2213 pid_t kid = -1;
2214 int rc;
2215
c81c35df 2216 /* Try to create the pipes. */
c91413e6
MW
2217 if (pipe(p0) || pipe(p1) || pipe(pe) ||
2218 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2219 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2220 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
2221 tvec_error(tv, "pipe failed: %s", strerror(errno));
2222 rc = -1; goto end;
2223 }
2224
c81c35df
MW
2225 /* Flush all of the stream buffers so that we don't get duplicated
2226 * output.
2227 */
c91413e6
MW
2228 fflush(0);
2229
c81c35df 2230 /* Try to set up the child process. */
c91413e6
MW
2231 kid = fork();
2232 if (kid < 0) {
2233 tvec_error(tv, "fork failed: %s", strerror(errno));
2234 rc = -1; goto end;
2235 }
2236
2237 if (!kid) {
c81c35df
MW
2238 /* Child process. */
2239
c91413e6
MW
2240 *kid_out = 0;
2241 *infd_out = p0[0]; p0[0] = -1;
2242 *outfd_out = p1[1]; p1[1] = -1;
2243 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
2244 fprintf(stderr, "failed to establish child stderr: %s",
2245 strerror(errno));
c81c35df 2246 _exit(127);
c91413e6
MW
2247 }
2248 } else {
c81c35df
MW
2249 /* Parent process. */
2250
c91413e6
MW
2251 *kid_out = kid; kid = -1;
2252 *infd_out = p1[0]; p1[0] = -1;
2253 *outfd_out = p0[1]; p0[1] = -1;
2254 *errfd_out = pe[0]; pe[0] = -1;
2255 }
2256
c81c35df 2257 /* All done. */
c91413e6 2258 rc = 0;
c81c35df 2259
c91413e6 2260end:
c81c35df 2261 /* Clean up. So much of this... */
c91413e6
MW
2262 if (p0[0] >= 0) close(p0[0]);
2263 if (p0[1] >= 0) close(p0[1]);
2264 if (p1[0] >= 0) close(p1[0]);
2265 if (p1[1] >= 0) close(p1[1]);
2266 if (pe[0] >= 0) close(pe[0]);
2267 if (pe[1] >= 0) close(pe[1]);
2268 return (rc);
2269}
2270
c81c35df
MW
2271/* --- @tvec_fork@ --- *
2272 *
2273 * Arguments: @pid_t *kid_out@ = where to put child process-id
2274 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2275 * descriptors
2276 * @struct tvec_state *tv@ = test vector state
2277 * @const struct tvec_remoteenv@ = the remote environment
2278 *
2279 * Returns: Zero on success, %$-1$% on failure.
2280 *
2281 * Use: Starts a remote server running in a fork of the main
2282 * process. This is useful for testing functions which might --
2283 * or are even intended to -- crash.
2284 */
2285
c91413e6
MW
2286int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2287 struct tvec_state *tv, const struct tvec_remoteenv *env)
2288{
2289 struct tvec_config config;
2290 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
2291 pid_t kid = -1;
2292 int infd = -1, outfd = -1, errfd = -1;
2293 int rc;
2294
2295 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2296 if (!kid) {
31d0247c 2297 if (tv->fp) fclose(tv->fp);
c91413e6
MW
2298 config.tests = rf->f.tests ? rf->f.tests : tv->tests;
2299 config.nrout = tv->nrout; config.nreg = tv->nreg;
2300 config.regsz = tv->regsz;
2301 _exit(tvec_remoteserver(infd, outfd, &config));
2302 }
2303
2304 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2305 rc = 0;
2306end:
2307 return (rc);
2308}
2309
c81c35df
MW
2310/* --- @tvec_exec@ --- *
2311 *
2312 * Arguments: @pid_t *kid_out@ = where to put child process-id
2313 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2314 * descriptors
2315 * @struct tvec_state *tv@ = test vector state
2316 * @const struct tvec_remoteenv@ = the remote environment
2317 *
2318 * Returns: Zero on success, %$-1$% on failure.
2319 *
2320 * Use: Starts a remote server by running some program. The command
2321 * given in the environment description will probably some hairy
2322 * shell rune allowing for configuration via files or
2323 * environment variables.
2324 */
2325
c91413e6
MW
2326int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2327 struct tvec_state *tv, const struct tvec_remoteenv *env)
2328{
2329 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
2330 pid_t kid = -1;
2331 int infd = -1, outfd = -1, errfd = -1;
2332 mdup_fd v[2];
2333 int rc;
2334
2335 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2336 if (!kid) {
2337 v[0].cur = infd; v[0].want = STDIN_FILENO;
2338 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
2339 if (mdup(v, 2)) {
2340 fprintf(stderr, "failed to establish standard file descriptors: %s",
2341 strerror(errno));
2342 exit(127);
2343 }
2344 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
2345 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
2346 exit(127);
2347 }
2348
2349 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2350 rc = 0;
2351end:
2352 return (rc);
2353}
e63124bc
MW
2354
2355/*----- That's all, folks -------------------------------------------------*/