GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/parse.y Lines: 0 292 0.0 %
Date: 2017-11-13 Branches: 0 217 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.31 2017/11/11 02:50:07 mlarkin Exp $	*/
2
3
/*
4
 * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6
 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7
 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8
 * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9
 * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10
 * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11
 *
12
 * Permission to use, copy, modify, and distribute this software for any
13
 * purpose with or without fee is hereby granted, provided that the above
14
 * copyright notice and this permission notice appear in all copies.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
 */
24
25
%{
26
#include <sys/types.h>
27
#include <sys/queue.h>
28
#include <sys/socket.h>
29
#include <sys/uio.h>
30
31
#include <machine/vmmvar.h>
32
33
#include <net/if.h>
34
#include <netinet/in.h>
35
#include <netinet/if_ether.h>
36
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <limits.h>
40
#include <stdarg.h>
41
#include <string.h>
42
#include <ctype.h>
43
#include <netdb.h>
44
#include <util.h>
45
#include <errno.h>
46
#include <err.h>
47
#include <pwd.h>
48
#include <grp.h>
49
50
#include "proc.h"
51
#include "vmd.h"
52
53
TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
54
static struct file {
55
	TAILQ_ENTRY(file)	 entry;
56
	FILE			*stream;
57
	char			*name;
58
	int			 lineno;
59
	int			 errors;
60
} *file, *topfile;
61
struct file	*pushfile(const char *, int);
62
int		 popfile(void);
63
int		 yyparse(void);
64
int		 yylex(void);
65
int		 yyerror(const char *, ...)
66
    __attribute__((__format__ (printf, 1, 2)))
67
    __attribute__((__nonnull__ (1)));
68
int		 kw_cmp(const void *, const void *);
69
int		 lookup(char *);
70
int		 lgetc(int);
71
int		 lungetc(int);
72
int		 findeol(void);
73
74
TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
75
struct sym {
76
	TAILQ_ENTRY(sym)	 entry;
77
	int			 used;
78
	int			 persist;
79
	char			*nam;
80
	char			*val;
81
};
82
int		 symset(const char *, const char *, int);
83
char		*symget(const char *);
84
85
ssize_t		 parse_size(char *, int64_t);
86
int		 parse_disk(char *);
87
88
static struct vmop_create_params vmc;
89
static struct vm_create_params	*vcp;
90
static struct vmd_switch	*vsw;
91
static struct vmd_vm		*vm;
92
static char			 vsw_type[IF_NAMESIZE];
93
static int			 vcp_disable;
94
static size_t			 vcp_nnics;
95
static int			 errors;
96
extern struct vmd		*env;
97
extern const char		*vmd_descsw[];
98
99
typedef struct {
100
	union {
101
		uint8_t		 lladdr[ETHER_ADDR_LEN];
102
		int64_t		 number;
103
		char		*string;
104
		struct {
105
			uid_t	 uid;
106
			int64_t	 gid;
107
		}		 owner;
108
	} v;
109
	int lineno;
110
} YYSTYPE;
111
112
%}
113
114
115
%token	INCLUDE ERROR
116
%token	ADD BOOT DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR LOCAL LOCKED
117
%token	MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SWITCH UP VM VMID
118
%token	<v.number>	NUMBER
119
%token	<v.string>	STRING
120
%type	<v.lladdr>	lladdr
121
%type	<v.number>	disable
122
%type	<v.number>	local
123
%type	<v.number>	locked
124
%type	<v.number>	updown
125
%type	<v.owner>	owner_id
126
%type	<v.string>	optstring
127
%type	<v.string>	string
128
129
%%
130
131
grammar		: /* empty */
132
		| grammar include '\n'
133
		| grammar '\n'
134
		| grammar varset '\n'
135
		| grammar main '\n'
136
		| grammar switch '\n'
137
		| grammar vm '\n'
138
		| grammar error '\n'		{ file->errors++; }
139
		;
140
141
include		: INCLUDE string		{
142
			struct file	*nfile;
143
144
			if ((nfile = pushfile($2, 0)) == NULL) {
145
				yyerror("failed to include file %s", $2);
146
				free($2);
147
				YYERROR;
148
			}
149
			free($2);
150
151
			file = nfile;
152
			lungetc('\n');
153
		}
154
		;
