1 /* 2 * Copyright (c) International Business Machines Corp., 2001 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the 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 to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 * 18 */ 19 20 /* 21 * File: ltpapicmd.c 22 * 23 * Description: This program impliments a command line version of some of the 24 * LTP harness API's. This will enable tests written in shell and 25 * other scripts to report problems and log results in the LTP 26 * harness format. The intent is to have a common format in which 27 * the C tests and tests written in scripts report results in 28 * a common format. 29 * 30 * The following LTP API's are available currently in command line 31 * form: 32 * tst_brk - Print result message and break remaining test cases 33 * tst_brkm - Print result message, including file contents, and 34 * break remaining test cases 35 * tst_res - Print result message, including file contents 36 * tst_resm - Print result message 37 * tst_flush - Print any messages pending because of CONDENSE mode, 38 * and flush output stream 39 * tst_exit - Exit test with a meaningful exit value 40 * 41 * These are the minimum set of functions or commands required to 42 * report results. 43 * 44 * Exit: All commands exit with 45 * 0 - on success 46 * -1 - on failure 47 * 48 * Description: Unlike the above commands tst_kvercmp, tst_kvercmp2 have an unusual 49 * exit status 50 * tst_kvercmp - Compare running kernel to specified version 51 * tst_kvercmp2 - Compare running kernel to specified vanilla version 52 * or distribution specific version 53 * Exit: 54 * 2 - running newer kernel 55 * 1 - running same age kernel 56 * 0 - running older kernel 57 * -1 - on failure 58 * History 59 * Dec 10 2002 - Created - Manoj Iyer manjo (at) mail.utexas.edu 60 * Dec 12 2002 - Modified - Code that checked if the environment variables 61 * TCID and TST_TOTAL were set did not print usage message. 62 * Modified code to print usage message in each case. 63 * Dec 16 2002 - Modified - Code to get the test number, gets environment 64 * variable TST_COUNT and initializes tst_count. 65 * Dec 16 2002 - Documentation and comment changes. 66 * Feb 11 2003 - tst_count was set to -1 during init or setup in the script. 67 * this was causing tst_resm to issue a warning message. 68 * This bug is now fixed. 69 * 70 */ 71 72 #include <sys/socket.h> 73 #include <stdio.h> 74 #include <string.h> 75 #include <stdlib.h> 76 #include <stdint.h> 77 #include "test.h" 78 #include "usctest.h" 79 #include "safe_macros.h" 80 81 char *TCID; /* Name of the testcase */ 82 int TST_TOTAL; /* Total number of testcases */ 83 84 static char cmd_name[1024]; /* name by which this program is invoked tst_brk etc */ 85 static char *tst_total; /* total number of tests in the file. */ 86 static char *tst_cntstr; /* sets the value of tst_count with this value */ 87 88 89 /* 90 * Function: ident_ttype - Return test result type. 91 * 92 * Description: This function will return the test result type, it actually 93 * the string that is entered by the user to an integer value that 94 * is understood by the API's. 95 * 96 * Return: test type TPASS, TFAIL, TBROK, TCONF, TWARN, or TINFO 97 * on success 98 * -1 on failure 99 */ 100 int ident_ttype(char *tstype) 101 { 102 /* test result type one of TPASS, TFAIL, etc */ 103 if (strcmp(tstype, "TBROK") == 0) 104 return TBROK; 105 else if (strcmp(tstype, "TFAIL") == 0) 106 return TFAIL; 107 else if (strcmp(tstype, "TPASS") == 0) 108 return TPASS; 109 else if (strcmp(tstype, "TCONF") == 0) 110 return TCONF; 111 else if (strcmp(tstype, "TWARN") == 0) 112 return TWARN; 113 else if (strcmp(tstype, "TINFO") == 0) 114 return TINFO; 115 else 116 return -1; 117 } 118 119 void tst_cat_file(const char *filename) 120 { 121 const char *cmd[] = {"cat", filename, NULL}; 122 123 tst_run_cmd(NULL, cmd, NULL, NULL, 0); 124 } 125 126 void apicmd_brk(int argc, char *argv[]) 127 { 128 int trestype; 129 char *file_name; 130 131 if (argc < 5) { 132 fprintf(stderr, "Usage: %s TTYPE FNAME FUNC STRING\n" 133 "\tTTYPE - Test Result Type; one of TFAIL, TBROK " 134 "and TCONF.\n" 135 "\tFNAME - Print contents of this file after the message\n" 136 "\tFUNC - Cleanup function (ignored), but MUST be provided\n" 137 "\tSTRING - Message explaining the test result\n", 138 cmd_name); 139 exit(1); 140 } 141 trestype = ident_ttype((argv++)[0]); 142 file_name = (argv++)[0]; 143 tst_cat_file(file_name); 144 argv++; 145 tst_brkm(trestype, NULL, "%s", *argv); 146 147 } 148 149 void apicmd_res(int argc, char *argv[]) 150 { 151 int trestype; 152 char *file_name; 153 154 if (argc < 4) { 155 fprintf(stderr, "Usage: %s TTYPE FNAME STRING\n" 156 "\tTTYPE - Test Result Type; one of TFAIL, TBROK " 157 "and TCONF.\n" 158 "\tFNAME - Print contents of this file after the message\n" 159 "\tSTRING - Message explaining the test result\n", 160 cmd_name); 161 exit(1); 162 } 163 trestype = ident_ttype((argv++)[0]); 164 file_name = (argv++)[0]; 165 tst_cat_file(file_name); 166 tst_resm(trestype, "%s", *argv); 167 } 168 169 void apicmd_brkm(int argc, char *argv[]) 170 { 171 int trestype; 172 173 if (argc < 4) { 174 fprintf(stderr, "Usage: %s TTYPE FUNC STRING\n" 175 "\tTTYPE - Test Result Type; one of TFAIL, TBROK " 176 "and TCONF.\n" 177 "\tFUNC - Cleanup function (ignored), but MUST be provided\n" 178 "\tSTRING - Message explaining the test result\n", 179 cmd_name); 180 exit(1); 181 } 182 trestype = ident_ttype((argv++)[0]); 183 argv++; 184 tst_brkm(trestype, NULL, "%s", *argv); 185 } 186 187 void apicmd_resm(int argc, char *argv[]) 188 { 189 int trestype; 190 191 if (argc < 3) { 192 fprintf(stderr, "Usage: %s TTYPE STRING\n" 193 "\tTTYPE - Test Result Type; one of TFAIL, TBROK" 194 "and TCONF.\n" 195 "\tSTRING - Message explaining the test result\n", 196 cmd_name); 197 exit(1); 198 } 199 trestype = ident_ttype((argv++)[0]); 200 tst_resm(trestype, "%s", *argv); 201 } 202 203 void apicmd_kvercmp(int argc, char *argv[]) 204 { 205 int exit_value; 206 207 if (argc < 4) { 208 fprintf(stderr, "Usage: %s NUM NUM NUM\n" 209 "Compares to the running kernel version.\n\n" 210 "\tNUM - A positive integer.\n" 211 "\tThe first NUM is the kernel VERSION\n" 212 "\tThe second NUM is the kernel PATCHLEVEL\n" 213 "\tThe third NUM is the kernel SUBLEVEL\n\n" 214 "\tExit status is 0 if the running kernel is older than the\n" 215 "\t\tkernel specified by NUM NUM NUM.\n" 216 "\tExit status is 1 for kernels of the same age.\n" 217 "\tExit status is 2 if the running kernel is newer.\n", 218 cmd_name); 219 exit(1); 220 } 221 exit_value = tst_kvercmp(atoi(argv[0]), atoi(argv[1]), 222 atoi(argv[2])); 223 if (exit_value < 0) 224 exit_value = 0; 225 else if (exit_value == 0) 226 exit_value = 1; 227 else if (exit_value > 0) 228 exit_value = 2; 229 exit(exit_value); 230 } 231 232 void apicmd_kvercmp2(int argc, char *argv[]) 233 { 234 int exit_value; 235 236 struct tst_kern_exv vers[100]; 237 unsigned int count; 238 239 char *saveptr1 = NULL; 240 char *saveptr2 = NULL; 241 char *token1; 242 243 if (TCID == NULL) 244 TCID = "outoftest"; 245 if (tst_cntstr == NULL) 246 tst_count = 0; 247 248 if (argc < 5) { 249 fprintf(stderr, "Usage: %s NUM NUM NUM KVERS\n" 250 "Compares to the running kernel version\n" 251 "based on vanilla kernel version NUM NUM NUM\n" 252 "or distribution specific kernel version KVERS\n\n" 253 "\tNUM - A positive integer.\n" 254 "\tThe first NUM is the kernel VERSION\n" 255 "\tThe second NUM is the kernel PATCHLEVEL\n" 256 "\tThe third NUM is the kernel SUBLEVEL\n\n" 257 "\tKVERS is a string of the form " 258 "\"DISTR1:VERS1 DISTR2:VERS2\",\n" 259 "\twhere DISTR1 is a distribution name\n" 260 "\tand VERS1 is the corresponding kernel version.\n" 261 "\tExample: \"RHEL6:2.6.39-400.208\"\n\n" 262 "\tIf running kernel matches a distribution in KVERS then\n" 263 "\tcomparison is performed based on version in KVERS,\n" 264 "\totherwise - based on NUM NUM NUM.\n\n" 265 "\tExit status is 0 if the running kernel is older.\n" 266 "\tExit status is 1 for kernels of the same age.\n" 267 "\tExit status is 2 if the running kernel is newer.\n", 268 cmd_name); 269 exit(3); 270 } 271 272 count = 0; 273 token1 = strtok_r(argv[3], " ", &saveptr1); 274 while ((token1 != NULL) && (count < 99)) { 275 vers[count].dist_name = strtok_r(token1, ":", &saveptr2); 276 vers[count].extra_ver = strtok_r(NULL, ":", &saveptr2); 277 278 if (vers[count].extra_ver == NULL) { 279 fprintf(stderr, "Incorrect KVERS format\n"); 280 exit(3); 281 } 282 283 count++; 284 285 token1 = strtok_r(NULL, " ", &saveptr1); 286 } 287 vers[count].dist_name = NULL; 288 vers[count].extra_ver = NULL; 289 290 exit_value = tst_kvercmp2(atoi(argv[0]), atoi(argv[1]), 291 atoi(argv[2]), vers); 292 293 if (exit_value < 0) 294 exit_value = 0; 295 else if (exit_value == 0) 296 exit_value = 1; 297 else if (exit_value > 0) 298 exit_value = 2; 299 exit(exit_value); 300 } 301 302 struct param_pair { 303 char *cmd; 304 int value; 305 }; 306 307 unsigned short apicmd_get_unused_port(int argc, char *argv[]) 308 { 309 if (argc != 3) 310 goto err; 311 312 const struct param_pair params[][3] = { 313 {{"ipv4", AF_INET}, {"ipv6", AF_INET6}, {NULL, 0}}, 314 {{"stream", SOCK_STREAM}, {"dgram", SOCK_DGRAM}, {NULL, 0}} 315 }; 316 317 int i; 318 const struct param_pair *p[2]; 319 for (i = 0; i < 2; ++i) { 320 for (p[i] = params[i]; p[i]->cmd; ++p[i]) { 321 if (!strcmp(p[i]->cmd, argv[i])) 322 break; 323 } 324 if (!p[i]->cmd) 325 goto err; 326 } 327 return tst_get_unused_port(NULL, p[0]->value, p[1]->value); 328 329 err: 330 fprintf(stderr, "Usage: tst_get_unused_port FAMILY TYPE\n" 331 "where FAMILY := { ipv4 | ipv6 }\n" 332 " TYPE := { stream | dgram }\n"); 333 exit(1); 334 } 335 336 int apicmd_fs_has_free(int argc, char *argv[]) 337 { 338 if (argc != 3) { 339 fprintf(stderr, "Usage: tst_fs_has_free path required_bytes\n" 340 "path: the pathname of the mounted filesystem\n" 341 "required_bytes: the required free space" 342 " (supports kB, MB and GB suffixes)\n"); 343 exit(2); 344 } 345 346 char *endptr; 347 unsigned int required_kib = strtoull(argv[1], &endptr, 0); 348 unsigned int mul = TST_BYTES; 349 350 if (*argv[1] == '\0') 351 goto fs_has_free_err; 352 353 if (*endptr != '\0') { 354 if (!strcasecmp(endptr, "kB")) { 355 mul = TST_KB; 356 } else if (!strcasecmp(endptr, "MB")) { 357 mul = TST_MB; 358 } else if (!strcasecmp(endptr, "GB")) { 359 mul = TST_GB; 360 } else { 361 goto fs_has_free_err; 362 } 363 } 364 365 exit(!tst_fs_has_free(NULL, argv[0], required_kib, mul)); 366 367 fs_has_free_err: 368 fprintf(stderr, "%s is not a valid size\n", argv[1]); 369 exit(2); 370 } 371 372 /* 373 * Function: main - entry point of this program 374 * 375 * Description: Parses the arguments to each command. Most commands have in 376 * common atlest 2 arguments, type of test result, which is one of 377 * TPASS, TFAIL, TBROK, TCONF, etc, and a message that describes 378 * the result. Other arguments are a file, the contents of which 379 * are printed after the type of test result and associated message 380 * is printed, also a cleanup function that will be executed. 381 * Currently this function name is ignored but MUST be provided 382 * for compatability reasons. 383 * 384 * The different commands are actually a hard link to this program 385 * the program invokes the appropriate function based on the 386 * command name with which it was invoked. 387 * 388 * Set the values for TCID to the name of the test case. 389 * set the value for TST_TOTAL for total number of tests this is 390 * required in case one test breaks and all following tests also 391 * should be reported as broken. 392 * Set tst_count before every individual test. 393 * 394 * Exit: 0 on success 395 * -1 on failure 396 */ 397 int main(int argc, char *argv[]) 398 { 399 strcpy(cmd_name, SAFE_BASENAME(NULL, (argv++)[0])); 400 401 TCID = getenv("TCID"); 402 tst_total = getenv("TST_TOTAL"); 403 tst_cntstr = getenv("TST_COUNT"); 404 if (TCID == NULL || tst_total == NULL || tst_cntstr == NULL) { 405 if (!strcmp(cmd_name, "tst_kvercmp") && 406 !strcmp(cmd_name, "tst_kvercmp2") && 407 !strcmp(cmd_name, "tst_fs_has_free") && 408 !strcmp(cmd_name, "tst_get_unused_port")) { 409 fprintf(stderr, 410 "\nSet variables TCID, TST_TOTAL, and TST_COUNT before each test:\n" 411 "export TCID=<test name>\n" 412 "export TST_TOTAL=<Total Number of Tests >\n" 413 "export TST_COUNT=<Test case number>\n\n"); 414 /* Make sure the user knows there's an error. */ 415 abort(); 416 } 417 } else { 418 TST_TOTAL = atoi(tst_total); 419 tst_count = atoi(tst_cntstr); 420 if (tst_count > 0) 421 tst_count--; 422 423 if (strcmp(TCID, " ") == 0) { 424 fprintf(stderr, 425 "Variable TCID not set, use: TCID=<test name>\n"); 426 exit(1); 427 } 428 if (TST_TOTAL <= 0) { 429 fprintf(stderr, 430 "Variable TST_TOTAL is set to 0, must be " 431 "greater than zero\n"); 432 exit(1); 433 } 434 } 435 436 if (strcmp(cmd_name, "tst_brk") == 0) { 437 apicmd_brk(argc, argv); 438 } else if (strcmp(cmd_name, "tst_res") == 0) { 439 apicmd_res(argc, argv); 440 } else if (strcmp(cmd_name, "tst_brkm") == 0) { 441 apicmd_brkm(argc, argv); 442 } else if (strcmp(cmd_name, "tst_resm") == 0) { 443 apicmd_resm(argc, argv); 444 } else if (strcmp(cmd_name, "tst_exit") == 0) { 445 tst_exit(); 446 } else if (strcmp(cmd_name, "tst_flush") == 0) { 447 tst_flush(); 448 } else if (strcmp(cmd_name, "tst_kvercmp") == 0) { 449 apicmd_kvercmp(argc, argv); 450 } else if (strcmp(cmd_name, "tst_kvercmp2") == 0) { 451 apicmd_kvercmp2(argc, argv); 452 } else if (strcmp(cmd_name, "tst_ncpus") == 0) { 453 printf("%li\n", tst_ncpus()); 454 } else if (strcmp(cmd_name, "tst_ncpus_conf") == 0) { 455 printf("%li\n", tst_ncpus_conf()); 456 } else if (strcmp(cmd_name, "tst_ncpus_max") == 0) { 457 printf("%li\n", tst_ncpus_max()); 458 } else if (strcmp(cmd_name, "tst_get_unused_port") == 0) { 459 printf("%u\n", apicmd_get_unused_port(argc, argv)); 460 } else if (strcmp(cmd_name, "tst_fs_has_free") == 0) { 461 apicmd_fs_has_free(argc, argv); 462 } 463 464 exit(0); 465 } 466