diff options
| -rw-r--r-- | Makefile-man.am | 10 | ||||
| -rw-r--r-- | man/sd_bus_path_encode.xml | 38 | ||||
| -rw-r--r-- | src/libsystemd/libsystemd.sym | 2 | ||||
| -rw-r--r-- | src/libsystemd/sd-bus/sd-bus.c | 165 | ||||
| -rw-r--r-- | src/libsystemd/sd-bus/test-bus-marshal.c | 31 | ||||
| -rw-r--r-- | src/systemd/sd-bus.h | 2 | 
6 files changed, 248 insertions, 0 deletions
| diff --git a/Makefile-man.am b/Makefile-man.am index 3b8038611b..7dd014116f 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -292,6 +292,8 @@ MANPAGES_ALIAS += \  	man/sd_bus_open_system_remote.3 \  	man/sd_bus_open_user.3 \  	man/sd_bus_path_decode.3 \ +	man/sd_bus_path_decode_many.3 \ +	man/sd_bus_path_encode_many.3 \  	man/sd_bus_ref.3 \  	man/sd_bus_release_name.3 \  	man/sd_bus_unref.3 \ @@ -578,6 +580,8 @@ man/sd_bus_open_system_machine.3: man/sd_bus_default.3  man/sd_bus_open_system_remote.3: man/sd_bus_default.3  man/sd_bus_open_user.3: man/sd_bus_default.3  man/sd_bus_path_decode.3: man/sd_bus_path_encode.3 +man/sd_bus_path_decode_many.3: man/sd_bus_path_encode.3 +man/sd_bus_path_encode_many.3: man/sd_bus_path_encode.3  man/sd_bus_ref.3: man/sd_bus_new.3  man/sd_bus_release_name.3: man/sd_bus_request_name.3  man/sd_bus_unref.3: man/sd_bus_new.3 @@ -1124,6 +1128,12 @@ man/sd_bus_open_user.html: man/sd_bus_default.html  man/sd_bus_path_decode.html: man/sd_bus_path_encode.html  	$(html-alias) +man/sd_bus_path_decode_many.html: man/sd_bus_path_encode.html +	$(html-alias) + +man/sd_bus_path_encode_many.html: man/sd_bus_path_encode.html +	$(html-alias) +  man/sd_bus_ref.html: man/sd_bus_new.html  	$(html-alias) diff --git a/man/sd_bus_path_encode.xml b/man/sd_bus_path_encode.xml index 21c22a8f7c..696dfd00ba 100644 --- a/man/sd_bus_path_encode.xml +++ b/man/sd_bus_path_encode.xml @@ -44,7 +44,9 @@    <refnamediv>      <refname>sd_bus_path_encode</refname> +    <refname>sd_bus_path_encode_many</refname>      <refname>sd_bus_path_decode</refname> +    <refname>sd_bus_path_decode_many</refname>      <refpurpose>Convert an external identifier into an object path and back</refpurpose>    </refnamediv> @@ -61,11 +63,25 @@        </funcprototype>        <funcprototype> +        <funcdef>int <function>sd_bus_path_encode_many</function></funcdef> +        <paramdef>char **<parameter>out</parameter></paramdef> +        <paramdef>const char *<parameter>path_template</parameter></paramdef> +        <paramdef>...</paramdef> +      </funcprototype> + +      <funcprototype>          <funcdef>int <function>sd_bus_path_decode</function></funcdef>          <paramdef>const char *<parameter>path</parameter></paramdef>          <paramdef>const char *<parameter>prefix</parameter></paramdef>          <paramdef>char **<parameter>ret_external_id</parameter></paramdef>        </funcprototype> + +      <funcprototype> +        <funcdef>int <function>sd_bus_path_decode_many</function></funcdef> +        <paramdef>const char *<parameter>path</parameter></paramdef> +        <paramdef>const char *<parameter>path_template</parameter></paramdef> +        <paramdef>...</paramdef> +      </funcprototype>      </funcsynopsis>    </refsynopsisdiv> @@ -109,6 +125,28 @@      invalid in a bus object path by <literal>_</literal>, followed by a      hexadecimal value. As a special case, the empty string will be      replaced by a lone <literal>_</literal>.</para> + +    <para><function>sd_bus_path_encode_many()</function> works like +    its counterpart <function>sd_bus_path_encode()</function>, but +    takes a path-template as argument and encodes multiple labels +    according to its embedded directives. For each +    <literal>%</literal> character found in the template, the caller +    must provide a string via var-args, which will be encoded and +    embedded at the position of the <literal>%</literal> character. +    Any other character in the template is copied verbatim into the +    encoded path.</para> + +    <para><function>sd_bus_path_decode_many()</function> does the +    reverse of <function>sd_bus_path_encode_many()</function>. It +    decodes the passed object path, according to the given +    path-template. For each <literal>%</literal> character in the +    template, the caller must provide an output storage +    (<literal>char **</literal>) via var-args. The decoded label +    will be stored there. Each <literal>%</literal> character will +    only match the current label. It will never match across labels. +    Furthermore, only a single such directive is allowed per label. +    If <literal>NULL</literal> is passed as output storage, the +    label is verified but not returned to the caller.</para>    </refsect1>    <refsect1> diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 518cbbb7ed..843a1e9880 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -477,4 +477,6 @@ global:  LIBSYSTEMD_227 {  global:          sd_bus_default_flush_close; +        sd_bus_path_decode_many; +        sd_bus_path_encode_many;  } LIBSYSTEMD_226; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 53d1c6f61d..3310d3859d 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -3454,6 +3454,171 @@ _public_ int sd_bus_path_decode(const char *path, const char *prefix, char **ext          return 1;  } +_public_ int sd_bus_path_encode_many(char **out, const char *path_template, ...) { +        _cleanup_strv_free_ char **labels = NULL; +        char *path, *path_pos, **label_pos; +        const char *sep, *template_pos; +        size_t path_length; +        va_list list; +        int r; + +        assert_return(out, -EINVAL); +        assert_return(path_template, -EINVAL); + +        path_length = strlen(path_template); + +        va_start(list, out); +        for (sep = strchr(path_template, '%'); sep; sep = strchr(sep + 1, '%')) { +                const char *arg; +                char *label; + +                arg = va_arg(list, const char *); +                if (!arg) { +                        va_end(list); +                        return -EINVAL; +                } + +                label = bus_label_escape(arg); +                if (!label) { +                        va_end(list); +                        return -ENOMEM; +                } + +                r = strv_consume(&labels, label); +                if (r < 0) { +                        va_end(list); +                        return r; +                } + +                /* add label length, but account for the format character */ +                path_length += strlen(label) - 1; +        } +        va_end(list); + +        path = malloc(path_length + 1); +        if (!path) +                return -ENOMEM; + +        path_pos = path; +        label_pos = labels; + +        for (template_pos = path_template; *template_pos; ) { +                sep = strchrnul(template_pos, '%'); +                path_pos = mempcpy(path_pos, template_pos, sep - template_pos); +                if (!*sep) +                        break; + +                path_pos = stpcpy(path_pos, *label_pos++); +                template_pos = sep + 1; +        } + +        *path_pos = 0; +        *out = path; +        return 0; +} + +_public_ int sd_bus_path_decode_many(const char *path, const char *path_template, ...) { +        _cleanup_strv_free_ char **labels = NULL; +        const char *template_pos, *path_pos; +        char **label_pos; +        va_list list; +        int r; + +        /* +         * This decodes an object-path based on a template argument. The +         * template consists of a verbatim path, optionally including special +         * directives: +         * +         *   - Each occurrence of '%' in the template matches an arbitrary +         *     substring of a label in the given path. At most one such +         *     directive is allowed per label. For each such directive, the +         *     caller must provide an output parameter (char **) via va_arg. If +         *     NULL is passed, the given label is verified, but not returned. +         *     For each matched label, the *decoded* label is stored in the +         *     passed output argument, and the caller is responsible to free +         *     it. Note that the output arguments are only modified if the +         *     actualy path matched the template. Otherwise, they're left +         *     untouched. +         * +         * This function returns <0 on error, 0 if the path does not match the +         * template, 1 if it matched. +         */ + +        assert_return(path, -EINVAL); +        assert_return(path_template, -EINVAL); + +        path_pos = path; + +        for (template_pos = path_template; *template_pos; ) { +                const char *sep; +                size_t length; +                char *label; + +                /* verify everything until the next '%' matches verbatim */ +                sep = strchrnul(template_pos, '%'); +                length = sep - template_pos; +                if (strncmp(path_pos, template_pos, length)) +                        return 0; + +                path_pos += length; +                template_pos += length; + +                if (!*template_pos) +                        break; + +                /* We found the next '%' character. Everything up until here +                 * matched. We now skip ahead to the end of this label and make +                 * sure it matches the tail of the label in the path. Then we +                 * decode the string in-between and save it for later use. */ + +                ++template_pos; /* skip over '%' */ + +                sep = strchrnul(template_pos, '/'); +                length = sep - template_pos; /* length of suffix to match verbatim */ + +                /* verify the suffixes match */ +                sep = strchrnul(path_pos, '/'); +                if (sep - path_pos < (ssize_t)length || +                    strncmp(sep - length, template_pos, length)) +                        return 0; + +                template_pos += length; /* skip over matched label */ +                length = sep - path_pos - length; /* length of sub-label to decode */ + +                /* store unescaped label for later use */ +                label = bus_label_unescape_n(path_pos, length); +                if (!label) +                        return -ENOMEM; + +                r = strv_consume(&labels, label); +                if (r < 0) +                        return r; + +                path_pos = sep; /* skip decoded label and suffix */ +        } + +        /* end of template must match end of path */ +        if (*path_pos) +                return 0; + +        /* copy the labels over to the caller */ +        va_start(list, path); +        for (label_pos = labels; label_pos && *label_pos; ++label_pos) { +                char **arg; + +                arg = va_arg(list, char **); +                if (arg) +                        *arg = *label_pos; +                else +                        free(*label_pos); +        } +        va_end(list); + +        free(labels); +        labels = NULL; +        return 1; +} +  _public_ int sd_bus_try_close(sd_bus *bus) {          int r; diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index b203707f27..ff6bba5988 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -66,6 +66,36 @@ static void test_bus_path_encode(void) {          assert_se(sd_bus_path_decode(e, "/foo/bar", &f) > 0 && streq(f, "foo.bar"));  } +static void test_bus_path_encode_many(void) { +        _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; + +        assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/prefix/bar", "/prefix/%bar", NULL) == 1); +        assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%/suffix", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/prefix/foobar/suffix", "/prefix/%/suffix", &a) == 1 && streq_ptr(a, "foobar")); +        assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", &b, &c) == 1 && streq_ptr(b, "foo") && streq_ptr(c, "bar")); +        assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", NULL, &d) == 1 && streq_ptr(d, "bar")); + +        assert_se(sd_bus_path_decode_many("/foo/bar", "/foo/bar/%", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar%", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/bar", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%bar", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar/suffix") == 1); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%%/suffix", NULL, NULL) == 0); /* multiple '%' are treated verbatim */ +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffi", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffix", &e) == 1 && streq_ptr(e, "bar")); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/%", NULL, NULL) == 1); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/%", NULL, NULL, NULL) == 1); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%/%/%", NULL, NULL, NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%", NULL, NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/", NULL, NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%", NULL) == 0); +        assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%", NULL) == 0); + +        assert_se(sd_bus_path_encode_many(&f, "/prefix/one_%_two/mid/three_%_four/suffix", "foo", "bar") >= 0 && streq_ptr(f, "/prefix/one_foo_two/mid/three_bar_four/suffix")); +} +  static void test_bus_label_escape_one(const char *a, const char *b) {          _cleanup_free_ char *t = NULL, *x = NULL, *y = NULL; @@ -393,6 +423,7 @@ int main(int argc, char *argv[]) {          test_bus_label_escape();          test_bus_path_encode();          test_bus_path_encode_unique(); +        test_bus_path_encode_many();          return 0;  } diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 0883203ae7..43cf247cdf 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -420,7 +420,9 @@ int sd_bus_error_add_map(const sd_bus_error_map *map);  /* Label escaping */  int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path); +int sd_bus_path_encode_many(char **out, const char *path_template, ...);  int sd_bus_path_decode(const char *path, const char *prefix, char **ret_external_id); +int sd_bus_path_decode_many(const char *path, const char *path_template, ...);  /* Tracking peers */ | 
