/* * Copyright (c) International Business Machines Corp., 2001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* * File: ltpapicmd.c * * Description: This program impliments a command line version of some of the * LTP harness API's. This will enable tests written in shell and * other scripts to report problems and log results in the LTP * harness format. The intent is to have a common format in which * the C tests and tests written in scripts report results in * a common format. * * The following LTP API's are available currently in command line * form: * tst_brk - Print result message and break remaining test cases * tst_brkm - Print result message, including file contents, and * break remaining test cases * tst_res - Print result message, including file contents * tst_resm - Print result message * tst_flush - Print any messages pending because of CONDENSE mode, * and flush output stream * tst_exit - Exit test with a meaningful exit value * * These are the minimum set of functions or commands required to * report results. * * Exit: All commands exit with * 0 - on success * -1 - on failure * * Description: Unlike the above commands tst_kvercmp, tst_kvercmp2 have an unusual * exit status * tst_kvercmp - Compare running kernel to specified version * tst_kvercmp2 - Compare running kernel to specified vanilla version * or distribution specific version * Exit: * 2 - running newer kernel * 1 - running same age kernel * 0 - running older kernel * -1 - on failure * History * Dec 10 2002 - Created - Manoj Iyer manjo@mail.utexas.edu * Dec 12 2002 - Modified - Code that checked if the environment variables * TCID and TST_TOTAL were set did not print usage message. * Modified code to print usage message in each case. * Dec 16 2002 - Modified - Code to get the test number, gets environment * variable TST_COUNT and initializes tst_count. * Dec 16 2002 - Documentation and comment changes. * Feb 11 2003 - tst_count was set to -1 during init or setup in the script. * this was causing tst_resm to issue a warning message. * This bug is now fixed. * */ #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include "test.h" #include "usctest.h" #include "safe_macros.h" char *TCID; /* Name of the testcase */ int TST_TOTAL; /* Total number of testcases */ static char cmd_name[1024]; /* name by which this program is invoked tst_brk etc */ static char *tst_total; /* total number of tests in the file. */ static char *tst_cntstr; /* sets the value of tst_count with this value */ /* * Function: ident_ttype - Return test result type. * * Description: This function will return the test result type, it actually * the string that is entered by the user to an integer value that * is understood by the API's. * * Return: test type TPASS, TFAIL, TBROK, TCONF, TWARN, or TINFO * on success * -1 on failure */ int ident_ttype(char *tstype) { /* test result type one of TPASS, TFAIL, etc */ if (strcmp(tstype, "TBROK") == 0) return TBROK; else if (strcmp(tstype, "TFAIL") == 0) return TFAIL; else if (strcmp(tstype, "TPASS") == 0) return TPASS; else if (strcmp(tstype, "TCONF") == 0) return TCONF; else if (strcmp(tstype, "TWARN") == 0) return TWARN; else if (strcmp(tstype, "TINFO") == 0) return TINFO; else return -1; } void tst_cat_file(const char *filename) { const char *cmd[] = {"cat", filename, NULL}; tst_run_cmd(NULL, cmd, NULL, NULL, 0); } void apicmd_brk(int argc, char *argv[]) { int trestype; char *file_name; if (argc < 5) { fprintf(stderr, "Usage: %s TTYPE FNAME FUNC STRING\n" "\tTTYPE - Test Result Type; one of TFAIL, TBROK " "and TCONF.\n" "\tFNAME - Print contents of this file after the message\n" "\tFUNC - Cleanup function (ignored), but MUST be provided\n" "\tSTRING - Message explaining the test result\n", cmd_name); exit(1); } trestype = ident_ttype((argv++)[0]); file_name = (argv++)[0]; tst_cat_file(file_name); argv++; tst_brkm(trestype, NULL, "%s", *argv); } void apicmd_res(int argc, char *argv[]) { int trestype; char *file_name; if (argc < 4) { fprintf(stderr, "Usage: %s TTYPE FNAME STRING\n" "\tTTYPE - Test Result Type; one of TFAIL, TBROK " "and TCONF.\n" "\tFNAME - Print contents of this file after the message\n" "\tSTRING - Message explaining the test result\n", cmd_name); exit(1); } trestype = ident_ttype((argv++)[0]); file_name = (argv++)[0]; tst_cat_file(file_name); tst_resm(trestype, "%s", *argv); } void apicmd_brkm(int argc, char *argv[]) { int trestype; if (argc < 4) { fprintf(stderr, "Usage: %s TTYPE FUNC STRING\n" "\tTTYPE - Test Result Type; one of TFAIL, TBROK " "and TCONF.\n" "\tFUNC - Cleanup function (ignored), but MUST be provided\n" "\tSTRING - Message explaining the test result\n", cmd_name); exit(1); } trestype = ident_ttype((argv++)[0]); argv++; tst_brkm(trestype, NULL, "%s", *argv); } void apicmd_resm(int argc, char *argv[]) { int trestype; if (argc < 3) { fprintf(stderr, "Usage: %s TTYPE STRING\n" "\tTTYPE - Test Result Type; one of TFAIL, TBROK" "and TCONF.\n" "\tSTRING - Message explaining the test result\n", cmd_name); exit(1); } trestype = ident_ttype((argv++)[0]); tst_resm(trestype, "%s", *argv); } void apicmd_kvercmp(int argc, char *argv[]) { int exit_value; if (argc < 4) { fprintf(stderr, "Usage: %s NUM NUM NUM\n" "Compares to the running kernel version.\n\n" "\tNUM - A positive integer.\n" "\tThe first NUM is the kernel VERSION\n" "\tThe second NUM is the kernel PATCHLEVEL\n" "\tThe third NUM is the kernel SUBLEVEL\n\n" "\tExit status is 0 if the running kernel is older than the\n" "\t\tkernel specified by NUM NUM NUM.\n" "\tExit status is 1 for kernels of the same age.\n" "\tExit status is 2 if the running kernel is newer.\n", cmd_name); exit(1); } exit_value = tst_kvercmp(atoi(argv[0]), atoi(argv[1]), atoi(argv[2])); if (exit_value < 0) exit_value = 0; else if (exit_value == 0) exit_value = 1; else if (exit_value > 0) exit_value = 2; exit(exit_value); } void apicmd_kvercmp2(int argc, char *argv[]) { int exit_value; struct tst_kern_exv vers[100]; unsigned int count; char *saveptr1 = NULL; char *saveptr2 = NULL; char *token1; if (TCID == NULL) TCID = "outoftest"; if (tst_cntstr == NULL) tst_count = 0; if (argc < 5) { fprintf(stderr, "Usage: %s NUM NUM NUM KVERS\n" "Compares to the running kernel version\n" "based on vanilla kernel version NUM NUM NUM\n" "or distribution specific kernel version KVERS\n\n" "\tNUM - A positive integer.\n" "\tThe first NUM is the kernel VERSION\n" "\tThe second NUM is the kernel PATCHLEVEL\n" "\tThe third NUM is the kernel SUBLEVEL\n\n" "\tKVERS is a string of the form " "\"DISTR1:VERS1 DISTR2:VERS2\",\n" "\twhere DISTR1 is a distribution name\n" "\tand VERS1 is the corresponding kernel version.\n" "\tExample: \"RHEL6:2.6.39-400.208\"\n\n" "\tIf running kernel matches a distribution in KVERS then\n" "\tcomparison is performed based on version in KVERS,\n" "\totherwise - based on NUM NUM NUM.\n\n" "\tExit status is 0 if the running kernel is older.\n" "\tExit status is 1 for kernels of the same age.\n" "\tExit status is 2 if the running kernel is newer.\n", cmd_name); exit(3); } count = 0; token1 = strtok_r(argv[3], " ", &saveptr1); while ((token1 != NULL) && (count < 99)) { vers[count].dist_name = strtok_r(token1, ":", &saveptr2); vers[count].extra_ver = strtok_r(NULL, ":", &saveptr2); if (vers[count].extra_ver == NULL) { fprintf(stderr, "Incorrect KVERS format\n"); exit(3); } count++; token1 = strtok_r(NULL, " ", &saveptr1); } vers[count].dist_name = NULL; vers[count].extra_ver = NULL; exit_value = tst_kvercmp2(atoi(argv[0]), atoi(argv[1]), atoi(argv[2]), vers); if (exit_value < 0) exit_value = 0; else if (exit_value == 0) exit_value = 1; else if (exit_value > 0) exit_value = 2; exit(exit_value); } struct param_pair { char *cmd; int value; }; unsigned short apicmd_get_unused_port(int argc, char *argv[]) { if (argc != 3) goto err; const struct param_pair params[][3] = { {{"ipv4", AF_INET}, {"ipv6", AF_INET6}, {NULL, 0}}, {{"stream", SOCK_STREAM}, {"dgram", SOCK_DGRAM}, {NULL, 0}} }; int i; const struct param_pair *p[2]; for (i = 0; i < 2; ++i) { for (p[i] = params[i]; p[i]->cmd; ++p[i]) { if (!strcmp(p[i]->cmd, argv[i])) break; } if (!p[i]->cmd) goto err; } return tst_get_unused_port(NULL, p[0]->value, p[1]->value); err: fprintf(stderr, "Usage: tst_get_unused_port FAMILY TYPE\n" "where FAMILY := { ipv4 | ipv6 }\n" " TYPE := { stream | dgram }\n"); exit(1); } int apicmd_fs_has_free(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: tst_fs_has_free path required_bytes\n" "path: the pathname of the mounted filesystem\n" "required_bytes: the required free space" " (supports kB, MB and GB suffixes)\n"); exit(2); } char *endptr; unsigned int required_kib = strtoull(argv[1], &endptr, 0); unsigned int mul = TST_BYTES; if (*argv[1] == '\0') goto fs_has_free_err; if (*endptr != '\0') { if (!strcasecmp(endptr, "kB")) { mul = TST_KB; } else if (!strcasecmp(endptr, "MB")) { mul = TST_MB; } else if (!strcasecmp(endptr, "GB")) { mul = TST_GB; } else { goto fs_has_free_err; } } exit(!tst_fs_has_free(NULL, argv[0], required_kib, mul)); fs_has_free_err: fprintf(stderr, "%s is not a valid size\n", argv[1]); exit(2); } /* * Function: main - entry point of this program * * Description: Parses the arguments to each command. Most commands have in * common atlest 2 arguments, type of test result, which is one of * TPASS, TFAIL, TBROK, TCONF, etc, and a message that describes * the result. Other arguments are a file, the contents of which * are printed after the type of test result and associated message * is printed, also a cleanup function that will be executed. * Currently this function name is ignored but MUST be provided * for compatability reasons. * * The different commands are actually a hard link to this program * the program invokes the appropriate function based on the * command name with which it was invoked. * * Set the values for TCID to the name of the test case. * set the value for TST_TOTAL for total number of tests this is * required in case one test breaks and all following tests also * should be reported as broken. * Set tst_count before every individual test. * * Exit: 0 on success * -1 on failure */ int main(int argc, char *argv[]) { strcpy(cmd_name, SAFE_BASENAME(NULL, (argv++)[0])); TCID = getenv("TCID"); tst_total = getenv("TST_TOTAL"); tst_cntstr = getenv("TST_COUNT"); if (TCID == NULL || tst_total == NULL || tst_cntstr == NULL) { if (!strcmp(cmd_name, "tst_kvercmp") && !strcmp(cmd_name, "tst_kvercmp2") && !strcmp(cmd_name, "tst_fs_has_free") && !strcmp(cmd_name, "tst_get_unused_port")) { fprintf(stderr, "\nSet variables TCID, TST_TOTAL, and TST_COUNT before each test:\n" "export TCID=<test name>\n" "export TST_TOTAL=<Total Number of Tests >\n" "export TST_COUNT=<Test case number>\n\n"); /* Make sure the user knows there's an error. */ abort(); } } else { TST_TOTAL = atoi(tst_total); tst_count = atoi(tst_cntstr); if (tst_count > 0) tst_count--; if (strcmp(TCID, " ") == 0) { fprintf(stderr, "Variable TCID not set, use: TCID=<test name>\n"); exit(1); } if (TST_TOTAL <= 0) { fprintf(stderr, "Variable TST_TOTAL is set to 0, must be " "greater than zero\n"); exit(1); } } if (strcmp(cmd_name, "tst_brk") == 0) { apicmd_brk(argc, argv); } else if (strcmp(cmd_name, "tst_res") == 0) { apicmd_res(argc, argv); } else if (strcmp(cmd_name, "tst_brkm") == 0) { apicmd_brkm(argc, argv); } else if (strcmp(cmd_name, "tst_resm") == 0) { apicmd_resm(argc, argv); } else if (strcmp(cmd_name, "tst_exit") == 0) { tst_exit(); } else if (strcmp(cmd_name, "tst_flush") == 0) { tst_flush(); } else if (strcmp(cmd_name, "tst_kvercmp") == 0) { apicmd_kvercmp(argc, argv); } else if (strcmp(cmd_name, "tst_kvercmp2") == 0) { apicmd_kvercmp2(argc, argv); } else if (strcmp(cmd_name, "tst_ncpus") == 0) { printf("%li\n", tst_ncpus()); } else if (strcmp(cmd_name, "tst_ncpus_conf") == 0) { printf("%li\n", tst_ncpus_conf()); } else if (strcmp(cmd_name, "tst_ncpus_max") == 0) { printf("%li\n", tst_ncpus_max()); } else if (strcmp(cmd_name, "tst_get_unused_port") == 0) { printf("%u\n", apicmd_get_unused_port(argc, argv)); } else if (strcmp(cmd_name, "tst_fs_has_free") == 0) { apicmd_fs_has_free(argc, argv); } exit(0); }