Home | History | Annotate | Download | only in apicmds
      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