155
156
varset		: STRING '=' STRING		{
157
			char *s = $1;
158
			while (*s++) {
159
				if (isspace((unsigned char)*s)) {
160
					yyerror("macro name cannot contain "
161
					    "whitespace");
162
					YYERROR;
163
				}
164
			}
165
			if (symset($1, $3, 0) == -1)
166
				fatalx("cannot store variable");
167
			free($1);
168
			free($3);
169
		}
170
		;
171
172
main		: LOCAL PREFIX STRING {
173
			struct address	 h;
174
175
			/* The local prefix is IPv4-only */
176
			if (host($3, &h) == -1 ||
177
			    h.ss.ss_family != AF_INET ||
178
			    h.prefixlen > 32 || h.prefixlen < 0) {
179
				yyerror("invalid local prefix: %s", $3);
180
				free($3);
181
				YYERROR;
182
			}
183
184
			memcpy(&env->vmd_cfg.cfg_localprefix, &h, sizeof(h));
185
		}
186
		;
187
188
switch		: SWITCH string			{
189
			if ((vsw = calloc(1, sizeof(*vsw))) == NULL)
190
				fatal("could not allocate switch");
191
192
			vsw->sw_id = env->vmd_nswitches + 1;
193
			vsw->sw_name = $2;
194
			vsw->sw_flags = VMIFF_UP;
195
196
			vcp_disable = 0;
197
		} '{' optnl switch_opts_l '}'	{
198
			if (strnlen(vsw->sw_ifname,
199
			    sizeof(vsw->sw_ifname)) == 0) {
200
				yyerror("switch \"%s\" is missing interface name",
201
				    vsw->sw_name);
202
				YYERROR;
203
			}
204
205
			if (vcp_disable) {
206
				log_debug("%s:%d: switch \"%s\""
207
				    " skipped (disabled)",
208
				    file->name, yylval.lineno, vsw->sw_name);
209
			} else if (!env->vmd_noaction) {
210
				TAILQ_INSERT_TAIL(env->vmd_switches, vsw, sw_entry);
211
				env->vmd_nswitches++;
212
				log_debug("%s:%d: switch \"%s\" registered",
213
				    file->name, yylval.lineno, vsw->sw_name);
214
			}
215
		}
216
		;
217
218
switch_opts_l	: switch_opts_l switch_opts nl
219
		| switch_opts optnl
220
		;
221
222
switch_opts	: disable			{
223
			vcp_disable = $1;
224
		}
225
		| GROUP string			{
226
			if (priv_validgroup($2) == -1) {
227
				yyerror("invalid group name: %s", $2);
228
				free($2);
229
				YYERROR;
230
			}
231
			vsw->sw_group = $2;
232
		}
233
		| INTERFACE string		{
234
			if (priv_getiftype($2, vsw_type, NULL) == -1 ||
235
			    priv_findname(vsw_type, vmd_descsw) == -1) {
236
				yyerror("invalid switch interface: %s", $2);
237
				free($2);
238
				YYERROR;
239
			}
240
241
			if (strlcpy(vsw->sw_ifname, $2,
242
			    sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) {
243
				yyerror("switch interface too long: %s", $2);
244
				free($2);
245
				YYERROR;
246
			}
247
			free($2);
248
		}
249
		| LOCKED LLADDR			{
250
			vsw->sw_flags |= VMIFF_LOCKED;
251
		}
252
		| RDOMAIN NUMBER		{
253
			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
254
				yyerror("invalid rdomain: %lld", $2);
255
				YYERROR;
256
			}
257
			vsw->sw_flags |= VMIFF_RDOMAIN;
258
			vsw->sw_rdomain = $2;
259
		}
260
		| updown			{
261
			if ($1)
262
				vsw->sw_flags |= VMIFF_UP;
263
			else
264
				vsw->sw_flags &= ~VMIFF_UP;
265
		}
266
		;
267
268
vm		: VM string			{
269
			unsigned int	 i;
270
271
			memset(&vmc, 0, sizeof(vmc));
272
			vcp = &vmc.vmc_params;
273
			vcp_disable = 0;
274
			vcp_nnics = 0;
275
276
			for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
277
				/* Set the interface to UP by default */
278
				vmc.vmc_ifflags[i] |= IFF_UP;
279
			}
280
281
			if (strlcpy(vcp->vcp_name, $2, sizeof(vcp->vcp_name)) >=
282
			    sizeof(vcp->vcp_name)) {
283
				yyerror("vm name too long");
284
				YYERROR;
285
			}
