varnish-cache/bin/varnishtest/vtc_process.c
0
/*-
1
 * Copyright (c) 2015 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Dridi Boukelmoune <dridi@varnish-software.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * XXX:
30
 *      -ignore-stderr (otherwise output to stderr is fail)
31
 */
32
33
#include "config.h"
34
35
#include <sys/ioctl.h>          // Linux: struct winsize
36
37
#include <ctype.h>
38
#include <fcntl.h>
39
#include <inttypes.h>
40
#include <poll.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#ifdef __sun
45
#  include <stropts.h>
46
#endif
47
#include <termios.h>
48
#include <unistd.h>
49
50
#include "vtc.h"
51
52
#include "vre.h"
53
#include "vev.h"
54
#include "vlu.h"
55
#include "vsb.h"
56
#include "vsub.h"
57
58
#include "teken.h"
59
60
struct process {
61
        unsigned                magic;
62
#define PROCESS_MAGIC           0x1617b43e
63
        char                    *name;
64
        struct vtclog           *vl;
65
        VTAILQ_ENTRY(process)   list;
66
67
        char                    *spec;
68
        char                    *dir;
69
        char                    *out;
70
        char                    *err;
71
        int                     fd_term;
72
        int                     fd_stderr;
73
        int                     f_stdout;
74
        int                     f_stderr;
75
        struct vlu              *vlu_stdout;
76
        struct vlu              *vlu_stderr;
77
        int                     log;
78
        pid_t                   pid;
79
        int                     expect_exit;
80
        int                     expect_signal;
81
        int                     allow_core;
82
83
        uintmax_t               stdout_bytes;
84
        uintmax_t               stderr_bytes;
85
86
        pthread_mutex_t         mtx;
87
        pthread_t               tp;
88
        unsigned                hasthread;
89
90
        int                     nlin;
91
        int                     ncol;
92
        int                     ansi_response;
93
        char                    **vram;
94
        teken_t                 tek[1];
95
};
96
97
static VTAILQ_HEAD(, process)   processes =
98
    VTAILQ_HEAD_INITIALIZER(processes);
99
100
static void term_resize(struct process *pp, int lin, int col);
101
102
/**********************************************************************
103
 * Terminal emulation
104
 */
105
106
static void
107 5484598
term_cursor(void *priv, const teken_pos_t *pos)
108
{
109 5484598
        (void)priv;
110 5484598
        (void)pos;
111 5484598
}
112
113
static void
114 34921461
term_putchar(void *priv, const teken_pos_t *pos, teken_char_t ch,
115
    const teken_attr_t *at)
116
{
117
        struct process *pp;
118
119 34921461
        CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
120 34921420
        (void)at;
121 34921420
        if (ch > 126 || ch < 32)
122 22480
                ch = '?';
123 34921461
        assert(pos->tp_row < pp->nlin);
124 34921465
        assert(pos->tp_col < pp->ncol);
125 34921533
        pp->vram[pos->tp_row][pos->tp_col] = ch;
126 34921533
}
127
128
static void
129 71463
term_fill(void *priv, const teken_rect_t *r, teken_char_t c,
130
    const teken_attr_t *a)
