1 /*
2  * Copyright (c) 2016 Cyril Hrubis <chrubis@suse.cz>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #define TST_NO_DEFAULT_MAIN
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/utsname.h>
23 #include <tst_test.h>
24 
25 enum op {
26 	EQ,
27 	NE,
28 	GE,
29 	GT,
30 	LE,
31 	LT,
32 	AND,
33 	OR,
34 	ERR,
35 };
36 
strtop(const char * op)37 static enum op strtop(const char *op)
38 {
39 	if (!strcmp(op, "-eq"))
40 		return EQ;
41 
42 	if (!strcmp(op, "-ne"))
43 		return NE;
44 
45 	if (!strcmp(op, "-ge"))
46 		return GE;
47 
48 	if (!strcmp(op, "-gt"))
49 		return GT;
50 
51 	if (!strcmp(op, "-le"))
52 		return LE;
53 
54 	if (!strcmp(op, "-lt"))
55 		return LT;
56 
57 	if (!strcmp(op, "-a"))
58 		return AND;
59 
60 	if (!strcmp(op, "-o"))
61 		return OR;
62 
63 	return ERR;
64 }
65 
help(const char * fname)66 static void help(const char *fname)
67 {
68 	printf("usage: %s -eq|-ne|-gt|-ge|-lt|-le kver [-a|-o] ...\n\n", fname);
69 	printf("-eq kver\tReturns true if kernel version is equal\n");
70 	printf("-ne kver\tReturns true if kernel version is not equal\n");
71 	printf("-gt kver\tReturns true if kernel version is greater\n");
72 	printf("-ge kver\tReturns true if kernel version is greater or equal\n");
73 	printf("-lt kver\tReturns true if kernel version is lesser\n");
74 	printf("-le kver\tReturns true if kernel version is lesser or equal\n");
75 	printf("-a  \t\tDoes logical and between two expressions\n");
76 	printf("-o  \t\tDoes logical or between two expressions\n\n");
77 	printf("Kernel version format has either one or two dots:\n\n");
78 	printf("'2.6' or '4.8.1'\n\n");
79 	printf("Kernel version can also be followed by a space separated list\n");
80 	printf("of extra versions prefixed by distribution which when matched\n");
81 	printf("take precedence:\n\n'3.0 RHEL6:2.6.18'\n\n");
82 }
83 
compare_kver(const char * cur_kver,char * kver)84 static int compare_kver(const char *cur_kver, char *kver)
85 {
86 	const char *ver, *exver;
87 	const char *distname = tst_kvcmp_distname(cur_kver);
88 	int v1, v2, v3;
89 
90 	ver = strtok(kver, " ");
91 
92 	while ((exver = strtok(NULL, " "))) {
93 		char *exkver = strchr(exver, ':');
94 
95 		if (!exkver) {
96 			fprintf(stderr, "Invalid extra version '%s'\n", exver);
97 			exit(2);
98 		}
99 
100 		*(exkver++) = '\0';
101 
102 		if (!distname || strcmp(distname, exver))
103 			continue;
104 
105 		return tst_kvexcmp(exkver, cur_kver);
106 	}
107 
108 	if (tst_parse_kver(ver, &v1, &v2, &v3)) {
109 		fprintf(stderr,
110 			"Invalid kernel version '%s'\n",
111 			ver);
112 		return 2;
113 	}
114 
115 	return tst_kvcmp(cur_kver, v1, v2, v3);
116 }
117 
main(int argc,char * argv[])118 int main(int argc, char *argv[])
119 {
120 	int i = 1;
121 	int ret = -1;
122 	enum op prev_op = ERR;
123 	struct utsname buf;
124 
125 	if (argc <= 1 || !strcmp(argv[1], "-h")) {
126 		help(argv[0]);
127 		return 0;
128 	}
129 
130 	uname(&buf);
131 
132 	while (i < argc) {
133 		const char *strop = argv[i++];
134 		char *strkver;
135 		int res;
136 
137 		enum op op = strtop(strop);
138 
139 		switch (op) {
140 		case EQ:
141 		case NE:
142 		case GE:
143 		case GT:
144 		case LE:
145 		case LT:
146 			if (ret != -1 && prev_op == ERR) {
147 				fprintf(stderr, "Expected -a or -o\n");
148 				return 2;
149 			}
150 
151 			if (i >= argc) {
152 				fprintf(stderr,
153 					"Expected kernel version after '%s'\n",
154 					strop);
155 				return 2;
156 			}
157 
158 			strkver = argv[i++];
159 		break;
160 		case AND:
161 		case OR:
162 			if (ret == -1) {
163 				fprintf(stderr,
164 					"The %s must follow expression\n",
165 					strop);
166 				return 2;
167 			}
168 			prev_op = op;
169 			continue;
170 		break;
171 		case ERR:
172 			fprintf(stderr, "Invalid operation %s\n", argv[i]);
173 			return 2;
174 		}
175 
176 		res = compare_kver(buf.release, strkver);
177 
178 		switch (op) {
179 		case EQ:
180 			res = (res == 0);
181 		break;
182 		case NE:
183 			res = (res != 0);
184 		break;
185 		case GE:
186 			res = (res >= 0);
187 		break;
188 		case GT:
189 			res = (res > 0);
190 		break;
191 		case LE:
192 			res = (res <= 0);
193 		break;
194 		case LT:
195 			res = (res < 0);
196 		break;
197 		default:
198 		break;
199 		}
200 
201 		switch (prev_op) {
202 		case ERR:
203 			ret = res;
204 		break;
205 		case AND:
206 			ret = ret && res;
207 			prev_op = ERR;
208 		break;
209 		case OR:
210 			ret = ret || res;
211 			prev_op = ERR;
212 		break;
213 		default:
214 		break;
215 		}
216 	}
217 
218 	if (prev_op != ERR) {
219 		fprintf(stderr, "Useless -a or -o at the end\n");
220 		return 2;
221 	}
222 
223 	return !ret;
224 }
225