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