131
{
132
        teken_pos_t p;
133
134
        /* Braindead implementation of fill() - just call putchar(). */
135 398434
        for (p.tp_row = r->tr_begin.tp_row;
136 398434
            p.tp_row < r->tr_end.tp_row; p.tp_row++)
137 30287394
                for (p.tp_col = r->tr_begin.tp_col;
138 30287394
                    p.tp_col < r->tr_end.tp_col; p.tp_col++)
139 30287394
                        term_putchar(priv, &p, c, a);
140 71463
}
141
142
static void
143 69424
term_copy(void *priv, const teken_rect_t *r, const teken_pos_t *p)
144
{
145
        struct process *pp;
146
        int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
147
148
        /*
149
         * Copying is a little tricky. We must make sure we do it in
150
         * correct order, to make sure we don't overwrite our own data.
151
         */
152 69424
        CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
153
154 69424
        nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
155 69424
        ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
156
157 69424
        if (p->tp_row < r->tr_begin.tp_row) {
158
                /* Copy from top to bottom. */
159 970808
                for (y = 0; y < nrow; y++)
160 1887488
                        memmove(&pp->vram[p->tp_row + y][p->tp_col],
161 943744
                            &pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol);
162 27064
        } else {
163
                /* Copy from bottom to top. */
164 236600
                for (y = nrow - 1; y >= 0; y--)
165 388480
                        memmove(&pp->vram[p->tp_row + y][p->tp_col],
166 194240
                            &pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol);
167
        }
168 69424
}
169
170
static void
171 640
term_respond(void *priv, const void *p, size_t l)
172
{
173
        struct process *pp;
174
        int r;
175
176 640
        CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
177
178 640
        vtc_dump(pp->vl, 4, "term_response", p, l);
179 640
        if (pp->ansi_response) {
180 80
                r = write(pp->fd_term, p, l);
181 80
                if (r != l)
182 0
                        vtc_fatal(pp->vl, "Could not write to process: %s",
183 0
                            strerror(errno));
184 80
        }
185 640
}
186
187
static void
188 5400
term_param(void *priv, int p, unsigned int v)
189
{
190
        struct process *pp;
191
192 5400
        CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
193 5400
        if (p == TP_132COLS && v)
194 480
                term_resize(pp, pp->nlin, 132);
195 1440
        if (p == TP_132COLS && !v)
196 960
                term_resize(pp, pp->nlin, 80);
197 5400
}
198
199
static const teken_funcs_t process_teken_func = {
200
        .tf_cursor      =       term_cursor,
201
        .tf_putchar     =       term_putchar,
202
        .tf_fill        =       term_fill,
203
        .tf_copy        =       term_copy,
204
        .tf_respond     =       term_respond,
205
        .tf_param       =       term_param,
206
};
207
208
static void
209 6240
term_screen_dump(const struct process *pp)
210
{
211
        int i;
212
        const teken_pos_t *pos;
213
214 163040
        for (i = 0; i < pp->nlin; i++)
215 156800
                vtc_dump(pp->vl, 3, "screen", pp->vram[i], pp->ncol);
216 6240
        pos = teken_get_cursor(pp->tek);
217 12480
        vtc_log(pp->vl, 3, "Cursor at line %d column %d",
218 6240
            pos->tp_row + 1, pos->tp_col + 1);
219 6240
}
220
221
static void
222 4000
term_resize(struct process *pp, int lin, int col)
223
{
224
        teken_pos_t pos;
225
        char **vram;
226
        int i, j;
227
228 4000
        vram = calloc(lin, sizeof *pp->vram);
229 4000
        AN(vram);
230 103840
        for (i = 0; i < lin; i++) {
231 99840
                vram[i] = calloc(col + 1L, 1);
232 99840
                AN(vram[i]);
233 99840
                memset(vram[i], ' ', col);
234 99840
                vram[i][col] = '\0';
235 99840
        }
236 4000
        if (pp->vram != NULL) {
237 43960
                for (i = 0; i < lin; i++) {
238 42480
                        if (i >= pp->nlin)
239 280
                                break;
240 42200
                        j = col;
241 42200
                        if (j > pp->ncol)
242 15360
                                j = pp->ncol;
243 42200
                        memcpy(vram[i], pp->vram[i], j);
244 42200
                }
245 44000
                for (i = 0; i < pp->nlin; i++)
246 42240
                        free(pp->vram[i]);
247 1760
                free(pp->vram);
248 1760
        }
249 4000
        pp->vram = vram;
250 4000
        pp->nlin = lin;
251 4000
        pp->ncol = col;
252
253 4000
        pos.tp_row = lin;
254 4000
        pos.tp_col = col;
255 4000
        teken_set_winsize(pp->tek, &pos);
256 4000
}
257
258
static int
259 196631
term_find_textline(const struct process *pp, int *x, int y, const char *pat)
260
{
261
        const char *t;
262
        int l;
263
264 196631
        if (*x == 0) {
265 155234
                t = strstr(pp->vram[y], pat);
266 155234
                if (t != NULL) {
267 2760
                        *x = 1 + (t - pp->vram[y]);
268 2760
                        return (1);
269
                }
270 193871
        } else if (*x <= pp->ncol) {
271 41397
                t = pp->vram[y] + *x - 1;
272 41397
                l = strlen(pat);
273 41397
                assert((*x - 1) + (l - 1) < pp->ncol);
274 41397
                if (!memcmp(t, pat, l))
275 7280
                        return (1);
276 34117
        }
277 186591
        return (0);
278 196631
}
279
280
static int
281 20411
term_find_text(const struct process *pp, int *x, int *y, const char *pat)
282
{
283
        int yy;
284
285 20411
        if (*y == 0) {
286 188604
                for (yy = 0; yy < pp->nlin; yy++) {
287 183932
                        if (term_find_textline(pp, x, yy, pat)) {
288 3040
                                *y = yy + 1;
289 3040
                                return (1);
290
                        }
291 180892
                }
292 17371
        } else if (*y <= pp->nlin) {
293 12699
                if (term_find_textline(pp, x, *y - 1, pat))
294 7000
                        return (1);
295 5699
        }
296 10371
        return (0);
297 20411
}
298
299
static void
300 10040
term_expect_text(struct process *pp,
301
    const char *lin, const char *col, const char *pat)
302
{
303 10040
        int x, y, l, d = 10000;
304
        char *t;
305
306 10040
        y = strtoul(lin, NULL, 0);
307 10040
        if (y < 0 || y > pp->nlin)
308 0
                vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin);
309 10040
        x = strtoul(col, NULL, 0);
310 10200
        for(l = 0; l <= 10 && x > pp->ncol; l++)        // wait for screen change
311 160
                usleep(100000);
312 10040
        if (x < 0 || x > pp->ncol)
313 0
                vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol);
314 10040
        l = strlen(pat);
315 10040
        if (x + l - 1 > pp->ncol)
316 0
                vtc_fatal(pp->vl, "XXX %d ncol %d", x + l - 1, pp->ncol);
317 10040
        PTOK(pthread_mutex_lock(&pp->mtx));
318 20411
        while (!term_find_text(pp, &x, &y, pat)) {
319 10371
                if (x != 0 && y != 0) {
320 4957
                        t = pp->vram[y - 1] + x - 1;
321 9914
                        vtc_log(pp->vl, 4,
322 4957
                            "text at %d,%d: '%.*s'", y, x, l, t);
323 4957
                }
324 10371
                PTOK(pthread_mutex_unlock(&pp->mtx));
325 10371
                usleep(d);
326 10371
                PTOK(pthread_mutex_lock(&pp->mtx));
327 10371
                if (d < 3000000)
328 10371
                        d += d;
329
        }
330 10040
        PTOK(pthread_mutex_unlock(&pp->mtx));
331 10040
        vtc_log(pp->vl, 4, "found expected text at %d,%d: '%s'", y, x, pat);
332 10040
}
333
334
static void
335 320
term_expect_cursor(const struct process *pp, const char *lin, const char *col)
336
{
337
        int x, y, l;
338
        const teken_pos_t *pos;
339
340 320
        pos = teken_get_cursor(pp->tek);
341 320
        y = strtoul(lin, NULL, 0);
342 320
        if (y < 0 || y > pp->nlin)
343 0
                vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin);
344 320
        x = strtoul(col, NULL, 0);
345 320
        for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change
346 0
                usleep(100000);
347 320
        if (x < 0 || x > pp->ncol)
348 0
                vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol);
349 320
        if (y != 0 && (y-1) != pos->tp_row)
350 0
                vtc_fatal(pp->vl, "Cursor on line %d (expected %d)",
351 0
                    pos->tp_row + 1, y);
352 240
        if (x != 0 && (x-1) != pos->tp_col)
