summaryrefslogtreecommitdiffstats
path: root/src/config-load.c
blob: 92abca0b22133b625d061f6936fd096f2d7dec6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "config-load.h"
#include "config-process.h"
#include "device.h"
#include "util.h"

#include <libubox/avl-cmp.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef struct _load_ctx {
	struct avl_tree subtypes;
	struct avl_tree devices;
} load_ctx_t;

static const char * extname(const char *filename) {
	const char *dot = strrchr(filename, '.');
	return dot ? (dot+1) : NULL;
}

static bool isfile(int fd) {
	struct stat buf;
	if (fstat(fd, &buf) < 0)
		return false;

	return (buf.st_mode & S_IFMT) == S_IFREG;
}

static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
	const char *ext = extname(filename);
	if (!ext)
		return true;

	struct avl_tree *target_tree;
	if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0)
		target_tree = &ctx->subtypes;
	else if (get_device_type(ext))
		target_tree = &ctx->devices;
	else
		return true;
	
	int fd = openat(dirfd, filename, O_RDONLY);
	if (fd < 0)
		return false;
	
	if (!isfile(fd)) {
		close(fd);
		return true;
	}

	FILE *f = fdopen(fd, "r");
	if (!f) {
		close(fd);
		return false;
	}
	
	ini_file_t *data = read_ini_file(f);
	fclose(f);

	if (!data)
		return false;
	
	config_object_t *obj = calloc(1, sizeof(*obj));
	if (!obj) {
		free_ini_file(data);
		return false;
	}
	obj->data = data;

	char *name = strndup(filename, (ext - filename) - 1);
	if (!name) {
		config_object_free(obj);
		return false;
	}
	NODE_NAME(obj) = name;

	obj->type = strdup(ext);
	if (!obj->type) {
		config_object_free(obj);
		return false;
	}

	avl_insert(target_tree, &obj->node);

	return true;
}

static bool read_config_dir(load_ctx_t *ctx, const char *path) {
	DIR *dir = opendir(path);
	if (!dir)
		return false;
	
	int fd = dirfd(dir);

	struct dirent *ent;
	while ((ent = readdir(dir)) != NULL)
		read_config_file(ctx, fd, ent->d_name);

	closedir(dir);

	return true;
}

bool read_config(const char *path) {
	load_ctx_t ctx;
	avl_init(&ctx.subtypes, avl_strcmp, true, NULL);
	avl_init(&ctx.devices, avl_strcmp, true, NULL);
	
	bool ret = read_config_dir(&ctx, path);

	struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes);
	struct avl_tree *devices = config_process_devices(&ctx.devices);

	free(subtypes);

	device_t *dev, *tmp;
	avl_remove_all_elements(devices, dev, node, tmp)
		dev->type->free_device(dev);

	free(devices);

	return ret;
}