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