353 0
                vtc_fatal(pp->vl, "Cursor in column %d (expected %d)",
354 0
                    pos->tp_col + 1, y);
355 320
}
356
357
static void
358 1360
term_match_text(struct process *pp,
359
    const char *lin, const char *col, const char *re)
360
{
361
        int i, l, err, erroff;
362
        struct vsb *vsb, re_vsb[1];
363
        size_t len;
364
        ssize_t x, y;
365
        vre_t *vre;
366
        char errbuf[VRE_ERROR_LEN];
367
368 1360
        vsb = VSB_new_auto();
369 1360
        AN(vsb);
370
371 1360
        y = strtoul(lin, NULL, 0);
372 1360
        if (y < 0 || y > pp->nlin)
373 0
                vtc_fatal(pp->vl, "YYY %zd nlin %d", y, pp->nlin);
374 1360
        x = strtoul(col, NULL, 0);
375 1360
        for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change
376 0
                usleep(100000);
377 1360
        if (x < 0 || x > pp->ncol)
378 0
                vtc_fatal(pp->vl, "XXX %zd ncol %d", x, pp->ncol);
379
380 1360
        if (x)
381 640
                x--;
382
383 720
        if (y)
384 720
                y--;
385
386 1360
        vre = VRE_compile(re, 0, &err, &erroff, 1);
387 1360
        if (vre == NULL) {
388 0
                AN(VSB_init(re_vsb, errbuf, sizeof errbuf));
389 0
                AZ(VRE_error(re_vsb, err));
390 0
                AZ(VSB_finish(re_vsb));
391 0
                VSB_fini(re_vsb);
392 0
                vtc_fatal(pp->vl, "invalid regexp \"%s\" at %d (%s)",
393 0
                    re, erroff, errbuf);
394
        }
395
396 1360
        PTOK(pthread_mutex_lock(&pp->mtx));
397
398 1360
        len = (pp->nlin - y) * (pp->ncol - x);
399 32960
        for (i = y; i < pp->nlin; i++) {
400 31600
                VSB_bcat(vsb, &pp->vram[i][x], pp->ncol - x);
401 31600
                VSB_putc(vsb, '\n');
402 31600
        }
403
404 1360
        AZ(VSB_finish(vsb));
405
406 1360
        if (VRE_match(vre, VSB_data(vsb), len, 0, NULL) < 1)
407 0
                vtc_fatal(pp->vl, "match failed: (\"%s\")", re);
408
        else
409 1360
                vtc_log(pp->vl, 4, "match succeeded");
410
411 1360
        PTOK(pthread_mutex_unlock(&pp->mtx));
412 1360
        VSB_destroy(&vsb);
413 1360
        VRE_free(&vre);
414 1360
}
415
416
/**********************************************************************
417
 * Allocate and initialize a process
418
 */
419
420
#define PROCESS_EXPAND(field, format, ...)                              \
421
        do {                                                            \
422
                vsb = macro_expandf(p->vl, format, __VA_ARGS__);        \
423
                AN(vsb);                                                \
424
                p->field = strdup(VSB_data(vsb));                       \
425
                AN(p->field);                                           \
426
                VSB_destroy(&vsb);                                      \
427
        } while (0)
428
429
static void
430 2240
process_coverage(struct process *p)
431
{
432
        const teken_attr_t *a;
433
        teken_pos_t pos;
434
        int fg, bg;
435
436
        // Code-Coverage of Teken
437
438 2240
        (void)teken_get_sequence(p->tek, TKEY_UP);
439 2240
        (void)teken_get_sequence(p->tek, TKEY_F1);
440 2240
        (void)teken_256to8(0);
441 2240
        (void)teken_256to16(0);
442 2240
        a = teken_get_defattr(p->tek);
443 2240
        teken_set_defattr(p->tek, a);
444 2240
        a = teken_get_curattr(p->tek);
445 2240
        teken_set_curattr(p->tek, a);
446 2240
        (void)teken_get_winsize(p->tek);
447 2240
        pos.tp_row = 0;
448 2240
        pos.tp_col = 8;
449 2240
        teken_set_cursor(p->tek, &pos);
450 2240
        teken_get_defattr_cons25(p->tek, &fg, &bg);
451 2240
}
452
453
static struct process *
454 2240
process_new(const char *name)
455
{
456
        struct process *p;
457
        struct vsb *vsb;
458
        char buf[1024];
459
460 2240
        ALLOC_OBJ(p, PROCESS_MAGIC);
461 2240
        AN(p);
462 2240
        REPLACE(p->name, name);
463 2240
        PTOK(pthread_mutex_init(&p->mtx, NULL));
464
465 2240
        p->vl = vtc_logopen("%s", name);
466 2240
        AN(p->vl);
467
468 2240
        PROCESS_EXPAND(dir, "${tmpdir}/%s", name);
469 2240
        PROCESS_EXPAND(out, "${tmpdir}/%s/term", name);
470 2240
        PROCESS_EXPAND(err, "${tmpdir}/%s/stderr", name);
471
472 2240
        bprintf(buf, "rm -rf %s ; mkdir -p %s ; touch %s %s",
473
            p->dir, p->dir, p->out, p->err);
474 2240
        AZ(system(buf));
475
476 2240
        p->fd_term = -1;
477
478 2240
        VTAILQ_INSERT_TAIL(&processes, p, list);
479 2240
        teken_init(p->tek, &process_teken_func, p);
480 2240
        term_resize(p, 24, 80);
481 2240
        process_coverage(p);
482 2240
        return (p);
483
}
484
485
#undef PROCESS_EXPAND
486
487
/**********************************************************************
488
 * Clean up process
489
 */
490
491
static void
492 2240
process_delete(struct process *p)
493
{
494
        int i;
495
496 2240
        CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC);
497 2240
        PTOK(pthread_mutex_destroy(&p->mtx));
498 2240
        vtc_logclose(p->vl);
499 2240
        free(p->name);
