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