1 /* $OpenBSD: parse.y,v 1.6 2014/11/20 05:51:20 jsg 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 __attribute__((__format__ (printf, 1, 2))) 58 __attribute__((__nonnull__ (1))); 59 int kw_cmp(const void *, const void *); 60 int lookup(char *); 61 int lgetc(int); 62 int lungetc(int); 63 int findeol(void); 64 65 struct ldom_config *conf; 66 67 struct opts { 68 uint64_t mac_addr; 69 uint64_t mtu; 70 } opts; 71 void opts_default(void); 72 73 typedef struct { 74 union { 75 int64_t number; 76 char *string; 77 struct opts opts; 78 } v; 79 int lineno; 80 } YYSTYPE; 81 82 %} 83 84 %token DOMAIN 85 %token VCPU MEMORY VDISK VNET 86 %token MAC_ADDR MTU 87 %token ERROR 88 %token <v.string> STRING 89 %token <v.number> NUMBER 90 %type <v.number> memory 91 %type <v.opts> vnet_opts vnet_opts_l vnet_opt 92 %type <v.opts> mac_addr 93 %type <v.opts> mtu 94 %% 95 96 grammar : /* empty */ 97 | grammar '\n' 98 | grammar domain '\n' 99 | grammar error '\n' { file->errors++; } 100 ; 101 102 domain : DOMAIN STRING optnl '{' optnl { 103 domain = xzalloc(sizeof(struct domain)); 104 domain->name = $2; 105 SIMPLEQ_INIT(&domain->vdisk_list); 106 SIMPLEQ_INIT(&domain->vnet_list); 107 } 108 domainopts_l '}' { 109 /* domain names need to be unique. */ 110 struct domain *odomain; 111 SIMPLEQ_FOREACH(odomain, &conf->domain_list, entry) 112 if (strcmp(odomain->name, $2) == 0) { 113 yyerror("duplicate domain name: %s", $2); 114 YYERROR; 115 } 116 SIMPLEQ_INSERT_TAIL(&conf->domain_list, domain, entry); 117 domain = NULL; 118 } 119 ; 120 121 domainopts_l : domainopts_l domainoptsl 122 | domainoptsl 123 ; 124 125 domainoptsl : domainopts nl 126 ; 127 128 domainopts : VCPU NUMBER { 129 domain->vcpu = $2; 130 } 131 | MEMORY memory { 132 domain->memory = $2; 133 } 134 | VDISK STRING { 135 struct vdisk *vdisk = xmalloc(sizeof(struct vdisk)); 136 vdisk->path = $2; 137 SIMPLEQ_INSERT_TAIL(&domain->vdisk_list, vdisk, entry); 138 } 139 | VNET vnet_opts { 140 struct vnet *vnet = xmalloc(sizeof(struct vnet)); 141 vnet->mac_addr = $2.mac_addr; 142 vnet->mtu = $2.mtu; 143 SIMPLEQ_INSERT_TAIL(&domain->vnet_list, vnet, entry); 144 } 145 ; 146 147 vnet_opts : { opts_default(); } 148 vnet_opts_l 149 { $$ = opts; } 150 | { opts_default(); $$ = opts; } 151 ; 152 vnet_opts_l : vnet_opts_l vnet_opt 153 | vnet_opt 154 ; 155 vnet_opt : mac_addr 156 | mtu 157 ; 158 159 mac_addr : MAC_ADDR '=' STRING { 160 struct ether_addr *ea; 161 162 if ((ea = ether_aton($3)) == NULL) { 163 yyerror("invalid address: %s", $3); 164 YYERROR; 165 } 166 167 opts.mac_addr = 168 (uint64_t)ea->ether_addr_octet[0] << 40 | 169 (uint64_t)ea->ether_addr_octet[1] << 32 | 170 ea->ether_addr_octet[2] << 24 | 171 ea->ether_addr_octet[3] << 16 | 172 ea->ether_addr_octet[4] << 8 | 173 ea->ether_addr_octet[5]; 174 } 175 ; 176 177 mtu : MTU '=' NUMBER { 178 opts.mtu = $3; 179 } 180 ; 181 182 memory : NUMBER { 183 $$ = $1; 184 } 185 | STRING { 186 uint64_t size; 187 char *cp; 188 189 size = strtoll($1, &cp, 10); 190 if (cp != NULL) { 191 if (strcmp(cp, "K") == 0) 192 size *= 1024; 193 else if (strcmp(cp, "M") == 0) 194 size *= 1024 * 1024; 195 else if (strcmp(cp, "G") == 0) 196 size *= 1024 * 1024 * 1024; 197 else { 198 yyerror("unknown unit %s", cp); 199 YYERROR; 200 } 201 } 202 $$ = size; 203 } 204 ; 205 206 optnl : '\n' optnl 207 | 208 ; 209 210 nl : '\n' optnl /* one newline or more */ 211 ; 212 213 %% 214 215 void 216 opts_default(void) 217 { 218 opts.mac_addr = -1; 219 opts.mtu = 1500; 220 } 221 222 struct keywords { 223 const char *k_name; 224 int k_val; 225 }; 226 227 int 228 yyerror(const char *fmt, ...) 229 { 230 va_list ap; 231 232 file->errors++; 233 va_start(ap, fmt); 234 fprintf(stderr, "%s:%d ", file->name, yylval.lineno); 235 vfprintf(stderr, fmt, ap); 236 fprintf(stderr, "\n"); 237 va_end(ap); 238 return (0); 239 } 240 241 int 242 kw_cmp(const void *k, const void *e) 243 { 244 return (strcmp(k, ((const struct keywords *)e)->k_name)); 245 } 246 247 int 248 lookup(char *s) 249 { 250 /* this has to be sorted always */ 251 static const struct keywords keywords[] = { 252 { "domain", DOMAIN}, 253 { "mac-addr", MAC_ADDR}, 254 { "memory", MEMORY}, 255 { "mtu", MTU}, 256 { "vcpu", VCPU}, 257 { "vdisk", VDISK}, 258 { "vnet", VNET} 259 }; 260 const struct keywords *p; 261 262 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 263 sizeof(keywords[0]), kw_cmp); 264 265 if (p) 266 return (p->k_val); 267 else 268 return (STRING); 269 } 270 271 #define MAXPUSHBACK 128 272 273 u_char *parsebuf; 274 int parseindex; 275 u_char pushback_buffer[MAXPUSHBACK]; 276 int pushback_index = 0; 277 278 int 279 lgetc(int quotec) 280 { 281 int c, next; 282 283 if (parsebuf) { 284 /* Read character from the parsebuffer instead of input. */ 285 if (parseindex >= 0) { 286 c = parsebuf[parseindex++]; 287 if (c != '\0') 288 return (c); 289 parsebuf = NULL; 290 } else 291 parseindex++; 292 } 293 294 if (pushback_index) 295 return (pushback_buffer[--pushback_index]); 296 297 if (quotec) { 298 if ((c = getc(file->stream)) == EOF) { 299 yyerror("reached end of file while parsing " 300 "quoted string"); 301 if (file == topfile || popfile() == EOF) 302 return (EOF); 303 return (quotec); 304 } 305 return (c); 306 } 307 308 while ((c = getc(file->stream)) == '\\') { 309 next = getc(file->stream); 310 if (next != '\n') { 311 c = next; 312 break; 313 } 314 yylval.lineno = file->lineno; 315 file->lineno++; 316 } 317 318 while (c == EOF) { 319 if (file == topfile || popfile() == EOF) 320 return (EOF); 321 c = getc(file->stream); 322 } 323 return (c); 324 } 325 326 int 327 lungetc(int c) 328 { 329 if (c == EOF) 330 return (EOF); 331 if (parsebuf) { 332 parseindex--; 333 if (parseindex >= 0) 334 return (c); 335 } 336 if (pushback_index < MAXPUSHBACK-1) 337 return (pushback_buffer[pushback_index++] = c); 338 else 339 return (EOF); 340 } 341 342 int 343 findeol(void) 344 { 345 int c; 346 347 parsebuf = NULL; 348 349 /* skip to either EOF or the first real EOL */ 350 while (1) { 351 if (pushback_index) 352 c = pushback_buffer[--pushback_index]; 353 else 354 c = lgetc(0); 355 if (c == '\n') { 356 file->lineno++; 357 break; 358 } 359 if (c == EOF) 360 break; 361 } 362 return (ERROR); 363 } 364 365 int 366 yylex(void) 367 { 368 u_char buf[8096]; 369 u_char *p; 370 int quotec, next, c; 371 int token; 372 373 p = buf; 374 while ((c = lgetc(0)) == ' ' || c == '\t') 375 ; /* nothing */ 376 377 yylval.lineno = file->lineno; 378 if (c == '#') 379 while ((c = lgetc(0)) != '\n' && c != EOF) 380 ; /* nothing */ 381 382 switch (c) { 383 case '\'': 384 case '"': 385 quotec = c; 386 while (1) { 387 if ((c = lgetc(quotec)) == EOF) 388 return (0); 389 if (c == '\n') { 390 file->lineno++; 391 continue; 392 } else if (c == '\\') { 393 if ((next = lgetc(quotec)) == EOF) 394 return (0); 395 if (next == quotec || c == ' ' || c == '\t') 396 c = next; 397 else if (next == '\n') { 398 file->lineno++; 399 continue; 400 } else 401 lungetc(next); 402 } else if (c == quotec) { 403 *p = '\0'; 404 break; 405 } else if (c == '\0') { 406 yyerror("syntax error"); 407 return (findeol()); 408 } 409 if (p + 1 >= buf + sizeof(buf) - 1) { 410 yyerror("string too long"); 411 return (findeol()); 412 } 413 *p++ = c; 414 } 415 yylval.v.string = xstrdup(buf); 416 return (STRING); 417 } 418 419 #define allowed_to_end_number(x) \ 420 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 421 422 if (c == '-' || isdigit(c)) { 423 do { 424 *p++ = c; 425 if ((unsigned)(p-buf) >= sizeof(buf)) { 426 yyerror("string too long"); 427 return (findeol()); 428 } 429 } while ((c = lgetc(0)) != EOF && isdigit(c)); 430 lungetc(c); 431 if (p == buf + 1 && buf[0] == '-') 432 goto nodigits; 433 if (c == EOF || allowed_to_end_number(c)) { 434 const char *errstr = NULL; 435 436 *p = '\0'; 437 yylval.v.number = strtonum(buf, LLONG_MIN, 438 LLONG_MAX, &errstr); 439 if (errstr) { 440 yyerror("\"%s\" invalid number: %s", 441 buf, errstr); 442 return (findeol()); 443 } 444 return (NUMBER); 445 } else { 446 nodigits: 447 while (p > buf + 1) 448 lungetc(*--p); 449 c = *--p; 450 if (c == '-') 451 return (c); 452 } 453 } 454 455 #define allowed_in_string(x) \ 456 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 457 x != '{' && x != '}' && x != '<' && x != '>' && \ 458 x != '!' && x != '=' && x != '/' && x != '#' && \ 459 x != ',')) 460 461 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 462 do { 463 *p++ = c; 464 if ((unsigned)(p-buf) >= sizeof(buf)) { 465 yyerror("string too long"); 466 return (findeol()); 467 } 468 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 469 lungetc(c); 470 *p = '\0'; 471 if ((token = lookup(buf)) == STRING) 472 yylval.v.string = xstrdup(buf); 473 return (token); 474 } 475 if (c == '\n') { 476 yylval.lineno = file->lineno; 477 file->lineno++; 478 } 479 if (c == EOF) 480 return (0); 481 return (c); 482 } 483 484 struct file * 485 pushfile(const char *name) 486 { 487 struct file *nfile; 488 489 nfile = xzalloc(sizeof(struct file)); 490 nfile->name = xstrdup(name); 491 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 492 warn("%s", nfile->name); 493 free(nfile->name); 494 free(nfile); 495 return (NULL); 496 } 497 nfile->lineno = 1; 498 TAILQ_INSERT_TAIL(&files, nfile, entry); 499 return (nfile); 500 } 501 502 int 503 popfile(void) 504 { 505 struct file *prev; 506 507 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 508 prev->errors += file->errors; 509 510 TAILQ_REMOVE(&files, file, entry); 511 fclose(file->stream); 512 free(file->name); 513 free(file); 514 file = prev; 515 return (file ? 0 : EOF); 516 } 517 518 int 519 parse_config(const char *filename, struct ldom_config *xconf) 520 { 521 int errors = 0; 522 523 conf = xconf; 524 525 if ((file = pushfile(filename)) == NULL) { 526 return (-1); 527 } 528 topfile = file; 529 530 yyparse(); 531 errors = file->errors; 532 popfile(); 533 534 return (errors ? -1 : 0); 535 } 536