286
287
			/* set default user/group permissions */
288
			vmc.vmc_uid = 0;
289
			vmc.vmc_gid = -1;
290
		} '{' optnl vm_opts_l '}'	{
291
			int ret;
292
293
			/* configured interfaces vs. number of interfaces */
294
			if (vcp_nnics > vcp->vcp_nnics)
295
				vcp->vcp_nnics = vcp_nnics;
296
297
			if (!env->vmd_noaction) {
298
				ret = vm_register(&env->vmd_ps, &vmc,
299
				    &vm, 0, 0);
300
				if (ret == -1 && errno == EALREADY) {
301
					log_debug("%s:%d: vm \"%s\""
302
					    " skipped (%s)",
303
					    file->name, yylval.lineno,
304
					    vcp->vcp_name, vm->vm_running ?
305
					    "running" : "already exists");
306
				} else if (ret == -1) {
307
					log_warn("%s:%d: vm \"%s\" failed",
308
					    file->name, yylval.lineno,
309
					    vcp->vcp_name);
310
					YYERROR;
311
				} else {
312
					if (vcp_disable)
313
						vm->vm_disabled = 1;
314
					log_debug("%s:%d: vm \"%s\" registered (%s)",
315
					    file->name, yylval.lineno,
316
					    vcp->vcp_name,
317
					    vcp_disable ? "disabled" : "enabled");
318
				}
319
				vm->vm_from_config = 1;
320
			}
321
		}
322
		;
323
324
vm_opts_l	: vm_opts_l vm_opts nl
325
		| vm_opts optnl
326
		;
327
328
vm_opts		: disable			{
329
			vcp_disable = $1;
330
		}
331
		| DISK string			{
332
			if (parse_disk($2) != 0) {
333
				yyerror("failed to parse disks: %s", $2);
334
				free($2);
335
				YYERROR;
336
			}
337
			free($2);
338
			vmc.vmc_flags |= VMOP_CREATE_DISK;
339
		}
340
		| local INTERFACE optstring iface_opts_o {
341
			unsigned int	i;
342
			char		type[IF_NAMESIZE];
343
344
			i = vcp_nnics;
345
			if (++vcp_nnics > VMM_MAX_NICS_PER_VM) {
346
				yyerror("too many interfaces: %zu", vcp_nnics);
347
				free($3);
348
				YYERROR;
349
			}
350
351
			if ($1)
352
				vmc.vmc_ifflags[i] |= VMIFF_LOCAL;
353
			if ($3 != NULL) {
354
				if (strcmp($3, "tap") != 0 &&
355
				    (priv_getiftype($3, type, NULL) == -1 ||
356
				    strcmp(type, "tap") != 0)) {
357
					yyerror("invalid interface: %s", $3);
358
					free($3);
359
					YYERROR;
360
				}
361
362
				if (strlcpy(vmc.vmc_ifnames[i], $3,
363
				    sizeof(vmc.vmc_ifnames[i])) >=
364
				    sizeof(vmc.vmc_ifnames[i])) {
365
					yyerror("interface name too long: %s",
366
					    $3);
367
					free($3);
368
					YYERROR;
369
				}
370
			}
371
			free($3);
372
			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
373
		}
374
		| BOOT string			{
375
			if (vcp->vcp_kernel[0] != '\0') {
376
				yyerror("kernel specified more than once");
377
				free($2);
378
				YYERROR;
379
380
			}
381
			if (strlcpy(vcp->vcp_kernel, $2,
382
			    sizeof(vcp->vcp_kernel)) >=
383
			    sizeof(vcp->vcp_kernel)) {
384
				yyerror("kernel name too long");
385
				free($2);
386
				YYERROR;
387
			}
388
			free($2);
389
			vmc.vmc_flags |= VMOP_CREATE_KERNEL;
390
		}
391
		| NIFS NUMBER			{
392
			if (vcp->vcp_nnics != 0) {
393
				yyerror("interfaces specified more than once");
394
				YYERROR;
395
			}
396
			if ($2 < 0 || $2 > VMM_MAX_NICS_PER_VM) {
397
				yyerror("too many interfaces: %lld", $2);
398
				YYERROR;
399
			}
400
			vcp->vcp_nnics = (size_t)$2;
401
			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
402
		}
