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