@@@ 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 -------------------------------------------------*/