403
		| MEMORY NUMBER			{
404
			ssize_t	 res;
405
			if (vcp->vcp_memranges[0].vmr_size != 0) {
406
				yyerror("memory specified more than once");
407
				YYERROR;
408
			}
409
			if ((res = parse_size(NULL, $2)) == -1) {
410
				yyerror("failed to parse size: %lld", $2);
411
				YYERROR;
412
			}
413
			vcp->vcp_memranges[0].vmr_size = (size_t)res;
414
			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
415
		}
416
		| MEMORY STRING			{
417
			ssize_t	 res;
418
			if (vcp->vcp_memranges[0].vmr_size != 0) {
419
				yyerror("argument specified more than once");
420
				free($2);
421
				YYERROR;
422
			}
423
			if ((res = parse_size($2, 0)) == -1) {
424
				yyerror("failed to parse size: %s", $2);
425
				free($2);
426
				YYERROR;
427
			}
428
			vcp->vcp_memranges[0].vmr_size = (size_t)res;
429
			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
430
		}
431
		| OWNER owner_id		{
432
			vmc.vmc_uid = $2.uid;
433
			vmc.vmc_gid = $2.gid;
434
		}
435
		;
436
437
owner_id	: /* none */		{
438
			$$.uid = 0;
439
			$$.gid = -1;
440
		}
441
		| NUMBER		{
442
			$$.uid = $1;
443
			$$.gid = -1;
444
		}
445
		| STRING		{
446
			char		*user, *group;
447
			struct passwd	*pw;
448
			struct group	*gr;
449
450
			$$.uid = 0;
451
			$$.gid = -1;
452
453
			user = $1;
454
			if ((group = strchr(user, ':')) != NULL) {
455
				if (group == user)
456
					user = NULL;
457
				*group++ = '\0';
458
			}
459
460
			if (user != NULL && *user) {
461
				if ((pw = getpwnam(user)) == NULL) {
462
					yyerror("failed to get user: %s",
463
					    user);
464
					free($1);
465
					YYERROR;
466
				}
467
				$$.uid = pw->pw_uid;
468
			}
469
470
			if (group != NULL && *group) {
471
				if ((gr = getgrnam(group)) == NULL) {
472
					yyerror("failed to get group: %s",
473
					    group);
474
					free($1);
475
					YYERROR;
476
				}
477
				$$.gid = gr->gr_gid;
478
			}
479
480
			free($1);
481
		}
482
		;
483
484
iface_opts_o	: '{' optnl iface_opts_l '}'
485
		| iface_opts_c
486
		| /* empty */
487
		;
488
489
iface_opts_l	: iface_opts_l iface_opts optnl
490
		| iface_opts optnl
491
		;
492
493
iface_opts_c	: iface_opts_c iface_opts optcomma
494
		| iface_opts
495
		;
496
497
iface_opts	: SWITCH string			{
498
			unsigned int	i = vcp_nnics;
499
500
			/* No need to check if the switch exists */
501
			if (strlcpy(vmc.vmc_ifswitch[i], $2,
502
			    sizeof(vmc.vmc_ifswitch[i])) >=
503
			    sizeof(vmc.vmc_ifswitch[i])) {
504
				yyerror("switch name too long: %s", $2);
505
				free($2);
506
				YYERROR;
507
			}
508
			free($2);
509
		}
510
		| GROUP string			{
511
			unsigned int	i = vcp_nnics;
512
513
			if (priv_validgroup($2) == -1) {
514
				yyerror("invalid group name: %s", $2);
515
				free($2);
516
				YYERROR;
517
			}
518
519
			/* No need to check if the group exists */
520
			(void)strlcpy(vmc.vmc_ifgroup[i], $2,
521
			    sizeof(vmc.vmc_ifgroup[i]));
522
			free($2);
523
		}
524
		| locked LLADDR lladdr		{
525
			if ($1)
526
				vmc.vmc_ifflags[vcp_nnics] |= VMIFF_LOCKED;
527
			memcpy(vcp->vcp_macs[vcp_nnics], $3, ETHER_ADDR_LEN);
528
		}
529
		| RDOMAIN NUMBER		{
530
			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
531
				yyerror("invalid rdomain: %lld", $2);
532
				YYERROR;
533
			}
534
			vmc.vmc_ifflags[vcp_nnics] |= VMIFF_RDOMAIN;
535
			vmc.vmc_ifrdomain[vcp_nnics] = $2;
536
		}
