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