1 /*
2  *  Advanced Linux Sound Architecture Control Program - Parse initialization files
3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
4  *		     Greg Kroah-Hartman <greg@kroah.com>,
5  *		     Kay Sievers <kay.sievers@vrfy.org>
6  *
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation; either version 2 of the License, or
11  *   (at your option) any later version.
12  *
13  *   This program is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stddef.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fnmatch.h>
33 #include <sys/stat.h>
34 #include <sys/un.h>
35 #include <sys/wait.h>
36 #include <sys/select.h>
37 #include <sys/types.h>
38 #include <dirent.h>
39 #include <math.h>
40 #include "aconfig.h"
41 #include "alsactl.h"
42 #include "list.h"
43 
44 #define PATH_SIZE	512
45 #define NAME_SIZE	128
46 #define EJUSTRETURN	0x7fffffff
47 
48 enum key_op {
49 	KEY_OP_UNSET,
50 	KEY_OP_MATCH,
51 	KEY_OP_NOMATCH,
52 	KEY_OP_ADD,
53 	KEY_OP_ASSIGN,
54 	KEY_OP_ASSIGN_FINAL
55 };
56 
57 struct pair {
58 	char *key;
59 	char *value;
60 	struct pair *next;
61 };
62 
63 struct space {
64 	struct pair *pairs;
65 	char *rootdir;
66 	char *go_to;
67 	char *program_result;
68 	const char *filename;
69 	int linenum;
70 	int log_run;
71 	int exit_code;
72 	int quit;
73 	unsigned int ctl_id_changed;
74 	snd_hctl_t *ctl_handle;
75 	snd_ctl_card_info_t *ctl_card_info;
76 	snd_ctl_elem_id_t *ctl_id;
77 	snd_ctl_elem_info_t *ctl_info;
78 	snd_ctl_elem_value_t *ctl_value;
79 };
80 
Perror(struct space *space, const char *fmt, ...)81 static void Perror(struct space *space, const char *fmt, ...)
82 {
83 	va_list arg;
84 	va_start(arg, fmt);
85 	fprintf(stderr, "%s:%i: ", space->filename, space->linenum);
86 	vfprintf(stderr, fmt, arg);
87 	putc('\n', stderr);
88 	va_end(arg);
89 }
90 
91 #include "init_sysdeps.c"
92 #include "init_utils_string.c"
93 #include "init_utils_run.c"
94 #include "init_sysfs.c"
95 
free_space(struct space *space)96 static void free_space(struct space *space)
97 {
98 	struct pair *pair = space->pairs;
99 	struct pair *next = pair;
100 
101 	while (next) {
102 		pair = next;
103 		next = pair->next;
104 		free(pair->value);
105 		free(pair->key);
106 		free(pair);
107 	}
108 	space->pairs = NULL;
109 	if (space->ctl_value) {
110 		snd_ctl_elem_value_free(space->ctl_value);
111 		space->ctl_value = NULL;
112 	}
113 	if (space->ctl_info) {
114 		snd_ctl_elem_info_free(space->ctl_info);
115 		space->ctl_info = NULL;
116 	}
117 	if (space->ctl_id) {
118 		snd_ctl_elem_id_free(space->ctl_id);
119 		space->ctl_id = NULL;
120 	}
121 	if (space->ctl_card_info) {
122 		snd_ctl_card_info_free(space->ctl_card_info);
123 		space->ctl_card_info = NULL;
124 	}
125 	if (space->ctl_handle) {
126 		snd_hctl_close(space->ctl_handle);
127 		space->ctl_handle = NULL;
128 	}
129 	if (space->rootdir)
130 		free(space->rootdir);
131 	if (space->program_result)
132 		free(space->program_result);
133 	if (space->go_to)
134 		free(space->go_to);
135 	free(space);
136 }
137 
value_find(struct space *space, const char *key)138 static struct pair *value_find(struct space *space, const char *key)
139 {
140 	struct pair *pair = space->pairs;
141 
142 	while (pair && strcmp(pair->key, key) != 0)
143 		pair = pair->next;
144 	return pair;
145 }
146 
value_set(struct space *space, const char *key, const char *value)147 static int value_set(struct space *space, const char *key, const char *value)
148 {
149 	struct pair *pair;
150 
151 	pair = value_find(space, key);
152 	if (pair) {
153 		free(pair->value);
154 		pair->value = strdup(value);
155 		if (pair->value == NULL)
156 			return -ENOMEM;
157 	} else {
158 		pair = malloc(sizeof(struct pair));
159 		if (pair == NULL)
160 			return -ENOMEM;
161 		pair->key = strdup(key);
162 		if (pair->key == NULL) {
163 			free(pair);
164 			return -ENOMEM;
165 		}
166 		pair->value = strdup(value);
167 		if (pair->value == NULL) {
168 			free(pair->key);
169 			free(pair);
170 			return -ENOMEM;
171 		}
172 		pair->next = space->pairs;
173 		space->pairs = pair;
174 	}
175 	return 0;
176 }
177 
init_space(struct space **space, int card)178 static int init_space(struct space **space, int card)
179 {
180 	struct space *res;
181 	char device[16];
182 	int err;
183 
184 	res = calloc(1, sizeof(struct space));
185 	if (res == NULL)
186 		return -ENOMEM;
187 	res->ctl_id_changed = ~0;
188 	res->linenum = -1;
189 	sprintf(device, "hw:%d", card);
190 	err = snd_hctl_open(&res->ctl_handle, device, 0);
191 	if (err < 0)
192 		goto error;
193 	err = snd_hctl_load(res->ctl_handle);
194 	if (err < 0)
195 		goto error;
196 	err = snd_ctl_card_info_malloc(&res->ctl_card_info);
197 	if (err < 0)
198 		goto error;
199 	err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info);
200 	if (err < 0)
201 		goto error;
202 	err = snd_ctl_elem_id_malloc(&res->ctl_id);
203 	if (err < 0)
204 		goto error;
205 	err = snd_ctl_elem_info_malloc(&res->ctl_info);
206 	if (err < 0)
207 		goto error;
208 	err = snd_ctl_elem_value_malloc(&res->ctl_value);
209 	if (err < 0)
210 		goto error;
211 	*space = res;
212 	return 0;
213  error:
214  	free_space(res);
215  	return err;
216 }
217 
cardinfo_get(struct space *space, const char *attr)218 static const char *cardinfo_get(struct space *space, const char *attr)
219 {
220 	if (strncasecmp(attr, "CARD", 4) == 0) {
221 		static char res[16];
222 		sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info));
223 		return res;
224 	}
225 	if (strncasecmp(attr, "ID", 2) == 0)
226 		return snd_ctl_card_info_get_id(space->ctl_card_info);
227 	if (strncasecmp(attr, "DRIVER", 6) == 0)
228 		return snd_ctl_card_info_get_driver(space->ctl_card_info);
229 	if (strncasecmp(attr, "NAME", 4) == 0)
230 		return snd_ctl_card_info_get_name(space->ctl_card_info);
231 	if (strncasecmp(attr, "LONGNAME", 8) == 0)
232 		return snd_ctl_card_info_get_longname(space->ctl_card_info);
233 	if (strncasecmp(attr, "MIXERNAME", 9) == 0)
234 		return snd_ctl_card_info_get_mixername(space->ctl_card_info);
235 	if (strncasecmp(attr, "COMPONENTS", 10) == 0)
236 		return snd_ctl_card_info_get_components(space->ctl_card_info);
237 	Perror(space, "unknown cardinfo{} attribute '%s'", attr);
238 	return NULL;
239 }
240 
check_id_changed(struct space *space, unsigned int what)241 static int check_id_changed(struct space *space, unsigned int what)
242 {
243 	snd_hctl_elem_t *elem;
244 	int err;
245 
246 	if ((space->ctl_id_changed & what & 1) != 0) {
247 		snd_ctl_elem_id_set_numid(space->ctl_id, 0);
248 		elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
249 		if (!elem)
250 			return -ENOENT;
251 		err = snd_hctl_elem_info(elem, space->ctl_info);
252 		if (err == 0)
253 			space->ctl_id_changed &= ~1;
254 		return err;
255 	}
256 	if ((space->ctl_id_changed & what & 2) != 0) {
257 		snd_ctl_elem_id_set_numid(space->ctl_id, 0);
258 		elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
259 		if (!elem)
260 			return -ENOENT;
261 		err = snd_hctl_elem_read(elem, space->ctl_value);
262 		if (err == 0)
263 			space->ctl_id_changed &= ~2;
264 		return err;
265 	}
266 	return 0;
267 }
268 
get_ctl_value(struct space *space)269 static const char *get_ctl_value(struct space *space)
270 {
271 	snd_ctl_elem_type_t type;
272 	unsigned int idx, count;
273 	static char res[1024], tmp[16];
274 	static const char hex[] = "0123456789abcdef";
275 	char *pos;
276 	const char *pos1;
277 
278 	type = snd_ctl_elem_info_get_type(space->ctl_info);
279 	count = snd_ctl_elem_info_get_count(space->ctl_info);
280 	res[0] = '\0';
281 	switch (type) {
282 	case SND_CTL_ELEM_TYPE_BOOLEAN:
283 		for (idx = 0; idx < count; idx++) {
284 			if (idx > 0)
285 				strlcat(res, ",", sizeof(res));
286 			strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res));
287 		}
288 		break;
289 	case SND_CTL_ELEM_TYPE_INTEGER:
290 		for (idx = 0; idx < count; idx++) {
291 			if (idx > 0)
292 				strlcat(res, ",", sizeof(res));
293 			snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx));
294 			strlcat(res, tmp, sizeof(res));
295 		}
296 		break;
297 	case SND_CTL_ELEM_TYPE_INTEGER64:
298 		for (idx = 0; idx < count; idx++) {
299 			if (idx > 0)
300 				strlcat(res, ",", sizeof(res));
301 			snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx));
302 			strlcat(res, tmp, sizeof(res));
303 		}
304 		break;
305 	case SND_CTL_ELEM_TYPE_ENUMERATED:
306 		for (idx = 0; idx < count; idx++) {
307 			if (idx > 0)
308 				strlcat(res, ",", sizeof(res));
309 			snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx));
310 			strlcat(res, tmp, sizeof(res));
311 		}
312 		break;
313 	case SND_CTL_ELEM_TYPE_BYTES:
314 	case SND_CTL_ELEM_TYPE_IEC958:
315 		if (type == SND_CTL_ELEM_TYPE_IEC958)
316 			count = sizeof(snd_aes_iec958_t);
317 		if (count > (sizeof(res)-1)/2)
318 			count = (sizeof(res)-1/2);
319 		pos = res;
320 		pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value);
321 		while (count > 0) {
322 			idx = *pos1++;
323 			*pos++ = hex[idx >> 4];
324 			*pos++ = hex[idx & 0x0f];
325 			count++;
326 		}
327 		*pos++ = '\0';
328 		break;
329 	default:
330 		Perror(space, "unknown element type '%i'", type);
331 		return NULL;
332 	}
333 	return res;
334 }
335 
336 /* Function to convert from percentage to volume. val = percentage */
337 #define convert_prange1(val, min, max) \
338         ceil((val) * ((max) - (min)) * 0.01 + (min))
339 
set_ctl_value(struct space *space, const char *value, int all)340 static int set_ctl_value(struct space *space, const char *value, int all)
341 {
342 	snd_ctl_elem_type_t type;
343 	unsigned int idx, idx2, count, items;
344 	const char *pos, *pos2;
345 	snd_hctl_elem_t *elem;
346 	int val;
347 	long lval;
348 
349 	type = snd_ctl_elem_info_get_type(space->ctl_info);
350 	count = snd_ctl_elem_info_get_count(space->ctl_info);
351 	switch (type) {
352 	case SND_CTL_ELEM_TYPE_BOOLEAN:
353 		for (idx = 0; idx < count; idx++) {
354 			while (*value == ' ')
355 				value++;
356 			if (*value == '\0')
357 				goto missing;
358 			val = strncasecmp(value, "true", 4) == 0 ||
359 				strncasecmp(value, "yes", 3) == 0 ||
360 				strncasecmp(value, "on", 2) == 0 ||
361 				strncasecmp(value, "1", 1) == 0;
362 			snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val);
363 			if (all)
364 				continue;
365 			pos = strchr(value, ',');
366 			value = pos ? pos + 1 : value + strlen(value) - 1;
367 		}
368 		break;
369 	case SND_CTL_ELEM_TYPE_INTEGER:
370 		for (idx = 0; idx < count; idx++) {
371 			while (*value == ' ')
372 				value++;
373 			pos = strchr(value, ',');
374 			if (pos)
375 				*(char *)pos = '\0';
376 			remove_trailing_chars((char *)value, ' ');
377 			items = pos ? (unsigned)(pos - value) : (unsigned)strlen(value);
378 			if (items > 1 && value[items-1] == '%') {
379 				val = convert_prange1(strtol(value, NULL, 0), snd_ctl_elem_info_get_min(space->ctl_info), snd_ctl_elem_info_get_max(space->ctl_info));
380 				snd_ctl_elem_value_set_integer(space->ctl_value, idx, val);
381 			} else if (items > 2 && value[items-2] == 'd' && value[items-1] == 'B') {
382 				val = strtol(value, NULL, 0) * 100;
383 				if ((pos2 = strchr(value, '.')) != NULL) {
384 					if (isdigit(*(pos2-1)) && isdigit(*(pos2-2))) {
385 						if (val < 0)
386 							val -= strtol(pos2 + 1, NULL, 0);
387 						else
388 							val += strtol(pos2 + 1, NULL, 0);
389 					} else if (isdigit(*(pos2-1))) {
390 						if (val < 0)
391 							val -= strtol(pos2 + 1, NULL, 0) * 10;
392 						else
393 							val += strtol(pos2 + 1, NULL, 0) * 10;
394 					}
395 				}
396 				val = snd_ctl_convert_from_dB(snd_hctl_ctl(space->ctl_handle), space->ctl_id, val, &lval, -1);
397 				if (val < 0) {
398 					dbg("unable to convert dB value '%s' to internal integer range", value);
399 					return val;
400 				}
401 				snd_ctl_elem_value_set_integer(space->ctl_value, idx, lval);
402 			} else {
403 				snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0));
404 			}
405 			if (all)
406 				continue;
407 			value = pos ? pos + 1 : value + strlen(value) - 1;
408 		}
409 		break;
410 	case SND_CTL_ELEM_TYPE_INTEGER64:
411 		for (idx = 0; idx < count; idx++) {
412 			while (*value == ' ')
413 				value++;
414 			snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0));
415 			if (all)
416 				continue;
417 			pos = strchr(value, ',');
418 			value = pos ? pos + 1 : value + strlen(value) - 1;
419 		}
420 		break;
421 	case SND_CTL_ELEM_TYPE_ENUMERATED:
422 		for (idx = 0; idx < count; idx++) {
423 			while (*value == ' ')
424 				value++;
425 			pos = strchr(value, ',');
426 			if (isdigit(value[0]) || value[0] == '-') {
427 				snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0));
428 			} else {
429 				if (pos)
430 					*(char *)pos = '\0';
431 				remove_trailing_chars((char *)value, ' ');
432 				items = snd_ctl_elem_info_get_items(space->ctl_info);
433 				for (idx2 = 0; idx2 < items; idx2++) {
434 					snd_ctl_elem_info_set_item(space->ctl_info, idx2);
435 					elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
436 					if (elem == NULL)
437 						return -ENOENT;
438 					val = snd_hctl_elem_info(elem, space->ctl_info);
439 					if (val < 0)
440 						return val;
441 					if (strcasecmp(snd_ctl_elem_info_get_item_name(space->ctl_info), value) == 0) {
442 						snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, idx2);
443 						break;
444 					}
445 				}
446 				if (idx2 >= items) {
447 					Perror(space, "wrong enum identifier '%s'", value);
448 					return -EINVAL;
449 				}
450 			}
451 			if (all)
452 				continue;
453 			value = pos ? pos + 1 : value + strlen(value) - 1;
454 		}
455 		break;
456 	case SND_CTL_ELEM_TYPE_BYTES:
457 	case SND_CTL_ELEM_TYPE_IEC958:
458 		if (type == SND_CTL_ELEM_TYPE_IEC958)
459 			count = sizeof(snd_aes_iec958_t);
460 		while (*value == ' ')
461 			value++;
462 		if (strlen(value) != count * 2) {
463 			Perror(space, "bad ctl value hexa length (should be %u bytes)", count);
464 			return -EINVAL;
465 		}
466 		for (idx = 0; idx < count; idx += 2) {
467 			int nibble1 = hextodigit(*(value++));
468 			int nibble2 = hextodigit(*(value++));
469 			if (nibble1 < 0 || nibble2 < 0) {
470 				Perror(space, "bad ctl hexa value");
471 				return -EINVAL;
472 			}
473 			val = (nibble1 << 4) | nibble2;
474 			snd_ctl_elem_value_set_byte(space->ctl_value, idx, val);
475 		}
476 		break;
477 	default:
478 		Perror(space, "unknown element type '%i'", type);
479 		return -EINVAL;
480 	}
481 	return 0;
482   missing:
483   	Perror(space, "missing some ctl values (line %i)", space->linenum);
484   	return -EINVAL;
485 }
486 
do_match(const char *key, enum key_op op, const char *key_value, const char *value)487 static int do_match(const char *key, enum key_op op,
488 		    const char *key_value, const char *value)
489 {
490 	int match;
491 
492 	if (value == NULL)
493 		return 0;
494 	dbg("match %s '%s' <-> '%s'", key, key_value, value);
495 	match = fnmatch(key_value, value, 0) == 0;
496 	if (match && op == KEY_OP_MATCH) {
497 		dbg("%s is true (matching value)", key);
498 		return 1;
499 	}
500 	if (!match && op == KEY_OP_NOMATCH) {
501 		dbg("%s is true (non-matching value)", key);
502 		return 1;
503 	}
504 	dbg("%s is false", key);
505 	return 0;
506 }
507 
ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id)508 static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id)
509 {
510 	if ((int)snd_ctl_elem_id_get_interface(pattern) != -1 &&
511 	    snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id))
512 	    	return 0;
513 	if ((int)snd_ctl_elem_id_get_device(pattern) != -1 &&
514 	    snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id))
515 		return 0;
516 	if ((int)snd_ctl_elem_id_get_subdevice(pattern) != -1 &&
517 	    snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id))
518 	    	return 0;
519 	if ((int)snd_ctl_elem_id_get_index(pattern) != -1 &&
520 	    snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id))
521 	    	return 0;
522 	if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0)
523 		return 0;
524 	return 1;
525 }
526 
elemid_get(struct space *space, const char *attr)527 static const char *elemid_get(struct space *space, const char *attr)
528 {
529 	long long val;
530 	snd_ctl_elem_type_t type;
531 	static char res[256];
532 
533 	if (strncasecmp(attr, "numid", 5) == 0) {
534 		val = snd_ctl_elem_id_get_numid(space->ctl_id);
535 	    	goto value;
536 	}
537 	if (strncasecmp(attr, "iface", 5) == 0 ||
538 	    strncasecmp(attr, "interface", 9) == 0)
539 	    	return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id));
540 	if (strncasecmp(attr, "device", 6) == 0) {
541 		val = snd_ctl_elem_id_get_device(space->ctl_id);
542 	    	goto value;
543 	}
544 	if (strncasecmp(attr, "subdev", 6) == 0) {
545 		val = snd_ctl_elem_id_get_subdevice(space->ctl_id);
546 	    	goto value;
547 	}
548 	if (strncasecmp(attr, "name", 4) == 0)
549 		return snd_ctl_elem_id_get_name(space->ctl_id);
550 	if (strncasecmp(attr, "index", 5) == 0) {
551 		val = snd_ctl_elem_id_get_index(space->ctl_id);
552 	    	goto value;
553 	}
554 	if (strncasecmp(attr, "type", 4) == 0) {
555 		if (check_id_changed(space, 1))
556 			return NULL;
557 		return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info));
558 	}
559 	if (strncasecmp(attr, "attr", 4) == 0) {
560 		if (check_id_changed(space, 1))
561 			return NULL;
562 		res[0] = '\0';
563 		if (snd_ctl_elem_info_is_readable(space->ctl_info))
564 			strcat(res, "r");
565 		if (snd_ctl_elem_info_is_writable(space->ctl_info))
566 			strcat(res, "w");
567 		if (snd_ctl_elem_info_is_volatile(space->ctl_info))
568 			strcat(res, "v");
569 		if (snd_ctl_elem_info_is_inactive(space->ctl_info))
570 			strcat(res, "i");
571 		if (snd_ctl_elem_info_is_locked(space->ctl_info))
572 			strcat(res, "l");
573 		if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info))
574 			strcat(res, "R");
575 		if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info))
576 			strcat(res, "W");
577 		if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info))
578 			strcat(res, "C");
579 		if (snd_ctl_elem_info_is_owner(space->ctl_info))
580 			strcat(res, "o");
581 		if (snd_ctl_elem_info_is_user(space->ctl_info))
582 			strcat(res, "u");
583 		return res;
584 	}
585 	if (strncasecmp(attr, "owner", 5) == 0) {
586 		if (check_id_changed(space, 1))
587 			return NULL;
588 		val = snd_ctl_elem_info_get_owner(space->ctl_info);
589 		goto value;
590 	}
591 	if (strncasecmp(attr, "count", 5) == 0) {
592 		if (check_id_changed(space, 1))
593 			return NULL;
594 		val = snd_ctl_elem_info_get_count(space->ctl_info);
595 		goto value;
596 	}
597 	if (strncasecmp(attr, "min", 3) == 0) {
598 		if (check_id_changed(space, 1))
599 			return NULL;
600 		type = snd_ctl_elem_info_get_type(space->ctl_info);
601 		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
602 			val = snd_ctl_elem_info_get_min64(space->ctl_info);
603 		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
604 			val = snd_ctl_elem_info_get_min(space->ctl_info);
605 		else
606 			goto empty;
607 		goto value;
608 	}
609 	if (strncasecmp(attr, "max", 3) == 0) {
610 		if (check_id_changed(space, 1))
611 			return NULL;
612 		type = snd_ctl_elem_info_get_type(space->ctl_info);
613 		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
614 			val = snd_ctl_elem_info_get_max64(space->ctl_info);
615 		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
616 			val = snd_ctl_elem_info_get_max(space->ctl_info);
617 		else
618 			goto empty;
619 		goto value;
620 	}
621 	if (strncasecmp(attr, "step", 3) == 0) {
622 		if (check_id_changed(space, 1))
623 			return NULL;
624 		type = snd_ctl_elem_info_get_type(space->ctl_info);
625 		if (type == SND_CTL_ELEM_TYPE_INTEGER64)
626 			val = snd_ctl_elem_info_get_step64(space->ctl_info);
627 		else if (type == SND_CTL_ELEM_TYPE_INTEGER)
628 			val = snd_ctl_elem_info_get_step(space->ctl_info);
629 		else
630 			goto empty;
631 		goto value;
632 	}
633 	if (strncasecmp(attr, "items", 5) == 0) {
634 		if (check_id_changed(space, 1))
635 			return NULL;
636 		if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED)
637 			val = snd_ctl_elem_info_get_items(space->ctl_info);
638 		else {
639 		  empty:
640 			res[0] = '\0';
641 			return res;
642 		}
643 		goto value;
644 	}
645 	if (strncasecmp(attr, "value", 5) == 0) {
646 		if (check_id_changed(space, 3))
647 			return NULL;
648 		return get_ctl_value(space);
649 	}
650 	if (strncasecmp(attr, "dBmin", 5) == 0) {
651 		long min, max;
652 		if (check_id_changed(space, 1))
653 			return NULL;
654 		if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
655 			goto empty;
656 		val = min;
657 dbvalue:
658 		sprintf(res, "%li.%02lidB", (long)(val / 100), labs(val % 100));
659 		return res;
660 	}
661 	if (strncasecmp(attr, "dBmax", 5) == 0) {
662 		long min, max;
663 		if (check_id_changed(space, 1))
664 			return NULL;
665 		if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
666 			goto empty;
667 		val = max;
668 		goto dbvalue;
669 	}
670 	if (strncasecmp(attr, "enums", 5) == 0) {
671 		unsigned int idx, items;
672 		snd_hctl_elem_t *elem;
673 		if (check_id_changed(space, 1))
674 			return NULL;
675 		if (snd_ctl_elem_info_get_type(space->ctl_info) != SND_CTL_ELEM_TYPE_ENUMERATED)
676 			goto empty;
677 		items = snd_ctl_elem_info_get_items(space->ctl_info);
678 		strcpy(res, "|");
679 		for (idx = 0; idx < items; idx++) {
680 			snd_ctl_elem_info_set_item(space->ctl_info, idx);
681 			elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
682 			if (elem == NULL)
683 				break;
684 			if (snd_hctl_elem_info(elem, space->ctl_info) < 0)
685 				break;
686 			strlcat(res, snd_ctl_elem_info_get_item_name(space->ctl_info), sizeof(res));
687 			strlcat(res, "|", sizeof(res));
688 		}
689 		return res;
690 	}
691 	if (strncasecmp(attr, "do_search", 9) == 0) {
692 		int err, index = 0;
693 		snd_hctl_elem_t *elem;
694 		snd_ctl_elem_id_t *id;
695 		char *pos = strchr(attr, ' ');
696 		if (pos)
697 			index = strtol(pos, NULL, 0);
698 		err = snd_ctl_elem_id_malloc(&id);
699 		if (err < 0)
700 			return NULL;
701 		elem = snd_hctl_first_elem(space->ctl_handle);
702 		while (elem) {
703 			snd_hctl_elem_get_id(elem, id);
704 			if (!ctl_match(space->ctl_id, id))
705 				goto next_search;
706 			if (index > 0) {
707 				index--;
708 				goto next_search;
709 			}
710 			strcpy(res, "1");
711 			snd_ctl_elem_id_copy(space->ctl_id, id);
712 			snd_ctl_elem_id_free(id);
713 			dbg("do_ctl_search found a control");
714 			return res;
715 		      next_search:
716 			elem = snd_hctl_elem_next(elem);
717 		}
718 		snd_ctl_elem_id_free(id);
719 		strcpy(res, "0");
720 		return res;
721 	}
722 	if (strncasecmp(attr, "do_count", 8) == 0) {
723 		int err, index = 0;
724 		snd_hctl_elem_t *elem;
725 		snd_ctl_elem_id_t *id;
726 		err = snd_ctl_elem_id_malloc(&id);
727 		if (err < 0)
728 			return NULL;
729 		elem = snd_hctl_first_elem(space->ctl_handle);
730 		while (elem) {
731 			snd_hctl_elem_get_id(elem, id);
732 			if (ctl_match(space->ctl_id, id))
733 				index++;
734 			elem = snd_hctl_elem_next(elem);
735 		}
736 		snd_ctl_elem_id_free(id);
737 		sprintf(res, "%d", index);
738 		dbg("do_ctl_count found %s controls", res);
739 		return res;
740 	}
741 	Perror(space, "unknown ctl{} attribute '%s'", attr);
742 	return NULL;
743   value:
744   	sprintf(res, "%lli", val);
745   	return res;
746 }
747 
elemid_set(struct space *space, const char *attr, const char *value)748 static int elemid_set(struct space *space, const char *attr, const char *value)
749 {
750 	unsigned int val;
751 	void (*fcn)(snd_ctl_elem_id_t *, unsigned int);
752 	snd_ctl_elem_iface_t iface;
753 	int err;
754 
755 	if (strncasecmp(attr, "numid", 5) == 0) {
756 		fcn = snd_ctl_elem_id_set_numid;
757 	    	goto value;
758 	}
759 	if (strncasecmp(attr, "iface", 5) == 0 ||
760 	    strncasecmp(attr, "interface", 9) == 0 ||
761 	    strncasecmp(attr, "reset", 5) == 0 ||
762 	    strncasecmp(attr, "search", 6) == 0) {
763 	    	if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) {
764 	    		iface = 0;
765 	    		goto search;
766 		}
767 	    	for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) {
768 	    		if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) {
769 			    	if (strncasecmp(attr, "reset", 5) == 0)
770 			    		snd_ctl_elem_id_clear(space->ctl_id);
771 			    	if (strncasecmp(attr, "search", 5) == 0) {
772 			    	  search:
773 			    		snd_ctl_elem_id_clear(space->ctl_id);
774 			    		/* -1 means all */
775 			    		snd_ctl_elem_id_set_interface(space->ctl_id, -1);
776 			    		snd_ctl_elem_id_set_device(space->ctl_id, -1);
777 			    		snd_ctl_elem_id_set_subdevice(space->ctl_id, -1);
778 			    		snd_ctl_elem_id_set_name(space->ctl_id, "*");
779 			    		snd_ctl_elem_id_set_index(space->ctl_id, -1);
780 			    		if (strlen(value) == 0)
781 			    			return 0;
782 				}
783 				snd_ctl_elem_id_set_interface(space->ctl_id, iface);
784 				space->ctl_id_changed = ~0;
785 			    	return 0;
786 			}
787 		}
788 		Perror(space, "unknown control interface name '%s'", value);
789 		return -EINVAL;
790 	}
791 	if (strncasecmp(attr, "device", 6) == 0) {
792 		fcn = snd_ctl_elem_id_set_device;
793 	    	goto value;
794 	}
795 	if (strncasecmp(attr, "subdev", 6) == 0) {
796 		fcn = snd_ctl_elem_id_set_subdevice;
797 	    	goto value;
798 	}
799 	if (strncasecmp(attr, "name", 4) == 0) {
800 		snd_ctl_elem_id_set_name(space->ctl_id, value);
801 	  	space->ctl_id_changed = ~0;
802 		return 0;
803 	}
804 	if (strncasecmp(attr, "index", 5) == 0) {
805 		fcn = snd_ctl_elem_id_set_index;
806 	    	goto value;
807 	}
808 	if (strncasecmp(attr, "values", 6) == 0 ||
809 	    strncasecmp(attr, "value", 5) == 0) {
810 		err = check_id_changed(space, 1);
811 		if (err < 0) {
812 			Perror(space, "control element not found");
813 			return err;
814 		}
815 		err = set_ctl_value(space, value, strncasecmp(attr, "values", 6) == 0);
816 		if (err < 0) {
817 			space->ctl_id_changed |= 2;
818 		} else {
819 			space->ctl_id_changed &= ~2;
820 			snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id);
821 			err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value);
822 			if (err < 0) {
823 				Perror(space, "value write error: %s", snd_strerror(err));
824 				return err;
825 			}
826 		}
827 	    	return err;
828 	}
829 	Perror(space, "unknown CTL{} attribute '%s'", attr);
830 	return -EINVAL;
831   value:
832   	val = (unsigned int)strtol(value, NULL, 0);
833   	fcn(space->ctl_id, val);
834   	space->ctl_id_changed = ~0;
835   	return 0;
836 }
837 
get_key(char **line, char **key, enum key_op *op, char **value)838 static int get_key(char **line, char **key, enum key_op *op, char **value)
839 {
840 	char *linepos;
841 	char *temp;
842 
843 	linepos = *line;
844 	if (linepos == NULL && linepos[0] == '\0')
845 		return -EINVAL;
846 
847 	/* skip whitespace */
848 	while (isspace(linepos[0]) || linepos[0] == ',')
849 		linepos++;
850 
851 	/* get the key */
852 	if (linepos[0] == '\0')
853 		return -EINVAL;
854 	*key = linepos;
855 
856 	while (1) {
857 		linepos++;
858 		if (linepos[0] == '\0')
859 			return -1;
860 		if (isspace(linepos[0]))
861 			break;
862 		if (linepos[0] == '=')
863 			break;
864 		if (linepos[0] == '+')
865 			break;
866 		if (linepos[0] == '!')
867 			break;
868 		if (linepos[0] == ':')
869 			break;
870 	}
871 
872 	/* remember end of key */
873 	temp = linepos;
874 
875 	/* skip whitespace after key */
876 	while (isspace(linepos[0]))
877 		linepos++;
878 	if (linepos[0] == '\0')
879 		return -EINVAL;
880 
881 	/* get operation type */
882 	if (linepos[0] == '=' && linepos[1] == '=') {
883 		*op = KEY_OP_MATCH;
884 		linepos += 2;
885 		dbg("operator=match");
886 	} else if (linepos[0] == '!' && linepos[1] == '=') {
887 		*op = KEY_OP_NOMATCH;
888 		linepos += 2;
889 		dbg("operator=nomatch");
890 	} else if (linepos[0] == '+' && linepos[1] == '=') {
891 		*op = KEY_OP_ADD;
892 		linepos += 2;
893 		dbg("operator=add");
894 	} else if (linepos[0] == '=') {
895 		*op = KEY_OP_ASSIGN;
896 		linepos++;
897 		dbg("operator=assign");
898 	} else if (linepos[0] == ':' && linepos[1] == '=') {
899 		*op = KEY_OP_ASSIGN_FINAL;
900 		linepos += 2;
901 		dbg("operator=assign_final");
902 	} else
903 		return -EINVAL;
904 
905 	/* terminate key */
906 	temp[0] = '\0';
907 	dbg("key='%s'", *key);
908 
909 	/* skip whitespace after operator */
910 	while (isspace(linepos[0]))
911 		linepos++;
912 	if (linepos[0] == '\0')
913 		return -EINVAL;
914 
915 	/* get the value*/
916 	if (linepos[0] != '"')
917 		return -EINVAL;
918 	linepos++;
919 	*value = linepos;
920 
921 	while (1) {
922 		temp = strchr(linepos, '"');
923 		if (temp && temp[-1] == '\\') {
924 			linepos = temp + 1;
925 			continue;
926 		}
927 		break;
928 	}
929 	if (!temp)
930 		return -EINVAL;
931 	temp[0] = '\0';
932 	temp++;
933 	dbg("value='%s'", *value);
934 
935 	/* move line to next key */
936 	*line = temp;
937 
938 	return 0;
939 }
940 
941 /* extract possible KEY{attr} */
get_key_attribute(struct space *space, char *str, char *res, size_t ressize)942 static char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize)
943 {
944 	char *pos;
945 	char *attr;
946 
947 	attr = strchr(str, '{');
948 	if (attr != NULL) {
949 		attr++;
950 		pos = strchr(attr, '}');
951 		if (pos == NULL) {
952 			Perror(space, "missing closing brace for format");
953 			return NULL;
954 		}
955 		pos[0] = '\0';
956 		strlcpy(res, attr, ressize);
957 		pos[0] = '}';
958 		dbg("attribute='%s'", res);
959 		return res;
960 	}
961 
962 	return NULL;
963 }
964 
965 /* extract possible {attr} and move str behind it */
get_format_attribute(struct space *space, char **str)966 static char *get_format_attribute(struct space *space, char **str)
967 {
968 	char *pos;
969 	char *attr = NULL;
970 
971 	if (*str[0] == '{') {
972 		pos = strchr(*str, '}');
973 		if (pos == NULL) {
974 			Perror(space, "missing closing brace for format");
975 			return NULL;
976 		}
977 		pos[0] = '\0';
978 		attr = *str+1;
979 		*str = pos+1;
980 		dbg("attribute='%s', str='%s'", attr, *str);
981 	}
982 	return attr;
983 }
984 
985 /* extract possible format length and move str behind it*/
get_format_len(struct space *space, char **str)986 static int get_format_len(struct space *space, char **str)
987 {
988 	int num;
989 	char *tail;
990 
991 	if (isdigit(*str[0])) {
992 		num = (int) strtoul(*str, &tail, 10);
993 		if (num > 0) {
994 			*str = tail;
995 			dbg("format length=%i", num);
996 			return num;
997 		} else {
998 			Perror(space, "format parsing error '%s'", *str);
999 		}
1000 	}
1001 	return -1;
1002 }
1003 
apply_format(struct space *space, char *string, size_t maxsize)1004 static void apply_format(struct space *space, char *string, size_t maxsize)
1005 {
1006 	char temp[PATH_SIZE];
1007 	char temp2[PATH_SIZE];
1008 	char *head, *tail, *pos, *cpos, *attr, *rest;
1009 	struct pair *pair;
1010 	int len;
1011 	int i;
1012 	int count;
1013 	enum subst_type {
1014 		SUBST_UNKNOWN,
1015 		SUBST_CARDINFO,
1016 		SUBST_CTL,
1017 		SUBST_RESULT,
1018 		SUBST_ATTR,
1019 		SUBST_SYSFSROOT,
1020 		SUBST_ENV,
1021 		SUBST_CONFIG,
1022 	};
1023 	static const struct subst_map {
1024 		char *name;
1025 		char fmt;
1026 		enum subst_type type;
1027 	} map[] = {
1028 		{ .name = "cardinfo",	.fmt = 'i',	.type = SUBST_CARDINFO },
1029 		{ .name = "ctl",	.fmt = 'C',	.type = SUBST_CTL },
1030 		{ .name = "result",	.fmt = 'c',	.type = SUBST_RESULT },
1031 		{ .name = "attr",	.fmt = 's',	.type = SUBST_ATTR },
1032 		{ .name = "sysfsroot",	.fmt = 'r',	.type = SUBST_SYSFSROOT },
1033 		{ .name = "env",	.fmt = 'E',	.type = SUBST_ENV },
1034 		{ .name = "config",	.fmt = 'g',	.type = SUBST_CONFIG },
1035 		{ NULL, '\0', 0 }
1036 	};
1037 	enum subst_type type;
1038 	const struct subst_map *subst;
1039 
1040 	head = string;
1041 	while (1) {
1042 		len = -1;
1043 		while (head[0] != '\0') {
1044 			if (head[0] == '$') {
1045 				/* substitute named variable */
1046 				if (head[1] == '\0')
1047 					break;
1048 				if (head[1] == '$') {
1049 					strlcpy(temp, head+2, sizeof(temp));
1050 					strlcpy(head+1, temp, maxsize);
1051 					head++;
1052 					continue;
1053 				}
1054 				head[0] = '\0';
1055 				for (subst = map; subst->name; subst++) {
1056 					if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) {
1057 						type = subst->type;
1058 						tail = head + strlen(subst->name)+1;
1059 						dbg("will substitute format name '%s'", subst->name);
1060 						goto found;
1061 					}
1062 				}
1063 			} else if (head[0] == '%') {
1064 				/* substitute format char */
1065 				if (head[1] == '\0')
1066 					break;
1067 				if (head[1] == '%') {
1068 					strlcpy(temp, head+2, sizeof(temp));
1069 					strlcpy(head+1, temp, maxsize);
1070 					head++;
1071 					continue;
1072 				}
1073 				head[0] = '\0';
1074 				tail = head+1;
1075 				len = get_format_len(space, &tail);
1076 				for (subst = map; subst->name; subst++) {
1077 					if (tail[0] == subst->fmt) {
1078 						type = subst->type;
1079 						tail++;
1080 						dbg("will substitute format char '%c'", subst->fmt);
1081 						goto found;
1082 					}
1083 				}
1084 			}
1085 			head++;
1086 		}
1087 		break;
1088 found:
1089 		attr = get_format_attribute(space, &tail);
1090 		strlcpy(temp, tail, sizeof(temp));
1091 		dbg("format=%i, string='%s', tail='%s'", type ,string, tail);
1092 
1093 		switch (type) {
1094 		case SUBST_CARDINFO:
1095 			if (attr == NULL)
1096 				Perror(space, "missing identification parametr for cardinfo");
1097 			else {
1098 				const char *value = cardinfo_get(space, attr);
1099 				if (value == NULL)
1100 					break;
1101 				strlcat(string, value, maxsize);
1102 				dbg("substitute cardinfo{%s} '%s'", attr, value);
1103 			}
1104 			break;
1105 		case SUBST_CTL:
1106 			if (attr == NULL)
1107 				Perror(space, "missing identification parametr for ctl");
1108 			else {
1109 				const char *value = elemid_get(space, attr);
1110 				if (value == NULL)
1111 					break;
1112 				strlcat(string, value, maxsize);
1113 				dbg("substitute ctl{%s} '%s'", attr, value);
1114 			}
1115 			break;
1116 		case SUBST_RESULT:
1117 			if (space->program_result == NULL)
1118 				break;
1119 			/* get part part of the result string */
1120 			i = 0;
1121 			if (attr != NULL)
1122 				i = strtoul(attr, &rest, 10);
1123 			if (i > 0) {
1124 				dbg("request part #%d of result string", i);
1125 				cpos = space->program_result;
1126 				while (--i) {
1127 					while (cpos[0] != '\0' && !isspace(cpos[0]))
1128 						cpos++;
1129 					while (isspace(cpos[0]))
1130 						cpos++;
1131 				}
1132 				if (i > 0) {
1133 					Perror(space, "requested part of result string not found");
1134 					break;
1135 				}
1136 				strlcpy(temp2, cpos, sizeof(temp2));
1137 				/* %{2+}c copies the whole string from the second part on */
1138 				if (rest[0] != '+') {
1139 					cpos = strchr(temp2, ' ');
1140 					if (cpos)
1141 						cpos[0] = '\0';
1142 				}
1143 				strlcat(string, temp2, maxsize);
1144 				dbg("substitute part of result string '%s'", temp2);
1145 			} else {
1146 				strlcat(string, space->program_result, maxsize);
1147 				dbg("substitute result string '%s'", space->program_result);
1148 			}
1149 			break;
1150 		case SUBST_ATTR:
1151 			if (attr == NULL)
1152 				Perror(space, "missing file parameter for attr");
1153 			else {
1154 				const char *value = NULL;
1155 				size_t size;
1156 
1157 				pair = value_find(space, "sysfs_device");
1158 				if (pair == NULL)
1159 					break;
1160 				value = sysfs_attr_get_value(pair->value, attr);
1161 
1162 				if (value == NULL)
1163 					break;
1164 
1165 				/* strip trailing whitespace and replace untrusted characters of sysfs value */
1166 				size = strlcpy(temp2, value, sizeof(temp2));
1167 				if (size >= sizeof(temp2))
1168 					size = sizeof(temp2)-1;
1169 				while (size > 0 && isspace(temp2[size-1]))
1170 					temp2[--size] = '\0';
1171 				count = replace_untrusted_chars(temp2);
1172 				if (count > 0)
1173 					Perror(space, "%i untrusted character(s) replaced" , count);
1174 				strlcat(string, temp2, maxsize);
1175 				dbg("substitute sysfs value '%s'", temp2);
1176 			}
1177 			break;
1178 		case SUBST_SYSFSROOT:
1179 			strlcat(string, sysfs_path, maxsize);
1180 			dbg("substitute sysfs_path '%s'", sysfs_path);
1181 			break;
1182 		case SUBST_ENV:
1183 			if (attr == NULL) {
1184 				dbg("missing attribute");
1185 				break;
1186 			}
1187 			pos = getenv(attr);
1188 			if (pos == NULL) {
1189 				dbg("env '%s' not available", attr);
1190 				break;
1191 			}
1192 			dbg("substitute env '%s=%s'", attr, pos);
1193 			strlcat(string, pos, maxsize);
1194 			break;
1195 		case SUBST_CONFIG:
1196 			if (attr == NULL) {
1197 				dbg("missing attribute");
1198 				break;
1199 			}
1200 			pair = value_find(space, attr);
1201 			if (pair == NULL)
1202 				break;
1203 			strlcat(string, pair->value, maxsize);
1204 			break;
1205 		default:
1206 			Perror(space, "unknown substitution type=%i", type);
1207 			break;
1208 		}
1209 		/* possibly truncate to format-char specified length */
1210 		if (len != -1) {
1211 			head[len] = '\0';
1212 			dbg("truncate to %i chars, subtitution string becomes '%s'", len, head);
1213 		}
1214 		strlcat(string, temp, maxsize);
1215 	}
1216 	/* unescape strings */
1217 	head = tail = string;
1218 	while (*head != '\0') {
1219 		if (*head == '\\') {
1220 			head++;
1221 			if (*head == '\0')
1222 				break;
1223 			switch (*head) {
1224 			case 'a': *tail++ = '\a'; break;
1225 			case 'b': *tail++ = '\b'; break;
1226 			case 'n': *tail++ = '\n'; break;
1227 			case 'r': *tail++ = '\r'; break;
1228 			case 't': *tail++ = '\t'; break;
1229 			case 'v': *tail++ = '\v'; break;
1230 			case '\\': *tail++ = '\\'; break;
1231 			default: *tail++ = *head; break;
1232 			}
1233 			head++;
1234 			continue;
1235 		}
1236 		if (*head)
1237 			*tail++ = *head++;
1238 	}
1239 	*tail = 0;
1240 }
1241 
1242 static
run_program1(struct space *space, const char *command0, char *result, size_t ressize, size_t *reslen ATTRIBUTE_UNUSED, int log ATTRIBUTE_UNUSED)1243 int run_program1(struct space *space,
1244 		 const char *command0, char *result,
1245 		 size_t ressize, size_t *reslen ATTRIBUTE_UNUSED,
1246 		 int log ATTRIBUTE_UNUSED)
1247 {
1248 	if (strncmp(command0, "__ctl_search", 12) == 0) {
1249 		const char *res = elemid_get(space, "do_search");
1250 		if (res == NULL || strcmp(res, "1") != 0)
1251 			return EXIT_FAILURE;
1252 		return EXIT_SUCCESS;
1253 	}
1254 	if (strncmp(command0, "__ctl_count", 11) == 0) {
1255 		const char *res = elemid_get(space, "do_count");
1256 		if (res == NULL || strcmp(res, "0") == 0)
1257 			return EXIT_FAILURE;
1258 		strlcpy(result, res, ressize);
1259 		return EXIT_SUCCESS;
1260 	}
1261 	Perror(space, "unknown buildin command '%s'", command0);
1262 	return EXIT_FAILURE;
1263 }
1264 
1265 static int parse(struct space *space, const char *filename);
1266 
new_root_dir(const char *filename)1267 static char *new_root_dir(const char *filename)
1268 {
1269 	char *res, *tmp;
1270 
1271 	res = strdup(filename);
1272 	if (res) {
1273 		tmp = strrchr(res, '/');
1274 		if (tmp)
1275 			*tmp = '\0';
1276 	}
1277 	dbg("new_root_dir '%s' '%s'", filename, res);
1278 	return res;
1279 }
1280 
1281 /* return non-zero if the file name has the extension ".conf" */
conf_name_filter(const struct dirent *d)1282 static int conf_name_filter(const struct dirent *d)
1283 {
1284 	char *ext = strrchr(d->d_name, '.');
1285 	return ext && !strcmp(ext, ".conf");
1286 }
1287 
parse_line(struct space *space, char *line, size_t linesize ATTRIBUTE_UNUSED)1288 static int parse_line(struct space *space, char *line, size_t linesize ATTRIBUTE_UNUSED)
1289 {
1290 	char *linepos;
1291 	char *key, *value, *attr, *temp;
1292 	struct pair *pair;
1293 	enum key_op op;
1294 	int err = 0, count;
1295 	char string[PATH_SIZE];
1296 	char result[PATH_SIZE];
1297 
1298 	linepos = line;
1299 	while (*linepos != '\0') {
1300 		op = KEY_OP_UNSET;
1301 
1302 		err = get_key(&linepos, &key, &op, &value);
1303 		if (err < 0)
1304 			goto invalid;
1305 
1306 		if (strncasecmp(key, "LABEL", 5) == 0) {
1307 			if (op != KEY_OP_ASSIGN) {
1308 				Perror(space, "invalid LABEL operation");
1309 				goto invalid;
1310 			}
1311 			if (space->go_to && strcmp(space->go_to, value) == 0) {
1312 				free(space->go_to);
1313 				space->go_to = NULL;
1314 			}
1315 			continue;
1316 		}
1317 
1318 		if (space->go_to) {
1319 			dbg("skip (GOTO '%s')", space->go_to);
1320 			break;		/* not for us */
1321 		}
1322 
1323 		if (strncasecmp(key, "CTL{", 4) == 0) {
1324 			attr = get_key_attribute(space, key + 3, string, sizeof(string));
1325 			if (attr == NULL) {
1326 				Perror(space, "error parsing CTL attribute");
1327 				goto invalid;
1328 			}
1329 			if (op == KEY_OP_ASSIGN) {
1330 				strlcpy(result, value, sizeof(result));
1331 				apply_format(space, result, sizeof(result));
1332 				dbg("ctl assign: '%s' '%s'", value, attr);
1333 				err = elemid_set(space, attr, result);
1334 				if (space->program_result) {
1335 					free(space->program_result);
1336 					space->program_result = NULL;
1337 				}
1338 				snprintf(string, sizeof(string), "%i", err);
1339 				space->program_result = strdup(string);
1340 				err = 0;
1341 				if (space->program_result == NULL)
1342 					break;
1343 			} else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1344 				if (strncmp(attr, "write", 5) == 0) {
1345 					strlcpy(result, value, sizeof(result));
1346 					apply_format(space, result, sizeof(result));
1347 					dbg("ctl write: '%s' '%s'", value, attr);
1348 					err = elemid_set(space, "values", result);
1349 					if (err == 0 && op == KEY_OP_NOMATCH)
1350 						break;
1351 					if (err != 0 && op == KEY_OP_MATCH)
1352 						break;
1353 				} else {
1354 					temp = (char *)elemid_get(space, attr);
1355 					dbg("ctl match: '%s' '%s' '%s'", attr, value, temp);
1356 					if (!do_match(key, op, value, temp))
1357 						break;
1358 				}
1359 			} else {
1360 				Perror(space, "invalid CTL{} operation");
1361 				goto invalid;
1362 			}
1363 			continue;
1364 		}
1365 		if (strcasecmp(key, "RESULT") == 0) {
1366 			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1367 				if (!do_match(key, op, value, space->program_result))
1368 					break;
1369 			} else if (op == KEY_OP_ASSIGN) {
1370 				if (space->program_result) {
1371 					free(space->program_result);
1372 					space->program_result = NULL;
1373 				}
1374 				strlcpy(string, value, sizeof(string));
1375 				apply_format(space, string, sizeof(string));
1376 				space->program_result = strdup(string);
1377 				if (space->program_result == NULL)
1378 					break;
1379  			} else {
1380 				Perror(space, "invalid RESULT operation");
1381 				goto invalid;
1382 			}
1383 			continue;
1384 		}
1385 		if (strcasecmp(key, "PROGRAM") == 0) {
1386 			if (op == KEY_OP_UNSET)
1387 				continue;
1388 			strlcpy(string, value, sizeof(string));
1389 			apply_format(space, string, sizeof(string));
1390 			if (space->program_result) {
1391 				free(space->program_result);
1392 				space->program_result = NULL;
1393 			}
1394 			if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) {
1395 				dbg("PROGRAM '%s' is false", string);
1396 				if (op != KEY_OP_NOMATCH)
1397 					break;
1398 			} else {
1399 				remove_trailing_chars(result, '\n');
1400 				count = replace_untrusted_chars(result);
1401 				if (count)
1402 					info("%i untrusted character(s) replaced", count);
1403 				dbg("PROGRAM '%s' result is '%s'", string, result);
1404 				space->program_result = strdup(result);
1405 				if (space->program_result == NULL)
1406 					break;
1407 				dbg("PROGRAM returned successful");
1408 				if (op == KEY_OP_NOMATCH)
1409 					break;
1410 			}
1411 			dbg("PROGRAM key is true");
1412 			continue;
1413 		}
1414 		if (strncasecmp(key, "CARDINFO{", 9) == 0) {
1415 			attr = get_key_attribute(space, key + 8, string, sizeof(string));
1416 			if (attr == NULL) {
1417 				Perror(space, "error parsing CARDINFO attribute");
1418 				goto invalid;
1419 			}
1420 			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1421 				dbg("cardinfo: '%s' '%s'", value, attr);
1422 				temp = (char *)cardinfo_get(space, attr);
1423 				if (!do_match(key, op, value, temp))
1424 					break;
1425 			} else {
1426 				Perror(space, "invalid CARDINFO{} operation");
1427 				goto invalid;
1428 			}
1429 			continue;
1430 		}
1431 		if (strncasecmp(key, "ATTR{", 5) == 0) {
1432 			attr = get_key_attribute(space, key + 4, string, sizeof(string));
1433 			if (attr == NULL) {
1434 				Perror(space, "error parsing ATTR attribute");
1435 				goto invalid;
1436 			}
1437 			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1438 				pair = value_find(space, "sysfs_device");
1439 				if (pair == NULL)
1440 					break;
1441 				dbg("sysfs_attr: '%s' '%s'", pair->value, attr);
1442 				temp = sysfs_attr_get_value(pair->value, attr);
1443 				if (!do_match(key, op, value, temp))
1444 					break;
1445 			} else {
1446 				Perror(space, "invalid ATTR{} operation");
1447 				goto invalid;
1448 			}
1449 			continue;
1450 		}
1451 		if (strncasecmp(key, "ENV{", 4) == 0) {
1452 			attr = get_key_attribute(space, key + 3, string, sizeof(string));
1453 			if (attr == NULL) {
1454 				Perror(space, "error parsing ENV attribute");
1455 				goto invalid;
1456 			}
1457 			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1458 				temp = getenv(attr);
1459 				dbg("env: '%s' '%s'", attr, temp);
1460 				if (!do_match(key, op, value, temp))
1461 					break;
1462 			} else if (op == KEY_OP_ASSIGN ||
1463 				   op == KEY_OP_ASSIGN_FINAL) {
1464 				strlcpy(result, value, sizeof(result));
1465 				apply_format(space, result, sizeof(result));
1466 				dbg("env set: '%s' '%s'", attr, result);
1467 				if (setenv(attr, result, op == KEY_OP_ASSIGN_FINAL))
1468 					break;
1469 			} else {
1470 				Perror(space, "invalid ENV{} operation");
1471 				goto invalid;
1472 			}
1473 			continue;
1474 		}
1475 		if (strcasecmp(key, "GOTO") == 0) {
1476 			if (op != KEY_OP_ASSIGN) {
1477 				Perror(space, "invalid GOTO operation");
1478 				goto invalid;
1479 			}
1480 			space->go_to = strdup(value);
1481 			if (space->go_to == NULL) {
1482 				err = -ENOMEM;
1483 				break;
1484 			}
1485 			continue;
1486 		}
1487 		if (strcasecmp(key, "INCLUDE") == 0) {
1488 			char *rootdir, *go_to;
1489 			const char *filename;
1490 			struct stat st;
1491 			int linenum;
1492 			if (op != KEY_OP_ASSIGN) {
1493 				Perror(space, "invalid INCLUDE operation");
1494 				goto invalid;
1495 			}
1496 			if (value[0] == '/')
1497 				strlcpy(string, value, sizeof(string));
1498 			else {
1499 				strlcpy(string, space->rootdir, sizeof(string));
1500 				strlcat(string, "/", sizeof(string));
1501 				strlcat(string, value, sizeof(string));
1502 			}
1503 			rootdir = space->rootdir;
1504 			go_to = space->go_to;
1505 			filename = space->filename;
1506 			linenum = space->linenum;
1507 			if (stat(string, &st)) {
1508 				Perror(space, "invalid filename '%s'", string);
1509 				continue;
1510 			}
1511 			if (S_ISDIR(st.st_mode)) {
1512 				struct dirent **list;
1513 				int i, num;
1514 				num = scandir(string, &list, conf_name_filter,
1515 					      alphasort);
1516 				if (num < 0) {
1517 					Perror(space, "invalid directory '%s'", string);
1518 					continue;
1519 				}
1520 				count = strlen(string);
1521 				for (i = 0; i < num; i++) {
1522 					string[count] = '\0';
1523 					strlcat(string, "/", sizeof(string));
1524 					strlcat(string, list[i]->d_name, sizeof(string));
1525 					space->go_to = NULL;
1526 					space->rootdir = new_root_dir(string);
1527 					free(list[i]);
1528 					if (space->rootdir) {
1529 						err = parse(space, string);
1530 						free(space->rootdir);
1531 					} else
1532 						err = -ENOMEM;
1533 					if (space->go_to) {
1534 						Perror(space, "unterminated GOTO '%s'", space->go_to);
1535 						free(space->go_to);
1536 					}
1537 					if (err)
1538 						break;
1539 				}
1540 				free(list);
1541 			} else {
1542 				space->go_to = NULL;
1543 				space->rootdir = new_root_dir(string);
1544 				if (space->rootdir) {
1545 					err = parse(space, string);
1546 					free(space->rootdir);
1547 				} else
1548 					err = -ENOMEM;
1549 				if (space->go_to) {
1550 					Perror(space, "unterminated GOTO '%s'", space->go_to);
1551 					free(space->go_to);
1552 				}
1553 			}
1554 			space->go_to = go_to;
1555 			space->rootdir = rootdir;
1556 			space->filename = filename;
1557 			space->linenum = linenum;
1558 			if (space->quit)
1559 				break;
1560 			if (err)
1561 				break;
1562 			continue;
1563 		}
1564 		if (strncasecmp(key, "ACCESS", 6) == 0) {
1565 			if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1566 				if (value[0] == '$') {
1567 					strlcpy(string, value, sizeof(string));
1568 					apply_format(space, string, sizeof(string));
1569 					if (string[0] == '/')
1570 						goto __access1;
1571 				}
1572 				if (value[0] != '/') {
1573 					strlcpy(string, space->rootdir, sizeof(string));
1574 					strlcat(string, "/", sizeof(string));
1575 					strlcat(string, value, sizeof(string));
1576 				} else {
1577 					strlcpy(string, value, sizeof(string));
1578 				}
1579 				apply_format(space, string, sizeof(string));
1580 			      __access1:
1581 				count = access(string, F_OK);
1582 				dbg("access(%s) = %i (%s)", string, count, value);
1583 				if (op == KEY_OP_MATCH && count != 0)
1584 					break;
1585 				if (op == KEY_OP_NOMATCH && count == 0)
1586 					break;
1587 			} else {
1588 				Perror(space, "invalid ACCESS operation");
1589 				goto invalid;
1590 			}
1591 			continue;
1592 		}
1593 		if (strncasecmp(key, "PRINT", 5) == 0) {
1594 			if (op != KEY_OP_ASSIGN) {
1595 				Perror(space, "invalid PRINT operation");
1596 				goto invalid;
1597 			}
1598 			strlcpy(string, value, sizeof(string));
1599 			apply_format(space, string, sizeof(string));
1600 			fwrite(string, strlen(string), 1, stdout);
1601 			continue;
1602 		}
1603 		if (strncasecmp(key, "ERROR", 5) == 0) {
1604 			if (op != KEY_OP_ASSIGN) {
1605 				Perror(space, "invalid ERROR operation");
1606 				goto invalid;
1607 			}
1608 			strlcpy(string, value, sizeof(string));
1609 			apply_format(space, string, sizeof(string));
1610 			fwrite(string, strlen(string), 1, stderr);
1611 			continue;
1612 		}
1613 		if (strncasecmp(key, "EXIT", 4) == 0) {
1614 			if (op != KEY_OP_ASSIGN) {
1615 				Perror(space, "invalid EXIT operation");
1616 				goto invalid;
1617 			}
1618 			strlcpy(string, value, sizeof(string));
1619 			apply_format(space, string, sizeof(string));
1620 			if (strcmp(string, "return") == 0)
1621 				return -EJUSTRETURN;
1622 			space->exit_code = strtol(string, NULL, 0);
1623 			space->quit = 1;
1624 			break;
1625 		}
1626 		if (strncasecmp(key, "CONFIG{", 7) == 0) {
1627 			attr = get_key_attribute(space, key + 6, string, sizeof(string));
1628 			if (attr == NULL) {
1629 				Perror(space, "error parsing CONFIG attribute");
1630 				goto invalid;
1631 			}
1632 			strlcpy(result, value, sizeof(result));
1633 			apply_format(space, result, sizeof(result));
1634 			if (op == KEY_OP_ASSIGN) {
1635 				err = value_set(space, attr, result);
1636 				dbg("CONFIG{%s}='%s'", attr, result);
1637 				break;
1638 			} else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
1639 				pair = value_find(space, attr);
1640 				if (pair == NULL)
1641 					break;
1642 				if (!do_match(key, op, result, pair->value))
1643 					break;
1644 			} else {
1645 				Perror(space, "invalid CONFIG{} operation");
1646 				goto invalid;
1647 			}
1648 		}
1649 
1650 		Perror(space, "unknown key '%s'", key);
1651 	}
1652 	return err;
1653 
1654 invalid:
1655 	Perror(space, "invalid rule");
1656 	return -EINVAL;
1657 }
1658 
parse(struct space *space, const char *filename)1659 static int parse(struct space *space, const char *filename)
1660 {
1661 	char *buf, *bufline, *line;
1662 	size_t bufsize, pos, count, linesize;
1663 	unsigned int linenum, i, j, linenum_adj;
1664 	int err;
1665 
1666 	dbg("start of file '%s'", filename);
1667 
1668 	if (file_map(filename, &buf, &bufsize) != 0) {
1669 		err = errno;
1670 		error("Unable to open file '%s': %s", filename, strerror(err));
1671 		return -err;
1672 	}
1673 
1674 	err = 0;
1675 	pos = 0;
1676 	linenum = 0;
1677 	linesize = 128;
1678 	line = malloc(linesize);
1679 	if (line == NULL) {
1680 		file_unmap(buf, bufsize);
1681 		return -ENOMEM;
1682 	}
1683 	space->filename = filename;
1684 	while (!err && pos < bufsize && !space->quit) {
1685 		count = line_width(buf, bufsize, pos);
1686 		bufline = buf + pos;
1687 		pos += count + 1;
1688 		linenum++;
1689 
1690 		/* skip whitespaces */
1691 		while (count > 0 && isspace(bufline[0])) {
1692 			bufline++;
1693 			count--;
1694 		}
1695 		if (count == 0)
1696 			continue;
1697 
1698 		/* comment check */
1699 		if (bufline[0] == '#')
1700 			continue;
1701 
1702 		if (count > linesize - 1) {
1703 			free(line);
1704 			line = NULL;
1705 			linesize = (count + 127 + 1) & ~127;
1706 			if (linesize > 2048) {
1707 				error("file %s, line %i too long", filename, linenum);
1708 				err = -EINVAL;
1709 				break;
1710 			}
1711 			line = malloc(linesize);
1712 			if (line == NULL) {
1713 				err = -EINVAL;
1714 				break;
1715 			}
1716 		}
1717 
1718 		/* skip backslash and newline from multiline rules */
1719 		linenum_adj = 0;
1720 		for (i = j = 0; i < count; i++) {
1721 			if (bufline[i] == '\\' && bufline[i+1] == '\n') {
1722 				linenum_adj++;
1723 				continue;
1724 			}
1725 			line[j++] = bufline[i];
1726 		}
1727 		line[j] = '\0';
1728 
1729 		dbg("read (%i) '%s'", linenum, line);
1730 		space->linenum = linenum;
1731 		err = parse_line(space, line, linesize);
1732 		if (err == -EJUSTRETURN) {
1733 			err = 0;
1734 			break;
1735 		}
1736 		linenum += linenum_adj;
1737 	}
1738 
1739 	free(line);
1740 	space->filename = NULL;
1741 	space->linenum = -1;
1742 	file_unmap(buf, bufsize);
1743 	dbg("end of file '%s'", filename);
1744 	return err ? err : -abs(space->exit_code);
1745 }
1746 
init(const char *cfgdir, const char *filename, int flags, const char *cardname)1747 int init(const char *cfgdir, const char *filename, int flags, const char *cardname)
1748 {
1749 	struct space *space;
1750 	struct snd_card_iterator iter;
1751 	int err = 0, lasterr = 0;
1752 
1753 	sysfs_init();
1754 	err = snd_card_iterator_sinit(&iter, cardname);
1755 	if (err < 0)
1756 		goto out;
1757 	while (snd_card_iterator_next(&iter)) {
1758 		err = snd_card_clean_cfgdir(cfgdir, iter.card);
1759 		if (err < 0) {
1760 			if (lasterr == 0)
1761 				lasterr = err;
1762 			continue;
1763 		}
1764 		err = init_ucm(flags, iter.card);
1765 		if (err == 0)
1766 			continue;
1767 		err = init_space(&space, iter.card);
1768 		if (err != 0)
1769 			continue;
1770 		space->rootdir = new_root_dir(filename);
1771 		if (space->rootdir != NULL) {
1772 			err = parse(space, filename);
1773 			if (!cardname && err <= -99) { /* non-fatal errors */
1774 				if (lasterr == 0)
1775 					lasterr = err;
1776 				err = 0;
1777 			}
1778 		}
1779 		free_space(space);
1780 		if (err < 0)
1781 			goto out;
1782 	}
1783 	err = lasterr ? lasterr : snd_card_iterator_error(&iter);
1784 out:
1785 	sysfs_cleanup();
1786 	return err;
1787 }
1788