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