• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Common test functions for ethtool
3  * Copyright 2011 Solarflare Communications Inc.
4  *
5  * Partly derived from kernel <linux/list.h>.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published
9  * by the Free Software Foundation, incorporated herein by reference.
10  */
11 
12 #include <assert.h>
13 #include <errno.h>
14 #include <setjmp.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/fcntl.h>
19 #include <unistd.h>
20 #define TEST_NO_WRAPPERS
21 #include "internal.h"
22 
23 /* List utilities */
24 
25 struct list_head {
26 	struct list_head *next, *prev;
27 };
28 
29 #define LIST_HEAD_INIT(name) { &(name), &(name) }
30 
init_list_head(struct list_head * list)31 static void init_list_head(struct list_head *list)
32 {
33 	list->next = list;
34 	list->prev = list;
35 }
36 
list_add(struct list_head * new,struct list_head * head)37 static void list_add(struct list_head *new, struct list_head *head)
38 {
39 	head->next->prev = new;
40 	new->next = head->next;
41 	new->prev = head;
42 	head->next = new;
43 }
44 
list_del(struct list_head * entry)45 static void list_del(struct list_head *entry)
46 {
47 	entry->next->prev = entry->prev;
48 	entry->prev->next = entry->next;
49 	entry->next = NULL;
50 	entry->prev = NULL;
51 }
52 
53 #define list_for_each_safe(pos, n, head) \
54 	for (pos = (head)->next, n = pos->next; pos != (head); \
55 		pos = n, n = pos->next)
56 
57 /* Free memory at end of test */
58 
59 static struct list_head malloc_list = LIST_HEAD_INIT(malloc_list);
60 
test_malloc(size_t size)61 void *test_malloc(size_t size)
62 {
63 	struct list_head *block = malloc(sizeof(*block) + size);
64 
65 	if (!block)
66 		return NULL;
67 	list_add(block, &malloc_list);
68 	return block + 1;
69 }
70 
test_calloc(size_t nmemb,size_t size)71 void *test_calloc(size_t nmemb, size_t size)
72 {
73 	void *ptr = test_malloc(nmemb * size);
74 
75 	if (ptr)
76 		memset(ptr, 0, nmemb * size);
77 	return ptr;
78 }
79 
test_strdup(const char * s)80 char *test_strdup(const char *s)
81 {
82 	size_t size = strlen(s) + 1;
83 	char *dup = test_malloc(size);
84 
85 	if (dup)
86 		memcpy(dup, s, size);
87 	return dup;
88 }
89 
test_free(void * ptr)90 void test_free(void *ptr)
91 {
92 	struct list_head *block;
93 
94 	if (!ptr)
95 		return;
96 	block = (struct list_head *)ptr - 1;
97 	list_del(block);
98 	free(block);
99 }
100 
test_realloc(void * ptr,size_t size)101 void *test_realloc(void *ptr, size_t size)
102 {
103 	struct list_head *block = NULL;
104 
105 	if (ptr) {
106 		block = (struct list_head *)ptr - 1;
107 		list_del(block);
108 	}
109 	block = realloc(block, sizeof(*block) + size);
110 	if (!block)
111 		return NULL;
112 	list_add(block, &malloc_list);
113 	return block + 1;
114 }
115 
test_free_all(void)116 static void test_free_all(void)
117 {
118 	struct list_head *block, *next;
119 
120 	list_for_each_safe(block, next, &malloc_list)
121 		free(block);
122 	init_list_head(&malloc_list);
123 }
124 
125 /* Close files at end of test */
126 
127 struct file_node {
128 	struct list_head link;
129 	FILE *fh;
130 	int fd;
131 };
132 
133 static struct list_head file_list = LIST_HEAD_INIT(file_list);
134 
test_open(const char * pathname,int flag,...)135 int test_open(const char *pathname, int flag, ...)
136 {
137 	struct file_node *node;
138 	mode_t mode;
139 
140 	if (flag & O_CREAT) {
141 		va_list ap;
142 		va_start(ap, flag);
143 		mode = va_arg(ap, mode_t);
144 		va_end(ap);
145 	} else {
146 		mode = 0;
147 	}
148 
149 	node = malloc(sizeof(*node));
150 	if (!node)
151 		return -1;
152 
153 	node->fd = open(pathname, flag, mode);
154 	if (node->fd < 0) {
155 		free(node);
156 		return -1;
157 	}
158 
159 	node->fh = NULL;
160 	list_add(&node->link, &file_list);
161 	return node->fd;
162 }
163 
test_socket(int domain,int type,int protocol)164 int test_socket(int domain, int type, int protocol)
165 {
166 	struct file_node *node;
167 
168 	node = malloc(sizeof(*node));
169 	if (!node)
170 		return -1;
171 
172 	node->fd = socket(domain, type, protocol);
173 	if (node->fd < 0) {
174 		free(node);
175 		return -1;
176 	}
177 
178 	node->fh = NULL;
179 	list_add(&node->link, &file_list);
180 	return node->fd;
181 }
182 
test_close(int fd)183 int test_close(int fd)
184 {
185 	struct list_head *head, *next;
186 
187 	if (fd >= 0) {
188 		list_for_each_safe(head, next, &file_list) {
189 			if (((struct file_node *)head)->fd == fd) {
190 				list_del(head);
191 				free(head);
192 				break;
193 			}
194 		}
195 	}
196 
197 	return close(fd);
198 }
199 
test_fopen(const char * path,const char * mode)200 FILE *test_fopen(const char *path, const char *mode)
201 {
202 	struct file_node *node;
203 
204 	node = malloc(sizeof(*node));
205 	if (!node)
206 		return NULL;
207 
208 	node->fh = fopen(path, mode);
209 	if (!node->fh) {
210 		free(node);
211 		return NULL;
212 	}
213 
214 	node->fd = -1;
215 	list_add(&node->link, &file_list);
216 	return node->fh;
217 }
218 
test_fclose(FILE * fh)219 int test_fclose(FILE *fh)
220 {
221 	struct list_head *head, *next;
222 
223 	assert(fh);
224 
225 	list_for_each_safe(head, next, &file_list) {
226 		if (((struct file_node *)head)->fh == fh) {
227 			list_del(head);
228 			free(head);
229 			break;
230 		}
231 	}
232 
233 	return fclose(fh);
234 }
235 
test_close_all(void)236 static void test_close_all(void)
237 {
238 	struct list_head *head, *next;
239 	struct file_node *node;
240 
241 	list_for_each_safe(head, next, &file_list) {
242 		node = (struct file_node *)head;
243 		if (node->fh)
244 			fclose(node->fh);
245 		else
246 			close(node->fd);
247 		free(node);
248 	}
249 	init_list_head(&file_list);
250 }
251 
252 /* Wrap test main function */
253 
254 static jmp_buf test_return;
255 static FILE *orig_stderr;
256 
test_exit(int rc)257 void test_exit(int rc)
258 {
259 	longjmp(test_return, rc + 1);
260 }
261 
test_ioctl(const struct cmd_expect * expect,void * cmd)262 int test_ioctl(const struct cmd_expect *expect, void *cmd)
263 {
264 	int rc;
265 
266 	if (!expect->cmd || *(u32 *)cmd != *(const u32 *)expect->cmd) {
267 		/* We have no idea how long this command structure is */
268 		fprintf(orig_stderr, "Unexpected ioctl: cmd=%#10x\n",
269 			*(u32 *)cmd);
270 		return TEST_IOCTL_MISMATCH;
271 	}
272 
273 	if (memcmp(cmd, expect->cmd, expect->cmd_len)) {
274 		fprintf(orig_stderr, "Expected ioctl structure:\n");
275 		dump_hex(orig_stderr, expect->cmd, expect->cmd_len, 0);
276 		fprintf(orig_stderr, "Actual ioctl structure:\n");
277 		dump_hex(orig_stderr, cmd, expect->cmd_len, 0);
278 		return TEST_IOCTL_MISMATCH;
279 	}
280 
281 	if (expect->resp)
282 		memcpy(cmd, expect->resp, expect->resp_len);
283 	rc = expect->rc;
284 
285 	/* Convert kernel return code according to libc convention */
286 	if (rc >= 0) {
287 		return rc;
288 	} else {
289 		errno = -rc;
290 		return -1;
291 	}
292 }
293 
test_cmdline(const char * args)294 int test_cmdline(const char *args)
295 {
296 	int argc, i;
297 	char **argv;
298 	const char *arg;
299 	size_t len;
300 	int dev_null = -1, orig_stdout_fd = -1, orig_stderr_fd = -1;
301 	int rc;
302 
303 	/* Convert line to argv */
304 	argc = 1;
305 	arg = args;
306 	for (;;) {
307 		len = strcspn(arg, " ");
308 		if (len == 0)
309 			break;
310 		argc++;
311 		if (arg[len] == 0)
312 			break;
313 		arg += len + 1;
314 	}
315 	argv = test_calloc(argc + 1, sizeof(argv[0]));
316 	argv[0] = test_strdup("ethtool");
317 	arg = args;
318 	for (i = 1; i < argc; i++) {
319 		len = strcspn(arg, " ");
320 		argv[i] = test_malloc(len + 1);
321 		memcpy(argv[i], arg, len);
322 		argv[i][len] = 0;
323 		arg += len + 1;
324 	}
325 
326 	dev_null = open("/dev/null", O_RDWR);
327 	if (dev_null < 0) {
328 		perror("open /dev/null");
329 		rc = -1;
330 		goto out;
331 	}
332 
333 	fflush(NULL);
334 	dup2(dev_null, STDIN_FILENO);
335 	if (getenv("TEST_TEST_VERBOSE")) {
336 		orig_stderr = stderr;
337 	} else {
338 		orig_stdout_fd = dup(STDOUT_FILENO);
339 		if (orig_stdout_fd < 0) {
340 			perror("dup stdout");
341 			rc = -1;
342 			goto out;
343 		}
344 		dup2(dev_null, STDOUT_FILENO);
345 		orig_stderr_fd = dup(STDERR_FILENO);
346 		if (orig_stderr_fd < 0) {
347 			perror("dup stderr");
348 			rc = -1;
349 			goto out;
350 		}
351 		orig_stderr = fdopen(orig_stderr_fd, "w");
352 		if (orig_stderr == NULL) {
353 			perror("fdopen orig_stderr_fd");
354 			rc = -1;
355 			goto out;
356 		}
357 		dup2(dev_null, STDERR_FILENO);
358 	}
359 
360 	rc = setjmp(test_return);
361 	rc = rc ? rc - 1 : test_main(argc, argv);
362 
363 out:
364 	fflush(NULL);
365 	if (orig_stderr_fd >= 0) {
366 		dup2(orig_stderr_fd, STDERR_FILENO);
367 		if (orig_stderr)
368 			fclose(orig_stderr);
369 		else
370 			close(orig_stderr_fd);
371 	}
372 	orig_stderr = NULL;
373 	if (orig_stdout_fd >= 0) {
374 		dup2(orig_stdout_fd, STDOUT_FILENO);
375 		close(orig_stdout_fd);
376 	}
377 	if (dev_null >= 0)
378 		close(dev_null);
379 
380 	test_free_all();
381 	test_close_all();
382 	return rc;
383 }
384