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 <stdbool.h>
29 #include <sys/stat.h>
30 #include <limits.h>
31 
rval_conf_name(snd_use_case_mgr_t * uc_mgr)32 static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr)
33 {
34 	if (uc_mgr->conf_file_name && uc_mgr->conf_file_name[0])
35 		return strdup(uc_mgr->conf_file_name);
36 	return NULL;
37 }
38 
rval_card_id(snd_use_case_mgr_t * uc_mgr)39 static char *rval_card_id(snd_use_case_mgr_t *uc_mgr)
40 {
41 	struct ctl_list *ctl_list;
42 
43 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
44 	if (ctl_list == NULL)
45 		return NULL;
46 	return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
47 }
48 
rval_card_driver(snd_use_case_mgr_t * uc_mgr)49 static char *rval_card_driver(snd_use_case_mgr_t *uc_mgr)
50 {
51 	struct ctl_list *ctl_list;
52 
53 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
54 	if (ctl_list == NULL)
55 		return NULL;
56 	return strdup(snd_ctl_card_info_get_driver(ctl_list->ctl_info));
57 }
58 
rval_card_name(snd_use_case_mgr_t * uc_mgr)59 static char *rval_card_name(snd_use_case_mgr_t *uc_mgr)
60 {
61 	struct ctl_list *ctl_list;
62 
63 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
64 	if (ctl_list == NULL)
65 		return NULL;
66 	return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info));
67 }
68 
rval_card_longname(snd_use_case_mgr_t * uc_mgr)69 static char *rval_card_longname(snd_use_case_mgr_t *uc_mgr)
70 {
71 	struct ctl_list *ctl_list;
72 
73 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
74 	if (ctl_list == NULL)
75 		return NULL;
76 	return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info));
77 }
78 
rval_card_components(snd_use_case_mgr_t * uc_mgr)79 static char *rval_card_components(snd_use_case_mgr_t *uc_mgr)
80 {
81 	struct ctl_list *ctl_list;
82 
83 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
84 	if (ctl_list == NULL)
85 		return NULL;
86 	return strdup(snd_ctl_card_info_get_components(ctl_list->ctl_info));
87 }
88 
rval_env(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)89 static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
90 {
91 	char *e;
92 
93 	e = getenv(id);
94 	if (e)
95 		return strdup(e);
96 	return NULL;
97 }
98 
rval_sysfs(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)99 static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
100 {
101 	char path[PATH_MAX], link[PATH_MAX + 1];
102 	struct stat sb;
103 	ssize_t len;
104 	char *e;
105 	int fd;
106 
107 	e = getenv("SYSFS_PATH");
108 	if (e == NULL)
109 		e = "/sys";
110 	if (id[0] == '/')
111 		id++;
112 	snprintf(path, sizeof(path), "%s/%s", e, id);
113 	if (lstat(path, &sb) != 0)
114 		return NULL;
115 	if (S_ISLNK(sb.st_mode)) {
116 		len = readlink(path, link, sizeof(link) - 1);
117 		if (len <= 0) {
118 			uc_error("sysfs: cannot read link '%s' (%d)", path, errno);
119 			return NULL;
120 		}
121 		link[len] = '\0';
122 		e = strrchr(link, '/');
123 		if (e)
124 			return strdup(e + 1);
125 		return NULL;
126 	}
127 	if (S_ISDIR(sb.st_mode))
128 		return NULL;
129 	if ((sb.st_mode & S_IRUSR) == 0)
130 		return NULL;
131 
132 	fd = open(path, O_RDONLY);
133 	if (fd < 0) {
134 		uc_error("sysfs open failed for '%s' (%d)", path, errno);
135 		return NULL;
136 	}
137 	len = read(fd, path, sizeof(path)-1);
138 	close(fd);
139 	if (len < 0) {
140 		uc_error("sysfs unable to read value '%s' (%d)", path, errno);
141 		return NULL;
142 	}
143 	while (len > 0 && path[len-1] == '\n')
144 		len--;
145 	path[len] = '\0';
146 	return strdup(path);
147 }
148 
149 #define MATCH_VARIABLE(name, id, fcn, empty_ok)				\
150 	if (strncmp((name), (id), sizeof(id) - 1) == 0) { 		\
151 		rval = fcn(uc_mgr);					\
152 		idsize = sizeof(id) - 1;				\
153 		allow_empty = (empty_ok);				\
154 		goto __rval;						\
155 	}
156 
157 #define MATCH_VARIABLE2(name, id, fcn)					\
158 	if (strncmp((name), (id), sizeof(id) - 1) == 0) {		\
159 		idsize = sizeof(id) - 1;				\
160 		tmp = strchr(value + idsize, '}');			\
161 		if (tmp) {						\
162 			rvalsize = tmp - (value + idsize);		\
163 			if (rvalsize > sizeof(v2)) {			\
164 				err = -ENOMEM;				\
165 				goto __error;				\
166 			}						\
167 			strncpy(v2, value + idsize, rvalsize);		\
168 			v2[rvalsize] = '\0';				\
169 			idsize += rvalsize + 1;				\
170 			rval = fcn(uc_mgr, v2);				\
171 			goto __rval;					\
172 		}							\
173 	}
174 
uc_mgr_get_substituted_value(snd_use_case_mgr_t * uc_mgr,char ** _rvalue,const char * value)175 int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr,
176 				 char **_rvalue,
177 				 const char *value)
178 {
179 	size_t size, nsize, idsize, rvalsize, dpos = 0;
180 	const char *tmp;
181 	char *r, *nr, *rval, v2[32];
182 	int err;
183 
184 	if (value == NULL)
185 		return -ENOENT;
186 
187 	size = strlen(value) + 1;
188 	r = malloc(size);
189 	if (r == NULL)
190 		return -ENOMEM;
191 
192 	while (*value) {
193 		if (*value == '$' && *(value+1) == '{') {
194 			bool allow_empty = false;
195 
196 			MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false);
197 			MATCH_VARIABLE(value, "${CardId}", rval_card_id, false);
198 			MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false);
199 			MATCH_VARIABLE(value, "${CardName}", rval_card_name, false);
200 			MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false);
201 			MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true);
202 			MATCH_VARIABLE2(value, "${env:", rval_env);
203 			MATCH_VARIABLE2(value, "${sys:", rval_sysfs);
204 			err = -EINVAL;
205 			tmp = strchr(value, '}');
206 			if (tmp) {
207 				strncpy(r, value, tmp + 1 - value);
208 				r[tmp + 1 - value] = '\0';
209 				uc_error("variable '%s' is not known!", r);
210 			} else {
211 				uc_error("variable reference '%s' is not complete", value);
212 			}
213 			goto __error;
214 __rval:
215 			if (rval == NULL || (!allow_empty && rval[0] == '\0')) {
216 				free(rval);
217 				strncpy(r, value, idsize);
218 				r[idsize] = '\0';
219 				uc_error("variable '%s' is not defined in this context!", r);
220 				err = -EINVAL;
221 				goto __error;
222 			}
223 			value += idsize;
224 			rvalsize = strlen(rval);
225 			nsize = size + rvalsize - idsize;
226 			if (nsize > size) {
227 				nr = realloc(r, nsize);
228 				if (nr == NULL) {
229 					free(rval);
230 					err = -ENOMEM;
231 					goto __error;
232 				}
233 				size = nsize;
234 				r = nr;
235 			}
236 			strcpy(r + dpos, rval);
237 			dpos += rvalsize;
238 			free(rval);
239 		} else {
240 			r[dpos++] = *value;
241 			value++;
242 		}
243 	}
244 	r[dpos] = '\0';
245 
246 	*_rvalue = r;
247 	return 0;
248 
249 __error:
250 	free(r);
251 	return err;
252 }
253