537
		| updown			{
538
			if ($1)
539
				vmc.vmc_ifflags[vcp_nnics] |= VMIFF_UP;
540
			else
541
				vmc.vmc_ifflags[vcp_nnics] &= ~VMIFF_UP;
542
		}
543
		;
544
545
optstring	: STRING			{ $$ = $1; }
546
		| /* empty */			{ $$ = NULL; }
547
		;
548
549
string		: STRING string			{
550
			if (asprintf(&$$, "%s%s", $1, $2) == -1)
551
				fatal("asprintf string");
552
			free($1);
553
			free($2);
554
		}
555
		| STRING
556
		;
557
558
lladdr		: STRING			{
559
			struct ether_addr *ea;
560
561
			if ((ea = ether_aton($1)) == NULL) {
562
				yyerror("invalid address: %s\n", $1);
563
				free($1);
564
				YYERROR;
565
			}
566
			free($1);
567
568
			memcpy($$, ea, ETHER_ADDR_LEN);
569
		}
570
		;
571
572
local		: /* empty */			{ $$ = 0; }
573
		| LOCAL				{ $$ = 1; }
574
		;
575
576
locked		: /* empty */			{ $$ = 0; }
577
		| LOCKED			{ $$ = 1; }
578
		;
579
580
updown		: UP				{ $$ = 1; }
581
		| DOWN				{ $$ = 0; }
582
		;
583
584
disable		: ENABLE			{ $$ = 0; }
585
		| DISABLE			{ $$ = 1; }
586
		;
587
588
optcomma	: ','
589
		|
590
		;
591
592
optnl		: '\n' optnl
593
		|
594
		;
595
596
nl		: '\n' optnl
597
		;
598
599
%%
600
601
struct keywords {
602
	const char	*k_name;
603
	int		 k_val;
604
};
605
606
int
607
yyerror(const char *fmt, ...)
608
{
609
	va_list		 ap;
610
	char		*msg;
611
612
	file->errors++;
613
	va_start(ap, fmt);
614
	if (vasprintf(&msg, fmt, ap) == -1)
615
		fatal("yyerror vasprintf");
616
	va_end(ap);
617
	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
618
	free(msg);
619
	return (0);
620
}
621
622
int
623
kw_cmp(const void *k, const void *e)
624
{
625
	return (strcmp(k, ((const struct keywords *)e)->k_name));
626
}
627
628
int
629
lookup(char *s)
630
{
631
	/* this has to be sorted always */
632
	static const struct keywords keywords[] = {
633
		{ "add",		ADD },
634
		{ "boot",		BOOT },
635
		{ "disable",		DISABLE },
636
		{ "disk",		DISK },
637
		{ "down",		DOWN },
638
		{ "enable",		ENABLE },
639
		{ "group",		GROUP },
640
		{ "id",			VMID },
641
		{ "include",		INCLUDE },
642
		{ "interface",		INTERFACE },
643
		{ "interfaces",		NIFS },
644
		{ "lladdr",		LLADDR },
645
		{ "local",		LOCAL },
646
		{ "locked",		LOCKED },
647
		{ "memory",		MEMORY },
648
		{ "owner",		OWNER },
649
		{ "prefix",		PREFIX },
650
		{ "rdomain",		RDOMAIN },
651
		{ "size",		SIZE },
652
		{ "switch",		SWITCH },
653
		{ "up",			UP },
654
		{ "vm",			VM }
655
	};
656
	const struct keywords	*p;
657
658
	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
659
	    sizeof(keywords[0]), kw_cmp);
660
661
	if (p)
662
		return (p->k_val);
663
	else
664
		return (STRING);
