5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
37 #include <sys/types.h>
53 #include "tvec-adhoc.h"
54 #include "tvec-bench.h"
55 #include "tvec-output.h"
56 #include "tvec-remote.h"
57 #include "tvec-types.h"
59 /*----- Preliminaries -----------------------------------------------------*/
61 /* The control macros I'm using below provoke `dangling-else' warnings from
62 * compilers. Suppress them. I generally don't care.
65 #if GCC_VERSION_P(7, 1)
66 # pragma GCC diagnostic ignored "-Wdangling-else"
67 #elif GCC_VERSION_P(4, 2)
68 # pragma GCC diagnostic ignored "-Wparentheses"
71 #if CLANG_VERSION_P(3, 1)
72 # pragma clang diagnostic ignored "-Wdangling-else"
75 /*----- Basic I/O ---------------------------------------------------------*/
77 /* --- @init_comms@ --- *
79 * Arguments: @struct tvec_remotecomms *rc@ = communication state
83 * Use: Initialize a communication state. This doesn't allocate any
84 * resurces: it just ensures that everything is set up so that
85 * subsequent operations -- in particular @release_comms@ --
89 static void init_comms(struct tvec_remotecomms *rc)
91 rc->bin = 0; rc->binsz = 0; DBCREATE(&rc->bout);
92 rc->infd = rc->outfd = -1; rc->f = 0;
95 /* --- @close_comms@ --- *
97 * Arguments: @struct tvec_remotecomms *rc@ = communication state
101 * Use: Close the input and output descriptors.
103 * If the descriptors are already closed -- or were never opened
104 * -- then nothing happens.
107 static void close_comms(struct tvec_remotecomms *rc)
110 if (rc->infd != rc->outfd) close(rc->infd);
114 { close(rc->outfd); rc->outfd = -1; }
115 rc->f |= TVRF_BROKEN;
118 /* --- @release_comms@ --- *
120 * Arguments: @struct tvec_state *tv@ = test-vector state
121 * @struct tvec_remotecomms *rc@ = communication state
125 * Use: Releases the resources -- most notably the input and output
126 * buffers -- held by the communication state. Also calls
130 static void release_comms(struct tvec_state *tv, struct tvec_remotecomms *rc)
131 { close_comms(rc); x_free(tv->a, rc->bin); DBDESTROY(&rc->bout); }
133 /* --- @setup_comms@ --- *
135 * Arguments: @struct tvec_remotecomms *rc@ = communication state
136 * @int infd, outfd@ = input and output file descriptors
140 * Use: Use the given descriptors for communication.
142 * Clears the private flags.
145 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
147 rc->infd = infd; rc->outfd = outfd;
148 rc->binoff = rc->binlen = 0;
154 * Arguments: @struct tvec_state *tv@ = test-vector state
155 * @struct tvec_remotecomms *rc@ = communication state
156 * @const char *msg, ...@ = format string and arguments
160 * Use: Reports the message as an error, closes communications and
161 * marks them as broken.
164 static PRINTF_LIKE(3, 4)
165 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
166 const char *msg, ...)
171 close_comms(rc); rc->f |= TVRF_BROKEN;
172 tvec_report_v(tv, TVLV_ERR, msg, &ap);
177 /* --- @send_all@ --- *
179 * Arguments: @struct tvec_state *tv@ = test-vector state
180 * @struct tvec_remotecomms *rc@ = communication state
181 * @const unsigned char *p@, @size_t sz@ = output buffer
183 * Returns: Zero on success, %$-1$% on error.
185 * Use: Send the output buffer over the communication state's output
186 * descriptor, even if it has to be written in multiple pieces.
189 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
190 const unsigned char *p, size_t sz)
196 n = write(rc->outfd, p, sz);
200 ret = ioerr(tv, rc, "failed to send: %s",
201 n ? strerror(errno) : "empty write");
210 /* --- @recv_all@ --- *
212 * Arguments: @struct tvec_state *tv@ = test-vector state
213 * @struct tvec_remotecomms *rc@ = communication state
214 * @unsigned f@ = flags (@RCVF_...@)
215 * @unsigned char *p@, @size_t sz@ = input buffer
216 * @size_t min@ = minimum acceptable size to read
217 * @size_t *n_out@ = size read
219 * Returns: A @RECV_...@ code.
221 * Use: Receive data on the communication state's input descriptor to
222 * read at least @min@ bytes into the input buffer, even if it
223 * has to be done in multiple pieces. If more data is readily
224 * available, then up to @sz@ bytes will be read in total.
226 * If the descriptor immediately reports end-of-file, and
227 * @RCVF_ALLOWEOF@ is set in @f@, then return @RECV_EOF@.
228 * Otherwise, EOF is treated as an I/O error, resulting in a
229 * call to @ioerr@ and a return code of @RECV_FAIL@. If the
230 * read succeeded, then set @*n_out@ to the number of bytes read
231 * and return @RECV_OK@.
234 #define RCVF_ALLOWEOF 1u
242 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
243 unsigned f, unsigned char *p, size_t sz,
244 size_t min, size_t *n_out)
250 n = read(rc->infd, p, sz);
252 p += n; sz -= n; tot += n;
253 if (tot >= min) break;
254 } else if (!n && !tot && (f&RCVF_ALLOWEOF))
255 { rc->f |= TVRF_BROKEN; return (RECV_EOF); }
257 return (ioerr(tv, rc, "failed to receive: %s",
258 n ? strerror(errno) : "unexpected end-of-file"));
260 *n_out = tot; return (RECV_OK);
265 /* --- @buferr@ --- *
267 * Arguments: @struct tvec_state *tv@ = test-vector state
268 * @struct tvec_remotecomms *rc@ = communication state
272 * Use: Report a problem preparing the output buffer.
275 static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
276 { return (ioerr(tv, rc, "failed to build output packet")); }
278 /* --- @malformed@ --- *
280 * Arguments: @struct tvec_state *tv@ = test-vector state
281 * @struct tvec_remotecomms *rc@ = communication state
285 * Use: Report an I/O error that the incoming packet is malformed.
288 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
289 { return (ioerr(tv, rc, "received malformed packet")); }
291 /* --- @remote_send@ --- *
293 * Arguments: @struct tvec_state *tv@ = test-vector state
294 * @struct tvec_remotecomms *rc@ = communication state
296 * Returns: Zero on success, %$-1$% on error.
298 * Use: Send the accuulated contents of the output buffer @rc->bout@.
300 * The function arranges to convert @SIGPIPE@ into an error.
302 * If the output buffer is broken, report this as an I/O error.
305 #define SENDBUFSZ 4096
307 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
309 void (*opipe)(int) = SIG_ERR;
312 /* Various preflight checks. */
313 if (rc->f&TVRF_BROKEN) { ret = -1; goto end; }
314 if (DBBAD(&rc->bout)) { ret = buferr(tv, rc); goto end; }
316 /* Arrange to trap broken-pipe errors. */
317 opipe = signal(SIGPIPE, SIG_IGN);
318 if (opipe == SIG_ERR) {
319 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
323 /* Transmit the packet. */
324 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
325 { ret = -1; goto end; }
327 /* Done. Put things back the way we found them. */
331 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
335 /* --- @receive_buffered@ --- *
337 * Arguments: @struct tvec_state *tv@ = test-vector state
338 * @struct tvec_remotecomms *rc@ = communication state
339 * @unsigned f@ = flags (@RCVF_...@)
340 * @size_t want@ = data block size required
342 * Returns: A @RECV_...@ code.
344 * Use: Reads a block of data from the input descriptor into the
347 * This is the main machinery for manipulating the input buffer.
348 * The buffer has three regions:
350 * * from the buffer start to @rc->binoff@ is `consumed';
351 * * from @rc->binoff@ to @rc->binlen@ is `available'; and
352 * * from @rc->binlen@ to @rc->binsz@ is `free'.
354 * Data is read into the start of the `free' region, and the
355 * `available' region is extended to include it. Data in the
356 * `consumed' region is periodically discarded by moving the
357 * data from the `available' region to the start of the buffer
358 * and decreasing @rc->binoff@ and @rc->binlen@.
360 * This function ensures that the `available' region contains at
361 * least @want@ bytes, by (a) extending the buffer, if
362 * necessary, so that @rc->binsz >= rc->binoff + want@, and (b)
363 * reading fresh data from the input descriptor to extend the
364 * `available' region.
366 * If absolutely no data is available, and @RCVF_ALLOWEOF@ is
367 * set in @f@, then return @RECV_EOF@. On I/O errors, including
368 * a short read or end-of-file if @RCVF_ALLOWEOF@ is clear,
369 * return @RECV_FAIL@. On success, return @RECV_OK@. The
370 * amount of data read is indicated by updating the input buffer
371 * variables as described above.
374 #define RECVBUFSZ 4096u
376 static int receive_buffered(struct tvec_state *tv,
377 struct tvec_remotecomms *rc,
378 unsigned f, size_t want)
383 /* If we can supply the caller's requirement from the buffer then do
386 if (rc->binlen - rc->binoff >= want) return (RECV_OK);
388 /* If the buffer is too small then we must grow it. */
389 GROWBUF_EXTEND(size_t, tv->a, rc->bin, rc->binsz, want, RECVBUFSZ, 1);
391 /* Shunt the unused existing material to the start of the buffer. */
392 memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff);
393 rc->binlen -= rc->binoff; rc->binoff = 0;
395 /* Satisfy the caller from the input stream, and try to fill up as much of
396 * the rest of the buffer as we can.
398 ret = recv_all(tv, rc, rc->binlen ? 0 : f,
399 rc->bin + rc->binlen, rc->binsz - rc->binlen,
400 want - rc->binlen, &sz);
401 if (ret) return (ret);
403 /* Note how much material we have and return. */
404 rc->binlen += sz; return (RECV_OK);
407 /* --- @remote_recv@ --- *
409 * Arguments: @struct tvec_state *tv@ = test-vector state
410 * @unsigned f@ = flags (@RCVF_...@)
411 * @buf *b_out@ = buffer to establish around the packet contents
413 * Returns: A @RECV_...@ code.
415 * Use: Receive a packet into the input buffer @rc->bin@ and
416 * establish @*b_out@ to read from it.
419 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
420 unsigned f, buf *b_out)
426 ASSIGN64(szmax, (size_t)-1);
428 /* Preflight checks. */
429 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
431 /* See if we can read the next packet length from what we already have. */
432 ret = receive_buffered(tv, rc, f, 8); if (ret) return (ret);
433 LOAD64_L_(k, rc->bin + rc->binoff); rc->binoff += 8;
434 if (CMP64(k, >, szmax))
435 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
436 (unsigned long)HI64(k), (unsigned long)LO64(k)));
437 want = GET64(size_t, k);
439 /* Read the next packet payload. */
440 ret = receive_buffered(tv, rc, 0, want); if (ret) return (ret);
441 buf_init(b_out, rc->bin + rc->binoff, want); rc->binoff += want;
445 /* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
447 * Arguments: @tag@ = control structure tag
448 * @struct tvec_state *tv@ = test-vector state
449 * @struct tvec_remotecomms *rc@ = communication state
450 * @unsigned fl@ = flags (@QF_...@)
451 * @unsigned pk@ = packet type
453 * Use: This is syntactically a statement head: the syntax is
454 * @QUEUEPK(tv, rc, f) body [else alt]@. The @body@ should
455 * write material to the output buffer @rc->bout@. The macro
456 * applies appropriate framing. If enough material has been
457 * collected, or if @QF_FORCE@ is set in @fl@, then
458 * @remote_send@ is invoked to transmit the buffered packets.
459 * If there is an error of any kind, then the @alt@ statement,
460 * if any, is executed.
464 #define QUEUEPK_TAG(tag, tv, rc, fl, pk) \
465 if ((rc)->f&TVRF_BROKEN) MC_GOELSE(tag##__else); else \
466 MC_ALLOWELSE(tag##__else) \
467 MC_AFTER(tag##__send, { \
468 if ((DBBAD(&(rc)->bout) && (buferr((tv), (rc)), 1)) || \
469 ((((fl)&QF_FORCE) || DBLEN(&(rc)->bout) >= SENDBUFSZ) && \
470 remote_send(tv, rc))) \
471 MC_GOELSE(tag##__else); \
473 DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L) \
474 MC_BEFORE(tag##__pkty, { \
475 dbuf_putu16l(&(rc)->bout, (pk)); \
478 #define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
480 /*----- Packet types ------------------------------------------------------*/
482 #define TVPF_ACK 0x0001u
484 #define TVPK_VER 0x0000u /* --> min, max: u16 *
486 #define TVPK_BGROUP 0x0002u /* --> name: str16
488 #define TVPK_SETVAR 0x0004u /* --> name: str16, rv: value
490 #define TVPK_TEST 0x0006u /* --> in: regs
492 #define TVPK_EGROUP 0x0008u /* --> --- *
495 #define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
496 #define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
498 #define TVPK_SKIPGRP 0x0104u /* <-- excuse: str16 */
499 #define TVPK_SKIP 0x0106u /* <-- excuse: str16 */
500 #define TVPK_FAIL 0x0108u /* <-- flag: u8, detail: str16 */
501 #define TVPK_DUMPREG 0x010au /* <-- ri: u16; disp: u16;
502 * flag: u8, rv: value */
503 #define TVPK_BBENCH 0x010cu /* <-- unit: u16, ident: regs */
504 #define TVPK_EBENCH 0x010eu /* <-- unit: u16; flags: u16;
507 /*----- Server ------------------------------------------------------------*/
509 /* Forward declaration of output operations. */
510 static const struct tvec_outops remote_ops;
512 static struct tvec_state srvtv; /* server's test-vector state */
513 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
514 static struct tvec_output srvout = { &remote_ops }; /* output state */
516 /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
518 * Arguments: @const char *status@ = progress status token format
519 * @va_list ap@ = argument tail
523 * Use: Reports the progress of a test execution to the client.
525 * The framework makes use of tokens beginning with %|%|%:
527 * * %|%IDLE|%: during the top-level server code;
529 * * %|%SETUP|%: during the enclosing environment's @before@
532 * * %|%RUN|%: during the environment's @run@ function, or the
535 * * %|%DONE|%: during the enclosing environment's @after@
538 * The intent is that a test can use the progress token to check
539 * that a function which is expected to crash does so at the
540 * correct point, so it's expected that more complex test
541 * functions and/or environments will set their own progress
542 * tokens to reflect what's going on.
545 int tvec_setprogress(const char *status, ...)
550 va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
554 int tvec_setprogress_v(const char *status, va_list *ap)
556 /* Force immediate output in case we crash before the buffer is output
559 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
560 dbuf_vputstrf16l(&srvrc.bout, status, ap);
565 /* --- @tvec_remoteserver@ --- *
567 * Arguments: @int infd@, @int outfd@ = input and output file descriptors
568 * @const struct tvec_config *config@ = test configuration
570 * Returns: Suggested exit code.
572 * Use: Run a test server, reading packets from @infd@ and writing
573 * responses and notifications to @outfd@, and invoking tests as
574 * described by @config@.
576 * This function is not particularly general purpose. It
577 * expects to `take over' the process, and makes use of private
581 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
587 const struct tvec_test *t, *const *tt;
589 const struct tvec_env *env = 0;
590 const struct tvec_vardef *vd = 0; void *varctx;
591 struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
595 /* Initialize the communication machinery. */
596 setup_comms(&srvrc, infd, outfd);
598 /* Begin a test session using our custom output driver. */
599 tvec_begin(&srvtv, config, &srvout);
601 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
602 * there's only version zero, so we return that.
604 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
605 if (buf_getu16l(&b, &pk)) goto bad;
606 if (pk != TVPK_VER) {
607 rc = ioerr(&srvtv, &srvrc,
608 "unexpected packet type 0x%04x instead of client version",
612 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
613 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
614 dbuf_putu16l(&srvrc.bout, 0);
615 else { rc = -1; goto end; }
617 /* Handle packets until the server closes the connection.
619 * The protocol looks much simpler from our point of view than from the
622 * * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
624 * * Receive zero or more @TVPK_BGROUP@. Open a test group, producing
625 * output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
627 * -- Receive zero or more @TVPK_TEST@. Run a test, producing output
628 * packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
630 * -- Receive @TVPK_EGROUP@. Maybe produce output packets, and
631 * answer with @TVPK_EGROUP | TVPF_ACK@.
637 /* Read a packet. End-of-file is expected here (and pretty much nowhere
638 * else). Otherwise, we expect to see @TVPK_BGROUP@.
640 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
641 if (rc == RECV_EOF) break;
642 else if (rc == RECV_FAIL) goto end;
643 if (buf_getu16l(&b, &pk)) goto bad;
650 /* Parse the packet payload. */
651 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
652 if (BLEFT(&b)) goto bad;
654 /* Find the group given its name. */
655 for (tt = srvtv.cfg.tests; *tt; tt++) {
657 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
660 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
665 /* Set up the test environment. */
666 srvtv.test = t; env = t->env;
667 if (env && env->setup == tvec_remotesetup)
668 env = ((struct tvec_remoteenv *)env)->r.env;
669 if (!env || !env->ctxsz) ctx = 0;
670 else ctx = pool_alloc(srvtv.p_group, env->ctxsz);
671 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
673 /* Initialize the registers. */
674 tvec_initregs(&srvtv);
676 /* Report that the group has been opened and that we're ready to run
679 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
680 else { rc = -1; goto end; }
682 /* Handle packets until we're told to end the group. */
685 /* Read a packet. We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
686 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
687 if (buf_getu16l(&b, &pk)) goto bad;
694 /* Check the payload. */
695 if (BLEFT(&b)) goto bad;
697 /* Leave the group loop. */
701 /* Set a subenvironment variable. */
703 /* Get the variable name. */
704 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
705 DRESET(&d); DPUTM(&d, p, sz); DPUTZ(&d);
707 /* Look up the variable definition. */
708 if (env && env->findvar) {
709 vd = env->findvar(&srvtv, d.buf, &varctx, ctx);
710 if (vd) goto found_var;
712 rc = tvec_unkregerr(&srvtv, d.buf); goto setvar_end;
715 /* Set up the register. */
716 if (vd->regsz <= sizeof(rbuf))
719 GROWBUF_REPLACE(size_t, srvtv.a, r_alloc, rsz, vd->regsz,
720 8*sizeof(void *), 1);
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);
730 /* Send the reply. */
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; }
741 /* Parse the packet payload. */
742 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
743 0, 0, srvtv.cfg.nreg, srvtv.cfg.regsz))
745 if (BLEFT(&b)) goto bad;
747 /* If we're not skipping the test group, then actually try to
750 if (!(srvtv.f&TVSF_SKIP)) {
752 /* Prepare the output registers and reset the test outcome.
753 * (The environment may force a skip.)
755 for (i = 0; i < srvtv.cfg.nrout; i++)
756 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
757 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
758 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
760 /* Invoke the environment @before@ function. */
761 tvec_setprogress("%%SETUP");
762 if (env && env->before) env->before(&srvtv, ctx);
764 /* Run the actual test. */
765 if (!(srvtv.f&TVSF_ACTIVE))
766 /* setup forced a skip */;
768 tvec_setprogress("%%RUN");
770 env->run(&srvtv, t->fn, ctx);
772 t->fn(srvtv.in, srvtv.out, ctx);
773 tvec_check(&srvtv, 0);
777 /* Conclude the test. */
778 tvec_setprogress("%%DONE");
779 if (env && env->after) env->after(&srvtv, ctx);
780 tvec_endtest(&srvtv);
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);
786 else { rc = -1; goto end; }
790 /* Some other kind of packet. Complain. */
792 rc = ioerr(&srvtv, &srvrc,
793 "unexpected packet type 0x%04x during test group",
801 /* The test group completed. */
803 /* Tear down the environment and release other resources. */
804 if (env && env->teardown) env->teardown(&srvtv, ctx);
805 tvec_releaseregs(&srvtv);
806 srvtv.test = 0; env = 0; ctx = 0; pool_recycle(srvtv.p_group);
808 /* Report completion. */
809 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
810 else { rc = -1; goto end; }
814 rc = ioerr(&srvtv, &srvrc,
815 "unexpected packet type 0x%04x at top level", pk);
821 /* Clean up and return. */
822 if (env && env->teardown) env->teardown(&srvtv, ctx);
823 if (vd) vd->def.ty->release(&r->v, &vd->def);
824 x_free(srvtv.a, r_alloc); dstr_destroy(&d);
825 if (srvtv.test) tvec_releaseregs(&srvtv);
826 release_comms(&srvtv, &srvrc); tvec_end(&srvtv);
830 /* Miscellaneous malformed packet. */
831 rc = malformed(&srvtv, &srvrc); goto end;
834 /*----- Server output driver ----------------------------------------------*/
836 /* --- @remote_bsession@ --- *
838 * Arguments: @struct tvec_output *o@ = output sink (ignored)
839 * @struct tvec_state *tv@ = the test state producing output
843 * Use: Begin a test session.
845 * The remote driver does nothing at all.
848 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
851 /* --- @remote_esession@ --- *
853 * Arguments: @struct tvec_output *o@ = output sink (ignored)
855 * Returns: Suggested exit code.
857 * Use: End a test session.
859 * The remote driver returns a suitable exit code without
863 static int remote_esession(struct tvec_output *o)
864 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
866 /* --- @remote_bgroup@ --- *
868 * Arguments: @struct tvec_output *o@ = output sink (ignored)
872 * Use: Begin a test group.
874 * This is a stub which should never be called.
877 static void remote_bgroup(struct tvec_output *o)
878 { assert(!"remote_bgroup"); }
880 /* --- @remote_skipgroup@ --- *
882 * Arguments: @struct tvec_output *o@ = output sink (ignored)
883 * @const char *excuse@, @va_list *ap@ = reason for skipping the
888 * Use: Report that a test group is being skipped.
890 * The remote driver sends a @TVPK_SKIP@ packet to its client.
893 static void remote_skipgroup(struct tvec_output *o,
894 const char *excuse, va_list *ap)
896 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
897 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
900 /* --- @remote_egroup@ --- *
902 * Arguments: @struct tvec_output *o@ = output sink (ignored)
906 * Use: Report that a test group has finished.
908 * This is a stub which should never be called.
911 static void remote_egroup(struct tvec_output *o)
912 { assert(!"remote_egroup"); }
914 /* --- @remote_btest@ --- *
916 * Arguments: @struct tvec_output *o@ = output sink (ignored)
920 * Use: Report that a test is starting.
922 * This is a stub which should never be called.
925 static void remote_btest(struct tvec_output *o)
926 { assert(!"remote_btest"); }
928 /* --- @remote_skip@, @remote_fail@ --- *
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
939 * Use: Report that a test has been skipped or failed.
941 * The remote driver sends a @TVPK_SKIP@ or @TVPK_FAIL@ packet
942 * to its client as appropriate.
945 static void remote_skip(struct tvec_output *o,
946 const char *excuse, va_list *ap)
948 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
949 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
952 static void remote_fail(struct tvec_output *o,
953 const char *detail, va_list *ap)
955 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
957 dbuf_putbyte(&srvrc.bout, 0);
959 dbuf_putbyte(&srvrc.bout, 1);
960 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
964 /* --- @remote_dumpreg@ --- *
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
973 * Use: Dump a register.
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.
980 static void remote_dumpreg(struct tvec_output *o,
981 unsigned disp, const union tvec_regval *rv,
982 const struct tvec_regdef *rd)
984 const struct tvec_regdef *reg;
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");
993 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
994 dbuf_putu16l(&srvrc.bout, r);
995 dbuf_putu16l(&srvrc.bout, disp);
997 dbuf_putbyte(&srvrc.bout, 0);
999 dbuf_putbyte(&srvrc.bout, 1);
1000 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
1005 /* --- @remote_etest@ --- *
1007 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1008 * @unsigned outcome@ = the test outcome
1012 * Use: Report that a test has finished.
1014 * The remote driver does nothing at all.
1017 static void remote_etest(struct tvec_output *o, unsigned outcome)
1020 /* --- @remote_report@ --- *
1022 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1023 * @unsigned level@ = message level (@TVLV_...@)
1024 * @const char *msg@, @va_list *ap@ = format string and
1029 * Use: Report a message to the user.
1031 * The remote driver sends a @TVPK_REPORT@ packet to its
1032 * client. If its attempt to transmit the packet fails, then
1033 * the message is written to the standard error stream instead,
1034 * in the hope that this will help it be noticed.
1037 static void remote_report(struct tvec_output *o, unsigned level,
1038 const char *msg, va_list *ap)
1040 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
1041 dbuf_putu16l(&srvrc.bout, level);
1042 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
1044 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
1045 vfprintf(stderr, msg, *ap);
1046 fputc('\n', stderr);
1050 /* --- @remote_bbench@ --- *
1052 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1053 * @const char *desc@ = adhoc test description, must be null
1054 * @unsigned unit@ = measurement unit (@BTU_...@)
1058 * Use: Report that a benchmark has started.
1060 * The remote driver sends a @TVPK_BBENCH@ packet to its client.
1063 static void remote_bbench(struct tvec_output *o,
1064 const char *desc, unsigned unit)
1067 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
1068 dbuf_putu16l(&srvrc.bout, unit);
1069 tvec_serialize(srvtv.in, DBUF_BUF(&srvrc.bout), srvtv.test->regs,
1070 TVRF_ID, TVRF_ID, srvtv.cfg.nreg, srvtv.cfg.regsz);
1074 /* --- @remote_ebench@ --- *
1076 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1077 * @const char *desc@ = adhoc test description, must be null
1078 * @unsigned unit@ = measurement unit (@BTU_...@)
1079 * @const struct bench_timing *t@ = measurement
1083 * Use: Report a benchmark's results.
1085 * The remote driver sends a @TVPK_EBENCH@ packet to its client.
1088 static void remote_ebench(struct tvec_output *o,
1089 const char *desc, unsigned unit,
1090 const struct bench_timing *t)
1093 QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
1094 dbuf_putu16l(&srvrc.bout, unit);
1095 if (!t || !(t->f&BTF_ANY))
1096 dbuf_putu16l(&srvrc.bout, 0);
1098 dbuf_putu16l(&srvrc.bout, t->f);
1099 dbuf_putf64l(&srvrc.bout, t->n);
1100 if (t->f&BTF_TIMEOK) dbuf_putf64l(&srvrc.bout, t->t);
1101 if (t->f&BTF_CYOK) dbuf_putf64l(&srvrc.bout, t->cy);
1106 static const struct tvec_benchoutops remote_benchops =
1107 { remote_bbench, remote_ebench };
1109 /* --- @remote_extend@ --- *
1111 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1112 * @const char *name@ = extension name
1114 * Returns: A pointer to the extension implementation, or null.
1117 static const void *remote_extend(struct tvec_output *o, const char *name)
1119 if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&remote_benchops);
1123 /* --- @remote_destroy@ --- *
1125 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1129 * Use: Release the resources held by the output driver.
1131 * The remote driver does nothing at all.
1134 static void remote_destroy(struct tvec_output *o)
1137 static const struct tvec_outops remote_ops = {
1138 remote_bsession, remote_esession,
1139 remote_bgroup, remote_skipgroup, remote_egroup,
1140 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
1141 remote_report, remote_extend, remote_destroy
1144 /*----- Pseudoregister definitions ----------------------------------------*/
1146 static tvec_setvarfn setvar_local, setvar_remote;
1148 static const struct tvec_flag exit_flags[] = {
1151 { "running", TVXF_CAUSEMASK, TVXST_RUN },
1152 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
1153 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
1154 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
1155 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
1156 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
1157 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
1158 { "error", TVXF_CAUSEMASK, TVXST_ERR },
1161 ;;; The signal name table is very boring to type. To make life less
1162 ;;; awful, put the signal names in this list and evaluate the code to
1163 ;;; get Emacs to regenerate it.
1165 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
1166 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
1167 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
1168 STKFLT INFO PWR THR LWP LIBRT LOST)))
1170 (goto-char (point-min))
1171 (search-forward (concat "***" "BEGIN siglist" "***"))
1172 (beginning-of-line 2)
1173 (delete-region (point)
1175 (search-forward "***END***")
1178 (dolist (sig signals)
1179 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
1183 /***BEGIN siglist***/
1185 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
1188 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
1191 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
1194 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
1197 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
1200 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
1203 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
1206 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
1209 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
1212 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
1215 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
1218 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
1221 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
1224 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
1227 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
1230 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
1233 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
1236 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
1239 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
1242 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
1245 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1248 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1251 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1254 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1257 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1260 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1263 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1266 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1269 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1272 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1275 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1278 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1281 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1284 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1287 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1290 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1293 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1296 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1299 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1302 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1305 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1309 /* This should be folded into the signal entries above. */
1310 { "signal", TVXF_SIG, TVXF_SIG },
1314 static const struct tvec_flaginfo exit_flaginfo =
1315 { "exit-status", exit_flags, &tvrange_uint };
1316 static const struct tvec_vardef exit_var =
1317 { sizeof(struct tvec_reg), setvar_local,
1318 { "@exit", &tvty_flags, -1, 0, { &exit_flaginfo } } };
1322 static const struct tvec_vardef progress_var =
1323 { sizeof(struct tvec_reg), setvar_local,
1324 { "@progress", &tvty_text, -1, 0 } };
1328 static const struct tvec_uassoc reconn_assocs[] = {
1329 { "on-demand", TVRCN_DEMAND },
1330 { "force", TVRCN_FORCE },
1331 { "skip", TVRCN_SKIP },
1334 static const struct tvec_uenuminfo reconn_enuminfo =
1335 { "remote-reconnection", reconn_assocs, &tvrange_uint };
1336 static const struct tvec_vardef reconn_var =
1337 { sizeof(struct tvec_reg), setvar_local,
1338 { "@reconnect", &tvty_uenum, -1, 0, { &reconn_enuminfo } } };
1340 /*----- Client ------------------------------------------------------------*/
1342 /* Connection state. */
1344 CONN_BROKEN = -2, /* previously broken */
1345 CONN_FAILED = -1, /* attempt freshly failed */
1346 CONN_ESTABLISHED = 0, /* previously established */
1347 CONN_FRESH = 1 /* freshly connected */
1350 /* --- @handle_packets@ --- *
1352 * Arguments: @struct tvec_state *tv@ = test-vector state
1353 * @struct tvec_remotectx *r@ = remote client context
1354 * @unsigned f@ = receive flags (@RCVF_...@)
1355 * @uint16 end@ = expected end packet type
1356 * @buf *b_out@ = buffer in which to return end packet payload
1358 * Returns: A @RECV_...@ code.
1360 * Use: Handles notification packets from the server until a final
1361 * termination packet is received.
1363 * The client/server protocol consists of a number of flows,
1364 * beginning with a request from the client, followed by a
1365 * number of notifications from the server, and terminated by an
1366 * acknowledgement to the original request indicating that the
1367 * server has completed acting on the original request.
1369 * This function handles the notifications issued by the server,
1370 * returning when one of the following occurs: (a) a packet of
1371 * type @end@ is received, in which case the function returns
1372 * @RECV_OK@ and the remainder of the packet payload is left in
1373 * @b_out@; (b) the flag @RCVF_ALLOWEOF@ was set in @f@ on entry
1374 * and end-of-file is received at a packet boundary, in which
1375 * case the function returns @RECV_EOF@; or (c) an I/O error
1376 * occurs, in which case @ioerr@ is called and the function
1377 * returns @RECV_FAIL@.
1380 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1381 unsigned f, uint16 end, buf *b_out)
1383 struct tvec_output *o;
1385 const char *p; size_t n;
1388 const struct tvec_regdef *rd;
1389 struct bench_timing bt;
1390 const struct tvec_benchoutops *bo;
1391 struct tvec_fallbackoutput fo;
1392 struct tvec_reg *reg = 0; size_t rsz = 0;
1398 /* Read the next packet. If we didn't receive one then end the loop.
1399 * Otherwise, retrieve the packet type and check it against @end@: quit
1400 * the loop if we get a match.
1402 rc = remote_recv(tv, &r->rc, f, b); if (rc) break;
1403 if (buf_getu16l(b, &pk)) goto bad;
1404 if (pk == end) { rc = 0; break; }
1406 /* Dispatch based on the packet type. */
1410 /* A progress report. Update the saved progress. */
1412 p = buf_getmem16l(b, &n); if (!p) goto bad;
1413 if (BLEFT(b)) goto bad;
1415 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1419 /* A report. Recover the message and pass it along. */
1421 if (buf_getu16l(b, &u)) goto bad;
1422 p = buf_getmem16l(b, &n); if (!p) goto bad;
1423 if (BLEFT(b)) goto bad;
1425 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1426 tvec_report(tv, u, "%s", d.buf);
1430 /* A request to skip the group. Recover the excuse message and pass
1434 p = buf_getmem16l(b, &n); if (!p) goto bad;
1435 if (BLEFT(b)) goto bad;
1437 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1438 tvec_skipgroup(tv, "%s", d.buf);
1442 /* A request to skip the test. Recover the excuse message and pass
1443 * it along, if it's not unreasonable.
1446 if (!(tv->f&TVSF_ACTIVE)) {
1447 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1451 p = buf_getmem16l(b, &n); if (!p) goto bad;
1452 if (BLEFT(b)) goto bad;
1454 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1455 tvec_skip(tv, "%s", d.buf);
1459 /* A report that the test failed. Recover the detail message, if
1460 * any, and pass it along, if it's not unreasonable.
1463 if (!(tv->f&TVSF_ACTIVE) &&
1464 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
1465 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
1470 rc = buf_getbyte(b); if (rc < 0) goto bad;
1471 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1473 if (BLEFT(b)) goto bad;
1478 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1479 tvec_fail(tv, "%s", d.buf);
1484 /* A request to dump a register. */
1486 /* Find the register definition. */
1487 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1488 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
1489 if (i == u) goto found_reg;
1490 rc = ioerr(tv, &r->rc,
1491 "register definition %u out of range for test `%s'",
1495 if (v >= TVRD_LIMIT) {
1496 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1500 /* Read the flag. If there's no register value, then `dump' its
1501 * absence. Otherwise retrieve the register value and dump it.
1503 rc = buf_getbyte(b); if (rc < 0) goto bad;
1505 tvec_dumpreg(tv, v, 0, rd);
1507 GROWBUF_REPLACE(size_t, tv->a, reg, rsz, tv->cfg.regsz,
1508 8*sizeof(void *), 1);
1509 rd->ty->init(®->v, rd);
1510 rc = rd->ty->frombuf(b, ®->v, rd);
1511 if (!rc) tvec_dumpreg(tv, v, ®->v, rd);
1512 rd->ty->release(®->v, rd);
1515 if (BLEFT(b)) goto bad;
1519 /* A report that we're starting a benchmark. Pass this along. */
1521 if (buf_getu16l(b, &u)) goto bad;
1522 if (tvec_deserialize(tv->in, b, tv->test->regs,
1523 TVRF_ID, TVRF_ID, tv->cfg.nreg, tv->cfg.regsz))
1525 if (BLEFT(b)) goto bad;
1526 if (u >= BTU_LIMIT) {
1527 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1531 bo = tvec_outputext(tv, &o, &fo,
1532 TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1533 bo->bbench(o, 0, u);
1537 /* A report that a benchmark completed. Pass this along. */
1539 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1540 if (u >= BTU_LIMIT) {
1541 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1544 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
1545 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
1546 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
1547 if (BLEFT(b)) goto bad;
1550 bo = tvec_outputext(tv, &o, &fo,
1551 TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1552 bo->ebench(o, 0, u, v&BTF_ANY ? &bt : 0);
1556 /* Something else. This is unexpected. */
1558 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1568 rc = malformed(tv, &r->rc); goto end;
1571 /* --- @reap_kid@ --- *
1573 * Arguments: @struct tvec_state *tv@ = test-vector state
1574 * @struct tvec_remotectx *r@ = remote client context
1578 * Use: Determine the exit status of a broken connection, setting
1579 * @r->exit@ appropriately.
1581 * If @r->kid@ is negative, the exit status has already been
1582 * set, and nothing further happens; this is not an error.
1584 * If @r->kid@ is zero, then there is no real child process
1585 * (e.g., because the remote connection is a network connection
1586 * or similar), so @r->exit@ is set equal to @RVXST_DISCONN@.
1588 * If @r->kid@ is positive, then it holds a child process id;
1589 * the function waits for it to end and collects its exit status
1591 * It is an error to call this function if the connection is not
1595 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1600 assert(r->rc.f&TVRF_BROKEN);
1602 { r->exit = TVXST_DISCONN; r->kid = -1; }
1603 else if (r->kid > 0) {
1604 kid = waitpid(r->kid, &st, 0);
1606 tvec_notice(tv, "failed to wait for remote child: %s",
1608 r->exit = TVXST_ERR;
1610 tvec_notice(tv, "remote child vanished without a trace");
1611 r->exit = TVXST_ERR;
1612 } else if (WIFCONTINUED(st))
1613 r->exit = TVXST_CONT;
1614 else if (WIFSIGNALED(st))
1615 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
1616 else if (WIFSTOPPED(st))
1617 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
1618 else if (WIFEXITED(st))
1619 r->exit = TVXST_EXIT | WEXITSTATUS(st);
1621 tvec_notice(tv, "remote child died with unknown status 0x%04x",
1623 r->exit = TVXST_UNK;
1629 /* --- @report_errline@ --- *
1631 * Arguments: @char *p@ = pointer to the line
1632 * @size_t n@ = length in characters
1633 * @void *ctx@ = context, secretly a @struct tvec_remotectx@
1637 * Use: Print a line of stderr output from the child. If
1638 * @TVRF_MUFFLE@ is set, then discard the line silently.
1640 * This is an @lbuf_func@, invoked via @drain_errfd@.
1643 static void report_errline(char *p, size_t n, void *ctx)
1645 struct tvec_remotectx *r = ctx;
1646 struct tvec_state *tv = r->tv;
1648 if (p && !(r->rc.f&TVRF_MUFFLE))
1649 tvec_notice(tv, "child process stderr: %s", p);
1652 /* --- @drain_errfd@ --- *
1654 * Arguments: @struct tvec_state *tv@ = test-vector state
1655 * @struct tvec_remotectx *r@ = remote client context
1656 * @unsigned f@ = receive flags (@ERF_...@)
1658 * Returns: Zero on success, %$-1$% on error.
1660 * Use: Collect material written by the child to its stderr stream
1663 * If @f@ has @ERF_SILENT@ set, then discard the stderr material
1664 * without reporting it. Otherwise it is reported as
1667 * if @f@ has @ERF_CLOSE@ set, then continue reading until
1668 * end-of-file is received; also, report any final partial line,
1669 * and close @r->errfd@.
1671 * If @r->errfd@ is already closed, or never established, then
1672 * do nothing and return successfully.
1675 #define ERF_SILENT 0x0001u
1676 #define ERF_CLOSE 0x0002u
1677 static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
1684 /* Preliminaries. Bail if there is no error stream to fetch. Arrange
1685 * (rather clumsily) to muffle the output if we're supposed to be client.
1686 * And set the nonblocking state on @errfd@ appropriately.
1688 if (r->errfd < 0) { rc = 0; goto end; }
1689 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
1690 else r->rc.f &= ~TVRF_MUFFLE;
1691 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
1692 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
1693 f&ERF_CLOSE ? "clear" : "set");
1697 /* Read pieces of error output and feed them into the line buffer. */
1699 sz = lbuf_free(&r->errbuf, &p);
1700 n = read(r->errfd, p, sz);
1703 if (errno == EINTR) continue;
1704 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1706 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1710 lbuf_flush(&r->errbuf, p, n);
1717 lbuf_close(&r->errbuf);
1718 close(r->errfd); r->errfd = -1;
1723 /* --- @disconnect_remote@ --- *
1725 * Arguments: @struct tvec_state *tv@ = test-vector state
1726 * @struct tvec_remotectx *r@ = remote client context
1727 * @unsigned f@ = receive flags (@DCF_...@)
1731 * Use: Disconnect and shut down all of the remote client state.
1733 * If @f@ has @DCF_KILL@ set then send the child process (if
1734 * any) @SIGTERM@ to make sure it shuts down in a timely manner.
1736 * In detail: this function closes the @infd@ and @outfd@
1737 * descriptors, drains and closes @errfd@, and collects the exit
1741 #define DCF_KILL 0x0100u
1742 #define DCF_QUITOK 0200u
1743 static void disconnect_remote(struct tvec_state *tv,
1744 struct tvec_remotectx *r, unsigned f)
1746 union tvec_regval rv;
1749 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1750 close_comms(&r->rc);
1751 drain_errfd(tv, r, f | ERF_CLOSE);
1754 if (!(f&ERF_SILENT) &&
1755 (!(f&DCF_QUITOK) || (r->exit != TVXST_EXIT &&
1756 r->exit != TVXST_DISCONN))) {
1758 tvty_flags.dump(&rv, &exit_var.def, TVSF_COMPACT, &dstr_printops, &d);
1759 tvec_error(tv, "remote connection closed with status %s", d.buf);
1765 /* --- @connect_remote@ --- *
1767 * Arguments: @struct tvec_state *tv@ = test-vector state
1768 * @struct tvec_remotectx *r@ = remote client context
1770 * Returns: Zero on success, %$-1$% on error.
1772 * Use: Connect to the test server.
1775 static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1777 const struct tvec_remoteenv *re = r->re;
1781 int infd = -1, outfd = -1, errfd = -1, rc;
1783 /* If we're already connected, then there's nothing to do. */
1784 if (r->kid >= 0) { rc = 0; goto end; }
1786 /* Set the preliminary progress indication. */
1787 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1789 /* Call the connection function to establish descriptors. */
1790 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1791 { rc = -1; goto end; }
1793 /* Establish communications state. */
1794 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
1795 lbuf_init(&r->errbuf, report_errline, r);
1796 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
1798 /* Do version negotiation. */
1799 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1800 dbuf_putu16l(&r->rc.bout, 0);
1801 dbuf_putu16l(&r->rc.bout, 0);
1802 } else { rc = -1; goto end; }
1803 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
1804 { rc = -1; goto end; }
1805 if (buf_getu16l(&b, &v)) goto bad;
1806 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1808 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
1813 /* Begin the test group at the server. */
1814 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1815 dbuf_putstr16l(&r->rc.bout, tv->test->name);
1816 else { rc = -1; goto end; }
1817 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
1818 { rc = -1; goto end; }
1819 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1824 if (rc) disconnect_remote(tv, r, DCF_KILL);
1827 rc = malformed(tv, &r->rc); goto end;
1830 /* --- @check_comms@ --- *
1832 * Arguments: @struct tvec_state *tv@ = test-vector state
1833 * @struct tvec_remotectx *r@ = remote client context
1835 * Returns: A @CONN_...@ code reflecting the current communication
1838 * Use: Determine the current connection state. If the connection
1839 * has recently broken (i.e., @TVRF_BROKEN@ is set in @r->rc.f@)
1840 * since the last time we checked then disconnect.
1843 static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1846 return (CONN_BROKEN);
1847 else if (r->rc.f&TVRF_BROKEN)
1848 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1850 return (CONN_ESTABLISHED);
1853 /* --- @try_reconnect@ --- *
1855 * Arguments: @struct tvec_state *tv@ = test-vector state
1856 * @struct tvec_remotectx *r@ = remote client context
1858 * Returns: A @CONN_...@ code reflecting the new communication state.
1860 * Use: Reconnects to the server according to the configured
1861 * @TVRCN_...@ policy.
1864 static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1868 switch (r->rc.f&TVRF_RCNMASK) {
1870 rc = check_comms(tv, r);
1871 if (rc < CONN_ESTABLISHED) {
1872 close_comms(&r->rc);
1873 if (connect_remote(tv, r)) rc = CONN_FAILED;
1874 else rc = CONN_FRESH;
1878 disconnect_remote(tv, r, DCF_KILL);
1879 if (connect_remote(tv, r)) rc = CONN_FAILED;
1880 else rc = CONN_FRESH;
1883 rc = check_comms(tv, r);
1891 /*----- Remote environment ------------------------------------------------*/
1893 /* --- @reset_vars@ --- *
1895 * Arguments: @struct tvec_remotectx *r@ = remote client context
1899 * Use: Reset the pseudoregisters set through @tvec_remoteset@.
1902 static void reset_vars(struct tvec_remotectx *r)
1904 const struct tvec_remoteenv *re = r->re;
1906 r->exwant = TVXST_RUN;
1907 r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) |
1908 (re->r.dflt_reconn&TVRF_RCNMASK);
1909 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1912 /* --- @tvec_remotesetup@ --- *
1914 * Arguments: @struct tvec_state *tv@ = test vector state
1915 * @const struct tvec_env *env@ = environment description
1916 * @void *pctx@ = parent context (ignored)
1917 * @void *ctx@ = context pointer to initialize
1921 * Use: Initialize a timeout environment context.
1923 * The environment description should be a @struct
1924 * tvec_remoteenv@ subclass suitable for use by the @connect@
1928 void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1929 void *pctx, void *ctx)
1931 struct tvec_remotectx *r = ctx;
1932 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1933 const struct tvec_env *subenv = re->r.env;
1935 r->tv = tv; r->subctx = 0;
1937 r->re = re; r->kid = -1;
1938 DCREATE(&r->prgwant); DCREATE(&r->progress);
1939 if (connect_remote(tv, r))
1940 tvec_skipgroup(tv, "failed to connect to test backend");
1942 if (subenv && subenv->ctxsz)
1943 r->subctx = pool_alloc(tv->p_group, subenv->ctxsz);
1944 if (subenv && subenv->setup)
1945 subenv->setup(tv, subenv, r, r->subctx);
1948 /* --- @tvec_remotefindvar@, @setvar_local@, @setvar_remote@ --- *
1950 * Arguments: @struct tvec_state *tv@ = test vector state
1951 * @const char *var@ = variable name to set
1952 * @const union tvec_regval *rv@ = register value
1953 * @void **ctx_out@ = where to put the @setvar@ context
1954 * @void *ctx@ = context pointer
1956 * Returns: @tvec_remotefindvar@ returns a pointer to the variable
1957 * definition, or null; @remote_setvar@ returns zero on success
1958 * or %$-1$% on error.
1960 * Use: Set a special variable. The following special variables are
1963 * * %|@exit|% is the expected exit status; see @TVXF_...@ and
1966 * * %|progress|% is the expected progress token when the test
1967 * completes. On successful completion, this will be
1968 * %|%DONE|%; it's %|%RUN|% on entry to the test function,
1969 * but that can call @tvec_setprogress@ to change it.
1971 * * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
1974 static int setvar_local(struct tvec_state *tv, const char *var,
1975 const union tvec_regval *rv, void *ctx)
1977 struct tvec_remotectx *r = ctx;
1979 if (STRCMP(var, ==, "@exit")) {
1980 if (r->rc.f&TVRF_SETEXIT) return (tvec_dupregerr(tv, var));
1981 r->exwant = rv->u; r->rc.f |= TVRF_SETEXIT; return (0);
1982 } else if (STRCMP(var, ==, "@progress")) {
1983 if (r->rc.f&TVRF_SETPRG) return (tvec_dupregerr(tv, var));
1984 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv->text.p, rv->text.sz);
1986 r->rc.f |= TVRF_SETPRG; return (0);
1987 } else if (STRCMP(var, ==, "@reconnect")) {
1988 if (r->rc.f&TVRF_SETRCN) return (tvec_dupregerr(tv, var));
1989 r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv->u&TVRF_RCNMASK) | TVRF_SETRCN;
1991 } else assert(!"unknown var");
1994 static int setvar_remote(struct tvec_state *tv, const char *var,
1995 const union tvec_regval *rv, void *ctx)
1997 struct tvec_remotectx *r = ctx;
2001 if (try_reconnect(tv, r) < 0) { rc = 0; goto end; }
2003 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_SETVAR) {
2004 dbuf_putstr16l(&r->rc.bout, var);
2005 r->vd.def.ty->tobuf(DBUF_BUF(&r->rc.bout), rv, &r->vd.def);
2006 } else { rc = -1; goto end; }
2008 rc = handle_packets(tv, r, 0, TVPK_SETVAR | TVPF_ACK, &b);
2010 ch = buf_getbyte(&b);
2011 if (ch < 0) { rc = malformed(tv, &r->rc); goto end; }
2012 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
2019 const struct tvec_vardef *tvec_remotefindvar
2020 (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
2022 struct tvec_remotectx *r = ctx;
2023 const struct tvec_remoteenv *re = r->re;
2024 const struct tvec_env *subenv = re->r.env;
2025 const struct tvec_vardef *vd; void *varctx;
2027 if (STRCMP(var, ==, "@exit"))
2028 { *ctx_out = r; return (&exit_var); }
2029 else if (STRCMP(var, ==, "@progress"))
2030 { *ctx_out = r; return (&progress_var); }
2031 else if (STRCMP(var, ==, "@reconnect"))
2032 { *ctx_out = r; return (&reconn_var); }
2033 else if (subenv && subenv->findvar) {
2034 vd = subenv->findvar(tv, var, &varctx, r->subctx);
2035 if (!vd) return (0);
2036 r->vd.regsz = vd->regsz; r->vd.setvar = setvar_remote;
2037 r->vd.def = vd->def;
2038 *ctx_out = r; return (&r->vd);
2043 /* --- @tvec_remotebefore@ --- *
2045 * Arguments: @struct tvec_state *tv@ = test vector state
2046 * @void *ctx@ = context pointer
2050 * Use: Invoke the subordinate environment's @before@ function.
2053 void tvec_remotebefore(struct tvec_state *tv, void *ctx)
2055 struct tvec_remotectx *r = ctx;
2056 const struct tvec_remoteenv *re = r->re;
2057 const struct tvec_env *subenv = re->r.env;
2059 if (subenv && subenv->before) subenv->before(tv, r->subctx);
2062 /* --- @tvec_remoterun@ --- *
2064 * Arguments: @struct tvec_state *tv@ = test vector state
2065 * @tvec_testfn *fn@ = test function to run
2066 * @void *ctx@ = context pointer for the test function
2070 * Use: Run a test on a remote server.
2073 void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
2075 struct tvec_remotectx *r = ctx;
2076 union tvec_regval rv;
2079 #define f_progress 2u
2084 /* Reconnect to the server according to policy. */
2085 switch (try_reconnect(tv, r)) {
2087 tvec_skip(tv, "failed to connect to test backend"); return;
2089 tvec_skip(tv, "no connection"); return;
2092 /* Set initial progress state. */
2093 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
2095 /* Send the command to the server and handle output. */
2096 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
2097 tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout), tv->test->regs,
2098 0, 0, tv->cfg.nreg, tv->cfg.regsz);
2100 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
2102 /* Deal with the outcome. */
2106 /* Some kind of error. Abandon ship. */
2109 tvec_skip(tv, "remote test runner communications failed");
2110 disconnect_remote(tv, r, 0);
2114 /* End-of-file at a packet boundary. The server crashed trying to run
2115 * our test. Collect the exit status and continue.
2121 /* Successful completion (or EOF). */
2123 /* Notice if the exit status isn't right. */
2124 if (r->exit != r->exwant) f |= f_exit;
2126 /* Notice if the progress token isn't right. */
2127 if (r->progress.len != r->prgwant.len ||
2128 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
2131 /* If we found something wrong but the test is passing so far, then
2132 * report the failure and dump the input registers.
2134 if (f && (tv->f&TVSF_ACTIVE))
2135 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
2137 /* If the test failed, then report the exit and progress states
2138 * relative to their expectations.
2140 if (!(tv->f&TVSF_ACTIVE) &&
2141 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
2143 /* Note here that the test failed. */
2146 /* Report exit status. */
2148 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
2149 &rv, &exit_var.def);
2152 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_var.def);
2155 /* Report progress token. */
2156 rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
2157 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
2158 &rv, &progress_var.def);
2160 rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
2161 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_var.def);
2165 /* If we received end-of-file, then close the connection. Suppress
2166 * error output if the test passed: it was presumably expected.
2169 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2178 /* --- @tvec_remoteafter@ --- *
2180 * Arguments: @struct tvec_state *tv@ = test vector state
2181 * @void *ctx@ = context pointer
2185 * Use: Reset variables to their default values.
2188 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
2190 struct tvec_remotectx *r = ctx;
2191 const struct tvec_remoteenv *re = r->re;
2192 const struct tvec_env *subenv = re->r.env;
2195 if (subenv && subenv->after) subenv->after(tv, r->subctx);
2198 /* --- @tvec_remoteteardown@ --- *
2200 * Arguments: @struct tvec_state *tv@ = test vector state
2201 * @void *ctx@ = context pointer
2205 * Use: Tear down the remote environment.
2208 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2210 struct tvec_remotectx *r = ctx;
2211 const struct tvec_remoteenv *re = r->re;
2212 const struct tvec_env *subenv = re->r.env;
2215 if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
2216 if (r->rc.outfd >= 0) {
2217 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
2218 if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
2219 if (BLEFT(&b)) malformed(tv, &r->rc);
2221 disconnect_remote(tv, r, DCF_QUITOK); release_comms(tv, &r->rc);
2222 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
2225 /*----- Connectors --------------------------------------------------------*/
2227 /* --- @fork_common@ --- *
2229 * Arguments: @pid_t *kid_out@ = where to put child process-id
2230 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2232 * @struct tvec_state *tv@ = test vector state
2234 * Returns: Zero on success, %$-1$% on failure.
2236 * Use: Common @fork@ machinery for the connectors. Create a
2237 * subprocess. On successful return, in the subprocess,
2238 * @*kid_out@ is zero, and the error descriptor replaces the
2239 * standard-error descriptor; in the parent, @*kid_out@ is the
2240 * child process-id, and @*errfd_out@ is a descriptor on which
2241 * the child's standard-error output can be read; in both
2242 * @*infd_out@ and @*outfd_out@ are descriptors for input and
2243 * output respectively -- they're opposite ends of pipes, but
2244 * obviously they're crossed over so that the parent's output
2245 * matches the child's input and %%\emph{vice versa}%%.
2248 static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
2249 int *errfd_out, struct tvec_state *tv)
2251 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
2255 /* Try to create the pipes. */
2256 if (pipe(p0) || pipe(p1) || pipe(pe) ||
2257 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2258 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2259 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
2260 tvec_error(tv, "pipe failed: %s", strerror(errno));
2264 /* Flush all of the stream buffers so that we don't get duplicated
2269 /* Try to set up the child process. */
2272 tvec_error(tv, "fork failed: %s", strerror(errno));
2277 /* Child process. */
2280 *infd_out = p0[0]; p0[0] = -1;
2281 *outfd_out = p1[1]; p1[1] = -1;
2282 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
2283 fprintf(stderr, "failed to establish child stderr: %s",
2288 /* Parent process. */
2290 *kid_out = kid; kid = -1;
2291 *infd_out = p1[0]; p1[0] = -1;
2292 *outfd_out = p0[1]; p0[1] = -1;
2293 *errfd_out = pe[0]; pe[0] = -1;
2300 /* Clean up. So much of this... */
2301 if (p0[0] >= 0) close(p0[0]);
2302 if (p0[1] >= 0) close(p0[1]);
2303 if (p1[0] >= 0) close(p1[0]);
2304 if (p1[1] >= 0) close(p1[1]);
2305 if (pe[0] >= 0) close(pe[0]);
2306 if (pe[1] >= 0) close(pe[1]);
2310 /* --- @tvec_fork@ --- *
2312 * Arguments: @pid_t *kid_out@ = where to put child process-id
2313 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2315 * @struct tvec_state *tv@ = test vector state
2316 * @const struct tvec_remoteenv@ = the remote environment
2318 * Returns: Zero on success, %$-1$% on failure.
2320 * Use: Starts a remote server running in a fork of the main
2321 * process. This is useful for testing functions which might --
2322 * or are even intended to -- crash.
2325 int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2326 struct tvec_state *tv, const struct tvec_remoteenv *env)
2328 struct tvec_config config;
2329 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
2331 int infd = -1, outfd = -1, errfd = -1;
2334 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2336 if (tv->fp) fclose(tv->fp);
2337 config.tests = rf->f.tests ? rf->f.tests : tv->cfg.tests;
2338 config.nrout = tv->cfg.nrout; config.nreg = tv->cfg.nreg;
2339 config.regsz = tv->cfg.regsz;
2340 _exit(tvec_remoteserver(infd, outfd, &config));
2343 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2349 /* --- @tvec_exec@ --- *
2351 * Arguments: @pid_t *kid_out@ = where to put child process-id
2352 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2354 * @struct tvec_state *tv@ = test vector state
2355 * @const struct tvec_remoteenv@ = the remote environment
2357 * Returns: Zero on success, %$-1$% on failure.
2359 * Use: Starts a remote server by running some program. The command
2360 * given in the environment description will probably some hairy
2361 * shell rune allowing for configuration via files or
2362 * environment variables.
2365 int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2366 struct tvec_state *tv, const struct tvec_remoteenv *env)
2368 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
2370 int infd = -1, outfd = -1, errfd = -1;
2374 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2376 v[0].cur = infd; v[0].want = STDIN_FILENO;
2377 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
2379 fprintf(stderr, "failed to establish standard file descriptors: %s",
2383 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
2384 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
2388 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2394 /*----- That's all, folks -------------------------------------------------*/