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 | |
parent | 391794a460140192b3466765ebc63797cea44000 (diff) | |
download | gnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.tar.gz gnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.zip |
implement config inline globbing
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/configuration.c | 259 | ||||
-rw-r--r-- | src/util/disk.c | 139 |
2 files changed, 347 insertions, 51 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 | ||
diff --git a/src/util/disk.c b/src/util/disk.c index 3bafe311d..1b909f13e 100644 --- a/src/util/disk.c +++ b/src/util/disk.c | |||
@@ -882,6 +882,143 @@ GNUNET_DISK_directory_scan (const char *dir_name, | |||
882 | return count; | 882 | return count; |
883 | } | 883 | } |
884 | 884 | ||
885 | /** | ||
886 | * Check for a simple wildcard match. | ||
887 | * Only asterisks are allowed. | ||
888 | * Asterisks match everything, including slashes. | ||
889 | * | ||
890 | * @param pattern pattern with wildcards | ||
891 | * @param str string to match against | ||
892 | * @returns true on match, false otherwise | ||
893 | */ | ||
894 | static bool | ||
895 | glob_match (const char *pattern, const char *str) | ||
896 | { | ||
897 | /* Position in the input string */ | ||
898 | const char *str_pos = str; | ||
899 | /* Position in the pattern */ | ||
900 | const char *pat_pos = pattern; | ||
901 | /* Backtrack position in string */ | ||
902 | const char *str_bt = NULL; | ||
903 | /* Backtrack position in pattern */ | ||
904 | const char *pat_bt = NULL; | ||
905 | |||
906 | for (;;) | ||
907 | { | ||
908 | if (*pat_pos == '*') | ||
909 | { | ||
910 | str_bt = str_pos; | ||
911 | pat_bt = pat_pos++; | ||
912 | } | ||
913 | else if (*pat_pos == *str_pos) | ||
914 | { | ||
915 | if ('\0' == *pat_pos) | ||
916 | return true; | ||
917 | str_pos++; | ||
918 | pat_pos++; | ||
919 | } | ||
920 | else | ||
921 | { | ||
922 | if (NULL == str_bt) | ||
923 | return false; | ||
924 | /* Backtrack to match one more | ||
925 | character as part of the asterisk. */ | ||
926 | str_pos = str_bt + 1; | ||
927 | if ('\0' == *str_pos) | ||
928 | return false; | ||
929 | pat_pos = pat_bt; | ||
930 | } | ||
931 | } | ||
932 | } | ||
933 | |||
934 | struct GlobClosure | ||
935 | { | ||
936 | const char *glob; | ||
937 | GNUNET_FileNameCallback cb; | ||
938 | void *cls; | ||
939 | }; | ||
940 | |||
941 | /** | ||
942 | * Function called with a filename. | ||
943 | * | ||
944 | * @param cls closure | ||
945 | * @param filename complete filename (absolute path) | ||
946 | * @return #GNUNET_OK to continue to iterate, | ||
947 | * #GNUNET_NO to stop iteration with no error, | ||
948 | * #GNUNET_SYSERR to abort iteration with error! | ||
949 | */ | ||
950 | static enum GNUNET_GenericReturnValue | ||
951 | glob_cb (void *cls, | ||
952 | const char *filename) | ||
953 | { | ||
954 | struct GlobClosure *gc = cls; | ||
955 | const char *fn; | ||
956 | |||
957 | fn = strrchr (filename, DIR_SEPARATOR); | ||
958 | fn = (NULL == fn) ? filename : (fn + 1); | ||
959 | |||
960 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
961 | "checking glob '%s' against '%s'\n", | ||
962 | gc->glob, | ||
963 | fn); | ||
964 | |||
965 | if (glob_match (gc->glob, fn)) | ||
966 | { | ||
967 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
968 | "found glob match '%s'\n", | ||
969 | filename); | ||
970 | gc->cb (gc->cls, filename); | ||
971 | } | ||
972 | return GNUNET_OK; | ||
973 | } | ||
974 | |||
975 | |||
976 | int | ||
977 | GNUNET_DISK_glob (const char *glob_pattern, | ||
978 | GNUNET_FileNameCallback callback, | ||
979 | void *callback_cls) | ||
980 | { | ||
981 | char *mypat = GNUNET_strdup (glob_pattern); | ||
982 | char *sep; | ||
983 | int ret; | ||
984 | |||
985 | sep = strrchr (mypat, DIR_SEPARATOR); | ||
986 | if (NULL == sep) | ||
987 | { | ||
988 | GNUNET_free (mypat); | ||
989 | return -1; | ||
990 | } | ||
991 | |||
992 | *sep = '\0'; | ||
993 | |||
994 | if (NULL != strchr (mypat, '*')) | ||
995 | { | ||
996 | GNUNET_free (mypat); | ||
997 | GNUNET_break (0); | ||
998 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
999 | "glob pattern may only contain '*' in the final path component\n"); | ||
1000 | return -1; | ||
1001 | } | ||
1002 | |||
1003 | { | ||
1004 | struct GlobClosure gc = { | ||
1005 | .glob = sep + 1, | ||
1006 | .cb = callback, | ||
1007 | .cls = callback_cls, | ||
1008 | }; | ||
1009 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1010 | "scanning directory '%s' for glob matches on '%s'\n", | ||
1011 | mypat, | ||
1012 | gc.glob); | ||
1013 | ret = GNUNET_DISK_directory_scan (mypat, | ||
1014 | glob_cb, | ||
1015 | &gc | ||
1016 | ); | ||
1017 | } | ||
1018 | GNUNET_free (mypat); | ||
1019 | return ret; | ||
1020 | } | ||
1021 | |||
885 | 1022 | ||
886 | /** | 1023 | /** |
887 | * Function that removes the given directory by calling | 1024 | * Function that removes the given directory by calling |
@@ -997,7 +1134,7 @@ GNUNET_DISK_file_copy (const char *src, | |||
997 | GNUNET_DISK_file_close (in); | 1134 | GNUNET_DISK_file_close (in); |
998 | GNUNET_DISK_file_close (out); | 1135 | GNUNET_DISK_file_close (out); |
999 | return GNUNET_OK; | 1136 | return GNUNET_OK; |
1000 | FAIL: | 1137 | FAIL: |
1001 | GNUNET_free (buf); | 1138 | GNUNET_free (buf); |
1002 | GNUNET_DISK_file_close (in); | 1139 | GNUNET_DISK_file_close (in); |
1003 | GNUNET_DISK_file_close (out); | 1140 | GNUNET_DISK_file_close (out); |