1 /* $OpenBSD: parse.y,v 1.4 2013/11/25 12:57:18 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Mark Kettenis <kettenis@openbsd.org> 5 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 6 * Copyright (c) 2001 Markus Friedl. All rights reserved. 7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 8 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 %{ 24 #include <sys/types.h> 25 #include <sys/socket.h> 26 #include <sys/queue.h> 27 28 #include <net/if.h> 29 #include <netinet/in.h> 30 #include <netinet/if_ether.h> 31 32 #include <ctype.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <limits.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include "ldomctl.h" 42 #include "util.h" 43 44 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 45 static struct file { 46 TAILQ_ENTRY(file) entry; 47 FILE *stream; 48 char *name; 49 int lineno; 50 int errors; 51 } *file, *topfile; 52 struct file *pushfile(const char *); 53 int popfile(void); 54 int yyparse(void); 55 int yylex(void); 56 int yyerror(const char *, ...); 57 int kw_cmp(const void *, const void *); 58 int lookup(char *); 59 int lgetc(int); 60 int lungetc(int); 61 int findeol(void); 62 63 struct ldom_config *conf; 64 65 struct opts { 66 uint64_t mac_addr; 67 uint64_t mtu; 68 } opts; 69 void opts_default(void); 70 71 typedef struct { 72 union { 73 int64_t number; 74 char *string; 75 struct opts opts; 76 } v; 77 int lineno; 78 } YYSTYPE; 79 80 %} 81 82 %token DOMAIN 83 %token VCPU MEMORY VDISK VNET 84 %token MAC_ADDR MTU 85 %token ERROR 86 %token <v.string> STRING 87 %token <v.number> NUMBER 88 %type <v.number> memory 89 %type <v.opts> vnet_opts vnet_opts_l vnet_opt 90 %type <v.opts> mac_addr 91 %type <v.opts> mtu 92 %% 93 94 grammar : /* empty */ 95 | grammar '\n' 96 | grammar domain '\n' 97 | grammar error '\n' { file->errors++; } 98 ; 99 100 domain : DOMAIN STRING optnl '{' optnl { 101 domain = xzalloc(sizeof(struct domain)); 102 domain->name = $2; 103 SIMPLEQ_INIT(&domain->vdisk_list); 104 SIMPLEQ_INIT(&domain->vnet_list); 105 } 106 domainopts_l '}' { 107 /* domain names need to be unique. */ 108 struct domain *odomain; 109 SIMPLEQ_FOREACH(odomain, &conf->domain_list, entry) 110 if (strcmp(odomain->name, $2) == 0) { 111 yyerror("duplicate domain name: %s", $2); 112 YYERROR; 113 } 114 SIMPLEQ_INSERT_TAIL(&conf->domain_list, domain, entry); 115 domain = NULL; 116 } 117 ; 118 119 domainopts_l : domainopts_l domainoptsl 120 | domainoptsl 121 ; 122 123 domainoptsl : domainopts nl 124 ; 125 126 domainopts : VCPU NUMBER { 127 domain->vcpu = $2; 128 } 129 | MEMORY memory { 130 domain->memory = $2; 131 } 132 | VDISK STRING { 133 struct vdisk *vdisk = xmalloc(sizeof(struct vdisk)); 134 vdisk->path = $2; 135 SIMPLEQ_INSERT_TAIL(&domain->vdisk_list, vdisk, entry); 136 } 137 | VNET vnet_opts { 138 struct vnet *vnet = xmalloc(sizeof(struct vnet)); 139 vnet->mac_addr = $2.mac_addr; 140 vnet->mtu = $2.mtu; 141 SIMPLEQ_INSERT_TAIL(&domain->vnet_list, vnet, entry); 142 } 143 ; 144 145 vnet_opts : { opts_default(); } 146 vnet_opts_l 147 { $$ = opts; } 148 | { opts_default(); $$ = opts; } 149 ; 150 vnet_opts_l : vnet_opts_l vnet_opt 151 | vnet_opt 152 ; 153 vnet_opt : mac_addr 154 | mtu 155 ; 156 157 mac_addr : MAC_ADDR '=' STRING { 158 struct ether_addr *ea; 159 160 if ((ea = ether_aton($3)) == NULL) { 161 yyerror("invalid address: %s", $3); 162 YYERROR; 163 } 164 165 opts.mac_addr = 166 (uint64_t)ea->ether_addr_octet[0] << 40 | 167 (uint64_t)ea->ether_addr_octet[1] << 32 | 168 ea->ether_addr_octet[2] << 24 | 169 ea->ether_addr_octet[3] << 16 | 170 ea->ether_addr_octet[4] << 8 | 171 ea->ether_addr_octet[5]; 172 } 173 ; 174 175 mtu : MTU '=' NUMBER { 176 opts.mtu = $3; 177 } 178 ; 179 180 memory : NUMBER { 181 $$ = $1; 182 } 183 | STRING { 184 uint64_t size; 185 char *cp; 186 187 size = strtoll($1, &cp, 10); 188 if (cp != NULL) { 189 if (strcmp(cp, "K") == 0) 190 size *= 1024; 191 else if (strcmp(cp, "M") == 0) 192 size *= 1024 * 1024; 193 else if (strcmp(cp, "G") == 0) 194 size *= 1024 * 1024 * 1024; 195 else { 196 yyerror("unknown unit %s", cp); 197 YYERROR; 198 } 199 } 200 $$ = size; 201 } 202 ; 203 204 optnl : '\n' optnl 205 | 206 ; 207 208 nl : '\n' optnl /* one newline or more */ 209 ; 210 211 %% 212 213 void 214 opts_default(void) 215 { 216 opts.mac_addr = -1; 217 opts.mtu = 1500; 218 } 219 220 struct keywords { 221 const char *k_name; 222 int k_val; 223 }; 224 225 int 226 yyerror(const char *fmt, ...) 227 { 228 va_list ap; 229 230 file->errors++; 231 va_start(ap, fmt); 232 fprintf(stderr, "%s:%d ", file->name, yylval.lineno); 233 vfprintf(stderr, fmt, ap); 234 fprintf(stderr, "\n"); 235 va_end(ap); 236 return (0); 237 } 238 239 int 240 kw_cmp(const void *k, const void *e) 241 { 242 return (strcmp(k, ((const struct keywords *)e)->k_name)); 243 } 244 245 int 246 lookup(char *s) 247 { 248 /* this has to be sorted always */ 249 static const struct keywords keywords[] = { 250 { "domain", DOMAIN}, 251 { "mac-addr", MAC_ADDR}, 252 { "memory", MEMORY}, 253 { "mtu", MTU}, 254 { "vcpu", VCPU}, 255 { "vdisk", VDISK}, 256 { "vnet", VNET} 257 }; 258 const struct keywords *p; 259 260 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 261 sizeof(keywords[0]), kw_cmp); 262 263 if (p) 264 return (p->k_val); 265 else 266 return (STRING); 267 } 268 269 #define MAXPUSHBACK 128 270 271 u_char *parsebuf; 272 int parseindex; 273 u_char pushback_buffer[MAXPUSHBACK]; 274 int pushback_index = 0; 275 276 int 277 lgetc(int quotec) 278 { 279 int c, next; 280 281 if (parsebuf) { 282 /* Read character from the parsebuffer instead of input. */ 283 if (parseindex >= 0) { 284 c = parsebuf[parseindex++]; 285 if (c != '\0') 286 return (c); 287 parsebuf = NULL; 288 } else 289 parseindex++; 290 } 291 292 if (pushback_index) 293 return (pushback_buffer[--pushback_index]); 294 295 if (quotec) { 296 if ((c = getc(file->stream)) == EOF) { 297 yyerror("reached end of file while parsing " 298 "quoted string"); 299 if (file == topfile || popfile() == EOF) 300 return (EOF); 301 return (quotec); 302 } 303 return (c); 304 } 305 306 while ((c = getc(file->stream)) == '\\') { 307 next = getc(file->stream); 308 if (next != '\n') { 309 c = next; 310 break; 311 } 312 yylval.lineno = file->lineno; 313 file->lineno++; 314 } 315 316 while (c == EOF) { 317 if (file == topfile || popfile() == EOF) 318 return (EOF); 319 c = getc(file->stream); 320 } 321 return (c); 322 } 323 324 int 325 lungetc(int c) 326 { 327 if (c == EOF) 328 return (EOF); 329 if (parsebuf) { 330 parseindex--; 331 if (parseindex >= 0) 332 return (c); 333 } 334 if (pushback_index < MAXPUSHBACK-1) 335 return (pushback_buffer[pushback_index++] = c); 336 else 337 return (EOF); 338 } 339 340 int 341 findeol(void) 342 { 343 int c; 344 345 parsebuf = NULL; 346 347 /* skip to either EOF or the first real EOL */ 348 while (1) { 349 if (pushback_index) 350 c = pushback_buffer[--pushback_index]; 351 else 352 c = lgetc(0); 353 if (c == '\n') { 354 file->lineno++; 355 break; 356 } 357 if (c == EOF) 358 break; 359 } 360 return (ERROR); 361 } 362 363 int 364 yylex(void) 365 { 366 u_char buf[8096]; 367 u_char *p; 368 int quotec, next, c; 369 int token; 370 371 p = buf; 372 while ((c = lgetc(0)) == ' ' || c == '\t') 373 ; /* nothing */ 374 375 yylval.lineno = file->lineno; 376 if (c == '#') 377 while ((c = lgetc(0)) != '\n' && c != EOF) 378 ; /* nothing */ 379 380 switch (c) { 381 case '\'': 382 case '"': 383 quotec = c; 384 while (1) { 385 if ((c = lgetc(quotec)) == EOF) 386 return (0); 387 if (c == '\n') { 388 file->lineno++; 389 continue; 390 } else if (c == '\\') { 391 if ((next = lgetc(quotec)) == EOF) 392 return (0); 393 if (next == quotec || c == ' ' || c == '\t') 394 c = next; 395 else if (next == '\n') { 396 file->lineno++; 397 continue; 398 } else 399 lungetc(next); 400 } else if (c == quotec) { 401 *p = '\0'; 402 break; 403 } 404 if (p + 1 >= buf + sizeof(buf) - 1) { 405 yyerror("string too long"); 406 return (findeol()); 407 } 408 *p++ = c; 409 } 410 yylval.v.string = xstrdup(buf); 411 return (STRING); 412 } 413 414 #define allowed_to_end_number(x) \ 415 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 416 417 if (c == '-' || isdigit(c)) { 418 do { 419 *p++ = c; 420 if ((unsigned)(p-buf) >= sizeof(buf)) { 421 yyerror("string too long"); 422 return (findeol()); 423 } 424 } while ((c = lgetc(0)) != EOF && isdigit(c)); 425 lungetc(c); 426 if (p == buf + 1 && buf[0] == '-') 427 goto nodigits; 428 if (c == EOF || allowed_to_end_number(c)) { 429 const char *errstr = NULL; 430 431 *p = '\0'; 432 yylval.v.number = strtonum(buf, LLONG_MIN, 433 LLONG_MAX, &errstr); 434 if (errstr) { 435 yyerror("\"%s\" invalid number: %s", 436 buf, errstr); 437 return (findeol()); 438 } 439 return (NUMBER); 440 } else { 441 nodigits: 442 while (p > buf + 1) 443 lungetc(*--p); 444 c = *--p; 445 if (c == '-') 446 return (c); 447 } 448 } 449 450 #define allowed_in_string(x) \ 451 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 452 x != '{' && x != '}' && x != '<' && x != '>' && \ 453 x != '!' && x != '=' && x != '/' && x != '#' && \ 454 x != ',')) 455 456 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 457 do { 458 *p++ = c; 459 if ((unsigned)(p-buf) >= sizeof(buf)) { 460 yyerror("string too long"); 461 return (findeol()); 462 } 463 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 464 lungetc(c); 465 *p = '\0'; 466 if ((token = lookup(buf)) == STRING) 467 yylval.v.string = xstrdup(buf); 468 return (token); 469 } 470 if (c == '\n') { 471 yylval.lineno = file->lineno; 472 file->lineno++; 473 } 474 if (c == EOF) 475 return (0); 476 return (c); 477 } 478 479 struct file * 480 pushfile(const char *name) 481 { 482 struct file *nfile; 483 484 nfile = xzalloc(sizeof(struct file)); 485 nfile->name = xstrdup(name); 486 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 487 warn("%s", nfile->name); 488 free(nfile->name); 489 free(nfile); 490 return (NULL); 491 } 492 nfile->lineno = 1; 493 TAILQ_INSERT_TAIL(&files, nfile, entry); 494 return (nfile); 495 } 496 497 int 498 popfile(void) 499 { 500 struct file *prev; 501 502 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 503 prev->errors += file->errors; 504 505 TAILQ_REMOVE(&files, file, entry); 506 fclose(file->stream); 507 free(file->name); 508 free(file); 509 file = prev; 510 return (file ? 0 : EOF); 511 } 512 513 int 514 parse_config(const char *filename, struct ldom_config *xconf) 515 { 516 int errors = 0; 517 518 conf = xconf; 519 520 if ((file = pushfile(filename)) == NULL) { 521 return (-1); 522 } 523 topfile = file; 524 525 yyparse(); 526 errors = file->errors; 527 popfile(); 528 529 return (errors ? -1 : 0); 530 } 531