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 }