chiark / gitweb /
@@@ output doc
[mLib] / test / tvec-remote.c
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>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41
42 #include "alloc.h"
43 #include "bench.h"
44 #include "buf.h"
45 #include "compiler.h"
46 #include "fdflags.h"
47 #include "lbuf.h"
48 #include "mdup.h"
49 #include "quis.h"
50 #include "tvec.h"
51
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
57
58 #if CLANG_VERSION_P(3, 1)
59 #  pragma clang diagnostic ignored "-Wdangling-else"
60 #endif
61
62 /*----- Basic I/O ---------------------------------------------------------*/
63
64 static 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 }
69
70 static 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
76 static void release_comms(struct tvec_remotecomms *rc)
77   { close_comms(rc); dbuf_destroy(&rc->bin); dbuf_destroy(&rc->bout); }
78
79 static 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 }
84
85 static int PRINTF_LIKE(3, 4)
86   ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
87         const char *msg, ...)
88 {
89   va_list ap;
90
91   va_start(ap, msg);
92   close_comms(rc); rc->f |= TVRF_BROKEN;
93   tvec_report_v(tv, TVLEV_ERR, msg, &ap);
94   va_end(ap);
95   return (-1);
96 }
97
98 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
99                     const unsigned char *p, size_t sz)
100 {
101   void (*opipe)(int) = SIG_ERR;
102   ssize_t n;
103   int ret;
104
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     }
110   while (sz) {
111     n = write(rc->outfd, p, sz);
112     if (n > 0)
113       { p += n; sz -= n; }
114     else {
115       ret = ioerr(tv, rc, "failed to send: %s",
116                  n ? strerror(errno) : "empty write");
117       goto end;
118     }
119   }
120   ret = 0;
121 end:
122   if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
123   return (ret);
124 }
125
126 #define RCVF_ALLOWEOF 1u
127 enum {
128   RECV_FAIL = -1,
129   RECV_OK = 0,
130   RECV_EOF = 1
131 };
132 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
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) {
140     n = read(rc->infd, p, sz);
141     if (n > 0)
142       { p += n; sz -= n; ff |= f_any; }
143     else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any))
144       return (RECV_EOF);
145     else
146       return (ioerr(tv, rc, "failed to receive: %s",
147                     n ? strerror(errno) : "unexpected end-of-file"));
148   }
149   return (RECV_OK);
150
151 #undef f_any
152 }
153
154 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
155 {
156   kludge64 k; unsigned char lenbuf[8];
157   const unsigned char *p; size_t sz;
158
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"));
162
163   p = BBASE(&rc->bout._b); sz = BLEN(&rc->bout._b);
164   ASSIGN64(k, sz); STORE64_L_(lenbuf, k);
165   if (send_all(tv, rc, lenbuf, sizeof(lenbuf))) return (-1);
166   if (send_all(tv, rc, p, sz)) return (-1);
167
168   return (0);
169 }
170
171 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
172                        unsigned f, buf *b_out)
173 {
174   kludge64 k, szmax; unsigned char lenbuf[8];
175   unsigned char *p;
176   size_t sz;
177   int ret;
178
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);
183   LOAD64_L_(k, lenbuf);
184   if (CMP64(k, >, szmax))
185     return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
186                   (unsigned long)HI64(k), (unsigned long)LO64(k)));
187
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);
192 }
193
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); })                \
202
203 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
204   { return (ioerr(tv, rc, "received malformed packet")); }
205
206 /*----- Packet types ------------------------------------------------------*/
207
208 #define TVPF_ACK        0x0001u
209
210 #define TVPK_VER        0x0000u         /* --> min, max: u16 */
211                                         /* <-- ver: u16 */
212
213 #define TVPK_REPORT     0x0100u         /* <-- level: u16; msg: string */
214 #define TVPK_PROGRESS   0x0102u         /* <-- st: str16 */
215
216 #define TVPK_BGROUP     0x0200u         /* --> name: str16
217                                          * <-- --- */
218 #define TVPK_TEST       0x0202u         /* --> in: regs
219                                          * <-- --- */
220 #define TVPK_EGROUP     0x0204u         /* --> --- */
221
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 */
230
231 /*----- Server ------------------------------------------------------------*/
232
233 static const struct tvec_outops remote_ops;
234
235 static struct tvec_state srvtv;
236 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT;
237 static struct tvec_output srvout = { &remote_ops };
238
239 int 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);
245 }
246
247 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
248 {
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;
270   }
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; }
274
275   tvec_setprogress("%IDLE");
276
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;
282
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
369 end:
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
376 bad:
377   rc = malformed(&srvtv, &srvrc); goto end;
378
379 #undef f_regslive
380 }
381
382 /*----- Server output driver ----------------------------------------------*/
383
384 static void remote_skipgroup(struct tvec_output *o,
385                              const char *excuse, va_list *ap)
386 {
387   SENDPK(&srvtv, &srvrc, TVPK_SKIPGRP)
388     buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
389 }
390
391 static void remote_skip(struct tvec_output *o,
392                         const char *excuse, va_list *ap)
393 {
394   SENDPK(&srvtv, &srvrc, TVPK_SKIP)
395     buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
396 }
397
398 static void remote_fail(struct tvec_output *o,
399                         const char *detail, va_list *ap)
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 }
409
410 static void remote_dumpreg(struct tvec_output *o,
411                            unsigned disp, const union tvec_regval *rv,
412                            const struct tvec_regdef *rd)
413 {
414   const struct tvec_regdef *reg;
415   unsigned r;
416
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
422 found:
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     }
432   }
433 }
434
435 static void remote_bbench(struct tvec_output *o,
436                           const char *ident, unsigned unit)
437 {
438   SENDPK(&srvtv, &srvrc, TVPK_BBENCH) {
439     buf_putstr32l(&srvrc.bout._b, ident);
440     buf_putu16l(&srvrc.bout._b, unit);
441   }
442 }
443
444 static void remote_ebench(struct tvec_output *o,
445                           const char *ident, unsigned unit,
446                           const struct bench_timing *t)
447 {
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
462 static void remote_report(struct tvec_output *o, unsigned level,
463                           const char *msg, va_list *ap)
464 {
465   const char *what;
466
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);
479   }
480 }
481
482 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
483   { ; }
484 static int remote_esession(struct tvec_output *o)
485   { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
486 static void remote_destroy(struct tvec_output *o)
487   { ; }
488 static void remote_etest(struct tvec_output *o, unsigned outcome)
489   { ; }
490
491 static void remote_bgroup(struct tvec_output *o)
492   { assert(!"remote_bgroup"); }
493 static void remote_egroup(struct tvec_output *o)
494   { assert(!"remote_egroup"); }
495 static void remote_btest(struct tvec_output *o)
496   { assert(!"remote_btest"); }
497
498 static const struct tvec_outops remote_ops = {
499   remote_bsession, remote_esession,
500   remote_bgroup, remote_skipgroup, remote_egroup,
501   remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
502   remote_bbench, remote_ebench,
503   remote_report,
504   remote_destroy
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
521 static 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
685 static const struct tvec_flaginfo exit_flaginfo =
686   { "exit-status", exit_flags, &tvrange_uint };
687 static const struct tvec_regdef exit_regdef =
688   { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
689
690 static const struct tvec_regdef progress_regdef =
691   { "@progress", 0, &tvty_string, 0 };
692
693 static const struct tvec_uassoc reconn_assocs[] = {
694   { "on-demand",        TVRCN_DEMAND },
695   { "force",            TVRCN_FORCE },
696   { "skip",             TVRCN_SKIP },
697   TVEC_ENDENUM
698 };
699
700 enum {
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
707 static const struct tvec_uenuminfo reconn_enuminfo =
708   { "remote-reconnection", reconn_assocs, &tvrange_uint };
709 static const struct tvec_regdef reconn_regdef =
710   { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
711
712 static 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;
781
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;
789
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         }
803
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
852 end:
853   DDESTROY(&d);
854   xfree(reg);
855   return (rc);
856 bad:
857   rc = malformed(tv, &r->rc); goto end;
858 }
859
860 static 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
893 static 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
904 static 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;
934 end:
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
943 static 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
953 static 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;
990 end:
991   if (rc) disconnect_remote(tv, r, DCF_KILL);
992   return (rc);
993 bad:
994   rc = malformed(tv, &r->rc); goto end;
995 }
996
997 static 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
1007 static 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
1034 static 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
1040 void 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
1057 int 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
1083 end:
1084   return (rc);
1085 }
1086
1087 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
1088 {
1089   struct tvec_remotectx *r = ctx;
1090
1091   reset_vars(r);
1092 }
1093
1094 void 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
1156 end:
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
1168 void 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
1180 static 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;
1220 end:
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
1230 int 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;
1249 end:
1250   return (rc);
1251 }
1252
1253 int 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;
1278 end:
1279   return (rc);
1280 }
1281
1282 /*----- That's all, folks -------------------------------------------------*/