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