1 /*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 * AUTHOR : Kent Rogers (from Dave Fenner's original)
4 * CO-PILOT : Rich Logan
5 * DATE STARTED : 05/01/90 (rewritten 1/96)
6 * Copyright (c) 2009-2016 Cyril Hrubis <chrubis@suse.cz>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it would be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * Further, this software is distributed without any warranty that it is
17 * free of the rightful claim of any third person regarding infringement
18 * or the like. Any license provided herein, whether implied or
19 * otherwise, applies only to this software file. Patent licenses, if
20 * any, provided herein do not apply to combinations of this program with
21 * other software, or any other product whatsoever.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
28 * Mountain View, CA 94043, or:
29 *
30 * http://www.sgi.com
31 *
32 * For further information regarding this notice, see:
33 *
34 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
35 */
36
37 #define _GNU_SOURCE
38
39 #include <pthread.h>
40 #include <assert.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49
50 #include "test.h"
51 #include "safe_macros.h"
52 #include "usctest.h"
53 #include "ltp_priv.h"
54 #include "tst_ansi_color.h"
55
56 long TEST_RETURN;
57 int TEST_ERRNO;
58
59 #define VERBOSE 1
60 #define NOPASS 3
61 #define DISCARD 4
62
63 #define MAXMESG 80 /* max length of internal messages */
64 #define USERMESG 2048 /* max length of user message */
65 #define TRUE 1
66 #define FALSE 0
67
68 /*
69 * EXPAND_VAR_ARGS - Expand the variable portion (arg_fmt) of a result
70 * message into the specified string.
71 *
72 * NOTE (garrcoop): arg_fmt _must_ be the last element in each function
73 * argument list that employs this.
74 */
75 #define EXPAND_VAR_ARGS(buf, arg_fmt, buf_len) do {\
76 va_list ap; \
77 assert(arg_fmt != NULL); \
78 va_start(ap, arg_fmt); \
79 vsnprintf(buf, buf_len, arg_fmt, ap); \
80 va_end(ap); \
81 assert(strlen(buf) > 0); \
82 } while (0)
83
84 #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
85 # ifdef __ANDROID__
86 # define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
87 PTHREAD_RECURSIVE_MUTEX_INITIALIZER
88 # else
89 /* MUSL: http://www.openwall.com/lists/musl/2017/02/20/5 */
90 # define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP { {PTHREAD_MUTEX_RECURSIVE} }
91 # endif
92 #endif
93
94 static pthread_mutex_t tmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
95
96 static void check_env(void);
97 static void tst_condense(int tnum, int ttype, const char *tmesg);
98 static void tst_print(const char *tcid, int tnum, int ttype, const char *tmesg);
99
100 static int T_exitval = 0; /* exit value used by tst_exit() */
101 static int passed_cnt;
102 static int T_mode = VERBOSE; /* flag indicating print mode: VERBOSE, */
103 /* NOPASS, DISCARD */
104
105 static char Warn_mesg[MAXMESG]; /* holds warning messages */
106
107 /*
108 * These are used for condensing output when NOT in verbose mode.
109 */
110 static int Buffered = FALSE; /* TRUE if condensed output is currently */
111 /* buffered (i.e. not yet printed) */
112 static char *Last_tcid; /* previous test case id */
113 static int Last_num; /* previous test case number */
114 static int Last_type; /* previous test result type */
115 static char *Last_mesg; /* previous test result message */
116
117 int tst_count = 0;
118
119 /*
120 * These globals must be defined in the test.
121 */
122 extern char *TCID; /* Test case identifier from the test source */
123 extern int TST_TOTAL; /* Total number of test cases from the test */
124
125
126 struct pair {
127 const char *name;
128 int val;
129 };
130
131 #define PAIR(def) [def] = {.name = #def, .val = def},
132 #define STRPAIR(key, value) [key] = {.name = value, .val = key},
133
134 #define PAIR_LOOKUP(pair_arr, idx) do { \
135 if (idx < 0 || (size_t)idx >= ARRAY_SIZE(pair_arr) || \
136 pair_arr[idx].name == NULL) \
137 return "???"; \
138 return pair_arr[idx].name; \
139 } while (0)
140
strttype(int ttype)141 const char *strttype(int ttype)
142 {
143 static const struct pair ttype_pairs[] = {
144 PAIR(TPASS)
145 PAIR(TFAIL)
146 PAIR(TBROK)
147 PAIR(TCONF)
148 PAIR(TWARN)
149 PAIR(TINFO)
150 };
151
152 PAIR_LOOKUP(ttype_pairs, TTYPE_RESULT(ttype));
153 }
154
155 #include "errnos.h"
156 #include "signame.h"
157
tst_res__(const char * file,const int lineno,int ttype,const char * arg_fmt,...)158 static void tst_res__(const char *file, const int lineno, int ttype,
159 const char *arg_fmt, ...)
160 {
161 pthread_mutex_lock(&tmutex);
162
163 char tmesg[USERMESG];
164 int len = 0;
165 int ttype_result = TTYPE_RESULT(ttype);
166
167 if (file && (ttype_result != TPASS && ttype_result != TINFO))
168 len = sprintf(tmesg, "%s:%d: ", file, lineno);
169 EXPAND_VAR_ARGS(tmesg + len, arg_fmt, USERMESG - len);
170
171 /*
172 * Save the test result type by ORing ttype into the current exit
173 * value (used by tst_exit()).
174 */
175 T_exitval |= ttype_result;
176
177 if (ttype_result == TPASS)
178 passed_cnt++;
179
180 check_env();
181
182 /*
183 * Set the test case number and print the results, depending on the
184 * display type.
185 */
186 if (ttype_result == TWARN || ttype_result == TINFO) {
187 tst_print(TCID, 0, ttype, tmesg);
188 } else {
189 if (tst_count < 0)
190 tst_print(TCID, 0, TWARN,
191 "tst_res(): tst_count < 0 is not valid");
192
193 /*
194 * Process each display type.
195 */
196 switch (T_mode) {
197 case DISCARD:
198 break;
199 case NOPASS: /* filtered by tst_print() */
200 tst_condense(tst_count + 1, ttype, tmesg);
201 break;
202 default: /* VERBOSE */
203 tst_print(TCID, tst_count + 1, ttype, tmesg);
204 break;
205 }
206
207 tst_count++;
208 }
209
210 pthread_mutex_unlock(&tmutex);
211 }
212
tst_condense(int tnum,int ttype,const char * tmesg)213 static void tst_condense(int tnum, int ttype, const char *tmesg)
214 {
215 int ttype_result = TTYPE_RESULT(ttype);
216
217 /*
218 * If this result is the same as the previous result, return.
219 */
220 if (Buffered == TRUE) {
221 if (strcmp(Last_tcid, TCID) == 0 && Last_type == ttype_result &&
222 strcmp(Last_mesg, tmesg) == 0)
223 return;
224
225 /*
226 * This result is different from the previous result. First,
227 * print the previous result.
228 */
229 tst_print(Last_tcid, Last_num, Last_type, Last_mesg);
230 free(Last_tcid);
231 free(Last_mesg);
232 }
233
234 /*
235 * If a file was specified, print the current result since we have no
236 * way of retaining the file contents for comparing with future
237 * results. Otherwise, buffer the current result info for next time.
238 */
239 Last_tcid = malloc(strlen(TCID) + 1);
240 strcpy(Last_tcid, TCID);
241 Last_num = tnum;
242 Last_type = ttype_result;
243 Last_mesg = malloc(strlen(tmesg) + 1);
244 strcpy(Last_mesg, tmesg);
245 Buffered = TRUE;
246 }
247
tst_flush(void)248 void tst_flush(void)
249 {
250 NO_NEWLIB_ASSERT("Unknown", 0);
251
252 pthread_mutex_lock(&tmutex);
253
254 /*
255 * Print out last line if in NOPASS mode.
256 */
257 if (Buffered == TRUE && T_mode == NOPASS) {
258 tst_print(Last_tcid, Last_num, Last_type, Last_mesg);
259 Buffered = FALSE;
260 }
261
262 fflush(stdout);
263
264 pthread_mutex_unlock(&tmutex);
265 }
266
tst_print(const char * tcid,int tnum,int ttype,const char * tmesg)267 static void tst_print(const char *tcid, int tnum, int ttype, const char *tmesg)
268 {
269 int err = errno;
270 const char *type;
271 int ttype_result = TTYPE_RESULT(ttype);
272 char message[USERMESG];
273 size_t size = 0;
274
275 /*
276 * Save the test result type by ORing ttype into the current exit value
277 * (used by tst_exit()). This is already done in tst_res(), but is
278 * also done here to catch internal warnings. For internal warnings,
279 * tst_print() is called directly with a case of TWARN.
280 */
281 T_exitval |= ttype_result;
282
283 /*
284 * If output mode is DISCARD, or if the output mode is NOPASS and this
285 * result is not one of FAIL, BROK, or WARN, just return. This check
286 * is necessary even though we check for DISCARD mode inside of
287 * tst_res(), since occasionally we get to this point without going
288 * through tst_res() (e.g. internal TWARN messages).
289 */
290 if (T_mode == DISCARD || (T_mode == NOPASS && ttype_result != TFAIL &&
291 ttype_result != TBROK
292 && ttype_result != TWARN))
293 return;
294
295 /*
296 * Build the result line and print it.
297 */
298 type = strttype(ttype);
299
300 if (T_mode == VERBOSE) {
301 size += snprintf(message + size, sizeof(message) - size,
302 "%-8s %4d ", tcid, tnum);
303 } else {
304 size += snprintf(message + size, sizeof(message) - size,
305 "%-8s %4d ", tcid, tnum);
306 }
307
308 if (size >= sizeof(message)) {
309 printf("%s: %i: line too long\n", __func__, __LINE__);
310 abort();
311 }
312
313 if (tst_color_enabled(STDOUT_FILENO))
314 size += snprintf(message + size, sizeof(message) - size,
315 "%s%s%s : %s", tst_ttype2color(ttype), type, ANSI_COLOR_RESET, tmesg);
316 else
317 size += snprintf(message + size, sizeof(message) - size,
318 "%s : %s", type, tmesg);
319
320 if (size >= sizeof(message)) {
321 printf("%s: %i: line too long\n", __func__, __LINE__);
322 abort();
323 }
324
325 if (ttype & TERRNO) {
326 size += snprintf(message + size, sizeof(message) - size,
327 ": errno=%s(%i): %s", tst_strerrno(err),
328 err, strerror(err));
329 }
330
331 if (size >= sizeof(message)) {
332 printf("%s: %i: line too long\n", __func__, __LINE__);
333 abort();
334 }
335
336 if (ttype & TTERRNO) {
337 size += snprintf(message + size, sizeof(message) - size,
338 ": TEST_ERRNO=%s(%i): %s",
339 tst_strerrno(TEST_ERRNO), (int)TEST_ERRNO,
340 strerror(TEST_ERRNO));
341 }
342
343 if (size >= sizeof(message)) {
344 printf("%s: %i: line too long\n", __func__, __LINE__);
345 abort();
346 }
347
348 if (ttype & TRERRNO) {
349 size += snprintf(message + size, sizeof(message) - size,
350 ": TEST_RETURN=%s(%i): %s",
351 tst_strerrno(TEST_RETURN), (int)TEST_RETURN,
352 strerror(TEST_RETURN));
353 }
354
355 if (size + 1 >= sizeof(message)) {
356 printf("%s: %i: line too long\n", __func__, __LINE__);
357 abort();
358 }
359
360 message[size] = '\n';
361 message[size + 1] = '\0';
362
363 fputs(message, stdout);
364 }
365
check_env(void)366 static void check_env(void)
367 {
368 static int first_time = 1;
369 char *value;
370
371 if (!first_time)
372 return;
373
374 first_time = 0;
375
376 /* BTOUTPUT not defined, use default */
377 if ((value = getenv(TOUTPUT)) == NULL) {
378 T_mode = VERBOSE;
379 return;
380 }
381
382 if (strcmp(value, TOUT_NOPASS_S) == 0) {
383 T_mode = NOPASS;
384 return;
385 }
386
387 if (strcmp(value, TOUT_DISCARD_S) == 0) {
388 T_mode = DISCARD;
389 return;
390 }
391
392 T_mode = VERBOSE;
393 return;
394 }
395
tst_exit(void)396 void tst_exit(void)
397 {
398 NO_NEWLIB_ASSERT("Unknown", 0);
399
400 pthread_mutex_lock(&tmutex);
401
402 tst_flush();
403
404 T_exitval &= ~TINFO;
405
406 if (T_exitval == TCONF && passed_cnt)
407 T_exitval &= ~TCONF;
408
409 exit(T_exitval);
410 }
411
tst_fork(void)412 pid_t tst_fork(void)
413 {
414 pid_t child;
415
416 NO_NEWLIB_ASSERT("Unknown", 0);
417
418 tst_flush();
419
420 child = fork();
421 if (child == 0)
422 T_exitval = 0;
423
424 return child;
425 }
426
tst_record_childstatus(void (* cleanup)(void),pid_t child)427 void tst_record_childstatus(void (*cleanup)(void), pid_t child)
428 {
429 int status, ttype_result;
430
431 NO_NEWLIB_ASSERT("Unknown", 0);
432
433 SAFE_WAITPID(cleanup, child, &status, 0);
434
435 if (WIFEXITED(status)) {
436 ttype_result = WEXITSTATUS(status);
437 ttype_result = TTYPE_RESULT(ttype_result);
438 T_exitval |= ttype_result;
439
440 if (ttype_result == TPASS)
441 tst_resm(TINFO, "Child process returned TPASS");
442
443 if (ttype_result & TFAIL)
444 tst_resm(TINFO, "Child process returned TFAIL");
445
446 if (ttype_result & TBROK)
447 tst_resm(TINFO, "Child process returned TBROK");
448
449 if (ttype_result & TCONF)
450 tst_resm(TINFO, "Child process returned TCONF");
451
452 } else {
453 tst_brkm(TBROK, cleanup, "child process(%d) killed by "
454 "unexpected signal %s(%d)", child,
455 tst_strsig(WTERMSIG(status)), WTERMSIG(status));
456 }
457 }
458
tst_vfork(void)459 pid_t tst_vfork(void)
460 {
461 NO_NEWLIB_ASSERT("Unknown", 0);
462
463 tst_flush();
464 return vfork();
465 }
466
467 /*
468 * Make tst_brk reentrant so that one can call the SAFE_* macros from within
469 * user-defined cleanup functions.
470 */
471 static int tst_brk_entered = 0;
472
tst_brk__(const char * file,const int lineno,int ttype,void (* func)(void),const char * arg_fmt,...)473 static void tst_brk__(const char *file, const int lineno, int ttype,
474 void (*func)(void), const char *arg_fmt, ...)
475 {
476 pthread_mutex_lock(&tmutex);
477
478 char tmesg[USERMESG];
479 int ttype_result = TTYPE_RESULT(ttype);
480
481 EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
482
483 /*
484 * Only FAIL, BROK, CONF, and RETR are supported by tst_brk().
485 */
486 if (ttype_result != TFAIL && ttype_result != TBROK &&
487 ttype_result != TCONF) {
488 sprintf(Warn_mesg, "%s: Invalid Type: %d. Using TBROK",
489 __func__, ttype_result);
490 tst_print(TCID, 0, TWARN, Warn_mesg);
491 /* Keep TERRNO, TTERRNO, etc. */
492 ttype = (ttype & ~ttype_result) | TBROK;
493 }
494
495 tst_res__(file, lineno, ttype, "%s", tmesg);
496 if (tst_brk_entered == 0) {
497 if (ttype_result == TCONF) {
498 tst_res__(file, lineno, ttype,
499 "Remaining cases not appropriate for "
500 "configuration");
501 } else if (ttype_result == TBROK) {
502 tst_res__(file, lineno, TBROK,
503 "Remaining cases broken");
504 }
505 }
506
507 /*
508 * If no cleanup function was specified, just return to the caller.
509 * Otherwise call the specified function.
510 */
511 if (func != NULL) {
512 tst_brk_entered++;
513 (*func) ();
514 tst_brk_entered--;
515 }
516 if (tst_brk_entered == 0)
517 tst_exit();
518
519 pthread_mutex_unlock(&tmutex);
520 }
521
tst_resm_(const char * file,const int lineno,int ttype,const char * arg_fmt,...)522 void tst_resm_(const char *file, const int lineno, int ttype,
523 const char *arg_fmt, ...)
524 {
525 char tmesg[USERMESG];
526
527 EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
528
529 if (tst_test)
530 tst_res_(file, lineno, ttype, "%s", tmesg);
531 else
532 tst_res__(file, lineno, ttype, "%s", tmesg);
533 }
534
535 typedef void (*tst_res_func_t)(const char *file, const int lineno,
536 int ttype, const char *fmt, ...);
537
tst_resm_hexd_(const char * file,const int lineno,int ttype,const void * buf,size_t size,const char * arg_fmt,...)538 void tst_resm_hexd_(const char *file, const int lineno, int ttype,
539 const void *buf, size_t size, const char *arg_fmt, ...)
540 {
541 char tmesg[USERMESG];
542 static const size_t symb_num = 2; /* xx */
543 static const size_t size_max = 16;
544 size_t offset;
545 size_t i;
546 char *pmesg = tmesg;
547 tst_res_func_t res_func;
548
549 if (tst_test)
550 res_func = tst_res_;
551 else
552 res_func = tst_res__;
553
554 EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
555 offset = strlen(tmesg);
556
557 if (size > size_max || size == 0 ||
558 (offset + size * (symb_num + 1)) >= USERMESG)
559 res_func(file, lineno, ttype, "%s", tmesg);
560 else
561 pmesg += offset;
562
563 for (i = 0; i < size; ++i) {
564 /* add space before byte except first one */
565 if (pmesg != tmesg)
566 *(pmesg++) = ' ';
567
568 sprintf(pmesg, "%02x", ((unsigned char *)buf)[i]);
569 pmesg += symb_num;
570 if ((i + 1) % size_max == 0 || i + 1 == size) {
571 res_func(file, lineno, ttype, "%s", tmesg);
572 pmesg = tmesg;
573 }
574 }
575 }
576
tst_brkm_(const char * file,const int lineno,int ttype,void (* func)(void),const char * arg_fmt,...)577 void tst_brkm_(const char *file, const int lineno, int ttype,
578 void (*func)(void), const char *arg_fmt, ...)
579 {
580 char tmesg[USERMESG];
581
582 EXPAND_VAR_ARGS(tmesg, arg_fmt, USERMESG);
583
584 if (tst_test) {
585 if (func) {
586 tst_brk_(file, lineno, TBROK,
587 "Non-NULL cleanup in newlib!");
588 }
589
590 tst_brk_(file, lineno, ttype, "%s", tmesg);
591 } else {
592 tst_brk__(file, lineno, ttype, func, "%s", tmesg);
593 }
594
595 /* Shouldn't be reached, but fixes build time warnings about noreturn. */
596 abort();
597 }
598
tst_require_root(void)599 void tst_require_root(void)
600 {
601 NO_NEWLIB_ASSERT("Unknown", 0);
602
603 if (geteuid() != 0)
604 tst_brkm(TCONF, NULL, "Test needs to be run as root");
605 }
606