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 { 1001 int i; 1002 lst_t *values; 1003 attrib_val_t *val; 1004 attrib_val_t *natv; 1005 1006 switch (atv->att_val_type) { 1007 case ATT_VAL_TYPE_NULL: 1008 natv = ATT_VAL_ALLOC_NULL(); 1009 break; 1010 case ATT_VAL_TYPE_VALUE: 1011 natv = ATT_VAL_ALLOC_VALUE( 1012 util_safe_strdup(atv->att_val_value)); 1013 break; 1014 case ATT_VAL_TYPE_LIST: 1015 values = util_safe_malloc(sizeof (lst_t)); 1016 lst_create(values); 1017 for (i = 0; i < lst_size(atv->att_val_values); 1018 i++) { 1019 val = lst_at(atv->att_val_values, i); 1020 lst_insert_tail(values, 1021 attrib_val_duplicate(val)); 1022 } 1023 natv = ATT_VAL_ALLOC_LIST(values); 1024 break; 1025 } 1026 1027 return (natv); 1028 } 1029 1030 attrib_t * 1031 attrib_duplicate(attrib_t *att) 1032 { 1033 attrib_t *natt = ATT_ALLOC(); 1034 natt->att_name = util_safe_strdup(att->att_name); 1035 natt->att_value = attrib_val_duplicate(att->att_value); 1036 return (natt); 1037 } 1038 1039 attrib_t * 1040 attrib_merge_add(attrib_t *eatt, attrib_t *natt) 1041 { 1042 int i; 1043 attrib_t *att; 1044 attrib_val_t *atv, *eatv, *natv; 1045 lst_t *values; 1046 1047 eatv = eatt->att_value; 1048 natv = natt->att_value; 1049 att = ATT_ALLOC(); 1050 att->att_name = util_safe_strdup(eatt->att_name); 1051 1052 if (eatv->att_val_type == ATT_VAL_TYPE_NULL) { 1053 1054 /* NULL + X -> X */ 1055 atv = attrib_val_duplicate(natv); 1056 1057 } else if (natv->att_val_type == ATT_VAL_TYPE_NULL) { 1058 1059 /* X + NULL -> X */ 1060 atv = attrib_val_duplicate(eatv); 1061 1062 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1063 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1064 1065 /* VALUE + VALUE -> LIST */ 1066 values = util_safe_malloc(sizeof (lst_t)); 1067 lst_create(values); 1068 lst_insert_tail(values, attrib_val_duplicate(eatv)); 1069 lst_insert_tail(values, attrib_val_duplicate(natv)); 1070 atv = ATT_VAL_ALLOC_LIST(values); 1071 1072 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1073 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1074 1075 /* VALUE + LIST -> LIST */ 1076 atv = attrib_val_duplicate(natv); 1077 lst_insert_head(atv->att_val_values, 1078 attrib_val_duplicate(eatv)); 1079 1080 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1081 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1082 1083 /* LIST + VALUE -> LIST */ 1084 atv = attrib_val_duplicate(eatv); 1085 lst_insert_tail(atv->att_val_values, 1086 attrib_val_duplicate(natv)); 1087 1088 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1089 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1090 1091 /* LIST + LIST -> LIST */ 1092 atv = attrib_val_duplicate(eatv); 1093 for (i = 0; i < lst_size(natv->att_val_values); i++) { 1094 lst_insert_tail(atv->att_val_values, 1095 attrib_val_duplicate( 1096 lst_at(natv->att_val_values, i))); 1097 } 1098 } 1099 1100 att->att_value = atv; 1101 return (att); 1102 } 1103 1104 int 1105 attrib_val_equal(attrib_val_t *xatv, attrib_val_t *yatv) 1106 { 1107 int i; 1108 attrib_val_t *xv, *yv; 1109 1110 if (xatv->att_val_type != yatv->att_val_type) 1111 return (1); 1112 1113 switch (xatv->att_val_type) { 1114 case ATT_VAL_TYPE_NULL: 1115 return (0); 1116 case ATT_VAL_TYPE_VALUE: 1117 if (SEQU(xatv->att_val_value, yatv->att_val_value)) { 1118 return (0); 1119 } 1120 return (1); 1121 case ATT_VAL_TYPE_LIST: 1122 if (lst_size(xatv->att_val_values) != 1123 lst_size(yatv->att_val_values)) 1124 return (1); 1125 for (i = 0; i < lst_size(xatv->att_val_values); 1126 i++) { 1127 xv = lst_at(xatv->att_val_values, i); 1128 yv = lst_at(yatv->att_val_values, i); 1129 if (attrib_val_equal(xv, yv) != 0) { 1130 return (1); 1131 } 1132 } 1133 break; 1134 } 1135 return (0); 1136 } 1137 1138 attrib_t * 1139 attrib_merge_remove(attrib_t *eatt, attrib_t *natt, lst_t *errlst) 1140 { 1141 1142 int i, j; 1143 attrib_t *att = NULL; 1144 attrib_val_t *eatv, *natv; 1145 attrib_val_t *ev, *nv1, *nv2; 1146 lst_t *values; 1147 boolean_t found; 1148 1149 eatv = eatt->att_value; 1150 natv = natt->att_value; 1151 1152 if (eatv->att_val_type == ATT_VAL_TYPE_NULL && 1153 natv->att_val_type == ATT_VAL_TYPE_NULL) { 1154 1155 /* NULL - NULL -> EMPTY */ 1156 att = attrib_duplicate(eatt); 1157 (void) strcpy(att->att_name, ""); 1158 1159 } else if (eatv->att_val_type == ATT_VAL_TYPE_NULL || 1160 (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1161 natv->att_val_type == ATT_VAL_TYPE_LIST) || 1162 (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1163 natv->att_val_type == ATT_VAL_TYPE_VALUE)) { 1164 1165 /* NULL - X -> ERR, VALUE - LIST -> ERR, LIST - VALUE -> ERR */ 1166 util_add_errmsg(errlst, gettext( 1167 "Can not remove attribute \"%s\""), 1168 eatt->att_name); 1169 1170 } else if (natv->att_val_type == ATT_VAL_TYPE_NULL) { 1171 1172 /* X - NULL -> X */ 1173 att = attrib_duplicate(eatt); 1174 1175 } else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE && 1176 natv->att_val_type == ATT_VAL_TYPE_VALUE) { 1177 1178 /* VALUE - VALUE -> {EMPTY | ERR} */ 1179 if (attrib_val_equal(eatv, natv) == 0) { 1180 att = ATT_ALLOC(); 1181 att->att_name = util_safe_strdup(""); 1182 att->att_value = ATT_VAL_ALLOC_NULL(); 1183 } else { 1184 util_add_errmsg(errlst, gettext( 1185 "Can not remove attribute \"%s\""), 1186 eatt->att_name); 1187 } 1188 1189 } else if (eatv->att_val_type == ATT_VAL_TYPE_LIST && 1190 natv->att_val_type == ATT_VAL_TYPE_LIST) { 1191 /* LIST - LIST -> {EMPTY | ERR | LIST} */ 1192 if (attrib_val_equal(eatv, natv) == 0) { 1193 att = ATT_ALLOC(); 1194 att->att_name = util_safe_strdup(""); 1195 att->att_value = ATT_VAL_ALLOC_NULL(); 1196 goto out; 1197 } 1198 1199 for (i = 0; i < lst_size(natv->att_val_values); i++) { 1200 nv1 = lst_at(natv->att_val_values, i); 1201 for (j = 0; j < lst_size(natv->att_val_values); 1202 j++) { 1203 nv2 = lst_at(natv->att_val_values, j); 1204 if (i != j && attrib_val_equal(nv1, nv2) == 0) { 1205 util_add_errmsg(errlst, gettext( 1206 "Duplicate values, can not remove" 1207 " attribute \"%s\""), 1208 eatt->att_name); 1209 goto out; 1210 } 1211 } 1212 1213 found = B_FALSE; 1214 for (j = 0; j < lst_size(eatv->att_val_values); 1215 j++) { 1216 ev = lst_at(eatv->att_val_values, j); 1217 if (attrib_val_equal(nv1, ev) == 0) { 1218 found = B_TRUE; 1219 break; 1220 } 1221 } 1222 1223 if (!found) { 1224 util_add_errmsg(errlst, gettext( 1225 "Value not found, can not remove" 1226 " attribute \"%s\""), 1227 eatt->att_name); 1228 goto out; 1229 } 1230 } 1231 1232 values = util_safe_malloc(sizeof (lst_t)); 1233 lst_create(values); 1234 for (i = 0; i < lst_size(eatv->att_val_values); i++) { 1235 ev = lst_at(eatv->att_val_values, i); 1236 found = B_FALSE; 1237 for (j = 0; j < lst_size(natv->att_val_values); 1238 j++) { 1239 nv1 = lst_at(natv->att_val_values, j); 1240 if (attrib_val_equal(ev, nv1) == 0) { 1241 found = B_TRUE; 1242 break; 1243 } 1244 } 1245 1246 if (!found) { 1247 lst_insert_tail(values, 1248 attrib_val_duplicate(ev)); 1249 } 1250 } 1251 att = ATT_ALLOC(); 1252 att->att_name = util_safe_strdup(eatt->att_name); 1253 att->att_value = ATT_VAL_ALLOC_LIST(values); 1254 } 1255 1256 out: 1257 return (att); 1258 } 1259 1260 attrib_t * 1261 attrib_merge(attrib_t *eatt, attrib_t *natt, int flags, lst_t *errlst) 1262 { 1263 attrib_t *att = NULL; 1264 1265 VERIFY(SEQU(eatt->att_name, natt->att_name)); 1266 1267 if (flags & F_MOD_ADD) { 1268 att = attrib_merge_add(eatt, natt); 1269 } else if (flags & F_MOD_REM) { 1270 att = attrib_merge_remove(eatt, natt, errlst); 1271 } 1272 1273 return (att); 1274 } 1275 1276 void 1277 attrib_merge_attrib_lst(lst_t **eattrs, lst_t *nattrs, int flags, 1278 lst_t *errlst) 1279 { 1280 1281 lst_t *attrs = NULL; 1282 int i, j; 1283 attrib_t *att, *natt, *eatt; 1284 boolean_t found; 1285 1286 if (flags & F_MOD_ADD) { 1287 attrs = util_safe_malloc(sizeof (lst_t)); 1288 lst_create(attrs); 1289 1290 for (i = 0; i < lst_size(*eattrs); i++) { 1291 eatt = lst_at(*eattrs, i); 1292 found = B_FALSE; 1293 for (j = 0; j < lst_size(nattrs); j++) { 1294 natt = lst_at(nattrs, j); 1295 if (SEQU(eatt->att_name, natt->att_name)) { 1296 found = B_TRUE; 1297 break; 1298 } 1299 } 1300 1301 att = found ? attrib_merge(eatt, natt, flags, errlst) : 1302 attrib_duplicate(eatt); 1303 if (att == NULL) { 1304 attrib_free_lst(attrs); 1305 UTIL_FREE_SNULL(attrs); 1306 goto out; 1307 } 1308 lst_insert_tail(attrs, att); 1309 } 1310 1311 for (i = 0; i < lst_size(nattrs); i++) { 1312 natt = lst_at(nattrs, i); 1313 found = B_FALSE; 1314 for (j = 0; j < lst_size(*eattrs); j++) { 1315 eatt = lst_at(*eattrs, j); 1316 if (SEQU(natt->att_name, eatt->att_name)) { 1317 found = B_TRUE; 1318 break; 1319 } 1320 } 1321 if (found) 1322 continue; 1323 lst_insert_tail(attrs, attrib_duplicate(natt)); 1324 } 1325 1326 } else if (flags & (F_MOD_REM | F_MOD_SUB)) { 1327 1328 for (i = 0; i < lst_size(nattrs); i++) { 1329 natt = lst_at(nattrs, i); 1330 for (j = 0; j < lst_size(nattrs); j++) { 1331 att = lst_at(nattrs, j); 1332 if (SEQU(natt->att_name, att->att_name) && 1333 i != j) { 1334 util_add_errmsg(errlst, gettext( 1335 "Duplicate Attributes \"%s\""), 1336 natt->att_name); 1337 goto out; 1338 } 1339 } 1340 1341 found = B_FALSE; 1342 for (j = 0; j < lst_size(*eattrs); j++) { 1343 eatt = lst_at(*eattrs, j); 1344 if (SEQU(eatt->att_name, natt->att_name)) { 1345 found = B_TRUE; 1346 break; 1347 } 1348 } 1349 if (!found) { 1350 util_add_errmsg(errlst, gettext( 1351 "Project does not contain \"%s\""), 1352 natt->att_name); 1353 goto out; 1354 } 1355 } 1356 1357 attrs = util_safe_malloc(sizeof (lst_t)); 1358 lst_create(attrs); 1359 1360 for (i = 0; i < lst_size(*eattrs); i++) { 1361 eatt = lst_at(*eattrs, i); 1362 found = B_FALSE; 1363 for (j = 0; j < lst_size(nattrs); j++) { 1364 natt = lst_at(nattrs, j); 1365 if (SEQU(eatt->att_name, natt->att_name)) { 1366 found = B_TRUE; 1367 break; 1368 } 1369 } 1370 1371 if (flags & F_MOD_REM) { 1372 att = found ? 1373 attrib_merge(eatt, natt, flags, errlst) : 1374 attrib_duplicate(eatt); 1375 } else if (flags & F_MOD_SUB) { 1376 att = attrib_duplicate(found ? natt : eatt); 1377 } 1378 1379 if (att == NULL) { 1380 attrib_free_lst(attrs); 1381 UTIL_FREE_SNULL(attrs); 1382 goto out; 1383 } else if (SEQU(att->att_name, "")) { 1384 attrib_free(att); 1385 } else { 1386 lst_insert_tail(attrs, att); 1387 } 1388 } 1389 1390 } else if (flags & F_MOD_REP) { 1391 attrs = util_safe_malloc(sizeof (lst_t)); 1392 lst_create(attrs); 1393 for (i = 0; i < lst_size(nattrs); i++) { 1394 natt = lst_at(nattrs, i); 1395 lst_insert_tail(attrs, attrib_duplicate(natt)); 1396 } 1397 } 1398 out: 1399 if (attrs != NULL) { 1400 attrib_free_lst(*eattrs); 1401 free(*eattrs); 1402 *eattrs = attrs; 1403 } 1404 }