diff options
author | Florian Dold <florian@dold.me> | 2021-07-28 14:31:38 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-07-28 15:27:39 +0200 |
commit | 7615d46b09275383bd244a0ef1d94b3a77559b88 (patch) | |
tree | f6452173470ae80b186a4ee6da24d1d88d5033f2 /src/util/configuration.c | |
parent | 391794a460140192b3466765ebc63797cea44000 (diff) | |
download | gnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.tar.gz gnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.zip |
implement config inline globbing
Diffstat (limited to 'src/util/configuration.c')
-rw-r--r-- | src/util/configuration.c | 259 |
1 files changed, 209 insertions, 50 deletions
diff --git a/src/util/configuration.c b/src/util/configuration.c index 4a1af10d3..da9cdb924 100644 --- a/src/util/configuration.c +++ b/src/util/configuration.c | |||
@@ -230,6 +230,120 @@ GNUNET_CONFIGURATION_parse_and_run (const char *filename, | |||
230 | return ret; | 230 | return ret; |
231 | } | 231 | } |
232 | 232 | ||
233 | struct InlineGlobClosure | ||
234 | { | ||
235 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
236 | }; | ||
237 | |||
238 | /** | ||
239 | * Function called with a filename. | ||
240 | * | ||
241 | * @param cls closure | ||
242 | * @param filename complete filename (absolute path) | ||
243 | * @return #GNUNET_OK to continue to iterate, | ||
244 | * #GNUNET_NO to stop iteration with no error, | ||
245 | * #GNUNET_SYSERR to abort iteration with error! | ||
246 | */ | ||
247 | static int | ||
248 | inline_glob_cb (void *cls, | ||
249 | const char *filename) | ||
250 | { | ||
251 | struct InlineGlobClosure *igc = cls; | ||
252 | |||
253 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
254 | "Reading globbed config file '%s'\n", | ||
255 | filename); | ||
256 | |||
257 | if (GNUNET_OK != | ||
258 | GNUNET_CONFIGURATION_parse (igc->cfg, | ||
259 | filename)) | ||
260 | { | ||
261 | return GNUNET_SYSERR; | ||
262 | } | ||
263 | return GNUNET_OK; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * Handle an inline directive. | ||
268 | * | ||
269 | * @returns #GNUNET_SYSERR on error, #GNUNET_OK otherwise | ||
270 | */ | ||
271 | enum GNUNET_GenericReturnValue | ||
272 | handle_inline (struct GNUNET_CONFIGURATION_Handle *cfg, | ||
273 | const char *path_or_glob, | ||
274 | bool path_is_glob, | ||
275 | const char *restrict_section, | ||
276 | const char *source_filename) | ||
277 | { | ||
278 | char *inline_path; | ||
279 | |||
280 | /* We support the section restriction only for non-globs */ | ||
281 | GNUNET_assert (! (path_is_glob && (NULL != restrict_section))); | ||
282 | |||
283 | if (NULL == source_filename) | ||
284 | { | ||
285 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
286 | "Refusing to parse inline configurations, " | ||
287 | "not allowed without source filename!\n"); | ||
288 | return GNUNET_SYSERR; | ||
289 | } | ||
290 | if ('/' == *path_or_glob) | ||
291 | inline_path = GNUNET_strdup (path_or_glob); | ||
292 | else | ||
293 | { | ||
294 | /* We compute the canonical, absolute path first, | ||
295 | so that relative imports resolve properly with symlinked | ||
296 | config files. */ | ||
297 | char *source_realpath; | ||
298 | char *endsep; | ||
299 | |||
300 | source_realpath = realpath (source_filename, | ||
301 | NULL); | ||
302 | if (NULL == source_realpath) | ||
303 | { | ||
304 | /* Couldn't even resolve path of base dir. */ | ||
305 | GNUNET_break (0); | ||
306 | /* failed to parse included config */ | ||
307 | return GNUNET_SYSERR; | ||
308 | } | ||
309 | endsep = strrchr (source_realpath, '/'); | ||
310 | GNUNET_assert (NULL != endsep); | ||
311 | *endsep = '\0'; | ||
312 | GNUNET_asprintf (&inline_path, | ||
313 | "%s/%s", | ||
314 | source_realpath, | ||
315 | path_or_glob); | ||
316 | free (source_realpath); | ||
317 | } | ||
318 | if (path_is_glob) | ||
319 | { | ||
320 | int nret; | ||
321 | struct InlineGlobClosure igc = { | ||
322 | .cfg = cfg, | ||
323 | }; | ||
324 | |||
325 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
326 | "processing config glob '%s'\n", | ||
327 | inline_path); | ||
328 | |||
329 | nret = GNUNET_DISK_glob (inline_path, inline_glob_cb, &igc); | ||
330 | if (-1 == nret) | ||
331 | { | ||
332 | GNUNET_free (inline_path); | ||
333 | return GNUNET_SYSERR; | ||
334 | } | ||
335 | } | ||
336 | else if (GNUNET_OK != | ||
337 | GNUNET_CONFIGURATION_parse (cfg, | ||
338 | inline_path)) | ||
339 | { | ||
340 | GNUNET_free (inline_path); | ||
341 | return GNUNET_SYSERR; | ||
342 | } | ||
343 | GNUNET_free (inline_path); | ||
344 | return GNUNET_OK; | ||
345 | } | ||
346 | |||
233 | 347 | ||
234 | enum GNUNET_GenericReturnValue | 348 | enum GNUNET_GenericReturnValue |
235 | GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | 349 | GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, |
@@ -237,28 +351,27 @@ GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | |||
237 | size_t size, | 351 | size_t size, |
238 | const char *source_filename) | 352 | const char *source_filename) |
239 | { | 353 | { |
240 | char *line; | ||
241 | char *line_orig; | ||
242 | size_t line_size; | 354 | size_t line_size; |
243 | char *pos; | ||
244 | unsigned int nr; | 355 | unsigned int nr; |
245 | size_t r_bytes; | 356 | size_t r_bytes; |
246 | size_t to_read; | 357 | size_t to_read; |
247 | size_t i; | ||
248 | int emptyline; | ||
249 | enum GNUNET_GenericReturnValue ret; | 358 | enum GNUNET_GenericReturnValue ret; |
250 | char *section; | 359 | char *section; |
251 | char *eq; | 360 | char *eq; |
252 | char *tag; | 361 | char *tag; |
253 | char *value; | 362 | char *value; |
363 | char *line_orig = NULL; | ||
254 | 364 | ||
255 | ret = GNUNET_OK; | 365 | ret = GNUNET_OK; |
256 | section = GNUNET_strdup (""); | 366 | section = NULL; |
257 | nr = 0; | 367 | nr = 0; |
258 | r_bytes = 0; | 368 | r_bytes = 0; |
259 | line_orig = NULL; | ||
260 | while (r_bytes < size) | 369 | while (r_bytes < size) |
261 | { | 370 | { |
371 | char *pos; | ||
372 | char *line; | ||
373 | bool emptyline; | ||
374 | |||
262 | GNUNET_free (line_orig); | 375 | GNUNET_free (line_orig); |
263 | /* fgets-like behaviour on buffer */ | 376 | /* fgets-like behaviour on buffer */ |
264 | to_read = size - r_bytes; | 377 | to_read = size - r_bytes; |
@@ -280,7 +393,7 @@ GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | |||
280 | nr++; | 393 | nr++; |
281 | /* tabs and '\r' are whitespace */ | 394 | /* tabs and '\r' are whitespace */ |
282 | emptyline = GNUNET_YES; | 395 | emptyline = GNUNET_YES; |
283 | for (i = 0; i < line_size; i++) | 396 | for (size_t i = 0; i < line_size; i++) |
284 | { | 397 | { |
285 | if (line[i] == '\t') | 398 | if (line[i] == '\t') |
286 | line[i] = ' '; | 399 | line[i] = ' '; |
@@ -294,7 +407,7 @@ GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | |||
294 | continue; | 407 | continue; |
295 | 408 | ||
296 | /* remove tailing whitespace */ | 409 | /* remove tailing whitespace */ |
297 | for (i = line_size - 1; | 410 | for (size_t i = line_size - 1; |
298 | (i >= 1) && (isspace ((unsigned char) line[i])); | 411 | (i >= 1) && (isspace ((unsigned char) line[i])); |
299 | i--) | 412 | i--) |
300 | line[i] = '\0'; | 413 | line[i] = '\0'; |
@@ -308,60 +421,103 @@ GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | |||
308 | ('%' == line[0]) ) | 421 | ('%' == line[0]) ) |
309 | continue; | 422 | continue; |
310 | 423 | ||
311 | /* handle special "@INLINE@" directive */ | 424 | /* Handle special directives. */ |
312 | if (0 == strncasecmp (line, | 425 | if ('@' == line[0]) |
313 | "@INLINE@ ", | ||
314 | strlen ("@INLINE@ "))) | ||
315 | { | 426 | { |
316 | char *inline_path; | 427 | char *end = strchr (line + 1, '@'); |
428 | char *directive; | ||
429 | enum GNUNET_GenericReturnValue directive_ret; | ||
317 | 430 | ||
318 | if (NULL == source_filename) | 431 | if (NULL == end) |
319 | { | 432 | { |
320 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 433 | LOG (GNUNET_ERROR_TYPE_WARNING, |
321 | "Refusing to parse @INLINE@ configurations, " | 434 | _ ("Bad directive in line %u\n"), |
322 | "not allowed without source filename!\n"); | 435 | nr); |
323 | ret = GNUNET_SYSERR; | 436 | ret = GNUNET_SYSERR; |
324 | break; | 437 | break; |
325 | } | 438 | } |
326 | /* FIXME: also trim space and end of line comment? */ | 439 | *end = '\0'; |
327 | value = &line[strlen ("@INLINE@ ")]; | 440 | directive = line + 1; |
328 | if ('/' == *value) | 441 | |
329 | inline_path = GNUNET_strdup (value); | 442 | if (0 == strcasecmp (directive, "INLINE")) |
330 | else | ||
331 | { | 443 | { |
332 | /* We compute the canonical, absolute path first, | 444 | const char *path = end + 1; |
333 | so that relative imports resolve properly with symlinked | 445 | |
334 | config files. */ | 446 | /* Skip space before path */ |
335 | char *source_realpath; | 447 | for (; isspace (*path); path++) |
336 | char *endsep; | 448 | ; |
337 | 449 | ||
338 | source_realpath = realpath (source_filename, | 450 | directive_ret = handle_inline (cfg, |
339 | NULL); | 451 | path, |
340 | if (NULL == source_realpath) | 452 | false, |
453 | NULL, | ||
454 | source_filename); | ||
455 | } | ||
456 | else if (0 == strcasecmp (directive, "INLINE-MATCHING")) | ||
457 | { | ||
458 | const char *path = end + 1; | ||
459 | |||
460 | /* Skip space before path */ | ||
461 | for (; isspace (*path); path++) | ||
462 | ; | ||
463 | |||
464 | directive_ret = handle_inline (cfg, | ||
465 | path, | ||
466 | true, | ||
467 | NULL, | ||
468 | source_filename); | ||
469 | } | ||
470 | else if (0 == strcasecmp (directive, "INLINE-SECRET")) | ||
471 | { | ||
472 | const char *secname = end + 1; | ||
473 | const char *path; | ||
474 | const char *secname_end; | ||
475 | |||
476 | /* Skip space before secname */ | ||
477 | for (; isspace (*secname); secname++) | ||
478 | ; | ||
479 | |||
480 | secname_end = strchr (secname, ' '); | ||
481 | |||
482 | if (NULL == secname_end) | ||
341 | { | 483 | { |
342 | /* Couldn't even resolve path of base dir. */ | 484 | LOG (GNUNET_ERROR_TYPE_WARNING, |
343 | GNUNET_break (0); | 485 | _ ("Bad inline-secret directive in line %u\n"), |
344 | ret = GNUNET_SYSERR; /* failed to parse included config */ | 486 | nr); |
487 | ret = GNUNET_SYSERR; | ||
345 | break; | 488 | break; |
346 | } | 489 | } |
347 | endsep = strrchr (source_realpath, '/'); | 490 | secname_end = '\0'; |
348 | GNUNET_assert (NULL != endsep); | 491 | path = secname_end + 1; |
349 | *endsep = '\0'; | 492 | |
350 | GNUNET_asprintf (&inline_path, | 493 | /* Skip space before path */ |
351 | "%s/%s", | 494 | for (; isspace (*path); path++) |
352 | source_realpath, | 495 | ; |
353 | value); | 496 | |
354 | free (source_realpath); | 497 | directive_ret = handle_inline (cfg, |
498 | path, | ||
499 | false, | ||
500 | secname, | ||
501 | source_filename); | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
506 | _ ("Unknown or malformed directive '%s' in line %u\n"), | ||
507 | directive, | ||
508 | nr); | ||
509 | ret = GNUNET_SYSERR; | ||
510 | break; | ||
355 | } | 511 | } |
356 | if (GNUNET_OK != | 512 | if (GNUNET_OK != directive_ret) |
357 | GNUNET_CONFIGURATION_parse (cfg, | ||
358 | inline_path)) | ||
359 | { | 513 | { |
360 | GNUNET_free (inline_path); | 514 | LOG (GNUNET_ERROR_TYPE_WARNING, |
361 | ret = GNUNET_SYSERR; /* failed to parse included config */ | 515 | _ ("Bad directive '%s' in line %u\n"), |
516 | directive, | ||
517 | nr); | ||
518 | ret = GNUNET_SYSERR; | ||
362 | break; | 519 | break; |
363 | } | 520 | } |
364 | GNUNET_free (inline_path); | ||
365 | continue; | 521 | continue; |
366 | } | 522 | } |
367 | if (('[' == line[0]) && (']' == line[line_size - 1])) | 523 | if (('[' == line[0]) && (']' == line[line_size - 1])) |
@@ -375,10 +531,13 @@ GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, | |||
375 | } | 531 | } |
376 | if (NULL != (eq = strchr (line, '='))) | 532 | if (NULL != (eq = strchr (line, '='))) |
377 | { | 533 | { |
534 | size_t i; | ||
535 | |||
378 | /* tag = value */ | 536 | /* tag = value */ |
379 | tag = GNUNET_strndup (line, eq - line); | 537 | tag = GNUNET_strndup (line, eq - line); |
380 | /* remove tailing whitespace */ | 538 | /* remove tailing whitespace */ |
381 | for (i = strlen (tag) - 1; (i >= 1) && (isspace ((unsigned char) tag[i])); | 539 | for (i = strlen (tag) - 1; |
540 | (i >= 1) && (isspace ((unsigned char) tag[i])); | ||
382 | i--) | 541 | i--) |
383 | tag[i] = '\0'; | 542 | tag[i] = '\0'; |
384 | 543 | ||