665
}
666
667
#define MAXPUSHBACK	128
668
669
u_char	*parsebuf;
670
int	 parseindex;
671
u_char	 pushback_buffer[MAXPUSHBACK];
672
int	 pushback_index = 0;
673
674
int
675
lgetc(int quotec)
676
{
677
	int		c, next;
678
679
	if (parsebuf) {
680
		/* Read character from the parsebuffer instead of input. */
681
		if (parseindex >= 0) {
682
			c = parsebuf[parseindex++];
683
			if (c != '\0')
684
				return (c);
685
			parsebuf = NULL;
686
		} else
687
			parseindex++;
688
	}
689
690
	if (pushback_index)
691
		return (pushback_buffer[--pushback_index]);
692
693
	if (quotec) {
694
		if ((c = getc(file->stream)) == EOF) {
695
			yyerror("reached end of file while parsing "
696
			    "quoted string");
697
			if (file == topfile || popfile() == EOF)
698
				return (EOF);
699
			return (quotec);
700
		}
701
		return (c);
702
	}
703
704
	while ((c = getc(file->stream)) == '\\') {
705
		next = getc(file->stream);
706
		if (next != '\n') {
707
			c = next;
708
			break;
709
		}
710
		yylval.lineno = file->lineno;
711
		file->lineno++;
712
	}
713
	if (c == '\t' || c == ' ') {
714
		/* Compress blanks to a single space. */
715
		do {
716
			c = getc(file->stream);
717
		} while (c == '\t' || c == ' ');
718
		ungetc(c, file->stream);
719
		c = ' ';
720
	}
721
722
	while (c == EOF) {
723
		if (file == topfile || popfile() == EOF)
724
			return (EOF);
725
		c = getc(file->stream);
726
	}
727
	return (c);
728
}
729
730
int
731
lungetc(int c)
732
{
733
	if (c == EOF)
734
		return (EOF);
735
	if (parsebuf) {
736
		parseindex--;
737
		if (parseindex >= 0)
738
			return (c);
739
	}
740
	if (pushback_index < MAXPUSHBACK-1)
741
		return (pushback_buffer[pushback_index++] = c);
742
	else
743
		return (EOF);
744
}
745
746
int
747
findeol(void)
748
{
749
	int	c;
750
751
	parsebuf = NULL;
752
753
	/* skip to either EOF or the first real EOL */
754
	while (1) {
755
		if (pushback_index)
756
			c = pushback_buffer[--pushback_index];
757
		else
758
			c = lgetc(0);
759
		if (c == '\n') {
760
			file->lineno++;
761
			break;
762
		}
763
		if (c == EOF)
764
			break;
765
	}
766
	return (ERROR);
767
}
768
769
int
770
yylex(void)
771
{
772
	u_char	 buf[8096];
773
	u_char	*p, *val;
774
	int	 quotec, next, c;
775
	int	 token;
776
777
top:
778
	p = buf;
779
	while ((c = lgetc(0)) == ' ' || c == '\t')
780
		; /* nothing */
781
782
	yylval.lineno = file->lineno;
783
	if (c == '#')
784
		while ((c = lgetc(0)) != '\n' && c != EOF)
785
			; /* nothing */
786
	if (c == '$' && parsebuf == NULL) {
787
		while (1) {
788
			if ((c = lgetc(0)) == EOF)
789
				return (0);
790
791
			if (p + 1 >= buf + sizeof(buf) - 1) {
792
				yyerror("string too long");
793
				return (findeol());
794
			}
795
			if (isalnum(c) || c == '_') {
796
				*p++ = c;
797
				continue;
798
			}
799
			*p = '\0';
800
			lungetc(c);
801
			break;
802
		}
803
		val = symget(buf);
804
		if (val == NULL) {
805
			yyerror("macro '%s' not defined", buf);
806
			return (findeol());
807
		}
808
		parsebuf = val;
809
		parseindex = 0;
810
		goto top;
811
	}
812
813
	switch (c) {
814
	case '\'':
815
	case '"':
816
		quotec = c;
817
		while (1) {
818
			if ((c = lgetc(quotec)) == EOF)
819
				return (0);
820
			if (c == '\n') {
821
				file->lineno++;
822
				continue;
823
			} else if (c == '\\') {
824
				if ((next = lgetc(quotec)) == EOF)
825
					return (0);
826
				if (next == quotec || c == ' ' || c == '\t')
827
					c = next;
828
				else if (next == '\n') {
829
					file->lineno++;
830
					continue;
831
				} else
832
					lungetc(next);
833
			} else if (c == quotec) {
834
				*p = '\0';
835
				break;
836
			} else if (c == '\0') {
837
				yyerror("syntax error");
838
				return (findeol());
839
			}
840
			if (p + 1 >= buf + sizeof(buf) - 1) {
841
				yyerror("string too long");
842
				return (findeol());
843
			}
844
			*p++ = c;
845
		}
846
		yylval.v.string = strdup(buf);
847
		if (yylval.v.string == NULL)
848
			fatal("yylex: strdup");
849
		return (STRING);
850
	}
851
852
#define allowed_to_end_number(x) \
853
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
854
855
	if (c == '-' || isdigit(c)) {
856
		do {
857
			*p++ = c;
858
			if ((unsigned)(p-buf) >= sizeof(buf)) {
859
				yyerror("string too long");
860
				return (findeol());
861
			}
862
		} while ((c = lgetc(0)) != EOF && isdigit(c));
863
		lungetc(c);
864
		if (p == buf + 1 && buf[0] == '-')
865
			goto nodigits;
866
		if (c == EOF || allowed_to_end_number(c)) {
867
			const char *errstr = NULL;
868
869
			*p = '\0';
870
			yylval.v.number = strtonum(buf, LLONG_MIN,
871
			    LLONG_MAX, &errstr);
872
			if (errstr) {
873
				yyerror("\"%s\" invalid number: %s",
874
				    buf, errstr);
875
				return (findeol());
876
			}
877
			return (NUMBER);
878
		} else {
879
nodigits:
880
			while (p > buf + 1)
881
				lungetc(*--p);
882
			c = *--p;
883
			if (c == '-')
884
				return (c);
885
		}
886
	}
887
888
#define allowed_in_string(x) \
889
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
890
	x != '{' && x != '}' && \
891
	x != '!' && x != '=' && x != '#' && \
892
	x != ','))
