• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2018 Cyril Hrubis <chrubis@suse.cz>
4  */
5 
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <sys/utsname.h>
11 
12 #define TST_NO_DEFAULT_MAIN
13 #include "tst_test.h"
14 #include "tst_kconfig.h"
15 
kconfig_path(char * path_buf,size_t path_buf_len)16 static const char *kconfig_path(char *path_buf, size_t path_buf_len)
17 {
18 	const char *path = getenv("KCONFIG_PATH");
19 	struct utsname un;
20 
21 	if (path) {
22 		if (!access(path, F_OK))
23 			return path;
24 
25 		tst_res(TWARN, "KCONFIG_PATH='%s' does not exist", path);
26 	}
27 
28 	if (!access("/proc/config.gz", F_OK))
29 		return "/proc/config.gz";
30 
31 	uname(&un);
32 
33 	/* Debian and derivatives */
34 	snprintf(path_buf, path_buf_len, "/boot/config-%s", un.release);
35 
36 	if (!access(path_buf, F_OK))
37 		return path_buf;
38 
39 	/* Clear Linux */
40 	snprintf(path_buf, path_buf_len, "/lib/kernel/config-%s", un.release);
41 
42 	if (!access(path_buf, F_OK))
43 		return path_buf;
44 
45 	tst_res(TINFO, "Couldn't locate kernel config!");
46 
47 	return NULL;
48 }
49 
50 static char is_gzip;
51 
open_kconfig(void)52 static FILE *open_kconfig(void)
53 {
54 	FILE *fp;
55 	char buf[1024];
56 	char path_buf[1024];
57 	const char *path = kconfig_path(path_buf, sizeof(path_buf));
58 
59 	if (!path)
60 		return NULL;
61 
62 	tst_res(TINFO, "Parsing kernel config '%s'", path);
63 
64 	is_gzip = !!strstr(path, ".gz");
65 
66 	if (is_gzip) {
67 		snprintf(buf, sizeof(buf), "zcat '%s'", path);
68 		fp = popen(buf, "r");
69 	} else {
70 		fp = fopen(path, "r");
71 	}
72 
73 	if (!fp)
74 		tst_brk(TBROK | TERRNO, "Failed to open '%s'", path);
75 
76 	return fp;
77 }
78 
close_kconfig(FILE * fp)79 static void close_kconfig(FILE *fp)
80 {
81 	if (is_gzip)
82 		pclose(fp);
83 	else
84 		fclose(fp);
85 }
86 
87 struct match {
88 	/* match len, string length up to \0 or = */
89 	size_t len;
90 	/* if set part of conf string after = */
91 	const char *val;
92 	/* if set the config option was matched already */
93 	int match;
94 };
95 
is_set(const char * str,const char * val)96 static int is_set(const char *str, const char *val)
97 {
98 	size_t vlen = strlen(val);
99 
100 	while (isspace(*str))
101 		str++;
102 
103 	if (strncmp(str, val, vlen))
104 		return 0;
105 
106 	switch (str[vlen]) {
107 	case ' ':
108 	case '\n':
109 	case '\0':
110 		return 1;
111 	break;
112 	default:
113 		return 0;
114 	}
115 }
116 
match(struct match * match,const char * conf,struct tst_kconfig_res * result,const char * line)117 static inline int match(struct match *match, const char *conf,
118                         struct tst_kconfig_res *result, const char *line)
119 {
120 	if (match->match)
121 		return 0;
122 
123 	const char *cfg = strstr(line, "CONFIG_");
124 
125 	if (!cfg)
126 		return 0;
127 
128 	if (strncmp(cfg, conf, match->len))
129 		return 0;
130 
131 	const char *val = &cfg[match->len];
132 
133 	switch (cfg[match->len]) {
134 	case '=':
135 		break;
136 	case ' ':
137 		if (is_set(val, "is not set")) {
138 			result->match = 'n';
139 			goto match;
140 		}
141 	/* fall through */
142 	default:
143 		return 0;
144 	}
145 
146 	if (is_set(val, "=y")) {
147 		result->match = 'y';
148 		goto match;
149 	}
150 
151 	if (is_set(val, "=m")) {
152 		result->match = 'm';
153 		goto match;
154 	}
155 
156 	result->match = 'v';
157 	result->value = strndup(val+1, strlen(val)-2);
158 
159 match:
160 	match->match = 1;
161 	return 1;
162 }
163 
tst_kconfig_read(const char * const * kconfigs,struct tst_kconfig_res results[],size_t cnt)164 void tst_kconfig_read(const char *const *kconfigs,
165                       struct tst_kconfig_res results[], size_t cnt)
166 {
167 	struct match matches[cnt];
168 	FILE *fp;
169 	unsigned int i, j;
170 	char buf[1024];
171 
172 	for (i = 0; i < cnt; i++) {
173 		const char *val = strchr(kconfigs[i], '=');
174 
175 		if (strncmp("CONFIG_", kconfigs[i], 7))
176 			tst_brk(TBROK, "Invalid config string '%s'", kconfigs[i]);
177 
178 		matches[i].match = 0;
179 		matches[i].len = strlen(kconfigs[i]);
180 
181 		if (val) {
182 			matches[i].val = val + 1;
183 			matches[i].len -= strlen(val);
184 		}
185 
186 		results[i].match = 0;
187 		results[i].value = NULL;
188 	}
189 
190 	fp = open_kconfig();
191 	if (!fp)
192 		tst_brk(TBROK, "Cannot parse kernel .config");
193 
194 	while (fgets(buf, sizeof(buf), fp)) {
195 		for (i = 0; i < cnt; i++) {
196 			if (match(&matches[i], kconfigs[i], &results[i], buf)) {
197 				for (j = 0; j < cnt; j++) {
198 					if (matches[j].match)
199 						break;
200 				}
201 
202 				if (j == cnt)
203 					goto exit;
204 			}
205 		}
206 
207 	}
208 
209 exit:
210 	close_kconfig(fp);
211 }
212 
array_len(const char * const kconfigs[])213 static size_t array_len(const char *const kconfigs[])
214 {
215 	size_t i = 0;
216 
217 	while (kconfigs[++i]);
218 
219 	return i;
220 }
221 
compare_res(struct tst_kconfig_res * res,const char * kconfig,char match,const char * val)222 static int compare_res(struct tst_kconfig_res *res, const char *kconfig,
223                        char match, const char *val)
224 {
225 	if (res->match != match) {
226 		tst_res(TINFO, "Needs kernel %s, have %c", kconfig, res->match);
227 		return 1;
228 	}
229 
230 	if (match != 'v')
231 		return 0;
232 
233 	if (strcmp(res->value, val)) {
234 		tst_res(TINFO, "Needs kernel %s, have %s", kconfig, res->value);
235 		return 1;
236 	}
237 
238 	return 0;
239 }
240 
tst_kconfig_check(const char * const kconfigs[])241 void tst_kconfig_check(const char *const kconfigs[])
242 {
243 	size_t cnt = array_len(kconfigs);
244 	struct tst_kconfig_res results[cnt];
245 	unsigned int i;
246 	int abort_test = 0;
247 
248 	tst_kconfig_read(kconfigs, results, cnt);
249 
250 	for (i = 0; i < cnt; i++) {
251 		if (results[i].match == 0) {
252 			tst_res(TINFO, "Missing kernel %s", kconfigs[i]);
253 			abort_test = 1;
254 			continue;
255 		}
256 
257 		if (results[i].match == 'n') {
258 			tst_res(TINFO, "Kernel %s is not set", kconfigs[i]);
259 			abort_test = 1;
260 			continue;
261 		}
262 
263 		const char *val = strchr(kconfigs[i], '=');
264 
265 		if (val) {
266 			char match = 'v';
267 			val++;
268 
269 			if (!strcmp(val, "y"))
270 				match = 'y';
271 
272 			if (!strcmp(val, "m"))
273 				match = 'm';
274 
275 			if (compare_res(&results[i], kconfigs[i], match, val))
276 				abort_test = 1;
277 
278 		}
279 
280 		free(results[i].value);
281 	}
282 
283 	if (abort_test)
284 		tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!");
285 }
286