500 2240
        free(p->dir);
501 2240
        free(p->out);
502 2240
        free(p->err);
503
504 59840
        for (i = 0; i < p->nlin; i++)
505 57600
                free(p->vram[i]);
506 2240
        free(p->vram);
507
508
        /*
509
         * We do not delete the directory, it may contain useful stdout
510
         * and stderr files. They will be deleted on account of belonging
511
         * to the test's tmpdir.
512
         */
513
514
        /* XXX: MEMLEAK (?) */
515 2240
        FREE_OBJ(p);
516 2240
}
517
518
static void
519 2240
process_undef(const struct process *p)
520
{
521 2240
        CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC);
522
523 2240
        macro_undef(p->vl, p->name, "dir");
524 2240
        macro_undef(p->vl, p->name, "out");
525 2240
        macro_undef(p->vl, p->name, "err");
526 2240
}
527
528
/**********************************************************************
529
 * Data stream handling
530
 */
531
532
static int
533 7240
process_vlu_func(void *priv, const char *l)
534
{
535
        struct process *p;
536
537 7240
        CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC);
538 7240
        vtc_dump(p->vl, 4, "output", l, -1);
539 7240
        return (0);
540
}
541
542
static int v_matchproto_(vev_cb_f)
543 173385
process_stdout(const struct vev *ev, int what)
544
{
545
        struct process *p;
546
        char buf[BUFSIZ];
547
        int i;
548
549 173385
        CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC);
550 173385
        (void)what;
551 173385
        i = read(p->fd_term, buf, sizeof buf);
552 173385
        if (i <= 0) {
553 2720
                vtc_log(p->vl, 4, "stdout read %d", i);
554 2720
                return (1);
555
        }
556 170666
        PTOK(pthread_mutex_lock(&p->mtx));
557 170663
        p->stdout_bytes += i;
558 170663
        PTOK(pthread_mutex_unlock(&p->mtx));
559 170664
        if (p->log == 1)
560 2124
                (void)VLU_Feed(p->vlu_stdout, buf, i);
561 168540
        else if (p->log == 2)
562 8238
                vtc_dump(p->vl, 4, "stdout", buf, i);
563 160302
        else if (p->log == 3)
564 1942
                vtc_hexdump(p->vl, 4, "stdout", buf, i);
565 170666
        assert(write(p->f_stdout, buf, i) == i);
566 170664
        PTOK(pthread_mutex_lock(&p->mtx));
567 170665
        teken_input(p->tek, buf, i);
568 170665
        PTOK(pthread_mutex_unlock(&p->mtx));
569 170666
        return (0);
570 173386
}
571
572
static int v_matchproto_(vev_cb_f)
573 3247
process_stderr(const struct vev *ev, int what)
574
{
575
        struct process *p;
576
        char buf[BUFSIZ];
577
        int i;
578
579 3247
        CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC);
580 3247
        (void)what;
581 3247
        i = read(p->fd_stderr, buf, sizeof buf);
582 3247
        if (i <= 0) {
583 2720
                vtc_log(p->vl, 4, "stderr read %d", i);
584 2720
                return (1);
585
        }
586 527
        PTOK(pthread_mutex_lock(&p->mtx));
587 527
        p->stderr_bytes += i;
588 527
        PTOK(pthread_mutex_unlock(&p->mtx));
589 527
        vtc_dump(p->vl, 4, "stderr", buf, i);
590 527
        assert(write(p->f_stderr, buf, i) == i);
591 527
        return (0);
592 3247
}
593
594
static void
595 0
process_cleanup(void *priv)
596
{
597 0
        struct vev_root *evb = priv;
598 0
        VEV_Destroy(&evb);
599 0
}
600
601
static void *
602 2720
process_thread(void *priv)
603
{
604
        struct process *p;
605
        struct vev_root *evb;
606
        struct vev *ev;
607
        int r;
608
609 2720
        CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC);
610
611 2720
        p->f_stdout = open(p->out, O_WRONLY|O_APPEND);
612 2720
        assert(p->f_stdout >= 0);
613 2720
        p->f_stderr = open(p->err, O_WRONLY|O_APPEND);
614 2720
        assert(p->f_stderr >= 0);
615
616 2720
        evb = VEV_New();
617 2720
        AN(evb);
618 2720
        pthread_cleanup_push(process_cleanup, evb);
619
620 2720
        ev = VEV_Alloc();
621 2720
        AN(ev);
622 2720
        ev->fd = p->fd_term;
623 2720
        ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR;
624 2720
        ev->callback = process_stdout;
625 2720
        ev->priv = p;
626 2720
        AZ(VEV_Start(evb, ev));
627
628 2720
        ev = VEV_Alloc();
629 2720
        AN(ev);
630 2720
        ev->fd = p->fd_stderr;
631 2720
        ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR;
632 2720
        ev->callback = process_stderr;
633 2720
        ev->priv = p;
634 2720
        AZ(VEV_Start(evb, ev));
635
636 2720
        if (p->log == 1) {
637 360
                p->vlu_stdout = VLU_New(process_vlu_func, p, 1024);
638 360
                AN(p->vlu_stdout);
639 360
                p->vlu_stderr = VLU_New(process_vlu_func, p, 1024);
640 360
                AN(p->vlu_stderr);
641 360
        }
642
643 2720
        do {
644 2720
                r = VEV_Once(evb);
645 2720
        } while (r == 1);
646
647 2720
        if (r < 0)
648 0
                vtc_fatal(p->vl, "VEV_Once() = %d, error %s", r,
649 0
                    strerror(errno));
650
651 5440
        vtc_wait4(p->vl, p->pid,
652 2720
            p->expect_exit, p->expect_signal, p->allow_core);
653 2720
        closefd(&p->f_stdout);
654 2720
        closefd(&p->f_stderr);
655
656 2720
        PTOK(pthread_mutex_lock(&p->mtx));
657
658
        /* NB: We keep the other macros around */
659 2720
        macro_undef(p->vl, p->name, "pid");
