• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  This library is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU Lesser General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This library is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  Lesser General Public License for more details.
11  *
12  *  You should have received a copy of the GNU Lesser General Public
13  *  License along with this library; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15  *
16  *  Support for the verb/device/modifier core logic and API,
17  *  command line tool and file parser was kindly sponsored by
18  *  Texas Instruments Inc.
19  *  Support for multiple active modifiers and devices,
20  *  transition sequences, multiple client access and user defined use
21  *  cases was kindly sponsored by Wolfson Microelectronics PLC.
22  *
23  *  Copyright (C) 2019 Red Hat Inc.
24  *  Authors: Jaroslav Kysela <perex@perex.cz>
25  */
26 
27 #include "ucm_local.h"
28 #include <regex.h>
29 
get_string(snd_config_t * compound,const char * key,const char ** str)30 static int get_string(snd_config_t *compound, const char *key, const char **str)
31 {
32 	snd_config_t *node;
33 	int err;
34 
35 	err = snd_config_search(compound, key, &node);
36 	if (err < 0)
37 		return err;
38 	return snd_config_get_string(node, str);
39 }
40 
if_eval_string(snd_use_case_mgr_t * uc_mgr,snd_config_t * eval)41 static int if_eval_string(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
42 {
43 	const char *string1 = NULL, *string2 = NULL;
44 	char *s1, *s2;
45 	int err;
46 
47 	if (uc_mgr->conf_format >= 3) {
48 		err = get_string(eval, "Empty", &string1);
49 		if (err < 0 && err != -ENOENT) {
50 			uc_error("String error (If.Condition.Empty)");
51 			return -EINVAL;
52 		}
53 
54 		if (string1) {
55 			err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
56 			if (err < 0)
57 				return err;
58 			err = s1 == NULL || s1[0] == '\0';
59 			free(s1);
60 			return err;
61 		}
62 	}
63 
64 	err = get_string(eval, "String1", &string1);
65 	if (err < 0 && err != -ENOENT) {
66 		uc_error("String error (If.Condition.String1)");
67 		return -EINVAL;
68 	}
69 
70 	err = get_string(eval, "String2", &string2);
71 	if (err < 0 && err != -ENOENT) {
72 		uc_error("String error (If.Condition.String2)");
73 		return -EINVAL;
74 	}
75 
76 	if (string1 || string2) {
77 		if (string1 == NULL) {
78 			uc_error("If.Condition.String1 not defined");
79 			return -EINVAL;
80 		}
81 		if (string2 == NULL) {
82 			uc_error("If.Condition.String2 not defined");
83 			return -EINVAL;
84 		}
85 		err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
86 		if (err < 0)
87 			return err;
88 		err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
89 		if (err < 0) {
90 			free(s1);
91 			return err;
92 		}
93 		err = strcasecmp(s1, s2) == 0;
94 		free(s2);
95 		free(s1);
96 		return err;
97 	}
98 
99 	err = get_string(eval, "Haystack", &string1);
100 	if (err < 0 && err != -ENOENT) {
101 		uc_error("String error (If.Condition.Haystack)");
102 		return -EINVAL;
103 	}
104 
105 	err = get_string(eval, "Needle", &string2);
106 	if (err < 0 && err != -ENOENT) {
107 		uc_error("String error (If.Condition.Needle)");
108 		return -EINVAL;
109 	}
110 
111 	if (string1 || string2) {
112 		if (string1 == NULL) {
113 			uc_error("If.Condition.Haystack not defined");
114 			return -EINVAL;
115 		}
116 		if (string2 == NULL) {
117 			uc_error("If.Condition.Needle not defined");
118 			return -EINVAL;
119 		}
120 		err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
121 		if (err < 0)
122 			return err;
123 		err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
124 		if (err < 0) {
125 			free(s1);
126 			return err;
127 		}
128 		err = strstr(s1, s2) != NULL;
129 		free(s2);
130 		free(s1);
131 		return err;
132 	}
133 
134 	uc_error("Unknown String condition arguments");
135 	return -EINVAL;
136 }
137 
if_eval_regex_match(snd_use_case_mgr_t * uc_mgr,snd_config_t * eval)138 static int if_eval_regex_match(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
139 {
140 	const char *string, *regex_string;
141 	char *s;
142 	regex_t re;
143 	int options = REG_EXTENDED | REG_ICASE;
144 	regmatch_t match[1];
145 	int err;
146 
147 	err = get_string(eval, "String", &string);
148 	if (err < 0) {
149 		uc_error("RegexMatch error (If.Condition.String)");
150 		return -EINVAL;
151 	}
152 
153 	err = get_string(eval, "Regex", &regex_string);
154 	if (err < 0) {
155 		uc_error("RegexMatch error (If.Condition.Regex)");
156 		return -EINVAL;
157 	}
158 
159 	err = uc_mgr_get_substituted_value(uc_mgr, &s, regex_string);
160 	if (err < 0)
161 		return err;
162 	err = regcomp(&re, s, options);
163 	if (err) {
164 		uc_error("Regex '%s' compilation failed (code %d)", s, err);
165 		free(s);
166 		return -EINVAL;
167 	}
168 	free(s);
169 
170 	err = uc_mgr_get_substituted_value(uc_mgr, &s, string);
171 	if (err < 0) {
172 		regfree(&re);
173 		return err;
174 	}
175 	err = regexec(&re, s, ARRAY_SIZE(match), match, 0);
176 	free(s);
177 	regfree(&re);
178 	return err == 0;
179 }
180 
if_eval_control_exists(snd_use_case_mgr_t * uc_mgr,snd_config_t * eval)181 static int if_eval_control_exists(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
182 {
183 	snd_ctl_t *ctl;
184 	struct ctl_list *ctl_list;
185 	const char *device = NULL, *ctldef, *enumval = NULL, *name;
186 	snd_ctl_elem_id_t *elem_id;
187 	snd_ctl_elem_info_t *elem_info;
188 	snd_ctl_elem_type_t type;
189 	char *s;
190 	int err, i, items;
191 
192 	snd_ctl_elem_id_alloca(&elem_id);
193 	snd_ctl_elem_info_alloca(&elem_info);
194 
195 	err = get_string(eval, "Device", &device);
196 	if (err < 0 && err != -ENOENT) {
197 		uc_error("ControlExists error (If.Condition.Device)");
198 		return -EINVAL;
199 	}
200 
201 	err = get_string(eval, "Control", &ctldef);
202 	if (err < 0) {
203 		uc_error("ControlExists error (If.Condition.Control)");
204 		return -EINVAL;
205 	}
206 
207 	err = get_string(eval, "ControlEnum", &enumval);
208 	if (err < 0 && err != -ENOENT) {
209 		uc_error("ControlExists error (If.Condition.ControlEnum)");
210 		return -EINVAL;
211 	}
212 
213 	err = uc_mgr_get_substituted_value(uc_mgr, &s, ctldef);
214 	if (err < 0)
215 		return err;
216 	err = snd_ctl_ascii_elem_id_parse(elem_id, s);
217 	free(s);
218 	if (err < 0) {
219 		uc_error("unable to parse element identificator (%s)", ctldef);
220 		return -EINVAL;
221 	}
222 
223 	if (device == NULL) {
224 		ctl = uc_mgr_get_ctl(uc_mgr);
225 		if (ctl == NULL) {
226 			uc_error("cannot determine control device");
227 			return -EINVAL;
228 		}
229 	} else {
230 		err = uc_mgr_get_substituted_value(uc_mgr, &s, device);
231 		if (err < 0)
232 			return err;
233 		err = uc_mgr_open_ctl(uc_mgr, &ctl_list, s, 1);
234 		free(s);
235 		if (err < 0)
236 			return err;
237 		ctl = ctl_list->ctl;
238 	}
239 
240 	snd_ctl_elem_info_set_id(elem_info, elem_id);
241 	err = snd_ctl_elem_info(ctl, elem_info);
242 	if (err < 0)
243 		return 0;
244 
245 	if (enumval) {
246 		type = snd_ctl_elem_info_get_type(elem_info);
247 		if (type != SND_CTL_ELEM_TYPE_ENUMERATED)
248 			return 0;
249 		err = uc_mgr_get_substituted_value(uc_mgr, &s, enumval);
250 		if (err < 0)
251 			return err;
252 		items = snd_ctl_elem_info_get_items(elem_info);
253 		for (i = 0; i < items; i++) {
254 			snd_ctl_elem_info_set_item(elem_info, i);
255 			err = snd_ctl_elem_info(ctl, elem_info);
256 			if (err < 0) {
257 				free(s);
258 				return err;
259 			}
260 			name = snd_ctl_elem_info_get_item_name(elem_info);
261 			if (strcasecmp(name, s) == 0) {
262 				free(s);
263 				return 1;
264 			}
265 		}
266 		free(s);
267 		return 0;
268 	}
269 
270 	return 1;
271 }
272 
if_eval_path(snd_use_case_mgr_t * uc_mgr,snd_config_t * eval)273 static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
274 {
275 	const char *path, *mode = "";
276 	int err, amode = F_OK;
277 
278 	if (uc_mgr->conf_format < 4) {
279 		uc_error("Path condition is supported in v4+ syntax");
280 		return -EINVAL;
281 	}
282 
283 	err = get_string(eval, "Path", &path);
284 	if (err < 0) {
285 		uc_error("Path error (If.Condition.Path)");
286 		return -EINVAL;
287 	}
288 
289 	err = get_string(eval, "Mode", &mode);
290 	if (err < 0 && err != -ENOENT) {
291 		uc_error("Path error (If.Condition.Mode)");
292 		return -EINVAL;
293 	}
294 
295 	if (strncasecmp(mode, "exist", 5) == 0) {
296 		amode = F_OK;
297 	} else if (strcasecmp(mode, "read") == 0) {
298 		amode = R_OK;
299 	} else if (strcasecmp(mode, "write") == 0) {
300 		amode = W_OK;
301 	} else if (strcasecmp(mode, "exec") == 0) {
302 		amode = X_OK;
303 	} else {
304 		uc_error("Path unknown mode (If.Condition.Mode)");
305 		return -EINVAL;
306 	}
307 
308 #ifdef HAVE_EACCESS
309 	if (eaccess(path, amode))
310 #else
311 	if (access(path, amode))
312 #endif
313 		return 0;
314 
315 	return 1;
316 }
317 
if_eval(snd_use_case_mgr_t * uc_mgr,snd_config_t * eval)318 static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
319 {
320 	const char *type;
321 	int err;
322 
323 	if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) {
324 		uc_error("compound type expected for If.Condition");
325 		return -EINVAL;
326 	}
327 
328 	err = get_string(eval, "Type", &type);
329 	if (err < 0) {
330 		uc_error("type block error (If.Condition)");
331 		return -EINVAL;
332 	}
333 
334 	if (strcmp(type, "AlwaysTrue") == 0)
335 		return 1;
336 
337 	if (strcmp(type, "String") == 0)
338 		return if_eval_string(uc_mgr, eval);
339 
340 	if (strcmp(type, "ControlExists") == 0)
341 		return if_eval_control_exists(uc_mgr, eval);
342 
343 	if (strcmp(type, "RegexMatch") == 0)
344 		return if_eval_regex_match(uc_mgr, eval);
345 
346 	if (strcmp(type, "Path") == 0)
347 		return if_eval_path(uc_mgr, eval);
348 
349 	uc_error("unknown If.Condition.Type");
350 	return -EINVAL;
351 }
352 
if_eval_one(snd_use_case_mgr_t * uc_mgr,snd_config_t * cond,snd_config_t ** result,snd_config_t ** before,snd_config_t ** after)353 static int if_eval_one(snd_use_case_mgr_t *uc_mgr,
354 		       snd_config_t *cond,
355 		       snd_config_t **result,
356 		       snd_config_t **before,
357 		       snd_config_t **after)
358 {
359 	snd_config_t *expr, *_true = NULL, *_false = NULL;
360 	int err;
361 
362 	*result = NULL;
363 
364 	if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) {
365 		uc_error("compound type expected for If.1");
366 		return -EINVAL;
367 	}
368 
369 	if (snd_config_search(cond, "Condition", &expr) < 0) {
370 		uc_error("condition block expected (If)");
371 		return -EINVAL;
372 	}
373 
374 	err = snd_config_search(cond, "True", &_true);
375 	if (err < 0 && err != -ENOENT) {
376 		uc_error("true block error (If)");
377 		return -EINVAL;
378 	}
379 
380 	err = snd_config_search(cond, "False", &_false);
381 	if (err < 0 && err != -ENOENT) {
382 		uc_error("false block error (If)");
383 		return -EINVAL;
384 	}
385 
386 	err = snd_config_search(cond, "Before", before);
387 	if (err < 0 && err != -ENOENT) {
388 		uc_error("before block identifier error");
389 		return -EINVAL;
390 	}
391 
392 	err = snd_config_search(cond, "After", after);
393 	if (err < 0 && err != -ENOENT) {
394 		uc_error("before block identifier error");
395 		return -EINVAL;
396 	}
397 
398 	err = if_eval(uc_mgr, expr);
399 	if (err > 0) {
400 		*result = _true;
401 		return 0;
402 	} else if (err == 0) {
403 		*result = _false;
404 		return 0;
405 	} else {
406 		return err;
407 	}
408 }
409 
410 #if 0
411 static void config_dump(snd_config_t *cfg)
412 {
413 	snd_output_t *out;
414 	snd_output_stdio_attach(&out, stderr, 0);
415 	snd_output_printf(out, "-----\n");
416 	snd_config_save(cfg, out);
417 	snd_output_close(out);
418 }
419 #endif
420 
421 /*
422  * put back the result from all conditions to the parent
423  */
uc_mgr_evaluate_condition(snd_use_case_mgr_t * uc_mgr,snd_config_t * parent,snd_config_t * cond)424 int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr,
425 			      snd_config_t *parent,
426 			      snd_config_t *cond)
427 {
428 	snd_config_iterator_t i, next;
429 	snd_config_t *a, *n, *before, *after;
430 	int err;
431 
432 	if (uc_mgr->conf_format < 2) {
433 		uc_error("conditions are not supported for v1 syntax");
434 		return -EINVAL;
435 	}
436 
437 	if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) {
438 		uc_error("compound type expected for If");
439 		return -EINVAL;
440 	}
441 
442 	snd_config_for_each(i, next, cond) {
443 		n = snd_config_iterator_entry(i);
444 		before = after = NULL;
445 		err = if_eval_one(uc_mgr, n, &a, &before, &after);
446 		if (err < 0)
447 			return err;
448 		if (a == NULL)
449 			continue;
450 		err = uc_mgr_evaluate_inplace(uc_mgr, a);
451 		if (err < 0)
452 			return err;
453 		err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after);
454 		if (err < 0)
455 			return err;
456 		snd_config_delete(a);
457 	}
458 	return 0;
459 }
460