chiark / gitweb /
@@@ remote works?
[mLib] / test / tvec-remote.c
CommitLineData
e63124bc
MW
1/* -*-c-*-
2 *
3 * Remote testing
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <errno.h>
c91413e6 31#include <signal.h>
e63124bc
MW
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
c91413e6 38#include <sys/wait.h>
e63124bc
MW
39#include <fcntl.h>
40#include <unistd.h>
41
42#include "alloc.h"
c91413e6 43#include "bench.h"
e63124bc 44#include "buf.h"
c91413e6
MW
45#include "compiler.h"
46#include "fdflags.h"
47#include "lbuf.h"
48#include "mdup.h"
49#include "quis.h"
e63124bc
MW
50#include "tvec.h"
51
c91413e6
MW
52#if GCC_VERSION_P(7, 1)
53# pragma GCC diagnostic ignored "-Wdangling-else"
54#elif GCC_VERSION_P(4, 2)
55# pragma GCC diagnostic ignored "-Wparentheses"
56#endif
e63124bc 57
c91413e6
MW
58#if CLANG_VERSION_P(3, 1)
59# pragma clang diagnostic ignored "-Wdangling-else"
60#endif
e63124bc 61
c91413e6 62/*----- Basic I/O ---------------------------------------------------------*/
e63124bc 63
c91413e6
MW
64static void init_comms(struct tvec_remotecomms *rc)
65{
66 dbuf_create(&rc->bin); dbuf_create(&rc->bout);
67 rc->infd = rc->outfd = -1; rc->f = 0;
68}
e63124bc 69
c91413e6
MW
70static void close_comms(struct tvec_remotecomms *rc)
71{
72 if (rc->infd >= 0) { close(rc->infd); rc->infd = -1; }
73 if (rc->outfd >= 0) { close(rc->outfd); rc->outfd = -1; }
74}
75
76static void release_comms(struct tvec_remotecomms *rc)
77 { close_comms(rc); dbuf_destroy(&rc->bin); dbuf_destroy(&rc->bout); }
78
79static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
80{
81 rc->infd = infd; rc->outfd = outfd; rc->f &= ~0xffu;
82 dbuf_reset(&rc->bin); dbuf_reset(&rc->bout);
83}
e63124bc
MW
84
85static int PRINTF_LIKE(3, 4)
c91413e6
MW
86 ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
87 const char *msg, ...)
e63124bc
MW
88{
89 va_list ap;
90
91 va_start(ap, msg);
c91413e6
MW
92 close_comms(rc); rc->f |= TVRF_BROKEN;
93 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
e63124bc
MW
94 va_end(ap);
95 return (-1);
96}
97
c91413e6 98static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
e63124bc
MW
99 const unsigned char *p, size_t sz)
100{
c91413e6 101 void (*opipe)(int) = SIG_ERR;
e63124bc 102 ssize_t n;
c91413e6 103 int ret;
e63124bc 104
c91413e6
MW
105 opipe = signal(SIGPIPE, SIG_IGN);
106 if (opipe == SIG_ERR) {
107 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
108 goto end;
109 }
e63124bc 110 while (sz) {
c91413e6 111 n = write(rc->outfd, p, sz);
e63124bc
MW
112 if (n > 0)
113 { p += n; sz -= n; }
c91413e6
MW
114 else {
115 ret = ioerr(tv, rc, "failed to send: %s",
116 n ? strerror(errno) : "empty write");
117 goto end;
118 }
e63124bc 119 }
c91413e6
MW
120 ret = 0;
121end:
122 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
123 return (ret);
e63124bc
MW
124}
125
126#define RCVF_ALLOWEOF 1u
c91413e6
MW
127enum {
128 RECV_FAIL = -1,
129 RECV_OK = 0,
130 RECV_EOF = 1
131};
132static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
e63124bc
MW
133 unsigned char *p, size_t sz, unsigned f)
134{
135 ssize_t n;
136 unsigned ff = 0;
137#define f_any 1u
138
139 while (sz) {
c91413e6 140 n = read(rc->infd, p, sz);
e63124bc
MW
141 if (n > 0)
142 { p += n; sz -= n; ff |= f_any; }
143 else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any))
c91413e6 144 return (RECV_EOF);
e63124bc 145 else
c91413e6 146 return (ioerr(tv, rc, "failed to receive: %s",
e63124bc
MW
147 n ? strerror(errno) : "unexpected end-of-file"));
148 }
c91413e6 149 return (RECV_OK);
e63124bc
MW
150
151#undef f_any
152}
153
c91413e6 154static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
e63124bc
MW
155{
156 kludge64 k; unsigned char lenbuf[8];
c91413e6 157 const unsigned char *p; size_t sz;
e63124bc 158
c91413e6
MW
159 if (rc->f&TVRF_BROKEN) return (-1);
160 if (BBAD(&rc->bout._b))
161 return (ioerr(tv, rc, "failed to build output packet buffer"));
e63124bc 162
c91413e6 163 p = BBASE(&rc->bout._b); sz = BLEN(&rc->bout._b);
e63124bc 164 ASSIGN64(k, sz); STORE64_L_(lenbuf, k);
c91413e6
MW
165 if (send_all(tv, rc, lenbuf, sizeof(lenbuf))) return (-1);
166 if (send_all(tv, rc, p, sz)) return (-1);
e63124bc
MW
167
168 return (0);
169}
170
c91413e6
MW
171static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
172 unsigned f, buf *b_out)
e63124bc
MW
173{
174 kludge64 k, szmax; unsigned char lenbuf[8];
175 unsigned char *p;
176 size_t sz;
c91413e6 177 int ret;
e63124bc 178
c91413e6
MW
179 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
180 ASSIGN64(szmax, (size_t)-1);
181 ret = recv_all(tv, rc, lenbuf, sizeof(lenbuf), f);
182 if (ret) return (ret);
e63124bc
MW
183 LOAD64_L_(k, lenbuf);
184 if (CMP64(k, >, szmax))
c91413e6 185 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
e63124bc
MW
186 (unsigned long)HI64(k), (unsigned long)LO64(k)));
187
c91413e6
MW
188 sz = GET64(size_t, k); dbuf_reset(&rc->bin); p = buf_get(&rc->bin._b, sz);
189 if (!p) return (ioerr(tv, rc, "failed to allocate receive buffer"));
190 if (recv_all(tv, rc, p, sz, 0)) return (RECV_FAIL);
191 buf_init(b_out, p, sz); return (RECV_OK);
e63124bc
MW
192}
193
c91413e6
MW
194#define SENDPK(tv, rc, pk) \
195 if ((rc)->f&TVRF_BROKEN) MC_GOELSE(body); else \
196 MC_BEFORE(setpk, \
197 { dbuf_reset(&(rc)->bout); \
198 buf_putu16l(&(rc)->bout._b, (pk)); }) \
199 MC_ALLOWELSE(body) \
200 MC_AFTER(send, \
201 { if (remote_send(tv, rc)) MC_GOELSE(body); }) \
e63124bc 202
c91413e6
MW
203static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
204 { return (ioerr(tv, rc, "received malformed packet")); }
e63124bc
MW
205
206/*----- Packet types ------------------------------------------------------*/
207
c91413e6 208#define TVPF_ACK 0x0001u
e63124bc 209
c91413e6
MW
210#define TVPK_VER 0x0000u /* --> min, max: u16 */
211 /* <-- ver: u16 */
e63124bc 212
c91413e6
MW
213#define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
214#define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
e63124bc 215
c91413e6
MW
216#define TVPK_BGROUP 0x0200u /* --> name: str16
217 * <-- --- */
218#define TVPK_TEST 0x0202u /* --> in: regs
219 * <-- --- */
220#define TVPK_EGROUP 0x0204u /* --> --- */
e63124bc 221
c91413e6
MW
222#define TVPK_SKIPGRP 0x0300u /* <-- excuse: str16 */
223#define TVPK_SKIP 0x0302u /* <-- excuse: str16 */
224#define TVPK_FAIL 0x0304u /* <-- flag: u8, detail: str16 */
225#define TVPK_DUMPREG 0x0306u /* <-- ri: u16; disp: u16;
226 * flag: u8, rv: value */
227#define TVPK_BBENCH 0x0308u /* <-- ident: str32; unit: u16 */
228#define TVPK_EBENCH 0x030au /* <-- ident: str32; unit: u16;
229 * flags: u16; n, t, cy: f64 */
e63124bc 230
c91413e6
MW
231/*----- Server ------------------------------------------------------------*/
232
233static const struct tvec_outops remote_ops;
234
235static struct tvec_state srvtv;
236static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT;
237static struct tvec_output srvout = { &remote_ops };
e63124bc 238
c91413e6
MW
239int tvec_setprogress(const char *status)
240{
241 SENDPK(&srvtv, &srvrc, TVPK_PROGRESS)
242 buf_putstr16l(&srvrc.bout._b, status);
243 else return (-1);
244 return (0);
e63124bc
MW
245}
246
c91413e6 247int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
e63124bc 248{
c91413e6
MW
249 uint16 pk, u, v;
250 unsigned i;
251 buf b;
252 const struct tvec_test *t;
253 void *p; size_t sz;
254 const struct tvec_env *env = 0;
255 unsigned f = 0;
256#define f_regslive 1u
257 void *ctx = 0;
258 int rc;
259
260 setup_comms(&srvrc, infd, outfd);
261 tvec_begin(&srvtv, config, &srvout);
262
263 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
264 if (buf_getu16l(&b, &pk)) goto bad;
265 if (pk != TVPK_VER) {
266 rc = ioerr(&srvtv, &srvrc,
267 "unexpected packet type 0x%04x instead of client version",
268 pk);
269 goto end;
e63124bc 270 }
c91413e6
MW
271 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
272 SENDPK(&srvtv, &srvrc, TVPK_VER | TVPF_ACK) buf_putu16l(&srvrc.bout._b, 0);
273 else { rc = -1; goto end; }
e63124bc 274
c91413e6 275 tvec_setprogress("%IDLE");
e63124bc 276
c91413e6
MW
277 for (;;) {
278 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
279 if (rc == RECV_EOF) break;
280 else if (rc == RECV_FAIL) goto end;
281 if (buf_getu16l(&b, &pk)) goto bad;
e63124bc 282
c91413e6
MW
283 switch (pk) {
284
285 case TVPK_BGROUP:
286 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
287 if (BLEFT(&b)) goto bad;
288 for (t = srvtv.tests; t->name; t++)
289 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
290 goto found_group;
291 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
292 (int)sz, (char *)p);
293 goto end;
294
295 found_group:
296 srvtv.test = t; env = t->env;
297 if (env && env->setup == tvec_remotesetup)
298 env = ((struct tvec_remoteenv *)env)->r.env;
299 if (!env || !env->ctxsz) ctx = 0;
300 else ctx = xmalloc(env->ctxsz);
301 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
302
303 SENDPK(&srvtv, &srvrc, TVPK_BGROUP | TVPF_ACK);
304 else { rc = -1; goto end; }
305
306 for (;;) {
307 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
308 if (buf_getu16l(&b, &pk)) goto bad;
309 switch (pk) {
310
311 case TVPK_EGROUP:
312 if (BLEFT(&b)) goto bad;
313 goto endgroup;
314
315 case TVPK_TEST:
316 tvec_initregs(&srvtv); f |= f_regslive;
317 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
318 srvtv.nreg, srvtv.regsz))
319 goto bad;
320 if (BLEFT(&b)) goto bad;
321
322 if (!(srvtv.f&TVSF_SKIP)) {
323 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
324 tvec_setprogress("%SETUP");
325 if (env && env->before) env->before(&srvtv, ctx);
326 if (!(srvtv.f&TVSF_ACTIVE))
327 /* setup forced a skip */;
328 else {
329 for (i = 0; i < srvtv.nrout; i++)
330 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
331 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
332 tvec_setprogress("%RUN");
333 if (env && env->run)
334 env->run(&srvtv, t->fn, ctx);
335 else {
336 t->fn(srvtv.in, srvtv.out, ctx);
337 tvec_check(&srvtv, 0);
338 }
339 }
340 tvec_setprogress("%DONE");
341 if (env && env->after) env->after(&srvtv, ctx);
342 tvec_endtest(&srvtv);
343 }
344 tvec_releaseregs(&srvtv); f &= ~f_regslive;
345 SENDPK(&srvtv, &srvrc, TVPK_TEST | TVPF_ACK);
346 else { rc = -1; goto end; }
347 tvec_setprogress("%IDLE");
348 break;
349
350 default:
351 rc = ioerr(&srvtv, &srvrc,
352 "unexpected packet type 0x%04x", pk);
353 goto end;
354
355 }
356 }
357
358 endgroup:
359 if (env && env->teardown) env->teardown(&srvtv, ctx);
360 xfree(ctx); t = 0; env = 0; ctx = 0;
361 break;
362
363 default:
364 goto bad;
365 }
366 }
367 rc = 0;
368
369end:
370 if (env && env->teardown) env->teardown(&srvtv, ctx);
371 xfree(ctx);
372 if (f&f_regslive) tvec_releaseregs(&srvtv);
373 release_comms(&srvrc);
374 return (rc ? 2 : 0);
375
376bad:
377 rc = malformed(&srvtv, &srvrc); goto end;
378
379#undef f_regslive
e63124bc
MW
380}
381
c91413e6
MW
382/*----- Server output driver ----------------------------------------------*/
383
e63124bc
MW
384static void remote_skipgroup(struct tvec_output *o,
385 const char *excuse, va_list *ap)
c91413e6
MW
386{
387 SENDPK(&srvtv, &srvrc, TVPK_SKIPGRP)
388 buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
389}
e63124bc
MW
390
391static void remote_skip(struct tvec_output *o,
392 const char *excuse, va_list *ap)
c91413e6
MW
393{
394 SENDPK(&srvtv, &srvrc, TVPK_SKIP)
395 buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
396}
e63124bc
MW
397
398static void remote_fail(struct tvec_output *o,
399 const char *detail, va_list *ap)
c91413e6
MW
400{
401 SENDPK(&srvtv, &srvrc, TVPK_FAIL)
402 if (!detail)
403 buf_putbyte(&srvrc.bout._b, 0);
404 else {
405 buf_putbyte(&srvrc.bout._b, 1);
406 buf_vputstrf16l(&srvrc.bout._b, detail, ap);
407 }
408}
e63124bc 409
c91413e6
MW
410static void remote_dumpreg(struct tvec_output *o,
411 unsigned disp, const union tvec_regval *rv,
412 const struct tvec_regdef *rd)
e63124bc 413{
c91413e6
MW
414 const struct tvec_regdef *reg;
415 unsigned r;
e63124bc 416
c91413e6
MW
417 /* Find the register definition. */
418 for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
419 if (reg == rd) goto found;
420 assert(!"unexpected register definition");
421
422found:
423 SENDPK(&srvtv, &srvrc, TVPK_DUMPREG) {
424 buf_putu16l(&srvrc.bout._b, r);
425 buf_putu16l(&srvrc.bout._b, disp);
426 if (!rv)
427 buf_putbyte(&srvrc.bout._b, 0);
428 else {
429 buf_putbyte(&srvrc.bout._b, 1);
430 rd->ty->tobuf(&srvrc.bout._b, rv, rd);
431 }
e63124bc
MW
432 }
433}
434
c91413e6
MW
435static void remote_bbench(struct tvec_output *o,
436 const char *ident, unsigned unit)
e63124bc 437{
c91413e6
MW
438 SENDPK(&srvtv, &srvrc, TVPK_BBENCH) {
439 buf_putstr32l(&srvrc.bout._b, ident);
440 buf_putu16l(&srvrc.bout._b, unit);
441 }
e63124bc
MW
442}
443
444static void remote_ebench(struct tvec_output *o,
c91413e6 445 const char *ident, unsigned unit,
e63124bc
MW
446 const struct bench_timing *t)
447{
c91413e6
MW
448 SENDPK(&srvtv, &srvrc, TVPK_EBENCH) {
449 buf_putstr32l(&srvrc.bout._b, ident);
450 buf_putu16l(&srvrc.bout._b, unit);
451 if (!t || !(t->f&BTF_ANY))
452 buf_putu16l(&srvrc.bout._b, 0);
453 else {
454 buf_putu16l(&srvrc.bout._b, t->f);
455 buf_putf64l(&srvrc.bout._b, t->n);
456 if (t->f&BTF_TIMEOK) buf_putf64l(&srvrc.bout._b, t->t);
457 if (t->f&BTF_CYOK) buf_putf64l(&srvrc.bout._b, t->cy);
458 }
459 }
460}
461
462static void remote_report(struct tvec_output *o, unsigned level,
463 const char *msg, va_list *ap)
464{
465 const char *what;
e63124bc 466
c91413e6
MW
467 SENDPK(&srvtv, &srvrc, TVPK_REPORT) {
468 buf_putu16l(&srvrc.bout._b, level);
469 buf_vputstrf16l(&srvrc.bout._b, msg, ap);
470 } else {
471 switch (level) {
472 case TVLEV_NOTE: what = "notice"; break;
473 case TVLEV_ERR: what = "ERROR"; break;
474 default: what = "(?level)"; break;
475 }
476 fprintf(stderr, "%s %s: ", QUIS, what);
477 vfprintf(stderr, msg, *ap);
478 fputc('\n', stderr);
e63124bc
MW
479 }
480}
481
c91413e6
MW
482static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
483 { ; }
e63124bc 484static int remote_esession(struct tvec_output *o)
c91413e6
MW
485 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
486static void remote_destroy(struct tvec_output *o)
487 { ; }
488static void remote_etest(struct tvec_output *o, unsigned outcome)
489 { ; }
490
e63124bc
MW
491static void remote_bgroup(struct tvec_output *o)
492 { assert(!"remote_bgroup"); }
c91413e6
MW
493static void remote_egroup(struct tvec_output *o)
494 { assert(!"remote_egroup"); }
e63124bc
MW
495static void remote_btest(struct tvec_output *o)
496 { assert(!"remote_btest"); }
e63124bc
MW
497
498static const struct tvec_outops remote_ops = {
e63124bc 499 remote_bsession, remote_esession,
c91413e6
MW
500 remote_bgroup, remote_skipgroup, remote_egroup,
501 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
e63124bc 502 remote_bbench, remote_ebench,
c91413e6 503 remote_report,
e63124bc 504 remote_destroy
c91413e6
MW
505};
506
507/*----- Client ------------------------------------------------------------*/
508
509#define TVXF_VALMASK 0x0fffu
510#define TVXF_SIG 0x1000u
511#define TVXF_CAUSEMASK 0xe000u
512#define TVXST_RUN 0x0000u
513#define TVXST_EXIT 0x2000u
514#define TVXST_KILL 0x4000u
515#define TVXST_CONT 0x6000u
516#define TVXST_STOP 0x8000u
517#define TVXST_DISCONN 0xa000u
518#define TVXST_UNK 0xc000u
519#define TVXST_ERR 0xe000u
520
521static const struct tvec_flag exit_flags[] = {
522 /*
523 ;;; The signal name table is very boring to type. To make life less
524 ;;; awful, put the signal names in this list and evaluate the code to
525 ;;; get Emacs to regenerate it.
526
527 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
528 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
529 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
530 STKFLT INFO PWR THR LWP LIBRT LOST)))
531 (save-excursion
532 (goto-char (point-min))
533 (search-forward (concat "***" "BEGIN siglist" "***"))
534 (beginning-of-line 2)
535 (delete-region (point)
536 (progn
537 (search-forward "***END***")
538 (beginning-of-line)
539 (point)))
540 (dolist (sig signals)
541 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
542 sig sig sig)))))
543 */
544
545 /***BEGIN siglist***/
546#ifdef SIGHUP
547 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
548#endif
549#ifdef SIGINT
550 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
551#endif
552#ifdef SIGQUIT
553 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
554#endif
555#ifdef SIGILL
556 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
557#endif
558#ifdef SIGTRAP
559 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
560#endif
561#ifdef SIGABRT
562 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
563#endif
564#ifdef SIGIOT
565 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
566#endif
567#ifdef SIGEMT
568 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
569#endif
570#ifdef SIGFPE
571 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
572#endif
573#ifdef SIGKILL
574 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
575#endif
576#ifdef SIGBUS
577 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
578#endif
579#ifdef SIGSEGV
580 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
581#endif
582#ifdef SIGSYS
583 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
584#endif
585#ifdef SIGPIPE
586 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
587#endif
588#ifdef SIGALRM
589 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
590#endif
591#ifdef SIGTERM
592 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
593#endif
594#ifdef SIGURG
595 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
596#endif
597#ifdef SIGSTOP
598 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
599#endif
600#ifdef SIGTSTP
601 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
602#endif
603#ifdef SIGCONT
604 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
605#endif
606#ifdef SIGCHLD
607 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
608#endif
609#ifdef SIGCLD
610 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
611#endif
612#ifdef SIGTTIN
613 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
614#endif
615#ifdef SIGTTOU
616 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
617#endif
618#ifdef SIGPOLL
619 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
620#endif
621#ifdef SIGIO
622 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
623#endif
624#ifdef SIGTIN
625 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
626#endif
627#ifdef SIGXCPU
628 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
629#endif
630#ifdef SIGXFSZ
631 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
632#endif
633#ifdef SIGVTALRM
634 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
635#endif
636#ifdef SIGPROF
637 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
638#endif
639#ifdef SIGWINCH
640 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
641#endif
642#ifdef SIGUSR1
643 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
644#endif
645#ifdef SIGUSR2
646 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
647#endif
648#ifdef SIGSTKFLT
649 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
650#endif
651#ifdef SIGINFO
652 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
653#endif
654#ifdef SIGPWR
655 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
656#endif
657#ifdef SIGTHR
658 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
659#endif
660#ifdef SIGLWP
661 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
662#endif
663#ifdef SIGLIBRT
664 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
665#endif
666#ifdef SIGLOST
667 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
668#endif
669 /***END***/
670
671 { "signal", TVXF_SIG, TVXF_SIG },
672
673 { "running", TVXF_CAUSEMASK, TVXST_RUN },
674 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
675 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
676 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
677 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
678 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
679 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
680 { "error", TVXF_CAUSEMASK, TVXST_ERR },
681
682 TVEC_ENDFLAGS
683};
684
685static const struct tvec_flaginfo exit_flaginfo =
686 { "exit-status", exit_flags, &tvrange_uint };
687static const struct tvec_regdef exit_regdef =
688 { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
689
690static const struct tvec_regdef progress_regdef =
691 { "@progress", 0, &tvty_string, 0 };
692
693static const struct tvec_uassoc reconn_assocs[] = {
694 { "on-demand", TVRCN_DEMAND },
695 { "force", TVRCN_FORCE },
696 { "skip", TVRCN_SKIP },
697 TVEC_ENDENUM
698};
699
700enum {
701 CONN_BROKEN = -2, /* previously broken */
702 CONN_FAILED = -1, /* attempt freshly failed */
703 CONN_ESTABLISHED = 0, /* previously established */
704 CONN_FRESH = 1 /* freshly connected */
705};
706
707static const struct tvec_uenuminfo reconn_enuminfo =
708 { "remote-reconnection", reconn_assocs, &tvrange_uint };
709static const struct tvec_regdef reconn_regdef =
710 { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
711
712static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
713 unsigned f, uint16 end, buf *b_out)
714{
715 struct tvec_output *o = tv->output;
716 uint16 pk, u, v;
717 const char *p; size_t n;
718 dstr d = DSTR_INIT;
719 buf *b = b_out;
720 const struct tvec_regdef *rd;
721 struct bench_timing bt;
722 struct tvec_reg *reg = 0;
723 unsigned i;
724 int rc;
725
726 for (;;) {
727 rc = remote_recv(tv, &r->rc, f, b); if (rc) goto end;
728 if (buf_getu16l(b, &pk)) goto bad;
729
730 switch (pk) {
731
732 case TVPK_PROGRESS:
733 p = buf_getmem16l(b, &n); if (!p) goto bad;
734 if (BLEFT(b)) goto bad;
735
736 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
737 break;
738
739 case TVPK_REPORT:
740 if (buf_getu16l(b, &u)) goto bad;
741 p = buf_getmem16l(b, &n); if (!p) goto bad;
742 if (BLEFT(b)) goto bad;
743
744 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
745 tvec_report(tv, u, "%s", d.buf);
746 break;
747
748 case TVPK_SKIPGRP:
749 p = buf_getmem16l(b, &n); if (!p) goto bad;
750 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
751 if (BLEFT(b)) goto bad;
752
753 tvec_skipgroup(tv, "%s", d.buf);
754 break;
755
756 case TVPK_SKIP:
757 if (!(tv->f&TVSF_ACTIVE)) {
758 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
759 goto end;
760 }
761
762 p = buf_getmem16l(b, &n); if (!p) goto bad;
763 if (BLEFT(b)) goto bad;
764
765 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
766 tvec_skip(tv, "%s", d.buf);
767 break;
768
769 case TVPK_FAIL:
770 if (!(tv->f&TVSF_ACTIVE) &&
771 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
772 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
773 tv->test->name);
774 goto end;
775 }
776
777 rc = buf_getbyte(b); if (rc < 0) goto bad;
778 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
779 else p = 0;
780 if (BLEFT(b)) goto bad;
e63124bc 781
c91413e6
MW
782 if (!p)
783 tvec_fail(tv, 0);
784 else {
785 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
786 tvec_fail(tv, "%s", d.buf);
787 }
788 break;
e63124bc 789
c91413e6
MW
790 case TVPK_DUMPREG:
791 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
792 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
793 if (i == u) goto found_reg;
794 rc = ioerr(tv, &r->rc,
795 "register definition %u out of range for test `%s'",
796 u, tv->test->name);
797 goto end;
798 found_reg:
799 if (v >= TVRD_LIMIT) {
800 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
801 goto end;
802 }
e63124bc 803
c91413e6
MW
804 rc = buf_getbyte(b); if (rc < 0) goto bad;
805 if (!rc)
806 tvec_dumpreg(tv, v, 0, rd);
807 else {
808 if (!reg) reg = xmalloc(tv->regsz);
809 rd->ty->init(&reg->v, rd);
810 rc = rd->ty->frombuf(b, &reg->v, rd);
811 if (!rc) tvec_dumpreg(tv, v, &reg->v, rd);
812 rd->ty->release(&reg->v, rd);
813 if (rc) goto bad;
814 }
815 if (BLEFT(b)) goto bad;
816 break;
817
818 case TVPK_BBENCH:
819 p = buf_getmem32l(b, &n); if (!p) goto bad;
820 if (buf_getu16l(b, &u)) goto bad;
821 if (BLEFT(b)) goto bad;
822 if (u >= TVBU_LIMIT) {
823 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
824 goto end;
825 }
826
827 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
828 o->ops->bbench(o, d.buf, u);
829 break;
830
831 case TVPK_EBENCH:
832 p = buf_getmem32l(b, &n); if (!p) goto bad;
833 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
834 if (u >= TVBU_LIMIT)
835 { rc = ioerr(tv, &r->rc, "unit code %u out of range", u); goto end; }
836 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
837 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
838 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
839 if (BLEFT(b)) goto bad;
840
841 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
842 o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
843 break;
844
845 default:
846 if (pk == end) { rc = 0; goto end; }
847 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
848 goto end;
849 }
850 }
851
852end:
853 DDESTROY(&d);
854 xfree(reg);
855 return (rc);
856bad:
857 rc = malformed(tv, &r->rc); goto end;
858}
859
860static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
861{
862 pid_t kid;
863 int st;
864
865 if (!r->kid)
866 { r->exit = TVXST_DISCONN; r->kid = -1; }
867 else if (r->kid > 0) {
868 kid = waitpid(r->kid, &st, 0);
869 if (kid < 0) {
870 tvec_notice(tv, "failed to wait for remote child: %s",
871 strerror(errno));
872 r->exit = TVXST_ERR;
873 } else if (!kid) {
874 tvec_notice(tv, "remote child vanished without a trace");
875 r->exit = TVXST_ERR;
876 } else if (WIFCONTINUED(st))
877 r->exit = TVXST_CONT;
878 else if (WIFSIGNALED(st))
879 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
880 else if (WIFSTOPPED(st))
881 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
882 else if (WIFEXITED(st))
883 r->exit = TVXST_EXIT | WEXITSTATUS(st);
884 else {
885 tvec_notice(tv, "remote child died with unknown status 0x%04x",
886 (unsigned)st);
887 r->exit = TVXST_UNK;
888 }
889 r->kid = -1;
890 }
891}
892
893static void report_errline(char *p, size_t n, void *ctx)
894{
895 struct tvec_remotectx *r = ctx;
896 struct tvec_state *tv = r->tv;
897
898 if (p && !(r->rc.f&TVRF_MUFFLE))
899 tvec_notice(tv, "child process stderr: %s", p);
900}
901
902#define ERF_SILENT 0x0001u
903#define ERF_CLOSE 0x0002u
904static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
905 unsigned f)
906{
907 char *p; size_t sz;
908 ssize_t n;
909 int rc;
910
911 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
912 else r->rc.f &= ~TVRF_MUFFLE;
913 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
914 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
915 f&ERF_CLOSE ? "clear" : "set");
916 goto end;
917 }
918
919 for (;;) {
920 sz = lbuf_free(&r->errbuf, &p);
921 n = read(r->errfd, p, sz);
922 if (!n) break;
923 if (n < 0) {
924 if (errno == EINTR) continue;
925 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
926 break;
927 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
928 strerror(errno));
929 goto end;
930 }
931 lbuf_flush(&r->errbuf, p, n);
932 }
933 rc = 0;
934end:
935 if (f&ERF_CLOSE) {
936 lbuf_close(&r->errbuf);
937 close(r->errfd);
938 }
939 return (rc);
940}
941
942#define DCF_KILL 0x0100u
943static void disconnect_remote(struct tvec_state *tv,
944 struct tvec_remotectx *r, unsigned f)
945{
946 if (r->kid < 0) return;
947 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
948 close_comms(&r->rc);
949 if (r->kid > 0) kill(r->kid, SIGTERM);
950 drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
951}
952
953static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
954{
955 const struct tvec_remoteenv *re = r->re;
956 pid_t kid = 0;
957 buf b;
958 uint16 v;
959 int infd = -1, outfd = -1, errfd = -1, rc;
960
961 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
962 if (r->kid >= 0) { rc = 0; goto end; }
963 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
964 { rc = -1; goto end; }
965 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
966 lbuf_init(&r->errbuf, report_errline, r);
967 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
968
969 SENDPK(tv, &r->rc, TVPK_VER) {
970 buf_putu16l(&r->rc.bout._b, 0);
971 buf_putu16l(&r->rc.bout._b, 0);
972 } else { rc = -1; goto end; }
973
974 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
975 { rc = -1; goto end; }
976 if (buf_getu16l(&b, &v)) goto bad;
977 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
978 if (v) {
979 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
980 goto end;
981 }
982
983 SENDPK(tv, &r->rc, TVPK_BGROUP)
984 buf_putstr16l(&r->rc.bout._b, tv->test->name);
985 else { rc = -1; goto end; }
986 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
987 { rc = -1; goto end; }
988 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
989 r->ver = v; rc = 0;
990end:
991 if (rc) disconnect_remote(tv, r, DCF_KILL);
992 return (rc);
993bad:
994 rc = malformed(tv, &r->rc); goto end;
995}
996
997static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
998{
999 if (r->kid < 0)
1000 return (CONN_BROKEN);
1001 else if (r->rc.f&TVRF_BROKEN)
1002 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1003 else
1004 return (CONN_ESTABLISHED);
1005}
1006
1007static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1008{
1009 int rc;
1010
1011 switch (r->rc.f&TVRF_RCNMASK) {
1012 case TVRCN_DEMAND:
1013 rc = check_comms(tv, r);
1014 if (rc < CONN_ESTABLISHED) {
1015 close_comms(&r->rc);
1016 if (connect_remote(tv, r)) rc = CONN_FAILED;
1017 else rc = CONN_FRESH;
1018 }
1019 break;
1020 case TVRCN_FORCE:
1021 disconnect_remote(tv, r, DCF_KILL);
1022 if (connect_remote(tv, r)) rc = CONN_FAILED;
1023 else rc = CONN_FRESH;
1024 break;
1025 case TVRCN_SKIP:
1026 rc = check_comms(tv, r);
1027 break;
1028 default:
1029 abort();
1030 }
1031 return (rc);
1032}
1033
1034static void reset_vars(struct tvec_remotectx *r)
1035{
1036 r->exwant = TVXST_RUN; r->rc.f = (r->rc.f&~TVRF_RCNMASK) | TVRCN_DEMAND;
1037 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1038}
1039
1040void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1041 void *pctx, void *ctx)
1042{
1043 struct tvec_remotectx *r = ctx;
1044 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1045
1046 assert(!re->r.env || tv->test->env == &re->_env);
1047
1048 r->tv = tv;
1049 init_comms(&r->rc);
1050 r->re = re; r->kid = -1;
1051 DCREATE(&r->prgwant); DCREATE(&r->progress);
1052 if (connect_remote(tv, r))
1053 tvec_skipgroup(tv, "failed to connect to test backend");
1054 reset_vars(r);
1055}
1056
1057int tvec_remoteset(struct tvec_state *tv, const char *var,
1058 const struct tvec_env *env, void *ctx)
1059{
1060 struct tvec_remotectx *r = ctx;
1061 union tvec_regval rv;
1062 int rc;
1063
1064 if (STRCMP(var, ==, "@exit")) {
1065 if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
1066 if (r) r->exwant = rv.u;
1067 rc = 1;
1068 } else if (STRCMP(var, ==, "@progress")) {
1069 tvty_string.init(&rv, &progress_regdef);
1070 rc = tvty_string.parse(&rv, &progress_regdef, tv);
1071 if (r && !rc)
1072 { DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.str.p, rv.str.sz); }
1073 tvty_string.release(&rv, &progress_regdef);
1074 if (rc) { rc = -1; goto end; }
1075 rc = 1;
1076 } else if (STRCMP(var, ==, "@reconnect")) {
1077 if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
1078 if (r) r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK);
1079 rc = 1;
1080 } else
1081 rc = 0;
1082
1083end:
1084 return (rc);
1085}
1086
1087void tvec_remoteafter(struct tvec_state *tv, void *ctx)
1088{
1089 struct tvec_remotectx *r = ctx;
1090
1091 reset_vars(r);
1092}
1093
1094void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
1095{
1096 struct tvec_remotectx *r = ctx;
1097 union tvec_regval rv;
1098 unsigned f = 0;
1099#define f_exit 1u
1100#define f_progress 2u
1101#define f_fail 4u
1102 buf b;
1103 int rc;
1104
1105 switch (try_reconnect(tv, r)) {
1106 case CONN_FAILED:
1107 tvec_skip(tv, "failed to connect to test backend"); return;
1108 case CONN_BROKEN:
1109 tvec_skip(tv, "no connection"); return;
1110 }
1111
1112 SENDPK(tv, &r->rc, TVPK_TEST)
1113 tvec_serialize(tv->in, &r->rc.bout._b,
1114 tv->test->regs, tv->nreg, tv->regsz);
1115 else { rc = -1; goto end; }
1116 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
1117 switch (rc) {
1118 case RECV_FAIL:
1119 goto end;
1120 case RECV_EOF:
1121 reap_kid(tv, r);
1122 /* fall through */
1123 case RECV_OK:
1124 if (r->exit != r->exwant) f |= f_exit;
1125 if (r->progress.len != r->prgwant.len ||
1126 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
1127 f |= f_progress;
1128 if (f && (tv->f&TVSF_ACTIVE))
1129 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
1130 if (!(tv->f&TVSF_ACTIVE) &&
1131 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
1132 f |= f_fail;
1133
1134 rv.u = r->exit;
1135 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
1136 &rv, &exit_regdef);
1137 if (f&f_exit) {
1138 rv.u = r->exwant;
1139 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
1140 }
1141
1142 rv.str.p = r->progress.buf; rv.str.sz = r->progress.len;
1143 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
1144 &rv, &progress_regdef);
1145 if (f&f_progress) {
1146 rv.str.p = r->prgwant.buf; rv.str.sz = r->prgwant.len;
1147 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
1148 }
1149 }
1150
1151 if (rc == RECV_EOF)
1152 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
1153 break;
1154 }
1155
1156end:
1157 if (rc) {
1158 if ((tv->f&TVSF_ACTIVE) && f)
1159 tvec_skip(tv, "remote test runner communications failed");
1160 disconnect_remote(tv, r, 0);
1161 }
1162
1163#undef f_exit
1164#undef f_progress
1165#undef f_fail
1166}
1167
1168void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
1169{
1170 struct tvec_remotectx *r = ctx;
1171
1172 if (r) {
1173 disconnect_remote(tv, r, 0); release_comms(&r->rc);
1174 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
1175 }
1176}
1177
1178/*----- Connectors --------------------------------------------------------*/
1179
1180static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
1181 int *errfd_out, struct tvec_state *tv)
1182{
1183 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
1184 pid_t kid = -1;
1185 int rc;
1186
1187 if (pipe(p0) || pipe(p1) || pipe(pe) ||
1188 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1189 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1190 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
1191 tvec_error(tv, "pipe failed: %s", strerror(errno));
1192 rc = -1; goto end;
1193 }
1194
1195 fflush(0);
1196
1197 kid = fork();
1198 if (kid < 0) {
1199 tvec_error(tv, "fork failed: %s", strerror(errno));
1200 rc = -1; goto end;
1201 }
1202
1203 if (!kid) {
1204 *kid_out = 0;
1205 *infd_out = p0[0]; p0[0] = -1;
1206 *outfd_out = p1[1]; p1[1] = -1;
1207 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
1208 fprintf(stderr, "failed to establish child stderr: %s",
1209 strerror(errno));
1210 exit(127);
1211 }
1212 } else {
1213 *kid_out = kid; kid = -1;
1214 *infd_out = p1[0]; p1[0] = -1;
1215 *outfd_out = p0[1]; p0[1] = -1;
1216 *errfd_out = pe[0]; pe[0] = -1;
1217 }
1218
1219 rc = 0;
1220end:
1221 if (p0[0] >= 0) close(p0[0]);
1222 if (p0[1] >= 0) close(p0[1]);
1223 if (p1[0] >= 0) close(p1[0]);
1224 if (p1[1] >= 0) close(p1[1]);
1225 if (pe[0] >= 0) close(pe[0]);
1226 if (pe[1] >= 0) close(pe[1]);
1227 return (rc);
1228}
1229
1230int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1231 struct tvec_state *tv, const struct tvec_remoteenv *env)
1232{
1233 struct tvec_config config;
1234 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
1235 pid_t kid = -1;
1236 int infd = -1, outfd = -1, errfd = -1;
1237 int rc;
1238
1239 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1240 if (!kid) {
1241 config.tests = rf->f.tests ? rf->f.tests : tv->tests;
1242 config.nrout = tv->nrout; config.nreg = tv->nreg;
1243 config.regsz = tv->regsz;
1244 _exit(tvec_remoteserver(infd, outfd, &config));
1245 }
1246
1247 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1248 rc = 0;
1249end:
1250 return (rc);
1251}
1252
1253int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1254 struct tvec_state *tv, const struct tvec_remoteenv *env)
1255{
1256 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
1257 pid_t kid = -1;
1258 int infd = -1, outfd = -1, errfd = -1;
1259 mdup_fd v[2];
1260 int rc;
1261
1262 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1263 if (!kid) {
1264 v[0].cur = infd; v[0].want = STDIN_FILENO;
1265 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
1266 if (mdup(v, 2)) {
1267 fprintf(stderr, "failed to establish standard file descriptors: %s",
1268 strerror(errno));
1269 exit(127);
1270 }
1271 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
1272 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
1273 exit(127);
1274 }
1275
1276 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1277 rc = 0;
1278end:
1279 return (rc);
1280}
e63124bc
MW
1281
1282/*----- That's all, folks -------------------------------------------------*/