660 2720
        p->pid = -1;
661
662 2720
        PTOK(pthread_mutex_unlock(&p->mtx));
663
664 2720
        pthread_cleanup_pop(0);
665 2720
        VEV_Destroy(&evb);
666 2720
        if (p->log == 1) {
667 360
                VLU_Destroy(&p->vlu_stdout);
668 360
                VLU_Destroy(&p->vlu_stderr);
669 360
        }
670 2720
        return (NULL);
671
}
672
673
static void
674 3040
process_winsz(struct process *p, int fd)
675
{
676
        struct winsize ws;
677
        int i;
678
679 3040
        memset(&ws, 0, sizeof ws);
680 3040
        ws.ws_row = (short)p->nlin;
681 3040
        ws.ws_col = (short)p->ncol;
682 3040
        i = ioctl(fd, TIOCSWINSZ, &ws);
683 3040
        if (i)
684 120
                vtc_log(p->vl, 4, "TIOCWINSZ %d %s", i, strerror(errno));
685 3040
}
686
687
static void
688 2720
process_init_term(struct process *p, int fd)
689
{
690
        struct termios tt;
691
        int i;
692
693 2720
        process_winsz(p, fd);
694
695 2720
        memset(&tt, 0, sizeof tt);
696 2720
        tt.c_cflag = CREAD | CS8 | HUPCL;
697 2720
        tt.c_iflag = BRKINT | ICRNL | IMAXBEL | IXON | IXANY;
698 2720
        tt.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOKE | ECHOCTL;
699 2720
        tt.c_oflag = OPOST | ONLCR;
700 2720
        i = cfsetispeed(&tt, B9600);
701 2720
        if (i)
702 0
                vtc_log(p->vl, 4, "cfsetispeed %d %s", i, strerror(errno));
703 0
        i = cfsetospeed(&tt, B9600);
704 0
        if (i)
705 0
                vtc_log(p->vl, 4, "cfsetospeed %d %s", i, strerror(errno));
706 0
        tt.c_cc[VEOF] = '\x04';                 // CTRL-D
707 0
        tt.c_cc[VERASE] = '\x08';               // CTRL-H (Backspace)
708 0
        tt.c_cc[VKILL] = '\x15';                // CTRL-U
709 0
        tt.c_cc[VINTR] = '\x03';                // CTRL-C
710 0
        tt.c_cc[VQUIT] = '\x1c';                // CTRL-backslash
711
712 0
        i = tcsetattr(fd, TCSAFLUSH, &tt);
713 0
        if (i)
714 0
                vtc_log(p->vl, 4, "TCSAFLUSH %d %s", i, strerror(errno));
715 2720
}
716
717
/**********************************************************************
718
 * Start the process thread
719
 */
720
721
static void
722 2720
process_start(struct process *p)
723
{
724
        struct vsb *cl;
725
        int fd2[2];
726
        int master, slave;
727
        const char *slavename;
728
        char c;
729
730 2720
        CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC);
731 2720
        if (p->hasthread)
732 0
                vtc_fatal(p->vl, "Already running, -wait first");
733
734 2720
        vtc_log(p->vl, 4, "CMD: %s", p->spec);
735
736 2720
        cl = macro_expand(p->vl, p->spec);
737 2720
        AN(cl);
738
739 2720
        master = posix_openpt(O_RDWR|O_NOCTTY);
740 2720
        assert(master >= 0);
741 2720
        AZ(grantpt(master));
742 2720
        AZ(unlockpt(master));
743 2720
        slavename = ptsname(master);
744 2720
        AN(slavename);
745
746 2720
        AZ(pipe(fd2));
747
748 2720
        p->pid = fork();
749 2720
        assert(p->pid >= 0);
750 5440
        if (p->pid == 0) {
751 2720
                assert(setsid() == getpid());
752 2720
                assert(dup2(fd2[1], STDERR_FILENO) == STDERR_FILENO);
753 2720
                AZ(close(STDIN_FILENO));
754 2720
                slave = open(slavename, O_RDWR);
755 2720
                assert(slave == STDIN_FILENO);
756
#ifdef __sun
757
                if (ioctl(slave, I_PUSH, "ptem"))
758
                        vtc_log(p->vl, 4, "PUSH ptem: %s", strerror(errno));
759
                if (ioctl(slave, I_PUSH, "ldterm"))
760
                        vtc_log(p->vl, 4, "PUSH ldterm: %s", strerror(errno));
761
                (void)ioctl(STDIN_FILENO, TIOCSCTTY, NULL);
762
#else
763 2720
                AZ(ioctl(STDIN_FILENO, TIOCSCTTY, NULL));
764
#endif
765 2720
                AZ(close(STDOUT_FILENO));
766 2720
                assert(dup2(slave, STDOUT_FILENO) == STDOUT_FILENO);
767 2720
                VSUB_closefrom(STDERR_FILENO + 1);
768 2720
                process_init_term(p, slave);
769
770 2720
                AZ(setenv("TERM", "xterm", 1));
771 2720
                AZ(unsetenv("TERMCAP"));
772
                // Not using NULL because GCC is now even more demented...
773 2720
                assert(write(STDERR_FILENO, "+", 1) == 1);
774 2720
                AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(cl), (char*)0));
775 0
                exit(1);
776
        }
777 2720
        vtc_log(p->vl, 3, "PID: %ld", (long)p->pid);
778 2720
        VSB_destroy(&cl);
779
780 2720
        assert(read(fd2[0], &c, 1) == 1);
781 2720
        p->fd_term = master;
782 2720
        closefd(&fd2[1]);
783 2720
        p->fd_stderr = fd2[0];
784 2720
        macro_def(p->vl, p->name, "pid", "%ld", (long)p->pid);
785 2720
        macro_def(p->vl, p->name, "dir", "%s", p->dir);
786 2720
        macro_def(p->vl, p->name, "out", "%s", p->out);
787 2720
        macro_def(p->vl, p->name, "err", "%s", p->err);
