aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-07-28 14:31:38 +0200
committerFlorian Dold <florian@dold.me>2021-07-28 15:27:39 +0200
commit7615d46b09275383bd244a0ef1d94b3a77559b88 (patch)
treef6452173470ae80b186a4ee6da24d1d88d5033f2 /src/util
parent391794a460140192b3466765ebc63797cea44000 (diff)
downloadgnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.tar.gz
gnunet-7615d46b09275383bd244a0ef1d94b3a77559b88.zip
implement config inline globbing
Diffstat (limited to 'src/util')
-rw-r--r--src/util/configuration.c259
-rw-r--r--src/util/disk.c139
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
233struct 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 */
247static int
248inline_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 */
271enum GNUNET_GenericReturnValue
272handle_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
234enum GNUNET_GenericReturnValue 348enum GNUNET_GenericReturnValue
235GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg, 349GNUNET_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 */
894static bool
895glob_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
934struct 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 */
950static enum GNUNET_GenericReturnValue
951glob_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
976int
977GNUNET_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;
1000FAIL: 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);