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