1 /* 2 * Copyright (c) 2016 Cyril Hrubis <chrubis (at) 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 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 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 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 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