788 2720
        p->hasthread = 1;
789 2720
        PTOK(pthread_create(&p->tp, NULL, process_thread, p));
790 2720
}
791
792
/**********************************************************************
793
 * Wait for process thread to stop
794
 */
795
796
static void
797 2720
process_wait(struct process *p)
798
{
799
        void *v;
800
801 2720
        if (p->hasthread) {
802 2720
                PTOK(pthread_join(p->tp, &v));
803 2720
                p->hasthread = 0;
804 2720
        }
805 5440
        vtc_log(p->vl, 4, "stdout %ju bytes, stderr %ju bytes",
806 2720
            p->stdout_bytes, p->stderr_bytes);
807 2720
}
808
809
/**********************************************************************
810
 * Send a signal to a process
811
 */
812
813
static void
814 1080
process_kill(struct process *p, const char *sig)
815
{
816 1080
        int j = 0;
817
        pid_t pid;
818
819 1080
        CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC);
820 1080
        AN(sig);
821
822 1080
        PTOK(pthread_mutex_lock(&p->mtx));
823 1080
        pid = p->pid;
824 1080
        PTOK(pthread_mutex_unlock(&p->mtx));
825
826 1080
        if (pid <= 0)
827 0
                vtc_fatal(p->vl, "Cannot signal a non-running process");
828
829 1080
        if (!strcmp(sig, "TERM"))
830 840
                j = SIGTERM;
831 240
        else if (!strcmp(sig, "INT"))
832 40
                j = SIGINT;
833 200
        else if (!strcmp(sig, "KILL"))
834 80
                j = SIGKILL;
835 120
        else if (!strcmp(sig, "HUP"))
836 120
                j = SIGHUP;
837 0
        else if (*sig == '-')
838 0
                j = strtoul(sig + 1, NULL, 10);
839
        else
840 0
                vtc_fatal(p->vl, "Could not grok signal (%s)", sig);
841
842 1080
        if (p->expect_signal == 0)
843 1040
                p->expect_signal = -j;
844 1080
        if (kill(-pid, j) < 0)
845 0
                vtc_fatal(p->vl, "Failed to send signal %d (%s)",
846 0
                    j, strerror(errno));
847
        else
848 1080
                vtc_log(p->vl, 4, "Sent signal %d", j);
849 1080
}
850
851
/**********************************************************************
852
 * Write to a process' stdin
853
 */
854
855
static void
856 5240
process_write(const struct process *p, const char *text)
857
{
858
        int r, len;
859
860 5240
        if (!p->hasthread)
861 0
                vtc_fatal(p->vl, "Cannot write to a non-running process");
862
863 5240
        len = strlen(text);
864 5240
        vtc_log(p->vl, 4, "Writing %d bytes", len);
865 5240
        r = write(p->fd_term, text, len);
866 5240
        if (r != len)
867 0
                vtc_fatal(p->vl, "Failed to write: len=%d %s (%d)",
868 0
                    len, strerror(errno), errno);
869 5240
}
870
871
static void
872 5000
process_write_hex(const struct process *p, const char *text)
873
{
874
        struct vsb *vsb;
875
876 5000
        if (!p->hasthread)
877 0
                vtc_fatal(p->vl, "Cannot write to a non-running process");
878
879 5000
        vsb = vtc_hex_to_bin(p->vl, text);
880 5000
        assert(VSB_len(vsb) >= 0);
881 5000
        vtc_hexdump(p->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb));
882 5000
        AZ(VSB_tofile(vsb, p->fd_term));
883 5000
        VSB_destroy(&vsb);
