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