893
894
	if (isalnum(c) || c == ':' || c == '_' || c == '/') {
895
		do {
896
			*p++ = c;
897
			if ((unsigned)(p-buf) >= sizeof(buf)) {
898
				yyerror("string too long");
899
				return (findeol());
900
			}
901
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
902
		lungetc(c);
903
		*p = '\0';
904
		if ((token = lookup(buf)) == STRING)
905
			if ((yylval.v.string = strdup(buf)) == NULL)
906
				fatal("yylex: strdup");
907
		return (token);
908
	}
909
	if (c == '\n') {
910
		yylval.lineno = file->lineno;
911
		file->lineno++;
912
	}
913
	if (c == EOF)
914
		return (0);
915
	return (c);
916
}
917
918
struct file *
919
pushfile(const char *name, int secret)
920
{
921
	struct file	*nfile;
922
923
	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
924
		log_warn("malloc");
925
		return (NULL);
926
	}
927
	if ((nfile->name = strdup(name)) == NULL) {
928
		log_warn("malloc");
929
		free(nfile);
930
		return (NULL);
931
	}
932
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
933
		free(nfile->name);
934
		free(nfile);
935
		return (NULL);
936
	}
937
	nfile->lineno = 1;
938
	TAILQ_INSERT_TAIL(&files, nfile, entry);
939
	return (nfile);
940
}
941
942
int
943
popfile(void)
944
{
945
	struct file	*prev;
946
947
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
948
		prev->errors += file->errors;
949
950
	TAILQ_REMOVE(&files, file, entry);
951
	fclose(file->stream);
952
	free(file->name);
953
	free(file);
954
	file = prev;
955
	return (file ? 0 : EOF);
956
}
957
958
int
959
parse_config(const char *filename)
960
{
961
	struct sym	*sym, *next;
962
963
	if ((file = pushfile(filename, 0)) == NULL) {
964
		log_warn("failed to open %s", filename);
965
		if (errno == ENOENT)
966
			return (0);
967
		return (-1);
968
	}
969
	topfile = file;
970
	setservent(1);
971
972
	/* Set the default switch type */
973
	(void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type));
974
975
	yyparse();
976
	errors = file->errors;
977
	popfile();
978
979
	endservent();
980
981
	/* Free macros and check which have not been used. */
982
	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
983
		if (!sym->used)
984
			fprintf(stderr, "warning: macro '%s' not "
985
			    "used\n", sym->nam);
986
		if (!sym->persist) {
987
			free(sym->nam);
988
			free(sym->val);
989
			TAILQ_REMOVE(&symhead, sym, entry);
990
			free(sym);
991
		}
992
	}
993
994
	if (errors)
995
		return (-1);
996
997
	return (0);