884 5000
}
885
886
static void
887 0
process_close(struct process *p)
888
{
889
890 0
        if (!p->hasthread)
891 0
                vtc_fatal(p->vl, "Cannot close a non-running process");
892
893 0
        process_kill(p, "HUP");
894 0
}
895
896
/* SECTION: process process
897
 *
898
 * Run a process with stdin+stdout on a pseudo-terminal and stderr on a pipe.
899
 *
900
 * Output from the pseudo-terminal is copied verbatim to ${pNAME_out},
901
 * and the -log/-dump/-hexdump flags will also put it in the vtc-log.
902
 *
903
 * The pseudo-terminal is not in ECHO mode, but if the programs run set
904
 * it to ECHO mode ("stty sane") any input sent to the process will also
905
 * appear in this stream because of the ECHO.
906
 *
907
 * Output from the stderr-pipe is copied verbatim to ${pNAME_err}, and
908
 * is always included in the vtc_log.
909
 *
910
 *      process pNAME SPEC [-allow-core] [-expect-exit N] [-expect-signal N]
911
 *              [-dump] [-hexdump] [-log]
912
 *              [-run] [-close] [-kill SIGNAL] [-start] [-stop] [-wait]
913
 *              [-write STRING] [-writeln STRING] [-writehex HEXSTRING]
914
 *              [-need-bytes [+]NUMBER]
915
 *              [-screen-dump] [-winsz LINES COLUMNSS] [-ansi-response]
916
 *              [-expect-cursor LINE COLUMN] [-expect-text LINE COLUMN TEXT]
917
 *              [-match-text LINE COLUMN REGEXP]
918
 *
919
 * pNAME
920
 *      Name of the process. It must start with 'p'.
921
 *
922
 * SPEC
923
 *      The command(s) to run in this process.
924
 *
925
 * \-hexdump
926
 *      Log output with vtc_hexdump(). Must be before -start/-run.
927
 *
928
 * \-dump
929
 *      Log output with vtc_dump(). Must be before -start/-run.
930
 *
931
 * \-log
932
 *      Log output with VLU/vtc_log(). Must be before -start/-run.
933
 *
934
 * \-start
935
 *      Start the process.
936
 *
937
 * \-expect-exit N
938
 *      Expect exit status N
939
 *
940
 * \-expect-signal N
941
 *      Expect signal in exit status N
942
 *
943
 * \-allow-core
944
 *      Core dump in exit status is OK
945
 *
946
 * \-wait
947
 *      Wait for the process to finish.
948
 *
949
 * \-run
950
 *      Shorthand for -start -wait.
951
 *
952
 *      In most cases, if you just want to start a process and wait for it
953
 *      to finish, you can use the ``shell`` command instead.
954
 *      The following commands are equivalent::
955
 *
956
 *          shell "do --something"
957
 *
958
 *          process p1 "do --something" -run
959
 *
960
 *      However, you may use the the ``process`` variant to conveniently
961
 *      collect the standard input and output without dealing with shell
962
 *      redirections yourself. The ``shell`` command can also expect an
963
 *      expression from either output, consider using it if you only need
964
 *      to match one.
965
 *
966
 * \-key KEYSYM
967
 *      Send emulated key-press.
968
 *      KEYSYM can be one of (NPAGE, PPAGE, HOME, END)
969
 *
970
 *
971
 * \-kill SIGNAL
972
 *      Send a signal to the process. The argument can be either
973
 *      the string "TERM", "INT", or "KILL" for SIGTERM, SIGINT or SIGKILL
974
 *      signals, respectively, or a hyphen (-) followed by the signal
975
 *      number.
976
 *
977
 *      If you need to use other signal names, you can use the ``kill``\(1)
978
 *      command directly::
979
 *
980
 *          shell "kill -USR1 ${pNAME_pid}"
981
 *
982
 *      Note that SIGHUP usage is discouraged in test cases.
983
 *
984
 * \-stop
985
 *      Shorthand for -kill TERM.
986
 *
987
 * \-close
988
 *      Alias for "-kill HUP"
989
 *
990
 * \-winsz LINES COLUMNS
991
 *      Change the terminal window size to LIN lines and COL columns.
992
 *
993
 * \-write STRING
994
 *      Write a string to the process' stdin.
995
 *
996
 * \-writeln STRING
997
 *      Same as -write followed by a newline (\\n).
998
 *
999
 * \-writehex HEXSTRING
1000
 *      Same as -write but interpreted as hexadecimal bytes.
1001
 *
1002
 * \-need-bytes [+]NUMBER
1003
 *      Wait until at least NUMBER bytes have been received in total.
1004
 *      If '+' is prefixed, NUMBER new bytes must be received.
1005
 *
1006
 * \-ansi-response
1007
 *      Respond to terminal respond-back sequences
1008
 *
1009
 * \-expect-cursor LINE COLUMN
1010
 *      Expect cursors location
1011
 *
1012
 * \-expect-text LINE COLUMNS TEXT
1013
 *      Wait for TEXT to appear at LIN,COL on the virtual screen.
1014
 *      Lines and columns are numbered 1...N
1015
 *      LIN==0 means "on any line"
1016
 *      COL==0 means "anywhere on the line"
1017
 *
1018
 * \-match-text LINE COLUMN REGEXP
1019
 *      Wait for the PAT regular expression to match the text at LIN,COL on the virtual screen.
1020
 *      Lines and columns are numbered 1...N
1021
 *      LIN==0 means "on any line"
1022
 *      COL==0 means "anywhere on the line"
1023
 *
1024
 *
1025
 * \-screen-dump
1026
 *      Dump the virtual screen into vtc_log
1027
 *
1028
 */
