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