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
56long TEST_RETURN;
57int 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
94static pthread_mutex_t tmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
95
96static void check_env(void);
97static void tst_condense(int tnum, int ttype, const char *tmesg);
98static void tst_print(const char *tcid, int tnum, int ttype, const char *tmesg);
99
100static int T_exitval = 0;	/* exit value used by tst_exit() */
101static int passed_cnt;
102static int T_mode = VERBOSE;	/* flag indicating print mode: VERBOSE, */
103				/* NOPASS, DISCARD */
104
105static char Warn_mesg[MAXMESG];	/* holds warning messages */
106
107/*
108 * These are used for condensing output when NOT in verbose mode.
109 */
110static int Buffered = FALSE;	/* TRUE if condensed output is currently */
111				/* buffered (i.e. not yet printed) */
112static char *Last_tcid;		/* previous test case id */
113static int Last_num;		/* previous test case number */
114static int Last_type;		/* previous test result type */
115static char *Last_mesg;		/* previous test result message */
116
117int tst_count = 0;
118
119/*
120 * These globals must be defined in the test.
121 */
122extern char *TCID;		/* Test case identifier from the test source */
123extern int TST_TOTAL;		/* Total number of test cases from the test */
124
125
126struct 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
141const 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
158static 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
213static 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
248void 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
267static 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
366static 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
396void 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
412pid_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
427void 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
459pid_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 */
471static int tst_brk_entered = 0;
472
473static 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
522void 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
535typedef void (*tst_res_func_t)(const char *file, const int lineno,
536		int ttype, const char *fmt, ...);
537
538void 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
577void 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
599void 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