1029
1030
void
1031 70760
cmd_process(CMD_ARGS)
1032
{
1033
        struct process *p, *p2;
1034
        uintmax_t u, v, bsnap;
1035
        unsigned lin,col;
1036 70760
        int spec_set = 0;
1037
1038 70760
        (void)priv;
1039
1040 70760
        if (av == NULL) {
1041
                /* Reset and free */
1042 41880
                VTAILQ_FOREACH_SAFE(p, &processes, list, p2) {
1043 2240
                        if (p->pid > 0) {
1044 200
                                process_kill(p, "TERM");
1045 200
                                sleep(1);
1046 200
                                if (p->pid > 0)
1047 0
                                        process_kill(p, "KILL");
1048 200
                        }
1049 360
                        if (p->hasthread)
1050 360
                                process_wait(p);
1051 2240
                        VTAILQ_REMOVE(&processes, p, list);
1052 2240
                        process_undef(p);
1053 2240
                        process_delete(p);
1054 2240
                }
1055 39640
                return;
1056
        }
1057
1058 31120
        AZ(strcmp(av[0], "process"));
1059 31120
        av++;
1060
1061 31120
        VTC_CHECK_NAME(vl, av[0], "Process", 'p');
1062 72400
        VTAILQ_FOREACH(p, &processes, list)
1063 70160
                if (!strcmp(p->name, av[0]))
1064 28880
                        break;
1065 55520
        if (p == NULL)
1066 2240
                p = process_new(av[0]);
1067 31120
        av++;
1068
1069 31120
        PTOK(pthread_mutex_lock(&p->mtx));
1070 31120
        bsnap = p->stdout_bytes;
1071 31120
        PTOK(pthread_mutex_unlock(&p->mtx));
1072
1073 70240
        for (; *av != NULL; av++) {
1074 39120
                if (vtc_error)
1075 0
                        break;
1076
1077 39120
                if (!strcmp(*av, "-allow-core")) {
1078 0
                        p->allow_core = 1;
1079 0
                        continue;
1080
                }
1081 39120
                if (!strcmp(*av, "-close")) {
1082 0
                        process_close(p);
1083 0
                        continue;
1084
                }
1085 39120
                if (!strcmp(*av, "-dump")) {
1086 760
                        if (p->hasthread)
1087 0
                                vtc_fatal(p->vl,
1088
                                    "Cannot dump a running process");
1089 760
                        p->log = 2;
1090 760
                        continue;
1091
                }
1092 38360
                if (!strcmp(*av, "-expect-exit")) {
1093 640
                        p->expect_exit = strtoul(av[1], NULL, 0);
1094 640
                        av++;
1095 640
                        continue;
1096
                }
1097 37720
                if (!strcmp(*av, "-expect-signal")) {
1098 0
                        p->expect_signal = strtoul(av[1], NULL, 0);
1099 0
                        av++;
1100 0
                        continue;
1101
                }
1102 37720
                if (!strcmp(*av, "-hexdump")) {
1103 160
                        if (p->hasthread)
1104 0
                                vtc_fatal(p->vl,
1105
                                    "Cannot dump a running process");
1106 160
                        p->log = 3;
1107 160
                        continue;
1108
                }
1109 37560
                if (!strcmp(*av, "-key")) {
1110 160
                        if (!strcmp(av[1], "NPAGE"))
1111 40
                                process_write(p, "\x1b\x5b\x36\x7e");
1112 120
                        else if (!strcmp(av[1], "PPAGE"))
1113 40
                                process_write(p, "\x1b\x5b\x35\x7e");
1114 80
                        else if (!strcmp(av[1], "HOME"))
1115 40
                                process_write(p, "\x1b\x4f\x48");
1116 40
                        else if (!strcmp(av[1], "END"))
1117 40
                                process_write(p, "\x1b\x4f\x46");
1118
                        else
1119 0
                                vtc_fatal(p->vl, "Unknown key %s", av[1]);
1120 160
                        continue;
1121
                }
1122 37400
                if (!strcmp(*av, "-kill")) {
1123 480
                        process_kill(p, av[1]);
1124 480
                        av++;
1125 480
                        continue;
1126
                }
1127 36920
                if (!strcmp(*av, "-log")) {
1128 360
                        if (p->hasthread)
1129 0
                                vtc_fatal(p->vl,
1130
                                    "Cannot log a running process");
1131 360
                        p->log = 1;
1132 360
                        continue;
1133
                }
1134 36560
                if (!strcmp(*av, "-need-bytes")) {
1135 360
                        u = strtoumax(av[1], NULL, 0);
1136 360
                        if (av[1][0] == '+')
1137 280
                                u += bsnap;
1138 360
                        av++;
1139 360
                        do {
1140 360
                                PTOK(pthread_mutex_lock(&p->mtx));
1141 716
                                v = p->stdout_bytes;
1142 716
                                PTOK(pthread_mutex_unlock(&p->mtx));
1143 716
                                vtc_log(p->vl, 4, "Have %ju bytes", v);
1144 716
                                usleep(500000);
1145 716
                        } while(v < u);
1146 360
                        continue;
1147
                }
1148 36200
                if (!strcmp(*av, "-run")) {
1149 760
                        process_start(p);
1150 760
                        process_wait(p);
1151 760
                        continue;
1152
                }
1153 35440
                if (!strcmp(*av, "-ansi-response")) {
1154 80
                        p->ansi_response = 1;
1155 80
                        continue;
1156
                }
1157 35360
                if (!strcmp(*av, "-expect-text")) {
1158 10040
                        AN(av[1]);
1159 10040
                        AN(av[2]);
1160 10040
                        AN(av[3]);
1161 10040
                        term_expect_text(p, av[1], av[2], av[3]);
1162 10040
                        av += 3;
1163 10040
                        continue;
1164
                }
1165 25320
                if (!strcmp(*av, "-expect-cursor")) {
1166 320
                        AN(av[1]);
1167 320
                        AN(av[2]);
1168 320
                        term_expect_cursor(p, av[1], av[2]);
1169 320
                        av += 2;
1170 320
                        continue;
1171
                }
1172 25000
                if (!strcmp(*av, "-match-text")) {
1173 1360
                        AN(av[1]);
1174 1360
                        AN(av[2]);
1175 1360
                        AN(av[3]);
1176 1360
                        term_match_text(p, av[1], av[2], av[3]);
1177 1360
                        av += 3;
1178 1360
                        continue;
1179
                }
1180 23640
                if (!strcmp(*av, "-screen_dump") ||
1181 17840
                    !strcmp(*av, "-screen-dump")) {
1182 6240
                        term_screen_dump(p);
1183 6240
                        continue;
1184
                }
1185 17400
                if (!strcmp(*av, "-start")) {
1186 1960
                        process_start(p);
1187 1960
                        continue;
1188
                }
1189 15440
                if (!strcmp(*av, "-stop")) {
1190 400
                        process_kill(p, "TERM");
1191 400
                        sleep(1);
1192 400
                        continue;
1193
                }
1194 15040
                if (!strcmp(*av, "-wait")) {
1195 1600
                        process_wait(p);
1196 1600
                        continue;
1197
                }
1198 13440
                if (!strcmp(*av, "-winsz")) {
1199 320
                        lin = atoi(av[1]);
1200 320
                        assert(lin > 1);
1201 320
                        col = atoi(av[2]);
1202 320
                        assert(col > 1);
1203 320
                        av += 2;
1204 320
                        PTOK(pthread_mutex_lock(&p->mtx));
1205 320
                        term_resize(p, lin, col);
1206 320
                        PTOK(pthread_mutex_unlock(&p->mtx));
1207 320
                        process_winsz(p, p->fd_term);
1208 320
                        continue;
1209
                }
1210 13120
                if (!strcmp(*av, "-write")) {
1211 4440
                        process_write(p, av[1]);
1212 4440
                        av++;
1213 4440
                        continue;
1214
                }
1215 8680
                if (!strcmp(*av, "-writehex")) {
1216 5000
                        process_write_hex(p, av[1]);
1217 5000
                        av++;
1218 5000
                        continue;
1219
                }
1220 3680
                if (!strcmp(*av, "-writeln")) {
1221 320
                        process_write(p, av[1]);
1222 320
                        process_write(p, "\n");
1223 320
                        av++;
1224 320
                        continue;
1225
                }
1226 3360
                if (**av == '-' || spec_set)
1227 0
                        vtc_fatal(p->vl, "Unknown process argument: %s", *av);
1228 3360
                REPLACE(p->spec, *av);
1229 3360
                spec_set = 1;
1230 3360
        }
1231 70760
}