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@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  */
ident_ttype(char * tstype)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 
tst_cat_file(const char * filename)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 
apicmd_brk(int argc,char * argv[])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 
apicmd_res(int argc,char * argv[])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 
apicmd_brkm(int argc,char * argv[])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 
apicmd_resm(int argc,char * argv[])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 
apicmd_kvercmp(int argc,char * argv[])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 
apicmd_kvercmp2(int argc,char * argv[])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 
apicmd_get_unused_port(int argc,char * argv[])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 
apicmd_fs_has_free(int argc,char * argv[])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  */
main(int argc,char * argv[])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