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

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