998
}
999
1000
int
1001
symset(const char *nam, const char *val, int persist)
1002
{
1003
	struct sym	*sym;
1004
1005
	TAILQ_FOREACH(sym, &symhead, entry) {
1006
		if (strcmp(nam, sym->nam) == 0)
1007
			break;
1008
	}
1009
1010
	if (sym != NULL) {
1011
		if (sym->persist == 1)
1012
			return (0);
1013
		else {
1014
			free(sym->nam);
1015
			free(sym->val);
1016
			TAILQ_REMOVE(&symhead, sym, entry);
1017
			free(sym);
1018
		}
1019
	}
1020
	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1021
		return (-1);
1022
1023
	sym->nam = strdup(nam);
1024
	if (sym->nam == NULL) {
1025
		free(sym);
1026
		return (-1);
1027
	}
1028
	sym->val = strdup(val);
1029
	if (sym->val == NULL) {
1030
		free(sym->nam);
1031
		free(sym);
1032
		return (-1);
1033
	}
1034
	sym->used = 0;
1035
	sym->persist = persist;
1036
	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1037
	return (0);
1038
}
1039
1040
int
1041
cmdline_symset(char *s)
1042
{
1043
	char	*sym, *val;
1044
	int	ret;
1045
	size_t	len;
1046
1047
	if ((val = strrchr(s, '=')) == NULL)
1048
		return (-1);
1049
1050
	len = (val - s) + 1;
1051
	if ((sym = malloc(len)) == NULL)
1052
		fatal("cmdline_symset: malloc");
1053
1054
	(void)strlcpy(sym, s, len);
1055
1056
	ret = symset(sym, val + 1, 1);
1057
	free(sym);
1058
1059
	return (ret);
1060
}
1061
1062
char *
1063
symget(const char *nam)
1064
{
1065
	struct sym	*sym;
1066
1067
	TAILQ_FOREACH(sym, &symhead, entry) {
1068
		if (strcmp(nam, sym->nam) == 0) {
1069
			sym->used = 1;
1070
			return (sym->val);
1071
		}
1072
	}
1073
	return (NULL);
1074
}
1075
1076
ssize_t
1077
parse_size(char *word, int64_t val)
1078
{
1079
	ssize_t		 size;
1080
	long long	 res;
1081
1082
	if (word != NULL) {
1083
		if (scan_scaled(word, &res) != 0) {
1084
			log_warn("invalid size: %s", word);
1085
			return (-1);
1086
		}
1087
		val = (int64_t)res;
1088
	}
1089
1090
	if (val < (1024 * 1024)) {
1091
		log_warnx("size must be at least one megabyte");
1092
		return (-1);
1093
	} else
1094
		size = val / 1024 / 1024;
1095
1096
	if ((size * 1024 * 1024) != val)
1097
		log_warnx("size rounded to %zd megabytes", size);
1098
1099
	return ((ssize_t)size);
1100
}
1101
1102
int
1103
parse_disk(char *word)
1104
{
1105
	if (vcp->vcp_ndisks >= VMM_MAX_DISKS_PER_VM) {
1106
		log_warnx("too many disks");
1107
		return (-1);
1108
	}
1109
1110
	if (strlcpy(vcp->vcp_disks[vcp->vcp_ndisks], word,
1111
	    VMM_MAX_PATH_DISK) >= VMM_MAX_PATH_DISK) {
1112
		log_warnx("disk path too long");
1113
		return (-1);
1114
	}
1115
1116
	vcp->vcp_ndisks++;
1117
1118
	return (0);
1119
}
1120
1121
int
1122
host(const char *str, struct address *h)
1123
{
1124
	struct addrinfo		 hints, *res;
1125
	int			 prefixlen;
1126
	char			*s, *p;
1127
	const char		*errstr;
1128
1129
	if ((s = strdup(str)) == NULL) {
1130
		log_warn("strdup");
1131
		goto fail;
1132
	}
1133
1134
	if ((p = strrchr(s, '/')) != NULL) {
1135
		*p++ = '\0';
1136
		prefixlen = strtonum(p, 0, 128, &errstr);
1137
		if (errstr) {
1138
			log_warnx("prefixlen is %s: %s", errstr, p);
1139
			goto fail;
1140
		}
1141
	} else
1142
		prefixlen = 128;
1143
1144
	memset(&hints, 0, sizeof(hints));
1145
	hints.ai_family = AF_UNSPEC;
1146
	hints.ai_flags = AI_NUMERICHOST;
1147
	if (getaddrinfo(s, NULL, &hints, &res) == 0) {
1148
		memset(h, 0, sizeof(*h));
1149
		memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
1150
		h->prefixlen = prefixlen;
1151
		freeaddrinfo(res);
1152
		free(s);
1153
		return (0);
1154
	}
1155
1156
 fail:
1157
	free(s);
1158
	return (-1);
1159
}