1 /*-
2  * Public Domain 2014-2018 MongoDB, Inc.
3  * Public Domain 2008-2014 WiredTiger, Inc.
4  *
5  * This is free and unencumbered software released into the public domain.
6  *
7  * Anyone is free to copy, modify, publish, use, compile, sell, or
8  * distribute this software, either in source code form or as a compiled
9  * binary, for any purpose, commercial or non-commercial, and by any
10  * means.
11  *
12  * In jurisdictions that recognize copyright laws, the author or authors
13  * of this software dedicate any and all copyright interest in the
14  * software to the public domain. We make this dedication for the benefit
15  * of the public at large and to the detriment of our heirs and
16  * successors. We intend this dedication to be an overt act of
17  * relinquishment in perpetuity of all present and future rights to this
18  * software under copyright law.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  */
28 #include "test_util.h"
29 
30 #ifndef _WIN32
31 #include <sys/wait.h>
32 #endif
33 
34 void (*custom_die)(void) = NULL;
35 const char *progname = "program name not set";
36 
37 /*
38  * testutil_die --
39  *	Report an error and abort.
40  */
41 void
testutil_die(int e,const char * fmt,...)42 testutil_die(int e, const char *fmt, ...)
43 {
44 	va_list ap;
45 
46 	/* Flush output to be sure it doesn't mix with fatal errors. */
47 	(void)fflush(stdout);
48 	(void)fflush(stderr);
49 
50 	/* Allow test programs to cleanup on fatal error. */
51 	if (custom_die != NULL)
52 		(*custom_die)();
53 
54 	fprintf(stderr, "%s: FAILED", progname);
55 	if (fmt != NULL) {
56 		fprintf(stderr, ": ");
57 		va_start(ap, fmt);
58 		vfprintf(stderr, fmt, ap);
59 		va_end(ap);
60 	}
61 	if (e != 0)
62 		fprintf(stderr, ": %s", wiredtiger_strerror(e));
63 	fprintf(stderr, "\n");
64 	fprintf(stderr, "process aborting\n");
65 
66 	abort();
67 }
68 
69 /*
70  * testutil_set_progname --
71  *	Set the global program name for error handling.
72  */
73 const char *
testutil_set_progname(char * const * argv)74 testutil_set_progname(char * const *argv)
75 {
76 	if ((progname = strrchr(argv[0], DIR_DELIM)) == NULL)
77 		progname = argv[0];
78 	else
79 		++progname;
80 	return (progname);
81 }
82 
83 /*
84  * testutil_work_dir_from_path --
85  *	Takes a buffer, its size and the intended work directory.
86  *	Creates the full intended work directory in buffer.
87  */
88 void
testutil_work_dir_from_path(char * buffer,size_t len,const char * dir)89 testutil_work_dir_from_path(char *buffer, size_t len, const char *dir)
90 {
91 	/* If no directory is provided, use the default. */
92 	if (dir == NULL)
93 		dir = DEFAULT_DIR;
94 
95 	if (len < strlen(dir) + 1)
96 		testutil_die(ENOMEM,
97 		    "Not enough memory in buffer for directory %s", dir);
98 
99 	strcpy(buffer, dir);
100 }
101 
102 /*
103  * testutil_clean_work_dir --
104  *	Remove the work directory.
105  */
106 void
testutil_clean_work_dir(const char * dir)107 testutil_clean_work_dir(const char *dir)
108 {
109 	size_t len;
110 	int ret;
111 	char *buf;
112 
113 #ifdef _WIN32
114 	/* Additional bytes for the Windows rd command. */
115 	len = 2 * strlen(dir) + strlen(RM_COMMAND) +
116 		strlen(DIR_EXISTS_COMMAND) + 4;
117 	if ((buf = malloc(len)) == NULL)
118 		testutil_die(ENOMEM, "Failed to allocate memory");
119 
120 	testutil_check(__wt_snprintf(
121 	    buf, len, "%s %s %s %s", DIR_EXISTS_COMMAND, dir, RM_COMMAND, dir));
122 #else
123 	len = strlen(dir) + strlen(RM_COMMAND) + 1;
124 	if ((buf = malloc(len)) == NULL)
125 		testutil_die(ENOMEM, "Failed to allocate memory");
126 
127 	testutil_check(__wt_snprintf(buf, len, "%s%s", RM_COMMAND, dir));
128 #endif
129 
130 	if ((ret = system(buf)) != 0 && ret != ENOENT)
131 		testutil_die(ret, "%s", buf);
132 	free(buf);
133 }
134 
135 /*
136  * testutil_make_work_dir --
137  *	Delete the existing work directory, then create a new one.
138  */
139 void
testutil_make_work_dir(const char * dir)140 testutil_make_work_dir(const char *dir)
141 {
142 	size_t len;
143 	char *buf;
144 
145 	testutil_clean_work_dir(dir);
146 
147 	/* Additional bytes for the mkdir command */
148 	len = strlen(dir) + strlen(MKDIR_COMMAND) + 1;
149 	if ((buf = malloc(len)) == NULL)
150 		testutil_die(ENOMEM, "Failed to allocate memory");
151 
152 	/* mkdir shares syntax between Windows and Linux */
153 	testutil_check(__wt_snprintf(buf, len, "%s%s", MKDIR_COMMAND, dir));
154 	testutil_check(system(buf));
155 	free(buf);
156 }
157 
158 /*
159  * testutil_progress --
160  *	Print a progress message to the progress file.
161  */
162 void
testutil_progress(TEST_OPTS * opts,const char * message)163 testutil_progress(TEST_OPTS *opts, const char *message)
164 {
165 	FILE *fp;
166 	time_t now;
167 
168 	if ((fp = fopen(opts->progress_file_name, "a")) == NULL)
169 		testutil_die(errno, "fopen");
170 	(void)time(&now);
171 	fprintf(fp, "[%" PRIuMAX "] %s\n", (uintmax_t)now, message);
172 	if (fclose(fp) != 0)
173 		testutil_die(errno, "fclose");
174 }
175 
176 /*
177  * testutil_cleanup --
178  *	Delete the existing work directory and free the options structure.
179  */
180 void
testutil_cleanup(TEST_OPTS * opts)181 testutil_cleanup(TEST_OPTS *opts)
182 {
183 	if (opts->conn != NULL)
184 		testutil_check(opts->conn->close(opts->conn, NULL));
185 
186 	if (!opts->preserve)
187 		testutil_clean_work_dir(opts->home);
188 
189 	free(opts->uri);
190 	free(opts->progress_file_name);
191 	free(opts->home);
192 }
193 
194 /*
195  * testutil_is_flag_set --
196  *	Return if an environment variable flag is set.
197  */
198 bool
testutil_is_flag_set(const char * flag)199 testutil_is_flag_set(const char *flag)
200 {
201 	const char *res;
202 	bool enable_long_tests;
203 
204 	if (__wt_getenv(NULL, flag, &res) != 0 || res == NULL)
205 		return (false);
206 
207 	/*
208 	 * This is a boolean test. So if the environment variable is set to any
209 	 * value other than 0, we return success.
210 	 */
211 	enable_long_tests = res[0] != '0';
212 
213 	free((void *)res);
214 
215 	return (enable_long_tests);
216 }
217 
218 #ifndef _WIN32
219 /*
220  * testutil_sleep_wait --
221  *	Wait for a process up to a number of seconds.
222  */
223 void
testutil_sleep_wait(uint32_t seconds,pid_t pid)224 testutil_sleep_wait(uint32_t seconds, pid_t pid)
225 {
226 	pid_t got;
227 	int status;
228 
229 	while (seconds > 0) {
230 		if ((got = waitpid(pid, &status, WNOHANG|WUNTRACED)) == pid) {
231 			if (WIFEXITED(status))
232 				testutil_die(EINVAL,
233 				    "Child process %" PRIu64 " exited early"
234 				    " with status %d", (uint64_t)pid,
235 				    WEXITSTATUS(status));
236 			if (WIFSIGNALED(status))
237 				testutil_die(EINVAL,
238 				    "Child process %" PRIu64 " terminated "
239 				    " with signal %d", (uint64_t)pid,
240 				    WTERMSIG(status));
241 		} else if (got == -1)
242 			testutil_die(errno, "waitpid");
243 
244 		--seconds;
245 		sleep(1);
246 	}
247 }
248 #endif
249 
250 /*
251  * dcalloc --
252  *	Call calloc, dying on failure.
253  */
254 void *
dcalloc(size_t number,size_t size)255 dcalloc(size_t number, size_t size)
256 {
257 	void *p;
258 
259 	if ((p = calloc(number, size)) != NULL)
260 		return (p);
261 	testutil_die(errno, "calloc: %" WT_SIZET_FMT "B", number * size);
262 }
263 
264 /*
265  * dmalloc --
266  *	Call malloc, dying on failure.
267  */
268 void *
dmalloc(size_t len)269 dmalloc(size_t len)
270 {
271 	void *p;
272 
273 	if ((p = malloc(len)) != NULL)
274 		return (p);
275 	testutil_die(errno, "malloc: %" WT_SIZET_FMT "B", len);
276 }
277 
278 /*
279  * drealloc --
280  *	Call realloc, dying on failure.
281  */
282 void *
drealloc(void * p,size_t len)283 drealloc(void *p, size_t len)
284 {
285 	void *t;
286 
287 	if ((t = realloc(p, len)) != NULL)
288 		return (t);
289 	testutil_die(errno, "realloc: %" WT_SIZET_FMT "B", len);
290 }
291 
292 /*
293  * dstrdup --
294  *	Call strdup, dying on failure.
295  */
296 void *
dstrdup(const void * str)297 dstrdup(const void *str)
298 {
299 	char *p;
300 
301 	if ((p = strdup(str)) != NULL)
302 		return (p);
303 	testutil_die(errno, "strdup");
304 }
305 
306 /*
307  * dstrndup --
308  *      Call emulating strndup, dying on failure. Don't use actual strndup here
309  *	as it is not supported within MSVC.
310  */
311 void *
dstrndup(const char * str,size_t len)312 dstrndup(const char *str, size_t len)
313 {
314 	char *p;
315 
316 	p = dcalloc(len + 1, sizeof(char));
317 	memcpy(p, str, len);
318 	return (p);
319 }
320 
321 /*
322  * example_setup --
323  *	Set the program name, create a home directory for the example programs.
324  */
325 const char *
example_setup(int argc,char * const * argv)326 example_setup(int argc, char * const *argv)
327 {
328 	const char *home;
329 
330 	(void)argc;					/* Unused variable */
331 
332 	(void)testutil_set_progname(argv);
333 
334 	/*
335 	 * Create a clean test directory for this run of the test program if the
336 	 * environment variable isn't already set (as is done by make check).
337 	 */
338 	if ((home = getenv("WIREDTIGER_HOME")) == NULL)
339 		home = "WT_HOME";
340 	testutil_make_work_dir(home);
341 	return (home);
342 }
343