1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <errno.h> 8 #include <locale.h> 9 #include <stddef.h> 10 #include <limits.h> 11 #include <rctl.h> 12 #include <regex.h> 13 #include <ctype.h> 14 15 #include <sys/debug.h> 16 17 #include "attrib.h" 18 #include "resctl.h" 19 #include "util.h" 20 21 #define BOSTR_REG_EXP "^" 22 #define EOSTR_REG_EXP "$" 23 #define EQUAL_REG_EXP "=" 24 #define STRN0_REG_EXP "(.*)" 25 #define IDENT_REG_EXP "[[:alpha:]][[:alnum:]_.-]*" 26 #define INTNM_REG_EXP "[[:digit:]]+" 27 #define SIGAC_REG_EXP "sig(nal)?(=.*)?" 28 #define SIGHD_REG_EXP "(signal|sig)" 29 #define SIGVL_REG_EXP "(([[:digit:]]+)|((SIG)?([[:upper:]]+)([+-][123])?))" 30 #define SIGNL_REG_EXP SIGHD_REG_EXP EQUAL_REG_EXP SIGVL_REG_EXP 31 #define STOCK_REG_EXP "([[:upper:]]{1,5}(.[[:upper:]]{1,5})?,)?" 32 #define ATTRB_REG_EXP "(" STOCK_REG_EXP IDENT_REG_EXP ")" 33 #define ATVAL_REG_EXP ATTRB_REG_EXP EQUAL_REG_EXP STRN0_REG_EXP 34 35 #define TO_EXP(X) BOSTR_REG_EXP X EOSTR_REG_EXP 36 37 #define POOLN_EXP TO_EXP(IDENT_REG_EXP) 38 #define INTNM_EXP TO_EXP(INTNM_REG_EXP) 39 #define SIGAC_EXP TO_EXP(SIGAC_REG_EXP) 40 #define SIGNL_EXP TO_EXP(SIGNL_REG_EXP) 41 #define ATTRB_EXP TO_EXP(ATTRB_REG_EXP) 42 #define ATVAL_EXP TO_EXP(ATVAL_REG_EXP) 43 44 #define MAX_OF(X, Y) (((X) > (Y)) ? (X) : (Y)) 45 46 #define SEQU(X, Y) (strcmp((X), (Y)) == 0) 47 #define SIN1(X, S1) ((SEQU((X), (S1)))) 48 #define SIN2(X, S1, S2) ((SEQU((X), (S1))) || (SIN1((X), (S2)))) 49 #define SIN3(X, S1, S2, S3) ((SEQU((X), (S1))) || (SIN2((X), (S2), (S3)))) 50 51 #define ATT_VAL_TYPE_NULL 0 52 #define ATT_VAL_TYPE_VALUE 1 53 #define ATT_VAL_TYPE_LIST 2 54 55 #define ATT_ALLOC() attrib_alloc() 56 #define ATT_VAL_ALLOC(T, V) attrib_val_alloc((T), (V)) 57 #define ATT_VAL_ALLOC_NULL() ATT_VAL_ALLOC(ATT_VAL_TYPE_NULL, NULL) 58 #define ATT_VAL_ALLOC_VALUE(V) ATT_VAL_ALLOC(ATT_VAL_TYPE_VALUE, (V)) 59 #define ATT_VAL_ALLOC_LIST(L) ATT_VAL_ALLOC(ATT_VAL_TYPE_LIST, (L)) 60 61 typedef struct attrib_val_s { 62 int att_val_type; 63 /*LINTED*/ 64 union { 65 char *att_val_value; 66 lst_t *att_val_values; 67 }; 68 } attrib_val_t; 69 70 typedef struct attrib_s { 71 char *att_name; 72 attrib_val_t *att_value; 73 } attrib_t; 74 75 76 attrib_t *attrib_alloc(); 77 attrib_val_t *attrib_val_alloc(int, void *); 78 char *attrib_val_tostring(attrib_val_t *, boolean_t); 79 80 int 81 attrib_validate_rctl(attrib_t *att, resctlrule_t *rule, lst_t *errlst) 82 { 83 int ret = 0; 84 char *atname = att->att_name; 85 attrib_val_t *atv, *atval = att->att_value; 86 attrib_val_t *priv, *val, *action; 87 char *vpriv, *vval, *vaction, *sigstr; 88 int atv_type = atval->att_val_type; 89 char *str; 90 int i, j, k; 91 92 int nmatch; 93 uint8_t rpriv, raction, sigval; 94 uint64_t rmax; 95 regex_t pintexp, sigacexp, signlexp; 96 regmatch_t *mat; 97 98 int nonecount, denycount, sigcount; 99 100 if (regcomp(&pintexp, INTNM_EXP, REG_EXTENDED) != 0) { 101 util_add_errmsg(errlst, gettext( 102 "Failed to compile regex for pos. integer")); 103 return (1); 104 } 105 106 if (regcomp(&sigacexp, SIGAC_EXP, REG_EXTENDED) != 0) { 107 util_add_errmsg(errlst, gettext( 108 "Failed to compile regex for sigaction")); 109 regfree(&pintexp); 110 return (1); 111 } 112 113 if (regcomp(&signlexp, SIGNL_EXP, REG_EXTENDED) != 0) { 114 util_add_errmsg(errlst, gettext( 115 "Failed to compile regex for signal")); 116 regfree(&pintexp); 117 regfree(&sigacexp); 118 return (1); 119 } 120 121 nmatch = signlexp.re_nsub + 1; 122 mat = util_safe_zmalloc(nmatch * sizeof (regmatch_t)); 123 124 if (atv_type != ATT_VAL_TYPE_LIST) { 125 util_add_errmsg(errlst, gettext( 126 "rctl \"%s\" missing value"), atname); 127 ret = 1; 128 goto out; 129 } 130 131 for (i = 0; i < lst_size(atval->att_val_values); i++) { 132 atv = lst_at(atval->att_val_values, i); 133 if (atv->att_val_type != ATT_VAL_TYPE_LIST) { 134 if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) { 135 util_add_errmsg(errlst, gettext( 136 "rctl \"%s\" value \"%s\" " 137 "should be in ()"), atname, str); 138 free(str); 139 } else { 140 util_add_errmsg(errlst, gettext( 141 "rctl \"%s\" value " 142 "should be in ()"), atname); 143 } 144 ret = 1; 145 continue; 146 } 147 /* Values should be in the form (priv, val, actions) */ 148 if (lst_size(atv->att_val_values) < 3) { 149 util_add_errmsg(errlst, gettext( 150 "rctl \"%s\" value should be in the form " 151 "(priv, val, action[,...])[,....]"), atname); 152 ret = 1; 153 continue; 154 } 155 156 priv = lst_at(atv->att_val_values, 0); 157 val = lst_at(atv->att_val_values, 1); 158 /* actions = el[2], el[3], ... */ 159 160 vpriv = priv->att_val_value; 161 rpriv = rule->resctl_privs; 162 163 if (priv->att_val_type != ATT_VAL_TYPE_VALUE) { 164 util_add_errmsg(errlst, gettext( 165 "rctl \"%s\" invalid privilege"), atname); 166 ret = 1; 167 } else if (!SIN3(vpriv, "basic", "privileged", "priv")) { 168 util_add_errmsg(errlst, gettext( 169 "rctl \"%s\" unknown privilege \"%s\""), 170 atname, vpriv); 171 ret = 1; 172 } else if (!( 173 ((rpriv & RESCTL_PRIV_PRIVE) && 174 SEQU(vpriv, "priv")) || 175 ((rpriv & RESCTL_PRIV_PRIVD) && 176 SEQU(vpriv, "privileged")) || 177 ((rpriv & RESCTL_PRIV_BASIC) && 178 SEQU(vpriv, "basic")))) { 179 util_add_errmsg(errlst, gettext( 180 "rctl \"%s\" privilege not allowed \"%s\""), 181 atname, vpriv); 182 ret = 1; 183 } 184 185 vval = val->att_val_value; 186 rmax = rule->resctl_max; 187 188 if (val->att_val_type != ATT_VAL_TYPE_VALUE) { 189 util_add_errmsg(errlst, gettext( 190 "rctl \"%s\" invalid value"), atname); 191 ret = 1; 192 } else if (regexec(&pintexp, vval, 0, NULL, 0) != 0) { 193 util_add_errmsg(errlst, gettext( 194 "rctl \"%s\" value \"%s\" is not an integer"), 195 atname, vval); 196 ret = 1; 197 } else if (strtoll(vval, NULL, 0) > rmax) { 198 util_add_errmsg(errlst, gettext( 199 "rctl \"%s\" value \"%s\" exceeds system limit"), 200 atname, vval); 201 ret = 1; 202 } 203 204 nonecount = 0; 205 denycount = 0; 206 sigcount = 0; 207 208 for (j = 2; j < lst_size(atv->att_val_values); j++) { 209 action = lst_at(atv->att_val_values, j); 210 211 if (action->att_val_type != ATT_VAL_TYPE_VALUE) { 212 util_add_errmsg(errlst, gettext( 213 "rctl \"%s\" invalid action"), atname); 214 ret = 1; 215 continue; 216 } 217 218 vaction = action->att_val_value; 219 220 if (regexec(&sigacexp, vaction, 0, NULL, 0) != 0 && 221 !SIN2(vaction, "none", "deny")) { 222 util_add_errmsg(errlst, gettext( 223 "rctl \"%s\" unknown action \"%s\""), 224 atname, vaction); 225 ret = 1; 226 continue; 227 } 228 229 raction = rule->resctl_action; 230 if (!(((raction & RESCTL_ACTN_SIGN) && 231 regexec(&sigacexp, vaction, 0, NULL, 0) == 0) || 232 ((raction & RESCTL_ACTN_NONE) && 233 SEQU(vaction, "none")) || 234 ((raction & RESCTL_ACTN_DENY) && 235 SEQU(vaction, "deny")))) { 236 util_add_errmsg(errlst, gettext( 237 "rctl \"%s\" action not allowed \"%s\""), 238 atname, vaction); 239 ret = 1; 240 continue; 241 } 242 243 if (SEQU(vaction, "none")) { 244 if (nonecount >= 1) { 245 util_add_errmsg(errlst, gettext( 246 "rctl \"%s\" duplicate action " 247 "none"), atname); 248 ret = 1; 249 } 250 nonecount++; 251 continue; 252 } 253 254 if (SEQU(vaction, "deny")) { 255 if (denycount >= 1) { 256 util_add_errmsg(errlst, gettext( 257 "rctl \"%s\" duplicate action " 258 "deny"), atname); 259 ret = 1; 260 } 261 denycount++; 262 continue; 263 } 264 265 /* At this point, the action must be signal. */ 266 if (sigcount >= 1) { 267 util_add_errmsg(errlst, gettext( 268 "rctl \"%s\" duplicate action sig"), 269 atname); 270 ret = 1; 271 } 272 sigcount++; 273 274 /* 275 * Make sure signal is correct format, on of: 276 * sig=## 277 * signal=## 278 * sig=SIGXXX 279 * signal=SIGXXX 280 * sig=XXX 281 * signal=XXX 282 */ 283 284 if (regexec(&signlexp, vaction, nmatch, mat, 0) != 0 || 285 (sigstr = util_substr(&signlexp, mat, vaction, 2)) 286 == NULL) { 287 util_add_errmsg(errlst, gettext( 288 "rctl \"%s\" invalid signal \"%s\""), 289 atname, vaction); 290 ret = 1; 291 continue; 292 } 293 294 /* Our version of sigstr =~ s/SIG// */ 295 if (strstr(sigstr, "SIG") != NULL) 296 sigstr = strstr(sigstr, "SIG") + 3; 297 298 sigval = 0; 299 for (k = 0; k < SIGS_CNT; k++) { 300 if (SEQU(sigs[k].sig, sigstr)) 301 sigval = sigs[k].mask; 302 } 303 free(sigstr); 304 305 if (sigval == 0) { 306 util_add_errmsg(errlst, gettext( 307 "rctl \"%s\" invalid signal \"%s\""), 308 atname, vaction); 309 ret = 1; 310 continue; 311 } 312 313 if (!(sigval & rule->resctl_sigs)) { 314 util_add_errmsg(errlst, gettext( 315 "rctl \"%s\" signal not allowed \"%s\""), 316 atname, vaction); 317 ret = 1; 318 continue; 319 } 320 } 321 if (nonecount > 0 && (denycount > 0 || sigcount > 0)) { 322 util_add_errmsg(errlst, gettext( 323 "rctl \"%s\" action \"none\" specified with " 324 "other actions"), 325 atname); 326 ret = 1; 327 } 328 } 329 330 out: 331 free(mat); 332 regfree(&signlexp); 333 regfree(&sigacexp); 334 regfree(&pintexp); 335 return (ret); 336 } 337 338 int 339 attrib_validate(attrib_t *att, lst_t *errlst) 340 { 341 int ret = 0; 342 char *atname = att->att_name; 343 attrib_val_t *atv = att->att_value; 344 int atv_type = atv->att_val_type; 345 char *str, *eptr; 346 long long ll; 347 348 resctl_info_t rinfo; 349 resctlrule_t rrule; 350 351 regex_t poolnexp; 352 if (regcomp(&poolnexp, POOLN_EXP, REG_EXTENDED) != 0) { 353 util_add_errmsg(errlst, gettext( 354 "Failed to compile poolname regular expression:")); 355 return (1); 356 } 357 358 if (SEQU(atname, "task.final")) { 359 if (atv_type != ATT_VAL_TYPE_NULL) { 360 util_add_errmsg(errlst, gettext( 361 "task.final should not have value")); 362 ret = 1; 363 } 364 } else if (SEQU(atname, "rcap.max-rss")) { 365 if (atv_type == ATT_VAL_TYPE_NULL) { 366 util_add_errmsg(errlst, gettext( 367 "rcap.max-rss missing value")); 368 ret = 1; 369 } else if (atv_type == ATT_VAL_TYPE_LIST) { 370 util_add_errmsg(errlst, gettext( 371 "rcap.max-rss should have single value")); 372 ret = 1; 373 } else if (atv_type == ATT_VAL_TYPE_VALUE) { 374 if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) { 375 ll = strtoll(str, &eptr, 0); 376 if (*eptr != '\0') { 377 util_add_errmsg(errlst, gettext( 378 "rcap.max-rss is not an integer " 379 "value: \"%s\""), str); 380 ret = 1; 381 } else if (ll == LLONG_MIN && errno == ERANGE) { 382 util_add_errmsg(errlst, gettext( 383 "rcap.max-rss too small")); 384 ret = 1; 385 } else if (ll == LLONG_MAX && errno == ERANGE) { 386 util_add_errmsg(errlst, gettext( 387 "rcap.max-rss too large")); 388 ret = 1; 389 } else if (ll < 0) { 390 util_add_errmsg(errlst, gettext( 391 "rcap.max-rss should not have " 392 "negative value: \"%s\""), str); 393 ret = 1; 394 } 395 free(str); 396 } else { 397 util_add_errmsg(errlst, gettext( 398 "rcap.max-rss has invalid value")); 399 ret = 1; 400 } 401 } 402 } else if (SEQU(atname, "project.pool")) { 403 if (atv_type == ATT_VAL_TYPE_NULL) { 404 util_add_errmsg(errlst, gettext( 405 "project.pool missing value")); 406 ret = 1; 407 } else if (atv_type == ATT_VAL_TYPE_LIST) { 408 util_add_errmsg(errlst, gettext( 409 "project.pool should have single value")); 410 ret = 1; 411 } else if (atv_type == ATT_VAL_TYPE_VALUE) { 412 if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) { 413 if (regexec(&poolnexp, str, 0, NULL, 0) != 0) { 414 util_add_errmsg(errlst, gettext( 415 "project.pool: invalid pool " 416 "name \"%s\""), str); 417 ret = 1; 418 } else if (resctl_pool_exist(str) != 0) { 419 util_add_errmsg(errlst, gettext( 420 "project.pool: pools not enabled " 421 "or pool does not exist: \"%s\""), 422 str); 423 ret = 1; 424 } 425 free(str); 426 } else { 427 util_add_errmsg(errlst, gettext( 428 "project.pool has invalid value ")); 429 ret = 1; 430 } 431 } 432 } else if (resctl_get_info(atname, &rinfo) == 0) { 433 resctl_get_rule(&rinfo, &rrule); 434 if (attrib_validate_rctl(att, &rrule, errlst) != 0) { 435 ret = 1; 436 } 437 } 438 439 regfree(&poolnexp); 440 return (ret); 441 } 442 443 int 444 attrib_validate_lst(lst_t *attribs, lst_t *errlst) 445 { 446 int i, j; 447 attrib_t *att; 448 char **atnames, **atlast; 449 char *atname; 450 int ret = 0; 451 452 atlast = atnames = util_safe_zmalloc( 453 (lst_size(attribs) + 1) * sizeof (char *)); 454 for (i = 0; i < lst_size(attribs); i++) { 455 att = lst_at(attribs, i); 456 457 /* Validate this attribute */ 458 if (attrib_validate(att, errlst) != 0) 459 ret = 1; 460 /* Make sure it is not duplicated */ 461 for (j = 0; (atname = atnames[j]) != NULL; j++) { 462 if (strcmp(atname, att->att_name) == 0) { 463 util_add_errmsg(errlst, gettext( 464 "Duplicate attributes \"%s\""), atname); 465 ret = 1; 466 } 467 } 468 /* 469 * Add it to the attribute name to the 470 * temporary list if not found 471 */ 472 if (atname == NULL) { 473 *atlast++ = att->att_name; 474 } 475 } 476 free(atnames); 477 return (ret); 478 } 479 480 attrib_t 481 *attrib_alloc() 482 { 483 return (util_safe_zmalloc(sizeof (attrib_t))); 484 } 485 486 attrib_val_t 487 *attrib_val_alloc(int type, void *val) 488 { 489 attrib_val_t *ret; 490 491 ret = util_safe_malloc(sizeof (attrib_val_t)); 492 ret->att_val_type = type; 493 ret->att_val_value = val; 494 return (ret); 495 } 496 497 char 498 *attrib_val_tostring(attrib_val_t *val, boolean_t innerlist) 499 { 500 char *ret = NULL; 501 char *vstring; 502 int i; 503 attrib_val_t *v; 504 switch (val->att_val_type) { 505 case ATT_VAL_TYPE_NULL: 506 return (util_safe_strdup("")); 507 case ATT_VAL_TYPE_VALUE: 508 return (util_safe_strdup(val->att_val_value)); 509 case ATT_VAL_TYPE_LIST: 510 /* Only innerlists need to be betweeen ( and ) */ 511 if (innerlist) 512 ret = UTIL_STR_APPEND1(ret, "("); 513 for (i = 0; i < lst_size(val->att_val_values); 514 i++) { 515 v = lst_at(val->att_val_values, i); 516 if (i > 0) { 517 ret = UTIL_STR_APPEND1(ret, ","); 518 } 519 if ((vstring = 520 attrib_val_tostring(v, B_TRUE)) == NULL) { 521 UTIL_FREE_SNULL(ret); 522 goto out; 523 } 524 ret = UTIL_STR_APPEND1(ret, vstring); 525 free(vstring); 526 } 527 if (innerlist) 528 ret = UTIL_STR_APPEND1(ret, ")"); 529 return (ret); 530 } 531 532 out: 533 return (ret); 534 } 535 536 char 537 *attrib_tostring(void *at) 538 { 539 attrib_t *att; 540 char *ret = NULL, *vstring; 541 542 att = (attrib_t *)at; 543 ret = UTIL_STR_APPEND1(ret, att->att_name); 544 if ((vstring = attrib_val_tostring(att->att_value, B_FALSE)) != NULL) { 545 if (strlen(vstring) > 0) 546 ret = UTIL_STR_APPEND2(ret, "=", vstring); 547 free(vstring); 548 return (ret); 549 } 550 UTIL_FREE_SNULL(ret); 551 return (ret); 552 } 553 554 char 555 *attrib_lst_tostring(lst_t *attrs) 556 { 557 int i; 558 attrib_t *att; 559 char *ret = NULL; 560 char *str; 561 562 ret = UTIL_STR_APPEND1(ret, ""); 563 for (i = 0; i < lst_size(attrs); i++) { 564 att = lst_at(attrs, i); 565 566 if ((str = attrib_tostring(att)) != NULL) { 567 if (i > 0) 568 ret = UTIL_STR_APPEND1(ret, ";"); 569 570 ret = UTIL_STR_APPEND1(ret, str); 571 free(str); 572 continue; 573 } 574 575 free(ret); 576 return (NULL); 577 } 578 return (ret); 579 } 580 581 void 582 attrib_val_free(attrib_val_t *atv) 583 { 584 attrib_val_t *val; 585 586 if (atv->att_val_type == ATT_VAL_TYPE_VALUE) { 587 free(atv->att_val_value); 588 } else if (atv->att_val_type == ATT_VAL_TYPE_LIST) { 589 while (!lst_is_empty(atv->att_val_values)) { 590 val = lst_at(atv->att_val_values, 0); 591 (void) lst_remove(atv->att_val_values, val); 592 attrib_val_free(val); 593 free(val); 594 } 595 free(atv->att_val_values); 596 } 597 } 598 599 void 600 attrib_free(attrib_t *att) 601 { 602 free(att->att_name); 603 if (att->att_value != NULL) { 604 attrib_val_free(att->att_value); 605 free(att->att_value); 606 } 607 } 608 void 609 attrib_free_lst(lst_t *attribs) 610 { 611 attrib_t *att; 612 613 if (attribs == NULL) 614 return; 615 616 while (!lst_is_empty(attribs)) { 617 att = lst_at(attribs, 0); 618 (void) lst_remove(attribs, att); 619 attrib_free(att); 620 free(att); 621 } 622 } 623 624 void 625 attrib_sort_lst(lst_t *attribs) 626 { 627 int i, j, n; 628 attrib_t *atti, *attj; 629 630 if (attribs == NULL) 631 return; 632 633 n = lst_size(attribs); 634 for (i = 0; i < n - 1; i++) { 635 for (j = i + 1; j < n; j++) { 636 atti = lst_at(attribs, i); 637 attj = lst_at(attribs, j); 638 if (strcmp(attj->att_name, atti->att_name) < 0) { 639 (void) lst_replace_at(attribs, i, attj); 640 (void) lst_replace_at(attribs, j, atti); 641 } 642 } 643 } 644 } 645 646 void 647 attrib_val_to_list(attrib_val_t *atv) 648 { 649 void *val; 650 int type; 651 attrib_val_t *mat; 652 653 if (atv->att_val_type == ATT_VAL_TYPE_LIST) 654 return; 655 656 val = atv->att_val_value; 657 type = atv->att_val_type; 658 659 atv->att_val_type = ATT_VAL_TYPE_LIST; 660 atv->att_val_values = util_safe_malloc(sizeof (lst_t)); 661 lst_create(atv->att_val_values); 662 663 if (type == ATT_VAL_TYPE_VALUE && val != NULL) { 664 mat = ATT_VAL_ALLOC_VALUE(val); 665 lst_insert_tail(atv->att_val_values, mat); 666 } 667 } 668 669 void 670 attrib_val_append(attrib_val_t *atv, char *token) 671 { 672 attrib_val_t *nat; 673 if (atv->att_val_type == ATT_VAL_TYPE_VALUE) { 674 /* convert this to LIST attribute */ 675 attrib_val_to_list(atv); 676 } 677 678 if (atv->att_val_type == ATT_VAL_TYPE_NULL) { 679 /* convert this to VALUE attribute */ 680 atv->att_val_type = ATT_VAL_TYPE_VALUE; 681 atv->att_val_value = util_safe_strdup(token); 682 } else if (atv->att_val_type == ATT_VAL_TYPE_LIST) { 683 /* append token to the list */ 684 nat = ATT_VAL_ALLOC_VALUE(util_safe_strdup(token)); 685 lst_insert_tail(atv->att_val_values, nat); 686 } 687 } 688 689 attrib_val_t 690 *attrib_val_parse(char *values, lst_t *errlst) 691 { 692 attrib_val_t *ret = NULL; 693 attrib_val_t *at; 694 attrib_val_t *nat; 695 lst_t stk; 696 697 char **tokens, *token, *usedtokens, *prev; 698 int i, error, parendepth; 699 700 error = parendepth = 0; 701 prev = ""; 702 703 if ((tokens = util_tokenize(values, errlst)) == NULL) { 704 goto out1; 705 } 706 707 lst_create(&stk); 708 usedtokens = UTIL_STR_APPEND1(NULL, ""); 709 710 at = ret = ATT_VAL_ALLOC_NULL(); 711 712 for (i = 0; (token = tokens[i]) != NULL; i++) { 713 714 usedtokens = UTIL_STR_APPEND1(usedtokens, token); 715 716 if (SEQU(token, ",")) { 717 if (SIN3(prev, ",", "(", "")) { 718 attrib_val_append(at, ""); 719 } 720 attrib_val_to_list(at); 721 prev = ","; 722 } else if (SEQU(token, "(")) { 723 if (!(SIN3(prev, "(", ",", ""))) { 724 util_add_errmsg(errlst, gettext( 725 "\"%s\" <- \"(\" unexpected"), usedtokens); 726 error = 1; 727 goto out; 728 } 729 730 switch (at->att_val_type) { 731 case ATT_VAL_TYPE_VALUE: 732 util_add_errmsg(errlst, gettext( 733 "\"%s\" <- \"%s\" unexpected"), 734 usedtokens, token); 735 error = 1; 736 goto out; 737 case ATT_VAL_TYPE_NULL: 738 /* Make is a LIST attrib */ 739 attrib_val_to_list(at); 740 /*FALLTHROUGH*/ 741 case ATT_VAL_TYPE_LIST: 742 /* Allocate NULL node */ 743 nat = ATT_VAL_ALLOC_NULL(); 744 attrib_val_to_list(nat); 745 lst_insert_tail( 746 at->att_val_values, nat); 747 /* push at down one level */ 748 lst_insert_head(&stk, at); 749 at = nat; 750 break; 751 } 752 parendepth++; 753 prev = "("; 754 } else if (SEQU(token, ")")) { 755 if (parendepth <= 0) { 756 util_add_errmsg(errlst, gettext( 757 "\"%s\" <- \")\" unexpected"), usedtokens); 758 error = 1; 759 goto out; 760 } 761 if (SIN2(prev, ",", "(")) { 762 attrib_val_append(at, ""); 763 } 764 765 if (!lst_is_empty(&stk)) { 766 at = lst_at(&stk, 0); 767 (void) lst_remove(&stk, at); 768 } 769 parendepth--; 770 prev = ")"; 771 } else { 772 if (!(SIN3(prev, ",", "(", ""))) { 773 util_add_errmsg(errlst, gettext( 774 "\"%s\" <- \"%s\" unexpected"), 775 usedtokens, token); 776 error = 1; 777 goto out; 778 } 779 780 attrib_val_append(at, token); 781 prev = token; 782 } 783 } 784 785 if (parendepth != 0) { 786 util_add_errmsg(errlst, gettext( 787 "\"%s\" <- \")\" missing"), 788 usedtokens); 789 error = 1; 790 goto out; 791 } 792 793 if (SIN2(prev, ",", "")) { 794 switch (at->att_val_type) { 795 case ATT_VAL_TYPE_NULL: 796 util_add_errmsg(errlst, gettext( 797 "\"%s\" unexpected"), 798 usedtokens); 799 error = 1; 800 goto out; 801 case ATT_VAL_TYPE_VALUE: 802 case ATT_VAL_TYPE_LIST: 803 attrib_val_append(at, ""); 804 break; 805 } 806 } 807 808 out: 809 while (!lst_is_empty(&stk)) { 810 at = lst_at(&stk, 0); 811 (void) lst_remove(&stk, at); 812 } 813 814 util_free_tokens(tokens); 815 free(tokens); 816 free(usedtokens); 817 if (error) { 818 attrib_val_free(ret); 819 UTIL_FREE_SNULL(ret); 820 } 821 out1: 822 return (ret); 823 } 824 825 attrib_t 826 *attrib_parse(regex_t *attrbexp, regex_t *atvalexp, char *att, int flags, 827 lst_t *errlst) 828 { 829 int nmatch = MAX_OF(attrbexp->re_nsub, atvalexp->re_nsub) + 1; 830 attrib_t *ret = NULL; 831 attrib_val_t *retv, *atv, *atvl; 832 char *values = NULL; 833 int vidx, nidx, vlen; 834 int scale; 835 836 char *num, *mod, *unit; 837 int i; 838 839 resctl_info_t rinfo; 840 resctlrule_t rrule; 841 842 regmatch_t *mat = util_safe_malloc(nmatch * sizeof (regmatch_t)); 843 ret = ATT_ALLOC(); 844 845 if (regexec(attrbexp, att, attrbexp->re_nsub + 1, mat, 0) == 0) { 846 ret->att_name = util_safe_strdup(att); 847 ret->att_value = ATT_VAL_ALLOC_NULL(); 848 } else if (regexec(atvalexp, att, 849 atvalexp->re_nsub + 1, mat, 0) == 0) { 850 vidx = atvalexp->re_nsub; 851 vlen = mat[vidx].rm_eo - mat[vidx].rm_so; 852 nidx = atvalexp->re_nsub - 3; 853 ret->att_name = util_substr(atvalexp, mat, att, nidx); 854 855 if (vlen > 0) { 856 values = util_substr(atvalexp, mat, att, vidx); 857 ret->att_value = attrib_val_parse(values, errlst); 858 free(values); 859 if (ret->att_value == NULL) { 860 util_add_errmsg(errlst, gettext( 861 "Invalid value on attribute \"%s\""), 862 ret->att_name); 863 attrib_free(ret); 864 UTIL_FREE_SNULL(ret); 865 goto out; 866 } 867 } else { 868 /* the value is an empty string */ 869 ret->att_value = ATT_VAL_ALLOC_NULL(); 870 } 871 } else { 872 util_add_errmsg(errlst, gettext( 873 "Invalid attribute \"%s\""), att); 874 attrib_free(ret); 875 UTIL_FREE_SNULL(ret); 876 goto out; 877 } 878 879 if (!(flags & F_PAR_UNT)) 880 goto out; 881 882 if (SEQU(ret->att_name, "rcap.max-rss")) { 883 values = attrib_val_tostring(ret->att_value, B_FALSE); 884 if (util_val2num(values, BYTES_SCALE, errlst, 885 &num, &mod, &unit) == 0) { 886 attrib_val_free(ret->att_value); 887 ret->att_value = ATT_VAL_ALLOC_VALUE(num); 888 free(mod); 889 free(unit); 890 } else { 891 attrib_free(ret); 892 UTIL_FREE_SNULL(ret); 893 goto out; 894 } 895 free(values); 896 } 897 898 if (resctl_get_info(ret->att_name, &rinfo) == 0) { 899 resctl_get_rule(&rinfo, &rrule); 900 retv = ret->att_value; 901 902 switch (rrule.resctl_type) { 903 case RESCTL_TYPE_BYTES: 904 scale = BYTES_SCALE; 905 break; 906 case RESCTL_TYPE_SCNDS: 907 scale = SCNDS_SCALE; 908 break; 909 case RESCTL_TYPE_COUNT: 910 scale = COUNT_SCALE; 911 break; 912 default: 913 scale = UNKWN_SCALE; 914 break; 915 } 916 917 if (retv->att_val_type != ATT_VAL_TYPE_LIST) 918 goto out; 919 920 921 for (i = 0; i < lst_size(retv->att_val_values); i++) { 922 atvl = atv = lst_at(retv->att_val_values, i); 923 924 /* 925 * Continue if not a list and the second value 926 * is not a scaler value 927 */ 928 if (atv->att_val_type != ATT_VAL_TYPE_LIST || 929 lst_size(atv->att_val_values) < 3 || 930 (atv = lst_at(atv->att_val_values, 1)) == NULL || 931 atv->att_val_type != ATT_VAL_TYPE_VALUE) { 932 continue; 933 } 934 values = attrib_val_tostring(atv, B_FALSE); 935 if (util_val2num(values, scale, errlst, 936 &num, &mod, &unit) == 0) { 937 attrib_val_free(atv); 938 atv = ATT_VAL_ALLOC_VALUE(num); 939 (void) lst_replace_at(atvl->att_val_values, 1, 940 atv); 941 free(mod); 942 free(unit); 943 944 } else { 945 free(values); 946 attrib_free(ret); 947 UTIL_FREE_SNULL(ret); 948 goto out; 949 } 950 free(values); 951 } 952 } 953 954 out: 955 free(mat); 956 return (ret); 957 } 958 959 lst_t 960 *attrib_parse_attributes(char *attribs, int flags, lst_t *errlst) 961 { 962 char *sattrs, *attrs, *att; 963 regex_t attrbexp, atvalexp; 964 965 attrib_t *natt = NULL; 966 lst_t *ret = NULL; 967 968 ret = util_safe_malloc(sizeof (lst_t)); 969 lst_create(ret); 970 971 if (regcomp(&attrbexp, ATTRB_EXP, REG_EXTENDED) != 0) 972 goto out1; 973 if (regcomp(&atvalexp, ATVAL_EXP, REG_EXTENDED) != 0) 974 goto out2; 975 976 sattrs = attrs = util_safe_strdup(attribs); 977 while ((att = strsep(&attrs, ";")) != NULL) { 978 if (*att == '\0') 979 continue; 980 if ((natt = attrib_parse(&attrbexp, 981 &atvalexp, att, flags, errlst)) == NULL) { 982 attrib_free_lst(ret); 983 UTIL_FREE_SNULL(ret); 984 break; 985 } 986 987 lst_insert_tail(ret, natt); 988 } 989 990 free(sattrs); 991 regfree(&atvalexp); 992 out2: 993 regfree(&attrbexp); 994 out1: 995 return (ret); 996 } 997 998 attrib_val_t 999 *attrib_val_duplicate(attrib_val_t *atv) { 1000 int i; 1001 lst_t *values; 1002 attrib_val_t *val; 1003 attrib_val_t *natv; 1004 1005 switch (atv->att_val_type) { 1006 case ATT_VAL_TYPE_NULL: 1007 natv = ATT_VAL_ALLOC_NULL(); 1008 break; 1009 case ATT_VAL_TYPE_VALUE: 1010 natv = ATT_VAL_ALLOC_VALUE( 1011 util_safe_strdup(atv->att_val_value)); 1012 break; 1013 case ATT_VAL_TYPE_LIST: 1014 values = util_safe_malloc(sizeof (lst_t)); 1015 lst_create(values); 1016 for (i = 0; i < lst_size(atv->att_val_values); 1017 i++) { 1018 val = lst_at(atv->att_val_values, i); 1019 lst_insert_tail(values, 1020 attrib_val_duplicate(val)); 1021 } 1022 natv = ATT_VAL_ALLOC_LIST(values); 1023 break; 1024 } 1025 1026 return (natv); 1027 } 1028 1029 attrib_t 1030 *attrib_duplicate(attrib_t *att) { 1031 attrib_t *natt = ATT_ALLOC(); 1032 natt->att_name = util_safe_strdup(att->att_name); 1033 natt->att_value = attrib_val_duplicate(att->att_value); 1034 return (natt); 1035 } 1036 1037 attrib_t 1038 *attrib_merge_add(attrib_t *eatt, attrib_t *natt) 1039 { 1040 int i; 1041 attrib_t *att; 1042 attrib_val_t *atv, *eatv, *natv; 1043 lst_t *values; 1044 1045 eatv = eatt->att_value; 1046 natv = natt->att_value; 1047 att = ATT_ALLOC(); 1048 att->att_name = util_safe_strdup(eatt->att_name); 1049 1050 if (eatv->att_val_type == ATT_VAL_TYPE_NULL) { 1051 1052 /* NULL + X -> X */ 1053 atv = attrib_val_duplicate(natv); 1054 1055 } else if (natv->att_val_type == ATT_VAL_TYPE_NULL) { 1056 1057 /* X + NULL -> X */ 1058 atv = attrib_val_duplicate(eatv); 1059 1060 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1061 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1062 1063 /* VALUE + VALUE -> LIST */ 1064 values = util_safe_malloc(sizeof (lst_t)); 1065 lst_create(values); 1066 lst_insert_tail(values, attrib_val_duplicate(eatv)); 1067 lst_insert_tail(values, attrib_val_duplicate(natv)); 1068 atv = ATT_VAL_ALLOC_LIST(values); 1069 1070 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1071 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1072 1073 /* VALUE + LIST -> LIST */ 1074 atv = attrib_val_duplicate(natv); 1075 lst_insert_head(atv->att_val_values, 1076 attrib_val_duplicate(eatv)); 1077 1078 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1079 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1080 1081 /* LIST + VALUE -> LIST */ 1082 atv = attrib_val_duplicate(eatv); 1083 lst_insert_tail(atv->att_val_values, 1084 attrib_val_duplicate(natv)); 1085 1086 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1087 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1088 1089 /* LIST + LIST -> LIST */ 1090 atv = attrib_val_duplicate(eatv); 1091 for (i = 0; i < lst_size(natv->att_val_values); i++) { 1092 lst_insert_tail(atv->att_val_values, 1093 attrib_val_duplicate( 1094 lst_at(natv->att_val_values, i))); 1095 } 1096 } 1097 1098 att->att_value = atv; 1099 return (att); 1100 } 1101 1102 int 1103 attrib_val_equal(attrib_val_t *xatv, attrib_val_t *yatv) 1104 { 1105 int i; 1106 attrib_val_t *xv, *yv; 1107 1108 if (xatv->att_val_type != yatv->att_val_type) 1109 return (1); 1110 1111 switch (xatv->att_val_type) { 1112 case ATT_VAL_TYPE_NULL: 1113 return (0); 1114 case ATT_VAL_TYPE_VALUE: 1115 if (SEQU(xatv->att_val_value, yatv->att_val_value)) { 1116 return (0); 1117 } 1118 return (1); 1119 case ATT_VAL_TYPE_LIST: 1120 if (lst_size(xatv->att_val_values) != 1121 lst_size(yatv->att_val_values)) 1122 return (1); 1123 for (i = 0; i < lst_size(xatv->att_val_values); 1124 i++) { 1125 xv = lst_at(xatv->att_val_values, i); 1126 yv = lst_at(yatv->att_val_values, i); 1127 if (attrib_val_equal(xv, yv) != 0) { 1128 return (1); 1129 } 1130 } 1131 break; 1132 } 1133 return (0); 1134 } 1135 1136 attrib_t 1137 *attrib_merge_remove(attrib_t *eatt, attrib_t *natt, lst_t *errlst) 1138 { 1139 1140 int i, j; 1141 attrib_t *att = NULL; 1142 attrib_val_t *eatv, *natv; 1143 attrib_val_t *ev, *nv1, *nv2; 1144 lst_t *values; 1145 boolean_t found; 1146 1147 eatv = eatt->att_value; 1148 natv = natt->att_value; 1149 1150 if (eatv->att_val_type == ATT_VAL_TYPE_NULL && 1151 natv->att_val_type == ATT_VAL_TYPE_NULL) { 1152 1153 /* NULL - NULL -> EMPTY */ 1154 att = attrib_duplicate(eatt); 1155 (void) strcpy(att->att_name, ""); 1156 1157 } else if (eatv->att_val_type == ATT_VAL_TYPE_NULL || 1158 (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1159 natv->att_val_type == ATT_VAL_TYPE_LIST) || 1160 (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1161 natv->att_val_type == ATT_VAL_TYPE_VALUE)) { 1162 1163 /* NULL - X -> ERR, VALUE - LIST -> ERR, LIST - VALUE -> ERR */ 1164 util_add_errmsg(errlst, gettext( 1165 "Can not remove attribute \"%s\""), 1166 eatt->att_name); 1167 1168 } else if (natv->att_val_type == ATT_VAL_TYPE_NULL) { 1169 1170 /* X - NULL -> X */ 1171 att = attrib_duplicate(eatt); 1172 1173 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1174 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1175 1176 /* VALUE - VALUE -> {EMPTY | ERR} */ 1177 if (attrib_val_equal(eatv, natv) == 0) { 1178 att = ATT_ALLOC(); 1179 att->att_name = util_safe_strdup(""); 1180 att->att_value = ATT_VAL_ALLOC_NULL(); 1181 } else { 1182 util_add_errmsg(errlst, gettext( 1183 "Can not remove attribute \"%s\""), 1184 eatt->att_name); 1185 } 1186 1187 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1188 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1189 /* LIST - LIST -> {EMPTY | ERR | LIST} */ 1190 if (attrib_val_equal(eatv, natv) == 0) { 1191 att = ATT_ALLOC(); 1192 att->att_name = util_safe_strdup(""); 1193 att->att_value = ATT_VAL_ALLOC_NULL(); 1194 goto out; 1195 } 1196 1197 for (i = 0; i < lst_size(natv->att_val_values); i++) { 1198 nv1 = lst_at(natv->att_val_values, i); 1199 for (j = 0; j < lst_size(natv->att_val_values); 1200 j++) { 1201 nv2 = lst_at(natv->att_val_values, j); 1202 if (i != j && attrib_val_equal(nv1, nv2) == 0) { 1203 util_add_errmsg(errlst, gettext( 1204 "Duplicate values, can not remove" 1205 " attribute \"%s\""), 1206 eatt->att_name); 1207 goto out; 1208 } 1209 } 1210 1211 found = B_FALSE; 1212 for (j = 0; j < lst_size(eatv->att_val_values); 1213 j++) { 1214 ev = lst_at(eatv->att_val_values, j); 1215 if (attrib_val_equal(nv1, ev) == 0) { 1216 found = B_TRUE; 1217 break; 1218 } 1219 } 1220 1221 if (!found) { 1222 util_add_errmsg(errlst, gettext( 1223 "Value not found, can not remove" 1224 " attribute \"%s\""), 1225 eatt->att_name); 1226 goto out; 1227 } 1228 } 1229 1230 values = util_safe_malloc(sizeof (lst_t)); 1231 lst_create(values); 1232 for (i = 0; i < lst_size(eatv->att_val_values); i++) { 1233 ev = lst_at(eatv->att_val_values, i); 1234 found = B_FALSE; 1235 for (j = 0; j < lst_size(natv->att_val_values); 1236 j++) { 1237 nv1 = lst_at(natv->att_val_values, j); 1238 if (attrib_val_equal(ev, nv1) == 0) { 1239 found = B_TRUE; 1240 break; 1241 } 1242 } 1243 1244 if (!found) { 1245 lst_insert_tail(values, 1246 attrib_val_duplicate(ev)); 1247 } 1248 } 1249 att = ATT_ALLOC(); 1250 att->att_name = util_safe_strdup(eatt->att_name); 1251 att->att_value = ATT_VAL_ALLOC_LIST(values); 1252 } 1253 1254 out: 1255 return (att); 1256 } 1257 1258 attrib_t 1259 *attrib_merge(attrib_t *eatt, attrib_t *natt, int flags, lst_t *errlst) 1260 { 1261 attrib_t *att = NULL; 1262 1263 VERIFY(SEQU(eatt->att_name, natt->att_name)); 1264 1265 if (flags & F_MOD_ADD) { 1266 att = attrib_merge_add(eatt, natt); 1267 } else if (flags & F_MOD_REM) { 1268 att = attrib_merge_remove(eatt, natt, errlst); 1269 } 1270 1271 return (att); 1272 } 1273 1274 void 1275 attrib_merge_attrib_lst(lst_t **eattrs, lst_t *nattrs, int flags, 1276 lst_t *errlst) { 1277 1278 lst_t *attrs = NULL; 1279 int i, j; 1280 attrib_t *att, *natt, *eatt; 1281 boolean_t found; 1282 1283 if (flags & F_MOD_ADD) { 1284 attrs = util_safe_malloc(sizeof (lst_t)); 1285 lst_create(attrs); 1286 1287 for (i = 0; i < lst_size(*eattrs); i++) { 1288 eatt = lst_at(*eattrs, i); 1289 found = B_FALSE; 1290 for (j = 0; j < lst_size(nattrs); j++) { 1291 natt = lst_at(nattrs, j); 1292 if (SEQU(eatt->att_name, natt->att_name)) { 1293 found = B_TRUE; 1294 break; 1295 } 1296 } 1297 1298 att = found ? attrib_merge(eatt, natt, flags, errlst) : 1299 attrib_duplicate(eatt); 1300 if (att == NULL) { 1301 attrib_free_lst(attrs); 1302 UTIL_FREE_SNULL(attrs); 1303 goto out; 1304 } 1305 lst_insert_tail(attrs, att); 1306 } 1307 1308 for (i = 0; i < lst_size(nattrs); i++) { 1309 natt = lst_at(nattrs, i); 1310 found = B_FALSE; 1311 for (j = 0; j < lst_size(*eattrs); j++) { 1312 eatt = lst_at(*eattrs, j); 1313 if (SEQU(natt->att_name, eatt->att_name)) { 1314 found = B_TRUE; 1315 break; 1316 } 1317 } 1318 if (found) 1319 continue; 1320 lst_insert_tail(attrs, attrib_duplicate(natt)); 1321 } 1322 1323 } else if (flags & (F_MOD_REM | F_MOD_SUB)) { 1324 1325 for (i = 0; i < lst_size(nattrs); i++) { 1326 natt = lst_at(nattrs, i); 1327 for (j = 0; j < lst_size(nattrs); j++) { 1328 att = lst_at(nattrs, j); 1329 if (SEQU(natt->att_name, att->att_name) && 1330 i != j) { 1331 util_add_errmsg(errlst, gettext( 1332 "Duplicate Attributes \"%s\""), 1333 natt->att_name); 1334 goto out; 1335 } 1336 } 1337 1338 found = B_FALSE; 1339 for (j = 0; j < lst_size(*eattrs); j++) { 1340 eatt = lst_at(*eattrs, j); 1341 if (SEQU(eatt->att_name, natt->att_name)) { 1342 found = B_TRUE; 1343 break; 1344 } 1345 } 1346 if (!found) { 1347 util_add_errmsg(errlst, gettext( 1348 "Project does not contain \"%s\""), 1349 natt->att_name); 1350 goto out; 1351 } 1352 } 1353 1354 attrs = util_safe_malloc(sizeof (lst_t)); 1355 lst_create(attrs); 1356 1357 for (i = 0; i < lst_size(*eattrs); i++) { 1358 eatt = lst_at(*eattrs, i); 1359 found = B_FALSE; 1360 for (j = 0; j < lst_size(nattrs); j++) { 1361 natt = lst_at(nattrs, j); 1362 if (SEQU(eatt->att_name, natt->att_name)) { 1363 found = B_TRUE; 1364 break; 1365 } 1366 } 1367 1368 if (flags & F_MOD_REM) { 1369 att = found ? 1370 attrib_merge(eatt, natt, flags, errlst) : 1371 attrib_duplicate(eatt); 1372 } else if (flags & F_MOD_SUB) { 1373 att = attrib_duplicate(found ? natt : eatt); 1374 } 1375 1376 if (att == NULL) { 1377 attrib_free_lst(attrs); 1378 UTIL_FREE_SNULL(attrs); 1379 goto out; 1380 } else if (SEQU(att->att_name, "")) { 1381 attrib_free(att); 1382 } else { 1383 lst_insert_tail(attrs, att); 1384 } 1385 } 1386 1387 } else if (flags & F_MOD_REP) { 1388 attrs = util_safe_malloc(sizeof (lst_t)); 1389 lst_create(attrs); 1390 for (i = 0; i < lst_size(nattrs); i++) { 1391 natt = lst_at(nattrs, i); 1392 lst_insert_tail(attrs, attrib_duplicate(natt)); 1393 } 1394 } 1395 out: 1396 if (attrs != NULL) { 1397 attrib_free_lst(*eattrs); 1398 free(*eattrs); 1399 *eattrs = attrs; 1400 } 1401 }