xref: /openbsd-src/usr.sbin/vmd/parse.y (revision 65bbee46cad7861cd5a570f338df9e976422e3ab)
1*65bbee46Sjsg /*	$OpenBSD: parse.y,v 1.71 2024/09/26 01:45:13 jsg Exp $	*/
2f01317bcSreyk 
3f01317bcSreyk /*
4789e0822Sreyk  * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
5f01317bcSreyk  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6f01317bcSreyk  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7f01317bcSreyk  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8f01317bcSreyk  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9f01317bcSreyk  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10f01317bcSreyk  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11f01317bcSreyk  *
12f01317bcSreyk  * Permission to use, copy, modify, and distribute this software for any
13f01317bcSreyk  * purpose with or without fee is hereby granted, provided that the above
14f01317bcSreyk  * copyright notice and this permission notice appear in all copies.
15f01317bcSreyk  *
16f01317bcSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17f01317bcSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18f01317bcSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19f01317bcSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20f01317bcSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21f01317bcSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22f01317bcSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23f01317bcSreyk  */
24f01317bcSreyk 
25f01317bcSreyk %{
26f01317bcSreyk #include <sys/types.h>
27f01317bcSreyk #include <sys/queue.h>
28789e0822Sreyk #include <sys/socket.h>
29f01317bcSreyk 
30ba66f564Sdv #include <dev/vmm/vmm.h>
31f01317bcSreyk 
322272e586Sdv #include <arpa/inet.h>
33789e0822Sreyk #include <net/if.h>
34789e0822Sreyk #include <netinet/in.h>
35789e0822Sreyk #include <netinet/if_ether.h>
36789e0822Sreyk 
37f94ca20eSmartijn #include <agentx.h>
38f01317bcSreyk #include <stdio.h>
39f01317bcSreyk #include <limits.h>
40f01317bcSreyk #include <stdarg.h>
41e2ceadc1Sreyk #include <unistd.h>
42f01317bcSreyk #include <ctype.h>
43f01317bcSreyk #include <netdb.h>
44f01317bcSreyk #include <util.h>
456d5856cfSreyk #include <errno.h>
46f01317bcSreyk #include <err.h>
47e2ceadc1Sreyk #include <fcntl.h>
48e5d5b350Sreyk #include <pwd.h>
49e5d5b350Sreyk #include <grp.h>
50f01317bcSreyk 
51f01317bcSreyk #include "vmd.h"
52f01317bcSreyk 
53f01317bcSreyk TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
54f01317bcSreyk static struct file {
55f01317bcSreyk 	TAILQ_ENTRY(file)	 entry;
56f01317bcSreyk 	FILE			*stream;
57f01317bcSreyk 	char			*name;
58f61f6eccSdenis 	size_t			 ungetpos;
59f61f6eccSdenis 	size_t			 ungetsize;
60f61f6eccSdenis 	u_char			*ungetbuf;
61f61f6eccSdenis 	int			 eof_reached;
62f01317bcSreyk 	int			 lineno;
63f01317bcSreyk 	int			 errors;
64f01317bcSreyk } *file, *topfile;
65f01317bcSreyk struct file	*pushfile(const char *, int);
66f01317bcSreyk int		 popfile(void);
67f01317bcSreyk int		 yyparse(void);
68f01317bcSreyk int		 yylex(void);
69f01317bcSreyk int		 yyerror(const char *, ...)
70f01317bcSreyk     __attribute__((__format__ (printf, 1, 2)))
71f01317bcSreyk     __attribute__((__nonnull__ (1)));
72f01317bcSreyk int		 kw_cmp(const void *, const void *);
73f01317bcSreyk int		 lookup(char *);
74f61f6eccSdenis int		 igetc(void);
75f01317bcSreyk int		 lgetc(int);
76f61f6eccSdenis void		 lungetc(int);
77f01317bcSreyk int		 findeol(void);
78f01317bcSreyk 
79f01317bcSreyk TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
80f01317bcSreyk struct sym {
81f01317bcSreyk 	TAILQ_ENTRY(sym)	 entry;
82f01317bcSreyk 	int			 used;
83f01317bcSreyk 	int			 persist;
84f01317bcSreyk 	char			*nam;
85f01317bcSreyk 	char			*val;
86f01317bcSreyk };
87f01317bcSreyk int		 symset(const char *, const char *, int);
88f01317bcSreyk char		*symget(const char *);
89f01317bcSreyk 
90f01317bcSreyk ssize_t		 parse_size(char *, int64_t);
91f224f92aSccardenas int		 parse_disk(char *, int);
92e2ceadc1Sreyk unsigned int	 parse_format(const char *);
93f01317bcSreyk 
94789e0822Sreyk static struct vmop_create_params vmc;
95789e0822Sreyk static struct vm_create_params	*vcp;
96789e0822Sreyk static struct vmd_switch	*vsw;
97b848b186Sdv static char			*kernel = NULL;
98789e0822Sreyk static char			 vsw_type[IF_NAMESIZE];
9973a98491Sdv static int			 vmc_disable;
10073a98491Sdv static size_t			 vmc_nnics;
101789e0822Sreyk static int			 errors;
102f01317bcSreyk extern struct vmd		*env;
103789e0822Sreyk extern const char		*vmd_descsw[];
104f01317bcSreyk 
105f01317bcSreyk typedef struct {
106f01317bcSreyk 	union {
107e14446e4Sreyk 		uint8_t		 lladdr[ETHER_ADDR_LEN];
108f01317bcSreyk 		int64_t		 number;
109f01317bcSreyk 		char		*string;
110e5d5b350Sreyk 		struct {
111e5d5b350Sreyk 			uid_t	 uid;
112e5d5b350Sreyk 			int64_t	 gid;
113e5d5b350Sreyk 		}		 owner;
114f01317bcSreyk 	} v;
115f01317bcSreyk 	int lineno;
116f01317bcSreyk } YYSTYPE;
117f01317bcSreyk 
118f01317bcSreyk %}
119f01317bcSreyk 
120f01317bcSreyk 
121f01317bcSreyk %token	INCLUDE ERROR
122f94ca20eSmartijn %token	ADD AGENTX ALLOW BOOT CDROM CONTEXT DEVICE DISABLE DISK DOWN ENABLE
123f94ca20eSmartijn %token	FORMAT GROUP
124c5ee7fb3Santon %token	INET6 INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NET NIFS OWNER
125de12a377Spd %token	PATH PREFIX RDOMAIN SIZE SOCKET SWITCH UP VM VMID STAGGERED START
126f4b47ae8Sbluhm %token  PARALLEL DELAY SEV
127f01317bcSreyk %token	<v.number>	NUMBER
128e47f0f66Sreyk %token	<v.string>	STRING
129e47f0f66Sreyk %type	<v.lladdr>	lladdr
130c5ee7fb3Santon %type	<v.number>	bootdevice
131f01317bcSreyk %type	<v.number>	disable
132f224f92aSccardenas %type	<v.number>	image_format
133470adcf5Sreyk %type	<v.number>	local
1342b2a5f0dSreyk %type	<v.number>	locked
135789e0822Sreyk %type	<v.number>	updown
136e5d5b350Sreyk %type	<v.owner>	owner_id
137e47f0f66Sreyk %type	<v.string>	optstring
138e47f0f66Sreyk %type	<v.string>	string
1396429e633Sreyk %type	<v.string>	vm_instance
140f4b47ae8Sbluhm %type	<v.number>	sev;
141f01317bcSreyk 
142f01317bcSreyk %%
143f01317bcSreyk 
144f01317bcSreyk grammar		: /* empty */
145f01317bcSreyk 		| grammar include '\n'
146f01317bcSreyk 		| grammar '\n'
147f01317bcSreyk 		| grammar varset '\n'
148c48cfcf4Sreyk 		| grammar main '\n'
149789e0822Sreyk 		| grammar switch '\n'
150789e0822Sreyk 		| grammar vm '\n'
151f01317bcSreyk 		| grammar error '\n'		{ file->errors++; }
152f01317bcSreyk 		;
153f01317bcSreyk 
154458ed070Sreyk include		: INCLUDE string		{
155f01317bcSreyk 			struct file	*nfile;
156f01317bcSreyk 
157f01317bcSreyk 			if ((nfile = pushfile($2, 0)) == NULL) {
158f01317bcSreyk 				yyerror("failed to include file %s", $2);
159f01317bcSreyk 				free($2);
160f01317bcSreyk 				YYERROR;
161f01317bcSreyk 			}
162f01317bcSreyk 			free($2);
163f01317bcSreyk 
164f01317bcSreyk 			file = nfile;
165f01317bcSreyk 			lungetc('\n');
166f01317bcSreyk 		}
167f01317bcSreyk 		;
168f01317bcSreyk 
169f01317bcSreyk varset		: STRING '=' STRING		{
1700c7b4ca6Sbenno 			char *s = $1;
1710c7b4ca6Sbenno 			while (*s++) {
1720c7b4ca6Sbenno 				if (isspace((unsigned char)*s)) {
1730c7b4ca6Sbenno 					yyerror("macro name cannot contain "
1740c7b4ca6Sbenno 					    "whitespace");
17516a0a906Skrw 					free($1);
17616a0a906Skrw 					free($3);
1770c7b4ca6Sbenno 					YYERROR;
1780c7b4ca6Sbenno 				}
1790c7b4ca6Sbenno 			}
180f01317bcSreyk 			if (symset($1, $3, 0) == -1)
181f01317bcSreyk 				fatalx("cannot store variable");
182f01317bcSreyk 			free($1);
183f01317bcSreyk 			free($3);
184f01317bcSreyk 		}
185f01317bcSreyk 		;
186f01317bcSreyk 
187723f86d2Sreyk main		: LOCAL INET6 {
188723f86d2Sreyk 			env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
189723f86d2Sreyk 		}
190723f86d2Sreyk 		| LOCAL INET6 PREFIX STRING {
1912272e586Sdv 			const char	*err;
192c48cfcf4Sreyk 
1932272e586Sdv 			if (parse_prefix6($4, &env->vmd_cfg.cfg_localprefix,
1942272e586Sdv 			    &err)) {
1952272e586Sdv 				yyerror("invalid local inet6 prefix: %s", err);
196723f86d2Sreyk 				YYERROR;
1972272e586Sdv 			} else {
198723f86d2Sreyk 				env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
199723f86d2Sreyk 				env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6;
2002272e586Sdv 			}
2012272e586Sdv 			free($4);
202723f86d2Sreyk 		}
203723f86d2Sreyk 		| LOCAL PREFIX STRING {
2042272e586Sdv 			const char	*err;
205723f86d2Sreyk 
2062272e586Sdv 			if (parse_prefix4($3, &env->vmd_cfg.cfg_localprefix,
2072272e586Sdv 			    &err)) {
2082272e586Sdv 				yyerror("invalid local prefix: %s", err);
209c48cfcf4Sreyk 				YYERROR;
210c48cfcf4Sreyk 			}
2112272e586Sdv 			free($3);
212c48cfcf4Sreyk 		}
2136cfffd57Sreyk 		| SOCKET OWNER owner_id {
2146cfffd57Sreyk 			env->vmd_ps.ps_csock.cs_uid = $3.uid;
2156cfffd57Sreyk 			env->vmd_ps.ps_csock.cs_gid = $3.gid == -1 ? 0 : $3.gid;
2166cfffd57Sreyk 		}
217f94ca20eSmartijn 		| AGENTX {
218f94ca20eSmartijn 			env->vmd_cfg.cfg_agentx.ax_enabled = 1;
219f94ca20eSmartijn 		} agentxopts {
220f94ca20eSmartijn 			if (env->vmd_cfg.cfg_agentx.ax_path[0] == '\0')
221f94ca20eSmartijn 				if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path,
222f94ca20eSmartijn 				    AGENTX_MASTER_PATH,
223f94ca20eSmartijn 				    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >=
224f94ca20eSmartijn 				    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) {
225f94ca20eSmartijn 					yyerror("invalid agentx path");
226f94ca20eSmartijn 					YYERROR;
227f94ca20eSmartijn 				}
228f94ca20eSmartijn 		}
229de12a377Spd 		| STAGGERED START PARALLEL NUMBER DELAY NUMBER {
230de12a377Spd 			env->vmd_cfg.cfg_flags |= VMD_CFG_STAGGERED_START;
231de12a377Spd 			env->vmd_cfg.delay.tv_sec = $6;
232de12a377Spd 			env->vmd_cfg.parallelism = $4;
233de12a377Spd 		}
234c48cfcf4Sreyk 		;
235c48cfcf4Sreyk 
236789e0822Sreyk switch		: SWITCH string			{
237789e0822Sreyk 			if ((vsw = calloc(1, sizeof(*vsw))) == NULL)
238789e0822Sreyk 				fatal("could not allocate switch");
239789e0822Sreyk 
240789e0822Sreyk 			vsw->sw_id = env->vmd_nswitches + 1;
241789e0822Sreyk 			vsw->sw_name = $2;
2422b2a5f0dSreyk 			vsw->sw_flags = VMIFF_UP;
243789e0822Sreyk 
24473a98491Sdv 			vmc_disable = 0;
245789e0822Sreyk 		} '{' optnl switch_opts_l '}'	{
246a123de80Smlarkin 			if (strnlen(vsw->sw_ifname,
247a123de80Smlarkin 			    sizeof(vsw->sw_ifname)) == 0) {
248d2de69e7Sreyk 				yyerror("switch \"%s\" "
249d2de69e7Sreyk 				    "is missing interface name",
250a123de80Smlarkin 				    vsw->sw_name);
251a123de80Smlarkin 				YYERROR;
252a123de80Smlarkin 			}
253a123de80Smlarkin 
25473a98491Sdv 			if (vmc_disable) {
255789e0822Sreyk 				log_debug("%s:%d: switch \"%s\""
256789e0822Sreyk 				    " skipped (disabled)",
257789e0822Sreyk 				    file->name, yylval.lineno, vsw->sw_name);
258789e0822Sreyk 			} else if (!env->vmd_noaction) {
259d2de69e7Sreyk 				TAILQ_INSERT_TAIL(env->vmd_switches,
260d2de69e7Sreyk 				    vsw, sw_entry);
261ea9c30d9Sedd 				env->vmd_nswitches++;
262ea9c30d9Sedd 				log_debug("%s:%d: switch \"%s\" registered",
263ea9c30d9Sedd 				    file->name, yylval.lineno, vsw->sw_name);
264789e0822Sreyk 			}
265789e0822Sreyk 		}
266789e0822Sreyk 		;
267789e0822Sreyk 
268789e0822Sreyk switch_opts_l	: switch_opts_l switch_opts nl
269789e0822Sreyk 		| switch_opts optnl
270789e0822Sreyk 		;
271789e0822Sreyk 
272789e0822Sreyk switch_opts	: disable			{
27373a98491Sdv 			vmc_disable = $1;
274789e0822Sreyk 		}
2754629ffa0Sreyk 		| GROUP string			{
2764629ffa0Sreyk 			if (priv_validgroup($2) == -1) {
2774629ffa0Sreyk 				yyerror("invalid group name: %s", $2);
2784629ffa0Sreyk 				free($2);
2794629ffa0Sreyk 				YYERROR;
2804629ffa0Sreyk 			}
2814629ffa0Sreyk 			vsw->sw_group = $2;
2824629ffa0Sreyk 		}
283789e0822Sreyk 		| INTERFACE string		{
284a123de80Smlarkin 			if (priv_getiftype($2, vsw_type, NULL) == -1 ||
285789e0822Sreyk 			    priv_findname(vsw_type, vmd_descsw) == -1) {
286789e0822Sreyk 				yyerror("invalid switch interface: %s", $2);
287789e0822Sreyk 				free($2);
288789e0822Sreyk 				YYERROR;
289789e0822Sreyk 			}
290789e0822Sreyk 
291789e0822Sreyk 			if (strlcpy(vsw->sw_ifname, $2,
292789e0822Sreyk 			    sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) {
293789e0822Sreyk 				yyerror("switch interface too long: %s", $2);
294789e0822Sreyk 				free($2);
295789e0822Sreyk 				YYERROR;
296789e0822Sreyk 			}
297789e0822Sreyk 			free($2);
298789e0822Sreyk 		}
2992b2a5f0dSreyk 		| LOCKED LLADDR			{
3002b2a5f0dSreyk 			vsw->sw_flags |= VMIFF_LOCKED;
3012b2a5f0dSreyk 		}
302f418e70cSreyk 		| RDOMAIN NUMBER		{
303f418e70cSreyk 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
304f418e70cSreyk 				yyerror("invalid rdomain: %lld", $2);
305f418e70cSreyk 				YYERROR;
306f418e70cSreyk 			}
307f418e70cSreyk 			vsw->sw_flags |= VMIFF_RDOMAIN;
308f418e70cSreyk 			vsw->sw_rdomain = $2;
309f418e70cSreyk 		}
310789e0822Sreyk 		| updown			{
311789e0822Sreyk 			if ($1)
3122b2a5f0dSreyk 				vsw->sw_flags |= VMIFF_UP;
313789e0822Sreyk 			else
3142b2a5f0dSreyk 				vsw->sw_flags &= ~VMIFF_UP;
315789e0822Sreyk 		}
316789e0822Sreyk 		;
317789e0822Sreyk 
3186429e633Sreyk vm		: VM string vm_instance		{
319789e0822Sreyk 			unsigned int	 i;
3206429e633Sreyk 			char		*name;
321789e0822Sreyk 
322789e0822Sreyk 			memset(&vmc, 0, sizeof(vmc));
323b848b186Sdv 			vmc.vmc_kernel = -1;
324b848b186Sdv 
325789e0822Sreyk 			vcp = &vmc.vmc_params;
32673a98491Sdv 			vmc_disable = 0;
32773a98491Sdv 			vmc_nnics = 0;
328789e0822Sreyk 
3296429e633Sreyk 			if ($3 != NULL) {
3306429e633Sreyk 				/* This is an instance of a pre-configured VM */
3316429e633Sreyk 				if (strlcpy(vmc.vmc_instance, $2,
3326429e633Sreyk 				    sizeof(vmc.vmc_instance)) >=
3336429e633Sreyk 				    sizeof(vmc.vmc_instance)) {
3346429e633Sreyk 					yyerror("vm %s name too long", $2);
3356429e633Sreyk 					free($2);
3366429e633Sreyk 					free($3);
3376429e633Sreyk 					YYERROR;
3386429e633Sreyk 				}
3396429e633Sreyk 
3406429e633Sreyk 				free($2);
3416429e633Sreyk 				name = $3;
3426429e633Sreyk 				vmc.vmc_flags |= VMOP_CREATE_INSTANCE;
3436429e633Sreyk 			} else
3446429e633Sreyk 				name = $2;
3456429e633Sreyk 
346d489aa7eSdv 			for (i = 0; i < VM_MAX_NICS_PER_VM; i++) {
347789e0822Sreyk 				/* Set the interface to UP by default */
348789e0822Sreyk 				vmc.vmc_ifflags[i] |= IFF_UP;
349789e0822Sreyk 			}
350789e0822Sreyk 
3516429e633Sreyk 			if (strlcpy(vcp->vcp_name, name,
3526429e633Sreyk 			    sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) {
353f01317bcSreyk 				yyerror("vm name too long");
3546429e633Sreyk 				free($2);
3556429e633Sreyk 				free($3);
356f01317bcSreyk 				YYERROR;
357f01317bcSreyk 			}
358e5d5b350Sreyk 
359e5d5b350Sreyk 			/* set default user/group permissions */
360476d73d1Sreyk 			vmc.vmc_owner.uid = 0;
361476d73d1Sreyk 			vmc.vmc_owner.gid = -1;
362f01317bcSreyk 		} '{' optnl vm_opts_l '}'	{
3636429e633Sreyk 			struct vmd_vm	*vm;
36438e15a96Sreyk 			int		 ret;
36538e15a96Sreyk 
366789e0822Sreyk 			/* configured interfaces vs. number of interfaces */
36773a98491Sdv 			if (vmc_nnics > vmc.vmc_nnics)
36873a98491Sdv 				vmc.vmc_nnics = vmc_nnics;
369789e0822Sreyk 
370371c1bb3Sedd 			if (!env->vmd_noaction) {
371e5d5b350Sreyk 				ret = vm_register(&env->vmd_ps, &vmc,
372e5d5b350Sreyk 				    &vm, 0, 0);
3736d5856cfSreyk 				if (ret == -1 && errno == EALREADY) {
3746d5856cfSreyk 					log_debug("%s:%d: vm \"%s\""
375ea9c30d9Sedd 					    " skipped (%s)",
3766d5856cfSreyk 					    file->name, yylval.lineno,
37719700f36Sjasper 					    vcp->vcp_name,
37819700f36Sjasper 					    (vm->vm_state & VM_STATE_RUNNING) ?
379ea9c30d9Sedd 					    "running" : "already exists");
3806d5856cfSreyk 				} else if (ret == -1) {
3816429e633Sreyk 					yyerror("vm \"%s\" failed: %s",
3826429e633Sreyk 					    vcp->vcp_name, strerror(errno));
383f01317bcSreyk 					YYERROR;
38438e15a96Sreyk 				} else {
38573a98491Sdv 					if (vmc_disable)
38619700f36Sjasper 						vm->vm_state |= VM_STATE_DISABLED;
387de12a377Spd 					else
388de12a377Spd 						vm->vm_state |= VM_STATE_WAITING;
389d2de69e7Sreyk 					log_debug("%s:%d: vm \"%s\" "
390d2de69e7Sreyk 					    "registered (%s)",
39138e15a96Sreyk 					    file->name, yylval.lineno,
392371c1bb3Sedd 					    vcp->vcp_name,
39373a98491Sdv 					    vmc_disable ?
394d2de69e7Sreyk 					    "disabled" : "enabled");
39538e15a96Sreyk 				}
396b848b186Sdv 				vm->vm_kernel_path = kernel;
397b848b186Sdv 				vm->vm_kernel = -1;
39856502f88Sedd 				vm->vm_from_config = 1;
399f01317bcSreyk 			}
400b848b186Sdv 			kernel = NULL;
401f01317bcSreyk 		}
402f01317bcSreyk 		;
403f01317bcSreyk 
4046429e633Sreyk vm_instance	: /* empty */			{ $$ = NULL; }
4056429e633Sreyk 		| INSTANCE string		{ $$ = $2; }
4066429e633Sreyk 		;
4076429e633Sreyk 
408f01317bcSreyk vm_opts_l	: vm_opts_l vm_opts nl
409f01317bcSreyk 		| vm_opts optnl
410f01317bcSreyk 		;
411f01317bcSreyk 
412f01317bcSreyk vm_opts		: disable			{
41373a98491Sdv 			vmc_disable = $1;
414f01317bcSreyk 		}
415f4b47ae8Sbluhm 		| sev				{
416f4b47ae8Sbluhm 			vcp->vcp_sev = 1;
417f4b47ae8Sbluhm 		}
418f224f92aSccardenas 		| DISK string image_format	{
419f224f92aSccardenas 			if (parse_disk($2, $3) != 0) {
420f01317bcSreyk 				yyerror("failed to parse disks: %s", $2);
421f01317bcSreyk 				free($2);
422f01317bcSreyk 				YYERROR;
423f01317bcSreyk 			}
424f01317bcSreyk 			free($2);
425619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_DISK;
426f01317bcSreyk 		}
427470adcf5Sreyk 		| local INTERFACE optstring iface_opts_o {
428789e0822Sreyk 			unsigned int	i;
429789e0822Sreyk 			char		type[IF_NAMESIZE];
430789e0822Sreyk 
43173a98491Sdv 			i = vmc_nnics;
43273a98491Sdv 			if (++vmc_nnics > VM_MAX_NICS_PER_VM) {
43373a98491Sdv 				yyerror("too many interfaces: %zu", vmc_nnics);
434470adcf5Sreyk 				free($3);
435f01317bcSreyk 				YYERROR;
436f01317bcSreyk 			}
437789e0822Sreyk 
438470adcf5Sreyk 			if ($1)
439470adcf5Sreyk 				vmc.vmc_ifflags[i] |= VMIFF_LOCAL;
440470adcf5Sreyk 			if ($3 != NULL) {
441470adcf5Sreyk 				if (strcmp($3, "tap") != 0 &&
442470adcf5Sreyk 				    (priv_getiftype($3, type, NULL) == -1 ||
443789e0822Sreyk 				    strcmp(type, "tap") != 0)) {
444470adcf5Sreyk 					yyerror("invalid interface: %s", $3);
445470adcf5Sreyk 					free($3);
446789e0822Sreyk 					YYERROR;
447789e0822Sreyk 				}
448789e0822Sreyk 
449470adcf5Sreyk 				if (strlcpy(vmc.vmc_ifnames[i], $3,
450789e0822Sreyk 				    sizeof(vmc.vmc_ifnames[i])) >=
451789e0822Sreyk 				    sizeof(vmc.vmc_ifnames[i])) {
452789e0822Sreyk 					yyerror("interface name too long: %s",
453470adcf5Sreyk 					    $3);
454470adcf5Sreyk 					free($3);
455789e0822Sreyk 					YYERROR;
456789e0822Sreyk 				}
457789e0822Sreyk 			}
458470adcf5Sreyk 			free($3);
459619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
460789e0822Sreyk 		}
4616b03ca83Sreyk 		| BOOT string			{
4624a91d1ffSreyk 			char	 path[PATH_MAX];
4634a91d1ffSreyk 
464b848b186Sdv 			if (kernel != NULL) {
465789e0822Sreyk 				yyerror("kernel specified more than once");
466789e0822Sreyk 				free($2);
467789e0822Sreyk 				YYERROR;
468789e0822Sreyk 
469789e0822Sreyk 			}
4704a91d1ffSreyk 			if (realpath($2, path) == NULL) {
47129d9fdbaSbluhm 				yyerror("kernel path not found: %s",
47229d9fdbaSbluhm 				    strerror(errno));
473f01317bcSreyk 				free($2);
474f01317bcSreyk 				YYERROR;
475f01317bcSreyk 			}
476f01317bcSreyk 			free($2);
4773ac1122dSdv 			kernel = malloc(sizeof(path));
4783ac1122dSdv 			if (kernel == NULL)
4793ac1122dSdv 				yyerror("malloc");
4803ac1122dSdv 			memcpy(kernel, &path, sizeof(path));
481619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_KERNEL;
482f01317bcSreyk 		}
483c5ee7fb3Santon 		| BOOT DEVICE bootdevice	{
484c5ee7fb3Santon 			vmc.vmc_bootdevice = $3;
485c5ee7fb3Santon 		}
48695ab188fSccardenas 		| CDROM string			{
48773a98491Sdv 			if (vmc.vmc_cdrom[0] != '\0') {
48895ab188fSccardenas 				yyerror("cdrom specified more than once");
48995ab188fSccardenas 				free($2);
49095ab188fSccardenas 				YYERROR;
49195ab188fSccardenas 
49295ab188fSccardenas 			}
49373a98491Sdv 			if (strlcpy(vmc.vmc_cdrom, $2,
49473a98491Sdv 			    sizeof(vmc.vmc_cdrom)) >=
49573a98491Sdv 			    sizeof(vmc.vmc_cdrom)) {
49695ab188fSccardenas 				yyerror("cdrom name too long");
49795ab188fSccardenas 				free($2);
49895ab188fSccardenas 				YYERROR;
49995ab188fSccardenas 			}
50095ab188fSccardenas 			free($2);
50195ab188fSccardenas 			vmc.vmc_flags |= VMOP_CREATE_CDROM;
50295ab188fSccardenas 		}
503f01317bcSreyk 		| NIFS NUMBER			{
50473a98491Sdv 			if (vmc.vmc_nnics != 0) {
505f01317bcSreyk 				yyerror("interfaces specified more than once");
506f01317bcSreyk 				YYERROR;
507f01317bcSreyk 			}
508d489aa7eSdv 			if ($2 < 0 || $2 > VM_MAX_NICS_PER_VM) {
509f01317bcSreyk 				yyerror("too many interfaces: %lld", $2);
510f01317bcSreyk 				YYERROR;
511f01317bcSreyk 			}
51273a98491Sdv 			vmc.vmc_nnics = (size_t)$2;
513619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_NETWORK;
514f01317bcSreyk 		}
515f01317bcSreyk 		| MEMORY NUMBER			{
516f01317bcSreyk 			ssize_t	 res;
517789e0822Sreyk 			if (vcp->vcp_memranges[0].vmr_size != 0) {
518f01317bcSreyk 				yyerror("memory specified more than once");
519f01317bcSreyk 				YYERROR;
520f01317bcSreyk 			}
521f01317bcSreyk 			if ((res = parse_size(NULL, $2)) == -1) {
522f01317bcSreyk 				yyerror("failed to parse size: %lld", $2);
523f01317bcSreyk 				YYERROR;
524f01317bcSreyk 			}
525789e0822Sreyk 			vcp->vcp_memranges[0].vmr_size = (size_t)res;
526619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
527f01317bcSreyk 		}
528f01317bcSreyk 		| MEMORY STRING			{
529f01317bcSreyk 			ssize_t	 res;
530789e0822Sreyk 			if (vcp->vcp_memranges[0].vmr_size != 0) {
531f01317bcSreyk 				yyerror("argument specified more than once");
532f01317bcSreyk 				free($2);
533f01317bcSreyk 				YYERROR;
534f01317bcSreyk 			}
535f01317bcSreyk 			if ((res = parse_size($2, 0)) == -1) {
536f01317bcSreyk 				yyerror("failed to parse size: %s", $2);
537f01317bcSreyk 				free($2);
538f01317bcSreyk 				YYERROR;
539f01317bcSreyk 			}
540789e0822Sreyk 			vcp->vcp_memranges[0].vmr_size = (size_t)res;
541619bf29aSreyk 			vmc.vmc_flags |= VMOP_CREATE_MEMORY;
542f01317bcSreyk 		}
543e5d5b350Sreyk 		| OWNER owner_id		{
544476d73d1Sreyk 			vmc.vmc_owner.uid = $2.uid;
545476d73d1Sreyk 			vmc.vmc_owner.gid = $2.gid;
546476d73d1Sreyk 		}
547476d73d1Sreyk 		| instance
548476d73d1Sreyk 		;
549476d73d1Sreyk 
550476d73d1Sreyk instance	: ALLOW INSTANCE '{' optnl instance_l '}'
551476d73d1Sreyk 		| ALLOW INSTANCE instance_flags
552476d73d1Sreyk 		;
553476d73d1Sreyk 
554476d73d1Sreyk instance_l	: instance_flags optcommanl instance_l
555476d73d1Sreyk 		| instance_flags optnl
556476d73d1Sreyk 		;
557476d73d1Sreyk 
558476d73d1Sreyk instance_flags	: BOOT		{ vmc.vmc_insflags |= VMOP_CREATE_KERNEL; }
559476d73d1Sreyk 		| MEMORY	{ vmc.vmc_insflags |= VMOP_CREATE_MEMORY; }
560476d73d1Sreyk 		| INTERFACE	{ vmc.vmc_insflags |= VMOP_CREATE_NETWORK; }
561476d73d1Sreyk 		| DISK		{ vmc.vmc_insflags |= VMOP_CREATE_DISK; }
562476d73d1Sreyk 		| CDROM		{ vmc.vmc_insflags |= VMOP_CREATE_CDROM; }
563476d73d1Sreyk 		| INSTANCE	{ vmc.vmc_insflags |= VMOP_CREATE_INSTANCE; }
564476d73d1Sreyk 		| OWNER owner_id {
565476d73d1Sreyk 			vmc.vmc_insowner.uid = $2.uid;
566476d73d1Sreyk 			vmc.vmc_insowner.gid = $2.gid;
567e5d5b350Sreyk 		}
568e5d5b350Sreyk 		;
569e5d5b350Sreyk 
5700389d116Skn owner_id	: NUMBER		{
571e5d5b350Sreyk 			$$.uid = $1;
572e5d5b350Sreyk 			$$.gid = -1;
573e5d5b350Sreyk 		}
574e5d5b350Sreyk 		| STRING		{
575e5d5b350Sreyk 			char		*user, *group;
576e5d5b350Sreyk 			struct passwd	*pw;
577e5d5b350Sreyk 			struct group	*gr;
578e5d5b350Sreyk 
579e5d5b350Sreyk 			$$.uid = 0;
580e5d5b350Sreyk 			$$.gid = -1;
581e5d5b350Sreyk 
582e5d5b350Sreyk 			user = $1;
583e5d5b350Sreyk 			if ((group = strchr(user, ':')) != NULL) {
584e5d5b350Sreyk 				if (group == user)
585e5d5b350Sreyk 					user = NULL;
586e5d5b350Sreyk 				*group++ = '\0';
587e5d5b350Sreyk 			}
588e5d5b350Sreyk 
589e5d5b350Sreyk 			if (user != NULL && *user) {
590e5d5b350Sreyk 				if ((pw = getpwnam(user)) == NULL) {
591e5d5b350Sreyk 					yyerror("failed to get user: %s",
592e5d5b350Sreyk 					    user);
593e5d5b350Sreyk 					free($1);
594e5d5b350Sreyk 					YYERROR;
595e5d5b350Sreyk 				}
596e5d5b350Sreyk 				$$.uid = pw->pw_uid;
597e5d5b350Sreyk 			}
598e5d5b350Sreyk 
599e5d5b350Sreyk 			if (group != NULL && *group) {
600e5d5b350Sreyk 				if ((gr = getgrnam(group)) == NULL) {
601e5d5b350Sreyk 					yyerror("failed to get group: %s",
602e5d5b350Sreyk 					    group);
603e5d5b350Sreyk 					free($1);
604e5d5b350Sreyk 					YYERROR;
605e5d5b350Sreyk 				}
606e5d5b350Sreyk 				$$.gid = gr->gr_gid;
607e5d5b350Sreyk 			}
608e5d5b350Sreyk 
609e5d5b350Sreyk 			free($1);
610e5d5b350Sreyk 		}
611f01317bcSreyk 		;
612f01317bcSreyk 
613f94ca20eSmartijn agentxopt	: CONTEXT STRING {
614f94ca20eSmartijn 			if (strlcpy(env->vmd_cfg.cfg_agentx.ax_context, $2,
615f94ca20eSmartijn 			    sizeof(env->vmd_cfg.cfg_agentx.ax_context)) >=
616f94ca20eSmartijn 			    sizeof(env->vmd_cfg.cfg_agentx.ax_context)) {
617f94ca20eSmartijn 				yyerror("agentx context too large");
618f94ca20eSmartijn 				free($2);
619f94ca20eSmartijn 				YYERROR;
620f94ca20eSmartijn 			}
621f94ca20eSmartijn 			free($2);
622f94ca20eSmartijn 		}
623f94ca20eSmartijn 		| PATH STRING {
624f94ca20eSmartijn 			if (strlcpy(env->vmd_cfg.cfg_agentx.ax_path, $2,
625f94ca20eSmartijn 			    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) >=
626f94ca20eSmartijn 			    sizeof(env->vmd_cfg.cfg_agentx.ax_path)) {
627f94ca20eSmartijn 				yyerror("agentx path too large");
628f94ca20eSmartijn 				free($2);
629f94ca20eSmartijn 				YYERROR;
630f94ca20eSmartijn 			}
631f94ca20eSmartijn 			free($2);
632f94ca20eSmartijn 			if (env->vmd_cfg.cfg_agentx.ax_path[0] != '/') {
633f94ca20eSmartijn 				yyerror("agentx path is not absolute");
634f94ca20eSmartijn 				YYERROR;
635f94ca20eSmartijn 			}
636f94ca20eSmartijn 		}
637f94ca20eSmartijn 
638f94ca20eSmartijn agentxopts	: /* none */
639f94ca20eSmartijn 		| agentxopts agentxopt
640f94ca20eSmartijn 		;
641f94ca20eSmartijn 
642f224f92aSccardenas image_format	: /* none 	*/	{
643e2ceadc1Sreyk 			$$ = 0;
644f224f92aSccardenas 		}
645f224f92aSccardenas 	     	| FORMAT string		{
646e2ceadc1Sreyk 			if (($$ = parse_format($2)) == 0) {
647f224f92aSccardenas 				yyerror("unrecognized disk format %s", $2);
648f224f92aSccardenas 				free($2);
649f224f92aSccardenas 				YYERROR;
650f224f92aSccardenas 			}
651f224f92aSccardenas 		}
652f224f92aSccardenas 		;
653f224f92aSccardenas 
654789e0822Sreyk iface_opts_o	: '{' optnl iface_opts_l '}'
6553e9d8149Sreyk 		| iface_opts_c
656789e0822Sreyk 		| /* empty */
657789e0822Sreyk 		;
658789e0822Sreyk 
659789e0822Sreyk iface_opts_l	: iface_opts_l iface_opts optnl
660789e0822Sreyk 		| iface_opts optnl
661789e0822Sreyk 		;
662789e0822Sreyk 
6633e9d8149Sreyk iface_opts_c	: iface_opts_c iface_opts optcomma
6643e9d8149Sreyk 		| iface_opts
6653e9d8149Sreyk 		;
6663e9d8149Sreyk 
667789e0822Sreyk iface_opts	: SWITCH string			{
66873a98491Sdv 			unsigned int	i = vmc_nnics;
669789e0822Sreyk 
670789e0822Sreyk 			/* No need to check if the switch exists */
671789e0822Sreyk 			if (strlcpy(vmc.vmc_ifswitch[i], $2,
672789e0822Sreyk 			    sizeof(vmc.vmc_ifswitch[i])) >=
673789e0822Sreyk 			    sizeof(vmc.vmc_ifswitch[i])) {
674789e0822Sreyk 				yyerror("switch name too long: %s", $2);
675789e0822Sreyk 				free($2);
676789e0822Sreyk 				YYERROR;
677789e0822Sreyk 			}
678789e0822Sreyk 			free($2);
679789e0822Sreyk 		}
6802b519c1fSreyk 		| GROUP string			{
68173a98491Sdv 			unsigned int	i = vmc_nnics;
6822b519c1fSreyk 
6832b519c1fSreyk 			if (priv_validgroup($2) == -1) {
6842b519c1fSreyk 				yyerror("invalid group name: %s", $2);
6852b519c1fSreyk 				free($2);
6862b519c1fSreyk 				YYERROR;
6872b519c1fSreyk 			}
6882b519c1fSreyk 
6892b519c1fSreyk 			/* No need to check if the group exists */
6902b519c1fSreyk 			(void)strlcpy(vmc.vmc_ifgroup[i], $2,
6912b519c1fSreyk 			    sizeof(vmc.vmc_ifgroup[i]));
6922b519c1fSreyk 			free($2);
6932b519c1fSreyk 		}
6942b2a5f0dSreyk 		| locked LLADDR lladdr		{
6952b2a5f0dSreyk 			if ($1)
69673a98491Sdv 				vmc.vmc_ifflags[vmc_nnics] |= VMIFF_LOCKED;
69773a98491Sdv 			memcpy(vmc.vmc_macs[vmc_nnics], $3, ETHER_ADDR_LEN);
698789e0822Sreyk 		}
699f418e70cSreyk 		| RDOMAIN NUMBER		{
700f418e70cSreyk 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
701f418e70cSreyk 				yyerror("invalid rdomain: %lld", $2);
702f418e70cSreyk 				YYERROR;
703f418e70cSreyk 			}
70473a98491Sdv 			vmc.vmc_ifflags[vmc_nnics] |= VMIFF_RDOMAIN;
70573a98491Sdv 			vmc.vmc_ifrdomain[vmc_nnics] = $2;
706f418e70cSreyk 		}
707789e0822Sreyk 		| updown			{
708789e0822Sreyk 			if ($1)
70973a98491Sdv 				vmc.vmc_ifflags[vmc_nnics] |= VMIFF_UP;
710789e0822Sreyk 			else
71173a98491Sdv 				vmc.vmc_ifflags[vmc_nnics] &= ~VMIFF_UP;
712789e0822Sreyk 		}
713789e0822Sreyk 		;
714789e0822Sreyk 
715789e0822Sreyk optstring	: STRING			{ $$ = $1; }
716789e0822Sreyk 		| /* empty */			{ $$ = NULL; }
717789e0822Sreyk 		;
718789e0822Sreyk 
719458ed070Sreyk string		: STRING string			{
720458ed070Sreyk 			if (asprintf(&$$, "%s%s", $1, $2) == -1)
721458ed070Sreyk 				fatal("asprintf string");
722458ed070Sreyk 			free($1);
723458ed070Sreyk 			free($2);
724458ed070Sreyk 		}
725458ed070Sreyk 		| STRING
726458ed070Sreyk 		;
727458ed070Sreyk 
728789e0822Sreyk lladdr		: STRING			{
729789e0822Sreyk 			struct ether_addr *ea;
730789e0822Sreyk 
731789e0822Sreyk 			if ((ea = ether_aton($1)) == NULL) {
732789e0822Sreyk 				yyerror("invalid address: %s\n", $1);
733789e0822Sreyk 				free($1);
734789e0822Sreyk 				YYERROR;
735789e0822Sreyk 			}
736789e0822Sreyk 			free($1);
737789e0822Sreyk 
738789e0822Sreyk 			memcpy($$, ea, ETHER_ADDR_LEN);
739789e0822Sreyk 		}
7406153f04aSdv 		| /* empty */ {
7416153f04aSdv 			memset($$, 0, ETHER_ADDR_LEN);
7426153f04aSdv 		}
743789e0822Sreyk 		;
744789e0822Sreyk 
745470adcf5Sreyk local		: /* empty */			{ $$ = 0; }
746470adcf5Sreyk 		| LOCAL				{ $$ = 1; }
747470adcf5Sreyk 		;
748470adcf5Sreyk 
7492b2a5f0dSreyk locked		: /* empty */			{ $$ = 0; }
7502b2a5f0dSreyk 		| LOCKED			{ $$ = 1; }
7512b2a5f0dSreyk 		;
7522b2a5f0dSreyk 
753789e0822Sreyk updown		: UP				{ $$ = 1; }
754789e0822Sreyk 		| DOWN				{ $$ = 0; }
755789e0822Sreyk 		;
756789e0822Sreyk 
757f01317bcSreyk disable		: ENABLE			{ $$ = 0; }
758f01317bcSreyk 		| DISABLE			{ $$ = 1; }
759f01317bcSreyk 		;
760f01317bcSreyk 
761f4b47ae8Sbluhm sev		: SEV				{ $$ = 1; }
762f4b47ae8Sbluhm 		;
763f4b47ae8Sbluhm 
764c5ee7fb3Santon bootdevice	: CDROM				{ $$ = VMBOOTDEV_CDROM; }
765c5ee7fb3Santon 		| DISK				{ $$ = VMBOOTDEV_DISK; }
766c5ee7fb3Santon 		| NET				{ $$ = VMBOOTDEV_NET; }
767c5ee7fb3Santon 		;
768c5ee7fb3Santon 
7693e9d8149Sreyk optcomma	: ','
7703e9d8149Sreyk 		|
7713e9d8149Sreyk 		;
7723e9d8149Sreyk 
773f01317bcSreyk optnl		: '\n' optnl
774f01317bcSreyk 		|
775f01317bcSreyk 		;
776f01317bcSreyk 
777476d73d1Sreyk optcommanl	: ',' optnl
778476d73d1Sreyk 		| nl
779476d73d1Sreyk 		;
780476d73d1Sreyk 
781f01317bcSreyk nl		: '\n' optnl
782f01317bcSreyk 		;
783f01317bcSreyk 
784f01317bcSreyk %%
785f01317bcSreyk 
786f01317bcSreyk struct keywords {
787f01317bcSreyk 	const char	*k_name;
788f01317bcSreyk 	int		 k_val;
789f01317bcSreyk };
790f01317bcSreyk 
791f01317bcSreyk int
792f01317bcSreyk yyerror(const char *fmt, ...)
793f01317bcSreyk {
794f01317bcSreyk 	va_list		 ap;
795f01317bcSreyk 	char		*msg;
796f01317bcSreyk 
797f01317bcSreyk 	file->errors++;
798f01317bcSreyk 	va_start(ap, fmt);
799f01317bcSreyk 	if (vasprintf(&msg, fmt, ap) == -1)
800f01317bcSreyk 		fatal("yyerror vasprintf");
801f01317bcSreyk 	va_end(ap);
8021dbf51d9Sreyk 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
803f01317bcSreyk 	free(msg);
804f01317bcSreyk 	return (0);
805f01317bcSreyk }
806f01317bcSreyk 
807f01317bcSreyk int
808f01317bcSreyk kw_cmp(const void *k, const void *e)
809f01317bcSreyk {
810f01317bcSreyk 	return (strcmp(k, ((const struct keywords *)e)->k_name));
811f01317bcSreyk }
812f01317bcSreyk 
813f01317bcSreyk int
814f01317bcSreyk lookup(char *s)
815f01317bcSreyk {
816f01317bcSreyk 	/* this has to be sorted always */
817f01317bcSreyk 	static const struct keywords keywords[] = {
818789e0822Sreyk 		{ "add",		ADD },
819f94ca20eSmartijn 		{ "agentx",		AGENTX },
820476d73d1Sreyk 		{ "allow",		ALLOW },
8216b03ca83Sreyk 		{ "boot",		BOOT },
82295ab188fSccardenas 		{ "cdrom",		CDROM },
823f94ca20eSmartijn 		{ "context",		CONTEXT},
824de12a377Spd 		{ "delay",		DELAY },
825c5ee7fb3Santon 		{ "device",		DEVICE },
826f01317bcSreyk 		{ "disable",		DISABLE },
827f01317bcSreyk 		{ "disk",		DISK },
828789e0822Sreyk 		{ "down",		DOWN },
829f01317bcSreyk 		{ "enable",		ENABLE },
830f224f92aSccardenas 		{ "format",		FORMAT },
8312b519c1fSreyk 		{ "group",		GROUP },
832f01317bcSreyk 		{ "id",			VMID },
833f01317bcSreyk 		{ "include",		INCLUDE },
834723f86d2Sreyk 		{ "inet6",		INET6 },
8356429e633Sreyk 		{ "instance",		INSTANCE },
836789e0822Sreyk 		{ "interface",		INTERFACE },
837f01317bcSreyk 		{ "interfaces",		NIFS },
838789e0822Sreyk 		{ "lladdr",		LLADDR },
839470adcf5Sreyk 		{ "local",		LOCAL },
8402b2a5f0dSreyk 		{ "locked",		LOCKED },
841f01317bcSreyk 		{ "memory",		MEMORY },
842c5ee7fb3Santon 		{ "net",		NET },
843e5d5b350Sreyk 		{ "owner",		OWNER },
844de12a377Spd 		{ "parallel",		PARALLEL },
845f94ca20eSmartijn 		{ "path",		PATH },
846c48cfcf4Sreyk 		{ "prefix",		PREFIX },
847f418e70cSreyk 		{ "rdomain",		RDOMAIN },
848f4b47ae8Sbluhm 		{ "sev",		SEV },
849f01317bcSreyk 		{ "size",		SIZE },
8506cfffd57Sreyk 		{ "socket",		SOCKET },
851de12a377Spd 		{ "staggered",		STAGGERED },
852de12a377Spd 		{ "start",		START  },
853789e0822Sreyk 		{ "switch",		SWITCH },
854789e0822Sreyk 		{ "up",			UP },
855f01317bcSreyk 		{ "vm",			VM }
856f01317bcSreyk 	};
857f01317bcSreyk 	const struct keywords	*p;
858f01317bcSreyk 
859f01317bcSreyk 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
860f01317bcSreyk 	    sizeof(keywords[0]), kw_cmp);
861f01317bcSreyk 
862f01317bcSreyk 	if (p)
863f01317bcSreyk 		return (p->k_val);
864f01317bcSreyk 	else
865f01317bcSreyk 		return (STRING);
866f01317bcSreyk }
867f01317bcSreyk 
868f61f6eccSdenis #define START_EXPAND	1
869f61f6eccSdenis #define DONE_EXPAND	2
870f01317bcSreyk 
871f61f6eccSdenis static int	expanding;
872f61f6eccSdenis 
873f61f6eccSdenis int
874f61f6eccSdenis igetc(void)
875f61f6eccSdenis {
876f61f6eccSdenis 	int	c;
877f61f6eccSdenis 
878f61f6eccSdenis 	while (1) {
879f61f6eccSdenis 		if (file->ungetpos > 0)
880f61f6eccSdenis 			c = file->ungetbuf[--file->ungetpos];
881f61f6eccSdenis 		else
882f61f6eccSdenis 			c = getc(file->stream);
883f61f6eccSdenis 
884f61f6eccSdenis 		if (c == START_EXPAND)
885f61f6eccSdenis 			expanding = 1;
886f61f6eccSdenis 		else if (c == DONE_EXPAND)
887f61f6eccSdenis 			expanding = 0;
888f61f6eccSdenis 		else
889f61f6eccSdenis 			break;
890f61f6eccSdenis 	}
891f61f6eccSdenis 	return (c);
892f61f6eccSdenis }
893f01317bcSreyk 
894f01317bcSreyk int
895f01317bcSreyk lgetc(int quotec)
896f01317bcSreyk {
897f01317bcSreyk 	int		c, next;
898f01317bcSreyk 
899f01317bcSreyk 	if (quotec) {
900f61f6eccSdenis 		if ((c = igetc()) == EOF) {
901f01317bcSreyk 			yyerror("reached end of file while parsing "
902f01317bcSreyk 			    "quoted string");
903f01317bcSreyk 			if (file == topfile || popfile() == EOF)
904f01317bcSreyk 				return (EOF);
905f01317bcSreyk 			return (quotec);
906f01317bcSreyk 		}
907f01317bcSreyk 		return (c);
908f01317bcSreyk 	}
909f01317bcSreyk 
910f61f6eccSdenis 	while ((c = igetc()) == '\\') {
911f61f6eccSdenis 		next = igetc();
912f01317bcSreyk 		if (next != '\n') {
913f01317bcSreyk 			c = next;
914f01317bcSreyk 			break;
915f01317bcSreyk 		}
916f01317bcSreyk 		yylval.lineno = file->lineno;
917f01317bcSreyk 		file->lineno++;
918f01317bcSreyk 	}
919f01317bcSreyk 	if (c == '\t' || c == ' ') {
920f01317bcSreyk 		/* Compress blanks to a single space. */
921f01317bcSreyk 		do {
922f01317bcSreyk 			c = getc(file->stream);
923f01317bcSreyk 		} while (c == '\t' || c == ' ');
924f01317bcSreyk 		ungetc(c, file->stream);
925f01317bcSreyk 		c = ' ';
926f01317bcSreyk 	}
927f01317bcSreyk 
928f61f6eccSdenis 	if (c == EOF) {
929f61f6eccSdenis 		/*
930f61f6eccSdenis 		 * Fake EOL when hit EOF for the first time. This gets line
931f61f6eccSdenis 		 * count right if last line in included file is syntactically
932f61f6eccSdenis 		 * invalid and has no newline.
933f61f6eccSdenis 		 */
934f61f6eccSdenis 		if (file->eof_reached == 0) {
935f61f6eccSdenis 			file->eof_reached = 1;
936f61f6eccSdenis 			return ('\n');
937f61f6eccSdenis 		}
938f01317bcSreyk 		while (c == EOF) {
939f01317bcSreyk 			if (file == topfile || popfile() == EOF)
940f01317bcSreyk 				return (EOF);
941f61f6eccSdenis 			c = igetc();
942f61f6eccSdenis 		}
943f01317bcSreyk 	}
944f01317bcSreyk 	return (c);
945f01317bcSreyk }
946f01317bcSreyk 
947f61f6eccSdenis void
948f01317bcSreyk lungetc(int c)
949f01317bcSreyk {
950f01317bcSreyk 	if (c == EOF)
951f61f6eccSdenis 		return;
952f61f6eccSdenis 
953f61f6eccSdenis 	if (file->ungetpos >= file->ungetsize) {
954f61f6eccSdenis 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
955f61f6eccSdenis 		if (p == NULL)
956a062aa9dSkrw 			err(1, "%s", __func__);
957f61f6eccSdenis 		file->ungetbuf = p;
958f61f6eccSdenis 		file->ungetsize *= 2;
959f01317bcSreyk 	}
960f61f6eccSdenis 	file->ungetbuf[file->ungetpos++] = c;
961f01317bcSreyk }
962f01317bcSreyk 
963f01317bcSreyk int
964f01317bcSreyk findeol(void)
965f01317bcSreyk {
966f01317bcSreyk 	int	c;
967f01317bcSreyk 
968f01317bcSreyk 	/* skip to either EOF or the first real EOL */
969f01317bcSreyk 	while (1) {
970f01317bcSreyk 		c = lgetc(0);
971f01317bcSreyk 		if (c == '\n') {
972f01317bcSreyk 			file->lineno++;
973f01317bcSreyk 			break;
974f01317bcSreyk 		}
975f01317bcSreyk 		if (c == EOF)
976f01317bcSreyk 			break;
977f01317bcSreyk 	}
978f01317bcSreyk 	return (ERROR);
979f01317bcSreyk }
980f01317bcSreyk 
981f01317bcSreyk int
982f01317bcSreyk yylex(void)
983f01317bcSreyk {
98408f6ba19Snaddy 	char	 buf[8096];
98508f6ba19Snaddy 	char	*p, *val;
986f01317bcSreyk 	int	 quotec, next, c;
987f01317bcSreyk 	int	 token;
988f01317bcSreyk 
989f01317bcSreyk top:
990f01317bcSreyk 	p = buf;
991f01317bcSreyk 	while ((c = lgetc(0)) == ' ' || c == '\t')
992f01317bcSreyk 		; /* nothing */
993f01317bcSreyk 
994f01317bcSreyk 	yylval.lineno = file->lineno;
995f01317bcSreyk 	if (c == '#')
996f01317bcSreyk 		while ((c = lgetc(0)) != '\n' && c != EOF)
997f01317bcSreyk 			; /* nothing */
998f61f6eccSdenis 	if (c == '$' && !expanding) {
999f01317bcSreyk 		while (1) {
1000f01317bcSreyk 			if ((c = lgetc(0)) == EOF)
1001f01317bcSreyk 				return (0);
1002f01317bcSreyk 
1003f01317bcSreyk 			if (p + 1 >= buf + sizeof(buf) - 1) {
1004f01317bcSreyk 				yyerror("string too long");
1005f01317bcSreyk 				return (findeol());
1006f01317bcSreyk 			}
1007f01317bcSreyk 			if (isalnum(c) || c == '_') {
1008f01317bcSreyk 				*p++ = c;
1009f01317bcSreyk 				continue;
1010f01317bcSreyk 			}
1011f01317bcSreyk 			*p = '\0';
1012f01317bcSreyk 			lungetc(c);
1013f01317bcSreyk 			break;
1014f01317bcSreyk 		}
1015f01317bcSreyk 		val = symget(buf);
1016f01317bcSreyk 		if (val == NULL) {
1017f01317bcSreyk 			yyerror("macro '%s' not defined", buf);
1018f01317bcSreyk 			return (findeol());
1019f01317bcSreyk 		}
1020f61f6eccSdenis 		p = val + strlen(val) - 1;
1021f61f6eccSdenis 		lungetc(DONE_EXPAND);
1022f61f6eccSdenis 		while (p >= val) {
102308f6ba19Snaddy 			lungetc((unsigned char)*p);
1024f61f6eccSdenis 			p--;
1025f61f6eccSdenis 		}
1026f61f6eccSdenis 		lungetc(START_EXPAND);
1027f01317bcSreyk 		goto top;
1028f01317bcSreyk 	}
1029f01317bcSreyk 
1030f01317bcSreyk 	switch (c) {
1031f01317bcSreyk 	case '\'':
1032f01317bcSreyk 	case '"':
1033f01317bcSreyk 		quotec = c;
1034f01317bcSreyk 		while (1) {
1035f01317bcSreyk 			if ((c = lgetc(quotec)) == EOF)
1036f01317bcSreyk 				return (0);
1037f01317bcSreyk 			if (c == '\n') {
1038f01317bcSreyk 				file->lineno++;
1039f01317bcSreyk 				continue;
1040f01317bcSreyk 			} else if (c == '\\') {
1041f01317bcSreyk 				if ((next = lgetc(quotec)) == EOF)
1042f01317bcSreyk 					return (0);
1043a1533359Ssashan 				if (next == quotec || next == ' ' ||
1044a1533359Ssashan 				    next == '\t')
1045f01317bcSreyk 					c = next;
1046f01317bcSreyk 				else if (next == '\n') {
1047f01317bcSreyk 					file->lineno++;
1048f01317bcSreyk 					continue;
1049f01317bcSreyk 				} else
1050f01317bcSreyk 					lungetc(next);
1051f01317bcSreyk 			} else if (c == quotec) {
1052f01317bcSreyk 				*p = '\0';
1053f01317bcSreyk 				break;
1054f01317bcSreyk 			} else if (c == '\0') {
1055f01317bcSreyk 				yyerror("syntax error");
1056f01317bcSreyk 				return (findeol());
1057f01317bcSreyk 			}
1058f01317bcSreyk 			if (p + 1 >= buf + sizeof(buf) - 1) {
1059f01317bcSreyk 				yyerror("string too long");
1060f01317bcSreyk 				return (findeol());
1061f01317bcSreyk 			}
1062f01317bcSreyk 			*p++ = c;
1063f01317bcSreyk 		}
1064f01317bcSreyk 		yylval.v.string = strdup(buf);
1065f01317bcSreyk 		if (yylval.v.string == NULL)
1066f01317bcSreyk 			fatal("yylex: strdup");
1067f01317bcSreyk 		return (STRING);
1068f01317bcSreyk 	}
1069f01317bcSreyk 
1070f01317bcSreyk #define allowed_to_end_number(x) \
1071f01317bcSreyk 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1072f01317bcSreyk 
1073f01317bcSreyk 	if (c == '-' || isdigit(c)) {
1074f01317bcSreyk 		do {
1075f01317bcSreyk 			*p++ = c;
1076915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1077f01317bcSreyk 				yyerror("string too long");
1078f01317bcSreyk 				return (findeol());
1079f01317bcSreyk 			}
1080f01317bcSreyk 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1081f01317bcSreyk 		lungetc(c);
1082f01317bcSreyk 		if (p == buf + 1 && buf[0] == '-')
1083f01317bcSreyk 			goto nodigits;
1084f01317bcSreyk 		if (c == EOF || allowed_to_end_number(c)) {
1085f01317bcSreyk 			const char *errstr = NULL;
1086f01317bcSreyk 
1087f01317bcSreyk 			*p = '\0';
1088f01317bcSreyk 			yylval.v.number = strtonum(buf, LLONG_MIN,
1089f01317bcSreyk 			    LLONG_MAX, &errstr);
1090f01317bcSreyk 			if (errstr) {
1091f01317bcSreyk 				yyerror("\"%s\" invalid number: %s",
1092f01317bcSreyk 				    buf, errstr);
1093f01317bcSreyk 				return (findeol());
1094f01317bcSreyk 			}
1095f01317bcSreyk 			return (NUMBER);
1096f01317bcSreyk 		} else {
1097f01317bcSreyk nodigits:
1098f01317bcSreyk 			while (p > buf + 1)
109908f6ba19Snaddy 				lungetc((unsigned char)*--p);
110008f6ba19Snaddy 			c = (unsigned char)*--p;
1101f01317bcSreyk 			if (c == '-')
1102f01317bcSreyk 				return (c);
1103f01317bcSreyk 		}
1104f01317bcSreyk 	}
1105f01317bcSreyk 
1106f01317bcSreyk #define allowed_in_string(x) \
1107f01317bcSreyk 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1108f01317bcSreyk 	x != '{' && x != '}' && \
1109f01317bcSreyk 	x != '!' && x != '=' && x != '#' && \
1110f01317bcSreyk 	x != ','))
1111f01317bcSreyk 
1112458ed070Sreyk 	if (isalnum(c) || c == ':' || c == '_' || c == '/') {
1113f01317bcSreyk 		do {
1114f01317bcSreyk 			*p++ = c;
1115915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1116f01317bcSreyk 				yyerror("string too long");
1117f01317bcSreyk 				return (findeol());
1118f01317bcSreyk 			}
1119f01317bcSreyk 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1120f01317bcSreyk 		lungetc(c);
1121f01317bcSreyk 		*p = '\0';
1122f01317bcSreyk 		if ((token = lookup(buf)) == STRING)
1123f01317bcSreyk 			if ((yylval.v.string = strdup(buf)) == NULL)
1124f01317bcSreyk 				fatal("yylex: strdup");
1125f01317bcSreyk 		return (token);
1126f01317bcSreyk 	}
1127f01317bcSreyk 	if (c == '\n') {
1128f01317bcSreyk 		yylval.lineno = file->lineno;
1129f01317bcSreyk 		file->lineno++;
1130f01317bcSreyk 	}
1131f01317bcSreyk 	if (c == EOF)
1132f01317bcSreyk 		return (0);
1133f01317bcSreyk 	return (c);
1134f01317bcSreyk }
1135f01317bcSreyk 
1136f01317bcSreyk struct file *
1137f01317bcSreyk pushfile(const char *name, int secret)
1138f01317bcSreyk {
1139f01317bcSreyk 	struct file	*nfile;
1140f01317bcSreyk 
1141f01317bcSreyk 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
11426a3d55f9Skrw 		log_warn("%s", __func__);
1143f01317bcSreyk 		return (NULL);
1144f01317bcSreyk 	}
1145f01317bcSreyk 	if ((nfile->name = strdup(name)) == NULL) {
11466a3d55f9Skrw 		log_warn("%s", __func__);
1147f01317bcSreyk 		free(nfile);
1148f01317bcSreyk 		return (NULL);
1149f01317bcSreyk 	}
1150f01317bcSreyk 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1151f01317bcSreyk 		free(nfile->name);
1152f01317bcSreyk 		free(nfile);
1153f01317bcSreyk 		return (NULL);
1154f01317bcSreyk 	}
1155f61f6eccSdenis 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
1156f61f6eccSdenis 	nfile->ungetsize = 16;
1157f61f6eccSdenis 	nfile->ungetbuf = malloc(nfile->ungetsize);
1158f61f6eccSdenis 	if (nfile->ungetbuf == NULL) {
11596a3d55f9Skrw 		log_warn("%s", __func__);
1160f61f6eccSdenis 		fclose(nfile->stream);
1161f61f6eccSdenis 		free(nfile->name);
1162f61f6eccSdenis 		free(nfile);
1163f61f6eccSdenis 		return (NULL);
1164f61f6eccSdenis 	}
1165f01317bcSreyk 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1166f01317bcSreyk 	return (nfile);
1167f01317bcSreyk }
1168f01317bcSreyk 
1169f01317bcSreyk int
1170f01317bcSreyk popfile(void)
1171f01317bcSreyk {
1172f01317bcSreyk 	struct file	*prev;
1173f01317bcSreyk 
1174f01317bcSreyk 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1175f01317bcSreyk 		prev->errors += file->errors;
1176f01317bcSreyk 
1177f01317bcSreyk 	TAILQ_REMOVE(&files, file, entry);
1178f01317bcSreyk 	fclose(file->stream);
1179f01317bcSreyk 	free(file->name);
1180f61f6eccSdenis 	free(file->ungetbuf);
1181f01317bcSreyk 	free(file);
1182f01317bcSreyk 	file = prev;
1183f01317bcSreyk 	return (file ? 0 : EOF);
1184f01317bcSreyk }
1185f01317bcSreyk 
1186f01317bcSreyk int
1187f01317bcSreyk parse_config(const char *filename)
1188f01317bcSreyk {
11895d396404Skn 	extern const char	 default_conffile[];
1190f01317bcSreyk 	struct sym		*sym, *next;
1191f01317bcSreyk 
1192f01317bcSreyk 	if ((file = pushfile(filename, 0)) == NULL) {
11935d396404Skn 		/* no default config file is fine */
11945d396404Skn 		if (errno == ENOENT && filename == default_conffile) {
11955d396404Skn 			log_debug("%s: missing", filename);
11965d396404Skn 			return (0);
11975d396404Skn 		}
1198f01317bcSreyk 		log_warn("failed to open %s", filename);
11998d6f4d1bSreyk 		if (errno == ENOENT)
1200f01317bcSreyk 			return (0);
12018d6f4d1bSreyk 		return (-1);
1202f01317bcSreyk 	}
1203f01317bcSreyk 	topfile = file;
1204f01317bcSreyk 	setservent(1);
1205f01317bcSreyk 
1206789e0822Sreyk 	/* Set the default switch type */
1207789e0822Sreyk 	(void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type));
1208789e0822Sreyk 
1209f94ca20eSmartijn 	env->vmd_cfg.cfg_agentx.ax_enabled = 0;
1210f94ca20eSmartijn 	env->vmd_cfg.cfg_agentx.ax_context[0] = '\0';
1211f94ca20eSmartijn 	env->vmd_cfg.cfg_agentx.ax_path[0] = '\0';
1212f94ca20eSmartijn 
1213f01317bcSreyk 	yyparse();
1214f01317bcSreyk 	errors = file->errors;
1215f01317bcSreyk 	popfile();
1216f01317bcSreyk 
1217f01317bcSreyk 	endservent();
1218f01317bcSreyk 
1219f01317bcSreyk 	/* Free macros and check which have not been used. */
122046bca67bSkrw 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1221f01317bcSreyk 		if (!sym->used)
1222f01317bcSreyk 			fprintf(stderr, "warning: macro '%s' not "
1223f01317bcSreyk 			    "used\n", sym->nam);
1224f01317bcSreyk 		if (!sym->persist) {
1225f01317bcSreyk 			free(sym->nam);
1226f01317bcSreyk 			free(sym->val);
1227f01317bcSreyk 			TAILQ_REMOVE(&symhead, sym, entry);
1228f01317bcSreyk 			free(sym);
1229f01317bcSreyk 		}
1230f01317bcSreyk 	}
1231f01317bcSreyk 
1232f01317bcSreyk 	if (errors)
1233f01317bcSreyk 		return (-1);
1234f01317bcSreyk 
1235f01317bcSreyk 	return (0);
1236f01317bcSreyk }
1237f01317bcSreyk 
1238f01317bcSreyk int
1239f01317bcSreyk symset(const char *nam, const char *val, int persist)
1240f01317bcSreyk {
1241f01317bcSreyk 	struct sym	*sym;
1242f01317bcSreyk 
124354c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
124454c95b7aSkrw 		if (strcmp(nam, sym->nam) == 0)
124554c95b7aSkrw 			break;
124654c95b7aSkrw 	}
1247f01317bcSreyk 
1248f01317bcSreyk 	if (sym != NULL) {
1249f01317bcSreyk 		if (sym->persist == 1)
1250f01317bcSreyk 			return (0);
1251f01317bcSreyk 		else {
1252f01317bcSreyk 			free(sym->nam);
1253f01317bcSreyk 			free(sym->val);
1254f01317bcSreyk 			TAILQ_REMOVE(&symhead, sym, entry);
1255f01317bcSreyk 			free(sym);
1256f01317bcSreyk 		}
1257f01317bcSreyk 	}
1258f01317bcSreyk 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1259f01317bcSreyk 		return (-1);
1260f01317bcSreyk 
1261f01317bcSreyk 	sym->nam = strdup(nam);
1262f01317bcSreyk 	if (sym->nam == NULL) {
1263f01317bcSreyk 		free(sym);
1264f01317bcSreyk 		return (-1);
1265f01317bcSreyk 	}
1266f01317bcSreyk 	sym->val = strdup(val);
1267f01317bcSreyk 	if (sym->val == NULL) {
1268f01317bcSreyk 		free(sym->nam);
1269f01317bcSreyk 		free(sym);
1270f01317bcSreyk 		return (-1);
1271f01317bcSreyk 	}
1272f01317bcSreyk 	sym->used = 0;
1273f01317bcSreyk 	sym->persist = persist;
1274f01317bcSreyk 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1275f01317bcSreyk 	return (0);
1276f01317bcSreyk }
1277f01317bcSreyk 
1278f01317bcSreyk int
1279f01317bcSreyk cmdline_symset(char *s)
1280f01317bcSreyk {
1281f01317bcSreyk 	char	*sym, *val;
1282f01317bcSreyk 	int	ret;
1283f01317bcSreyk 
1284f01317bcSreyk 	if ((val = strrchr(s, '=')) == NULL)
1285f01317bcSreyk 		return (-1);
1286ed1b9eb8Smiko 	sym = strndup(s, val - s);
1287ed1b9eb8Smiko 	if (sym == NULL)
1288ed1b9eb8Smiko 		fatal("%s: strndup", __func__);
1289f01317bcSreyk 	ret = symset(sym, val + 1, 1);
1290f01317bcSreyk 	free(sym);
1291f01317bcSreyk 
1292f01317bcSreyk 	return (ret);
1293f01317bcSreyk }
1294f01317bcSreyk 
1295f01317bcSreyk char *
1296f01317bcSreyk symget(const char *nam)
1297f01317bcSreyk {
1298f01317bcSreyk 	struct sym	*sym;
1299f01317bcSreyk 
130054c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
1301f01317bcSreyk 		if (strcmp(nam, sym->nam) == 0) {
1302f01317bcSreyk 			sym->used = 1;
1303f01317bcSreyk 			return (sym->val);
1304f01317bcSreyk 		}
130554c95b7aSkrw 	}
1306f01317bcSreyk 	return (NULL);
1307f01317bcSreyk }
1308f01317bcSreyk 
1309f01317bcSreyk ssize_t
1310f01317bcSreyk parse_size(char *word, int64_t val)
1311f01317bcSreyk {
1312e545c54cSdv 	char		 result[FMT_SCALED_STRSIZE];
1313f01317bcSreyk 	ssize_t		 size;
1314f01317bcSreyk 	long long	 res;
1315f01317bcSreyk 
1316f01317bcSreyk 	if (word != NULL) {
1317f01317bcSreyk 		if (scan_scaled(word, &res) != 0) {
1318e545c54cSdv 			log_warn("invalid memory size: %s", word);
1319f01317bcSreyk 			return (-1);
1320f01317bcSreyk 		}
1321f01317bcSreyk 		val = (int64_t)res;
1322f01317bcSreyk 	}
1323f01317bcSreyk 
1324f01317bcSreyk 	if (val < (1024 * 1024)) {
1325e545c54cSdv 		log_warnx("memory size must be at least 1MB");
1326f01317bcSreyk 		return (-1);
1327e545c54cSdv 	}
1328f01317bcSreyk 
1329e545c54cSdv 	if (val > VMM_MAX_VM_MEM_SIZE) {
1330e545c54cSdv 		if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0)
1331e545c54cSdv 			log_warnx("memory size too large (limit is %s)",
1332e545c54cSdv 			    result);
1333e545c54cSdv 		else
1334e545c54cSdv 			log_warnx("memory size too large");
1335e545c54cSdv 		return (-1);
1336e545c54cSdv 	}
1337e545c54cSdv 
1338e545c54cSdv 	/* Round down to the megabyte. */
1339e545c54cSdv 	size = (val / (1024 * 1024)) * (1024 * 1024);
1340e545c54cSdv 
1341e545c54cSdv 	if (size != val) {
1342e545c54cSdv 		if (fmt_scaled(size, result) == 0)
1343ead1b146Sdv 			log_debug("memory size rounded to %s", result);
1344e545c54cSdv 		else
1345ead1b146Sdv 			log_debug("memory size rounded to %zd bytes", size);
1346e545c54cSdv 	}
1347f01317bcSreyk 
1348f01317bcSreyk 	return ((ssize_t)size);
1349f01317bcSreyk }
1350f01317bcSreyk 
1351f01317bcSreyk int
1352f224f92aSccardenas parse_disk(char *word, int type)
1353f01317bcSreyk {
1354e2ceadc1Sreyk 	char	 buf[BUFSIZ], path[PATH_MAX];
1355e2ceadc1Sreyk 	int	 fd;
1356e2ceadc1Sreyk 	ssize_t	 len;
13576429e633Sreyk 
135873a98491Sdv 	if (vmc.vmc_ndisks >= VM_MAX_DISKS_PER_VM) {
1359f01317bcSreyk 		log_warnx("too many disks");
1360f01317bcSreyk 		return (-1);
1361f01317bcSreyk 	}
1362f01317bcSreyk 
13636429e633Sreyk 	if (realpath(word, path) == NULL) {
13646429e633Sreyk 		log_warn("disk %s", word);
13656429e633Sreyk 		return (-1);
13666429e633Sreyk 	}
13676429e633Sreyk 
1368e2ceadc1Sreyk 	if (!type) {
1369e2ceadc1Sreyk 		/* Use raw as the default format */
1370e2ceadc1Sreyk 		type = VMDF_RAW;
1371e2ceadc1Sreyk 
1372e2ceadc1Sreyk 		/* Try to derive the format from the file signature */
1373e2ceadc1Sreyk 		if ((fd = open(path, O_RDONLY)) != -1) {
1374e2ceadc1Sreyk 			len = read(fd, buf, sizeof(buf));
1375e2ceadc1Sreyk 			close(fd);
1376e2ceadc1Sreyk 			if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) &&
1377e2ceadc1Sreyk 			    strncmp(buf, VM_MAGIC_QCOW,
1378e2ceadc1Sreyk 			    strlen(VM_MAGIC_QCOW)) == 0) {
1379e2ceadc1Sreyk 				/* The qcow version will be checked later */
1380e2ceadc1Sreyk 				type = VMDF_QCOW2;
1381e2ceadc1Sreyk 			}
1382e2ceadc1Sreyk 		}
1383e2ceadc1Sreyk 	}
1384e2ceadc1Sreyk 
138573a98491Sdv 	if (strlcpy(vmc.vmc_disks[vmc.vmc_ndisks], path,
138673a98491Sdv 	    sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) >=
138773a98491Sdv 	    sizeof(vmc.vmc_disks[vmc.vmc_ndisks])) {
1388f01317bcSreyk 		log_warnx("disk path too long");
1389f01317bcSreyk 		return (-1);
1390f01317bcSreyk 	}
139173a98491Sdv 	vmc.vmc_disktypes[vmc.vmc_ndisks] = type;
1392f01317bcSreyk 
139373a98491Sdv 	vmc.vmc_ndisks++;
1394f01317bcSreyk 
1395f01317bcSreyk 	return (0);
1396f01317bcSreyk }
1397c48cfcf4Sreyk 
1398e2ceadc1Sreyk unsigned int
1399e2ceadc1Sreyk parse_format(const char *word)
1400e2ceadc1Sreyk {
1401e2ceadc1Sreyk 	if (strcasecmp(word, "raw") == 0)
1402e2ceadc1Sreyk 		return (VMDF_RAW);
1403e2ceadc1Sreyk 	else if (strcasecmp(word, "qcow2") == 0)
1404e2ceadc1Sreyk 		return (VMDF_QCOW2);
1405e2ceadc1Sreyk 	return (0);
1406e2ceadc1Sreyk }
1407e2ceadc1Sreyk 
14082272e586Sdv /*
14092272e586Sdv  * Parse an ipv4 address and prefix for local interfaces and validate
14102272e586Sdv  * constraints for vmd networking.
14112272e586Sdv  */
1412c48cfcf4Sreyk int
14132272e586Sdv parse_prefix4(const char *str, struct local_prefix *out, const char **errstr)
1414c48cfcf4Sreyk {
14152272e586Sdv 	struct addrinfo		 hints, *res = NULL;
14162272e586Sdv 	struct sockaddr_storage	 ss;
14172272e586Sdv 	struct in_addr		 addr;
14182272e586Sdv 	int			 mask = 16;
14192272e586Sdv 	char			*p, *ps;
1420c48cfcf4Sreyk 
14212272e586Sdv 	if ((ps = strdup(str)) == NULL)
14222272e586Sdv 		fatal("%s: strdup", __func__);
14232272e586Sdv 
14242272e586Sdv 	if ((p = strrchr(ps, '/')) != NULL) {
14252272e586Sdv 		mask = strtonum(p + 1, 1, 16, errstr);
14262272e586Sdv 		if (errstr != NULL && *errstr) {
14272272e586Sdv 			free(ps);
14282272e586Sdv 			return (1);
14292272e586Sdv 		}
14302272e586Sdv 		p[0] = '\0';
1431c48cfcf4Sreyk 	}
1432c48cfcf4Sreyk 
14332272e586Sdv 	/* Attempt to construct an address from the user input. */
1434c48cfcf4Sreyk 	memset(&hints, 0, sizeof(hints));
14352272e586Sdv 	hints.ai_family = AF_INET;
14362272e586Sdv 	hints.ai_socktype = SOCK_DGRAM;
1437c48cfcf4Sreyk 	hints.ai_flags = AI_NUMERICHOST;
14382272e586Sdv 
14392272e586Sdv 	if (getaddrinfo(ps, NULL, &hints, &res) == 0) {
14402272e586Sdv 		memset(&ss, 0, sizeof(ss));
14412272e586Sdv 		memcpy(&ss, res->ai_addr, res->ai_addrlen);
14422272e586Sdv 		addr.s_addr = ss2sin(&ss)->sin_addr.s_addr;
1443c48cfcf4Sreyk 		freeaddrinfo(res);
14442272e586Sdv 	} else { /* try 10/8 parsing */
14452272e586Sdv 		memset(&addr, 0, sizeof(addr));
14462272e586Sdv 		if (inet_net_pton(AF_INET, ps, &addr, sizeof(addr)) == -1) {
14472272e586Sdv 			if (errstr)
14482272e586Sdv 				*errstr = "invalid format";
14492272e586Sdv 			free(ps);
14502272e586Sdv 			return (1);
14512272e586Sdv 		}
14522272e586Sdv 	}
14532272e586Sdv 	free(ps);
14542272e586Sdv 
14552272e586Sdv 	/*
14562272e586Sdv 	 * Validate the prefix by comparing it with the mask. Since we
14572272e586Sdv 	 * constrain the mask length to 16 above, this also validates
14582272e586Sdv 	 * we reserve the last 16 bits for use by vmd to assign vm id
14592272e586Sdv 	 * and interface id.
14602272e586Sdv 	 */
14612272e586Sdv 	if ((addr.s_addr & prefixlen2mask(mask)) != addr.s_addr) {
14622272e586Sdv 		if (errstr)
14632272e586Sdv 			*errstr = "bad mask";
14642272e586Sdv 		return (1);
14652272e586Sdv 	}
14662272e586Sdv 
14672272e586Sdv 	/* Copy out the local prefix. */
14682272e586Sdv 	out->lp_in.s_addr = addr.s_addr;
14692272e586Sdv 	out->lp_mask.s_addr = prefixlen2mask(mask);
1470c48cfcf4Sreyk 	return (0);
1471c48cfcf4Sreyk }
1472c48cfcf4Sreyk 
14732272e586Sdv /*
14742272e586Sdv  * Parse an ipv6 address and prefix for local interfaces and validate
14752272e586Sdv  * constraints for vmd networking.
14762272e586Sdv  */
14772272e586Sdv int
14782272e586Sdv parse_prefix6(const char *str, struct local_prefix *out, const char **errstr)
14792272e586Sdv {
14802272e586Sdv 	struct addrinfo		 hints, *res = NULL;
14812272e586Sdv 	struct sockaddr_storage	 ss;
14822272e586Sdv 	struct in6_addr		 addr6, mask6;
14832272e586Sdv 	size_t			 i;
14842272e586Sdv 	int			 mask = 64, err;
14852272e586Sdv 	char			*p, *ps;
14862272e586Sdv 
14872272e586Sdv 	if ((ps = strdup(str)) == NULL)
14882272e586Sdv 		fatal("%s: strdup", __func__);
14892272e586Sdv 
14902272e586Sdv 	if ((p = strrchr(ps, '/')) != NULL) {
14912272e586Sdv 		mask = strtonum(p + 1, 0, 64, errstr);
14922272e586Sdv 		if (errstr != NULL && *errstr) {
14932272e586Sdv 			free(ps);
14942272e586Sdv 			return (1);
14952272e586Sdv 		}
14962272e586Sdv 		p[0] = '\0';
14972272e586Sdv 	}
14982272e586Sdv 
14992272e586Sdv 	/* Attempt to construct an address from the user input. */
15002272e586Sdv 	memset(&hints, 0, sizeof(hints));
15012272e586Sdv 	hints.ai_family = AF_INET6;
15022272e586Sdv 	hints.ai_socktype = SOCK_DGRAM;
15032272e586Sdv 	hints.ai_flags = AI_NUMERICHOST;
15042272e586Sdv 
15052272e586Sdv 	if ((err = getaddrinfo(ps, NULL, &hints, &res)) != 0) {
15062272e586Sdv 		if (errstr)
15072272e586Sdv 			*errstr = gai_strerror(err);
15082272e586Sdv 		free(ps);
15092272e586Sdv 		return (1);
15102272e586Sdv 	}
15112272e586Sdv 	free(ps);
15122272e586Sdv 
15132272e586Sdv 	memset(&ss, 0, sizeof(ss));
15142272e586Sdv 	memcpy(&ss, res->ai_addr, res->ai_addrlen);
15152272e586Sdv 	freeaddrinfo(res);
15162272e586Sdv 
15172272e586Sdv 	memcpy(&addr6, (void*)&ss2sin6(&ss)->sin6_addr, sizeof(addr6));
15182272e586Sdv 	prefixlen2mask6(mask, &mask6);
15192272e586Sdv 
15202272e586Sdv 	/*
15212272e586Sdv 	 * Validate the prefix by comparing it with the mask. Since we
15222272e586Sdv 	 * constrain the mask length to 64 above, this also validates
15232272e586Sdv 	 * that we're reserving bits for the encoding of the ipv4
15242272e586Sdv 	 * address, the vm id, and interface id. */
15252272e586Sdv 	for (i = 0; i < 16; i++) {
15262272e586Sdv 		if ((addr6.s6_addr[i] & mask6.s6_addr[i]) != addr6.s6_addr[i]) {
15272272e586Sdv 			if (errstr)
15282272e586Sdv 				*errstr = "bad mask";
15292272e586Sdv 			return (1);
15302272e586Sdv 		}
15312272e586Sdv 	}
15322272e586Sdv 
15332272e586Sdv 	/* Copy out the local prefix. */
15342272e586Sdv 	memcpy(&out->lp_in6, &addr6, sizeof(out->lp_in6));
15352272e586Sdv 	memcpy(&out->lp_mask6, &mask6, sizeof(out->lp_mask6));
15362272e586Sdv 	return (0);
1537c48cfcf4Sreyk }
1538