blob: cd63d1d470bbbd167d442c99893f2233bcd0cde2 [file] [log] [blame]
Ben Hutchingscfe91ed2011-10-31 18:42:29 +00001/****************************************************************************
2 * Common test functions for ethtool
3 * Copyright 2011 Solarflare Communications Inc.
4 *
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +00005 * Partly derived from kernel <linux/list.h>.
6 *
Ben Hutchingscfe91ed2011-10-31 18:42:29 +00007 * 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
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +000012#include <assert.h>
Ben Hutchings70943b52012-06-01 23:08:21 +010013#include <errno.h>
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +000014#include <setjmp.h>
15#include <stdarg.h>
Ben Hutchingscfe91ed2011-10-31 18:42:29 +000016#include <stdlib.h>
17#include <string.h>
18#include <sys/fcntl.h>
Ben Hutchingscfe91ed2011-10-31 18:42:29 +000019#include <unistd.h>
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +000020#define TEST_NO_WRAPPERS
Ben Hutchingscfe91ed2011-10-31 18:42:29 +000021#include "internal.h"
22
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +000023/* List utilities */
24
25struct list_head {
26 struct list_head *next, *prev;
27};
28
29#define LIST_HEAD_INIT(name) { &(name), &(name) }
30
31static void init_list_head(struct list_head *list)
32{
33 list->next = list;
34 list->prev = list;
35}
36
37static 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
45static 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
59static struct list_head malloc_list = LIST_HEAD_INIT(malloc_list);
60
61void *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
71void *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
80char *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
90void 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
101void *test_realloc(void *ptr, size_t size)
102{
Maciej Żenczykowski685adc72016-03-11 09:58:19 -0800103 struct list_head *block = NULL;
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000104
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
116static 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
127struct file_node {
128 struct list_head link;
129 FILE *fh;
130 int fd;
131};
132
133static struct list_head file_list = LIST_HEAD_INIT(file_list);
134
135int 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
164int 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
183int 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
200FILE *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
219int 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
236static 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
254static jmp_buf test_return;
Ben Hutchings70943b52012-06-01 23:08:21 +0100255static FILE *orig_stderr;
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000256
257void test_exit(int rc)
258{
259 longjmp(test_return, rc + 1);
260}
261
Ben Hutchings70943b52012-06-01 23:08:21 +0100262int 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
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000294int test_cmdline(const char *args)
295{
296 int argc, i;
297 char **argv;
298 const char *arg;
299 size_t len;
Ben Hutchings70943b52012-06-01 23:08:21 +0100300 int dev_null = -1, orig_stdout_fd = -1, orig_stderr_fd = -1;
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000301 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 }
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000315 argv = test_calloc(argc + 1, sizeof(argv[0]));
316 argv[0] = test_strdup("ethtool");
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000317 arg = args;
318 for (i = 1; i < argc; i++) {
319 len = strcspn(arg, " ");
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000320 argv[i] = test_malloc(len + 1);
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000321 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);
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000334 dup2(dev_null, STDIN_FILENO);
Ben Hutchings70943b52012-06-01 23:08:21 +0100335 if (getenv("TEST_TEST_VERBOSE")) {
336 orig_stderr = stderr;
337 } else {
338 orig_stdout_fd = dup(STDOUT_FILENO);
339 if (orig_stdout_fd < 0) {
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000340 perror("dup stdout");
341 rc = -1;
342 goto out;
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000343 }
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000344 dup2(dev_null, STDOUT_FILENO);
Ben Hutchings70943b52012-06-01 23:08:21 +0100345 orig_stderr_fd = dup(STDERR_FILENO);
346 if (orig_stderr_fd < 0) {
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000347 perror("dup stderr");
348 rc = -1;
349 goto out;
350 }
Ben Hutchings70943b52012-06-01 23:08:21 +0100351 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 }
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000357 dup2(dev_null, STDERR_FILENO);
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000358 }
359
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000360 rc = setjmp(test_return);
361 rc = rc ? rc - 1 : test_main(argc, argv);
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000362
363out:
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000364 fflush(NULL);
Ben Hutchings70943b52012-06-01 23:08:21 +0100365 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);
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000371 }
Ben Hutchings70943b52012-06-01 23:08:21 +0100372 orig_stderr = NULL;
373 if (orig_stdout_fd >= 0) {
374 dup2(orig_stdout_fd, STDOUT_FILENO);
375 close(orig_stdout_fd);
Ben Hutchingsdfacc4a2011-10-31 18:29:35 +0000376 }
377 if (dev_null >= 0)
378 close(dev_null);
379
380 test_free_all();
381 test_close_all();
Ben Hutchingscfe91ed2011-10-31 18:42:29 +0000382 return rc;
383}