1
2 /* You need at least dot.conf 1.0.9 to compile and run this example !! */
3
4 #include <stdio.h> /* fprintf(), stderr */
5 #include <stdlib.h> /* realloc() */
6 #include <string.h> /* strcmp() */
7 /* this example does not work for WIN32 */
8
9 #ifndef WIN32
10 #include <dlfcn.h> /* dlopen(), dlerror(), dlclose() */
11 #include <unistd.h>
12 #endif
13
14 #include <libpool.h>
15 #include <dotconf.h>
16
17 struct config_context {
18 long current_context;
19 char current_end_token[1024];
20
21 pool_t *pool;
22 };
23
24 struct ptr_list {
25 int n_entries;
26 void **entries;
27 };
28
29 static DOTCONF_CB(section_open);
30 static DOTCONF_CB(section_close);
31 static DOTCONF_CB(common_option);
32 static DOTCONF_CB(addmodule_option);
33
34 /*
35 // the last field is used to specify the context needed for
36 // each option. These will be checked at runtime by our contextchecker
37 //
38 */
39 static const configoption_t options[] = {
40 {"AddModule", ARG_LIST, addmodule_option, NULL, 0},
41 {"ToggleOption", ARG_TOGGLE, common_option, NULL, 0},
42 LAST_OPTION
43 };
44
45 /*
46 // The pointer list of dynamic options.
47 // This is necessary to be able to free it up later.
48 //
49 */
50 struct ptr_list memory_junk;
51
52 #define MAX_MODULES 10
53 static void *handles[MAX_MODULES]; /* handles of dynamically loaded modules */
54
context_checker(command_t * cmd,unsigned long option_context)55 const char *context_checker(command_t * cmd, unsigned long option_context)
56 {
57 struct config_context *context = cmd->context;
58
59 if (context->current_context != option_context) {
60 return pool_strcat(context->pool, "Option '", cmd->name,
61 "' not allowed in <",
62 (strlen(context->current_end_token) >
63 2) ? context->current_end_token +
64 2 : "global>", " context", NULL);
65 }
66
67 return NULL;
68 }
69
FUNC_ERRORHANDLER(error_handler)70 FUNC_ERRORHANDLER(error_handler)
71 {
72 fprintf(stderr, "%s:%ld:[error] %s\n",
73 configfile->filename, configfile->line, msg);
74
75 /* continue reading the configfile ; return 1 stop after first error found */
76 return 0;
77 }
78
main(int argc,char * argv[])79 int main(int argc, char *argv[])
80 {
81 configfile_t *configfile;
82 struct config_context context;
83
84 if (argc < 2) {
85 fprintf(stderr, "Usage : %s <configfile>\n", argv[0]);
86 return 1;
87 }
88 context.current_end_token[0] = '\0';
89 context.current_context = 0;
90 context.pool = pool_new(NULL);
91
92 memory_junk.n_entries = 0;
93 memory_junk.entries = 0;
94
95 memset(handles, 0, sizeof(handles));
96
97 configfile = dotconf_create(argv[1], options, (void *)&context,
98 CASE_INSENSITIVE | DUPLICATE_OPTION_NAMES);
99 if (!configfile) {
100 fprintf(stderr, "Error opening configuration file\n");
101 return 1;
102 }
103 configfile->errorhandler = (dotconf_errorhandler_t) error_handler;
104 configfile->contextchecker = (dotconf_contextchecker_t) context_checker;
105
106 if (dotconf_command_loop(configfile) == 0) {
107 fprintf(stderr, "Error reading configuration file\n");
108 return 1;
109 }
110
111 dotconf_cleanup(configfile);
112 pool_free(context.pool);
113
114 /* clean up the possible memjunk which needed to stay in memory */
115 if (memory_junk.n_entries) {
116 int idx;
117 for (idx = 0; idx < memory_junk.n_entries; idx++) {
118 free(memory_junk.entries[idx]);
119 }
120 }
121 free(memory_junk.entries);
122
123 return 0;
124 }
125
DOTCONF_CB(section_open)126 DOTCONF_CB(section_open)
127 {
128 struct config_context *context = (struct config_context *)ctx;
129 const char *old_end_token = context->current_end_token;
130 int prev_context = context->current_context;
131 const char *err = 0;
132
133 context->current_context = (long)cmd->option->info;
134 sprintf(context->current_end_token, "</%s", cmd->name + 1);
135
136 while (!cmd->configfile->eof) {
137 err = dotconf_command_loop_until_error(cmd->configfile);
138 if (!err) {
139 err =
140 pool_strcat(context->pool, "</", cmd->name + 1,
141 " is missing", NULL);
142 break;
143 }
144
145 if (err == context->current_end_token) {
146 break;
147 }
148 dotconf_warning(cmd->configfile, DCLOG_ERR, 0, err);
149 }
150
151 sprintf(context->current_end_token, "%s", old_end_token);
152 context->current_context = prev_context;
153
154 if (err != context->current_end_token) {
155 return err;
156 }
157
158 return NULL;
159 }
160
DOTCONF_CB(section_close)161 DOTCONF_CB(section_close)
162 {
163 struct config_context *context = (struct config_context *)ctx;
164
165 if (!context->current_end_token) {
166 return pool_strcat(context->pool, cmd->name,
167 " without matching <", cmd->name + 2,
168 " section", NULL);
169 }
170
171 if (strcmp(context->current_end_token, cmd->name) != 0) {
172 return pool_strcat(context->pool, "Expected '",
173 context->current_end_token, "' but saw ",
174 cmd->name, NULL);
175 }
176
177 return context->current_end_token;
178 }
179
DOTCONF_CB(common_option)180 DOTCONF_CB(common_option)
181 {
182 fprintf(stderr,
183 "common_option : Option %s called Not doing anything with it...\n",
184 cmd->name);
185 return NULL;
186 }
187
188 /*
189 // We expect option name filename
190 // e.g. AddModule first ./plugins/decision-test.so
191 //
192 // So in the list :
193 // 0 -> name
194 // 1 -> so_filename
195 */
DOTCONF_CB(addmodule_option)196 DOTCONF_CB(addmodule_option)
197 {
198 struct config_context *context = (struct config_context *)ctx;
199 configoption_t *module_options;
200 const char *error = 0;
201 int handle_idx = -1;
202 char filename[FILENAME_MAX] = "";
203 void *shared_object = 0;
204
205 fprintf(stderr, "addmodule_option : Option %s called\n", cmd->name);
206 if (cmd->arg_count < 2) {
207 return pool_strcat(context->pool,
208 "Not enough parameters to option ",
209 cmd->name, " expected 2", NULL);
210 }
211 // load the damn thing
212 for (handle_idx = 0; handle_idx < MAX_MODULES; handle_idx++) {
213 if (handles[handle_idx] == 0) {
214 snprintf(filename, 128, "./%s.so", cmd->data.list[1]);
215 if (access(filename, R_OK) == 0) { /* if file access is permitted */
216 /* load library */
217 handles[handle_idx] =
218 dlopen(filename, RTLD_LAZY);
219 if (!handles[handle_idx]) {
220 fprintf(stderr,
221 "Error opening library: %s\n",
222 dlerror());
223 return "Error opening library";
224 }
225 shared_object = handles[handle_idx];
226 break;
227 } else {
228 return pool_strcat(context->pool,
229 "Can't open file ", filename,
230 NULL);
231 }
232 }
233 }
234 if (handle_idx == MAX_MODULES) {
235 return "Out of handle space. Not loading module\n";
236 }
237 // get the options
238 module_options = dlsym(shared_object, "options");
239 error = dlerror();
240 if (error) {
241 fprintf(stderr,
242 "addmodule_option() : Error finding the options variable in %s p=%p (%s)\n",
243 cmd->data.list[1], shared_object, error);
244 dlclose(shared_object);
245 handles[handle_idx] = 0;
246 return NULL;
247 }
248
249 /*
250 // Scope the options of this module to a <NAME></NAME> block where NAME is
251 // the name that was specified in the configfile.
252 //
253 // The context field holds a unique identifier so we can verify in our
254 // contextchecker that this option belongs to the right scope.
255 //
256 */
257 {
258 char *begin_context_tag =
259 (char *)malloc(strlen(cmd->data.list[1]) + 2 + 1);
260 char *end_context_tag =
261 (char *)malloc(strlen(cmd->data.list[1]) + 3 + 1);
262 configoption_t *scope_options = 0;
263 int opt_idx = -1;
264
265 scope_options =
266 (configoption_t *) malloc(3 * sizeof(configoption_t));
267 if (!scope_options || !begin_context_tag || !end_context_tag) {
268 return "Error allocating memory";
269 }
270 sprintf(begin_context_tag, "<%s>", cmd->data.list[0]);
271 sprintf(end_context_tag, "</%s>", cmd->data.list[0]);
272
273 // create our two extra options (scope begin/end) and a NULL option to close
274 // the list
275 scope_options[0].name = begin_context_tag;
276 scope_options[0].type = ARG_NONE;
277 scope_options[0].callback = section_open;
278 scope_options[0].info = shared_object;
279 scope_options[0].context = CTX_ALL;
280
281 scope_options[1].name = end_context_tag;
282 scope_options[1].type = ARG_NONE;
283 scope_options[1].callback = section_close;
284 scope_options[1].info = NULL;
285 scope_options[1].context = (long)shared_object;
286
287 scope_options[2].name = "";
288 scope_options[2].type = 0;
289 scope_options[2].callback = NULL;
290 scope_options[2].info = NULL;
291 scope_options[2].context = 0;
292
293 /* Set the context field of all options from the module to the identifier */
294 for (opt_idx = 0; strlen(module_options[opt_idx].name);
295 opt_idx++) {
296 module_options[opt_idx].context = (long)shared_object;
297 }
298
299 memory_junk.entries = realloc(memory_junk.entries,
300 (memory_junk.n_entries +
301 3) * sizeof(void *));
302 memory_junk.entries[memory_junk.n_entries++] =
303 begin_context_tag;
304 memory_junk.entries[memory_junk.n_entries++] = end_context_tag;
305 memory_junk.entries[memory_junk.n_entries++] = scope_options;
306
307 dotconf_register_options(cmd->configfile, scope_options);
308 dotconf_register_options(cmd->configfile, module_options);
309 }
310
311 fprintf(stderr, "Successfully loaded module %s (%s)\n",
312 cmd->data.list[1], cmd->data.list[1]);
313
314 return NULL;
315 }
316