1 /* $OpenBSD: parse.y,v 1.59 2021/10/15 15:01:29 naddy 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 <unistd.h> 43 #include <ctype.h> 44 #include <netdb.h> 45 #include <util.h> 46 #include <errno.h> 47 #include <err.h> 48 #include <fcntl.h> 49 #include <pwd.h> 50 #include <grp.h> 51 52 #include "vmd.h" 53 54 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 55 static struct file { 56 TAILQ_ENTRY(file) entry; 57 FILE *stream; 58 char *name; 59 size_t ungetpos; 60 size_t ungetsize; 61 u_char *ungetbuf; 62 int eof_reached; 63 int lineno; 64 int errors; 65 } *file, *topfile; 66 struct file *pushfile(const char *, int); 67 int popfile(void); 68 int yyparse(void); 69 int yylex(void); 70 int yyerror(const char *, ...) 71 __attribute__((__format__ (printf, 1, 2))) 72 __attribute__((__nonnull__ (1))); 73 int kw_cmp(const void *, const void *); 74 int lookup(char *); 75 int igetc(void); 76 int lgetc(int); 77 void lungetc(int); 78 int findeol(void); 79 80 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 81 struct sym { 82 TAILQ_ENTRY(sym) entry; 83 int used; 84 int persist; 85 char *nam; 86 char *val; 87 }; 88 int symset(const char *, const char *, int); 89 char *symget(const char *); 90 91 ssize_t parse_size(char *, int64_t); 92 int parse_disk(char *, int); 93 unsigned int parse_format(const char *); 94 95 static struct vmop_create_params vmc; 96 static struct vm_create_params *vcp; 97 static struct vmd_switch *vsw; 98 static char vsw_type[IF_NAMESIZE]; 99 static int vcp_disable; 100 static size_t vcp_nnics; 101 static int errors; 102 extern struct vmd *env; 103 extern const char *vmd_descsw[]; 104 105 typedef struct { 106 union { 107 uint8_t lladdr[ETHER_ADDR_LEN]; 108 int64_t number; 109 char *string; 110 struct { 111 uid_t uid; 112 int64_t gid; 113 } owner; 114 } v; 115 int lineno; 116 } YYSTYPE; 117 118 %} 119 120 121 %token INCLUDE ERROR 122 %token ADD ALLOW BOOT CDROM DEVICE DISABLE DISK DOWN ENABLE FORMAT GROUP 123 %token INET6 INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NET NIFS OWNER 124 %token PATH PREFIX RDOMAIN SIZE SOCKET SWITCH UP VM VMID STAGGERED START 125 %token PARALLEL DELAY 126 %token <v.number> NUMBER 127 %token <v.string> STRING 128 %type <v.lladdr> lladdr 129 %type <v.number> bootdevice 130 %type <v.number> disable 131 %type <v.number> image_format 132 %type <v.number> local 133 %type <v.number> locked 134 %type <v.number> updown 135 %type <v.owner> owner_id 136 %type <v.string> optstring 137 %type <v.string> string 138 %type <v.string> vm_instance 139 140 %% 141 142 grammar : /* empty */ 143 | grammar include '\n' 144 | grammar '\n' 145 | grammar varset '\n' 146 | grammar main '\n' 147 | grammar switch '\n' 148 | grammar vm '\n' 149 | grammar error '\n' { file->errors++; } 150 ; 151 152 include : INCLUDE string { 153 struct file *nfile; 154 155 if ((nfile = pushfile($2, 0)) == NULL) { 156 yyerror("failed to include file %s", $2); 157 free($2); 158 YYERROR; 159 } 160 free($2); 161 162 file = nfile; 163 lungetc('\n'); 164 } 165 ; 166 167 varset : STRING '=' STRING { 168 char *s = $1; 169 while (*s++) { 170 if (isspace((unsigned char)*s)) { 171 yyerror("macro name cannot contain " 172 "whitespace"); 173 free($1); 174 free($3); 175 YYERROR; 176 } 177 } 178 if (symset($1, $3, 0) == -1) 179 fatalx("cannot store variable"); 180 free($1); 181 free($3); 182 } 183 ; 184 185 main : LOCAL INET6 { 186 env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; 187 } 188 | LOCAL INET6 PREFIX STRING { 189 struct address h; 190 191 if (host($4, &h) == -1 || 192 h.ss.ss_family != AF_INET6 || 193 h.prefixlen > 64 || h.prefixlen < 0) { 194 yyerror("invalid local inet6 prefix: %s", $4); 195 free($4); 196 YYERROR; 197 } 198 199 env->vmd_cfg.cfg_flags |= VMD_CFG_INET6; 200 env->vmd_cfg.cfg_flags &= ~VMD_CFG_AUTOINET6; 201 memcpy(&env->vmd_cfg.cfg_localprefix6, &h, sizeof(h)); 202 } 203 | LOCAL PREFIX STRING { 204 struct address h; 205 206 if (host($3, &h) == -1 || 207 h.ss.ss_family != AF_INET || 208 h.prefixlen > 32 || h.prefixlen < 0) { 209 yyerror("invalid local prefix: %s", $3); 210 free($3); 211 YYERROR; 212 } 213 214 memcpy(&env->vmd_cfg.cfg_localprefix, &h, sizeof(h)); 215 } 216 | SOCKET OWNER owner_id { 217 env->vmd_ps.ps_csock.cs_uid = $3.uid; 218 env->vmd_ps.ps_csock.cs_gid = $3.gid == -1 ? 0 : $3.gid; 219 } 220 | STAGGERED START PARALLEL NUMBER DELAY NUMBER { 221 env->vmd_cfg.cfg_flags |= VMD_CFG_STAGGERED_START; 222 env->vmd_cfg.delay.tv_sec = $6; 223 env->vmd_cfg.parallelism = $4; 224 } 225 ; 226 227 switch : SWITCH string { 228 if ((vsw = calloc(1, sizeof(*vsw))) == NULL) 229 fatal("could not allocate switch"); 230 231 vsw->sw_id = env->vmd_nswitches + 1; 232 vsw->sw_name = $2; 233 vsw->sw_flags = VMIFF_UP; 234 235 vcp_disable = 0; 236 } '{' optnl switch_opts_l '}' { 237 if (strnlen(vsw->sw_ifname, 238 sizeof(vsw->sw_ifname)) == 0) { 239 yyerror("switch \"%s\" " 240 "is missing interface name", 241 vsw->sw_name); 242 YYERROR; 243 } 244 245 if (vcp_disable) { 246 log_debug("%s:%d: switch \"%s\"" 247 " skipped (disabled)", 248 file->name, yylval.lineno, vsw->sw_name); 249 } else if (!env->vmd_noaction) { 250 TAILQ_INSERT_TAIL(env->vmd_switches, 251 vsw, sw_entry); 252 env->vmd_nswitches++; 253 log_debug("%s:%d: switch \"%s\" registered", 254 file->name, yylval.lineno, vsw->sw_name); 255 } 256 } 257 ; 258 259 switch_opts_l : switch_opts_l switch_opts nl 260 | switch_opts optnl 261 ; 262 263 switch_opts : disable { 264 vcp_disable = $1; 265 } 266 | GROUP string { 267 if (priv_validgroup($2) == -1) { 268 yyerror("invalid group name: %s", $2); 269 free($2); 270 YYERROR; 271 } 272 vsw->sw_group = $2; 273 } 274 | INTERFACE string { 275 if (priv_getiftype($2, vsw_type, NULL) == -1 || 276 priv_findname(vsw_type, vmd_descsw) == -1) { 277 yyerror("invalid switch interface: %s", $2); 278 free($2); 279 YYERROR; 280 } 281 282 if (strlcpy(vsw->sw_ifname, $2, 283 sizeof(vsw->sw_ifname)) >= sizeof(vsw->sw_ifname)) { 284 yyerror("switch interface too long: %s", $2); 285 free($2); 286 YYERROR; 287 } 288 free($2); 289 } 290 | LOCKED LLADDR { 291 vsw->sw_flags |= VMIFF_LOCKED; 292 } 293 | RDOMAIN NUMBER { 294 if ($2 < 0 || $2 > RT_TABLEID_MAX) { 295 yyerror("invalid rdomain: %lld", $2); 296 YYERROR; 297 } 298 vsw->sw_flags |= VMIFF_RDOMAIN; 299 vsw->sw_rdomain = $2; 300 } 301 | updown { 302 if ($1) 303 vsw->sw_flags |= VMIFF_UP; 304 else 305 vsw->sw_flags &= ~VMIFF_UP; 306 } 307 ; 308 309 vm : VM string vm_instance { 310 unsigned int i; 311 char *name; 312 313 memset(&vmc, 0, sizeof(vmc)); 314 vcp = &vmc.vmc_params; 315 vcp_disable = 0; 316 vcp_nnics = 0; 317 318 if ($3 != NULL) { 319 /* This is an instance of a pre-configured VM */ 320 if (strlcpy(vmc.vmc_instance, $2, 321 sizeof(vmc.vmc_instance)) >= 322 sizeof(vmc.vmc_instance)) { 323 yyerror("vm %s name too long", $2); 324 free($2); 325 free($3); 326 YYERROR; 327 } 328 329 free($2); 330 name = $3; 331 vmc.vmc_flags |= VMOP_CREATE_INSTANCE; 332 } else 333 name = $2; 334 335 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 336 /* Set the interface to UP by default */ 337 vmc.vmc_ifflags[i] |= IFF_UP; 338 } 339 340 if (strlcpy(vcp->vcp_name, name, 341 sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) { 342 yyerror("vm name too long"); 343 free($2); 344 free($3); 345 YYERROR; 346 } 347 348 /* set default user/group permissions */ 349 vmc.vmc_owner.uid = 0; 350 vmc.vmc_owner.gid = -1; 351 } '{' optnl vm_opts_l '}' { 352 struct vmd_vm *vm; 353 int ret; 354 355 /* configured interfaces vs. number of interfaces */ 356 if (vcp_nnics > vcp->vcp_nnics) 357 vcp->vcp_nnics = vcp_nnics; 358 359 if (!env->vmd_noaction) { 360 ret = vm_register(&env->vmd_ps, &vmc, 361 &vm, 0, 0); 362 if (ret == -1 && errno == EALREADY) { 363 log_debug("%s:%d: vm \"%s\"" 364 " skipped (%s)", 365 file->name, yylval.lineno, 366 vcp->vcp_name, 367 (vm->vm_state & VM_STATE_RUNNING) ? 368 "running" : "already exists"); 369 } else if (ret == -1) { 370 yyerror("vm \"%s\" failed: %s", 371 vcp->vcp_name, strerror(errno)); 372 YYERROR; 373 } else { 374 if (vcp_disable) 375 vm->vm_state |= VM_STATE_DISABLED; 376 else 377 vm->vm_state |= VM_STATE_WAITING; 378 log_debug("%s:%d: vm \"%s\" " 379 "registered (%s)", 380 file->name, yylval.lineno, 381 vcp->vcp_name, 382 vcp_disable ? 383 "disabled" : "enabled"); 384 } 385 vm->vm_from_config = 1; 386 } 387 } 388 ; 389 390 vm_instance : /* empty */ { $$ = NULL; } 391 | INSTANCE string { $$ = $2; } 392 ; 393 394 vm_opts_l : vm_opts_l vm_opts nl 395 | vm_opts optnl 396 ; 397 398 vm_opts : disable { 399 vcp_disable = $1; 400 } 401 | DISK string image_format { 402 if (parse_disk($2, $3) != 0) { 403 yyerror("failed to parse disks: %s", $2); 404 free($2); 405 YYERROR; 406 } 407 free($2); 408 vmc.vmc_flags |= VMOP_CREATE_DISK; 409 } 410 | local INTERFACE optstring iface_opts_o { 411 unsigned int i; 412 char type[IF_NAMESIZE]; 413 414 i = vcp_nnics; 415 if (++vcp_nnics > VMM_MAX_NICS_PER_VM) { 416 yyerror("too many interfaces: %zu", vcp_nnics); 417 free($3); 418 YYERROR; 419 } 420 421 if ($1) 422 vmc.vmc_ifflags[i] |= VMIFF_LOCAL; 423 if ($3 != NULL) { 424 if (strcmp($3, "tap") != 0 && 425 (priv_getiftype($3, type, NULL) == -1 || 426 strcmp(type, "tap") != 0)) { 427 yyerror("invalid interface: %s", $3); 428 free($3); 429 YYERROR; 430 } 431 432 if (strlcpy(vmc.vmc_ifnames[i], $3, 433 sizeof(vmc.vmc_ifnames[i])) >= 434 sizeof(vmc.vmc_ifnames[i])) { 435 yyerror("interface name too long: %s", 436 $3); 437 free($3); 438 YYERROR; 439 } 440 } 441 free($3); 442 vmc.vmc_flags |= VMOP_CREATE_NETWORK; 443 } 444 | BOOT string { 445 char path[PATH_MAX]; 446 447 if (vcp->vcp_kernel[0] != '\0') { 448 yyerror("kernel specified more than once"); 449 free($2); 450 YYERROR; 451 452 } 453 if (realpath($2, path) == NULL) { 454 yyerror("kernel path not found: %s", 455 strerror(errno)); 456 free($2); 457 YYERROR; 458 } 459 free($2); 460 if (strlcpy(vcp->vcp_kernel, path, 461 sizeof(vcp->vcp_kernel)) >= 462 sizeof(vcp->vcp_kernel)) { 463 yyerror("kernel name too long"); 464 YYERROR; 465 } 466 vmc.vmc_flags |= VMOP_CREATE_KERNEL; 467 } 468 | BOOT DEVICE bootdevice { 469 vmc.vmc_bootdevice = $3; 470 } 471 | CDROM string { 472 if (vcp->vcp_cdrom[0] != '\0') { 473 yyerror("cdrom specified more than once"); 474 free($2); 475 YYERROR; 476 477 } 478 if (strlcpy(vcp->vcp_cdrom, $2, 479 sizeof(vcp->vcp_cdrom)) >= 480 sizeof(vcp->vcp_cdrom)) { 481 yyerror("cdrom name too long"); 482 free($2); 483 YYERROR; 484 } 485 free($2); 486 vmc.vmc_flags |= VMOP_CREATE_CDROM; 487 } 488 | NIFS NUMBER { 489 if (vcp->vcp_nnics != 0) { 490 yyerror("interfaces specified more than once"); 491 YYERROR; 492 } 493 if ($2 < 0 || $2 > VMM_MAX_NICS_PER_VM) { 494 yyerror("too many interfaces: %lld", $2); 495 YYERROR; 496 } 497 vcp->vcp_nnics = (size_t)$2; 498 vmc.vmc_flags |= VMOP_CREATE_NETWORK; 499 } 500 | MEMORY NUMBER { 501 ssize_t res; 502 if (vcp->vcp_memranges[0].vmr_size != 0) { 503 yyerror("memory specified more than once"); 504 YYERROR; 505 } 506 if ((res = parse_size(NULL, $2)) == -1) { 507 yyerror("failed to parse size: %lld", $2); 508 YYERROR; 509 } 510 vcp->vcp_memranges[0].vmr_size = (size_t)res; 511 vmc.vmc_flags |= VMOP_CREATE_MEMORY; 512 } 513 | MEMORY STRING { 514 ssize_t res; 515 if (vcp->vcp_memranges[0].vmr_size != 0) { 516 yyerror("argument specified more than once"); 517 free($2); 518 YYERROR; 519 } 520 if ((res = parse_size($2, 0)) == -1) { 521 yyerror("failed to parse size: %s", $2); 522 free($2); 523 YYERROR; 524 } 525 vcp->vcp_memranges[0].vmr_size = (size_t)res; 526 vmc.vmc_flags |= VMOP_CREATE_MEMORY; 527 } 528 | OWNER owner_id { 529 vmc.vmc_owner.uid = $2.uid; 530 vmc.vmc_owner.gid = $2.gid; 531 } 532 | instance 533 ; 534 535 instance : ALLOW INSTANCE '{' optnl instance_l '}' 536 | ALLOW INSTANCE instance_flags 537 ; 538 539 instance_l : instance_flags optcommanl instance_l 540 | instance_flags optnl 541 ; 542 543 instance_flags : BOOT { vmc.vmc_insflags |= VMOP_CREATE_KERNEL; } 544 | MEMORY { vmc.vmc_insflags |= VMOP_CREATE_MEMORY; } 545 | INTERFACE { vmc.vmc_insflags |= VMOP_CREATE_NETWORK; } 546 | DISK { vmc.vmc_insflags |= VMOP_CREATE_DISK; } 547 | CDROM { vmc.vmc_insflags |= VMOP_CREATE_CDROM; } 548 | INSTANCE { vmc.vmc_insflags |= VMOP_CREATE_INSTANCE; } 549 | OWNER owner_id { 550 vmc.vmc_insowner.uid = $2.uid; 551 vmc.vmc_insowner.gid = $2.gid; 552 } 553 ; 554 555 owner_id : NUMBER { 556 $$.uid = $1; 557 $$.gid = -1; 558 } 559 | STRING { 560 char *user, *group; 561 struct passwd *pw; 562 struct group *gr; 563 564 $$.uid = 0; 565 $$.gid = -1; 566 567 user = $1; 568 if ((group = strchr(user, ':')) != NULL) { 569 if (group == user) 570 user = NULL; 571 *group++ = '\0'; 572 } 573 574 if (user != NULL && *user) { 575 if ((pw = getpwnam(user)) == NULL) { 576 yyerror("failed to get user: %s", 577 user); 578 free($1); 579 YYERROR; 580 } 581 $$.uid = pw->pw_uid; 582 } 583 584 if (group != NULL && *group) { 585 if ((gr = getgrnam(group)) == NULL) { 586 yyerror("failed to get group: %s", 587 group); 588 free($1); 589 YYERROR; 590 } 591 $$.gid = gr->gr_gid; 592 } 593 594 free($1); 595 } 596 ; 597 598 image_format : /* none */ { 599 $$ = 0; 600 } 601 | FORMAT string { 602 if (($$ = parse_format($2)) == 0) { 603 yyerror("unrecognized disk format %s", $2); 604 free($2); 605 YYERROR; 606 } 607 } 608 ; 609 610 iface_opts_o : '{' optnl iface_opts_l '}' 611 | iface_opts_c 612 | /* empty */ 613 ; 614 615 iface_opts_l : iface_opts_l iface_opts optnl 616 | iface_opts optnl 617 ; 618 619 iface_opts_c : iface_opts_c iface_opts optcomma 620 | iface_opts 621 ; 622 623 iface_opts : SWITCH string { 624 unsigned int i = vcp_nnics; 625 626 /* No need to check if the switch exists */ 627 if (strlcpy(vmc.vmc_ifswitch[i], $2, 628 sizeof(vmc.vmc_ifswitch[i])) >= 629 sizeof(vmc.vmc_ifswitch[i])) { 630 yyerror("switch name too long: %s", $2); 631 free($2); 632 YYERROR; 633 } 634 free($2); 635 } 636 | GROUP string { 637 unsigned int i = vcp_nnics; 638 639 if (priv_validgroup($2) == -1) { 640 yyerror("invalid group name: %s", $2); 641 free($2); 642 YYERROR; 643 } 644 645 /* No need to check if the group exists */ 646 (void)strlcpy(vmc.vmc_ifgroup[i], $2, 647 sizeof(vmc.vmc_ifgroup[i])); 648 free($2); 649 } 650 | locked LLADDR lladdr { 651 if ($1) 652 vmc.vmc_ifflags[vcp_nnics] |= VMIFF_LOCKED; 653 memcpy(vcp->vcp_macs[vcp_nnics], $3, ETHER_ADDR_LEN); 654 } 655 | RDOMAIN NUMBER { 656 if ($2 < 0 || $2 > RT_TABLEID_MAX) { 657 yyerror("invalid rdomain: %lld", $2); 658 YYERROR; 659 } 660 vmc.vmc_ifflags[vcp_nnics] |= VMIFF_RDOMAIN; 661 vmc.vmc_ifrdomain[vcp_nnics] = $2; 662 } 663 | updown { 664 if ($1) 665 vmc.vmc_ifflags[vcp_nnics] |= VMIFF_UP; 666 else 667 vmc.vmc_ifflags[vcp_nnics] &= ~VMIFF_UP; 668 } 669 ; 670 671 optstring : STRING { $$ = $1; } 672 | /* empty */ { $$ = NULL; } 673 ; 674 675 string : STRING string { 676 if (asprintf(&$$, "%s%s", $1, $2) == -1) 677 fatal("asprintf string"); 678 free($1); 679 free($2); 680 } 681 | STRING 682 ; 683 684 lladdr : STRING { 685 struct ether_addr *ea; 686 687 if ((ea = ether_aton($1)) == NULL) { 688 yyerror("invalid address: %s\n", $1); 689 free($1); 690 YYERROR; 691 } 692 free($1); 693 694 memcpy($$, ea, ETHER_ADDR_LEN); 695 } 696 | /* empty */ { 697 memset($$, 0, ETHER_ADDR_LEN); 698 } 699 ; 700 701 local : /* empty */ { $$ = 0; } 702 | LOCAL { $$ = 1; } 703 ; 704 705 locked : /* empty */ { $$ = 0; } 706 | LOCKED { $$ = 1; } 707 ; 708 709 updown : UP { $$ = 1; } 710 | DOWN { $$ = 0; } 711 ; 712 713 disable : ENABLE { $$ = 0; } 714 | DISABLE { $$ = 1; } 715 ; 716 717 bootdevice : CDROM { $$ = VMBOOTDEV_CDROM; } 718 | DISK { $$ = VMBOOTDEV_DISK; } 719 | NET { $$ = VMBOOTDEV_NET; } 720 ; 721 722 optcomma : ',' 723 | 724 ; 725 726 optnl : '\n' optnl 727 | 728 ; 729 730 optcommanl : ',' optnl 731 | nl 732 ; 733 734 nl : '\n' optnl 735 ; 736 737 %% 738 739 struct keywords { 740 const char *k_name; 741 int k_val; 742 }; 743 744 int 745 yyerror(const char *fmt, ...) 746 { 747 va_list ap; 748 char *msg; 749 750 file->errors++; 751 va_start(ap, fmt); 752 if (vasprintf(&msg, fmt, ap) == -1) 753 fatal("yyerror vasprintf"); 754 va_end(ap); 755 log_warnx("%s:%d: %s", file->name, yylval.lineno, msg); 756 free(msg); 757 return (0); 758 } 759 760 int 761 kw_cmp(const void *k, const void *e) 762 { 763 return (strcmp(k, ((const struct keywords *)e)->k_name)); 764 } 765 766 int 767 lookup(char *s) 768 { 769 /* this has to be sorted always */ 770 static const struct keywords keywords[] = { 771 { "add", ADD }, 772 { "allow", ALLOW }, 773 { "boot", BOOT }, 774 { "cdrom", CDROM }, 775 { "delay", DELAY }, 776 { "device", DEVICE }, 777 { "disable", DISABLE }, 778 { "disk", DISK }, 779 { "down", DOWN }, 780 { "enable", ENABLE }, 781 { "format", FORMAT }, 782 { "group", GROUP }, 783 { "id", VMID }, 784 { "include", INCLUDE }, 785 { "inet6", INET6 }, 786 { "instance", INSTANCE }, 787 { "interface", INTERFACE }, 788 { "interfaces", NIFS }, 789 { "lladdr", LLADDR }, 790 { "local", LOCAL }, 791 { "locked", LOCKED }, 792 { "memory", MEMORY }, 793 { "net", NET }, 794 { "owner", OWNER }, 795 { "parallel", PARALLEL }, 796 { "prefix", PREFIX }, 797 { "rdomain", RDOMAIN }, 798 { "size", SIZE }, 799 { "socket", SOCKET }, 800 { "staggered", STAGGERED }, 801 { "start", START }, 802 { "switch", SWITCH }, 803 { "up", UP }, 804 { "vm", VM } 805 }; 806 const struct keywords *p; 807 808 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 809 sizeof(keywords[0]), kw_cmp); 810 811 if (p) 812 return (p->k_val); 813 else 814 return (STRING); 815 } 816 817 #define START_EXPAND 1 818 #define DONE_EXPAND 2 819 820 static int expanding; 821 822 int 823 igetc(void) 824 { 825 int c; 826 827 while (1) { 828 if (file->ungetpos > 0) 829 c = file->ungetbuf[--file->ungetpos]; 830 else 831 c = getc(file->stream); 832 833 if (c == START_EXPAND) 834 expanding = 1; 835 else if (c == DONE_EXPAND) 836 expanding = 0; 837 else 838 break; 839 } 840 return (c); 841 } 842 843 int 844 lgetc(int quotec) 845 { 846 int c, next; 847 848 if (quotec) { 849 if ((c = igetc()) == EOF) { 850 yyerror("reached end of file while parsing " 851 "quoted string"); 852 if (file == topfile || popfile() == EOF) 853 return (EOF); 854 return (quotec); 855 } 856 return (c); 857 } 858 859 while ((c = igetc()) == '\\') { 860 next = igetc(); 861 if (next != '\n') { 862 c = next; 863 break; 864 } 865 yylval.lineno = file->lineno; 866 file->lineno++; 867 } 868 if (c == '\t' || c == ' ') { 869 /* Compress blanks to a single space. */ 870 do { 871 c = getc(file->stream); 872 } while (c == '\t' || c == ' '); 873 ungetc(c, file->stream); 874 c = ' '; 875 } 876 877 if (c == EOF) { 878 /* 879 * Fake EOL when hit EOF for the first time. This gets line 880 * count right if last line in included file is syntactically 881 * invalid and has no newline. 882 */ 883 if (file->eof_reached == 0) { 884 file->eof_reached = 1; 885 return ('\n'); 886 } 887 while (c == EOF) { 888 if (file == topfile || popfile() == EOF) 889 return (EOF); 890 c = igetc(); 891 } 892 } 893 return (c); 894 } 895 896 void 897 lungetc(int c) 898 { 899 if (c == EOF) 900 return; 901 902 if (file->ungetpos >= file->ungetsize) { 903 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 904 if (p == NULL) 905 err(1, "%s", __func__); 906 file->ungetbuf = p; 907 file->ungetsize *= 2; 908 } 909 file->ungetbuf[file->ungetpos++] = c; 910 } 911 912 int 913 findeol(void) 914 { 915 int c; 916 917 /* skip to either EOF or the first real EOL */ 918 while (1) { 919 c = lgetc(0); 920 if (c == '\n') { 921 file->lineno++; 922 break; 923 } 924 if (c == EOF) 925 break; 926 } 927 return (ERROR); 928 } 929 930 int 931 yylex(void) 932 { 933 char buf[8096]; 934 char *p, *val; 935 int quotec, next, c; 936 int token; 937 938 top: 939 p = buf; 940 while ((c = lgetc(0)) == ' ' || c == '\t') 941 ; /* nothing */ 942 943 yylval.lineno = file->lineno; 944 if (c == '#') 945 while ((c = lgetc(0)) != '\n' && c != EOF) 946 ; /* nothing */ 947 if (c == '$' && !expanding) { 948 while (1) { 949 if ((c = lgetc(0)) == EOF) 950 return (0); 951 952 if (p + 1 >= buf + sizeof(buf) - 1) { 953 yyerror("string too long"); 954 return (findeol()); 955 } 956 if (isalnum(c) || c == '_') { 957 *p++ = c; 958 continue; 959 } 960 *p = '\0'; 961 lungetc(c); 962 break; 963 } 964 val = symget(buf); 965 if (val == NULL) { 966 yyerror("macro '%s' not defined", buf); 967 return (findeol()); 968 } 969 p = val + strlen(val) - 1; 970 lungetc(DONE_EXPAND); 971 while (p >= val) { 972 lungetc((unsigned char)*p); 973 p--; 974 } 975 lungetc(START_EXPAND); 976 goto top; 977 } 978 979 switch (c) { 980 case '\'': 981 case '"': 982 quotec = c; 983 while (1) { 984 if ((c = lgetc(quotec)) == EOF) 985 return (0); 986 if (c == '\n') { 987 file->lineno++; 988 continue; 989 } else if (c == '\\') { 990 if ((next = lgetc(quotec)) == EOF) 991 return (0); 992 if (next == quotec || next == ' ' || 993 next == '\t') 994 c = next; 995 else if (next == '\n') { 996 file->lineno++; 997 continue; 998 } else 999 lungetc(next); 1000 } else if (c == quotec) { 1001 *p = '\0'; 1002 break; 1003 } else if (c == '\0') { 1004 yyerror("syntax error"); 1005 return (findeol()); 1006 } 1007 if (p + 1 >= buf + sizeof(buf) - 1) { 1008 yyerror("string too long"); 1009 return (findeol()); 1010 } 1011 *p++ = c; 1012 } 1013 yylval.v.string = strdup(buf); 1014 if (yylval.v.string == NULL) 1015 fatal("yylex: strdup"); 1016 return (STRING); 1017 } 1018 1019 #define allowed_to_end_number(x) \ 1020 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 1021 1022 if (c == '-' || isdigit(c)) { 1023 do { 1024 *p++ = c; 1025 if ((size_t)(p-buf) >= sizeof(buf)) { 1026 yyerror("string too long"); 1027 return (findeol()); 1028 } 1029 } while ((c = lgetc(0)) != EOF && isdigit(c)); 1030 lungetc(c); 1031 if (p == buf + 1 && buf[0] == '-') 1032 goto nodigits; 1033 if (c == EOF || allowed_to_end_number(c)) { 1034 const char *errstr = NULL; 1035 1036 *p = '\0'; 1037 yylval.v.number = strtonum(buf, LLONG_MIN, 1038 LLONG_MAX, &errstr); 1039 if (errstr) { 1040 yyerror("\"%s\" invalid number: %s", 1041 buf, errstr); 1042 return (findeol()); 1043 } 1044 return (NUMBER); 1045 } else { 1046 nodigits: 1047 while (p > buf + 1) 1048 lungetc((unsigned char)*--p); 1049 c = (unsigned char)*--p; 1050 if (c == '-') 1051 return (c); 1052 } 1053 } 1054 1055 #define allowed_in_string(x) \ 1056 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 1057 x != '{' && x != '}' && \ 1058 x != '!' && x != '=' && x != '#' && \ 1059 x != ',')) 1060 1061 if (isalnum(c) || c == ':' || c == '_' || c == '/') { 1062 do { 1063 *p++ = c; 1064 if ((size_t)(p-buf) >= sizeof(buf)) { 1065 yyerror("string too long"); 1066 return (findeol()); 1067 } 1068 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 1069 lungetc(c); 1070 *p = '\0'; 1071 if ((token = lookup(buf)) == STRING) 1072 if ((yylval.v.string = strdup(buf)) == NULL) 1073 fatal("yylex: strdup"); 1074 return (token); 1075 } 1076 if (c == '\n') { 1077 yylval.lineno = file->lineno; 1078 file->lineno++; 1079 } 1080 if (c == EOF) 1081 return (0); 1082 return (c); 1083 } 1084 1085 struct file * 1086 pushfile(const char *name, int secret) 1087 { 1088 struct file *nfile; 1089 1090 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 1091 log_warn("%s", __func__); 1092 return (NULL); 1093 } 1094 if ((nfile->name = strdup(name)) == NULL) { 1095 log_warn("%s", __func__); 1096 free(nfile); 1097 return (NULL); 1098 } 1099 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 1100 free(nfile->name); 1101 free(nfile); 1102 return (NULL); 1103 } 1104 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 1105 nfile->ungetsize = 16; 1106 nfile->ungetbuf = malloc(nfile->ungetsize); 1107 if (nfile->ungetbuf == NULL) { 1108 log_warn("%s", __func__); 1109 fclose(nfile->stream); 1110 free(nfile->name); 1111 free(nfile); 1112 return (NULL); 1113 } 1114 TAILQ_INSERT_TAIL(&files, nfile, entry); 1115 return (nfile); 1116 } 1117 1118 int 1119 popfile(void) 1120 { 1121 struct file *prev; 1122 1123 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 1124 prev->errors += file->errors; 1125 1126 TAILQ_REMOVE(&files, file, entry); 1127 fclose(file->stream); 1128 free(file->name); 1129 free(file->ungetbuf); 1130 free(file); 1131 file = prev; 1132 return (file ? 0 : EOF); 1133 } 1134 1135 int 1136 parse_config(const char *filename) 1137 { 1138 struct sym *sym, *next; 1139 1140 if ((file = pushfile(filename, 0)) == NULL) { 1141 log_warn("failed to open %s", filename); 1142 if (errno == ENOENT) 1143 return (0); 1144 return (-1); 1145 } 1146 topfile = file; 1147 setservent(1); 1148 1149 /* Set the default switch type */ 1150 (void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type)); 1151 1152 yyparse(); 1153 errors = file->errors; 1154 popfile(); 1155 1156 endservent(); 1157 1158 /* Free macros and check which have not been used. */ 1159 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 1160 if (!sym->used) 1161 fprintf(stderr, "warning: macro '%s' not " 1162 "used\n", sym->nam); 1163 if (!sym->persist) { 1164 free(sym->nam); 1165 free(sym->val); 1166 TAILQ_REMOVE(&symhead, sym, entry); 1167 free(sym); 1168 } 1169 } 1170 1171 if (errors) 1172 return (-1); 1173 1174 return (0); 1175 } 1176 1177 int 1178 symset(const char *nam, const char *val, int persist) 1179 { 1180 struct sym *sym; 1181 1182 TAILQ_FOREACH(sym, &symhead, entry) { 1183 if (strcmp(nam, sym->nam) == 0) 1184 break; 1185 } 1186 1187 if (sym != NULL) { 1188 if (sym->persist == 1) 1189 return (0); 1190 else { 1191 free(sym->nam); 1192 free(sym->val); 1193 TAILQ_REMOVE(&symhead, sym, entry); 1194 free(sym); 1195 } 1196 } 1197 if ((sym = calloc(1, sizeof(*sym))) == NULL) 1198 return (-1); 1199 1200 sym->nam = strdup(nam); 1201 if (sym->nam == NULL) { 1202 free(sym); 1203 return (-1); 1204 } 1205 sym->val = strdup(val); 1206 if (sym->val == NULL) { 1207 free(sym->nam); 1208 free(sym); 1209 return (-1); 1210 } 1211 sym->used = 0; 1212 sym->persist = persist; 1213 TAILQ_INSERT_TAIL(&symhead, sym, entry); 1214 return (0); 1215 } 1216 1217 int 1218 cmdline_symset(char *s) 1219 { 1220 char *sym, *val; 1221 int ret; 1222 1223 if ((val = strrchr(s, '=')) == NULL) 1224 return (-1); 1225 sym = strndup(s, val - s); 1226 if (sym == NULL) 1227 fatal("%s: strndup", __func__); 1228 ret = symset(sym, val + 1, 1); 1229 free(sym); 1230 1231 return (ret); 1232 } 1233 1234 char * 1235 symget(const char *nam) 1236 { 1237 struct sym *sym; 1238 1239 TAILQ_FOREACH(sym, &symhead, entry) { 1240 if (strcmp(nam, sym->nam) == 0) { 1241 sym->used = 1; 1242 return (sym->val); 1243 } 1244 } 1245 return (NULL); 1246 } 1247 1248 ssize_t 1249 parse_size(char *word, int64_t val) 1250 { 1251 ssize_t size; 1252 long long res; 1253 1254 if (word != NULL) { 1255 if (scan_scaled(word, &res) != 0) { 1256 log_warn("invalid size: %s", word); 1257 return (-1); 1258 } 1259 val = (int64_t)res; 1260 } 1261 1262 if (val < (1024 * 1024)) { 1263 log_warnx("size must be at least one megabyte"); 1264 return (-1); 1265 } else 1266 size = val / 1024 / 1024; 1267 1268 if ((size * 1024 * 1024) != val) 1269 log_warnx("size rounded to %zd megabytes", size); 1270 1271 return ((ssize_t)size); 1272 } 1273 1274 int 1275 parse_disk(char *word, int type) 1276 { 1277 char buf[BUFSIZ], path[PATH_MAX]; 1278 int fd; 1279 ssize_t len; 1280 1281 if (vcp->vcp_ndisks >= VMM_MAX_DISKS_PER_VM) { 1282 log_warnx("too many disks"); 1283 return (-1); 1284 } 1285 1286 if (realpath(word, path) == NULL) { 1287 log_warn("disk %s", word); 1288 return (-1); 1289 } 1290 1291 if (!type) { 1292 /* Use raw as the default format */ 1293 type = VMDF_RAW; 1294 1295 /* Try to derive the format from the file signature */ 1296 if ((fd = open(path, O_RDONLY)) != -1) { 1297 len = read(fd, buf, sizeof(buf)); 1298 close(fd); 1299 if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 1300 strncmp(buf, VM_MAGIC_QCOW, 1301 strlen(VM_MAGIC_QCOW)) == 0) { 1302 /* The qcow version will be checked later */ 1303 type = VMDF_QCOW2; 1304 } 1305 } 1306 } 1307 1308 if (strlcpy(vcp->vcp_disks[vcp->vcp_ndisks], path, 1309 VMM_MAX_PATH_DISK) >= VMM_MAX_PATH_DISK) { 1310 log_warnx("disk path too long"); 1311 return (-1); 1312 } 1313 vmc.vmc_disktypes[vcp->vcp_ndisks] = type; 1314 1315 vcp->vcp_ndisks++; 1316 1317 return (0); 1318 } 1319 1320 unsigned int 1321 parse_format(const char *word) 1322 { 1323 if (strcasecmp(word, "raw") == 0) 1324 return (VMDF_RAW); 1325 else if (strcasecmp(word, "qcow2") == 0) 1326 return (VMDF_QCOW2); 1327 return (0); 1328 } 1329 1330 int 1331 host(const char *str, struct address *h) 1332 { 1333 struct addrinfo hints, *res; 1334 int prefixlen; 1335 char *s, *p; 1336 const char *errstr; 1337 1338 if ((s = strdup(str)) == NULL) { 1339 log_warn("%s", __func__); 1340 goto fail; 1341 } 1342 1343 if ((p = strrchr(s, '/')) != NULL) { 1344 *p++ = '\0'; 1345 prefixlen = strtonum(p, 0, 128, &errstr); 1346 if (errstr) { 1347 log_warnx("prefixlen is %s: %s", errstr, p); 1348 goto fail; 1349 } 1350 } else 1351 prefixlen = 128; 1352 1353 memset(&hints, 0, sizeof(hints)); 1354 hints.ai_family = AF_UNSPEC; 1355 hints.ai_flags = AI_NUMERICHOST; 1356 if (getaddrinfo(s, NULL, &hints, &res) == 0) { 1357 memset(h, 0, sizeof(*h)); 1358 memcpy(&h->ss, res->ai_addr, res->ai_addrlen); 1359 h->prefixlen = prefixlen; 1360 freeaddrinfo(res); 1361 free(s); 1362 return (0); 1363 } 1364 1365 fail: 1366 free(s); 1367 return (-1); 1368 } 1369