discover/pxe: Format IPAPPEND mac addresses correctly
[petitboot] / test / parser / utils.c
1
2 #include <assert.h>
3 #include <err.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10
11 #include <talloc/talloc.h>
12 #include <types/types.h>
13 #include <url/url.h>
14
15 #include "device-handler.h"
16 #include "parser.h"
17 #include "resource.h"
18 #include "event.h"
19 #include "platform.h"
20
21 #include "parser-test.h"
22
23 struct p_item {
24         struct list_item list;
25         struct parser *parser;
26 };
27
28 struct test_file {
29         struct discover_device  *dev;
30         enum {
31                 TEST_FILE,
32                 TEST_DIR,
33         }                       type;
34         const char              *name;
35         void                    *data;
36         int                     size;
37         struct list_item        list;
38 };
39
40 STATIC_LIST(parsers);
41
42 void __register_parser(struct parser *parser)
43 {
44         struct p_item* i = talloc(NULL, struct p_item);
45
46         i->parser = parser;
47         list_add(&parsers, &i->list);
48 }
49
50 static void __attribute__((destructor)) __cleanup_parsers(void)
51 {
52         struct p_item *item, *tmp;
53
54         list_for_each_entry_safe(&parsers, item, tmp, list)
55                 talloc_free(item);
56 }
57
58 static struct discover_device *test_create_device_simple(
59                 struct parser_test *test)
60 {
61         static int dev_idx;
62         char name[10];
63
64         sprintf(name, "__test%d", dev_idx++);
65
66         return test_create_device(test, name);
67 }
68
69 struct discover_device *test_create_device(struct parser_test *test,
70                 const char *name)
71 {
72         struct discover_device *dev;
73
74         dev = discover_device_create(test->handler, name);
75
76         dev->device->id = talloc_strdup(dev, name);
77         dev->device_path = talloc_asprintf(dev, "/dev/%s", name);
78         dev->mount_path = talloc_asprintf(dev, "/test/mount/%s", name);
79         dev->mounted = true;
80
81         return dev;
82 }
83
84 static struct discover_context *test_create_context(struct parser_test *test)
85 {
86         struct discover_context *ctx;
87
88         ctx = talloc_zero(test, struct discover_context);
89         assert(ctx);
90
91         list_init(&ctx->boot_options);
92         ctx->device = test_create_device_simple(test);
93         ctx->test_data = test;
94         device_handler_add_device(test->handler, ctx->device);
95
96         return ctx;
97 }
98
99 /* define our own test platform */
100 static bool test_platform_probe(struct platform *p __attribute__((unused)),
101                 void *ctx __attribute__((unused)))
102 {
103         return true;
104 }
105
106 struct platform test_platform = {
107         .name = "test",
108         .probe = test_platform_probe,
109 };
110
111 register_platform(test_platform);
112
113 struct parser_test *test_init(void)
114 {
115         struct parser_test *test;
116
117         test = talloc_zero(NULL, struct parser_test);
118         platform_init(NULL);
119         test->handler = device_handler_init(NULL, NULL, 0);
120         test->ctx = test_create_context(test);
121         list_init(&test->files);
122
123         return test;
124 }
125
126 void test_fini(struct parser_test *test)
127 {
128         device_handler_destroy(test->handler);
129         talloc_free(test);
130         platform_fini();
131 }
132
133 void __test_read_conf_data(struct parser_test *test,
134                 struct discover_device *dev, const char *conf_file,
135                 const char *buf, size_t len)
136 {
137         test_add_file_data(test, dev, conf_file, buf, len);
138 }
139
140 void test_read_conf_file(struct parser_test *test, const char *filename,
141                 const char *conf_file)
142 {
143         struct stat stat;
144         size_t size;
145         char *path;
146         int fd, rc;
147         char *buf;
148
149         path = talloc_asprintf(test, "%s/%s", TEST_CONF_BASE, filename);
150
151         fd = open(path, O_RDONLY);
152         if (fd < 0)
153                 err(EXIT_FAILURE, "Can't open test conf file %s\n", path);
154
155         rc = fstat(fd, &stat);
156         assert(!rc);
157         (void)rc;
158
159         size = stat.st_size;
160         buf = talloc_array(test, char, size + 1);
161
162         rc = read(fd, buf, size);
163         assert(rc == (ssize_t)size);
164
165         *(buf + size) = '\0';
166
167         close(fd);
168         talloc_free(path);
169
170         test_add_file_data(test, test->ctx->device, conf_file, buf, size);
171 }
172
173 void test_add_file_data(struct parser_test *test, struct discover_device *dev,
174                 const char *filename, const void *data, int size)
175 {
176         struct test_file *file;
177
178         file = talloc_zero(test, struct test_file);
179         file->type = TEST_FILE;
180         file->dev = dev;
181         file->name = filename;
182         file->data = talloc_memdup(test, data, size);
183         file->size = size;
184         list_add(&test->files, &file->list);
185 }
186
187 void test_add_dir(struct parser_test *test, struct discover_device *dev,
188                 const char *dirname)
189 {
190         struct test_file *file;
191
192         file = talloc_zero(test, struct test_file);
193         file->type = TEST_DIR;
194         file->dev = dev;
195         file->name = dirname;
196         list_add(&test->files, &file->list);
197 }
198
199 void test_set_event_source(struct parser_test *test)
200 {
201         test->ctx->event = talloc_zero(test->ctx, struct event);
202 }
203
204 void test_set_event_param(struct event *event, const char *name,
205                 const char *value)
206 {
207         event_set_param(event, name, value);
208 }
209
210 void test_set_event_device(struct event *event, const char *dev)
211 {
212         event->device = talloc_strdup(event, dev);
213 }
214
215 int parser_request_file(struct discover_context *ctx,
216                 struct discover_device *dev, const char *filename,
217                 char **buf, int *len)
218 {
219         struct parser_test *test = ctx->test_data;
220         struct test_file *file;
221         char *tmp;
222
223         list_for_each_entry(&test->files, file, list) {
224                 if (file->dev != dev)
225                         continue;
226                 if (strcmp(file->name, filename))
227                         continue;
228                 if (file->type != TEST_FILE)
229                         continue;
230
231                 /* the read_file() interface always adds a trailing null
232                  * for string-safety; do the same here */
233                 tmp = talloc_array(test, char, file->size + 1);
234                 memcpy(tmp, file->data, file->size);
235                 tmp[file->size] = '\0';
236                 *buf = tmp;
237                 *len = file->size;
238                 return 0;
239         }
240
241         return -1;
242 }
243
244 int parser_check_dir(struct discover_context *ctx,
245                 struct discover_device *dev, const char *dirname)
246 {
247         struct parser_test *test = ctx->test_data;
248         struct test_file *file;
249
250         printf("%s: %s\n", __func__, dirname);
251
252         list_for_each_entry(&test->files, file, list) {
253                 if (file->dev != dev)
254                         continue;
255                 if (strcmp(file->name, dirname))
256                         continue;
257                 return file->type == TEST_DIR ? 0 : -1;
258         }
259
260         return -1;
261 }
262
263 int parser_replace_file(struct discover_context *ctx,
264                 struct discover_device *dev, const char *filename,
265                 char *buf, int len)
266 {
267         struct parser_test *test = ctx->test_data;
268         struct test_file *f, *file;
269
270         list_for_each_entry(&test->files, f, list) {
271                 if (f->dev != dev)
272                         continue;
273                 if (strcmp(f->name, filename))
274                         continue;
275
276                 file = f;
277                 break;
278         }
279
280         if (!file) {
281                 file = talloc_zero(test, struct test_file);
282                 file->dev = dev;
283                 file->name = filename;
284                 list_add(&test->files, &file->list);
285         }
286
287         file->data = talloc_memdup(test, buf, len);
288         file->size = len;
289         return 0;
290 }
291
292 int parser_request_url(struct discover_context *ctx, struct pb_url *url,
293                 char **buf, int *len)
294 {
295         struct parser_test *test = ctx->test_data;
296         struct test_file *file;
297         char *tmp;
298
299         list_for_each_entry(&test->files, file, list) {
300                 if (file->dev)
301                         continue;
302
303                 if (strcmp(file->name, url->full))
304                         continue;
305
306                 /* the read_file() interface always adds a trailing null
307                  * for string-safety; do the same here */
308                 tmp = talloc_array(test, char, file->size + 1);
309                 memcpy(tmp, file->data, file->size);
310                 tmp[file->size] = '\0';
311                 *buf = tmp;
312                 *len = file->size;
313                 return 0;
314         }
315
316         return -1;
317 }
318
319 int test_run_parser(struct parser_test *test, const char *parser_name)
320 {
321         struct p_item* i;
322
323         list_for_each_entry(&parsers, i, list) {
324                 if (strcmp(i->parser->name, parser_name))
325                         continue;
326                 test->ctx->parser = i->parser;
327                 return i->parser->parse(test->ctx);
328         }
329
330         errx(EXIT_FAILURE, "%s: parser '%s' not found", __func__, parser_name);
331 }
332
333 bool resource_resolve(struct device_handler *handler, struct parser *parser,
334                 struct resource *resource)
335 {
336         if (!resource)
337                 return true;
338         if (resource->resolved)
339                 return true;
340
341         assert(parser);
342         assert(parser->resolve_resource);
343
344         return parser->resolve_resource(handler, resource);
345 }
346
347 void boot_option_resolve(struct device_handler *handler,
348                 struct discover_boot_option *opt)
349 {
350         resource_resolve(handler, opt->source, opt->boot_image);
351         resource_resolve(handler, opt->source, opt->initrd);
352         resource_resolve(handler, opt->source, opt->icon);
353 }
354
355 void test_hotplug_device(struct parser_test *test, struct discover_device *dev)
356 {
357         struct discover_boot_option *opt;
358
359         device_handler_add_device(test->handler, dev);
360
361         list_for_each_entry(&test->ctx->boot_options, opt, list)
362                 boot_option_resolve(test->handler, opt);
363 }
364
365 void test_remove_device(struct parser_test *test, struct discover_device *dev)
366 {
367         struct discover_boot_option *opt, *tmp;
368
369         if (dev == test->ctx->device) {
370                 list_for_each_entry_safe(&test->ctx->boot_options,
371                                 opt, tmp, list) {
372                         list_remove(&opt->list);
373                         talloc_free(opt);
374                 }
375         }
376
377         device_handler_remove(test->handler, dev);
378 }
379
380 struct discover_boot_option *get_boot_option(struct discover_context *ctx,
381                 int idx)
382 {
383         struct discover_boot_option *opt;
384         int i = 0;
385
386         list_for_each_entry(&ctx->boot_options, opt, list) {
387                 if (i++ == idx)
388                         return opt;
389         }
390
391         assert(0);
392
393         return NULL;
394 }
395
396 void __check_boot_option_count(struct discover_context *ctx, int count,
397                 const char *file, int line)
398 {
399         struct discover_boot_option *opt;
400         int defaults = 0, i = 0;
401
402         list_for_each_entry(&ctx->boot_options, opt, list) {
403                 i++;
404                 if (opt->option->is_default)
405                         defaults++;
406         }
407
408         if (defaults > 1) {
409                 fprintf(stderr, "%s:%d: parser returned multiple default "
410                                 "options\n", file, line);
411                 exit(EXIT_FAILURE);
412         }
413
414         if (i == count)
415                 return;
416
417         fprintf(stderr, "%s:%d: boot option count check failed\n", file, line);
418         fprintf(stderr, "expected %d options, got %d:\n", count, i);
419
420         i = 1;
421         list_for_each_entry(&ctx->boot_options, opt, list)
422                 fprintf(stderr, "  %2d: %s [%s]\n", i++, opt->option->name,
423                                 opt->option->id);
424
425         exit(EXIT_FAILURE);
426 }
427
428 void __check_args(struct discover_boot_option *opt, const char *args,
429                 const char *file, int line)
430 {
431         int rc;
432
433         if (!opt->option->boot_args && !args)
434                 return;
435
436         if (!opt->option->boot_args) {
437                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
438                 fprintf(stderr, "  no arguments parsed\n");
439                 fprintf(stderr, "  expected '%s'\n", args);
440                 exit(EXIT_FAILURE);
441         }
442
443         rc = strcmp(opt->option->boot_args, args);
444         if (rc) {
445                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
446                 fprintf(stderr, "  got      '%s'\n", opt->option->boot_args);
447                 fprintf(stderr, "  expected '%s'\n", args);
448                 exit(EXIT_FAILURE);
449         }
450 }
451
452 void __check_name(struct discover_boot_option *opt, const char *name,
453                 const char *file, int line)
454 {
455         int rc;
456
457         rc = strcmp(opt->option->name, name);
458         if (rc) {
459                 fprintf(stderr, "%s:%d: name check failed\n", file, line);
460                 fprintf(stderr, "  got      '%s'\n", opt->option->name);
461                 fprintf(stderr, "  expected '%s'\n", name);
462                 exit(EXIT_FAILURE);
463         }
464 }
465
466 void __check_is_default(struct discover_boot_option *opt,
467                 const char *file, int line)
468 {
469         if (opt->option->is_default)
470                 return;
471
472         fprintf(stderr, "%s:%d: default check failed\n", file, line);
473         exit(EXIT_FAILURE);
474 }
475
476 void __check_resolved_local_resource(struct resource *res,
477                 struct discover_device *dev, const char *local_path,
478                 const char *file, int line)
479 {
480         const char *exp_url, *got_url;
481
482         if (!res)
483                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
484
485         if (!res->resolved)
486                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
487                                 file, line);
488
489         exp_url = talloc_asprintf(res, "file://%s%s",
490                         dev->mount_path, local_path);
491         got_url = pb_url_to_string(res->url);
492
493         if (strcmp(got_url, exp_url)) {
494                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
495                 fprintf(stderr, "  got      '%s'\n", got_url);
496                 fprintf(stderr, "  expected '%s'\n", exp_url);
497                 exit(EXIT_FAILURE);
498         }
499 }
500
501 void __check_resolved_url_resource(struct resource *res,
502                 const char *url, const char *file, int line)
503 {
504         char *res_url;
505
506         if (!res)
507                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
508
509         if (!res->resolved)
510                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
511                                 file, line);
512
513         res_url = pb_url_to_string(res->url);
514         if (strcmp(url, res_url)) {
515                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
516                 fprintf(stderr, "  got      '%s'\n", res_url);
517                 fprintf(stderr, "  expected '%s'\n", url);
518                 exit(EXIT_FAILURE);
519         }
520 }
521 void __check_unresolved_resource(struct resource *res,
522                 const char *file, int line)
523 {
524         if (!res)
525                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
526
527         if (res->resolved)
528                 errx(EXIT_FAILURE, "%s:%d: Resource is resolved", file, line);
529 }
530
531 void __check_not_present_resource(struct resource *res,
532                 const char *file, int line)
533 {
534         if (res)
535                 errx(EXIT_FAILURE, "%s:%d: Resource present", file, line);
536 }
537
538 static void dump_file_data(const void *buf, int len)
539 {
540         int i, j, hex_len = strlen("00 ");
541         const int row_len = 16;
542
543         for (i = 0; i < len; i += row_len) {
544                 char hbuf[row_len * hex_len + 1];
545                 char cbuf[row_len + strlen("|") + 1];
546
547                 for (j = 0; (j < row_len) && ((i+j) < len); j++) {
548                         char c = ((const char *)buf)[i + j];
549
550                         snprintf(hbuf + j * hex_len, hex_len + 1, "%02x ", c);
551
552                         if (!isprint(c))
553                                 c = '.';
554
555                         snprintf(cbuf + j, hex_len + 1, "%c", c);
556                 }
557
558                 strcat(cbuf, "|");
559
560                 fprintf(stderr, "%08x  %*s |%s\n", i,
561                                 0 - (int)sizeof(hbuf) + 1, hbuf, cbuf);
562         }
563 }
564
565 void __check_file_contents(struct parser_test *test,
566                 struct discover_device *dev, const char *filename,
567                 const char *buf, int len,
568                 const char *srcfile, int srcline)
569 {
570         struct test_file *f, *file = NULL;
571
572         list_for_each_entry(&test->files, f, list) {
573                 if (f->dev != dev)
574                         continue;
575                 if (strcmp(f->name, filename))
576                         continue;
577
578                 file = f;
579                 break;
580         }
581
582         if (!file)
583                 errx(EXIT_FAILURE, "%s:%d: File '%s' not found",
584                                 srcfile, srcline, filename);
585
586         if (file->size != len || memcmp(file->data, buf, len)) {
587                 fprintf(stderr, "%s:%d: File '%s' data/size mismatch\n",
588                                 srcfile, srcline, filename);
589                 fprintf(stderr, "Expected:\n");
590                 dump_file_data(buf, len);
591                 fprintf(stderr, "Got:\n");
592                 dump_file_data(file->data, file->size);
593                 exit(EXIT_FAILURE);
594         }
595 }