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 }