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>
52 /*----- Preliminaries -----------------------------------------------------*/
54 /* The control macros I'm using below provoke `dangling-else' warnings from
55 * compilers. Suppress them. I generally don't care.
58 #if GCC_VERSION_P(7, 1)
59 # pragma GCC diagnostic ignored "-Wdangling-else"
60 #elif GCC_VERSION_P(4, 2)
61 # pragma GCC diagnostic ignored "-Wparentheses"
64 #if CLANG_VERSION_P(3, 1)
65 # pragma clang diagnostic ignored "-Wdangling-else"
68 /*----- Basic I/O ---------------------------------------------------------*/
70 /* --- @init_comms@ --- *
72 * Arguments: @struct tvec_remotecomms *rc@ = communication state
76 * Use: Initialize a communication state. This doesn't allocate any
77 * resurces: it just ensures that everything is set up so that
78 * subsequent operations -- in particular @release_comms@ --
82 static void init_comms(struct tvec_remotecomms *rc)
84 rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout);
85 rc->infd = rc->outfd = -1; rc->f = 0;
88 /* --- @close_comms@ --- *
90 * Arguments: @struct tvec_remotecomms *rc@ = communication state
94 * Use: Close the input and output descriptors.
96 * If the descriptors are already closed -- or were never opened
97 * -- then nothing happens.
100 static void close_comms(struct tvec_remotecomms *rc)
102 if (rc->infd >= 0) { close(rc->infd); rc->infd = -1; }
103 if (rc->outfd >= 0) { close(rc->outfd); rc->outfd = -1; }
106 /* --- @release_comms@ --- *
108 * Arguments: @struct tvec_remotecomms *rc@ = communication state
112 * Use: Releases the resources -- most notably the input and output
113 * buffers -- held by the communication state. Also calls
117 static void release_comms(struct tvec_remotecomms *rc)
118 { close_comms(rc); xfree(rc->bin); dbuf_destroy(&rc->bout); }
120 /* --- @setup_comms@ --- *
122 * Arguments: @struct tvec_remotecomms *rc@ = communication state
123 * @int infd, outfd@ = input and output file descriptors
127 * Use: Use the given descriptors for communication.
129 * Clears the private flags.
132 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
134 rc->infd = infd; rc->outfd = outfd;
135 rc->binoff = rc->binlen = 0;
141 * Arguments: @struct tvec_state *tv@ = test-vector state
142 * @struct tvec_remotecomms *rc@ = communication state
143 * @const char *msg, ...@ = format string and arguments
147 * Use: Reports the message as an error, closes communications and
148 * marks them as broken.
151 static PRINTF_LIKE(3, 4)
152 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
153 const char *msg, ...)
158 close_comms(rc); rc->f |= TVRF_BROKEN;
159 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
164 /* --- @send_all@ --- *
166 * Arguments: @struct tvec_state *tv@ = test-vector state
167 * @struct tvec_remotecomms *rc@ = communication state
168 * @const unsigned char *p@, @size_t sz@ = output buffer
170 * Returns: Zero on success, %$-1$% on error.
172 * Use: Send the output buffer over the communication state's output
173 * descriptor, even if it has to be written in multiple pieces.
176 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
177 const unsigned char *p, size_t sz)
183 n = write(rc->outfd, p, sz);
187 ret = ioerr(tv, rc, "failed to send: %s",
188 n ? strerror(errno) : "empty write");
197 /* --- @recv_all@ --- *
199 * Arguments: @struct tvec_state *tv@ = test-vector state
200 * @struct tvec_remotecomms *rc@ = communication state
201 * @unsigned f@ = flags (@RCVF_...@)
202 * @unsigned char *p@, @size_t sz@ = input buffer
203 * @size_t min@ = minimum acceptable size to read
204 * @size_t *n_out@ = size read
206 * Returns: An @RECV_...@ code.
208 * Use: Receive data on the communication state's input descriptor to
209 * read at least @min@ bytes into the input buffer, even if it
210 * has to be done in multiple pieces. If more data is readily
211 * available, then up to @sz@ bytes will be read in total.
213 * If the descriptor immediately reports end-of-file, and
214 * @RCVF_ALLOWEOF@ is set in @f@, then return @RECV_EOF@.
215 * Otherwise, EOF is treated as an I/O error, resulting in a
216 * call to @ioerr@ and a return code of @RECV_FAIL@. If the
217 * read succeeded, then set @*n_out@ to the number of bytes read
218 * and return @RECV_OK@.
221 #define RCVF_ALLOWEOF 1u
229 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
230 unsigned f, unsigned char *p, size_t sz,
231 size_t min, size_t *n_out)
237 n = read(rc->infd, p, sz);
239 p += n; sz -= n; tot += n;
240 if (tot >= min) break;
241 } else if (!n && !tot && (f&RCVF_ALLOWEOF))
244 return (ioerr(tv, rc, "failed to receive: %s",
245 n ? strerror(errno) : "unexpected end-of-file"));
247 *n_out = tot; return (RECV_OK);
252 /* --- @buferr@ --- *
254 * Arguments: @struct tvec_state *tv@ = test-vector state
255 * @struct tvec_remotecomms *rc@ = communication state
259 * Use: Report a problem preparing the output buffer.
262 static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
263 { return (ioerr(tv, rc, "failed to build output packet")); }
265 /* --- @malformed@ --- *
267 * Arguments: @struct tvec_state *tv@ = test-vector state
268 * @struct tvec_remotecomms *rc@ = communication state
272 * Use: Report an I/O error that the incoming packet is malformed.
275 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
276 { return (ioerr(tv, rc, "received malformed packet")); }
278 /* --- @remote_send@ --- *
280 * Arguments: @struct tvec_state *tv@ = test-vector state
281 * @struct tvec_remotecomms *rc@ = communication state
283 * Returns: Zero on success, %$-1$% on error.
285 * Use: Send the accuulated contents of the output buffer @rc->bout@.
287 * The function arranges to convert @SIGPIPE@ into an error.
289 * If the output buffer is broken, report this as an I/O error.
292 #define SENDBUFSZ 4096
294 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
296 void (*opipe)(int) = SIG_ERR;
299 /* Various preflight checks. */
300 if (rc->f&TVRF_BROKEN) { ret = -1; goto end; }
301 if (DBBAD(&rc->bout)) { ret = buferr(tv, rc); goto end; }
303 /* Arrange to trap broken-pipe errors. */
304 opipe = signal(SIGPIPE, SIG_IGN);
305 if (opipe == SIG_ERR) {
306 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
310 /* Transmit the packet. */
311 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
312 { ret = -1; goto end; }
314 /* Done. Put things back the way we found them. */
318 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
322 /* --- @receive_buffered@ --- *
324 * Arguments: @struct tvec_state *tv@ = test-vector state
325 * @struct tvec_remotecomms *rc@ = communication state
326 * @unsigned f@ = flags (@RCVF_...@)
327 * @size_t want@ = data block size required
329 * Returns: An @RECV_...@ code.
331 * Use: Reads a block of data from the input descriptor into the
334 * This is the main machinery for manipulating the input buffer.
335 * The buffer has three regions:
337 * * from the buffer start to @rc->binoff@ is `consumed';
338 * * from @rc->binoff@ to @rc->binlen@ is `available'; and
339 * * from @rc->binlen@ to @rc->binsz@ is `free'.
341 * Data is read into the start of the `free' region, and the
342 * `available' region is extended to include it. Data in the
343 * `consumed' region is periodically discarded by moving the
344 * data from the `available' region to the start of the buffer
345 * and decreasing @rc->binoff@ and @rc->binlen@.
347 * This function ensures that the `available' region contains at
348 * least @want@ bytes, by (a) extending the buffer, if
349 * necessary, so that @rc->binsz >= rc->binoff + want@, and (b)
350 * reading fresh data from the input descriptor to extend the
351 * `available' region.
353 * If absolutely no data is available, and @RCVF_ALLOWEOF@ is
354 * set in @f@, then return @RECV_EOF@. On I/O errors, including
355 * a short read or end-of-file if @RCVF_ALLOWEOF@ is clear,
356 * return @RECV_FAIL@. On success, return @RECV_OK@. The
357 * amount of data read is indicated by updating the input buffer
358 * variables as described above.
361 #define RECVBUFSZ 4096u
363 static int receive_buffered(struct tvec_state *tv,
364 struct tvec_remotecomms *rc,
365 unsigned f, size_t want)
370 /* If we can supply the caller's requirement from the buffer then do
373 if (rc->binlen - rc->binoff >= want) return (RECV_OK);
375 /* If the buffer is too small then we must grow it. */
376 if (want > rc->binsz) {
377 sz = rc->binsz; if (!sz) sz = RECVBUFSZ;
378 while (sz < want) { assert(sz < (size_t)-1/2); sz *= 2; }
379 if (!rc->bin) rc->bin = xmalloc(sz);
380 else rc->bin = xrealloc(rc->bin, sz, rc->binsz);
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;
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.
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);
396 /* Note how much material we have and return. */
397 rc->binlen += sz; return (RECV_OK);
400 /* --- @remote_recv@ --- *
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
406 * Returns: An @RECV_...@ code.
408 * Use: Receive a packet into the input buffer @rc->bin@ and
409 * establish @*b_out@ to read from it.
412 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
413 unsigned f, buf *b_out)
419 ASSIGN64(szmax, (size_t)-1);
421 /* Preflight checks. */
422 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
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;
427 if (CMP64(k, >, szmax))
428 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
429 (unsigned long)HI64(k), (unsigned long)LO64(k)));
430 want = GET64(size_t, k);
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;
438 /* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
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
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.
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); \
466 DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L) \
467 MC_BEFORE(tag##__pkty, { \
468 dbuf_putu16l(&(rc)->bout, (pk)); \
471 #define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
473 /*----- Packet types ------------------------------------------------------*/
475 #define TVPF_ACK 0x0001u
477 #define TVPK_VER 0x0000u /* --> min, max: u16 *
479 #define TVPK_BGROUP 0x0002u /* --> name: str16
481 #define TVPK_TEST 0x0004u /* --> in: regs
483 #define TVPK_EGROUP 0x0006u /* --> --- *
486 #define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
487 #define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
489 #define TVPK_SKIPGRP 0x0104u /* <-- excuse: str16 */
490 #define TVPK_SKIP 0x0106u /* <-- excuse: str16 */
491 #define TVPK_FAIL 0x0108u /* <-- flag: u8, detail: str16 */
492 #define TVPK_DUMPREG 0x010au /* <-- ri: u16; disp: u16;
493 * flag: u8, rv: value */
494 #define TVPK_BBENCH 0x010cu /* <-- ident: str32; unit: u16 */
495 #define TVPK_EBENCH 0x010eu /* <-- ident: str32; unit: u16;
496 * flags: u16; n, t, cy: f64 */
498 /*----- Server ------------------------------------------------------------*/
500 /* Forward declaration of output operations. */
501 static const struct tvec_outops remote_ops;
503 static struct tvec_state srvtv; /* server's test-vector state */
504 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
505 static struct tvec_output srvout = { &remote_ops }; /* output state */
507 /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
509 * Arguments: @const char *status@ = progress status token format
510 * @va_list ap@ = argument tail
514 * Use: Reports the progress of a test execution to the client.
516 * The framework makes use of tokens beginning with %|%|%:
518 * * %|%IDLE|%: during the top-level server code;
520 * * %|%SETUP|%: during the enclosing environment's @before@
523 * * %|%RUN|%: during the environment's @run@ function, or the
526 * * %|%DONE|%: during the enclosing environment's @after@
529 * The intent is that a test can use the progress token to check
530 * that a function which is expected to crash does so at the
531 * correct point, so it's expected that more complex test
532 * functions and/or environments will set their own progress
533 * tokens to reflect what's going on.
536 int tvec_setprogress(const char *status, ...)
541 va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
545 int tvec_setprogress_v(const char *status, va_list *ap)
547 /* Force immediate output in case we crash before the buffer is output
550 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
551 dbuf_vputstrf16l(&srvrc.bout, status, ap);
556 /* --- @tvec_remoteserver@ --- *
558 * Arguments: @int infd@, @int outfd@ = input and output file descriptors
559 * @const struct tvec_config *config@ = test configuration
561 * Returns: Suggested exit code.
563 * Use: Run a test server, reading packets from @infd@ and writing
564 * responses and notifications to @outfd@, and invoking tests as
565 * described by @config@.
567 * This function is not particularly general purpose. It
568 * expects to `take over' the process, and makes use of private
572 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
577 const struct tvec_test *t;
579 const struct tvec_env *env = 0;
583 /* Initialize the communication machinery. */
584 setup_comms(&srvrc, infd, outfd);
586 /* Begin a test session using our custom output driver. */
587 tvec_begin(&srvtv, config, &srvout);
589 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
590 * there's only version zero, so we return that.
592 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
593 if (buf_getu16l(&b, &pk)) goto bad;
594 if (pk != TVPK_VER) {
595 rc = ioerr(&srvtv, &srvrc,
596 "unexpected packet type 0x%04x instead of client version",
600 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
601 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
602 dbuf_putu16l(&srvrc.bout, 0);
603 else { rc = -1; goto end; }
605 /* Handle packets until the server closes the connection.
607 * The protocol looks much simpler from our point of view than from the
610 * * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
612 * * Receive zero or more @TVPK_BGROUP@. Open a test group, producing
613 * output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
615 * -- Receive zero or more @TVPK_TEST@. Run a test, producing output
616 * packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
618 * -- Receive @TVPK_EGROUP@. Maybe produce output packets, and
619 * answer with @TVPK_EGROUP | TVPF_ACK@.
625 /* Read a packet. End-of-file is expected here (and pretty much nowhere
626 * else). Otherwise, we expect to see @TVPK_BGROUP@.
628 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
629 if (rc == RECV_EOF) break;
630 else if (rc == RECV_FAIL) goto end;
631 if (buf_getu16l(&b, &pk)) goto bad;
638 /* Parse the packet payload. */
639 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
640 if (BLEFT(&b)) goto bad;
642 /* Find the group given its name. */
643 for (t = srvtv.tests; t->name; t++)
644 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
646 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
651 /* Set up the test environment. */
652 srvtv.test = t; env = t->env;
653 if (env && env->setup == tvec_remotesetup)
654 env = ((struct tvec_remoteenv *)env)->r.env;
655 if (!env || !env->ctxsz) ctx = 0;
656 else ctx = xmalloc(env->ctxsz);
657 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
659 /* Initialize the registers. */
660 tvec_initregs(&srvtv);
662 /* Report that the group has been opened and that we're ready to run
665 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
666 else { rc = -1; goto end; }
668 /* Handle packets until we're told to end the group. */
671 /* Read a packet. We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
672 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
673 if (buf_getu16l(&b, &pk)) goto bad;
680 /* Check the payload. */
681 if (BLEFT(&b)) goto bad;
683 /* Leave the group loop. */
689 /* Parse the packet payload. */
690 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
691 srvtv.nreg, srvtv.regsz))
693 if (BLEFT(&b)) goto bad;
695 /* If we're not skipping the test group, then actually try to
698 if (!(srvtv.f&TVSF_SKIP)) {
700 /* Prepare the output registers and reset the test outcome.
701 * (The environment may force a skip.)
703 for (i = 0; i < srvtv.nrout; i++)
704 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
705 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
706 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
708 /* Invoke the environment @before@ function. */
709 tvec_setprogress("%%SETUP");
710 if (env && env->before) env->before(&srvtv, ctx);
712 /* Run the actual test. */
713 if (!(srvtv.f&TVSF_ACTIVE))
714 /* setup forced a skip */;
716 tvec_setprogress("%%RUN");
718 env->run(&srvtv, t->fn, ctx);
720 t->fn(srvtv.in, srvtv.out, ctx);
721 tvec_check(&srvtv, 0);
725 /* Conclude the test. */
726 tvec_setprogress("%%DONE");
727 if (env && env->after) env->after(&srvtv, ctx);
728 tvec_endtest(&srvtv);
731 /* Reset the input registers and report completion. */
732 tvec_releaseregs(&srvtv); tvec_initregs(&srvtv);
733 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_TEST | TVPF_ACK);
734 else { rc = -1; goto end; }
738 /* Some other kind of packet. Complain. */
740 rc = ioerr(&srvtv, &srvrc,
741 "unexpected packet type 0x%04x during test group",
749 /* The test group completed. */
751 /* Tear down the environment and release other resources. */
752 if (env && env->teardown) env->teardown(&srvtv, ctx);
753 tvec_releaseregs(&srvtv);
754 xfree(ctx); srvtv.test = 0; env = 0; ctx = 0;
756 /* Report completion. */
757 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
758 else { rc = -1; goto end; }
762 rc = ioerr(&srvtv, &srvrc,
763 "unexpected packet type 0x%04x at top level", pk);
769 /* Clean up and return. */
770 if (env && env->teardown) env->teardown(&srvtv, ctx);
772 if (srvtv.test) tvec_releaseregs(&srvtv);
773 release_comms(&srvrc); tvec_end(&srvtv);
777 /* Miscellaneous malformed packet. */
778 rc = malformed(&srvtv, &srvrc); goto end;
781 /*----- Server output driver ----------------------------------------------*/
783 static void remote_skipgroup(struct tvec_output *o,
784 const char *excuse, va_list *ap)
786 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
787 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
790 static void remote_skip(struct tvec_output *o,
791 const char *excuse, va_list *ap)
793 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
794 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
797 static void remote_fail(struct tvec_output *o,
798 const char *detail, va_list *ap)
800 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
802 dbuf_putbyte(&srvrc.bout, 0);
804 dbuf_putbyte(&srvrc.bout, 1);
805 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
809 static void remote_dumpreg(struct tvec_output *o,
810 unsigned disp, const union tvec_regval *rv,
811 const struct tvec_regdef *rd)
813 const struct tvec_regdef *reg;
816 /* Find the register definition. */
817 for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
818 if (reg == rd) goto found;
819 assert(!"unexpected register definition");
822 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
823 dbuf_putu16l(&srvrc.bout, r);
824 dbuf_putu16l(&srvrc.bout, disp);
826 dbuf_putbyte(&srvrc.bout, 0);
828 dbuf_putbyte(&srvrc.bout, 1);
829 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
834 static void remote_bbench(struct tvec_output *o,
835 const char *ident, unsigned unit)
837 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
838 dbuf_putstr32l(&srvrc.bout, ident);
839 dbuf_putu16l(&srvrc.bout, unit);
843 static void remote_ebench(struct tvec_output *o,
844 const char *ident, unsigned unit,
845 const struct bench_timing *t)
847 QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
848 dbuf_putstr32l(&srvrc.bout, ident);
849 dbuf_putu16l(&srvrc.bout, unit);
850 if (!t || !(t->f&BTF_ANY))
851 dbuf_putu16l(&srvrc.bout, 0);
853 dbuf_putu16l(&srvrc.bout, t->f);
854 dbuf_putf64l(&srvrc.bout, t->n);
855 if (t->f&BTF_TIMEOK) dbuf_putf64l(&srvrc.bout, t->t);
856 if (t->f&BTF_CYOK) dbuf_putf64l(&srvrc.bout, t->cy);
861 static void remote_report(struct tvec_output *o, unsigned level,
862 const char *msg, va_list *ap)
864 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
865 dbuf_putu16l(&srvrc.bout, level);
866 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
868 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
869 vfprintf(stderr, msg, *ap);
874 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
876 static int remote_esession(struct tvec_output *o)
877 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
878 static void remote_destroy(struct tvec_output *o)
880 static void remote_etest(struct tvec_output *o, unsigned outcome)
883 static void remote_bgroup(struct tvec_output *o)
884 { assert(!"remote_bgroup"); }
885 static void remote_egroup(struct tvec_output *o)
886 { assert(!"remote_egroup"); }
887 static void remote_btest(struct tvec_output *o)
888 { assert(!"remote_btest"); }
890 static const struct tvec_outops remote_ops = {
891 remote_bsession, remote_esession,
892 remote_bgroup, remote_skipgroup, remote_egroup,
893 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
894 remote_bbench, remote_ebench,
899 /*----- Client ------------------------------------------------------------*/
901 #define TVXF_VALMASK 0x0fffu
902 #define TVXF_SIG 0x1000u
903 #define TVXF_CAUSEMASK 0xe000u
904 #define TVXST_RUN 0x0000u
905 #define TVXST_EXIT 0x2000u
906 #define TVXST_KILL 0x4000u
907 #define TVXST_CONT 0x6000u
908 #define TVXST_STOP 0x8000u
909 #define TVXST_DISCONN 0xa000u
910 #define TVXST_UNK 0xc000u
911 #define TVXST_ERR 0xe000u
913 static const struct tvec_flag exit_flags[] = {
915 ;;; The signal name table is very boring to type. To make life less
916 ;;; awful, put the signal names in this list and evaluate the code to
917 ;;; get Emacs to regenerate it.
919 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
920 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
921 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
922 STKFLT INFO PWR THR LWP LIBRT LOST)))
924 (goto-char (point-min))
925 (search-forward (concat "***" "BEGIN siglist" "***"))
926 (beginning-of-line 2)
927 (delete-region (point)
929 (search-forward "***END***")
932 (dolist (sig signals)
933 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
937 /***BEGIN siglist***/
939 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
942 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
945 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
948 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
951 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
954 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
957 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
960 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
963 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
966 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
969 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
972 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
975 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
978 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
981 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
984 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
987 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
990 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
993 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
996 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
999 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1002 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1005 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1008 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1011 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1014 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1017 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1020 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1023 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1026 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1029 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1032 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1035 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1038 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1041 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1044 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1047 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1050 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1053 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1056 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1059 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1063 { "signal", TVXF_SIG, TVXF_SIG },
1065 { "running", TVXF_CAUSEMASK, TVXST_RUN },
1066 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
1067 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
1068 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
1069 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
1070 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
1071 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
1072 { "error", TVXF_CAUSEMASK, TVXST_ERR },
1077 static const struct tvec_flaginfo exit_flaginfo =
1078 { "exit-status", exit_flags, &tvrange_uint };
1079 static const struct tvec_regdef exit_regdef =
1080 { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
1082 static const struct tvec_regdef progress_regdef =
1083 { "@progress", 0, &tvty_string, 0 };
1085 static const struct tvec_uassoc reconn_assocs[] = {
1086 { "on-demand", TVRCN_DEMAND },
1087 { "force", TVRCN_FORCE },
1088 { "skip", TVRCN_SKIP },
1093 CONN_BROKEN = -2, /* previously broken */
1094 CONN_FAILED = -1, /* attempt freshly failed */
1095 CONN_ESTABLISHED = 0, /* previously established */
1096 CONN_FRESH = 1 /* freshly connected */
1099 static const struct tvec_uenuminfo reconn_enuminfo =
1100 { "remote-reconnection", reconn_assocs, &tvrange_uint };
1101 static const struct tvec_regdef reconn_regdef =
1102 { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
1104 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1105 unsigned f, uint16 end, buf *b_out)
1107 struct tvec_output *o = tv->output;
1109 const char *p; size_t n;
1112 const struct tvec_regdef *rd;
1113 struct bench_timing bt;
1114 struct tvec_reg *reg = 0;
1119 rc = remote_recv(tv, &r->rc, f, b); if (rc) goto end;
1120 if (buf_getu16l(b, &pk)) goto bad;
1121 if (pk == end) break;
1126 p = buf_getmem16l(b, &n); if (!p) goto bad;
1127 if (BLEFT(b)) goto bad;
1129 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1133 if (buf_getu16l(b, &u)) goto bad;
1134 p = buf_getmem16l(b, &n); if (!p) goto bad;
1135 if (BLEFT(b)) goto bad;
1137 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1138 tvec_report(tv, u, "%s", d.buf);
1142 p = buf_getmem16l(b, &n); if (!p) goto bad;
1143 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1144 if (BLEFT(b)) goto bad;
1146 tvec_skipgroup(tv, "%s", d.buf);
1150 if (!(tv->f&TVSF_ACTIVE)) {
1151 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1155 p = buf_getmem16l(b, &n); if (!p) goto bad;
1156 if (BLEFT(b)) goto bad;
1158 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1159 tvec_skip(tv, "%s", d.buf);
1163 if (!(tv->f&TVSF_ACTIVE) &&
1164 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
1165 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
1170 rc = buf_getbyte(b); if (rc < 0) goto bad;
1171 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1173 if (BLEFT(b)) goto bad;
1178 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1179 tvec_fail(tv, "%s", d.buf);
1184 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1185 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
1186 if (i == u) goto found_reg;
1187 rc = ioerr(tv, &r->rc,
1188 "register definition %u out of range for test `%s'",
1192 if (v >= TVRD_LIMIT) {
1193 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1197 rc = buf_getbyte(b); if (rc < 0) goto bad;
1199 tvec_dumpreg(tv, v, 0, rd);
1201 if (!reg) reg = xmalloc(tv->regsz);
1202 rd->ty->init(®->v, rd);
1203 rc = rd->ty->frombuf(b, ®->v, rd);
1204 if (!rc) tvec_dumpreg(tv, v, ®->v, rd);
1205 rd->ty->release(®->v, rd);
1208 if (BLEFT(b)) goto bad;
1212 p = buf_getmem32l(b, &n); if (!p) goto bad;
1213 if (buf_getu16l(b, &u)) goto bad;
1214 if (BLEFT(b)) goto bad;
1215 if (u >= TVBU_LIMIT) {
1216 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1220 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1221 o->ops->bbench(o, d.buf, u);
1225 p = buf_getmem32l(b, &n); if (!p) goto bad;
1226 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1227 if (u >= TVBU_LIMIT) {
1228 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1231 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
1232 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
1233 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
1234 if (BLEFT(b)) goto bad;
1236 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1237 o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
1241 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1252 rc = malformed(tv, &r->rc); goto end;
1255 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1261 { r->exit = TVXST_DISCONN; r->kid = -1; }
1262 else if (r->kid > 0) {
1263 kid = waitpid(r->kid, &st, 0);
1265 tvec_notice(tv, "failed to wait for remote child: %s",
1267 r->exit = TVXST_ERR;
1269 tvec_notice(tv, "remote child vanished without a trace");
1270 r->exit = TVXST_ERR;
1271 } else if (WIFCONTINUED(st))
1272 r->exit = TVXST_CONT;
1273 else if (WIFSIGNALED(st))
1274 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
1275 else if (WIFSTOPPED(st))
1276 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
1277 else if (WIFEXITED(st))
1278 r->exit = TVXST_EXIT | WEXITSTATUS(st);
1280 tvec_notice(tv, "remote child died with unknown status 0x%04x",
1282 r->exit = TVXST_UNK;
1288 static void report_errline(char *p, size_t n, void *ctx)
1290 struct tvec_remotectx *r = ctx;
1291 struct tvec_state *tv = r->tv;
1293 if (p && !(r->rc.f&TVRF_MUFFLE))
1294 tvec_notice(tv, "child process stderr: %s", p);
1297 #define ERF_SILENT 0x0001u
1298 #define ERF_CLOSE 0x0002u
1299 static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
1306 if (r->errfd < 0) { rc = 0; goto end; }
1307 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
1308 else r->rc.f &= ~TVRF_MUFFLE;
1309 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
1310 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
1311 f&ERF_CLOSE ? "clear" : "set");
1316 sz = lbuf_free(&r->errbuf, &p);
1317 n = read(r->errfd, p, sz);
1320 if (errno == EINTR) continue;
1321 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1323 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1327 lbuf_flush(&r->errbuf, p, n);
1332 lbuf_close(&r->errbuf);
1333 close(r->errfd); r->errfd = -1;
1338 #define DCF_KILL 0x0100u
1339 static void disconnect_remote(struct tvec_state *tv,
1340 struct tvec_remotectx *r, unsigned f)
1342 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1343 close_comms(&r->rc);
1344 drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
1347 static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1349 const struct tvec_remoteenv *re = r->re;
1353 int infd = -1, outfd = -1, errfd = -1, rc;
1355 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1356 if (r->kid >= 0) { rc = 0; goto end; }
1357 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1358 { rc = -1; goto end; }
1359 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
1360 lbuf_init(&r->errbuf, report_errline, r);
1361 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
1363 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1364 dbuf_putu16l(&r->rc.bout, 0);
1365 dbuf_putu16l(&r->rc.bout, 0);
1366 } else { rc = -1; goto end; }
1368 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
1369 { rc = -1; goto end; }
1370 if (buf_getu16l(&b, &v)) goto bad;
1371 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1373 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
1377 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1378 dbuf_putstr16l(&r->rc.bout, tv->test->name);
1379 else { rc = -1; goto end; }
1380 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
1381 { rc = -1; goto end; }
1382 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1385 if (rc) disconnect_remote(tv, r, DCF_KILL);
1388 rc = malformed(tv, &r->rc); goto end;
1391 static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1394 return (CONN_BROKEN);
1395 else if (r->rc.f&TVRF_BROKEN)
1396 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1398 return (CONN_ESTABLISHED);
1401 static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1405 switch (r->rc.f&TVRF_RCNMASK) {
1407 rc = check_comms(tv, r);
1408 if (rc < CONN_ESTABLISHED) {
1409 close_comms(&r->rc);
1410 if (connect_remote(tv, r)) rc = CONN_FAILED;
1411 else rc = CONN_FRESH;
1415 disconnect_remote(tv, r, DCF_KILL);
1416 if (connect_remote(tv, r)) rc = CONN_FAILED;
1417 else rc = CONN_FRESH;
1420 rc = check_comms(tv, r);
1428 static void reset_vars(struct tvec_remotectx *r)
1430 r->exwant = TVXST_RUN;
1431 r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) | TVRCN_DEMAND;
1432 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1435 void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1436 void *pctx, void *ctx)
1438 struct tvec_remotectx *r = ctx;
1439 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1441 assert(!re->r.env || tv->test->env == &re->_env);
1445 r->re = re; r->kid = -1;
1446 DCREATE(&r->prgwant); DCREATE(&r->progress);
1447 if (connect_remote(tv, r))
1448 tvec_skipgroup(tv, "failed to connect to test backend");
1452 int tvec_remoteset(struct tvec_state *tv, const char *var, void *ctx)
1454 struct tvec_remotectx *r = ctx;
1455 union tvec_regval rv;
1458 if (STRCMP(var, ==, "@exit")) {
1459 if (r->rc.f&TVRF_SETEXIT) { rc = tvec_dupreg(tv, var); goto end; }
1460 if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
1461 r->exwant = rv.u; r->rc.f |= TVRF_SETEXIT; rc = 1;
1462 } else if (STRCMP(var, ==, "@progress")) {
1463 if (r->rc.f&TVRF_SETPRG) { rc = tvec_dupreg(tv, var); goto end; }
1464 tvty_string.init(&rv, &progress_regdef);
1465 rc = tvty_string.parse(&rv, &progress_regdef, tv);
1467 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.str.p, rv.str.sz);
1468 r->rc.f |= TVRF_SETPRG;
1470 tvty_string.release(&rv, &progress_regdef);
1471 if (rc) { rc = -1; goto end; }
1473 } else if (STRCMP(var, ==, "@reconnect")) {
1474 if (r->rc.f&TVRF_SETRCN) { rc = tvec_dupreg(tv, var); goto end; }
1475 if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
1476 r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK) | TVRF_SETRCN;
1485 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
1487 struct tvec_remotectx *r = ctx;
1492 void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
1494 struct tvec_remotectx *r = ctx;
1495 union tvec_regval rv;
1498 #define f_progress 2u
1503 switch (try_reconnect(tv, r)) {
1505 tvec_skip(tv, "failed to connect to test backend"); return;
1507 tvec_skip(tv, "no connection"); return;
1510 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
1511 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
1512 tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
1513 tv->test->regs, tv->nreg, tv->regsz);
1514 else { rc = -1; goto end; }
1515 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
1523 if (r->exit != r->exwant) f |= f_exit;
1524 if (r->progress.len != r->prgwant.len ||
1525 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
1527 if (f && (tv->f&TVSF_ACTIVE))
1528 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
1529 if (!(tv->f&TVSF_ACTIVE) &&
1530 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
1534 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
1538 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
1541 rv.str.p = r->progress.buf; rv.str.sz = r->progress.len;
1542 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
1543 &rv, &progress_regdef);
1545 rv.str.p = r->prgwant.buf; rv.str.sz = r->prgwant.len;
1546 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
1551 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
1557 if ((tv->f&TVSF_ACTIVE) && f)
1558 tvec_skip(tv, "remote test runner communications failed");
1559 disconnect_remote(tv, r, 0);
1567 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
1569 struct tvec_remotectx *r = ctx;
1572 if (r->rc.outfd >= 0) {
1573 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
1574 if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
1575 if (BLEFT(&b)) malformed(tv, &r->rc);
1577 disconnect_remote(tv, r, 0); release_comms(&r->rc);
1578 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
1581 /*----- Connectors --------------------------------------------------------*/
1583 static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
1584 int *errfd_out, struct tvec_state *tv)
1586 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
1590 if (pipe(p0) || pipe(p1) || pipe(pe) ||
1591 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1592 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1593 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
1594 tvec_error(tv, "pipe failed: %s", strerror(errno));
1602 tvec_error(tv, "fork failed: %s", strerror(errno));
1608 *infd_out = p0[0]; p0[0] = -1;
1609 *outfd_out = p1[1]; p1[1] = -1;
1610 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
1611 fprintf(stderr, "failed to establish child stderr: %s",
1616 *kid_out = kid; kid = -1;
1617 *infd_out = p1[0]; p1[0] = -1;
1618 *outfd_out = p0[1]; p0[1] = -1;
1619 *errfd_out = pe[0]; pe[0] = -1;
1624 if (p0[0] >= 0) close(p0[0]);
1625 if (p0[1] >= 0) close(p0[1]);
1626 if (p1[0] >= 0) close(p1[0]);
1627 if (p1[1] >= 0) close(p1[1]);
1628 if (pe[0] >= 0) close(pe[0]);
1629 if (pe[1] >= 0) close(pe[1]);
1633 int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1634 struct tvec_state *tv, const struct tvec_remoteenv *env)
1636 struct tvec_config config;
1637 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
1639 int infd = -1, outfd = -1, errfd = -1;
1642 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1644 if (tv->fp) fclose(tv->fp);
1645 config.tests = rf->f.tests ? rf->f.tests : tv->tests;
1646 config.nrout = tv->nrout; config.nreg = tv->nreg;
1647 config.regsz = tv->regsz;
1648 _exit(tvec_remoteserver(infd, outfd, &config));
1651 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1657 int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1658 struct tvec_state *tv, const struct tvec_remoteenv *env)
1660 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
1662 int infd = -1, outfd = -1, errfd = -1;
1666 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1668 v[0].cur = infd; v[0].want = STDIN_FILENO;
1669 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
1671 fprintf(stderr, "failed to establish standard file descriptors: %s",
1675 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
1676 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
1680 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1686 /*----- That's all, folks -------------------------------------------------*/