chiark / gitweb /
@@@ so much mess
[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 <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40
41 #include "alloc.h"
42 #include "buf.h"
43 #include "tvec.h"
44
45 /*----- Data structures ---------------------------------------------------*/
46
47 struct tvec_remote {
48   int infd, outfd;
49   dbuf bin, bout;
50   unsigned f;
51 #define TVRF_BROKEN 1u
52 };
53
54 struct tvec_remotectx {
55   struct tvec_remote r;
56   pid_t kid;
57 };
58
59 struct remote_output {
60   struct tvec_output _o;
61   struct tvec_remote r;
62 };
63
64 /*----- Basic I/O ---------------------------------------------------------*/
65
66 static int PRINTF_LIKE(3, 4)
67   ioerr(struct tvec_state *tv, struct tvec_remote *r, const char *msg, ...)
68 {
69   va_list ap;
70
71   va_start(ap, msg);
72   r->f |= TVRF_BROKEN;
73   tvec_write(tv, msg, &ap);
74   va_end(ap);
75   return (-1);
76 }
77
78 static int send_all(struct tvec_state *tv, struct tvec_remote *r,
79                     const unsigned char *p, size_t sz)
80 {
81   ssize_t n;
82
83   while (sz) {
84     n = write(r->outfd, p, sz);
85     if (n > 0)
86       { p += n; sz -= n; }
87     else
88       return (ioerr(tv, r, "failed to send: %s",
89                     n ? strerror(errno) : "empty write"));
90   }
91   return (0);
92 }
93
94 #define RCVF_ALLOWEOF 1u
95 static int recv_all(struct tvec_state *tv, struct tvec_remote *r,
96                     unsigned char *p, size_t sz, unsigned f)
97 {
98   ssize_t n;
99   unsigned ff = 0;
100 #define f_any 1u
101
102   while (sz) {
103     n = read(r->infd, p, sz);
104     if (n > 0)
105       { p += n; sz -= n; ff |= f_any; }
106     else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any))
107       return (1);
108     else
109       return (ioerr(tv, r, "failed to receive: %s",
110                     n ? strerror(errno) : "unexpected end-of-file"));
111   }
112   return (0);
113
114 #undef f_any
115 }
116
117 int tvec_send(struct tvec_state *tv, struct tvec_reomte *r)
118 {
119   kludge64 k; unsigned char lenbuf[8];
120   const char *p; size_t sz;
121
122   if (r->f&TVRF_BROKEN) return (-1);
123   if (BBAD(&r->bout.b))
124     return (ioerr(tv, r, "failed to build output packet buffer");
125
126   p = BBASE(r->bout.b); sz = BLEN(&r->bout.b);
127   ASSIGN64(k, sz); STORE64_L_(lenbuf, k);
128   if (send_all(tv, r, lenbuf, sizeof(lenbuf))) return (-1);
129   if (send_all(tv, r, p, sz)) return (-1);
130
131   return (0);
132 }
133
134 int tvec_recv(struct tvec_state *tv, struct tvec_reomte *r, buf *b_out)
135 {
136   kludge64 k, szmax; unsigned char lenbuf[8];
137   unsigned char *p;
138   size_t sz;
139   int rc;
140
141   if (r->f&TVRF_BROKEN) return (-1);
142   ASSIGN64(k, (size_t)-1);
143   rc = recv_all(tv, r, lenbuf, sizeof(lenbuf), RCVF_ALLOWEOF);
144     if (rc) return (rc);
145   LOAD64_L_(k, lenbuf);
146   if (CMP64(k, >, szmax))
147     return (ioerr(tv, r, "packet size 0x%08lx%08lx out of range",
148                   (unsigned long)HI64(k), (unsigned long)LO64(k)));
149
150   sz = GET64(size_t, k); buf_reset(&r->bin); p = buf_get(&r->bin.b, sz);
151     if (!p) return (ioerr(tv, r, "failed to allocate receive buffer"));
152   if (recv_all(tv, r, p, sz, 0)) return (-1);
153   buf_init(b_out, p, sz); return (0);
154 }
155
156 /*----- Data formatting primitives ----------------------------------------*/
157
158
159 /*----- Packet types ------------------------------------------------------*/
160
161 #define TVPK_ERROR      0x0001          /* msg: string */
162 #define TVPK_NOTICE     0x0002          /* msg: string */
163 #define TVPK_STATUS     0x0003          /* st: char */
164
165 #define TVPK_BGROUP     0x0101          /* name: string */
166 #define TVPK_TEST       0x0102          /* in: regs */
167 #define TVPK_EGROUP     0x0103          /* --- */
168
169 #define TVPK_SKIPGRP    0x0201          /* excuse: string */
170 #define TVPK_SKIP       0x0202          /* excuse: string */
171 #define TVPK_FAIL       0x0203          /* detail: string */
172 #define TVPK_MISMATCH   0x0204          /* in, out: regs */
173 #define TVPK_BBENCH     0x0205          /* in: regs */
174 #define TVPK_EBENCH     0x0206         /* flags: u16; n: u64; t, cy: float */
175
176 /*----- The output driver -------------------------------------------------*/
177
178 #define SENDPK(ro, pk)                                                  \
179         MC_BEFORE(setpk,                                                \
180           { buf_reset(&(ro)->r.bout);                                   \
181             buf_putu16l(&(ro)->r.bout.b, (pk)); })                      \
182         MC_AFTER(send,                                                  \
183           { tvec_send(&ro->_o.tv, &ro->r); })
184
185 static int sendstr(struct tvec_output *o, unsigned pk,
186                    const char *p, va_list *ap)
187 {
188   struct remote_output *ro = (struct remote_output *)o;
189
190   if (ro->r.f&TVRF_BROKEN) return (-1);
191   dbuf_reset(&ro->r.bout);
192   buf_putu16l(&ro->r.bout.b, TVPK_ERROR);
193   buf_vputstrf16l(&ro->r.bout.b, msg, ap);
194   return (tvec_send(ro->_o.tv, &ro->r));
195 }
196
197 static void report(struct tvec_output *o, unsigned pk,
198                    const char *msg, va_list *ap)
199 {
200   if (sendstr(o, pk, msg, ap)) {
201     fprintf(stderr, "%s: ", QUIS);
202     vfprintf(stderr, msg, *ap);
203     fputc('\n', stderr);
204   }
205 }
206
207 static void remote_error(struct tvec_output *o, const char *msg, va_list *ap)
208   { report(o, TVPK_ERROR, msg, ap); }
209
210 static void remote_notice(struct tvec_output *o,
211                           const char *msg, va_list *ap)
212   { report(o, TVPK_NOTICE, msg, ap); }
213
214 static void remote_setstatus(struct tvec_ouptut *o, int st)
215 {
216   struct remote_output *ro = (struct remote_output *)o;
217   SENDPK(ro, TVPK_STATUS) buf_putbyte(&ro->r.bout.b, st);
218 }
219
220 static void remote_skipgroup(struct tvec_output *o,
221                              const char *excuse, va_list *ap)
222   { sendstr(o, TVPK_SKIPGRP, excuse, ap); }
223
224 static void remote_skip(struct tvec_output *o,
225                         const char *excuse, va_list *ap)
226   { sendstr(o, TVPK_SKIP, excuse, ap); }
227
228 static void remote_fail(struct tvec_output *o,
229                         const char *detail, va_list *ap)
230   { sendstr(o, TVPK_FAIL, detail, ap); }
231
232 static void remote_mismatch(struct tvec_output *o)
233 {
234   struct remote_output *ro = (struct remote_output *)o;
235   struct tvec_state *rv = ro->_o.tv;
236
237   SENDPK(ro, TVPK_MISMATCH) {
238     tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz);
239     tvec_serialize(tv, &ro->r.bout.b, tv->out, tv->nrout, tv->regsz);
240   }
241 }
242
243 static void remote_bbench(struct tvec_output *o)
244 {
245   struct remote_output *ro = (struct remote_output *)o;
246   struct tvec_state *rv = ro->_o.tv;
247
248   SENDPK(ro, TVPK_BBENCH)
249     tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz);
250 }
251
252 static void remote_ebench(struct tvec_output *o,
253                           const struct bench_timing *t)
254 {
255   struct remote_output *ro = (struct remote_output *)o;
256   kludge64 k;
257
258   SENDPK(ro, TVPK_EBENCH) {
259     buf_putu16l(&ro->r.bout.b, t->f);
260     ASSIGN64(k, t->n); buf_putk64l(&ro->r.bout.b, k);
261     if (t->f&BTF_TIMEOK) buf_putf64l(&ro->r.bout.b, t->t);
262     if (t->f&BTF_CYOK) buf_putf64l(&ro->r.bout.b, t->cy);
263   }
264 }
265
266 static void remote_write(struct tvec_output *o, const char *p, size_t sz)
267   { assert(!"remote_write"); }
268 static void remote_bsession(struct tvec_output *o)
269   { assert(!"remote_bsession"); }
270 static int remote_esession(struct tvec_output *o)
271   { assert(!"remote_esession"); return (-1); }
272 static void remote_bgroup(struct tvec_output *o)
273   { assert(!"remote_bgroup"); }
274 static void remote_btest(struct tvec_output *o)
275   { assert(!"remote_btest"); }
276 static void remote_egroup(struct tvec_output *o, unsigned outcome)
277   { assert(!"remote_egroup"); }
278 static void remote_etest(struct tvec_output *o, unsigned outcome)
279   { assert(!"remote_etest"); }
280
281 static void remote_destroy(struct tvec_output *o)
282 {
283 }
284
285 static const struct tvec_outops remote_ops = {
286   remote_error, remote_notice, remote_setstatus, remote_write,
287   remote_bsession, remote_esession,
288   remote_bgroup, remote_egroup, remote_skip,
289   remote_btest, remote_skip, remote_fail, remote_mismatch, remote_etest,
290   remote_bbench, remote_ebench,
291   remote_destroy
292
293 /*----- Main code ---------------------------------------------------------*/
294
295
296
297 /*----- That's all, folks -------------------------------------------------*/