Add ddsconf to generate md, rnc and xsd configuration documentation
Signed-off-by: Jeroen Koekkoek <jeroen@koekkoek.nl>
This commit is contained in:
parent
9c04099937
commit
b25f10ff33
20 changed files with 5596 additions and 5087 deletions
19
.travis.yml
19
.travis.yml
|
@ -45,7 +45,7 @@ linux_gcc8: &linux_gcc8
|
|||
apt:
|
||||
update: true
|
||||
sources: [ ubuntu-toolchain-r-test ]
|
||||
packages: [ gcc-8, g++-8, trang ]
|
||||
packages: [ gcc-8, g++-8 ]
|
||||
before_install:
|
||||
- eval "export CC=gcc-8"
|
||||
- eval "export CXX=g++-8"
|
||||
|
@ -61,7 +61,7 @@ linux_clang: &linux_clang
|
|||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages: [ trang ]
|
||||
packages: [ ]
|
||||
before_install:
|
||||
- eval "export CC=clang"
|
||||
- eval "export CXX=clang++"
|
||||
|
@ -75,7 +75,7 @@ osx_xcode: &osx_xcode
|
|||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages: [ python3, trang ]
|
||||
packages: [ python3 ]
|
||||
before_install:
|
||||
- eval "export CC=clang"
|
||||
- eval "export CXX=clang++"
|
||||
|
@ -184,7 +184,6 @@ script:
|
|||
- mkdir build
|
||||
- cd build
|
||||
- conan install -b missing -s arch=${ARCH} -s build_type=${BUILD_TYPE} ../${CONANFILE:-conanfile.txt}
|
||||
- which trang && BUILD_SCHEMA=1 || BUILD_SCHEMA=0
|
||||
- cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE}
|
||||
-DCMAKE_INSTALL_PREFIX=${INSTALLPREFIX}
|
||||
-DUSE_SANITIZER=${ASAN}
|
||||
|
@ -193,7 +192,7 @@ script:
|
|||
-DENABLE_LIFESPAN=${LIFESPAN}
|
||||
-DENABLE_DEADLINE_MISSED=${DEADLINE}
|
||||
-DBUILD_TESTING=on
|
||||
-DBUILD_SCHEMA=${BUILD_SCHEMA}
|
||||
-DBUILD_SCHEMA=on
|
||||
-DWERROR=on
|
||||
-G "${GENERATOR}" ..
|
||||
- |
|
||||
|
@ -218,11 +217,15 @@ script:
|
|||
CMAKE_LINKER_FLAGS="-DCMAKE_LINKER_FLAGS=-fsanitize=${USE_SANITIZER}";
|
||||
CMAKE_C_FLAGS="-DCMAKE_C_FLAGS=-fsanitize=${USE_SANITIZER}";
|
||||
fi
|
||||
- cd ..
|
||||
- |
|
||||
if [ ${BUILD_SCHEMA} ]; then
|
||||
git diff --exit-code; # check that no files (e.g. generated schemas) are changed
|
||||
if [ "${SSL}" = "YES" ] && [ "${SECURITY}" = "YES" ]; then
|
||||
cmake --build . --config ${BUILD_TYPE} --target schema && \
|
||||
cmake --build . --config ${BUILD_TYPE} --target options_doc && \
|
||||
diff --strip-trailing-cr ../etc/cyclonedds.rnc docs/cyclonedds.rnc && \
|
||||
diff --strip-trailing-cr ../etc/cyclonedds.xsd docs/cyclonedds.xsd && \
|
||||
diff --strip-trailing-cr ../docs/manual/options.md docs/manual/options.md
|
||||
fi
|
||||
- cd ..
|
||||
- mkdir helloworld_build
|
||||
- cd helloworld_build
|
||||
- cmake -DCMAKE_PREFIX_PATH=${INSTALLPREFIX}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
# Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindTrang
|
||||
---------
|
||||
Finds Trang, an xml schema transpiler
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This will define the following variables:
|
||||
|
||||
``Trang_FOUND``
|
||||
True if the system has the Foo library.
|
||||
``Trang_VERSION``
|
||||
The version of the Foo library which was found.
|
||||
``Trang_TRANG_CMD``
|
||||
List of command line args to run Trang.
|
||||
``Trang_TRANG_EXECUTABLE``
|
||||
Path to the Trang executable file
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
find_program(Trang_TRANG_EXECUTABLE trang)
|
||||
|
||||
execute_process(
|
||||
COMMAND "${Trang_TRANG_EXECUTABLE}"
|
||||
ERROR_VARIABLE trang_output
|
||||
)
|
||||
if(trang_output MATCHES "Trang version ([0-9]+)")
|
||||
set(TRANG_VERSION "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
message(ERROR "Could not parse version from Trang output: '${trang_output}'")
|
||||
set(TRANG_VERSION "0")
|
||||
endif()
|
||||
|
||||
set(Trang_TRANG_CMD "${Trang_TRANG_EXECUTABLE}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
|
||||
Trang
|
||||
REQUIRED_VARS Trang_TRANG_CMD
|
||||
VERSION_VAR TRANG_VERSION
|
||||
)
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
|
||||
# Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
|
@ -8,36 +8,28 @@
|
|||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
|
||||
# These are all various expressions of the configuration schema
|
||||
get_filename_component(config_c_PATH "../src/core/ddsi/src/q_config.c" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# although the following files are generated, we check them into source control for convenience
|
||||
get_filename_component(config_rnc_PATH "../etc/cyclonedds.rnc" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
get_filename_component(config_xsd_PATH "../etc/cyclonedds.xsd" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
get_filename_component(config_md_PATH "./manual/options.md" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
# although the following files are generated, they are checked into source
|
||||
# control for convenience, but the user must copy them manually
|
||||
set(cyclonedds_rnc "${CMAKE_CURRENT_BINARY_DIR}/cyclonedds.rnc")
|
||||
set(cyclonedds_xsd "${CMAKE_CURRENT_BINARY_DIR}/cyclonedds.xsd")
|
||||
set(options_md "${CMAKE_CURRENT_BINARY_DIR}/manual/options.md")
|
||||
|
||||
if(BUILD_SCHEMA OR BUILD_DOCS)
|
||||
find_package(Perl REQUIRED)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${config_md_PATH}" "${config_rnc_PATH}"
|
||||
COMMAND
|
||||
${PERL_EXECUTABLE} -w "${CMAKE_CURRENT_SOURCE_DIR}/makernc.pl" "${config_c_PATH}" "${config_md_PATH}" "${config_rnc_PATH}"
|
||||
COMMENT "Generating Relax NG schema and Markdown documentation for config options"
|
||||
DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/makernc.pl"
|
||||
"${config_c_PATH}")
|
||||
add_custom_target(options_doc ALL DEPENDS "${config_md_PATH}" "${config_rnc_PATH}")
|
||||
|
||||
find_package(Trang REQUIRED)
|
||||
OUTPUT "${cyclonedds_rnc}" "${cyclonedds_xsd}"
|
||||
COMMAND ddsconf ARGS -f rnc -o "${cyclonedds_rnc}"
|
||||
COMMAND ddsconf ARGS -f xsd -o "${cyclonedds_xsd}"
|
||||
DEPENDS ddsconf)
|
||||
add_custom_target(schema DEPENDS "${cyclonedds_rnc}" "${cyclonedds_xsd}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${config_xsd_PATH}"
|
||||
COMMAND ${Trang_TRANG_CMD} -I rnc -O xsd "${config_rnc_PATH}" "${config_xsd_PATH}"
|
||||
COMMENT "Translating schema into XSD"
|
||||
DEPENDS "${config_rnc_PATH}")
|
||||
add_custom_target(options_xsd ALL DEPENDS "${config_xsd_PATH}")
|
||||
OUTPUT "${options_md}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory manual
|
||||
COMMAND ddsconf ARGS -f md -o "${options_md}"
|
||||
DEPENDS ddsconf)
|
||||
add_custom_target(options_doc DEPENDS "${options_md}")
|
||||
endif()
|
||||
|
||||
if(BUILD_DOCS)
|
||||
|
|
682
docs/makernc.pl
682
docs/makernc.pl
|
@ -1,682 +0,0 @@
|
|||
: # -*- perl -*-
|
||||
eval 'exec perl -w -S $0 "$@"'
|
||||
if 0;
|
||||
|
||||
use strict;
|
||||
use Data::Dumper;
|
||||
|
||||
if (@ARGV != 3) {
|
||||
print STDERR "usage: $0 input output.md output.rnc\n";
|
||||
exit 2;
|
||||
}
|
||||
|
||||
my $input = $ARGV[0];
|
||||
my $output_md = $ARGV[1];
|
||||
my $output_rnc = $ARGV[2];
|
||||
|
||||
# This "perl" script extracts the configuration elements and their types and descriptions
|
||||
# from the source and generates a RELAX NG Compact Form (RNC) and a MarkDown version of
|
||||
# it. The scare quotes are necessary because it really is just a translation to perl
|
||||
# syntax of an old gawk script, originally used for generating input to a half-baked
|
||||
# Java-based configuration editor.
|
||||
#
|
||||
# There are tools out there to convert RNC to, e.g., XSD. RNC has the advantage of being
|
||||
# understood by Emacs' nXML mode, but more importantly, of being fairly straightforward to
|
||||
# generate.
|
||||
#
|
||||
# In an ideal world it would be a bit less fragile in its parsing of the input, and
|
||||
# besides one should generate the C code for the configuration tables from a sensible
|
||||
# source format, rather than try to extract it from the C source.
|
||||
#
|
||||
# Other issues:
|
||||
# - knowledge of conversion functions in here
|
||||
# - hard definitions of enums in here
|
||||
# - some other hard-coded knowledge of the top level nodes
|
||||
#
|
||||
# INCANTATION
|
||||
#
|
||||
# trang is a tool for converting (among other things) RNC to XSD
|
||||
#
|
||||
# perl -w ../docs/makernc.pl ../src/core/ddsi/src/q_config.c ../docs/manual/options.md ../etc/cyclonedds.rnc \
|
||||
# && java -jar trang-20091111/trang.jar -I rnc -O xsd ../etc/cyclonedds.rnc ../etc/cyclonedds.xsd
|
||||
$|=1;
|
||||
my $debug = 0;
|
||||
|
||||
my %typehint2xmltype =
|
||||
("____" => "____",
|
||||
"nop" => "____",
|
||||
"networkAddress" => "String",
|
||||
"partitionAddress" => "String",
|
||||
"networkAddresses" => "String",
|
||||
"ipv4" => "String",
|
||||
"boolean" => "Boolean",
|
||||
"boolean_default" => "Enum",
|
||||
"string" => "String",
|
||||
"tracingOutputFileName" => "String",
|
||||
"verbosity" => "Enum",
|
||||
"tracemask" => "Comma",
|
||||
"peer" => "String",
|
||||
"float" => "Float",
|
||||
"int" => "Int",
|
||||
"int32" => "Int",
|
||||
"uint" => "Int",
|
||||
"uint32" => "Int",
|
||||
"natint" => "Int",
|
||||
"natint_255" => "Int",
|
||||
"domainId" => "String",
|
||||
"participantIndex" => "String",
|
||||
"port" => "Int",
|
||||
"dyn_port" => "Int",
|
||||
"duration_inf" => "String",
|
||||
"duration_ms_1hr" => "String",
|
||||
"duration_ms_1s" => "String",
|
||||
"duration_100ms_1hr" => "String",
|
||||
"duration_us_1s" => "String",
|
||||
"memsize" => "String",
|
||||
"bandwidth" => "String",
|
||||
"standards_conformance" => "Enum",
|
||||
"locators" => "Enum",
|
||||
"service_name" => "String",
|
||||
"sched_class" => "Enum",
|
||||
"cipher" => "Enum",
|
||||
"besmode" => "Enum",
|
||||
"retransmit_merging" => "Enum",
|
||||
"sched_prio_class" => "Enum",
|
||||
"sched_class" => "Enum",
|
||||
"maybe_int32" => "String",
|
||||
"maybe_memsize" => "String",
|
||||
"maybe_duration_inf" => "String",
|
||||
"allow_multicast" => "Comma",
|
||||
"transport_selector" => "Enum",
|
||||
"many_sockets_mode" => "Enum",
|
||||
"xcheck" => "Comma",
|
||||
"min_tls_version" => "String");
|
||||
|
||||
my %typehint2unit =
|
||||
("duration_inf" => "duration_inf",
|
||||
"duration_ms_1hr" => "duration",
|
||||
"duration_100ms_1hr" => "duration",
|
||||
"duration_ms_1s" => "duration",
|
||||
"duration_us_1s" => "duration",
|
||||
"bandwidth" => "bandwidth",
|
||||
"memsize" => "memsize",
|
||||
"maybe_memsize" => "memsize",
|
||||
"maybe_duration_inf" => "duration_inf");
|
||||
|
||||
my %enum_values =
|
||||
("locators" => "local;none",
|
||||
"standards_conformance" => "lax;strict;pedantic",
|
||||
"verbosity" => "finest;finer;fine;config;info;warning;severe;none",
|
||||
"besmode" => "full;writers;minimal",
|
||||
"retransmit_merging" => "never;adaptive;always",
|
||||
"sched_prio_class" => "relative;absolute",
|
||||
"sched_class" => "realtime;timeshare;default",
|
||||
"cipher" => "null;blowfish;aes128;aes192;aes256",
|
||||
"boolean_default" => "false;true;default",
|
||||
"many_sockets_mode" => "false;true;single;none;many",
|
||||
"transport_selector" => "default;udp;udp6;tcp;tcp6;raweth");
|
||||
|
||||
# should extrace these from the source ...
|
||||
my %comma_values =
|
||||
("tracemask" => "|fatal;error;warning;info;config;discovery;data;radmin;timing;traffic;topic;tcp;plist;whc;throttle;rhc;content;trace",
|
||||
"allow_multicast" => "default|false;spdp;asm;ssm;true",
|
||||
"xcheck" => "|whc;rhc;all");
|
||||
|
||||
my %range =
|
||||
("port" => "1;65535",
|
||||
"dyn_port" => "-1;65535",
|
||||
"general_cfgelems/startupmodeduration" => "0;60000",
|
||||
"natint_255" => "0;255",
|
||||
"duration_ms_1hr" => "0;1hr",
|
||||
"duration_100ms_1hr" => "100ms;1hr",
|
||||
"duration_ms_1s" => "0;1s",
|
||||
"duration_us_1s" => "0;1s");
|
||||
|
||||
my %unit_blurb =
|
||||
("bandwidth" => "\n<p>The unit must be specified explicitly. Recognised units: <i>X</i>b/s, <i>X</i>bps for bits/s or <i>X</i>B/s, <i>X</i>Bps for bytes/s; where <i>X</i> is an optional prefix: k for 10<sup>3</sup>, Ki for 2<sup>10</sup>, M for 10<sup>6</sup>, Mi for 2<sup>20</sup>, G for 10<sup>9</sup>, Gi for 2<sup>30</sup>.</p>",
|
||||
"memsize" => "\n<p>The unit must be specified explicitly. Recognised units: B (bytes), kB & KiB (2<sup>10</sup> bytes), MB & MiB (2<sup>20</sup> bytes), GB & GiB (2<sup>30</sup> bytes).</p>",
|
||||
"duration" => "\n<p>The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.</p>",
|
||||
"duration_inf" => "\n<p>Valid values are finite durations with an explicit unit or the keyword 'inf' for infinity. Recognised units: ns, us, ms, s, min, hr, day.</p>");
|
||||
|
||||
my %unit_patterns =
|
||||
("memsize" => '0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B',
|
||||
"bandwidth" => '0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?[Bb][p/]s',
|
||||
"duration" => '0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)',
|
||||
"duration_inf" => 'inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)');
|
||||
|
||||
while (my ($k, $v) = each %typehint2xmltype) {
|
||||
die "script error: values of enum type $k unknown\n" if $v eq "Enum" && $enum_values{$k} eq "";
|
||||
}
|
||||
|
||||
my %elem;
|
||||
my %typehint_seen;
|
||||
|
||||
my %tables;
|
||||
|
||||
my @root = read_config ($input);
|
||||
|
||||
{
|
||||
open my $fh, ">:unix", "$output_rnc" or die "can't open $output_rnc";
|
||||
print $fh "default namespace = \"https://cdds.io/config\"\n";
|
||||
print $fh "namespace a = \"http://relaxng.org/ns/compatibility/annotations/1.0\"\n";
|
||||
print $fh "grammar {\n";
|
||||
print $fh " start =\n";
|
||||
my $isfirst = 1;
|
||||
conv_table($fh, \&conv_to_rnc, \@root, "/", " ", "", \$isfirst);
|
||||
for (sort keys %unit_patterns) {
|
||||
printf $fh " %s = xsd:token { pattern = \"%s\" }\n", $_, $unit_patterns{$_};
|
||||
}
|
||||
print $fh "}\n";
|
||||
close $fh;
|
||||
}
|
||||
|
||||
{
|
||||
open my $fh, ">:unix", "$output_md" or die "can't open $output_md";
|
||||
my $sep_blurb = "";
|
||||
conv_table($fh, \&conv_to_md, \@root, "/", " ", "", \$sep_blurb);
|
||||
close $fh;
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
sub clean_description {
|
||||
my ($desc) = @_;
|
||||
$desc =~ s/^\s*BLURB\s*\(\s*//s;
|
||||
$desc =~ s/^\s*"//s;
|
||||
$desc =~ s/\s*"(\s*\))? *(\}\s*,\s*$)?$//s;
|
||||
$desc =~ s/\\"/"/g;
|
||||
$desc =~ s/\\n\s*\\/\n/g;
|
||||
$desc =~ s/\\\\/\\/g;
|
||||
$desc =~ s/\n\n/\n/g;
|
||||
# should fix the source ...
|
||||
$desc =~ s/DDSI2E?/Cyclone DDS/g;
|
||||
return $desc;
|
||||
}
|
||||
|
||||
sub html_to_md {
|
||||
my ($desc) = @_;
|
||||
$desc =~ s/^\<\/p\>//gs;
|
||||
$desc =~ s/\<\/p\>//gs;
|
||||
$desc =~ s/<sup>/^/s;
|
||||
$desc =~ s/<\/sup>//s;
|
||||
$desc =~ s/\<p\>/\n\n/gs;
|
||||
$desc =~ s/\<\/?(i|ul)\>//gs;
|
||||
$desc =~ s/\<li\>(.*?)\<\/li\>\n*/* $1\n/gs;
|
||||
$desc =~ s/"/"/gs;
|
||||
$desc =~ s/\n+/\n/gs;
|
||||
$desc =~ s/^\n*//s;
|
||||
return $desc;
|
||||
}
|
||||
|
||||
sub kind_to_kstr {
|
||||
my ($kind, $typehint, $table, $name) = @_;
|
||||
if ($kind eq "GROUP" || $kind eq "MGROUP") {
|
||||
return "element";
|
||||
} elsif ($kind eq "ATTR") {
|
||||
return "$typehint2xmltype{$typehint}";
|
||||
} elsif ($kind eq "LEAF") {
|
||||
return "$typehint2xmltype{$typehint}";
|
||||
} else {
|
||||
die "error: $kind unrecognized kind ($table/$name)\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub store_entry {
|
||||
my ($name, $table, $kind, $subtables, $multiplicity, $defaultvalue, $typehint, $description) = @_;
|
||||
$name =~ s/\|.*//; # aliases are not visible in osplconf
|
||||
my $ltable = lc $table;
|
||||
my $lname = lc $name;
|
||||
die "error: no mapping defined for type $typehint\n" if $typehint2xmltype{$typehint} eq "";
|
||||
my $ub = exists $typehint2unit{$typehint} && exists $unit_blurb{$typehint2unit{$typehint}} ? $unit_blurb{$typehint2unit{$typehint}} : "";
|
||||
if ($kind eq "GROUP" || $kind eq "MGROUP") {
|
||||
# GROUP and MGROUP have no data, so also no default value
|
||||
$defaultvalue = undef;
|
||||
} elsif ($defaultvalue eq "" || $defaultvalue eq "NULL") {
|
||||
$defaultvalue = undef;
|
||||
} else {
|
||||
$defaultvalue =~ s/^"(.*)"$/$1/;
|
||||
}
|
||||
|
||||
my ($min_occ, $max_occ);
|
||||
if ($multiplicity =~ /MAX/) {
|
||||
$min_occ = $max_occ = 0;
|
||||
} elsif ($multiplicity == 0 || $multiplicity == 1) {
|
||||
# multiplicity = 0 => special case, treat as-if 1
|
||||
# multiplicity = 1 => required if no default
|
||||
if ($kind eq "GROUP" || $kind eq "MGROUP") {
|
||||
$min_occ = 0;
|
||||
} elsif (not defined $defaultvalue) {
|
||||
$min_occ = 1;
|
||||
} else {
|
||||
$min_occ = 0;
|
||||
}
|
||||
$max_occ = 1;
|
||||
} else {
|
||||
$min_occ = 0; $max_occ = $multiplicity;
|
||||
}
|
||||
|
||||
my $kstr = kind_to_kstr($kind, $typehint, $table, $name);
|
||||
|
||||
my $desc = clean_description($description).$ub;
|
||||
$desc .= "<p>The default value is: "$defaultvalue".</p>" if defined $defaultvalue;
|
||||
my $fs;
|
||||
push @{$tables{$table}}, { table => $table, name => $name,
|
||||
kind => $kind, kstr => $kstr,
|
||||
subtables => $subtables, multiplicity => $multiplicity,
|
||||
min_occ => $min_occ, max_occ => $max_occ, root => 0,
|
||||
defaultvalue => $defaultvalue, typehint => $typehint,
|
||||
description => $desc };
|
||||
# typehint_seen is for verifying no bogus type hints are defined in this script
|
||||
$typehint_seen{$typehint} = 1;
|
||||
}
|
||||
|
||||
sub fmtblurb {
|
||||
my ($blurb) = @_;
|
||||
my $isbullet = ($blurb =~ s/^\* //);
|
||||
my $maxlen = $isbullet ? 78 : 74;
|
||||
my @words = split ' ', $blurb;
|
||||
my @lines = ();
|
||||
while (@words > 0) {
|
||||
my $x = shift @words;
|
||||
while (@words > 0 && (length $x) + 1 + (length $words[0]) < $maxlen) {
|
||||
$x .= " " . shift @words;
|
||||
}
|
||||
push @lines, "$x";
|
||||
}
|
||||
my $sep = "\n" . ($isbullet ? " " : "");
|
||||
return ($isbullet ? "* " : "") . join $sep, @lines;
|
||||
}
|
||||
|
||||
sub print_description_rnc {
|
||||
my ($fh, $desc, $indent) = @_;
|
||||
my $x = $desc;
|
||||
my @xs = split /\n+/, $x;
|
||||
$_ = fmtblurb ($_) for @xs;
|
||||
$x = join "\n\n", @xs;
|
||||
return 0 if $x =~ /^\s$/s;
|
||||
print $fh "[ a:documentation [ xml:lang=\"en\" \"\"\"\n$x\"\"\" ] ]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub print_description_md {
|
||||
my ($fh, $desc, $indent) = @_;
|
||||
my $x = html_to_md ($desc);
|
||||
my @xs = split /\n+/, $x;
|
||||
$_ = fmtblurb ($_) for @xs;
|
||||
$x = join "\n\n", @xs;
|
||||
return 0 if $x =~ /^\s$/s;
|
||||
print $fh "$x\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub conv_to_rnc {
|
||||
my ($fh, $fs, $name, $fqname, $indent, $prefix, $isfirstref) = @_;
|
||||
|
||||
printf $fh "${indent}%s", ($$isfirstref ? "" : "& ");
|
||||
print_description_rnc ($fh, $fs->{description}, $indent);
|
||||
printf $fh "${indent}%s %s {\n", ($fs->{kind} eq "ATTR" ? "attribute" : "element"), $name;
|
||||
|
||||
my $sub_isfirst = 1;
|
||||
conv_table($fh, \&conv_to_rnc, $fs->{subtables}, $fqname, "${indent} ", $prefix, \$sub_isfirst);
|
||||
my $sep = $sub_isfirst ? "" : "& ";
|
||||
|
||||
if ($fs->{kind} eq "GROUP" || $fs->{kind} eq "MGROUP") {
|
||||
printf $fh "${indent} ${sep}empty\n" if $sub_isfirst;
|
||||
} elsif ($fs->{kstr} eq "Boolean") {
|
||||
printf $fh "${indent} ${sep}xsd:boolean\n";
|
||||
} elsif ($fs->{kstr} eq "Comma") {
|
||||
die unless exists $comma_values{$fs->{typehint}};
|
||||
my $pat = "";
|
||||
my @xs = split /\|/, $comma_values{$fs->{typehint}};
|
||||
my $allowempty = 0;
|
||||
for (@xs) {
|
||||
if ($_ eq "") { $allowempty = 1; next; }
|
||||
(my $vs = $_) =~ s/;/|/g;
|
||||
$pat .= "|" unless $pat eq "";
|
||||
if ($vs =~ /\|/) {
|
||||
$pat .= "(($vs)(,($vs))*)";
|
||||
} else {
|
||||
$pat .= $vs;
|
||||
}
|
||||
}
|
||||
$pat .= "|" if $allowempty;
|
||||
printf $fh "${indent} ${sep}xsd:token { pattern = \"%s\" }\n", $pat;
|
||||
} elsif ($fs->{kstr} eq "Enum") {
|
||||
die unless exists $enum_values{$fs->{typehint}};
|
||||
my @vs = split /;/, $enum_values{$fs->{typehint}};
|
||||
printf $fh "${indent} ${sep}(%s)\n", (join '|', map { "\"$_\"" } @vs);
|
||||
} elsif ($fs->{kstr} eq "Int") {
|
||||
printf $fh "${indent} ${sep}xsd:integer\n";
|
||||
#if (exists $range{$lctn} || exists $range{$fs->{typehint}}) {
|
||||
# # integer with range
|
||||
# my $rr = exists $range{$lctn} ? $range{$lctn} : $range{$fs->{typehint}};
|
||||
# my @vs = split /;/, $range{$lctn};
|
||||
#}
|
||||
} elsif ($typehint2unit{$fs->{typehint}}) {
|
||||
# number with unit
|
||||
printf $fh "${indent} ${sep}$typehint2unit{$fs->{typehint}}\n";
|
||||
} elsif ($typehint2xmltype{$fs->{typehint}} =~ /String$/) {
|
||||
printf $fh "${indent} ${sep}text\n";
|
||||
} else {
|
||||
die;
|
||||
}
|
||||
|
||||
my $suffix;
|
||||
if ($fs->{min_occ} == 0) {
|
||||
$suffix = ($fs->{max_occ} == 1) ? "?" : "*";
|
||||
} else {
|
||||
$suffix = ($fs->{max_occ} == 1) ? "" : "+";
|
||||
}
|
||||
printf $fh "${indent}}%s\n", $suffix;
|
||||
$$isfirstref = 0;
|
||||
}
|
||||
|
||||
sub list_children_md {
|
||||
my ($fh, $fs, $name, $fqname, $indent, $prefix, $children) = @_;
|
||||
if ($fs->{kind} eq "ATTR") {
|
||||
push @{$children->{attributes}}, $name;
|
||||
} else {
|
||||
push @{$children->{elements}}, $name;
|
||||
}
|
||||
}
|
||||
|
||||
sub conv_to_md {
|
||||
my ($fh, $fs, $name, $fqname, $indent, $prefix, $separator_blurb_ref) = @_;
|
||||
|
||||
print $fh $$separator_blurb_ref;
|
||||
$$separator_blurb_ref = "\n\n";
|
||||
|
||||
# Print fully-qualified element/attribute name as a heading, with the heading level
|
||||
# determined by the nesting level. The nesting level can be computed from the number of
|
||||
# slashes :)
|
||||
(my $slashes = $fqname) =~ s/[^\/]//g;
|
||||
die unless length $slashes >= 2;
|
||||
my $level = (length $slashes) - 1;
|
||||
printf $fh "%s $fqname\n", ("#"x$level);
|
||||
|
||||
# Describe type (boolean, integer, &c.); for a group list its attributes and children as
|
||||
# links to their descriptions
|
||||
{
|
||||
my %children = ("attributes" => [], "elements" => []);
|
||||
conv_table($fh, \&list_children_md, $fs->{subtables}, "", "${indent} ", $prefix, \%children);
|
||||
if (@{$children{attributes}} > 0) {
|
||||
my @xs = sort @{$children{attributes}};
|
||||
my @ys = map { my $lt = lc "$fqname\[\@$_]"; $lt =~ s/[^a-z0-9]//g; "[$_](#$lt)" } @xs;
|
||||
printf $fh "Attributes: %s\n\n", (join ', ', @ys);
|
||||
}
|
||||
if (@{$children{elements}} > 0) {
|
||||
my @xs = sort @{$children{elements}};
|
||||
my @ys = map { my $lt = lc "$fqname\[\@$_]"; $lt =~ s/[^a-z0-9]//g; "[$_](#$lt)" } @xs;
|
||||
printf $fh "Children: %s\n\n", (join ', ', @ys);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fs->{kind} eq "GROUP" || $fs->{kind} eq "MGROUP") {
|
||||
# nothing to see here
|
||||
} elsif ($fs->{kstr} eq "Boolean") {
|
||||
printf $fh "Boolean\n";
|
||||
} elsif ($fs->{kstr} eq "Comma") {
|
||||
die unless exists $comma_values{$fs->{typehint}};
|
||||
my $pat = "";
|
||||
my @xs = split /\|/, $comma_values{$fs->{typehint}};
|
||||
if (@xs > 1) {
|
||||
printf $fh "One of:\n";
|
||||
}
|
||||
my $allowempty = 0;
|
||||
for (@xs) {
|
||||
if ($_ eq "") { $allowempty = 1; next; }
|
||||
my @vs = split /;/, $_;
|
||||
if (@vs > 1) {
|
||||
printf $fh "* Comma-separated list of: %s\n", (join ', ', @vs);
|
||||
} else {
|
||||
printf $fh "* Keyword: %s\n", $vs[0];
|
||||
}
|
||||
}
|
||||
printf $fh "* Or empty\n" if $allowempty;
|
||||
} elsif ($fs->{kstr} eq "Enum") {
|
||||
die unless exists $enum_values{$fs->{typehint}};
|
||||
my @vs = split /;/, $enum_values{$fs->{typehint}};
|
||||
printf $fh "One of: %s\n", (join ', ', @vs);
|
||||
} elsif ($fs->{kstr} eq "Int") {
|
||||
printf $fh "Integer\n";
|
||||
#if (exists $range{$lctn} || exists $range{$fs->{typehint}}) {
|
||||
# # integer with range
|
||||
# my $rr = exists $range{$lctn} ? $range{$lctn} : $range{$fs->{typehint}};
|
||||
# my @vs = split /;/, $range{$lctn};
|
||||
#}
|
||||
} elsif ($typehint2unit{$fs->{typehint}}) {
|
||||
# number with unit
|
||||
printf $fh "Number-with-unit\n";
|
||||
} elsif ($typehint2xmltype{$fs->{typehint}} =~ /String$/) {
|
||||
printf $fh "Text\n";
|
||||
} else {
|
||||
die;
|
||||
}
|
||||
|
||||
# Descriptive text
|
||||
printf $fh "\n";
|
||||
print_description_md ($fh, $fs->{description}, $indent);
|
||||
|
||||
# Generate attributes & children
|
||||
conv_table($fh, \&conv_to_md, $fs->{subtables}, $fqname, "${indent} ", $prefix, $separator_blurb_ref);
|
||||
}
|
||||
|
||||
sub conv_table {
|
||||
my ($fh, $convsub, $tablenames, $fqname, $indent, $prefix, $closure) = @_;
|
||||
my $elems = 0;
|
||||
for (@$tablenames) {
|
||||
next unless exists $tables{$_};
|
||||
for my $fs (sort { $a->{name} cmp $b->{name} } @{$tables{$_}}) {
|
||||
my $fqname1;
|
||||
if ($fs->{kind} eq "ATTR") {
|
||||
die unless $elems == 0;
|
||||
$fqname1 = "${fqname}[\@$fs->{name}]";
|
||||
} else {
|
||||
$fqname1 = "$fqname/$fs->{name}";
|
||||
$elems++;
|
||||
}
|
||||
my $prefix1 = ($fs->{table} eq "internal_cfgelems") ? "<b>Internal</b>" : $prefix;
|
||||
&$convsub ($fh, $fs, $fs->{name}, $fqname1, $indent, $prefix1, $closure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub read_config {
|
||||
my %incl = (# included options
|
||||
'DDSI_INCLUDE_SSL' => 1,
|
||||
'DDSI_INCLUDE_NETWORK_PARTITIONS' => 1,
|
||||
'DDSI_INCLUDE_SSM' => 1,
|
||||
'DDSI_INCLUDE_SECURITY' => 1,
|
||||
# excluded options
|
||||
'DDSI_INCLUDE_NETWORK_CHANNELS' => 0,
|
||||
'DDSI_INCLUDE_BANDWIDTH_LIMITING' => 0);
|
||||
my ($input) = @_;
|
||||
my ($name, $table, $kind, @subtables, $multiplicity, $defaultvalue, $typehint, $description);
|
||||
my ($gobbling_description, $in_table, $rest, $deprecated);
|
||||
my @stk = (); # stack of conditional nesting, for each: copy/discard/ignore
|
||||
open FH, "<", $input or die "can't open $input\n";
|
||||
while (<FH>) {
|
||||
s/[\r\n]+$//s;
|
||||
|
||||
# ignore parts guarded by #if/#ifdef/#if!/#ifndef if $incl says so
|
||||
if (/^\s*\#\s*if(n?def|\s*!)?\s*([A-Za-z_][A-Za-z_0-9]*)\s*(?:\/(?:\/.*|\*.*?\*\/)\s*)?$/) {
|
||||
my $x = (not defined $1 || $1 eq "def") ? -1 : 1; my $var = $2;
|
||||
die if $var =~ /^DDSI_INCLUDE_/ && !exists $incl{$var};
|
||||
push @stk, (not defined $incl{$var}) ? 0 : $incl{$var} ? $x : -$x;
|
||||
} elsif (/^\s*\#\s*if/) { # ignore any other conditional
|
||||
push @stk, 0;
|
||||
} elsif (/^\s*\#\s*else/) {
|
||||
$stk[$#stk] = -$stk[$#stk];
|
||||
} elsif (/^\s*\#\s*endif/) {
|
||||
pop @stk;
|
||||
}
|
||||
next if grep {$_ < 0} @stk;
|
||||
|
||||
if ($gobbling_description) {
|
||||
$description .= $_;
|
||||
#print " .. $_\n";
|
||||
}
|
||||
|
||||
if ($gobbling_description && /(^|")(\s*\)) *\} *, *$/) {
|
||||
$gobbling_description = 0;
|
||||
my @st = @subtables;
|
||||
store_entry ($name, $table, $kind, \@st, $multiplicity, $defaultvalue, $typehint, $description)
|
||||
unless $deprecated;
|
||||
next;
|
||||
}
|
||||
|
||||
if ($gobbling_description) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (/^[ \t]*(#[ \t]*(if|ifdef|ifndef|else|endif).*)?$/) { # skip empty lines, preproc
|
||||
next;
|
||||
}
|
||||
|
||||
if (/^ *END_MARKER *$/) {
|
||||
if (!$in_table) {
|
||||
warn "END_MARKER seen while not in a table";
|
||||
}
|
||||
$in_table = 0;
|
||||
print "END_MARKER $table\n" if $debug;
|
||||
next;
|
||||
}
|
||||
|
||||
if (/^static +const +struct +cfgelem +([A-Za-z_0-9]+)\s*\[/) {
|
||||
$in_table = 1;
|
||||
$table = $1;
|
||||
print "TABLE $table\n" if $debug;
|
||||
next;
|
||||
}
|
||||
|
||||
if ($in_table && /^ *WILDCARD *, *$|^ *\{ *(MOVED) *\(/) {
|
||||
next;
|
||||
}
|
||||
|
||||
# Recognise all "normal" entries: attributes, groups, leaves and
|
||||
# leaves with attributes. This doesn't recognise the ones used for the
|
||||
# root groups: those are dealt with by the next pattern
|
||||
if ($in_table && /^ *\{ *((?:DEPRECATED_)?(?:ATTR|GROUP|GROUP_W_ATTRS|MGROUP|LEAF|LEAF_W_ATTRS)) *\(/) {
|
||||
$rest = $_;
|
||||
# extract kind
|
||||
$rest =~ s/^ *\{ *((?:DEPRECATED_)?(?:ATTR|GROUP|GROUP_W_ATTRS|MGROUP|LEAF|LEAF_W_ATTRS)) *\( *(.*)/$2/;
|
||||
$kind = $1;
|
||||
$deprecated = ($kind =~ s/^DEPRECATED_//);
|
||||
# extract name + reference to subtables
|
||||
$rest =~ s/\"([A-Za-z_0-9|]+)\" *(.*)/$2/;
|
||||
$name = $1;
|
||||
my ($subelems, $subattrs) = ("", "");
|
||||
if ($kind eq "GROUP" || $kind eq "GROUP_W_ATTRS" || $kind eq "MGROUP") {
|
||||
$rest =~ s/, *([A-Za-z_0-9]+) *(.*)/$2/;
|
||||
$subelems = $1;
|
||||
}
|
||||
if ($kind eq "LEAF_W_ATTRS" || $kind eq "GROUP_W_ATTRS" || $kind eq "MGROUP") {
|
||||
$rest =~ s/, *([A-Za-z_0-9]+) *(.*)/$2/;
|
||||
$subattrs = $1;
|
||||
}
|
||||
@subtables = ();
|
||||
push @subtables, $subattrs if $subattrs ne "";
|
||||
push @subtables, $subelems if $subelems ne "";
|
||||
$rest =~ s/ *\) *, *//;
|
||||
print " kind $kind name $name subtables @subtables -- $rest\n" if $debug;
|
||||
|
||||
# don't care about the distinction between GROUP/LEAF and
|
||||
# GROUP/LEAF_W_ATTRS in the remainer of the code: we simply
|
||||
# rely on subtables.
|
||||
$kind =~ s/_W_ATTRS//;
|
||||
}
|
||||
|
||||
# Root groups: use a special trick, which allows them to do groups
|
||||
# with attributes. Which the DDSI2 proper doesn't use, but which the
|
||||
# service configuration stuff does rely on.
|
||||
if ($in_table && /^ *\{ *"([A-Za-z_0-9|]+)" *, */) {
|
||||
$rest = $_;
|
||||
# root elements are all groups, formatted as: <name>, <subelems>,
|
||||
# <attrs>, NODATA, description. They're therefore pretty easy to
|
||||
# parse.
|
||||
$kind = "GROUP";
|
||||
$rest =~ s/^ *\{ *\"([A-Za-z_0-9|]+)\" *, *(.*)/$2/;
|
||||
$name = $1;
|
||||
# then follow the sub-elements and the attributes
|
||||
$rest =~ s/([A-Za-z_0-9]+) *, *(.*)/$2/;
|
||||
my $subelems = $1;
|
||||
$rest =~ s/([A-Za-z_0-9]+) *, *(.*)/$2/;
|
||||
my $subattrs = $1;
|
||||
# then we require NODATA (could do this in the pattern also)
|
||||
die "error: NODATA expected" unless $rest =~ /^NODATA *,/;
|
||||
# multiplicity is hard coded: we want to allow multiple ddsi2 services
|
||||
$multiplicity = 0;
|
||||
@subtables = ();
|
||||
push @subtables, $subattrs if $subattrs ne "";
|
||||
push @subtables, $subelems if $subelems ne "";
|
||||
$rest =~ s/([A-Za-z_0-9]+) *, *(.*)/$2/;
|
||||
}
|
||||
|
||||
# Extract stuff specific to ATTRs, LEAFs and MGROUPs
|
||||
if ($in_table && ($kind eq "ATTR" || $kind eq "LEAF" || $kind eq "MGROUP")) {
|
||||
# extract multiplicity
|
||||
$rest =~ s/([0-9]+|U?INT(?:16|32|64)?_MAX) *, *(.*)/$2/;
|
||||
$multiplicity = $1;
|
||||
# extract default value
|
||||
$rest =~ s/(\"(?:[^\"]*)\"|NULL|0) *, *(.*)/$2/;
|
||||
$defaultvalue = $1;
|
||||
if ($defaultvalue eq "0") {
|
||||
$defaultvalue = "NULL";
|
||||
}
|
||||
# skip reference to internal name (either ABSOFF(field),
|
||||
# RELOFF(field,field) or <int>,<int> (the latter being used by
|
||||
# "verbosity")
|
||||
$rest =~ s/(ABSOFF *\( *[A-Za-z_0-9.]+ *\)|RELOFF *\( *[A-Za-z_0-9.]+ *, *[A-Za-z_0-9. ]+\)|[0-9]+ *, *[0-9]+) *, *//;
|
||||
# skip init function
|
||||
$rest =~ s/([A-Za-z_0-9]+|0) *, *//;
|
||||
# type hint from conversion function
|
||||
$rest =~ s/(uf_(?:[A-Za-z_0-9]+)|NULL|0) *, *(.*)/$2/;
|
||||
$typehint = $1;
|
||||
$typehint =~ s/^uf_//;
|
||||
# accept typehint = NULL for a LEAF_WITH_ATTRS: there is no defined
|
||||
# "syntax" for groups that have only attributes, pretending it is a
|
||||
# group because that causes us to emit an "element" and not a
|
||||
# "leaf".
|
||||
if ($typehint eq "0" || $typehint eq "NULL") {
|
||||
$kind = "GROUP";
|
||||
$typehint = "____";
|
||||
}
|
||||
# skip free, print functions
|
||||
$rest =~ s/([A-Za-z_0-9]+|0) *, *([A-Za-z_0-9]+|0) *, *//;
|
||||
#print " .. multiplicity $multiplicity default $defaultvalue typehint $typehint\n";
|
||||
}
|
||||
|
||||
# Extract description (or NULL, if not to be included in the configurator XML)
|
||||
if ($in_table) {
|
||||
#print " .. $rest\n";
|
||||
# description or NULL
|
||||
if ($rest =~ /NULL *\} *, *$/) {
|
||||
# no description - discard this one/simply continue with next one
|
||||
} elsif ($rest =~ /(?:BLURB\s*\(\s*)?(".*")(?:\s*\))? *\} *, *$/) {
|
||||
# description ending on same line
|
||||
$description = $1;
|
||||
my @st = @subtables;
|
||||
store_entry ($name, $table, $kind, \@st, $multiplicity, $defaultvalue, $typehint, $description)
|
||||
unless $deprecated;
|
||||
} else {
|
||||
# strip the quotes &c. once the full text has been gathered
|
||||
$description = $rest;
|
||||
$gobbling_description = 1;
|
||||
}
|
||||
#print " .. gobbling $gobbling_description";
|
||||
next;
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
my @roots = @{$tables{cyclonedds_root_cfgelems}};
|
||||
die "error: cyclonedds_root_cfgelems has no or multiple entries\n" if @roots != 1;
|
||||
die "error: root_cfgelems doesn't exist\n" unless exists $tables{root_cfgelems};
|
||||
my $root = $roots[0];
|
||||
die "error: root_cfgelems doesn't exist\n" unless defined $root;
|
||||
$root->{min_occ} = $root->{max_occ} = $root->{isroot} = 1;
|
||||
while (my ($k, $v) = each %typehint_seen) {
|
||||
warn "script warning: type mapping defined for $k but not used" if $v == 0;
|
||||
}
|
||||
return ("cyclonedds_root_cfgelems");
|
||||
}
|
File diff suppressed because it is too large
Load diff
1734
etc/cyclonedds.rnc
1734
etc/cyclonedds.rnc
File diff suppressed because it is too large
Load diff
1852
etc/cyclonedds.xsd
1852
etc/cyclonedds.xsd
File diff suppressed because it is too large
Load diff
|
@ -112,9 +112,9 @@ CU_Test(ddsc_security_config, empty, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
#ifndef DDSI_INCLUDE_SECURITY
|
||||
"config: //CycloneDDS/Domain: DDSSecurity: unknown element*",
|
||||
#else
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -114,6 +114,8 @@ PREPEND(hdrs_private_ddsi "${CMAKE_CURRENT_LIST_DIR}/include/dds/ddsi"
|
|||
ddsi_cdrstream.h
|
||||
ddsi_time.h
|
||||
ddsi_ownip.h
|
||||
ddsi_cfgunits.h
|
||||
ddsi_cfgelems.h
|
||||
q_addrset.h
|
||||
q_bitset.h
|
||||
q_bswap.h
|
||||
|
|
1998
src/core/ddsi/include/dds/ddsi/ddsi_cfgelems.h
Normal file
1998
src/core/ddsi/include/dds/ddsi/ddsi_cfgelems.h
Normal file
File diff suppressed because it is too large
Load diff
48
src/core/ddsi/include/dds/ddsi/ddsi_cfgunits.h
Normal file
48
src/core/ddsi/include/dds/ddsi/ddsi_cfgunits.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#ifndef DDSI_CFGUNITS_H
|
||||
#define DDSI_CFGUNITS_H
|
||||
|
||||
static const struct cfgunit cfgunits[] = {
|
||||
UNIT("bandwidth",
|
||||
DESCRIPTION(
|
||||
"<p>The unit must be specified explicitly. Recognised units: "
|
||||
"<i>X</i>b/s, <i>X</i>bps for bits/s or <i>X</i>B/s, <i>X</i>Bps for "
|
||||
"bytes/s; where <i>X</i> is an optional prefix: k for 10<sup>3</sup>, "
|
||||
"Ki for 2<sup>10</sup>, M for 10<sup>6</sup>, Mi for 2<sup>20</sup>, "
|
||||
"G for 10<sup>9</sup>, Gi for 2<sup>30</sup>.</p>"),
|
||||
PATTERN(
|
||||
"0|(\\d+(\\.\\d*)?([Ee][\\-+]?\\d+)?|\\.\\d+([Ee][\\-+]?\\d+)?) *([kMG]i?)?[Bb][p/]s")),
|
||||
UNIT("duration",
|
||||
DESCRIPTION(
|
||||
"<p>The unit must be specified explicitly. Recognised units: ns, us, ms, "
|
||||
"s, min, hr, day.</p>"),
|
||||
PATTERN(
|
||||
"0|(\\d+(\\.\\d*)?([Ee][\\-+]?\\d+)?|\\.\\d+([Ee][\\-+]?\\d+)?) *([num]?s|min|hr|day)")),
|
||||
UNIT("duration_inf",
|
||||
DESCRIPTION(
|
||||
"<p>Valid values are finite durations with an explicit unit or the "
|
||||
"keyword 'inf' for infinity. Recognised units: ns, us, ms, s, min, hr, "
|
||||
"day.</p>"),
|
||||
PATTERN(
|
||||
"inf|0|(\\d+(\\.\\d*)?([Ee][\\-+]?\\d+)?|\\.\\d+([Ee][\\-+]?\\d+)?) *([num]?s|min|hr|day)")),
|
||||
UNIT("memsize",
|
||||
DESCRIPTION(
|
||||
"<p>The unit must be specified explicitly. Recognised units: B (bytes), "
|
||||
"kB & KiB (2<sup>10</sup> bytes), MB & MiB (2<sup>20</sup> bytes), GB & "
|
||||
"GiB (2<sup>30</sup> bytes).</p>"),
|
||||
PATTERN(
|
||||
"0|(\\d+(\\.\\d*)?([Ee][\\-+]?\\d+)?|\\.\\d+([Ee][\\-+]?\\d+)?) *([kMG]i?)?B")),
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
#endif /* DDSI_CFGUNITS_H */
|
|
@ -68,7 +68,6 @@ struct cfgelem {
|
|||
update_fun_t update;
|
||||
free_fun_t free;
|
||||
print_fun_t print;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
struct cfgst_nodekey {
|
||||
|
@ -212,804 +211,79 @@ DI(if_omg_security);
|
|||
#endif
|
||||
#undef DI
|
||||
|
||||
#define CO(name) ((int) offsetof (struct config, name))
|
||||
#define ABSOFF(name) 0, CO (name)
|
||||
#define RELOFF(parent,name) 1, ((int) offsetof (struct parent, name))
|
||||
#define NODATA 1, NULL, 0, 0, 0, 0, 0, 0
|
||||
#define END_MARKER { NULL, NULL, NULL, NODATA, NULL }
|
||||
#define WILDCARD { "*", NULL, NULL, NODATA, NULL }
|
||||
#define LEAF(name) name, NULL, NULL
|
||||
#define DEPRECATED_LEAF(name) "|" name, NULL, NULL
|
||||
#define LEAF_W_ATTRS(name, attrs) name, NULL, attrs
|
||||
#define GROUP(name, children) name, children, NULL, 1, NULL, 0, 0, 0, 0, 0, 0
|
||||
#define GROUP_W_ATTRS(name, children, attrs) name, children, attrs, 1, NULL, 0, 0, 0, 0, 0, 0
|
||||
#define MGROUP(name, children, attrs) name, children, attrs
|
||||
#define ATTR(name) name, NULL, NULL
|
||||
#define DEPRECATED_ATTR(name) "|" name, NULL, NULL
|
||||
/* drop extra information, i.e. DESCRIPTION, RANGE, UNIT and VALUES */
|
||||
#define ELEMENT( \
|
||||
name, elems, attrs, multip, dflt, \
|
||||
relofst, elemofst, \
|
||||
init, update, free, print, ...) \
|
||||
{ \
|
||||
name, elems, attrs, multip, dflt, \
|
||||
relofst, elemofst, \
|
||||
init, update, free, print \
|
||||
}
|
||||
|
||||
#define DEPRECATED(name) "|" name
|
||||
#define MEMBER(name) 0, ((int) offsetof (struct config, name))
|
||||
#define MEMBEROF(parent, name) 1, ((int) offsetof (struct parent, name))
|
||||
#define FUNCTIONS(init, update, free, print) init, update, free, print
|
||||
#define DESCRIPTION(...) /* drop */
|
||||
#define RANGE(...) /* drop */
|
||||
#define UNIT(...) /* drop */
|
||||
#define VALUES(...) /* drop */
|
||||
|
||||
#define NOMEMBER 0, 0
|
||||
#define NOFUNCTIONS 0, 0, 0, 0
|
||||
#define NODATA 1, NULL, NOMEMBER, NOFUNCTIONS
|
||||
#define END_MARKER { NULL, NULL, NULL, NODATA }
|
||||
|
||||
/* MOVED: whereto must be a path starting with CycloneDDS, may not be used in/for lists and only for elements, may not be chained */
|
||||
#define MOVED(name, whereto) ">" name, NULL, NULL, 0, whereto, 0, 0, 0, 0, 0, 0, NULL
|
||||
#if 0
|
||||
#define BLURB(text) text
|
||||
#else
|
||||
#define BLURB(text) NULL
|
||||
#endif
|
||||
static const struct cfgelem general_cfgelems[] = {
|
||||
{ LEAF("NetworkInterfaceAddress"), 1, "auto", ABSOFF(networkAddressString), 0, uf_networkAddress, ff_free, pf_networkAddress,
|
||||
BLURB("<p>This element specifies the preferred network interface for use by DDSI2E. The preferred network interface determines the IP address that DDSI2E advertises in the discovery protocol (but see also General/ExternalNetworkAddress), and is also the only interface over which multicasts are transmitted. The interface can be identified by its IP address, network interface name or network portion of the address. If the value \"auto\" is entered here, DDSI2E will select what it considers the most suitable interface.</p>") },
|
||||
{ LEAF("MulticastRecvNetworkInterfaceAddresses"), 1, "preferred", ABSOFF(networkRecvAddressStrings), 0, uf_networkAddresses, ff_networkAddresses, pf_networkAddresses,
|
||||
BLURB("<p>This element specifies on which network interfaces DDSI2E listens to multicasts. The following options are available:</p>\n\
|
||||
<ul>\n\
|
||||
<li><i>all</i>: listen for multicasts on all multicast-capable interfaces; or</li>\n\
|
||||
<li><i>any</i>: listen for multicasts on the operating system default interface; or</li>\n\
|
||||
<li><i>preferred</i>: listen for multicasts on the preferred interface (General/NetworkInterfaceAddress); or</li>\n\
|
||||
<li><i>none</i>: does not listen for multicasts on any interface; or</li>\n\
|
||||
<li>a comma-separated list of network addresses: configures DDSI2E to listen for multicasts on all of the listed addresses.</li>\n\
|
||||
</ul>\n\
|
||||
<p>If DDSI2E is in IPv6 mode and the address of the preferred network interface is a link-local address, \"all\" is treated as a synonym for \"preferred\" and a comma-separated list is treated as \"preferred\" if it contains the preferred interface and as \"none\" if not.</p>") },
|
||||
{ LEAF("ExternalNetworkAddress"), 1, "auto", ABSOFF(externalAddressString), 0, uf_networkAddress, ff_free, pf_networkAddress,
|
||||
BLURB("<p>This element allows explicitly overruling the network address DDSI2E advertises in the discovery protocol, which by default is the address of the preferred network interface (General/NetworkInterfaceAddress), to allow DDSI2E to communicate across a Network Address Translation (NAT) device.</p>") },
|
||||
{ LEAF("ExternalNetworkMask"), 1, "0.0.0.0", ABSOFF(externalMaskString), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element specifies the network mask of the external network address. This element is relevant only when an external network address (General/ExternalNetworkAddress) is explicitly configured. In this case locators received via the discovery protocol that are within the same external subnet (as defined by this mask) will be translated to an internal address by replacing the network portion of the external address with the corresponding portion of the preferred network interface address. This option is IPv4-only.</p>") },
|
||||
{ LEAF("AllowMulticast"), 1, "default", ABSOFF(allowMulticast), 0, uf_allow_multicast, 0, pf_allow_multicast,
|
||||
BLURB("<p>This element controls whether DDSI2E uses multicasts for data traffic.</p>\n\
|
||||
<p>It is a comma-separated list of some of the following keywords: \"spdp\", \"asm\", \"ssm\", or either of \"false\" or \"true\", or \"default\".</p>\n\
|
||||
<ul>\n\
|
||||
<li><i>spdp</i>: enables the use of ASM (any-source multicast) for participant discovery, joining the multicast group on the discovery socket, transmitting SPDP messages to this group, but never advertising nor using any multicast address in any discovery message, thus forcing unicast communications for all endpoint discovery and user data.</li>\n\
|
||||
<li><i>asm</i>: enables the use of ASM for all traffic, including receiving SPDP but not transmitting SPDP messages via multicast</li>\n\
|
||||
<li><i>ssm</i>: enables the use of SSM (source-specific multicast) for all non-SPDP traffic (if supported)</li>\n\
|
||||
</ul>\n\
|
||||
<p>When set to \"false\" all multicasting is disabled. The default, \"true\" enables full use of multicasts. Listening for multicasts can be controlled by General/MulticastRecvNetworkInterfaceAddresses.</p>\n\
|
||||
<p>\"default\" maps on spdp if the network is a WiFi network, on true if it is a wired network</p>") },
|
||||
{ LEAF("PreferMulticast"), 1, "false", ABSOFF(prefer_multicast), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>When false (default) Cyclone DDS uses unicast for data whenever there a single unicast suffices. Setting this to true makes it prefer multicasting data, falling back to unicast only when no multicast address is available.</p>") },
|
||||
{ LEAF("MulticastTimeToLive"), 1, "32", ABSOFF(multicast_ttl), 0, uf_natint_255, 0, pf_int,
|
||||
BLURB("<p>This element specifies the time-to-live setting for outgoing multicast packets.</p>") },
|
||||
{ LEAF("DontRoute"), 1, "false", ABSOFF(dontRoute), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element allows setting the SO_DONTROUTE option for outgoing packets, to bypass the local routing tables. This is generally useful only when the routing tables cannot be trusted, which is highly unusual.</p>") },
|
||||
{ LEAF ("UseIPv6"), 1, "default", ABSOFF (compat_use_ipv6), 0, uf_boolean_default, 0, pf_nop,
|
||||
BLURB("<p>Deprecated (use Transport instead)</p>") },
|
||||
{ LEAF ("Transport"), 1, "default", ABSOFF (transport_selector), 0, uf_transport_selector, 0, pf_transport_selector,
|
||||
BLURB("<p>This element allows selecting the transport to be used (udp, udp6, tcp, tcp6, raweth)</p>") },
|
||||
{ LEAF("EnableMulticastLoopback"), 1, "true", ABSOFF(enableMulticastLoopback), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element specifies whether DDSI2E allows IP multicast packets to be visible to all DDSI participants in the same node, including itself. It must be \"true\" for intra-node multicast communications, but if a node runs only a single DDSI2E service and does not host any other DDSI-capable programs, it should be set to \"false\" for improved performance.</p>") },
|
||||
{ LEAF("MaxMessageSize"), 1, "4096 B", ABSOFF(max_msg_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element specifies the maximum size of the UDP payload that DDSI2E will generate. DDSI2E will try to maintain this limit within the bounds of the DDSI specification, which means that in some cases (especially for very low values of MaxMessageSize) larger payloads may sporadically be observed (currently up to 1192 B).</p>\n\
|
||||
<p>On some networks it may be necessary to set this item to keep the packetsize below the MTU to prevent IP fragmentation. In those cases, it is generally advisable to also consider reducing Internal/FragmentSize.</p>") },
|
||||
{ LEAF("FragmentSize"), 1, "1280 B", ABSOFF(fragment_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element specifies the size of DDSI sample fragments generated by DDSI2E. Samples larger than FragmentSize are fragmented into fragments of FragmentSize bytes each, except the last one, which may be smaller. The DDSI spec mandates a minimum fragment size of 1025 bytes, but DDSI2E will do whatever size is requested, accepting fragments of which the size is at least the minimum of 1025 and FragmentSize.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#define MOVED(name, whereto) \
|
||||
{ ">" name, NULL, NULL, 0, whereto, NOMEMBER, NOFUNCTIONS }
|
||||
#define NOP(name) \
|
||||
{ name, NULL, NULL, 1, NULL, NOMEMBER, 0, uf_nop, 0, pf_nop }
|
||||
#define WILDCARD { "*", NULL, NULL, NODATA }
|
||||
|
||||
#ifdef DDSI_INCLUDE_SECURITY
|
||||
/** Security Configuration */
|
||||
static const struct cfgelem authentication_library_attributes[] = {
|
||||
{ ATTR ("path"), 1, "dds_security_auth", RELOFF (config_omg_security_listelem, cfg.authentication_plugin.library_path), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element points to the path of Authentication plugin library.</p>\n\
|
||||
<p>It can be either absolute path excluding file extension ( /usr/lib/dds_security_auth ) or single file without extension ( dds_security_auth ).</p>\n\
|
||||
<p>If single file is supplied, the library located by way of the current working directory, or LD_LIBRARY_PATH for Unix systems, and PATH for Windows systems.</p>") },
|
||||
{ ATTR ("initFunction"), 1, "init_authentication", RELOFF (config_omg_security_listelem, cfg.authentication_plugin.library_init), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the initialization function of Authentication plugin. This function is called after loading the plugin library for instantiation purposes. Init function must return an object that implements DDS Security Authentication interface.</p>") },
|
||||
{ ATTR ("finalizeFunction"), 1, "finalize_authentication", RELOFF (config_omg_security_listelem, cfg.authentication_plugin.library_finalize), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the finalization function of Authentication plugin. This function is called to let the plugin release its resources.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
/* Visual Studio requires indirect expansion */
|
||||
#define EXPAND(macro, args) macro args
|
||||
|
||||
static const struct cfgelem access_control_library_attributes[] = {
|
||||
{ ATTR ("path"), 1, "dds_security_ac", RELOFF (config_omg_security_listelem, cfg.access_control_plugin.library_path), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element points to the path of Access Control plugin library.</p>\n\
|
||||
<p>It can be either absolute path excluding file extension ( /usr/lib/dds_security_ac ) or single file without extension ( dds_security_ac ).</p>\n\
|
||||
<p>If single file is supplied, the library located by way of the current working directory, or LD_LIBRARY_PATH for Unix systems, and PATH for Windows systems.</p>") },
|
||||
{ ATTR ("initFunction"), 1, "init_access_control", RELOFF (config_omg_security_listelem, cfg.access_control_plugin.library_init), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the initialization function of Access Control plugin. This function is called after loading the plugin library for instantiation purposes. Init function must return an object that implements DDS Security Access Control interface.</p>") },
|
||||
{ ATTR ("finalizeFunction"), 1, "finalize_access_control", RELOFF (config_omg_security_listelem, cfg.access_control_plugin.library_finalize), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the finalization function of Access Control plugin. This function is called to let the plugin release its resources.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#define BOOL(name, attrs, multip, dflt, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, __VA_ARGS__))
|
||||
#define INT(name, attrs, multip, dflt, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, __VA_ARGS__))
|
||||
#define STRING(name, attrs, multip, dflt, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, __VA_ARGS__))
|
||||
#define ENUM(name, attrs, multip, dflt, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, __VA_ARGS__))
|
||||
#define LIST(name, attrs, multip, dflt, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, __VA_ARGS__))
|
||||
#define GROUP(name, elems, attrs, multip, ...) \
|
||||
EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, __VA_ARGS__))
|
||||
|
||||
static const struct cfgelem cryptography_library_attributes[] = {
|
||||
{ ATTR ("path"), 1, "dds_security_crypto", RELOFF (config_omg_security_listelem, cfg.cryptography_plugin.library_path), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element points to the path of Cryptographic plugin library.</p>\n\
|
||||
<p>It can be either absolute path excluding file extension ( /usr/lib/dds_security_crypto ) or single file without extension ( dds_security_crypto ).</p>\n\
|
||||
<p>If single file is supplied, the library located by way of the current working directory, or LD_LIBRARY_PATH for Unix systems, and PATH for Windows systems.</p>") },
|
||||
{ ATTR ("initFunction"), 1, "init_crypto", RELOFF (config_omg_security_listelem, cfg.cryptography_plugin.library_init), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the initialization function of Cryptographic plugin. This function is called after loading the plugin library for instantiation purposes. Init function must return an object that implements DDS Security Cryptographic interface.</p>") },
|
||||
{ ATTR ("finalizeFunction"), 1, "finalize_crypto", RELOFF (config_omg_security_listelem, cfg.cryptography_plugin.library_finalize), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element names the finalization function of Cryptographic plugin. This function is called to let the plugin release its resources.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
|
||||
static const struct cfgelem authentication_config_elements[] = {
|
||||
{ LEAF_W_ATTRS("Library", authentication_library_attributes), 1, "", RELOFF (config_omg_security_listelem, cfg.authentication_plugin), 0, 0, 0, pf_string,
|
||||
BLURB("<p>This element specifies the library to be loaded as the DDS Security Access Control plugin.</p>") },
|
||||
{ LEAF ("IdentityCertificate"), 1, NULL, RELOFF (config_omg_security_listelem, cfg.authentication_properties.identity_certificate), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>Identity certificate that will be used for identifying all participants in the OSPL instance.<br>The content is URI to a X509 certificate signed by the IdentityCA in PEM format containing the signed public key.</p>\n\
|
||||
<p>Supported URI schemes: file, data</p>\n\
|
||||
<p>Examples:</p>\n\
|
||||
<p><IdentityCertificate>file:participant1_identity_cert.pem</IdentityCertificate></p>\n\
|
||||
<p><IdentityCertificate>data:,-----BEGIN CERTIFICATE-----<br>\n\
|
||||
MIIDjjCCAnYCCQDCEu9...6rmT87dhTo=<br>\n\
|
||||
-----END CERTIFICATE-----</IdentityCertificate></p>") },
|
||||
{ LEAF ("IdentityCA"), 1, NULL, RELOFF (config_omg_security_listelem, cfg.authentication_properties.identity_ca), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>URI to the X509 certificate [39] of the Identity CA that is the signer of Identity Certificate.</p>\n\
|
||||
<p>Supported URI schemes: file, data</p>\n\
|
||||
<p>The file and data schemas shall refer to a X.509 v3 certificate (see X.509 v3 ITU-T Recommendation X.509 (2005) [39]) in PEM format.</p>\n\
|
||||
<p>Examples:</p>\n\
|
||||
<p><IdentityCA>file:identity_ca.pem</IdentityCA></p>\n\
|
||||
<p><IdentityCA>data:,-----BEGIN CERTIFICATE-----<br>\n\
|
||||
MIIC3DCCAcQCCQCWE5x+Z...PhovK0mp2ohhRLYI0ZiyYQ==<br>\n\
|
||||
-----END CERTIFICATE-----</IdentityCA></p>") },
|
||||
{ LEAF ("PrivateKey"), 1, NULL, RELOFF (config_omg_security_listelem, cfg.authentication_properties.private_key), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>URI to access the private Private Key for all of the participants in the OSPL federation.</p>\n\
|
||||
<p>Supported URI schemes: file, data</p>\n\
|
||||
<p>Examples:</p>\n\
|
||||
<p><PrivateKey>file:identity_ca_private_key.pem</PrivateKey></p>\n\
|
||||
<p><PrivateKey>data:,-----BEGIN RSA PRIVATE KEY-----<br>\n\
|
||||
MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br>\n\
|
||||
-----END RSA PRIVATE KEY-----</PrivateKey></p>") },
|
||||
{ LEAF ("Password"), 1, "", RELOFF (config_omg_security_listelem, cfg.authentication_properties.password), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>A password used to decrypt the private_key.</p>\n\
|
||||
The value of the password property shall be interpreted as the Base64 encoding of the AES-128 key that shall be used to decrypt the private_key using AES128-CBC.</p>\n\
|
||||
If the password property is not present, then the value supplied in the private_key property must contain the unencrypted private key. </p>") },
|
||||
{ LEAF ("TrustedCADirectory"), 1, "", RELOFF (config_omg_security_listelem, cfg.authentication_properties.trusted_ca_dir), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>Trusted CA Directory which contains trusted CA certificates as separated files.</p>") },
|
||||
{ LEAF ("IncludeOptionalFields"), 1, "false", RELOFF (config_omg_security_listelem, cfg.authentication_properties.include_optional_fields), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>The authentication handshake tokens may contain optional fields to be included for finding interoperability problems.\n\
|
||||
If this parameter is set to true the optional fields are included in the handshake token exchange.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem access_control_config_elements[] = {
|
||||
{ LEAF_W_ATTRS("Library", access_control_library_attributes), 1, "", RELOFF (config_omg_security_listelem, cfg.access_control_plugin), 0, 0, 0, pf_string,
|
||||
BLURB("<p>This element specifies the library to be loaded as the DDS Security Access Control plugin.</p>") },
|
||||
{ LEAF ("PermissionsCA"), 1, "", RELOFF (config_omg_security_listelem, cfg.access_control_properties.permissions_ca), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>URI to a X509 certificate for the PermissionsCA in PEM format.</p>\n\
|
||||
<p>Supported URI schemes: file, data</p>\n\
|
||||
<p>The file and data schemas shall refer to a X.509 v3 certificate (see X.509 v3 ITU-T Recommendation X.509 (2005) [39]) in PEM format.</p><br>\n\
|
||||
<p>Examples:</p><br>\n\
|
||||
<p><PermissionsCA>file:permissions_ca.pem</PermissionsCA></p>\n\
|
||||
<p><PermissionsCA>file:/home/myuser/permissions_ca.pem</PermissionsCA></p><br>\n\
|
||||
<p><PermissionsCA>data:<strong>,</strong>-----BEGIN CERTIFICATE-----</p>\n\
|
||||
<p>MIIC3DCCAcQCCQCWE5x+Z ... PhovK0mp2ohhRLYI0ZiyYQ==</p>\n\
|
||||
<p>-----END CERTIFICATE-----</PermissionsCA></p>") },
|
||||
{ LEAF ("Governance"), 1, "", RELOFF (config_omg_security_listelem, cfg.access_control_properties.governance), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>URI to the shared Governance Document signed by the Permissions CA in S/MIME format</p>\n\
|
||||
<p>URI schemes: file, data</p><br>\n\
|
||||
<p>Examples file URIs:</p>\n\
|
||||
<p><Governance>file:governance.smime</Governance></p>\n\
|
||||
<p><Governance>file:/home/myuser/governance.smime</Governance></p><br>\n\
|
||||
<p><Governance><![CDATA[data:,MIME-Version: 1.0</p>\n\
|
||||
<p>Content-Type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=\"sha-256\"; boundary=\"----F9A8A198D6F08E1285A292ADF14DD04F\"</p>\n\
|
||||
<p>This is an S/MIME signed message </p>\n\
|
||||
<p>------F9A8A198D6F08E1285A292ADF14DD04F</p>\n\
|
||||
<p><?xml version=\"1.0\" encoding=\"UTF-8\"?></p>\n\
|
||||
<p><dds xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"</p>\n\
|
||||
<p>xsi:noNamespaceSchemaLocation=\"omg_shared_ca_governance.xsd\"></p>\n\
|
||||
<p><domain_access_rules></p>\n\
|
||||
<p> . . . </p>\n\
|
||||
<p></domain_access_rules></p>\n\
|
||||
<p></dds></p>\n\
|
||||
<p>...</p>\n\
|
||||
<p>------F9A8A198D6F08E1285A292ADF14DD04F</p>\n\
|
||||
<p>Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"</p>\n\
|
||||
<p>Content-Transfer-Encoding: base64</p>\n\
|
||||
<p>Content-Disposition: attachment; filename=\"smime.p7s\"</p>\n\
|
||||
<p>MIIDuAYJKoZIhv ...al5s=</p>\n\
|
||||
<p>------F9A8A198D6F08E1285A292ADF14DD04F-]]</Governance></p>") },
|
||||
{ LEAF ("Permissions"), 1, "", RELOFF (config_omg_security_listelem, cfg.access_control_properties.permissions), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>URI to the DomainParticipant permissions document signed by the Permissions CA in S/MIME format</p>\n\
|
||||
<p>The permissions document specifies the permissions to be applied to a domain.</p><br>\n\
|
||||
<p>Example file URIs:</p>\n\
|
||||
<p><Permissions>file:permissions_document.p7s</Permissions></p>\n\
|
||||
<p><Permissions>file:/path_to/permissions_document.p7s</Permissions></p>\n\
|
||||
<p>Example data URI:</p>\n\
|
||||
<p><Permissions><![CDATA[data:,.........]]</Permissions></p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem cryptography_config_elements[] = {
|
||||
{ LEAF_W_ATTRS("Library", cryptography_library_attributes), 1, "", RELOFF (config_omg_security_listelem, cfg.cryptography_plugin), 0, 0, 0, pf_string,
|
||||
BLURB("<p>This element specifies the library to be loaded as the DDS Security Cryptographic plugin.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem security_omg_config_elements[] = {
|
||||
{ GROUP ("Authentication", authentication_config_elements),
|
||||
BLURB("<p>This element configures the Authentication plugin of the DDS Security specification.</p>") },
|
||||
{ GROUP ("AccessControl", access_control_config_elements),
|
||||
BLURB("<p>This element configures the Access Control plugin of the DDS Security specification.</p>") },
|
||||
{ GROUP ("Cryptographic", cryptography_config_elements),
|
||||
BLURB("<p>This element configures the Cryptographic plugin of the DDS Security specification.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#endif /* DDSI_INCLUDE_SECURITY */
|
||||
|
||||
|
||||
#ifdef DDSI_INCLUDE_NETWORK_PARTITIONS
|
||||
static const struct cfgelem networkpartition_cfgattrs[] = {
|
||||
{ ATTR("Name"), 1, NULL, RELOFF(config_networkpartition_listelem, name), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies the name of this DDSI2E network partition. Two network partitions cannot have the same name.</p>") },
|
||||
{ ATTR("Address"), 1, NULL, RELOFF(config_networkpartition_listelem, address_string), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies the multicast addresses associated with the network partition as a comma-separated list. Readers matching this network partition (cf. Partitioning/PartitionMappings) will listen for multicasts on all of these addresses and advertise them in the discovery protocol. The writers will select the most suitable address from the addresses advertised by the readers.</p>") },
|
||||
{ ATTR("Connected"), 1, "true", RELOFF(config_networkpartition_listelem, connected), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This attribute is a placeholder.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem networkpartitions_cfgelems[] = {
|
||||
{ LEAF_W_ATTRS("NetworkPartition", networkpartition_cfgattrs), INT_MAX, 0, ABSOFF(networkPartitions), if_network_partition, 0, 0, 0,
|
||||
BLURB("<p>This element defines a DDSI2E network partition.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem ignoredpartitions_cfgattrs[] = {
|
||||
{ ATTR("DCPSPartitionTopic"), 1, NULL, RELOFF(config_ignoredpartition_listelem, DCPSPartitionTopic), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies a partition and a topic expression, separated by a single '.', that are used to determine if a given partition and topic will be ignored or not. The expressions may use the usual wildcards '*' and '?'. DDSI2E will consider an wildcard DCPS partition to match an expression iff there exists a string that satisfies both expressions.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem ignoredpartitions_cfgelems[] = {
|
||||
{ LEAF_W_ATTRS("IgnoredPartition", ignoredpartitions_cfgattrs), INT_MAX, 0, ABSOFF(ignoredPartitions), if_ignored_partition, 0, 0, 0,
|
||||
BLURB("<p>This element can be used to prevent certain combinations of DCPS partition and topic from being transmitted over the network. DDSI2E will complete ignore readers and writers for which all DCPS partitions as well as their topic is ignored, not even creating DDSI readers and writers to mirror the DCPS ones.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem partitionmappings_cfgattrs[] = {
|
||||
{ ATTR("NetworkPartition"), 1, NULL, RELOFF(config_partitionmapping_listelem, networkPartition), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies which DDSI2E network partition is to be used for DCPS partition/topic combinations matching the DCPSPartitionTopic attribute within this PartitionMapping element.</p>") },
|
||||
{ ATTR("DCPSPartitionTopic"), 1, NULL, RELOFF(config_partitionmapping_listelem, DCPSPartitionTopic), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies a partition and a topic expression, separated by a single '.', that are used to determine if a given partition and topic maps to the DDSI2E network partition named by the NetworkPartition attribute in this PartitionMapping element. The expressions may use the usual wildcards '*' and '?'. DDSI2E will consider a wildcard DCPS partition to match an expression if there exists a string that satisfies both expressions.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem partitionmappings_cfgelems[] = {
|
||||
{ LEAF_W_ATTRS("PartitionMapping", partitionmappings_cfgattrs), INT_MAX, 0, ABSOFF(partitionMappings), if_partition_mapping, 0, 0, 0,
|
||||
BLURB("<p>This element defines a mapping from a DCPS partition/topic combination to a DDSI2E network partition. This allows partitioning data flows by using special multicast addresses for part of the data and possibly also encrypting the data flow.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem partitioning_cfgelems[] = {
|
||||
{ GROUP("NetworkPartitions", networkpartitions_cfgelems),
|
||||
BLURB("<p>The NetworkPartitions element specifies the DDSI2E network partitions.</p>") },
|
||||
{ GROUP("IgnoredPartitions", ignoredpartitions_cfgelems),
|
||||
BLURB("<p>The IgnoredPartitions element specifies DCPS partition/topic combinations that are not distributed over the network.</p>") },
|
||||
{ GROUP("PartitionMappings", partitionmappings_cfgelems),
|
||||
BLURB("<p>The PartitionMappings element specifies the mapping from DCPS partition/topic combinations to DDSI2E network partitions.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#endif /* DDSI_INCLUDE_NETWORK_PARTITIONS */
|
||||
|
||||
#ifdef DDSI_INCLUDE_NETWORK_CHANNELS
|
||||
static const struct cfgelem channel_cfgelems[] = {
|
||||
#ifdef DDSI_INCLUDE_BANDWIDTH_LIMITING
|
||||
{ LEAF("DataBandwidthLimit"), 1, "inf", RELOFF(config_channel_listelem, data_bandwidth_limit), 0, uf_bandwidth, 0, pf_bandwidth,
|
||||
BLURB("<p>This element specifies the maximum transmit rate of new samples and directly related data, for this channel. Bandwidth limiting uses a leaky bucket scheme. The default value \"inf\" means DDSI2E imposes no limitation, the underlying operating system and hardware will likely limit the maimum transmit rate.</p>") },
|
||||
{ LEAF("AuxiliaryBandwidthLimit"), 1, "inf", RELOFF(config_channel_listelem, auxiliary_bandwidth_limit), 0, uf_bandwidth, 0, pf_bandwidth,
|
||||
BLURB("<p>This element specifies the maximum transmit rate of auxiliary traffic on this channel (e.g. retransmits, heartbeats, etc). Bandwidth limiting uses a leaky bucket scheme. The default value \"inf\" means DDSI2E imposes no limitation, the underlying operating system and hardware will likely limit the maimum transmit rate.</p>") },
|
||||
#endif
|
||||
{ LEAF("DiffServField"), 1, "0", RELOFF(config_channel_listelem, diffserv_field), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This element describes the DiffServ setting the channel will apply to the networking messages. This parameter determines the value of the diffserv field of the IP version 4 packets sent on this channel which allows QoS setting to be applied to the network traffic send on this channel.<br/>\n\
|
||||
Windows platform support for setting the diffserv field is dependent on the OS version.<br/>\n\
|
||||
For Windows versions XP SP2 and 2003 to use the diffserv field the following parameter should be added to the register:<br/><br>\n\
|
||||
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\TcpIp\\Parameters\\DisableUserTOSSetting<br/><br/>\n\
|
||||
The type of this parameter is a DWORD and its value should be set to 0 to allow setting of the diffserv field.<br/><br/>\n\
|
||||
For Windows version 7 or higher a new API (qWAVE) has been introduced. For these platforms the specified diffserv value is mapped to one of the support traffic types.\n\
|
||||
The mapping is as follows: 1-8 background traffic; 9-40 excellent traffic; 41-55 audio/video traffic; 56 voice traffic; 57-63 control traffic.\n\
|
||||
When an application is run without Administrative priveleges then only the diffserv value of 0, 8, 40 or 56 is allowed.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem channel_cfgattrs[] = {
|
||||
{ ATTR("Name"), 1, NULL, RELOFF(config_channel_listelem, name), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This attribute specifies name of this channel. The name should uniquely identify the channel.</p>") },
|
||||
{ ATTR("TransportPriority"), 1, "0", RELOFF(config_channel_listelem, priority), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This attribute sets the transport priority threshold for the channel. Each DCPS data writer has a \"transport_priority\" QoS and this QoS is used to select a channel for use by this writer. The selected channel is the one with the largest threshold not greater than the writer's transport priority, and if no such channel exists, the channel with the lowest threshold.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem channels_cfgelems[] = {
|
||||
{ MGROUP("Channel", channel_cfgelems, channel_cfgattrs), INT_MAX, 0, ABSOFF(channels), if_channel, 0, 0, 0,
|
||||
BLURB("<p>This element defines a channel.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#endif /* DDSI_INCLUDE_NETWORK_CHANNELS */
|
||||
|
||||
static const struct cfgelem thread_properties_sched_cfgelems[] = {
|
||||
{ LEAF("Class"), 1, "default", RELOFF(config_thread_properties_listelem, sched_class), 0, uf_sched_class, 0, pf_sched_class,
|
||||
BLURB("<p>This element specifies the thread scheduling class (<i>realtime</i>, <i>timeshare</i> or <i>default</i>). The user may need special privileges from the underlying operating system to be able to assign some of the privileged scheduling classes.</p>") },
|
||||
{ LEAF("Priority"), 1, "default", RELOFF(config_thread_properties_listelem, sched_priority), 0, uf_maybe_int32, 0, pf_maybe_int32,
|
||||
BLURB("<p>This element specifies the thread priority (decimal integer or <i>default</i>). Only priorities that are supported by the underlying operating system can be assigned to this element. The user may need special privileges from the underlying operating system to be able to assign some of the privileged priorities.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem thread_properties_cfgattrs[] = {
|
||||
{ ATTR("Name"), 1, NULL, RELOFF(config_thread_properties_listelem, name), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>The Name of the thread for which properties are being set. The following threads exist:</p>\n\
|
||||
<ul><li><i>gc</i>: garbage collector thread involved in deleting entities;</li>\n\
|
||||
<li><i>recv</i>: receive thread, taking data from the network and running the protocol state machine;</li>\n\
|
||||
<li><i>dq.builtins</i>: delivery thread for DDSI-builtin data, primarily for discovery;</li>\n\
|
||||
<li><i>lease</i>: DDSI liveliness monitoring;</li>\n\
|
||||
<li><i>tev</i>: general timed-event handling, retransmits and discovery;</li>\n\
|
||||
<li><i>fsm</i>: finite state machine thread for handling security handshake;</li>\n\
|
||||
<li><i>xmit.CHAN</i>: transmit thread for channel CHAN;</li>\n\
|
||||
<li><i>dq.CHAN</i>: delivery thread for channel CHAN;</li>\n\
|
||||
<li><i>tev.CHAN</i>: timed-event thread for channel CHAN.</li></ul>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem thread_properties_cfgelems[] = {
|
||||
{ GROUP("Scheduling", thread_properties_sched_cfgelems),
|
||||
BLURB("<p>This element configures the scheduling properties of the thread.</p>") },
|
||||
{ LEAF("StackSize"), 1, "default", RELOFF(config_thread_properties_listelem, stack_size), 0, uf_maybe_memsize, 0, pf_maybe_memsize,
|
||||
BLURB("<p>This element configures the stack size for this thread. The default value <i>default</i> leaves the stack size at the operating system default.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem threads_cfgelems[] = {
|
||||
{ MGROUP("Thread", thread_properties_cfgelems, thread_properties_cfgattrs), INT_MAX, 0, ABSOFF(thread_properties), if_thread_properties, 0, 0, 0,
|
||||
BLURB("<p>This element is used to set thread properties.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem compatibility_cfgelems[] = {
|
||||
{ LEAF("StandardsConformance"), 1, "lax", ABSOFF(standards_conformance), 0, uf_standards_conformance, 0, pf_standards_conformance,
|
||||
BLURB("<p>This element sets the level of standards conformance of this instance of the DDSI2E Service. Stricter conformance typically means less interoperability with other implementations. Currently three modes are defined:</p>\n\
|
||||
<ul><li><i>pedantic</i>: very strictly conform to the specification, ultimately for compliancy testing, but currently of little value because it adheres even to what will most likely turn out to be editing errors in the DDSI standard. Arguably, as long as no errata have been published it is the current text that is in effect, and that is what pedantic currently does.</li>\n\
|
||||
<li><i>strict</i>: a slightly less strict view of the standard than does pedantic: it follows the established behaviour where the standard is obviously in error.</li>\n\
|
||||
<li><i>lax</i>: attempt to provide the smoothest possible interoperability, anticipating future revisions of elements in the standard in areas that other implementations do not adhere to, even though there is no good reason not to.</li></ul>\n\
|
||||
<p>The default setting is \"lax\".</p>") },
|
||||
{ LEAF("ExplicitlyPublishQosSetToDefault"), 1, "false", ABSOFF(explicitly_publish_qos_set_to_default), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element specifies whether QoS settings set to default values are explicitly published in the discovery protocol. Implementations are to use the default value for QoS settings not published, which allows a significant reduction of the amount of data that needs to be exchanged for the discovery protocol, but this requires all implementations to adhere to the default values specified by the specifications.</p>\n\
|
||||
<p>When interoperability is required with an implementation that does not follow the specifications in this regard, setting this option to true will help.</p>") },
|
||||
{ LEAF ("ManySocketsMode"), 1, "single", ABSOFF (many_sockets_mode), 0, uf_many_sockets_mode, 0, pf_many_sockets_mode,
|
||||
BLURB("<p>This option specifies whether a network socket will be created for each domain participant on a host. The specification seems to assume that each participant has a unique address, and setting this option will ensure this to be the case. This is not the default.</p>\n\
|
||||
<p>Disabling it slightly improves performance and reduces network traffic somewhat. It also causes the set of port numbers needed by DDSI2E to become predictable, which may be useful for firewall and NAT configuration.</p>") },
|
||||
{ LEAF("AssumeRtiHasPmdEndpoints"), 1, "false", ABSOFF(assume_rti_has_pmd_endpoints), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This option assumes ParticipantMessageData endpoints required by the liveliness protocol are present in RTI participants even when not properly advertised by the participant discovery protocol.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem internal_test_cfgelems[] = {
|
||||
{ LEAF("XmitLossiness"), 1, "0", ABSOFF(xmit_lossiness), 0, uf_int, 0, pf_int,
|
||||
BLURB("<p>This element controls the fraction of outgoing packets to drop, specified as samples per thousand.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem internal_watermarks_cfgelems[] = {
|
||||
{ LEAF("WhcLow"), 1, "1 kB", ABSOFF(whc_lowwater_mark), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element sets the low-water mark for the DDSI2E WHCs, expressed in bytes. A suspended writer resumes transmitting when its DDSI2E WHC shrinks to this size.</p>") },
|
||||
{ LEAF("WhcHigh"), 1, "100 kB", ABSOFF(whc_highwater_mark), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element sets the maximum allowed high-water mark for the DDSI2E WHCs, expressed in bytes. A writer is suspended when the WHC reaches this size.</p>") },
|
||||
{ LEAF("WhcHighInit"), 1, "30 kB", ABSOFF(whc_init_highwater_mark), 0, uf_maybe_memsize, 0, pf_maybe_memsize,
|
||||
BLURB("<p>This element sets the initial level of the high-water mark for the DDSI2E WHCs, expressed in bytes.</p>") },
|
||||
{ LEAF("WhcAdaptive|WhcAdaptative"), 1, "true", ABSOFF(whc_adaptive), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether DDSI2E will adapt the high-water mark to current traffic conditions, based on retransmit requests and transmit pressure.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem control_topic_cfgattrs[] = {
|
||||
{ DEPRECATED_ATTR("Enable"), 1, "false", ABSOFF(enable_control_topic), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether DDSI2E should create a topic to control DDSI2E's behaviour dynamically.<p>") },
|
||||
{ DEPRECATED_ATTR("InitialReset"), 1, "inf", ABSOFF(initial_deaf_mute_reset), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This element controls after how much time an initial deaf/mute state will automatically reset.<p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem control_topic_cfgelems[] = {
|
||||
{ DEPRECATED_LEAF ("Deaf"), 1, "false", ABSOFF (initial_deaf), 0, uf_deaf_mute, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether DDSI2E defaults to deaf mode or to normal mode. This controls both the initial behaviour and what behaviour it auto-reverts to.</p>") },
|
||||
{ DEPRECATED_LEAF ("Mute"), 1, "false", ABSOFF (initial_mute), 0, uf_deaf_mute, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether DDSI2E defaults to mute mode or to normal mode. This controls both the initial behaviour and what behaviour it auto-reverts to.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem rediscovery_blacklist_duration_attrs[] = {
|
||||
{ ATTR("enforce"), 1, "false", ABSOFF(prune_deleted_ppant.enforce_delay), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This attribute controls whether the configured time during which recently deleted participants will not be rediscovered (i.e., \"black listed\") is enforced and following complete removal of the participant in DDSI2E, or whether it can be rediscovered earlier provided all traces of that participant have been removed already.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem heartbeat_interval_attrs[] = {
|
||||
{ ATTR ("min"), 1, "5 ms", ABSOFF (const_hb_intv_min), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This attribute sets the minimum interval that must have passed since the most recent heartbeat from a writer, before another asynchronous (not directly related to writing) will be sent.</p>") },
|
||||
{ ATTR ("minsched"), 1, "20 ms", ABSOFF (const_hb_intv_sched_min), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This attribute sets the minimum interval for periodic heartbeats. Other events may still cause heartbeats to go out.</p>") },
|
||||
{ ATTR ("max"), 1, "8 s", ABSOFF (const_hb_intv_sched_max), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This attribute sets the maximum interval for periodic heartbeats.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem liveliness_monitoring_attrs[] = {
|
||||
{ ATTR("StackTraces"), 1, "true", ABSOFF(noprogress_log_stacktraces), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether or not to write stack traces to the DDSI2 trace when a thread fails to make progress (on select platforms only).</p>") },
|
||||
{ ATTR("Interval"), 1, "1s", ABSOFF(liveliness_monitoring_interval), 0, uf_duration_100ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This element controls the interval at which to check whether threads have been making progress.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem multiple_recv_threads_attrs[] = {
|
||||
{ ATTR("maxretries"), 1, "4294967295", ABSOFF(recv_thread_stop_maxretries), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>Receive threads dedicated to a single socket can only be triggered for termination by sending a packet. Reception of any packet will do, so termination failure due to packet loss is exceedingly unlikely, but to eliminate all risks, it will retry as many times as specified by this attribute before aborting.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem internal_cfgelems[] = {
|
||||
{ MOVED("MaxMessageSize", "CycloneDDS/General/MaxMessageSize") },
|
||||
{ MOVED("FragmentSize", "CycloneDDS/General/FragmentSize") },
|
||||
{ LEAF("DeliveryQueueMaxSamples"), 1, "256", ABSOFF(delivery_queue_maxsamples), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element controls the Maximum size of a delivery queue, expressed in samples. Once a delivery queue is full, incoming samples destined for that queue are dropped until space becomes available again.</p>") },
|
||||
{ LEAF("PrimaryReorderMaxSamples"), 1, "128", ABSOFF(primary_reorder_maxsamples), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element sets the maximum size in samples of a primary re-order administration. Each proxy writer has one primary re-order administration to buffer the packet flow in case some packets arrive out of order. Old samples are forwarded to secondary re-order administrations associated with readers in need of historical data.</p>") },
|
||||
{ LEAF("SecondaryReorderMaxSamples"), 1, "128", ABSOFF(secondary_reorder_maxsamples), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element sets the maximum size in samples of a secondary re-order administration. The secondary re-order administration is per reader in need of historical data.</p>") },
|
||||
{ LEAF("DefragUnreliableMaxSamples"), 1, "4", ABSOFF(defrag_unreliable_maxsamples), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element sets the maximum number of samples that can be defragmented simultaneously for a best-effort writers.</p>") },
|
||||
{ LEAF("DefragReliableMaxSamples"), 1, "16", ABSOFF(defrag_reliable_maxsamples), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element sets the maximum number of samples that can be defragmented simultaneously for a reliable writer. This has to be large enough to handle retransmissions of historical data in addition to new samples.</p>") },
|
||||
{ LEAF("BuiltinEndpointSet"), 1, "writers", ABSOFF(besmode), 0, uf_besmode, 0, pf_besmode,
|
||||
BLURB("<p>This element controls which participants will have which built-in endpoints for the discovery and liveliness protocols. Valid values are:</p>\n\
|
||||
<ul><li><i>full</i>: all participants have all endpoints;</li>\n\
|
||||
<li><i>writers</i>: all participants have the writers, but just one has the readers;</li>\n\
|
||||
<li><i>minimal</i>: only one participant has built-in endpoints.</li></ul>\n\
|
||||
<p>The default is <i>writers</i>, as this is thought to be compliant and reasonably efficient. <i>Minimal</i> may or may not be compliant but is most efficient, and <i>full</i> is inefficient but certain to be compliant. See also Internal/ConservativeBuiltinReaderStartup.</p>") },
|
||||
{ LEAF("MeasureHbToAckLatency"), 1, "false", ABSOFF(meas_hb_to_ack_latency), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element enables heartbeat-to-ack latency among DDSI2E services by prepending timestamps to Heartbeat and AckNack messages and calculating round trip times. This is non-standard behaviour. The measured latencies are quite noisy and are currently not used anywhere.</p>") },
|
||||
{ LEAF("UnicastResponseToSPDPMessages"), 1, "true", ABSOFF(unicast_response_to_spdp_messages), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether the response to a newly discovered participant is sent as a unicasted SPDP packet, instead of rescheduling the periodic multicasted one. There is no known benefit to setting this to <i>false</i>.</p>") },
|
||||
{ LEAF("SynchronousDeliveryPriorityThreshold"), 1, "0", ABSOFF(synchronous_delivery_priority_threshold), 0, uf_int, 0, pf_int,
|
||||
BLURB("<p>This element controls whether samples sent by a writer with QoS settings latency_budget <= SynchronousDeliveryLatencyBound and transport_priority greater than or equal to this element's value will be delivered synchronously from the \"recv\" thread, all others will be delivered asynchronously through delivery queues. This reduces latency at the expense of aggregate bandwidth.</p>") },
|
||||
{ LEAF("SynchronousDeliveryLatencyBound"), 1, "inf", ABSOFF(synchronous_delivery_latency_bound), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This element controls whether samples sent by a writer with QoS settings transport_priority >= SynchronousDeliveryPriorityThreshold and a latency_budget at most this element's value will be delivered synchronously from the \"recv\" thread, all others will be delivered asynchronously through delivery queues. This reduces latency at the expense of aggregate bandwidth.</p>") },
|
||||
{ LEAF("MaxParticipants"), 1, "0", ABSOFF(max_participants), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This elements configures the maximum number of DCPS domain participants this DDSI2E instance is willing to service. 0 is unlimited.</p>") },
|
||||
{ LEAF("AccelerateRexmitBlockSize"), 1, "0", ABSOFF(accelerate_rexmit_block_size), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>Proxy readers that are assumed to sill be retrieving historical data get this many samples retransmitted when they NACK something, even if some of these samples have sequence numbers outside the set covered by the NACK.</p>") },
|
||||
{ LEAF("RetransmitMerging"), 1, "never", ABSOFF(retransmit_merging), 0, uf_retransmit_merging, 0, pf_retransmit_merging,
|
||||
BLURB("<p>This elements controls the addressing and timing of retransmits. Possible values are:</p>\n\
|
||||
<ul><li><i>never</i>: retransmit only to the NACK-ing reader;</li>\n \
|
||||
<li><i>adaptive</i>: attempt to combine retransmits needed for reliability, but send historical (transient-local) data to the requesting reader only;</li>\n\
|
||||
<li><i>always</i>: do not distinguish between different causes, always try to merge.</li></ul>\n\
|
||||
<p>The default is <i>never</i>. See also Internal/RetransmitMergingPeriod.</p>") },
|
||||
{ LEAF("RetransmitMergingPeriod"), 1, "5 ms", ABSOFF(retransmit_merging_period), 0, uf_duration_us_1s, 0, pf_duration,
|
||||
BLURB("<p>This setting determines the size of the time window in which a NACK of some sample is ignored because a retransmit of that sample has been multicasted too recently. This setting has no effect on unicasted retransmits.</p>\n\
|
||||
<p>See also Internal/RetransmitMerging.</p>") },
|
||||
{ LEAF_W_ATTRS("HeartbeatInterval", heartbeat_interval_attrs), 1, "100 ms", ABSOFF(const_hb_intv_sched), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This element allows configuring the base interval for sending writer heartbeats and the bounds within which it can vary.</p>") },
|
||||
{ LEAF("MaxQueuedRexmitBytes"), 1, "50 kB", ABSOFF(max_queued_rexmit_bytes), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This setting limits the maximum number of bytes queued for retransmission. The default value of 0 is unlimited unless an AuxiliaryBandwidthLimit has been set, in which case it becomes NackDelay * AuxiliaryBandwidthLimit. It must be large enough to contain the largest sample that may need to be retransmitted.</p>") },
|
||||
{ LEAF("MaxQueuedRexmitMessages"), 1, "200", ABSOFF(max_queued_rexmit_msgs), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This settings limits the maximum number of samples queued for retransmission.</p>") },
|
||||
{ LEAF("LeaseDuration"), 1, "10 s", ABSOFF(lease_duration), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This setting controls the default participant lease duration. <p>") },
|
||||
{ LEAF("WriterLingerDuration"), 1, "1 s", ABSOFF(writer_linger_duration), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This setting controls the maximum duration for which actual deletion of a reliable writer with unacknowledged data in its history will be postponed to provide proper reliable transmission.<p>") },
|
||||
{ LEAF("MinimumSocketReceiveBufferSize"), 1, "default", ABSOFF(socket_min_rcvbuf_size), 0, uf_maybe_memsize, 0, pf_maybe_memsize,
|
||||
BLURB("<p>This setting controls the minimum size of socket receive buffers. The operating system provides some size receive buffer upon creation of the socket, this option can be used to increase the size of the buffer beyond that initially provided by the operating system. If the buffer size cannot be increased to the specified size, an error is reported.</p>\n\
|
||||
<p>The default setting is the word \"default\", which means DDSI2E will attempt to increase the buffer size to 1MB, but will silently accept a smaller buffer should that attempt fail.</p>") },
|
||||
{ LEAF("MinimumSocketSendBufferSize"), 1, "64 KiB", ABSOFF(socket_min_sndbuf_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This setting controls the minimum size of socket send buffers. This setting can only increase the size of the send buffer, if the operating system by default creates a larger buffer, it is left unchanged.</p>") },
|
||||
{ LEAF("NackDelay"), 1, "10 ms", ABSOFF(nack_delay), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This setting controls the delay between receipt of a HEARTBEAT indicating missing samples and a NACK (ignored when the HEARTBEAT requires an answer). However, no NACK is sent if a NACK had been scheduled already for a response earlier than the delay requests: then that NACK will incorporate the latest information.</p>") },
|
||||
{ LEAF("AutoReschedNackDelay"), 1, "1 s", ABSOFF(auto_resched_nack_delay), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This setting controls the interval with which a reader will continue NACK'ing missing samples in the absence of a response from the writer, as a protection mechanism against writers incorrectly stopping the sending of HEARTBEAT messages.</p>") },
|
||||
{ LEAF("PreEmptiveAckDelay"), 1, "10 ms", ABSOFF(preemptive_ack_delay), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This setting controls the delay between the discovering a remote writer and sending a pre-emptive AckNack to discover the range of data available.</p>") },
|
||||
{ LEAF("ScheduleTimeRounding"), 1, "0 ms", ABSOFF(schedule_time_rounding), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds.</p>") },
|
||||
#ifdef DDSI_INCLUDE_BANDWIDTH_LIMITING
|
||||
{ LEAF("AuxiliaryBandwidthLimit"), 1, "inf", ABSOFF(auxiliary_bandwidth_limit), 0, uf_bandwidth, 0, pf_bandwidth,
|
||||
BLURB("<p>This element specifies the maximum transmit rate of auxiliary traffic not bound to a specific channel, such as discovery traffic, as well as auxiliary traffic related to a certain channel if that channel has elected to share this global AuxiliaryBandwidthLimit. Bandwidth limiting uses a leaky bucket scheme. The default value \"inf\" means DDSI2E imposes no limitation, the underlying operating system and hardware will likely limit the maimum transmit rate.</p>") },
|
||||
#endif
|
||||
{ LEAF("DDSI2DirectMaxThreads"), 1, "1", ABSOFF(ddsi2direct_max_threads), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element sets the maximum number of extra threads for an experimental, undocumented and unsupported direct mode.</p>") },
|
||||
{ LEAF("SquashParticipants"), 1, "false", ABSOFF(squash_participants), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether DDSI2E advertises all the domain participants it serves in DDSI (when set to <i>false</i>), or rather only one domain participant (the one corresponding to the DDSI2E process; when set to <i>true</i>). In the latter case DDSI2E becomes the virtual owner of all readers and writers of all domain participants, dramatically reducing discovery traffic (a similar effect can be obtained by setting Internal/BuiltinEndpointSet to \"minimal\" but with less loss of information).</p>") },
|
||||
{ LEAF("SPDPResponseMaxDelay"), 1, "0 ms", ABSOFF(spdp_response_delay_max), 0, uf_duration_ms_1s, 0, pf_duration,
|
||||
BLURB("<p>Maximum pseudo-random delay in milliseconds between discovering a remote participant and responding to it.</p>") },
|
||||
{ LEAF("LateAckMode"), 1, "false", ABSOFF(late_ack_mode), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>Ack a sample only when it has been delivered, instead of when committed to delivering it.</p>") },
|
||||
{ LEAF("RetryOnRejectBestEffort"), 1, "false", ABSOFF(retry_on_reject_besteffort), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>Whether or not to locally retry pushing a received best-effort sample into the reader caches when resource limits are reached.</p>") },
|
||||
{ LEAF("GenerateKeyhash"), 1, "false", ABSOFF(generate_keyhash), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>When true, include keyhashes in outgoing data for topics with keys.</p>") },
|
||||
{ LEAF("MaxSampleSize"), 1, "2147483647 B", ABSOFF(max_sample_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This setting controls the maximum (CDR) serialised size of samples that DDSI2E will forward in either direction. Samples larger than this are discarded with a warning.</p>") },
|
||||
{ LEAF("WriteBatch"), 1, "false", ABSOFF(whc_batch), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element enables the batching of write operations. By default each write operation writes through the write cache and out onto the transport. Enabling write batching causes multiple small write operations to be aggregated within the write cache into a single larger write. This gives greater throughput at the expense of latency. Currently there is no mechanism for the write cache to automatically flush itself, so that if write batching is enabled, the application may have to use the dds_write_flush function to ensure that all samples are written.</p>") },
|
||||
{ LEAF_W_ATTRS("LivelinessMonitoring", liveliness_monitoring_attrs), 1, "false", ABSOFF(liveliness_monitoring), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether or not implementation should internally monitor its own liveliness. If liveliness monitoring is enabled, stack traces can be dumped automatically when some thread appears to have stopped making progress.</p>") },
|
||||
{ LEAF("MonitorPort"), 1, "-1", ABSOFF(monitor_port), 0, uf_int, 0, pf_int,
|
||||
BLURB("<p>This element allows configuring a service that dumps a text description of part the internal state to TCP clients. By default (-1), this is disabled; specifying 0 means a kernel-allocated port is used; a positive number is used as the TCP port number.</p>") },
|
||||
{ LEAF("AssumeMulticastCapable"), 1, "", ABSOFF(assumeMulticastCapable), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This element controls which network interfaces are assumed to be capable of multicasting even when the interface flags returned by the operating system state it is not (this provides a workaround for some platforms). It is a comma-separated lists of patterns (with ? and * wildcards) against which the interface names are matched.</p>") },
|
||||
{ LEAF("PrioritizeRetransmit"), 1, "true", ABSOFF(prioritize_retransmit), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether retransmits are prioritized over new data, speeding up recovery.</p>") },
|
||||
{ LEAF("UseMulticastIfMreqn"), 1, "0", ABSOFF(use_multicast_if_mreqn), 0, uf_int, 0, pf_int,
|
||||
BLURB("<p>Do not use.</p>") },
|
||||
{ LEAF("SendAsync"), 1, "false", ABSOFF(xpack_send_async), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element controls whether the actual sending of packets occurs on the same thread that prepares them, or is done asynchronously by another thread.</p>") },
|
||||
{ LEAF_W_ATTRS("RediscoveryBlacklistDuration", rediscovery_blacklist_duration_attrs), 1, "0s", ABSOFF(prune_deleted_ppant.delay), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This element controls for how long a remote participant that was previously deleted will remain on a blacklist to prevent rediscovery, giving the software on a node time to perform any cleanup actions it needs to do. To some extent this delay is required internally by DDSI2E, but in the default configuration with the 'enforce' attribute set to false, DDSI2E will reallow rediscovery as soon as it has cleared its internal administration. Setting it to too small a value may result in the entry being pruned from the blacklist before DDSI2E is ready, it is therefore recommended to set it to at least several seconds.</p>") },
|
||||
{ LEAF_W_ATTRS("MultipleReceiveThreads", multiple_recv_threads_attrs), 1, "default", ABSOFF(multiple_recv_threads), 0, uf_boolean_default, 0, pf_boolean_default,
|
||||
BLURB("<p>This element controls whether all traffic is handled by a single receive thread (false) or whether multiple receive threads may be used to improve latency (true). By default it is disabled on Windows because it appears that one cannot count on being able to send packets to oneself, which is necessary to stop the thread during shutdown. Currently multiple receive threads are only used for connectionless transport (e.g., UDP) and ManySocketsMode not set to single (the default).</p>") },
|
||||
{ MGROUP("ControlTopic", control_topic_cfgelems, control_topic_cfgattrs), 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
BLURB("<p>The ControlTopic element allows configured whether DDSI2E provides a special control interface via a predefined topic or not.<p>") },
|
||||
{ GROUP("Test", internal_test_cfgelems),
|
||||
BLURB("<p>Testing options.</p>") },
|
||||
{ GROUP("Watermarks", internal_watermarks_cfgelems),
|
||||
BLURB("<p>Watermarks for flow-control.</p>") },
|
||||
{ LEAF("EnableExpensiveChecks"), 1, "", ABSOFF(enabled_xchecks), 0, uf_xcheck, 0, pf_xcheck,
|
||||
BLURB("<p>This element enables expensive checks in builds with assertions enabled and is ignored otherwise. Recognised categories are:</p>\n\
|
||||
<ul><li><i>whc</i>: writer history cache checking</li>\n\
|
||||
<li><i>rhc</i>: reader history cache checking</li>\n\
|
||||
<p>In addition, there is the keyword <i>all</i> that enables all checks.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem sizing_cfgelems[] = {
|
||||
{ LEAF("ReceiveBufferSize"), 1, "1 MiB", ABSOFF(rbuf_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element sets the size of a single receive buffer. Many receive buffers may be needed. The minimum workable size a little bit larger than Sizing/ReceiveBufferChunkSize, and the value used is taken as the configured value and the actual minimum workable size.</p>") },
|
||||
{ LEAF("ReceiveBufferChunkSize"), 1, "128 KiB", ABSOFF(rmsg_chunk_size), 0, uf_memsize, 0, pf_memsize,
|
||||
BLURB("<p>This element specifies the size of one allocation unit in the receive buffer. Must be greater than the maximum packet size by a modest amount (too large packets are dropped). Each allocation is shrunk immediately after processing a message, or freed straightaway.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem discovery_ports_cfgelems[] = {
|
||||
{ LEAF("Base"), 1, "7400", ABSOFF(ports.base), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the base port number (refer to the DDSI 2.1 specification, section 9.6.1, constant PB).</p>") },
|
||||
{ LEAF("DomainGain"), 1, "250", ABSOFF(ports.dg), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the domain gain, relating domain ids to sets of port numbers (refer to the DDSI 2.1 specification, section 9.6.1, constant DG).</p>") },
|
||||
{ LEAF("ParticipantGain"), 1, "2", ABSOFF(ports.pg), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the participant gain, relating p0, participant index to sets of port numbers (refer to the DDSI 2.1 specification, section 9.6.1, constant PG).</p>") },
|
||||
{ LEAF("MulticastMetaOffset"), 1, "0", ABSOFF(ports.d0), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the port number for multicast meta traffic (refer to the DDSI 2.1 specification, section 9.6.1, constant d0).</p>") },
|
||||
{ LEAF("UnicastMetaOffset"), 1, "10", ABSOFF(ports.d1), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the port number for unicast meta traffic (refer to the DDSI 2.1 specification, section 9.6.1, constant d1).</p>") },
|
||||
{ LEAF("MulticastDataOffset"), 1, "1", ABSOFF(ports.d2), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the port number for multicast data traffic (refer to the DDSI 2.1 specification, section 9.6.1, constant d2).</p>") },
|
||||
{ LEAF("UnicastDataOffset"), 1, "11", ABSOFF(ports.d3), 0, uf_uint, 0, pf_uint,
|
||||
BLURB("<p>This element specifies the port number for unicast data traffic (refer to the DDSI 2.1 specification, section 9.6.1, constant d3).</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem tcp_cfgelems[] = {
|
||||
{ LEAF ("Enable"), 1, "default", ABSOFF (compat_tcp_enable), 0, uf_boolean_default, 0, pf_nop,
|
||||
BLURB("<p>This element enables the optional TCP transport - deprecated, use General/Transport instead.</p>") },
|
||||
{ LEAF("NoDelay"), 1, "true", ABSOFF(tcp_nodelay), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element enables the TCP_NODELAY socket option, preventing multiple DDSI messages being sent in the same TCP request. Setting this option typically optimises latency over throughput.</p>") },
|
||||
{ LEAF("Port"), 1, "-1", ABSOFF(tcp_port), 0, uf_dyn_port, 0, pf_int,
|
||||
BLURB("<p>This element specifies the TCP port number on which DDSI2E accepts connections. If the port is set it is used in entity locators, published with DDSI discovery. Dynamically allocated if zero. Disabled if -1 or not configured. If disabled other DDSI services will not be able to establish connections with the service, the service can only communicate by establishing connections to other services.</p>") },
|
||||
{ LEAF("ReadTimeout"), 1, "2 s", ABSOFF(tcp_read_timeout), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This element specifies the timeout for blocking TCP read operations. If this timeout expires then the connection is closed.</p>") },
|
||||
{ LEAF("WriteTimeout"), 1, "2 s", ABSOFF(tcp_write_timeout), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This element specifies the timeout for blocking TCP write operations. If this timeout expires then the connection is closed.</p>") },
|
||||
{ LEAF ("AlwaysUsePeeraddrForUnicast"), 1, "false", ABSOFF (tcp_use_peeraddr_for_unicast), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>Setting this to true means the unicast addresses in SPDP packets will be ignored and the peer address from the TCP connection will be used instead. This may help work around incorrectly advertised addresses when using TCP.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
#ifdef DDSI_INCLUDE_SSL
|
||||
static const struct cfgelem ssl_cfgelems[] = {
|
||||
{ LEAF("Enable"), 1, "false", ABSOFF(ssl_enable), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This enables SSL/TLS for TCP.</p>") },
|
||||
{ LEAF("CertificateVerification"), 1, "true", ABSOFF(ssl_verify), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>If disabled this allows SSL connections to occur even if an X509 certificate fails verification.</p>") },
|
||||
{ LEAF("VerifyClient"), 1, "true", ABSOFF(ssl_verify_client), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This enables an SSL server checking the X509 certificate of a connecting client.</p>") },
|
||||
{ LEAF("SelfSignedCertificates"), 1, "false", ABSOFF(ssl_self_signed), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This enables the use of self signed X509 certificates.</p>") },
|
||||
{ LEAF("KeystoreFile"), 1, "keystore", ABSOFF(ssl_keystore), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>The SSL/TLS key and certificate store file name. The keystore must be in PEM format.</p>") },
|
||||
{ LEAF("KeyPassphrase"), 1, "secret", ABSOFF(ssl_key_pass), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>The SSL/TLS key pass phrase for encrypted keys.</p>") },
|
||||
{ LEAF("Ciphers"), 1, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", ABSOFF(ssl_ciphers), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>The set of ciphers used by SSL/TLS</p>") },
|
||||
{ LEAF("EntropyFile"), 1, "", ABSOFF(ssl_rand_file), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>The SSL/TLS random entropy file name.</p>") },
|
||||
{ LEAF("MinimumTLSVersion"), 1, "1.3", ABSOFF(ssl_min_version), 0, uf_min_tls_version, 0, pf_min_tls_version,
|
||||
BLURB("<p>The minimum TLS version that may be negotiated, valid values are 1.2 and 1.3.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct cfgelem tp_cfgelems[] = {
|
||||
{ LEAF("Enable"), 1, "false", ABSOFF(tp_enable), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This element enables the optional thread pool.</p>") },
|
||||
{ LEAF("Threads"), 1, "4", ABSOFF(tp_threads), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This elements configures the initial number of threads in the thread pool.</p>") },
|
||||
{ LEAF("ThreadMax"), 1, "8", ABSOFF(tp_max_threads), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This elements configures the maximum number of threads in the thread pool.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem discovery_peer_cfgattrs[] = {
|
||||
{ ATTR("Address"), 1, NULL, RELOFF(config_peer_listelem, peer), 0, uf_ipv4, ff_free, pf_string,
|
||||
BLURB("<p>This element specifies an IP address to which discovery packets must be sent, in addition to the default multicast address (see also General/AllowMulticast). Both a hostnames and a numerical IP address is accepted; the hostname or IP address may be suffixed with :PORT to explicitly set the port to which it must be sent. Multiple Peers may be specified.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem discovery_peers_group_cfgelems[] = {
|
||||
{ MGROUP("Peer", NULL, discovery_peer_cfgattrs), INT_MAX, NULL, ABSOFF(peers_group), if_peer, 0, 0, 0,
|
||||
BLURB("<p>This element statically configures an addresses for discovery.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem discovery_peers_cfgelems[] = {
|
||||
{ MGROUP("Peer", NULL, discovery_peer_cfgattrs), INT_MAX, NULL, ABSOFF(peers), if_peer, 0, 0, 0,
|
||||
BLURB("<p>This element statically configures an addresses for discovery.</p>") },
|
||||
{ GROUP("Group", discovery_peers_group_cfgelems),
|
||||
BLURB("<p>This element statically configures a fault tolerant group of addresses for discovery. Each member of the group is tried in sequence until one succeeds.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem discovery_cfgelems[] = {
|
||||
{ LEAF("Tag"), 0, "", ABSOFF(domainTag), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>String extension for domain id that remote participants must match to be discovered.</p>") },
|
||||
{ LEAF ("ExternalDomainId"), 1, "default", ABSOFF (extDomainId), 0, uf_maybe_int32, 0, pf_maybe_int32,
|
||||
BLURB("<p>An override for the domain id, to be used in discovery and for determining the port number mapping. This allows creating multiple domains in a single process while making them appear as a single domain on the network. The value \"default\" disables the override.</p>") },
|
||||
{ LEAF("DSGracePeriod"), 1, "30 s", ABSOFF(ds_grace_period), 0, uf_duration_inf, 0, pf_duration,
|
||||
BLURB("<p>This setting controls for how long endpoints discovered via a Cloud discovery service will survive after the discovery service disappeared, allowing reconnect without loss of data when the discovery service restarts (or another instance takes over).</p>") },
|
||||
{ GROUP("Peers", discovery_peers_cfgelems),
|
||||
BLURB("<p>This element statically configures addresses for discovery.</p>") },
|
||||
{ LEAF("ParticipantIndex"), 1, "none", ABSOFF(participantIndex), 0, uf_participantIndex, 0, pf_participantIndex,
|
||||
BLURB("<p>This element specifies the DDSI participant index used by this instance of the DDSI2E service for discovery purposes. Only one such participant id is used, independent of the number of actual DomainParticipants on the node. It is either:</p>\n\
|
||||
<ul><li><i>auto</i>: which will attempt to automatically determine an available participant index (see also Discovery/MaxAutoParticipantIndex), or</li>\n \
|
||||
<li>a non-negative integer less than 120, or</li>\n\
|
||||
<li><i>none</i>:, which causes it to use arbitrary port numbers for unicast sockets which entirely removes the constraints on the participant index but makes unicast discovery impossible.</li></ul>\n\
|
||||
<p>The default is <i>auto</i>. The participant index is part of the port number calculation and if predictable port numbers are needed and fixing the participant index has no adverse effects, it is recommended that the second be option be used.</p>") },
|
||||
{ LEAF("MaxAutoParticipantIndex"), 1, "9", ABSOFF(maxAutoParticipantIndex), 0, uf_natint, 0, pf_int,
|
||||
BLURB("<p>This element specifies the maximum DDSI participant index selected by this instance of the DDSI2E service if the Discovery/ParticipantIndex is \"auto\".</p>") },
|
||||
{ LEAF("SPDPMulticastAddress"), 1, "239.255.0.1", ABSOFF(spdpMulticastAddressString), 0, uf_ipv4, ff_free, pf_string,
|
||||
BLURB("<p>This element specifies the multicast address that is used as the destination for the participant discovery packets. In IPv4 mode the default is the (standardised) 239.255.0.1, in IPv6 mode it becomes ff02::ffff:239.255.0.1, which is a non-standardised link-local multicast address.</p>") },
|
||||
{ LEAF("SPDPInterval"), 1, "30 s", ABSOFF(spdp_interval), 0, uf_duration_ms_1hr, 0, pf_duration,
|
||||
BLURB("<p>This element specifies the interval between spontaneous transmissions of participant discovery packets.</p>") },
|
||||
{ LEAF("DefaultMulticastAddress"), 1, "auto", ABSOFF(defaultMulticastAddressString), 0, uf_networkAddress, 0, pf_networkAddress,
|
||||
BLURB("<p>This element specifies the default multicast address for all traffic other than participant discovery packets. It defaults to Discovery/SPDPMulticastAddress.</p>") },
|
||||
{ GROUP("Ports", discovery_ports_cfgelems),
|
||||
BLURB("<p>The Ports element allows specifying various parameters related to the port numbers used for discovery. These all have default values specified by the DDSI 2.1 specification and rarely need to be changed.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem tracing_cfgelems[] = {
|
||||
{ LEAF("Category|EnableCategory"), 1, "", 0, 0, 0, uf_tracemask, 0, pf_tracemask,
|
||||
BLURB("<p>This element enables individual logging categories. These are enabled in addition to those enabled by Tracing/Verbosity. Recognised categories are:</p>\n\
|
||||
<ul><li><i>fatal</i>: all fatal errors, errors causing immediate termination</li>\n\
|
||||
<li><i>error</i>: failures probably impacting correctness but not necessarily causing immediate termination</li>\n\
|
||||
<li><i>warning</i>: abnormal situations that will likely not impact correctness</li>\n\
|
||||
<li><i>config</i>: full dump of the configuration</li>\n\
|
||||
<li><i>info</i>: general informational notices</li>\n\
|
||||
<li><i>discovery</i>: all discovery activity</li>\n\
|
||||
<li><i>data</i>: include data content of samples in traces</li>\n\
|
||||
<li><i>radmin</i>: receive buffer administration</li>\n\
|
||||
<li><i>timing</i>: periodic reporting of CPU loads per thread</li>\n\
|
||||
<li><i>traffic</i>: periodic reporting of total outgoing data</li>\n\
|
||||
<li><i>whc</i>: tracing of writer history cache changes</li>\n\
|
||||
<li><i>tcp</i>: tracing of TCP-specific activity</li>\n\
|
||||
<li><i>topic</i>: tracing of topic definitions</li>\n\
|
||||
<li>>i>plist</i>: tracing of discovery parameter list interpretation</li> </ul>\n\
|
||||
<p>In addition, there is the keyword <i>trace</i> that enables all but <i>radmin</i>, <i>topic</i>, <i>plist</i> and <i>whc</i></p>.\n\
|
||||
<p>The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful is <i>trace</i>.</p>") },
|
||||
{ LEAF("Verbosity"), 1, "none", 0, 0, 0, uf_verbosity, 0, pf_nop,
|
||||
BLURB("<p>This element enables standard groups of categories, based on a desired verbosity level. This is in addition to the categories enabled by the Tracing/Category setting. Recognised verbosity levels and the categories they map to are:</p>\n\
|
||||
<ul><li><i>none</i>: no DDSI2E log</li>\n\
|
||||
<li><i>severe</i>: error and fatal</li>\n\
|
||||
<li><i>warning</i>: <i>severe</i> + warning</li>\n\
|
||||
<li><i>info</i>: <i>warning</i> + info</li>\n\
|
||||
<li><i>config</i>: <i>info</i> + config</li>\n\
|
||||
<li><i>fine</i>: <i>config</i> + discovery</li>\n\
|
||||
<li><i>finer</i>: <i>fine</i> + traffic and timing</li>\n\
|
||||
<li><i>finest</i>: <i>finer</i> + trace</li></ul>\n\
|
||||
<p>While <i>none</i> prevents any message from being written to a DDSI2 log file.</p>\n\
|
||||
<p>The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are <i>config</i>, <i>fine</i> and <i>finest</i>.</p>") },
|
||||
{ LEAF("OutputFile"), 1, "cyclonedds.log", ABSOFF(tracefile), 0, uf_tracingOutputFileName, ff_free, pf_string,
|
||||
BLURB("<p>This option specifies where the logging is printed to. Note that <i>stdout</i> and <i>stderr</i> are treated as special values, representing \"standard out\" and \"standard error\" respectively. No file is created unless logging categories are enabled using the Tracing/Verbosity or Tracing/EnabledCategory settings.</p>") },
|
||||
{ LEAF("AppendToFile"), 1, "false", ABSOFF(tracingAppendToFile), 0, uf_boolean, 0, pf_boolean,
|
||||
BLURB("<p>This option specifies whether the output is to be appended to an existing log file. The default is to create a new log file each time, which is generally the best option if a detailed log is generated.</p>") },
|
||||
{ LEAF("PacketCaptureFile"), 1, "", ABSOFF(pcap_file), 0, uf_string, ff_free, pf_string,
|
||||
BLURB("<p>This option specifies the file to which received and sent packets will be logged in the \"pcap\" format suitable for analysis using common networking tools, such as WireShark. IP and UDP headers are fictitious, in particular the destination address of received packets. The TTL may be used to distinguish between sent and received packets: it is 255 for sent packets and 128 for received ones. Currently IPv4 only.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem domain_cfgattrs[] = {
|
||||
{ ATTR("Id"), 0, "any", ABSOFF(domainId), 0, uf_domainId, 0, pf_domainId,
|
||||
BLURB("<p>Domain id this configuration applies to, or \"any\" if it applies to all domain ids.</p>") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem domain_cfgelems[] = {
|
||||
{ MOVED("Id", "CycloneDDS/Domain[@Id]") },
|
||||
{ GROUP("General", general_cfgelems),
|
||||
BLURB("<p>The General element specifies overall DDSI2E service settings.</p>") },
|
||||
#ifdef DDSI_INCLUDE_SECURITY
|
||||
{ MGROUP ("DDSSecurity", security_omg_config_elements, NULL), INT_MAX, NULL, ABSOFF(omg_security_configuration), if_omg_security, 0, 0, 0,
|
||||
BLURB("<p>This element is used to configure DDSI2E with the DDS Security specification plugins and settings.</p>") },
|
||||
#endif
|
||||
#ifdef DDSI_INCLUDE_NETWORK_PARTITIONS
|
||||
{ GROUP("Partitioning", partitioning_cfgelems),
|
||||
BLURB("<p>The Partitioning element specifies DDSI2E network partitions and how DCPS partition/topic combinations are mapped onto the network partitions.</p>") },
|
||||
#endif
|
||||
#ifdef DDSI_INCLUDE_NETWORK_CHANNELS
|
||||
{ GROUP("Channels", channels_cfgelems),
|
||||
BLURB("<p>This element is used to group a set of channels. The channels are independent data paths through DDSI2E and by using separate threads and setting their priorities appropriately, chanenls can be used to map transport priorities to operating system scheduler priorities, ensuring system-wide end-to-end priority preservation.</p>") },
|
||||
#endif
|
||||
{ GROUP("Threads", threads_cfgelems),
|
||||
BLURB("<p>This element is used to set thread properties.</p>") },
|
||||
{ GROUP("Sizing", sizing_cfgelems),
|
||||
BLURB("<p>The Sizing element specifies a variety of configuration settings dealing with expected system sizes, buffer sizes, &c.</p>") },
|
||||
{ GROUP("Compatibility", compatibility_cfgelems),
|
||||
BLURB("<p>The Compatibility elements allows specifying various settings related to compatability with standards and with other DDSI implementations.</p>") },
|
||||
{ GROUP("Discovery", discovery_cfgelems),
|
||||
BLURB("<p>The Discovery element allows specifying various parameters related to the discovery of peers.</p>") },
|
||||
{ GROUP("Tracing", tracing_cfgelems),
|
||||
BLURB("<p>The Tracing element controls the amount and type of information that is written into the tracing log by the DDSI service. This is useful to track the DDSI service during application development.</p>") },
|
||||
{ GROUP("Internal|Unsupported", internal_cfgelems),
|
||||
BLURB("<p>The Internal elements deal with a variety of settings that evolving and that are not necessarily fully supported. For the vast majority of the Internal settings, the functionality per-se is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options.</p>") },
|
||||
{ GROUP("TCP", tcp_cfgelems),
|
||||
BLURB("<p>The TCP element allows specifying various parameters related to running DDSI over TCP.</p>") },
|
||||
{ GROUP("ThreadPool", tp_cfgelems),
|
||||
BLURB("<p>The ThreadPool element allows specifying various parameters related to using a thread pool to send DDSI messages to multiple unicast addresses (TCP or UDP).</p>") },
|
||||
#ifdef DDSI_INCLUDE_SSL
|
||||
{ GROUP("SSL", ssl_cfgelems),
|
||||
BLURB("<p>The SSL element allows specifying various parameters related to using SSL/TLS for DDSI over TCP.</p>") },
|
||||
#endif
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem root_cfgelems[] = {
|
||||
{ GROUP_W_ATTRS("Domain", domain_cfgelems, domain_cfgattrs),
|
||||
BLURB("<p>The General element specifying Domain related settings.</p>") },
|
||||
{ MOVED("General", "CycloneDDS/Domain/General") },
|
||||
#ifdef DDSI_INCLUDE_NETWORK_PARTITIONS
|
||||
{ MOVED("Partitioning", "CycloneDDS/Domain/Partitioning") },
|
||||
#endif
|
||||
#ifdef DDSI_INCLUDE_NETWORK_CHANNELS
|
||||
{ MOVED("Channels", "CycloneDDS/Domain/Channels") },
|
||||
#endif
|
||||
{ MOVED("Threads", "CycloneDDS/Domain/Threads") },
|
||||
{ MOVED("Sizing", "CycloneDDS/Domain/Sizing") },
|
||||
{ MOVED("Compatibility", "CycloneDDS/Domain/Compatibility") },
|
||||
{ MOVED("Discovery", "CycloneDDS/Domain/Discovery") },
|
||||
{ MOVED("Tracing", "CycloneDDS/Domain/Tracing") },
|
||||
{ MOVED("Internal|Unsupported", "CycloneDDS/Domain/Internal") },
|
||||
{ MOVED("TCP", "CycloneDDS/Domain/TCP") },
|
||||
{ MOVED("ThreadPool", "CycloneDDS/Domain/ThreadPool") },
|
||||
#ifdef DDSI_INCLUDE_SECURITY
|
||||
{ MOVED("DDSSecurity", "CycloneDDS/Domain/DDSSecurity") },
|
||||
#endif
|
||||
#ifdef DDSI_INCLUDE_SSL
|
||||
{ MOVED("SSL", "CycloneDDS/Domain/SSL") },
|
||||
#endif
|
||||
{ MOVED("DDSI2E|DDSI2", "CycloneDDS/Domain") },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem root_cfgattrs[] = {
|
||||
{ ATTR("xmlns"), 0, "", 0, 0, 0, uf_nop, 0, pf_nop, NULL },
|
||||
{ ATTR("xmlns:xsi"), 0, "", 0, 0, 0, uf_nop, 0, pf_nop, NULL },
|
||||
{ ATTR("xsi:schemaLocation"), 0, "", 0, 0, 0, uf_nop, 0, pf_nop, NULL },
|
||||
{ ATTR("xsi:noNamespaceSchemaLocation"), 0, "", 0, 0, 0, uf_nop, 0, pf_nop, NULL },
|
||||
END_MARKER
|
||||
};
|
||||
|
||||
static const struct cfgelem cyclonedds_root_cfgelems[] = {
|
||||
{ "CycloneDDS", root_cfgelems, root_cfgattrs, NODATA, BLURB("CycloneDDS configuration") },
|
||||
END_MARKER
|
||||
};
|
||||
#include "dds/ddsi/ddsi_cfgelems.h"
|
||||
|
||||
static const struct cfgelem root_cfgelem = {
|
||||
"/", cyclonedds_root_cfgelems, NULL, NODATA, NULL
|
||||
"/", cyclonedds_root_cfgelems, NULL, NODATA
|
||||
};
|
||||
|
||||
#undef ATTR
|
||||
#undef GROUP
|
||||
#undef LEAF_W_ATTRS
|
||||
#undef LEAF
|
||||
#undef WILDCARD
|
||||
#undef END_MARKER
|
||||
#undef DEPRECATED
|
||||
#undef MEMBER
|
||||
#undef MEMBEROF
|
||||
#undef FUNCTIONS
|
||||
#undef NOMEMBER
|
||||
#undef NOFUNCTIONS
|
||||
#undef NODATA
|
||||
#undef RELOFF
|
||||
#undef ABSOFF
|
||||
#undef CO
|
||||
#undef END_MARKER
|
||||
#undef MOVED
|
||||
#undef NOP
|
||||
#undef WILDCARD
|
||||
#undef EXPAND
|
||||
#undef BOOL
|
||||
#undef INT
|
||||
#undef STRING
|
||||
#undef ENUM
|
||||
#undef LIST
|
||||
#undef GROUP
|
||||
#undef ELEMENT
|
||||
|
||||
static const struct unit unittab_duration[] = {
|
||||
{ "ns", 1 },
|
||||
|
|
|
@ -99,9 +99,9 @@ CU_Test(ddssec_config, empty, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
{
|
||||
dds_entity_t domain;
|
||||
const char *log_expected[] = {
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -148,9 +148,9 @@ CU_Test(ddssec_config, missing, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
{
|
||||
dds_entity_t domain;
|
||||
const char *log_expected[] = {
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/DDSSecurity/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCertificate/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/IdentityCA/#text: element missing in configuration*",
|
||||
"config: //CycloneDDS/Domain/Security/Authentication/PrivateKey/#text: element missing in configuration*",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -185,26 +185,26 @@ CU_Test(ddssec_config, all, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
{
|
||||
dds_entity_t domain, participant;
|
||||
const char *log_expected[] = {
|
||||
"config: Domain/DDSSecurity/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Password/#text: testtext_Password_testtext*",
|
||||
"config: Domain/DDSSecurity/Authentication/TrustedCADirectory/#text: testtext_Dir_testtext*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/Password/#text: testtext_Password_testtext*",
|
||||
"config: Domain/Security/Authentication/TrustedCADirectory/#text: testtext_Dir_testtext*",
|
||||
"config: Domain/Security/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/Security/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/Security/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/Security/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
/* The config should have been parsed into the participant QoS. */
|
||||
PARTICIPANT_QOS_ALL_OK ("", ",0:\"dds.sec.auth.password\":\"testtext_Password_testtext\",0:\"dds.sec.auth.trusted_ca_dir\":\"testtext_Dir_testtext\"", ""),
|
||||
NULL
|
||||
|
@ -256,26 +256,26 @@ CU_Test(ddssec_config, security, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
{
|
||||
dds_entity_t domain, participant;
|
||||
const char *log_expected[] = {
|
||||
"config: Domain/DDSSecurity/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Password/#text: {}*",
|
||||
"config: Domain/DDSSecurity/Authentication/TrustedCADirectory/#text: {}*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/Password/#text: {}*",
|
||||
"config: Domain/Security/Authentication/TrustedCADirectory/#text: {}*",
|
||||
"config: Domain/Security/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/Security/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/Security/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/Security/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
/* The config should have been parsed into the participant QoS. */
|
||||
PARTICIPANT_QOS_ALL_OK ("", ",0:\"dds.sec.auth.password\":\"\",0:\"dds.sec.auth.trusted_ca_dir\":\"\"", ""),
|
||||
NULL
|
||||
|
@ -324,26 +324,26 @@ CU_Test(ddssec_config, deprecated, .init = ddsrt_init, .fini = ddsrt_fini)
|
|||
{
|
||||
dds_entity_t domain, participant;
|
||||
const char *log_expected[] = {
|
||||
"config: Domain/DDSSecurity/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/DDSSecurity/Authentication/Password/#text: testtext_Password_testtext*",
|
||||
"config: Domain/DDSSecurity/Authentication/TrustedCADirectory/#text: testtext_Dir_testtext*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/DDSSecurity/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/DDSSecurity/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/DDSSecurity/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library/#text: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@path]: "WRAPPERLIB_PATH("dds_security_authentication_wrapper")"*",
|
||||
"config: Domain/Security/Authentication/Library[@initFunction]: init_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/Library[@finalizeFunction]: finalize_test_authentication_all_ok*",
|
||||
"config: Domain/Security/Authentication/IdentityCertificate/#text: "TEST_IDENTITY_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/IdentityCA/#text: "TEST_IDENTITY_CA_CERTIFICATE_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/PrivateKey/#text: "TEST_IDENTITY_PRIVATE_KEY_DUMMY"*",
|
||||
"config: Domain/Security/Authentication/Password/#text: testtext_Password_testtext*",
|
||||
"config: Domain/Security/Authentication/TrustedCADirectory/#text: testtext_Dir_testtext*",
|
||||
"config: Domain/Security/AccessControl/Library/#text: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@path]: "WRAPPERLIB_PATH("dds_security_access_control_wrapper")"*",
|
||||
"config: Domain/Security/AccessControl/Library[@initFunction]: init_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/Library[@finalizeFunction]: finalize_test_access_control_all_ok*",
|
||||
"config: Domain/Security/AccessControl/PermissionsCA/#text: file:Permissions_CA.pem*",
|
||||
"config: Domain/Security/AccessControl/Governance/#text: file:Governance.p7s*",
|
||||
"config: Domain/Security/AccessControl/Permissions/#text: file:Permissions.p7s*",
|
||||
"config: Domain/Security/Cryptographic/Library/#text: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@path]: "WRAPPERLIB_PATH("dds_security_cryptography_wrapper")"*",
|
||||
"config: Domain/Security/Cryptographic/Library[@initFunction]: init_test_cryptography_all_ok*",
|
||||
"config: Domain/Security/Cryptographic/Library[@finalizeFunction]: finalize_test_cryptography_all_ok*",
|
||||
/* The config should have been parsed into the participant QoS. */
|
||||
PARTICIPANT_QOS_ALL_OK ("", ",0:\"dds.sec.auth.password\":\"testtext_Password_testtext\",0:\"dds.sec.auth.trusted_ca_dir\":\"testtext_Dir_testtext\"", ""),
|
||||
NULL
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
set(CMAKE_INSTALL_TOOLSDIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/tools")
|
||||
add_subdirectory(pubsub)
|
||||
add_subdirectory(ddsls)
|
||||
add_subdirectory(ddsconf)
|
||||
if(BUILD_IDLC)
|
||||
add_subdirectory(ddsperf)
|
||||
endif()
|
||||
|
|
17
src/tools/ddsconf/CMakeLists.txt
Normal file
17
src/tools/ddsconf/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the Eclipse Public License v. 2.0 which is available at
|
||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
# v. 1.0 which is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
#
|
||||
add_executable(ddsconf ddsconf.c rnc.c md.c xsd.c)
|
||||
target_link_libraries(ddsconf PRIVATE ddsc)
|
||||
target_include_directories(ddsconf
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:ddsc,INCLUDE_DIRECTORIES>>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
653
src/tools/ddsconf/ddsconf.c
Normal file
653
src/tools/ddsconf/ddsconf.c
Normal file
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dds/ddsrt/heap.h"
|
||||
#include "dds/ddsrt/io.h"
|
||||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/misc.h"
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
|
||||
/* configuration units */
|
||||
#define UNIT(str, ...) { .name = str, __VA_ARGS__ }
|
||||
#define DESCRIPTION(str) .description = str
|
||||
#define PATTERN(str) .pattern = str
|
||||
#define END_MARKER { NULL, NULL, NULL }
|
||||
|
||||
#include "dds/ddsi/ddsi_cfgunits.h"
|
||||
/* undefine unit macros */
|
||||
#undef UNIT
|
||||
#undef DESCRIPTION
|
||||
#undef PATTERN
|
||||
#undef END_MARKER
|
||||
|
||||
|
||||
/* configuration elements */
|
||||
#define DEPRECATED(name) "|" name
|
||||
#define MEMBER(name) /* drop */
|
||||
#define MEMBEROF(parent,name) /* drop */
|
||||
#define FUNCTIONS(...) /* drop */
|
||||
#define DESCRIPTION(str) .description = str
|
||||
#define RANGE(str) .range = str
|
||||
#define UNIT(str) .unit = str
|
||||
#define VALUES(...) .values = (const char *[]){ __VA_ARGS__, NULL }
|
||||
|
||||
#define NOMEMBER /* drop */
|
||||
#define NOFUNCTIONS /* drop */
|
||||
#define NOMETADATA { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL }
|
||||
#define END_MARKER { NULL, NULL, NULL, 0, NULL, NULL, NOMETADATA }
|
||||
|
||||
#define ELEMENT(name, elems, attrs, multip, dflt, desc, ...) \
|
||||
{ name, elems, attrs, multip, dflt, desc, { __VA_ARGS__ } }
|
||||
|
||||
#define MOVED(name, whereto) \
|
||||
{ ">" name, NULL, NULL, 0, whereto, NULL, NOMETADATA }
|
||||
|
||||
#define EXPAND(macro, args) macro args /* Visual Studio */
|
||||
|
||||
#define NOP(name) \
|
||||
EXPAND(ELEMENT, (name, NULL, NULL, 1, NULL, NULL, .type = "nop"))
|
||||
#define BOOL(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "bool", __VA_ARGS__))
|
||||
#define INT(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "int", __VA_ARGS__))
|
||||
#define ENUM(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "enum", __VA_ARGS__))
|
||||
#define STRING(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "string", __VA_ARGS__))
|
||||
#define LIST(name, attrs, multip, dflt, ofst, funcs, desc, ...) \
|
||||
EXPAND(ELEMENT, (name, NULL, attrs, multip, dflt, desc, .type = "list", __VA_ARGS__))
|
||||
#define GROUP(name, elems, attrs, multip, ofst, funcs, desc) \
|
||||
EXPAND(ELEMENT, (name, elems, attrs, multip, NULL, desc, .type = "group"))
|
||||
|
||||
#include "dds/ddsi/ddsi_cfgelems.h"
|
||||
/* undefine element macros */
|
||||
#undef DEPRECATED
|
||||
#undef MEMBER
|
||||
#undef MEMBEROF
|
||||
#undef FUNCTIONS
|
||||
#undef DESCRIPTION
|
||||
#undef RANGE
|
||||
#undef UNIT
|
||||
#undef VALUES
|
||||
#undef NOMEMBER
|
||||
#undef NOFUNCTIONS
|
||||
#undef NOMETADATA
|
||||
#undef END_MARKER
|
||||
#undef ELEMENT
|
||||
#undef MOVED
|
||||
#undef NOP
|
||||
#undef BOOL
|
||||
#undef INT
|
||||
#undef ENUM
|
||||
#undef STRING
|
||||
#undef LIST
|
||||
#undef GROUP
|
||||
|
||||
|
||||
const char *schema(void) { return "config"; }
|
||||
|
||||
const char *url(void) { return "https://cdds.io/config"; }
|
||||
|
||||
const char *name(const struct cfgelem *elem)
|
||||
{
|
||||
if (elem->meta.name)
|
||||
return (const char *)elem->meta.name;
|
||||
return elem->name;
|
||||
}
|
||||
|
||||
static char spaces[32];
|
||||
|
||||
void print(FILE *out, unsigned int cols, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
assert((size_t)cols < sizeof(spaces));
|
||||
spaces[cols] = '\0';
|
||||
fprintf(out, "%s", spaces);
|
||||
spaces[cols] = ' ';
|
||||
va_start(ap, fmt);
|
||||
vfprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
static const char fmt[] = "usage: %s [OPTIONS] -f FORMAT\n";
|
||||
fprintf(stderr, fmt, prog);
|
||||
}
|
||||
|
||||
static void help(const char *prog)
|
||||
{
|
||||
static const char fmt[] = "\
|
||||
usage: %s -f FORMAT\n\
|
||||
\n\
|
||||
OPTIONS:\n\
|
||||
-h print help message\n\
|
||||
-f FORMAT output format. one of md, rnc or xsd\n\
|
||||
-o FILENAME output file. specify - to use stdout\n\
|
||||
";
|
||||
|
||||
fprintf(stdout, fmt, prog);
|
||||
}
|
||||
|
||||
struct cfgelem *nextelem(const struct cfgelem *list, const struct cfgelem *elem)
|
||||
{
|
||||
const struct cfgelem *next = NULL;
|
||||
if (list) {
|
||||
const struct cfgelem *ce = list;
|
||||
/* find next lexicographic element in list */
|
||||
for (; ce && ce->name; ce++) {
|
||||
if (ismoved(ce) || isdeprecated(ce) || isnop(ce))
|
||||
continue;
|
||||
if ((!elem || strcmp(ce->name, elem->name) > 0) &&
|
||||
(!next || strcmp(ce->name, next->name) < 0))
|
||||
next = ce;
|
||||
}
|
||||
}
|
||||
return (struct cfgelem *)next;
|
||||
}
|
||||
|
||||
struct cfgelem *firstelem(const struct cfgelem *list)
|
||||
{
|
||||
return nextelem(list, NULL);
|
||||
}
|
||||
|
||||
const struct cfgunit *findunit(const struct cfgunit *units, const char *name)
|
||||
{
|
||||
for (const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
if (strcmp(cu->name, name) == 0) {
|
||||
return cu;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ismoved(const struct cfgelem *elem)
|
||||
{
|
||||
return elem && elem->name && elem->name[0] == '>';
|
||||
}
|
||||
|
||||
int isdeprecated(const struct cfgelem *elem)
|
||||
{
|
||||
return elem && elem->name && elem->name[0] == '|';
|
||||
}
|
||||
|
||||
int isgroup(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "group") == 0;
|
||||
}
|
||||
|
||||
int isnop(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "nop") == 0;
|
||||
}
|
||||
|
||||
int isbool(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "bool") == 0;
|
||||
}
|
||||
|
||||
int isint(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "int") == 0;
|
||||
}
|
||||
|
||||
int isstring(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "string") == 0;
|
||||
}
|
||||
|
||||
int isenum(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "enum") == 0;
|
||||
}
|
||||
|
||||
int islist(const struct cfgelem *elem)
|
||||
{
|
||||
return strcmp(elem->meta.type, "list") == 0;
|
||||
}
|
||||
|
||||
int minimum(const struct cfgelem *elem)
|
||||
{
|
||||
switch (elem->multiplicity) {
|
||||
case 0: /* special case, treat as-if 1 */
|
||||
case 1: /* required if there is no default */
|
||||
if (isgroup(elem))
|
||||
return 0;
|
||||
return (!elem->value);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int maximum(const struct cfgelem *elem)
|
||||
{
|
||||
switch (elem->multiplicity) {
|
||||
case INT_MAX:
|
||||
return 0;
|
||||
case 0:
|
||||
case 1:
|
||||
return 1;
|
||||
default:
|
||||
return elem->multiplicity;
|
||||
}
|
||||
}
|
||||
|
||||
int haschildren(const struct cfgelem *elem)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (elem->children != NULL) {
|
||||
for (const struct cfgelem *e = elem->children; e->name; e++) {
|
||||
cnt += !(ismoved(e) || isdeprecated(e));
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int hasattributes(const struct cfgelem *elem)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (elem->attributes != NULL) {
|
||||
for (const struct cfgelem *e = elem->attributes; e->name; e++) {
|
||||
cnt += !(ismoved(e) || isdeprecated(e));
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* remove element aliases "s/\|.*$//" */
|
||||
static int sanitize_names(struct cfgelem *elem)
|
||||
{
|
||||
char *end;
|
||||
if (!elem->name || ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if ((end = strchr(elem->name, '|'))) {
|
||||
assert(!elem->meta.name);
|
||||
size_t len = (uintptr_t)end - (uintptr_t)elem->name;
|
||||
if (!(elem->meta.name = ddsrt_malloc(len + 1)))
|
||||
return -1;
|
||||
memcpy(elem->meta.name, elem->name, len);
|
||||
elem->meta.name[len] = '\0';
|
||||
}
|
||||
if (elem->children) {
|
||||
for (struct cfgelem *ce = elem->children; ce->name; ce++) {
|
||||
if (sanitize_names(ce) != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (elem->attributes) {
|
||||
for (struct cfgelem *ce = elem->attributes; ce->name; ce++) {
|
||||
if (sanitize_names(ce) != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_enum_pattern(struct cfgelem *elem)
|
||||
{
|
||||
char *pat;
|
||||
const char **vals;
|
||||
size_t cnt = 0, len = 0, pos = 0, size = 0;
|
||||
assert(!elem->meta.pattern);
|
||||
assert(elem->meta.values);
|
||||
for (vals = elem->meta.values; *vals; vals++) {
|
||||
cnt++;
|
||||
size += strlen(*vals) + 2;
|
||||
}
|
||||
size += (cnt - 1) + 2 + 1;
|
||||
if (!(pat = ddsrt_malloc(size)))
|
||||
return -1;
|
||||
pat[pos++] = '(';
|
||||
for (vals = elem->meta.values; *vals; vals++) {
|
||||
if (vals != elem->meta.values)
|
||||
pat[pos++] = '|';
|
||||
pat[pos++] = '"';
|
||||
len = strlen(*vals);
|
||||
assert(pos < size - len);
|
||||
memcpy(pat+pos, *vals, len);
|
||||
pos += len;
|
||||
pat[pos++] = '"';
|
||||
}
|
||||
pat[pos++] = ')';
|
||||
pat[pos++] = '\0';
|
||||
assert(pos == size);
|
||||
elem->meta.pattern = pat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_list_pattern(struct cfgelem *elem)
|
||||
{
|
||||
char *pat = NULL, *lst;
|
||||
const char **vals, *val = (elem->value ? elem->value : "");
|
||||
size_t cnt = 0, len = 0, pos = 0, size = 0;
|
||||
assert(elem->meta.values != NULL);
|
||||
for (vals = elem->meta.values; *vals != NULL; vals++) {
|
||||
cnt++;
|
||||
size += strlen(*vals);
|
||||
}
|
||||
size += (cnt - 1) + 2 + 1;
|
||||
if (!(lst = ddsrt_malloc(size)))
|
||||
return -1;
|
||||
lst[pos++] = '(';
|
||||
for (vals = elem->meta.values; *vals != NULL; vals++) {
|
||||
if (vals != elem->meta.values)
|
||||
lst[pos++] = '|';
|
||||
len = strlen(*vals);
|
||||
assert(pos < size - len);
|
||||
memcpy(lst+pos, *vals, len);
|
||||
pos += len;
|
||||
}
|
||||
lst[pos++] = ')';
|
||||
lst[pos++] = '\0';
|
||||
assert(pos == size);
|
||||
if (strlen(val) != 0)
|
||||
ddsrt_asprintf(&pat, "%s|(%s(,%s)*)", val, lst, lst);
|
||||
else
|
||||
ddsrt_asprintf(&pat, "(%s(,%s)*)|", lst, lst);
|
||||
free(lst);
|
||||
elem->meta.pattern = pat;
|
||||
return pat ? 0 : -1;
|
||||
}
|
||||
|
||||
/* generate patterns for enum and list elements */
|
||||
int makepattern(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
(void)units;
|
||||
if (!elem->meta.pattern) {
|
||||
if (isenum(elem))
|
||||
return generate_enum_pattern(elem);
|
||||
if (islist(elem))
|
||||
return generate_list_pattern(elem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compare(struct cfgelem *a, struct cfgelem *b)
|
||||
{
|
||||
/* shallow compare (just pointer) is good enough for now */
|
||||
if (a->children != b->children)
|
||||
return -1;
|
||||
if (a->attributes != b->attributes)
|
||||
return -1;
|
||||
if (strcmp(a->meta.type, b->meta.type) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int duplicate(struct cfgelem *curs, struct cfgelem *elem)
|
||||
{
|
||||
if (curs == elem)
|
||||
return 0;
|
||||
if (curs->meta.flags & FLAG_DUPLICATE)
|
||||
return 0;
|
||||
if (elem->meta.flags & FLAG_DUPLICATE)
|
||||
return 0;
|
||||
if (strcmp(curs->name, elem->name) != 0)
|
||||
return 0;
|
||||
if (compare(curs, elem) != 0)
|
||||
return -1;
|
||||
curs->meta.flags |= FLAG_DUPLICATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expand(struct cfgelem *curs, struct cfgelem *elem)
|
||||
{
|
||||
if (curs == elem)
|
||||
return 0;
|
||||
if (strcmp(curs->name, elem->name) != 0)
|
||||
return 0;
|
||||
curs->meta.flags &= ~FLAG_DUPLICATE;
|
||||
curs->meta.flags |= FLAG_EXPAND;
|
||||
elem->meta.flags |= FLAG_EXPAND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int walk(
|
||||
struct cfgelem *root,
|
||||
struct cfgelem *elem,
|
||||
int(*func)(struct cfgelem *, struct cfgelem *))
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
if (func(root, elem) == -1)
|
||||
return -1;
|
||||
|
||||
ce = firstelem(root->children);
|
||||
while (ce) {
|
||||
if (walk(ce, elem, func) == -1)
|
||||
return -1;
|
||||
ce = nextelem(root->children, ce);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark(struct cfgelem *elem, struct cfgelem *root)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
if (walk(root, elem, &duplicate) != 0)
|
||||
walk(root, elem, &expand);
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
mark(ce, root);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
|
||||
#define BLOCK (1024)
|
||||
|
||||
static int
|
||||
format(
|
||||
char **strp,
|
||||
size_t *lenp,
|
||||
size_t *posp,
|
||||
const char *src,
|
||||
const char *(*xlat)(const char *, const char **))
|
||||
{
|
||||
char buf[2] = { '\0', '\0' };
|
||||
size_t nonspc, pos;
|
||||
const char *alt, *end, *ptr, *sub;
|
||||
|
||||
nonspc = pos = *posp;
|
||||
for (ptr = src, end = ptr; *ptr; ptr = end) {
|
||||
if (xlat && (alt = xlat(ptr, &end))) {
|
||||
sub = alt;
|
||||
} else {
|
||||
buf[0] = *ptr;
|
||||
end = ptr + 1;
|
||||
sub = (const char *)buf;
|
||||
}
|
||||
for (size_t cnt = 0; sub[cnt]; cnt++) {
|
||||
if (pos == *lenp) {
|
||||
char *str;
|
||||
size_t len = *lenp + BLOCK;
|
||||
if (!(str = ddsrt_realloc(*strp, (len + 1) * sizeof(char)))) {
|
||||
if (*strp)
|
||||
ddsrt_free(*strp);
|
||||
return -1;
|
||||
}
|
||||
*strp = str;
|
||||
*lenp = len;
|
||||
}
|
||||
(*strp)[pos++] = sub[cnt];
|
||||
}
|
||||
}
|
||||
if (nonspc != *posp && nonspc < pos)
|
||||
pos = ++nonspc;
|
||||
(*strp)[pos] = '\0';
|
||||
(*posp) = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DFLTFMT "<p>The default value is: \"%s\".</p>"
|
||||
|
||||
int makedescription(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units,
|
||||
const char *(*xlat)(const char *, const char **))
|
||||
{
|
||||
if (elem->description) {
|
||||
char *src = NULL;
|
||||
char *dest = elem->meta.description;
|
||||
const char *dflt = "";
|
||||
size_t len = 0, pos = 0;
|
||||
const struct cfgunit *unit;
|
||||
|
||||
if (isgroup(elem)) {
|
||||
src = ddsrt_strdup(elem->description);
|
||||
} else {
|
||||
if (elem->value)
|
||||
dflt = elem->value;
|
||||
if (elem->meta.unit) {
|
||||
unit = findunit(units, elem->meta.unit);
|
||||
assert(unit);
|
||||
ddsrt_asprintf(
|
||||
&src, "%s\n%s\n"DFLTFMT, elem->description, unit->description, dflt);
|
||||
} else {
|
||||
ddsrt_asprintf(
|
||||
&src, "%s\n"DFLTFMT, elem->description, dflt);
|
||||
}
|
||||
}
|
||||
|
||||
if (!src)
|
||||
return -1;
|
||||
format(&dest, &len, &pos, src, xlat);
|
||||
ddsrt_free(src);
|
||||
if (!dest)
|
||||
return -1;
|
||||
elem->meta.description = dest;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init(struct cfgelem *root)
|
||||
{
|
||||
if (sanitize_names(root) != 0)
|
||||
return -1;
|
||||
mark(root, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fini(struct cfgelem *elem)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
if (!elem)
|
||||
return;
|
||||
if (elem->meta.name)
|
||||
ddsrt_free(elem->meta.name);
|
||||
if (elem->meta.title)
|
||||
ddsrt_free(elem->meta.title);
|
||||
if (elem->meta.pattern)
|
||||
ddsrt_free(elem->meta.pattern);
|
||||
if (elem->meta.description)
|
||||
ddsrt_free(elem->meta.description);
|
||||
elem->meta.name = NULL;
|
||||
elem->meta.title = NULL;
|
||||
elem->meta.pattern = NULL;
|
||||
elem->meta.description = NULL;
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
fini(ce);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
fini(ce);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
int code = EXIT_FAILURE;
|
||||
FILE *out = NULL;
|
||||
const char *file = "-";
|
||||
enum { rnc, xsd, md } format = rnc;
|
||||
|
||||
while ((opt = getopt(argc, argv, "f:o:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
if (strcmp(optarg, "rnc") == 0) {
|
||||
format = rnc;
|
||||
} else if (strcmp(optarg, "xsd") == 0) {
|
||||
format = xsd;
|
||||
} else if (strcmp(optarg, "md") == 0) {
|
||||
format = md;
|
||||
} else {
|
||||
fprintf(stderr, "illegal output format: %s\n", optarg);
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
help(argv[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 'o':
|
||||
file = (const char *)optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (init(cyclonedds_root_cfgelems) == -1) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
goto exit_failure;
|
||||
}
|
||||
|
||||
DDSRT_WARNING_MSVC_OFF(4996)
|
||||
if (strcmp(file, "-") == 0) {
|
||||
out = stdout;
|
||||
} else if ((out = fopen(file, "wb")) == NULL) {
|
||||
fprintf(stderr, "cannot open %s for writing\n", file);
|
||||
goto exit_failure;
|
||||
}
|
||||
DDSRT_WARNING_MSVC_ON(4996)
|
||||
|
||||
memset(spaces, ' ', sizeof(spaces));
|
||||
|
||||
switch (format) {
|
||||
case rnc:
|
||||
if (printrnc(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
case xsd:
|
||||
if (printxsd(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
case md:
|
||||
if (printmd(out, cyclonedds_root_cfgelems, cfgunits) == 0)
|
||||
code = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
exit_failure:
|
||||
fini(cyclonedds_root_cfgelems);
|
||||
if (out != NULL && out != stdout)
|
||||
fclose(out);
|
||||
|
||||
return code;
|
||||
}
|
80
src/tools/ddsconf/ddsconf.h
Normal file
80
src/tools/ddsconf/ddsconf.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct cfgunit {
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *pattern;
|
||||
};
|
||||
|
||||
#define FLAG_DUPLICATE (1u<<0) /* exact same element exists, print other */
|
||||
#define FLAG_EXPAND (1u<<1) /* element with same name exists, expand */
|
||||
#define FLAG_REFERENCE (1u<<2)
|
||||
#define FLAG_NOMIN (1u<<3)
|
||||
#define FLAG_NOMAX (1u<<4)
|
||||
|
||||
struct cfgmeta {
|
||||
char *name;
|
||||
char *title;
|
||||
char *pattern;
|
||||
char *description;
|
||||
unsigned int flags;
|
||||
const char *type;
|
||||
const char *unit;
|
||||
const char *range;
|
||||
const char **values;
|
||||
};
|
||||
|
||||
struct cfgelem {
|
||||
const char *name;
|
||||
struct cfgelem *children;
|
||||
struct cfgelem *attributes;
|
||||
int multiplicity;
|
||||
const char *value;
|
||||
const char *description;
|
||||
struct cfgmeta meta;
|
||||
};
|
||||
|
||||
int makedescription(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units,
|
||||
const char *(*xlat)(const char *, const char **));
|
||||
int makepattern(
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units);
|
||||
|
||||
const char *schema(void);
|
||||
const char *url(void);
|
||||
const char *name(const struct cfgelem *elem);
|
||||
int ismoved(const struct cfgelem *elem);
|
||||
int isdeprecated(const struct cfgelem *elem);
|
||||
int isgroup(const struct cfgelem *elem);
|
||||
int isnop(const struct cfgelem *elem);
|
||||
int isbool(const struct cfgelem *elem);
|
||||
int isint(const struct cfgelem *elem);
|
||||
int isstring(const struct cfgelem *elem);
|
||||
int isenum(const struct cfgelem *elem);
|
||||
int islist(const struct cfgelem *elem);
|
||||
int minimum(const struct cfgelem *elem);
|
||||
int maximum(const struct cfgelem *elem);
|
||||
int haschildren(const struct cfgelem *elem);
|
||||
int hasattributes(const struct cfgelem *elem);
|
||||
struct cfgelem *firstelem(const struct cfgelem *list);
|
||||
struct cfgelem *nextelem(const struct cfgelem *list, const struct cfgelem *elem);
|
||||
const struct cfgunit *findunit(const struct cfgunit *units, const char *name);
|
||||
void print(FILE *out, unsigned int cols, const char *fmt, ...);
|
||||
int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
||||
int printxsd(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
||||
int printmd(FILE *out, struct cfgelem *elem, const struct cfgunit *units);
|
290
src/tools/ddsconf/md.c
Normal file
290
src/tools/ddsconf/md.c
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
#include "dds/ddsrt/heap.h"
|
||||
|
||||
static const char *xlatmd(const char *str, const char **end)
|
||||
{
|
||||
static struct { const char *search; const char *replace; } tr[] = {
|
||||
{ "<p>", "" }, { "</p>", "" },
|
||||
{ "<b>", "" }, { "</b>", "" },
|
||||
{ "<i>", "" }, { "</i>", "" },
|
||||
{ "<li>", " * " }, { "</li>", "\n" },
|
||||
{ "<ul>", "" }, { "</ul>", "" },
|
||||
{ "<sup>", "^" }, { "</sup>", "" },
|
||||
{ "*", "\\*" }, { "_", "\\_" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* replace </p>\s*<p> by double newline */
|
||||
if (strncmp(str, "</p>", 4) == 0) {
|
||||
const char *ptr = str + 4;
|
||||
while (*ptr == '\n' || *ptr == ' ') ptr++;
|
||||
if (strncmp(ptr, "<p>", 3) == 0) {
|
||||
*end = ptr;
|
||||
return "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t cnt = 0; tr[cnt].search; cnt++) {
|
||||
size_t len = strlen(tr[cnt].search);
|
||||
if (strncmp(str, tr[cnt].search, len) == 0) {
|
||||
*end = str + len;
|
||||
return tr[cnt].replace;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char hashes[16];
|
||||
|
||||
static void printhead(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(level < sizeof(hashes));
|
||||
hashes[level+1] = '\0';
|
||||
fprintf(out, "%s %s\n", hashes, elem->meta.title);
|
||||
hashes[level+1] = '#';
|
||||
}
|
||||
|
||||
static void printlink(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
int chr;
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(elem->meta.title);
|
||||
fputc('#', out);
|
||||
for (const char *ptr = elem->meta.title; *ptr; ptr++) {
|
||||
chr = (unsigned char)*ptr;
|
||||
if (chr >= 'A' && chr <= 'Z')
|
||||
chr = (chr - 'A') + 'a';
|
||||
if (chr >= 'a' && chr <= 'z')
|
||||
fputc(chr, out);
|
||||
}
|
||||
}
|
||||
|
||||
static void printtype(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)level;
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(!isgroup(elem));
|
||||
if (isbool(elem)) {
|
||||
fprintf(out, "Boolean\n");
|
||||
} else if (islist(elem)) {
|
||||
assert(elem->meta.values);
|
||||
fprintf(out, "One of:\n");
|
||||
if (elem->value && strlen(elem->value))
|
||||
fprintf(out, "* Keyword: %s\n", elem->value);
|
||||
fprintf(out, "* Comma-separated list of: ");
|
||||
for (const char **v = elem->meta.values; *v; v++) {
|
||||
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
if (!elem->value || !strlen(elem->value))
|
||||
fprintf(out, "* Or empty\n");
|
||||
} else if (isenum(elem)) {
|
||||
assert(elem->meta.values);
|
||||
fprintf(out, "One of: ");
|
||||
for (const char **v = elem->meta.values; *v; v++) {
|
||||
fprintf(out, "%s%s", v == elem->meta.values ? "" : ", ", *v);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
} else if (isint(elem)) {
|
||||
fprintf(out, "Integer\n");
|
||||
} else if (elem->meta.unit) {
|
||||
fprintf(out, "Number-with-unit\n");
|
||||
} else if (isstring(elem)) {
|
||||
fprintf(out, "Text\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define FLAG_LF (1u<<0)
|
||||
|
||||
static void printattr(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (flags & FLAG_LF)
|
||||
fputs("\n\n\n", out);
|
||||
printhead(out, level, flags, elem, units);
|
||||
printtype(out, level, flags, elem, units);
|
||||
fputs("\n", out);
|
||||
if (elem->description)
|
||||
fputs(elem->meta.description, out);
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int level,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n\n\n");
|
||||
printhead(out, level, flags, elem, units);
|
||||
flags &= ~FLAG_LF;
|
||||
if (hasattributes(elem)) {
|
||||
int cnt = 0;
|
||||
const char *sep = "Attributes: ";
|
||||
struct cfgelem *ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce)) {
|
||||
fprintf(out, "%s[%s](", sep, name(ce));
|
||||
printlink(out, level, flags, ce, units);
|
||||
fprintf(out, ")");
|
||||
sep = ", ";
|
||||
cnt++;
|
||||
}
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
if (cnt != 0) {
|
||||
fprintf(out, "\n");
|
||||
flags |= FLAG_LF;
|
||||
}
|
||||
}
|
||||
if (haschildren(elem)) {
|
||||
const char *sep = "Children: ";
|
||||
struct cfgelem *ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
fprintf(out, "%s[%s](", sep, name(ce));
|
||||
printlink(out, level, flags, ce, units);
|
||||
fprintf(out, ")");
|
||||
sep = ", ";
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
flags |= FLAG_LF;
|
||||
} else if (!isgroup(elem)) {
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n");
|
||||
printtype(out, level+1, flags, elem, units);
|
||||
flags |= FLAG_LF;
|
||||
}
|
||||
if (elem->description) {
|
||||
if (flags & FLAG_LF)
|
||||
fprintf(out, "\n");
|
||||
fputs(elem->meta.description, out);
|
||||
}
|
||||
if (hasattributes(elem)) {
|
||||
struct cfgelem *ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce))
|
||||
printattr(out, level, flags, ce, units);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
if (isgroup(elem)) {
|
||||
struct cfgelem *ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printelem(out, level+1, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int maketitles(
|
||||
struct cfgelem *elem, int attr, const char *path, size_t pathlen)
|
||||
{
|
||||
char *str;
|
||||
size_t namelen = 0, len = 0;
|
||||
struct cfgelem *ce;
|
||||
if (ismoved(elem) || isdeprecated(elem) || elem->meta.title)
|
||||
return 0;
|
||||
namelen = strlen(name(elem));
|
||||
if (!(str = ddsrt_malloc(pathlen + namelen + (attr ? 4 : 2))))
|
||||
return -1;
|
||||
memcpy(str, path, pathlen);
|
||||
len += pathlen;
|
||||
if (attr) {
|
||||
str[len++] = '[';
|
||||
str[len++] = '@';
|
||||
} else {
|
||||
str[len++] = '/';
|
||||
}
|
||||
memcpy(str+len, name(elem), namelen);
|
||||
len += namelen;
|
||||
if (attr) {
|
||||
str[len++] = ']';
|
||||
}
|
||||
str[len] = '\0';
|
||||
elem->meta.title = str;
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
if (maketitles(ce, 0, str, len) == -1)
|
||||
return -1;
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (maketitles(ce, 1, str, len) == -1)
|
||||
return -1;
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int initmd(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, xlatmd) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initmd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initmd(ce, units) == - 1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printmd(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initmd(elem, units) == -1)
|
||||
return -1;
|
||||
if (maketitles(elem, 0, "/", 1) == -1)
|
||||
return -1;
|
||||
memset(hashes, '#', sizeof(hashes));
|
||||
printelem(out, 0u, 0u, elem, units);
|
||||
return 0;
|
||||
}
|
163
src/tools/ddsconf/rnc.c
Normal file
163
src/tools/ddsconf/rnc.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
#include "dds/ddsrt/heap.h"
|
||||
|
||||
#define FLAG_AMP (1u<<0)
|
||||
#define FLAG_ROOT (1u<<1)
|
||||
|
||||
static const char *amp[] = { "", "& " };
|
||||
static const char docfmt[] = "%s[ a:documentation [ xml:lang=\"en\" \"\"\"\n";
|
||||
static const char elemfmt[] = "%selement %s {\n";
|
||||
static const char attrfmt[] = "%sattribute %s {\n";
|
||||
|
||||
static const char *suffix(const struct cfgelem *elem)
|
||||
{
|
||||
if (minimum(elem) == 0)
|
||||
return maximum(elem) == 1 ? "?" : "*";
|
||||
else
|
||||
return maximum(elem) == 1 ? "" : "+";
|
||||
}
|
||||
|
||||
static void
|
||||
printtype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
if (strcmp(elem->meta.type, "string") == 0) {
|
||||
if (elem->meta.unit != NULL) {
|
||||
print(out, cols, "%s%s\n", amp[(flags & FLAG_AMP)], elem->meta.unit);
|
||||
} else {
|
||||
print(out, cols, "%stext\n", amp[(flags & FLAG_AMP)]);
|
||||
}
|
||||
} else if (strcmp(elem->meta.type, "bool") == 0) {
|
||||
print(out, cols, "%sxsd:boolean\n", amp[(flags & FLAG_AMP)]);
|
||||
} else if (strcmp(elem->meta.type, "int") == 0) {
|
||||
print(out, cols, "%sxsd:integer\n", amp[(flags & FLAG_AMP)]);
|
||||
} else if (strcmp(elem->meta.type, "enum") == 0) {
|
||||
assert(elem->meta.pattern != NULL);
|
||||
print(out, cols, "%s%s\n", amp[(flags & FLAG_AMP)], elem->meta.pattern);
|
||||
} else if (strcmp(elem->meta.type, "list") == 0) {
|
||||
assert(elem->meta.pattern != NULL);
|
||||
print(out, cols, "%sxsd:token { pattern = \"%s\" }\n", amp[(flags & FLAG_AMP)], elem->meta.pattern);
|
||||
} else {
|
||||
print(out, cols, "%sempty\n", amp[(flags & FLAG_AMP)]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printattr(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
if (elem->description != NULL) {
|
||||
print(out, cols, docfmt, amp[(flags & FLAG_AMP)]);
|
||||
fputs(elem->meta.description, out);
|
||||
print(out, 0, "\"\"\" ] ]\n");
|
||||
flags &= ~FLAG_AMP;
|
||||
}
|
||||
print(out, cols, attrfmt, amp[(flags & FLAG_AMP)], name(elem));
|
||||
printtype(out, cols+2, flags, elem, units);
|
||||
print(out, cols, "}%s\n", suffix(elem));
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (elem->description != NULL) {
|
||||
print(out, cols, docfmt, amp[(flags & FLAG_AMP)]);
|
||||
fputs(elem->meta.description, out);
|
||||
print(out, 0, "\"\"\" ] ]\n");
|
||||
flags &= ~FLAG_AMP;
|
||||
}
|
||||
print(out, cols, elemfmt, amp[(flags & FLAG_AMP)], name(elem));
|
||||
flags &= ~FLAG_AMP;
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
if (!isnop(ce)) {
|
||||
printattr(out, cols+2, flags & ~FLAG_ROOT, ce, units);
|
||||
flags |= FLAG_AMP;
|
||||
}
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
if (haschildren(elem)) {
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printelem(out, cols+2, flags & ~FLAG_ROOT, ce, units);
|
||||
flags |= FLAG_AMP;
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
} else if (!hasattributes(elem) ||
|
||||
!(isgroup(elem) || (isstring(elem) && !elem->meta.unit)))
|
||||
{
|
||||
printtype(out, cols+2, flags, elem, units);
|
||||
}
|
||||
print(out, cols, "}%s\n", (flags & FLAG_ROOT) ? "" : suffix(elem));
|
||||
}
|
||||
|
||||
static int initrnc(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, 0) == -1)
|
||||
return -1;
|
||||
if (makepattern(elem, units) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initrnc(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initrnc(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printrnc(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initrnc(elem, units) == -1)
|
||||
return -1;
|
||||
print(out, 0, "default namespace = \"%s\"\n", url());
|
||||
print(out, 0, "namespace a = \"http://relaxng.org/ns/compatibility/annotations/1.0\"\n");
|
||||
print(out, 0, "grammar {\n");
|
||||
print(out, 0, " start =\n");
|
||||
printelem(out, 2, FLAG_ROOT, elem, units);
|
||||
for(const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
static const char *fmt = " %s = xsd:token { pattern = \"%s\" }\n";
|
||||
print(out, 0, fmt, cu->name, cu->pattern);
|
||||
}
|
||||
print(out, 0, "}");
|
||||
return 0;
|
||||
}
|
421
src/tools/ddsconf/xsd.c
Normal file
421
src/tools/ddsconf/xsd.c
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Copyright(c) 2020 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ddsconf.h"
|
||||
|
||||
static const char *xlatxsd(const char *str, const char **end)
|
||||
{
|
||||
const char *sub;
|
||||
|
||||
if (*str == '&') /* ampersand */
|
||||
sub = "&";
|
||||
else if (*str == '<') /* less-than sign */
|
||||
sub = "<";
|
||||
else if (*str == '>') /* greater-than sign */
|
||||
sub =">";
|
||||
else
|
||||
return NULL;
|
||||
|
||||
*end = ++str;
|
||||
return sub;
|
||||
}
|
||||
|
||||
static const char *isbuiltintype(const struct cfgelem *elem)
|
||||
{
|
||||
if (strcmp(elem->meta.type, "bool") == 0) {
|
||||
return "boolean";
|
||||
} else if (strcmp(elem->meta.type, "int") == 0) {
|
||||
return "integer";
|
||||
} else if (strcmp(elem->meta.type, "string") == 0) {
|
||||
return "string";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int iscomplextype(const struct cfgelem *elem)
|
||||
{
|
||||
return haschildren(elem) != 0 || hasattributes(elem) != 0 || isgroup(elem);
|
||||
}
|
||||
|
||||
static void
|
||||
printdesc(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
if (!elem->description)
|
||||
return;
|
||||
assert(elem->meta.description);
|
||||
print(out, cols+0, "<xs:annotation>\n");
|
||||
print(out, cols+2, "<xs:documentation>\n");
|
||||
fputs(elem->meta.description, out);
|
||||
fputs("</xs:documentation>\n", out);
|
||||
print(out, cols+0, "</xs:annotation>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printenum(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
print(out, cols+0, "<xs:simpleType>\n");
|
||||
print(out, cols+2, "<xs:restriction base=\"xs:token\">\n");
|
||||
for(const char **v = elem->meta.values; v && *v; v++) {
|
||||
print(out, cols+4, "<xs:enumeration value=\"%s\"/>\n", *v);
|
||||
}
|
||||
print(out, cols+2, "</xs:restriction>\n");
|
||||
print(out, cols+0, "</xs:simpleType>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printlist(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
(void)flags;
|
||||
(void)units;
|
||||
print(out, cols+0, "<xs:simpleType>\n");
|
||||
print(out, cols+2, "<xs:restriction base=\"xs:token\">\n");
|
||||
print(out, cols+4, "<xs:pattern value=\"%s\"/>\n", elem->meta.pattern);
|
||||
print(out, cols+2, "</xs:restriction>\n");
|
||||
print(out, cols+0, "</xs:simpleType>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printattr(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
const char fmt[] = "<xs:attribute name=\"%s\"%s>\n";
|
||||
char type[64], required[32];
|
||||
|
||||
(void)flags;
|
||||
(void)units;
|
||||
assert(elem != NULL);
|
||||
assert(!haschildren(elem));
|
||||
if (ismoved(elem) || isdeprecated(elem) || isnop(elem))
|
||||
return;
|
||||
|
||||
type[0] = '\0';
|
||||
if (elem->meta.unit)
|
||||
snprintf(type, sizeof(type), " type=\"config:%s\"", elem->meta.unit);
|
||||
else if (!isstring(elem))
|
||||
snprintf(type, sizeof(type), " type=\"xs:%s\"", isbuiltintype(elem));
|
||||
|
||||
required[0] = '\0';
|
||||
if (minimum(elem))
|
||||
snprintf(type, sizeof(type), " use=\"required\"");
|
||||
|
||||
print(out, cols, fmt, name(elem), type, required);
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
print(out, cols, "</xs:attribute>\n");
|
||||
}
|
||||
|
||||
static void printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units);
|
||||
|
||||
static void
|
||||
printref(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
if (elem->meta.flags & FLAG_EXPAND) {
|
||||
printelem(out, cols, flags | FLAG_REFERENCE, elem, units);
|
||||
} else {
|
||||
char minattr[32] = "";
|
||||
char maxattr[32] = "";
|
||||
const char fmt[] = "<xs:element %s%sref=\"%s:%s\"/>\n";
|
||||
if (!(flags & FLAG_NOMIN) && minimum(elem) != 1)
|
||||
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (!(flags & FLAG_NOMAX) &&maximum(elem) == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
|
||||
else if (!(FLAG_NOMAX) && maximum(elem) != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
print(out, cols, fmt, minattr, maxattr, schema(), name(elem));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printcomplextype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
char minattr[32] = "";
|
||||
char maxattr[32] = "";
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (flags & FLAG_REFERENCE) {
|
||||
if (minimum(elem) != 1)
|
||||
snprintf(minattr, sizeof(minattr), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (maximum(elem) == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"unbounded\" ");
|
||||
else if (maximum(elem) != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
}
|
||||
|
||||
print(out, cols, "<xs:element %s%sname=\"%s\">\n", minattr, maxattr, name(elem));
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
|
||||
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
|
||||
if (!haschildren(elem) && !hasattributes(elem)) {
|
||||
/* special case, group has only deprecated children and/or attributes */
|
||||
print(out, cols+2, "<xs:complexType/>\n");
|
||||
} else {
|
||||
int cnt;
|
||||
unsigned int ofst = 0;
|
||||
print(out, cols+2, "<xs:complexType>\n");
|
||||
|
||||
if ((cnt = haschildren(elem))) {
|
||||
const char *cont = NULL;
|
||||
struct cfgelem *ce;
|
||||
int min[2], max[2], eq[2] = { 1, 1 };
|
||||
assert(isgroup(elem));
|
||||
|
||||
minattr[0] = '\0';
|
||||
maxattr[0] = '\0';
|
||||
|
||||
if (cnt == 1) {
|
||||
/* minOccurs and maxOccurs placed in element */
|
||||
cont = "sequence";
|
||||
} else {
|
||||
assert(cnt > 1);
|
||||
ce = firstelem(elem->children);
|
||||
min[0] = min[1] = minimum(ce);
|
||||
max[0] = max[1] = maximum(ce);
|
||||
assert(min[1] <= max[1] || max[1] == 0);
|
||||
ce = nextelem(elem->children, ce);
|
||||
assert(ce);
|
||||
while (ce) {
|
||||
min[1] = minimum(ce);
|
||||
max[1] = maximum(ce);
|
||||
assert(min[1] <= max[1] || max[1] == 0);
|
||||
if (min[1] != min[0]) {
|
||||
eq[0] = 0;
|
||||
if (min[1] < min[0])
|
||||
min[0] = min[1];
|
||||
}
|
||||
if (max[1] != max[0]) {
|
||||
eq[1] = 0;
|
||||
if ((max[0] != 0 && max[1] > max[0]) || max[1] == 0)
|
||||
max[0] = max[1];
|
||||
}
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
if (min[0] > 1 || max[0] != 1 /* unbounded or >1 */) {
|
||||
cont = "choice";
|
||||
if (eq[0]) {
|
||||
if (min[0] != 1)
|
||||
snprintf(minattr, sizeof(minattr), " minOccurs=\"%d\"", min[0]);
|
||||
flags |= FLAG_NOMIN;
|
||||
}
|
||||
if (eq[1]) {
|
||||
if (max[0] == 0)
|
||||
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"unbounded\"");
|
||||
else if (max[0] != 1)
|
||||
snprintf(maxattr, sizeof(maxattr), " maxOccurs=\"%d\"", max[0]);
|
||||
flags |= FLAG_NOMAX;
|
||||
}
|
||||
} else {
|
||||
/* any order, each zero or one time */
|
||||
/* minOccurs placed in element maxOccurs always one */
|
||||
cont = "all";
|
||||
}
|
||||
}
|
||||
|
||||
print(out, cols+4, "<xs:%s%s%s>\n", cont, minattr, maxattr);
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
printref(out, cols+6, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
print(out, cols+4, "</xs:%s>\n", cont);
|
||||
flags &= ~(FLAG_NOMIN | FLAG_NOMAX);
|
||||
} else if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) {
|
||||
ofst = 4;
|
||||
print(out, cols+4, "<xs:simpleContent>\n");
|
||||
if (isenum(elem) || islist(elem)) {
|
||||
print(out, cols+6, "<xs:restriction base=\"xs:anyType\">\n");
|
||||
if (isenum(elem))
|
||||
printenum(out, cols+8, flags, elem, units);
|
||||
else
|
||||
printlist(out, cols+8, flags, elem, units);
|
||||
} else {
|
||||
const char extfmt[] = "<xs:extension base=\"%s:%s\">\n";
|
||||
if (elem->meta.unit)
|
||||
print(out, cols+6, extfmt, schema(), elem->meta.unit);
|
||||
else
|
||||
print(out, cols+6, extfmt, "xs", isbuiltintype(elem));
|
||||
}
|
||||
}
|
||||
if (hasattributes(elem)) {
|
||||
struct cfgelem *ce;
|
||||
ce = firstelem(elem->attributes);
|
||||
while (ce) {
|
||||
printattr(out, cols+ofst+4, flags, ce, units);
|
||||
ce = nextelem(elem->attributes, ce);
|
||||
}
|
||||
}
|
||||
if (!isgroup(elem) && (!isstring(elem) || elem->meta.unit)) {
|
||||
if (isenum(elem) || islist(elem))
|
||||
print(out, cols+6, "</xs:restriction>\n");
|
||||
else
|
||||
print(out, cols+6, "</xs:extension>\n");
|
||||
print(out, cols+4, "</xs:simpleContent>\n");
|
||||
}
|
||||
print(out, cols+2, "</xs:complexType>\n");
|
||||
}
|
||||
print(out, cols, "</xs:element>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printsimpletype(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
char min[32] = "";
|
||||
char max[32] = "";
|
||||
const char *type;
|
||||
const char fmt[] = "<xs:element %s%sname=\"%s\">\n";
|
||||
const char builtinfmt[] = "<xs:element %s%sname=\"%s\" type=\"%s:%s\">\n";
|
||||
|
||||
assert(!ismoved(elem) && !isdeprecated(elem));
|
||||
|
||||
if (flags & FLAG_REFERENCE) {
|
||||
if (minimum(elem) != 1)
|
||||
snprintf(min, sizeof(min), "minOccurs=\"%d\" ", minimum(elem));
|
||||
if (maximum(elem) == 0)
|
||||
snprintf(max, sizeof(max), "maxOccurs=\"unbounded\" ");
|
||||
else if (maximum(elem) != 1)
|
||||
snprintf(max, sizeof(max), "maxOccurs=\"%d\" ", maximum(elem));
|
||||
}
|
||||
|
||||
if (!(type = isbuiltintype(elem)))
|
||||
print(out, cols, fmt, min, max, name(elem));
|
||||
else if (elem->meta.unit)
|
||||
print(out, cols, builtinfmt, min, max, name(elem), schema(), elem->meta.unit);
|
||||
else
|
||||
print(out, cols, builtinfmt, min, max, name(elem), "xs", type);
|
||||
printdesc(out, cols+2, flags, elem, units);
|
||||
|
||||
if (isenum(elem))
|
||||
printenum(out, cols+2, flags, elem, units);
|
||||
else if (islist(elem))
|
||||
printlist(out, cols+2, flags, elem, units);
|
||||
|
||||
print(out, cols, "</xs:element>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
printelem(
|
||||
FILE *out,
|
||||
unsigned int cols,
|
||||
unsigned int flags,
|
||||
const struct cfgelem *elem,
|
||||
const struct cfgunit *units)
|
||||
{
|
||||
struct cfgelem *ce;
|
||||
|
||||
assert(!(ismoved(elem) || isdeprecated(elem)));
|
||||
|
||||
if (!(elem->meta.flags & FLAG_DUPLICATE) && (flags & FLAG_EXPAND)) {
|
||||
if (iscomplextype(elem))
|
||||
printcomplextype(out, cols, flags, elem, units);
|
||||
else
|
||||
printsimpletype(out, cols, flags, elem, units);
|
||||
}
|
||||
|
||||
if (!(flags & FLAG_REFERENCE)) {
|
||||
ce = firstelem(elem->children);
|
||||
while (ce) {
|
||||
if (ce->meta.flags & FLAG_EXPAND)
|
||||
flags &= ~FLAG_EXPAND;
|
||||
else
|
||||
flags |= FLAG_EXPAND;
|
||||
printelem(out, cols, flags, ce, units);
|
||||
ce = nextelem(elem->children, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int initxsd(struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (ismoved(elem) || isdeprecated(elem))
|
||||
return 0;
|
||||
if (makedescription(elem, units, &xlatxsd) == -1)
|
||||
return -1;
|
||||
if (makepattern(elem, units) == -1)
|
||||
return -1;
|
||||
for (struct cfgelem *ce = elem->children; ce && ce->name; ce++) {
|
||||
if (initxsd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
for (struct cfgelem *ce = elem->attributes; ce && ce->name; ce++) {
|
||||
if (initxsd(ce, units) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int printxsd(FILE *out, struct cfgelem *elem, const struct cfgunit *units)
|
||||
{
|
||||
if (initxsd(elem, units) == -1)
|
||||
return -1;
|
||||
print(out, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
print(out, 0, "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
|
||||
"elementFormDefault=\"qualified\" targetNamespace=\"%s\" xmlns:%s=\"%s\">\n",
|
||||
url(), schema(), url());
|
||||
printelem(out, 2, FLAG_EXPAND, elem, units);
|
||||
for (const struct cfgunit *cu = units; cu->name; cu++) {
|
||||
print(out, 2, "<xs:simpleType name=\"%s\">\n", cu->name);
|
||||
print(out, 4, "<xs:restriction base=\"xs:token\">\n");
|
||||
print(out, 6, "<xs:pattern value=\"%s\"/>\n", cu->pattern);
|
||||
print(out, 4, "</xs:restriction>\n");
|
||||
print(out, 2, "</xs:simpleType>\n");
|
||||
}
|
||||
print(out, 0, "</xs:schema>");
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue