1 /* ircd_config.c - NextIRCd
2 * Copyright (C) 2012-2013 Philipp Kreil (pk910)
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "ircd_config.h"
25 static char *config_file = "ircd.conf";
27 struct GlobalConfig global_config;
28 static struct ConfigReloadCallback *config_reload_callbacks = NULL;
30 struct ConfigReloadCallback {
31 confreload_callback_t *callback;
32 struct ConfigReloadCallback *next;
35 #define CONFIG_READ_BUF 512
44 #define ENTRYTYPE_BLOCK 1
45 #define ENTRYTYPE_STRING 2
46 #define ENTRYTYPE_INTEGER 3
52 struct ConfigEntry *elements;
56 struct ConfigEntry *next;
59 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, struct ConfigFile *configfile);
60 static void free_entry_rekursiv(struct ConfigEntry *centry);
61 static int parse_config_block(struct ConfigEntry *centry);
63 #define PARSER_FLAG_ESCAPED 0x01
64 #define PARSER_FLAG_BLOCK 0x02
65 #define PARSER_FLAG_STRING 0x04
66 #define PARSER_FLAG_EXPECT_END 0x08
67 #define PARSER_FLAG_NEXTCHAR 0x10
68 #define PARSER_FLAG_INTEGER 0x20
69 #define PARSER_FLAG_EOBN 0x40 /* End of Block name */
70 #define PARSER_FLAG_COMMAND 0x80
71 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, struct ConfigFile *configfile) {
73 int type = (centry ? 0 : ENTRYTYPE_BLOCK);
76 struct ConfigEntry *sub_entrys = NULL;
77 parse_config_recursive_process:
79 if(flags & PARSER_FLAG_NEXTCHAR) {
84 flags |= PARSER_FLAG_NEXTCHAR;
85 if(flags & PARSER_FLAG_EOBN) {
86 flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR);
87 struct ConfigEntry *new_entry = malloc(sizeof(*new_entry));
88 if (!new_entry) return buffer;
89 new_entry->name = strdup(cbuf);
90 buffer = parse_config_recursive(new_entry, buffer, configfile);
93 new_entry->next = sub_entrys;
95 new_entry->next = NULL;
96 sub_entrys = new_entry;
97 centry->data.elements = sub_entrys;
99 configfile->return_value |= parse_config_block(new_entry);
100 free_entry_rekursiv(new_entry);
103 if(flags & PARSER_FLAG_ESCAPED) {
104 cbuf[cbufpos++] = *buffer;
105 flags &= ~PARSER_FLAG_ESCAPED;
108 if(flags & PARSER_FLAG_EXPECT_END) {
116 if(flags & PARSER_FLAG_STRING) {
118 cbuf[cbufpos] = '\0';
119 flags &= ~PARSER_FLAG_STRING;
120 if(type == ENTRYTYPE_STRING) {
121 flags |= PARSER_FLAG_EXPECT_END;
123 centry->data.str_value = strdup(cbuf);
124 } else if(type == ENTRYTYPE_BLOCK) {
125 flags |= PARSER_FLAG_EOBN;
127 } else if(*buffer == '\\')
128 flags |= PARSER_FLAG_ESCAPED;
130 cbuf[cbufpos++] = *buffer;
133 if(flags & PARSER_FLAG_INTEGER) {
134 if(!isdigit(*buffer)) {
135 cbuf[cbufpos] = '\0';
136 flags &= ~PARSER_FLAG_INTEGER;
137 if(type == ENTRYTYPE_INTEGER) {
138 flags |= PARSER_FLAG_EXPECT_END;
139 /* a pointer should be big enough to store a int value in it ;) */
141 centry->data.int_value = atoi(cbuf);
149 cbuf[cbufpos++] = *buffer;
152 if(flags & PARSER_FLAG_COMMAND) {
153 int found_command = 0;
158 buffer = strchr(buffer, '\r');
160 buffer = strchr(tmp_buffer, '\n');
162 buffer = tmp_buffer + strlen(tmp_buffer)-1;
166 //simple search for the next */
167 buffer = strstr(buffer, "*/")+1;
170 flags &= ~PARSER_FLAG_COMMAND;
176 flags |= PARSER_FLAG_ESCAPED;
179 if(!(flags & PARSER_FLAG_STRING)) {
180 flags |= PARSER_FLAG_COMMAND;
184 flags |= PARSER_FLAG_BLOCK;
185 type = ENTRYTYPE_BLOCK;
188 if(flags & PARSER_FLAG_BLOCK)
189 flags &= ~PARSER_FLAG_BLOCK;
190 flags |= PARSER_FLAG_EXPECT_END;
193 flags |= PARSER_FLAG_STRING;
195 type = ENTRYTYPE_STRING;
213 type = ENTRYTYPE_INTEGER;
214 flags |= PARSER_FLAG_INTEGER;
216 cbuf[cbufpos++] = *buffer;
222 /* try to get more data */
223 if(configfile->pending_bytes) {
224 int read_bytes = configfile->pending_bytes;
225 if(read_bytes > CONFIG_READ_BUF)
226 read_bytes = CONFIG_READ_BUF;
227 read_bytes = fread(configfile->content_buffer, 1, read_bytes, configfile->file);
228 configfile->content_buffer[read_bytes] = '\0';
229 configfile->pending_bytes -= read_bytes;
230 buffer = configfile->content_buffer;
231 goto parse_config_recursive_process;
235 return buffer; //end of the buffer
238 static void free_entry_rekursiv(struct ConfigEntry *centry) {
239 if(centry->type == ENTRYTYPE_BLOCK) {
240 struct ConfigEntry *subentry, *nextentry;
241 for(subentry = centry->data.elements; subentry; subentry = nextentry) {
242 nextentry = subentry->next;
243 free_entry_rekursiv(subentry);
245 } else if(centry->type == ENTRYTYPE_STRING && centry->data.str_value) {
246 free(centry->data.str_value);
252 static int parse_config_port_block(struct ConfigEntry *centry);
254 static int parse_config_block(struct ConfigEntry *centry) {
256 if(!stricmp(centry->name, "Port"))
257 ret |= parse_config_port_block(centry);
259 // ERROR - unknown block name
264 static int parse_config_port_block(struct ConfigEntry *centry) {
265 struct ConfigPortObject *port;
266 struct ConfigEntry *sentry;
268 if(centry->type != ENTRYTYPE_BLOCK)
270 port = calloc(1, sizeof(*port));
271 for(sentry = centry->data.elements; sentry; sentry = sentry->next) {
272 if(!stricmp(sentry->name, "port") && sentry->type == ENTRYTYPE_INTEGER)
273 port->port = sentry->data.int_value;
274 else if(!stricmp(sentry->name, "secure") && sentry->type == ENTRYTYPE_INTEGER)
275 port->secure = (sentry->data.int_value != 0);
276 else if(!stricmp(sentry->name, "server") && sentry->type == ENTRYTYPE_INTEGER)
277 port->server = (sentry->data.int_value != 0);
278 else if(!stricmp(sentry->name, "ip4only") && sentry->type == ENTRYTYPE_INTEGER)
279 port->ip4only = (sentry->data.int_value != 0);
280 else if(!stricmp(sentry->name, "ip6only") && sentry->type == ENTRYTYPE_INTEGER)
281 port->ip6only = (sentry->data.int_value != 0);
282 else if(!stricmp(sentry->name, "bind") && sentry->type == ENTRYTYPE_STRING) {
283 port->bind_addr = sentry->data.str_value;
284 sentry->data.str_value = NULL; //prevents freeing later
286 // ERROR - unknown field in port block
290 if(port->port && !(port->ip4only && port->ip6only)) {
291 port->next = global_config.ports;
292 global_config.ports = port;
301 void init_config(char *configfile) {
303 config_file = configfile;
304 memset(&global_config, 0, sizeof(global_config));
308 void reload_config() {
309 struct ConfigFile config;
310 config.file = fopen(config_file, "rb");
313 printf("Error loading config file.\n");
316 fseek(config.file, 0, SEEK_END);
317 config.pending_bytes = ftell(config.file);
320 char content_buffer[CONFIG_READ_BUF+1];
321 config.content_buffer = content_buffer;
323 int read_bytes = config.pending_bytes;
324 if(read_bytes > CONFIG_READ_BUF)
325 read_bytes = CONFIG_READ_BUF;
326 read_bytes = fread(config.content_buffer, 1, read_bytes, config.file);
327 config.content_buffer[read_bytes] = '\0';
328 config.pending_bytes -= read_bytes;
330 config.return_value = 0;
331 parse_config_recursive(NULL, config.content_buffer, &config);
335 if(!config.return_value) {
336 //call all the callbacks
338 printf("Error loading config file (%d).\n", config.return_value);
342 void reload_config_callback(confreload_callback_t *callback) {
343 struct ConfigReloadCallback *reload_callback = malloc(sizeof(*reload_callback));
344 reload_callback->callback = callback;
345 reload_callback->next = config_reload_callbacks;
346 config_reload_callbacks = reload_callback;