]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path: add new "systemd-path" utility for querying paths described in file-hierarchy(7)
authorLennart Poettering <lennart@poettering.net>
Wed, 2 Jul 2014 10:23:36 +0000 (12:23 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 2 Jul 2014 10:23:36 +0000 (12:23 +0200)
This new tool is based on "sd-path", a new (so far unexported) API for
libsystemd, that can hopefully grow into a workable API covering /opt
and more one day.

14 files changed:
.gitignore
Makefile-man.am
Makefile.am
man/file-hierarchy.xml
man/systemd-path.xml [new file with mode: 0644]
src/libsystemd/sd-path/Makefile [new symlink]
src/libsystemd/sd-path/sd-path.c [new file with mode: 0644]
src/path/Makefile [new symlink]
src/path/path.c [new file with mode: 0644]
src/shared/architecture.h
src/shared/strv.c
src/shared/strv.h
src/shared/util.c
src/systemd/sd-path.h [new file with mode: 0644]

index fe8c32d42ce84b1ef4064c344830b4313e32c254..9523ea027e7d9f0c19c9e17dac602fdd173bf2f4 100644 (file)
@@ -86,6 +86,7 @@
 /systemd-networkd-wait-online
 /systemd-notify
 /systemd-nspawn
+/systemd-path
 /systemd-quotacheck
 /systemd-random-seed
 /systemd-rc-local-generator
index 3840bb138d9443ac703a13fadcf9413ec2f0b462..5c289dda2a90a8062238f03944a8e6a4a812bc03 100644 (file)
@@ -74,6 +74,7 @@ MANPAGES += \
        man/systemd-machine-id-setup.1 \
        man/systemd-notify.1 \
        man/systemd-nspawn.1 \
+       man/systemd-path.1 \
        man/systemd-remount-fs.service.8 \
        man/systemd-run.1 \
        man/systemd-shutdownd.service.8 \
@@ -1619,6 +1620,7 @@ EXTRA_DIST += \
        man/systemd-networkd.service.xml \
        man/systemd-notify.xml \
        man/systemd-nspawn.xml \
+       man/systemd-path.xml \
        man/systemd-quotacheck.service.xml \
        man/systemd-random-seed.service.xml \
        man/systemd-readahead-replay.service.xml \
index 092153b567e6f965a30f448c5fb87479e24c42f4..d2edf3daaf36b3f991034902ab95a872ab2b2c84 100644 (file)
@@ -187,6 +187,8 @@ AM_CPPFLAGS = \
        -DPOLKIT_AGENT_BINARY_PATH=\"$(bindir)/pkttyagent\" \
        -DQUOTACHECK=\"$(QUOTACHECK)\" \
        -DKEXEC=\"$(KEXEC)\" \
+       -DLIBDIR=\"$(libdir)\" \
+       -DROOTLIBDIR=\"$(rootlibdir)\" \
        -I $(top_srcdir)/src \
        -I $(top_builddir)/src/shared \
        -I $(top_srcdir)/src/shared \
@@ -344,7 +346,8 @@ bin_PROGRAMS = \
        systemd-detect-virt \
        systemd-delta \
        systemd-analyze \
-       systemd-run
+       systemd-run \
+       systemd-path
 
 dist_bin_SCRIPTS = \
        src/kernel-install/kernel-install
@@ -2096,6 +2099,14 @@ systemd_notify_LDADD = \
        libsystemd-internal.la \
        libsystemd-shared.la
 
+# ------------------------------------------------------------------------------
+systemd_path_SOURCES = \
+       src/path/path.c
+
+systemd_path_LDADD = \
+       libsystemd-internal.la \
+       libsystemd-shared.la
+
 # ------------------------------------------------------------------------------
 systemd_ask_password_SOURCES = \
        src/ask-password/ask-password.c
@@ -2221,6 +2232,7 @@ libsystemd_internal_la_SOURCES = \
        src/systemd/sd-login.h \
        src/systemd/sd-id128.h \
        src/systemd/sd-daemon.h \
+       src/systemd/sd-path.h \
        src/libsystemd/sd-bus/sd-bus.c \
        src/libsystemd/sd-bus/bus-control.c \
        src/libsystemd/sd-bus/bus-control.h \
@@ -2274,7 +2286,8 @@ libsystemd_internal_la_SOURCES = \
        src/libsystemd/sd-rtnl/rtnl-util.c \
        src/libsystemd/sd-id128/sd-id128.c \
        src/libsystemd/sd-daemon/sd-daemon.c \
-       src/libsystemd/sd-login/sd-login.c
+       src/libsystemd/sd-login/sd-login.c \
+       src/libsystemd/sd-path/sd-path.c
 
 nodist_libsystemd_internal_la_SOURCES = \
        src/libsystemd/libsystemd.sym \
@@ -2377,7 +2390,8 @@ pkginclude_HEADERS += \
        src/systemd/sd-utf8.h \
        src/systemd/sd-event.h \
        src/systemd/sd-rtnl.h \
-       src/systemd/sd-resolve.h
+       src/systemd/sd-resolve.h \
+       src/systemd/sd-path.h
 endif
 
 lib_LTLIBRARIES += \
index 4d542caad4ec91d2dba016efb2a6baf25195a1e9..a996bb69d2e2bc37cec362cec8074d2e292d4767 100644 (file)
                 subset of these specifications that defines more
                 strictly the suggestions and restrictions systemd
                 makes on the file system hierarchy.</para>
+
+                <para>Many of the paths described here are queriable
+                with the
+                <citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                tool.</para>
         </refsect1>
 
         <refsect1>
                         <varlistentry>
                                 <term><filename>/usr/lib/<replaceable>arch-id</replaceable></filename></term>
                                 <listitem><para>Location for placing
-                                dynamic libraries, called <varname>$libdir</varname>.
-                                The architecture identifier to use, is defined on <ulink
+                                dynamic libraries, also called <varname>$libdir</varname>.
+                                The architecture identifier to use is defined on <ulink
                                 url="https://wiki.debian.org/Multiarch/Tuples">Multiarch Architecture Specifiers (Tuples)</ulink>
                                 list. Legacy locations of <varname>$libdir</varname> are
                                 <filename>/usr/lib</filename>,
                                 <varname>$libdir</varname> for the
                                 primary architecture of the system,
                                 invoke:
-                                <programlisting># pkg-config --variable=libdir systemd</programlisting></para></listitem>
+                                <programlisting># pkg-config --variable=libdir systemd</programlisting> or
+                                <programlisting># systemd-path system-library-arch</programlisting>
+                                </para></listitem>
+
                         </varlistentry>
 
                         <varlistentry>
                 of these directories are also standardized (though
                 more weakly) by the <ulink
                 url="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG
-                Base Directory Specification</ulink>.</para>
+                Base Directory Specification</ulink>. Additional
+                locations for high-level user resources are defined by
+                <ulink
+                url="http://www.freedesktop.org/wiki/Software/xdg-user-dirs/">xdg-user-dirs</ulink>.</para>
 
                 <variablelist>
                         <varlistentry>
                 <para>
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>hier</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
diff --git a/man/systemd-path.xml b/man/systemd-path.xml
new file mode 100644 (file)
index 0000000..fc01d5e
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="systemd-path"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+        <refentryinfo>
+                <title>systemd-path</title>
+                <productname>systemd</productname>
+
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Lennart</firstname>
+                                <surname>Poettering</surname>
+                                <email>lennart@poettering.net</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+
+        <refmeta>
+                <refentrytitle>systemd-path</refentrytitle>
+                <manvolnum>1</manvolnum>
+        </refmeta>
+
+        <refnamediv>
+                <refname>systemd-path</refname>
+                <refpurpose>List and query system and user paths</refpurpose>
+        </refnamediv>
+
+        <refsynopsisdiv>
+                <cmdsynopsis>
+                        <command>systemd-path <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">NAME</arg></command>
+                </cmdsynopsis>
+        </refsynopsisdiv>
+
+        <refsect1>
+                <title>Description</title>
+
+                <para><command>systemd-path</command> may be used to
+                query system and user paths. The tool makes many of
+                the paths described in
+                <citerefentry><refentrytitle>file-hierarchy</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+                queriable.</para>
+
+                <para>When invoked without arguments a list of known
+                paths and their current values is shown. When at least
+                one argument is passed the path with this is name is
+                queried and its value shown. The variables whose name
+                begins with <literal>search-</literal> don't refer to
+                individual paths, but instead a to a list of
+                colon-separated search paths, in their order of
+                precedence.</para>
+        </refsect1>
+
+        <refsect1>
+                <title>Options</title>
+
+                <para>The following options are understood:</para>
+
+                <variablelist>
+                        <varlistentry>
+                                <term><option>--suffix=</option></term>
+
+                                <listitem><para>The printed paths are
+                                suffixed by the specified
+                                string.</para></listitem>
+                        </varlistentry>
+
+                        <xi:include href="standard-options.xml" xpointer="help" />
+                        <xi:include href="standard-options.xml" xpointer="version" />
+                </variablelist>
+
+        </refsect1>
+
+        <refsect1>
+                <title>Exit status</title>
+
+                <para>On success, 0 is returned, a non-zero failure
+                code otherwise.</para>
+        </refsect1>
+
+        <refsect1>
+                <title>See Also</title>
+                <para>
+                        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>file-hierarchy</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+                </para>
+        </refsect1>
+
+</refentry>
diff --git a/src/libsystemd/sd-path/Makefile b/src/libsystemd/sd-path/Makefile
new file mode 120000 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
new file mode 100644 (file)
index 0000000..44c1b8b
--- /dev/null
@@ -0,0 +1,626 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "architecture.h"
+#include "path-util.h"
+#include "strv.h"
+#include "sd-path.h"
+
+static int from_environment(const char *envname, const char *fallback, const char **ret) {
+        assert(ret);
+
+        if (envname) {
+                const char *e;
+
+                e = secure_getenv(envname);
+                if (e && path_is_absolute(e)) {
+                        *ret = e;
+                        return 0;
+                }
+        }
+
+        if (fallback) {
+                *ret = fallback;
+                return 0;
+        }
+
+        return -ENXIO;
+}
+
+static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
+        _cleanup_free_ char *h = NULL;
+        char *cc = NULL;
+        int r;
+
+        assert(suffix);
+        assert(buffer);
+        assert(ret);
+
+        if (envname) {
+                const char *e = NULL;
+
+                e = secure_getenv(envname);
+                if (e && path_is_absolute(e)) {
+                        *ret = e;
+                        return 0;
+                }
+        }
+
+        r = get_home_dir(&h);
+        if (r < 0)
+                return r;
+
+        if (endswith(h, "/"))
+                cc = strappend(h, suffix);
+        else
+                cc = strjoin(h, "/", suffix, NULL);
+        if (!cc)
+                return -ENOMEM;
+
+        *buffer = cc;
+        *ret = cc;
+        return 0;
+}
+
+static int from_user_dir(const char *field, char **buffer, const char **ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *b = NULL;
+        const char *fn = NULL;
+        char line[LINE_MAX];
+        size_t n;
+        int r;
+
+        assert(field);
+        assert(buffer);
+        assert(ret);
+
+        r = from_home_dir(NULL, ".config/user-dirs.dirs", &b, &fn);
+        if (r < 0)
+                return r;
+
+        f = fopen(fn, "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        goto fallback;
+
+                return -errno;
+        }
+
+        /* This is an awful parse, but it follows closely what
+         * xdg-user-dirs does upstream */
+
+        n = strlen(field);
+        FOREACH_LINE(line, f, return -errno) {
+                char *l, *p, *e;
+
+                l = strstrip(line);
+
+                if (!strneq(l, field, n))
+                        continue;
+
+                p = l + n;
+                p += strspn(p, WHITESPACE);
+
+                if (*p != '=')
+                        continue;
+                p++;
+
+                p += strspn(p, WHITESPACE);
+
+                if (*p != '"')
+                        continue;
+                p++;
+
+                e = strrchr(p, '"');
+                if (!e)
+                        continue;
+                *e = 0;
+
+                /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
+                if (startswith(p, "$HOME/")) {
+                        _cleanup_free_ char *h = NULL;
+                        char *cc;
+
+                        r = get_home_dir(&h);
+                        if (r < 0)
+                                return r;
+
+                        cc = strappend(h, p+5);
+                        if (!cc)
+                                return -ENOMEM;
+
+                        *buffer = cc;
+                        *ret = cc;
+                        return 0;
+                } else if (streq(p, "$HOME")) {
+
+                        r = get_home_dir(buffer);
+                        if (r < 0)
+                                return r;
+
+                        *ret = *buffer;
+                        return 0;
+                } else if (path_is_absolute(p)) {
+                        char *copy;
+
+                        copy = strdup(p);
+                        if (!copy)
+                                return -ENOMEM;
+
+                        *buffer = copy;
+                        *ret = copy;
+                        return 0;
+                }
+        }
+
+fallback:
+        /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
+        if (streq(field, "XDG_DESKTOP_DIR")) {
+                _cleanup_free_ char *h = NULL;
+                char *cc;
+
+                r = get_home_dir(&h);
+                if (r < 0)
+                        return r;
+
+                cc = strappend(h, "/Desktop");
+                if (!cc)
+                        return -ENOMEM;
+
+                *buffer = cc;
+                *ret = cc;
+        } else {
+
+                r = get_home_dir(buffer);
+                if (r < 0)
+                        return r;
+
+                *ret = *buffer;
+        }
+
+        return 0;
+}
+
+static int get_path(uint64_t type, char **buffer, const char **ret) {
+        int r;
+
+        assert(buffer);
+        assert(ret);
+
+        switch (type) {
+
+        case SD_PATH_TEMPORARY:
+                return from_environment("TMPDIR", "/tmp", ret);
+
+        case SD_PATH_TEMPORARY_LARGE:
+                return from_environment("TMPDIR", "/var/tmp", ret);
+
+        case SD_PATH_SYSTEM_BINARIES:
+                *ret = "/usr/bin";
+                return 0;
+
+        case SD_PATH_SYSTEM_INCLUDE:
+                *ret = "/usr/include";
+                return 0;
+
+        case SD_PATH_SYSTEM_LIBRARY_PRIVATE:
+                *ret = "/usr/lib";
+                return 0;
+
+        case SD_PATH_SYSTEM_LIBRARY_ARCH:
+                *ret = LIBDIR;
+                return 0;
+
+        case SD_PATH_SYSTEM_SHARED:
+                *ret = "/usr/share";
+                return 0;
+
+        case SD_PATH_SYSTEM_CONFIGURATION_FACTORY:
+                *ret = "/usr/share/factory/etc";
+                return 0;
+
+        case SD_PATH_SYSTEM_STATE_FACTORY:
+                *ret = "/usr/share/factory/var";
+                return 0;
+
+        case SD_PATH_SYSTEM_CONFIGURATION:
+                *ret = "/etc";
+                return 0;
+
+        case SD_PATH_SYSTEM_RUNTIME:
+                *ret = "/run";
+                return 0;
+
+        case SD_PATH_SYSTEM_RUNTIME_LOGS:
+                *ret = "/run/log";
+                return 0;
+
+        case SD_PATH_SYSTEM_STATE_PRIVATE:
+                *ret = "/var/lib";
+                return 0;
+
+        case SD_PATH_SYSTEM_STATE_LOGS:
+                *ret = "/var/log";
+                return 0;
+
+        case SD_PATH_SYSTEM_STATE_CACHE:
+                *ret = "/var/cache";
+                return 0;
+
+        case SD_PATH_SYSTEM_STATE_SPOOL:
+                *ret = "/var/spool";
+                return 0;
+
+        case SD_PATH_USER_BINARIES:
+                return from_home_dir(NULL, ".local/bin", buffer, ret);
+
+        case SD_PATH_USER_LIBRARY_PRIVATE:
+                return from_home_dir(NULL, ".local/lib", buffer, ret);
+
+        case SD_PATH_USER_LIBRARY_ARCH:
+                return from_home_dir(NULL, ".local/lib/" ARCH_TUPLE, buffer, ret);
+
+        case SD_PATH_USER_SHARED:
+                return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret);
+
+        case SD_PATH_USER_CONFIGURATION:
+                return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret);
+
+        case SD_PATH_USER_RUNTIME:
+                return from_environment("XDG_RUNTIME_DIR", NULL, ret);
+
+        case SD_PATH_USER_STATE_CACHE:
+                return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret);
+
+        case SD_PATH_USER:
+                r = get_home_dir(buffer);
+                if (r < 0)
+                        return r;
+
+                *ret = *buffer;
+                return 0;
+
+        case SD_PATH_USER_DOCUMENTS:
+                return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
+
+        case SD_PATH_USER_MUSIC:
+                return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
+
+        case SD_PATH_USER_PICTURES:
+                return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
+
+        case SD_PATH_USER_VIDEOS:
+                return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
+
+        case SD_PATH_USER_DOWNLOAD:
+                return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
+
+        case SD_PATH_USER_PUBLIC:
+                return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
+
+        case SD_PATH_USER_TEMPLATES:
+                return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
+
+        case SD_PATH_USER_DESKTOP:
+                return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
+        }
+
+        return -ENOTSUP;
+}
+
+int sd_path_home(uint64_t type, const char *suffix, char **path) {
+        char *buffer = NULL, *cc;
+        const char *ret;
+        int r;
+
+        assert_return(path, -EINVAL);
+
+        if (IN_SET(type,
+                   SD_PATH_SEARCH_BINARIES,
+                   SD_PATH_SEARCH_LIBRARY_PRIVATE,
+                   SD_PATH_SEARCH_LIBRARY_ARCH,
+                   SD_PATH_SEARCH_SHARED,
+                   SD_PATH_SEARCH_CONFIGURATION_FACTORY,
+                   SD_PATH_SEARCH_STATE_FACTORY,
+                   SD_PATH_SEARCH_CONFIGURATION)) {
+
+                _cleanup_strv_free_ char **l = NULL;
+
+                r = sd_path_search(type, suffix, &l);
+                if (r < 0)
+                        return r;
+
+                buffer = strv_join(l, ":");
+                if (!buffer)
+                        return -ENOMEM;
+
+                *path = buffer;
+                return 0;
+        }
+
+        r = get_path(type, &buffer, &ret);
+        if (r < 0)
+                return r;
+
+        if (!suffix) {
+                if (!buffer) {
+                        buffer = strdup(ret);
+                        if (!buffer)
+                                return -ENOMEM;
+                }
+
+                *path = buffer;
+                return 0;
+        }
+
+        suffix += strspn(suffix, "/");
+
+        if (endswith(ret, "/"))
+                cc = strappend(ret, suffix);
+        else
+                cc = strjoin(ret, "/", suffix, NULL);
+
+        free(buffer);
+
+        if (!cc)
+                return -ENOMEM;
+
+        *path = cc;
+        return 0;
+}
+
+static int search_from_environment(
+                char ***list,
+                const char *env_home,
+                const char *home_suffix,
+                const char *env_search,
+                bool env_search_sufficient,
+                const char *first, ...) {
+
+        const char *e;
+        char *h = NULL;
+        char **l = NULL;
+        int r;
+
+        assert(list);
+
+        if (env_search) {
+                e = secure_getenv(env_search);
+                if (e) {
+                        l = strv_split(e, ":");
+                        if (!l)
+                                return -ENOMEM;
+
+                        if (env_search_sufficient) {
+                                *list = l;
+                                return 0;
+                        }
+                }
+        }
+
+        if (!l && first) {
+                va_list ap;
+
+                va_start(ap, first);
+                l = strv_new_ap(first, ap);
+                va_end(ap);
+
+                if (!l)
+                        return -ENOMEM;
+        }
+
+        if (env_home) {
+                e = secure_getenv(env_home);
+                if (e && path_is_absolute(e)) {
+                        h = strdup(e);
+                        if (!h) {
+                                strv_free(l);
+                                return -ENOMEM;
+                        }
+                }
+        }
+
+        if (!h && home_suffix) {
+                e = secure_getenv("HOME");
+                if (e && path_is_absolute(e)) {
+                        if (endswith(e, "/"))
+                                h = strappend(e, home_suffix);
+                        else
+                                h = strjoin(e, "/", home_suffix, NULL);
+
+                        if (!h) {
+                                strv_free(l);
+                                return -ENOMEM;
+                        }
+                }
+        }
+
+        if (h) {
+                r = strv_consume_prepend(&l, h);
+                if (r < 0) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+        }
+
+        *list = l;
+        return 0;
+}
+
+static int get_search(uint64_t type, char ***list) {
+
+        assert(list);
+
+        switch(type) {
+
+        case SD_PATH_SEARCH_BINARIES:
+                return search_from_environment(list,
+                                               NULL,
+                                               ".local/bin",
+                                               "PATH",
+                                               true,
+                                               "/usr/local/sbin",
+                                               "/usr/local/bin",
+                                               "/usr/sbin",
+                                               "/usr/bin",
+#ifdef HAVE_SPLIT_USR
+                                               "/sbin",
+                                               "/bin",
+#endif
+                                               NULL);
+
+        case SD_PATH_SEARCH_LIBRARY_PRIVATE:
+                return search_from_environment(list,
+                                               NULL,
+                                               ".local/lib",
+                                               NULL,
+                                               false,
+                                               "/usr/local/lib",
+                                               "/usr/lib",
+#ifdef HAVE_SPLIT_USR
+                                               "/lib",
+#endif
+                                               NULL);
+
+        case SD_PATH_SEARCH_LIBRARY_ARCH:
+                return search_from_environment(list,
+                                               NULL,
+                                               ".local/lib/" ARCH_TUPLE,
+                                               "LD_LIBRARY_PATH",
+                                               true,
+                                               LIBDIR,
+#ifdef HAVE_SPLIT_USR
+                                               ROOTLIBDIR,
+#endif
+                                               NULL);
+
+        case SD_PATH_SEARCH_SHARED:
+                return search_from_environment(list,
+                                               "XDG_DATA_HOME",
+                                               ".local/share",
+                                               "XDG_DATA_DIRS",
+                                               false,
+                                               "/usr/local/share",
+                                               "/usr/share",
+                                               NULL);
+
+        case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
+                return search_from_environment(list,
+                                               NULL,
+                                               NULL,
+                                               NULL,
+                                               false,
+                                               "/usr/local/share/factory/etc",
+                                               "/usr/share/factory/etc",
+                                               NULL);
+
+        case SD_PATH_SEARCH_STATE_FACTORY:
+                return search_from_environment(list,
+                                               NULL,
+                                               NULL,
+                                               NULL,
+                                               false,
+                                               "/usr/local/share/factory/var",
+                                               "/usr/share/factory/var",
+                                               NULL);
+
+        case SD_PATH_SEARCH_CONFIGURATION:
+                return search_from_environment(list,
+                                               "XDG_CONFIG_HOME",
+                                               ".config",
+                                               "XDG_CONFIG_DIRS",
+                                               false,
+                                               "/etc",
+                                               NULL);
+        }
+
+        return -ENOTSUP;
+}
+
+int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
+        char **l, **i, **j, **n;
+        int r;
+
+        assert_return(paths, -EINVAL);
+
+        if (!IN_SET(type,
+                    SD_PATH_SEARCH_BINARIES,
+                    SD_PATH_SEARCH_LIBRARY_PRIVATE,
+                    SD_PATH_SEARCH_LIBRARY_ARCH,
+                    SD_PATH_SEARCH_SHARED,
+                    SD_PATH_SEARCH_CONFIGURATION_FACTORY,
+                    SD_PATH_SEARCH_STATE_FACTORY,
+                    SD_PATH_SEARCH_CONFIGURATION)) {
+
+                char *p;
+
+                r = sd_path_home(type, suffix, &p);
+                if (r < 0)
+                        return r;
+
+                l = new(char*, 2);
+                if (!l) {
+                        free(p);
+                        return -ENOMEM;
+                }
+
+                l[0] = p;
+                l[1] = NULL;
+
+                *paths = l;
+                return 0;
+        }
+
+        r = get_search(type, &l);
+        if (r < 0)
+                return r;
+
+        if (!suffix) {
+                *paths = l;
+                return 0;
+        }
+
+        n = new(char*, strv_length(l)+1);
+        if (!n) {
+                strv_free(l);
+                return -ENOMEM;
+        }
+
+        j = n;
+        STRV_FOREACH(i, l) {
+
+                if (endswith(*i, "/"))
+                        *j = strappend(*i, suffix);
+                else
+                        *j = strjoin(*i, "/", suffix, NULL);
+
+                if (!*j) {
+                        strv_free(l);
+                        strv_free(n);
+                        return -ENOMEM;
+                }
+
+                j++;
+        }
+
+        *j = NULL;
+        *paths = n;
+        return 0;
+}
diff --git a/src/path/Makefile b/src/path/Makefile
new file mode 120000 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/path/path.c b/src/path/path.c
new file mode 100644 (file)
index 0000000..c2936e0
--- /dev/null
@@ -0,0 +1,208 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <getopt.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sd-path.h"
+#include "build.h"
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+
+static const char *arg_suffix = NULL;
+
+static const char* const path_table[_SD_PATH_MAX] = {
+        [SD_PATH_TEMPORARY] = "temporary",
+        [SD_PATH_TEMPORARY_LARGE] = "temporary-large",
+        [SD_PATH_SYSTEM_BINARIES] = "system-binaries",
+        [SD_PATH_SYSTEM_INCLUDE] = "system-include",
+        [SD_PATH_SYSTEM_LIBRARY_PRIVATE] = "system-library-private",
+        [SD_PATH_SYSTEM_LIBRARY_ARCH] = "system-library-arch",
+        [SD_PATH_SYSTEM_SHARED] = "system-shared",
+        [SD_PATH_SYSTEM_CONFIGURATION_FACTORY] = "system-configuration-factory",
+        [SD_PATH_SYSTEM_STATE_FACTORY] = "system-state-factory",
+        [SD_PATH_SYSTEM_CONFIGURATION] = "system-configuration",
+        [SD_PATH_SYSTEM_RUNTIME] = "system-runtime",
+        [SD_PATH_SYSTEM_RUNTIME_LOGS] = "system-runtime-logs",
+        [SD_PATH_SYSTEM_STATE_PRIVATE] = "system-state-private",
+        [SD_PATH_SYSTEM_STATE_LOGS] = "system-state-logs",
+        [SD_PATH_SYSTEM_STATE_CACHE] = "system-state-cache",
+        [SD_PATH_SYSTEM_STATE_SPOOL] = "system-state-spool",
+        [SD_PATH_USER_BINARIES] = "user-binaries",
+        [SD_PATH_USER_LIBRARY_PRIVATE] = "user-library-private",
+        [SD_PATH_USER_LIBRARY_ARCH] = "user-library-arch",
+        [SD_PATH_USER_SHARED] = "user-shared",
+        [SD_PATH_USER_CONFIGURATION] = "user-configuration",
+        [SD_PATH_USER_RUNTIME] = "user-runtime",
+        [SD_PATH_USER_STATE_CACHE] = "user-state-cache",
+        [SD_PATH_USER] = "user",
+        [SD_PATH_USER_DOCUMENTS] = "user-documents",
+        [SD_PATH_USER_MUSIC] = "user-music",
+        [SD_PATH_USER_PICTURES] = "user-pictures",
+        [SD_PATH_USER_VIDEOS] = "user-videos",
+        [SD_PATH_USER_DOWNLOAD] = "user-download",
+        [SD_PATH_USER_PUBLIC] = "user-public",
+        [SD_PATH_USER_TEMPLATES] = "user-templates",
+        [SD_PATH_USER_DESKTOP] = "user-desktop",
+        [SD_PATH_SEARCH_BINARIES] = "search-binaries",
+        [SD_PATH_SEARCH_LIBRARY_PRIVATE] = "search-library-private",
+        [SD_PATH_SEARCH_LIBRARY_ARCH] = "search-library-arch",
+        [SD_PATH_SEARCH_SHARED] = "search-shared",
+        [SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory",
+        [SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory",
+        [SD_PATH_SEARCH_CONFIGURATION] = "search-configuration",
+};
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [NAME...]\n\n"
+               "Show system and user paths.\n\n"
+               "  -h --help             Show this help\n"
+               "     --version          Show package version\n"
+               "     --suffix=SUFFIX    Suffix to append to paths\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int list_homes(void) {
+        uint64_t i = 0;
+        int r = 0;
+
+        for (i = 0; i < ELEMENTSOF(path_table); i++) {
+                _cleanup_free_ char *p = NULL;
+                int q;
+
+                q = sd_path_home(i, arg_suffix, &p);
+                if (q == -ENXIO)
+                        continue;
+                if (q < 0) {
+                        log_error("Failed to query %s: %s", path_table[i], strerror(-r));
+                        r = q;
+                        continue;
+                }
+
+                printf("%s: %s\n", path_table[i], p);
+        }
+
+        return r;
+}
+
+static int print_home(const char *n) {
+        uint64_t i = 0;
+        int r;
+
+        for (i = 0; i < ELEMENTSOF(path_table); i++) {
+                if (streq(path_table[i], n)) {
+                        _cleanup_free_ char *p = NULL;
+
+                        r = sd_path_home(i, arg_suffix, &p);
+                        if (r < 0) {
+                                log_error("Failed to query %s: %s", n, strerror(-r));
+                                return r;
+                        }
+
+                        printf("%s\n", p);
+                        return 0;
+                }
+        }
+
+        log_error("Path %s not known.", n);
+        return -ENOTSUP;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_SUFFIX,
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "suffix",    required_argument, NULL, ARG_SUFFIX    },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case ARG_SUFFIX:
+                        arg_suffix = optarg;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+        }
+
+        return 1;
+}
+
+int main(int argc, char* argv[]) {
+        int r;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                goto finish;
+
+        if (argc > optind) {
+                int i, q;
+
+                for (i = optind; i < argc; i++) {
+                        q = print_home(argv[i]);
+                        if (q < 0)
+                                r = q;
+                }
+        } else
+                r = list_homes();
+
+
+finish:
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
index 20e848bd8f6245858a1c5e57c2755bb12fe2d1f1..08079244dc15c7acb86f1c915c7a66351f008fd9 100644 (file)
@@ -23,6 +23,8 @@
 
 #include "util.h"
 
+/* A cleaned up architecture definition */
+
 typedef enum Architecture {
         ARCHITECTURE_X86 = 0,
         ARCHITECTURE_X86_64,
@@ -38,7 +40,9 @@ typedef enum Architecture {
         ARCHITECTURE_SPARC,
         ARCHITECTURE_SPARC64,
         ARCHITECTURE_MIPS,
+        ARCHITECTURE_MIPS_LE,
         ARCHITECTURE_MIPS64,
+        ARCHITECTURE_MIPS64_LE,
         ARCHITECTURE_ALPHA,
         ARCHITECTURE_ARM,
         ARCHITECTURE_ARM_BE,
@@ -55,64 +59,107 @@ typedef enum Architecture {
 
 Architecture uname_architecture(void);
 
+/*
+ * ARCH_TUPLE should resolve to the local architecture systemd is
+ * built for, according to the Debian tuple list:
+ *
+ * https://wiki.debian.org/Multiarch/Tuples
+ *
+ */
+
 #if defined(__x86_64__)
 #  define native_architecture() ARCHITECTURE_X86_64
+#  define ARCH_TUPLE "x86_64-linux-gnu"
 #elif defined(__i386__)
 #  define native_architecture() ARCHITECTURE_X86
+#  define ARCH_TUPLE "i386-linux-gnu"
 #elif defined(__powerpc64__)
 #  if defined(WORDS_BIGENDIAN)
 #    define native_architecture() ARCHITECTURE_PPC64
+#    define ARCH_TUPLE "ppc64-linux-gnu"
 #  else
 #    define native_architecture() ARCHITECTURE_PPC64_LE
+#    error "Missing ARCH_TUPLE for PPC64LE"
 #  endif
 #elif defined(__powerpc__)
 #  if defined(WORDS_BIGENDIAN)
 #    define native_architecture() ARCHITECTURE_PPC
+#    define ARCH_TUPLE "powerpc-linux-gnu"
 #  else
 #    define native_architecture() ARCHITECTURE_PPC_LE
+#    error "Missing ARCH_TUPLE for PPCLE"
 #  endif
 #elif defined(__ia64__)
 #  define native_architecture() ARCHITECTURE_IA64
+#  define ARCH_TUPLE "ia64-linux-gnu"
 #elif defined(__hppa64__)
 #  define native_architecture() ARCHITECTURE_PARISC64
+#  error "Missing ARCH_TUPLE for HPPA64"
 #elif defined(__hppa__)
 #  define native_architecture() ARCHITECTURE_PARISC
+#  define ARCH_TUPLE "hppa‑linux‑gnu"
 #elif defined(__s390x__)
 #  define native_architecture() ARCHITECTURE_S390X
+#  define ARCH_TUPLE "s390x-linux-gnu"
 #elif defined(__s390__)
 #  define native_architecture() ARCHITECTURE_S390
+#  define ARCH_TUPLE "s390-linux-gnu"
 #elif defined(__sparc64__)
 #  define native_architecture() ARCHITECTURE_SPARC64
+#  define ARCH_TUPLE "sparc64-linux-gnu"
 #elif defined(__sparc__)
 #  define native_architecture() ARCHITECTURE_SPARC
+#  define ARCH_TUPLE "sparc-linux-gnu"
 #elif defined(__mips64__)
-#  define native_architecture() ARCHITECTURE_MIPS64
+#  if defined(WORDS_BIGENDIAN)
+#    define native_architecture() ARCHITECTURE_MIPS64
+#    error "Missing ARCH_TUPLE for MIPS64"
+#  else
+#    define native_architecture() ARCHITECTURE_MIPS64_LE
+#    error "Missing ARCH_TUPLE for MIPS64_LE"
+#  endif
 #elif defined(__mips__)
-#  define native_architecture() ARCHITECTURE_MIPS
+#  if defined(WORDS_BIGENDIAN)
+#    define native_architecture() ARCHITECTURE_MIPS
+#    define ARCH_TUPLE "mips-linux-gnu"
+#  else
+#    define native_architecture() ARCHITECTURE_MIPS_LE
+#    define ARCH_TUPLE "mipsel-linux-gnu"
+#endif
 #elif defined(__alpha__)
 #  define native_architecture() ARCHITECTURE_ALPHA
+#  define ARCH_TUPLE "alpha-linux-gnu"
 #elif defined(__aarch64__)
 #  if defined(WORDS_BIGENDIAN)
 #    define native_architecture() ARCHITECTURE_ARM64_BE
+#    define ARCH_TUPLE "aarch64_be-linux-gnu"
 #  else
 #    define native_architecture() ARCHITECTURE_ARM64
+#    define ARCH_TUPLE "aarch64-linux-gnu"
 #  endif
 #elif defined(__arm__)
 #  if defined(WORDS_BIGENDIAN)
 #    define native_architecture() ARCHITECTURE_ARM_BE
+#    error "Missing ARCH_TUPLE for ARM_BE"
 #  else
 #    define native_architecture() ARCHITECTURE_ARM
+#    error "Missing ARCH_TUPLE for ARM"
 #  endif
 #elif defined(__sh64__)
 #  define native_architecture() ARCHITECTURE_SH64
+#  error "Missing ARCH_TUPLE for SH64"
 #elif defined(__sh__)
 #  define native_architecture() ARCHITECTURE_SH
+#  define ARCH_TUPLE "sh4-linux-gnu"
 #elif defined(__m68k__)
 #  define native_architecture() ARCHITECTURE_M68K
+#  define ARCH_TUPLE "m68k-linux-gnu"
 #elif defined(__tilegx__)
 #  define native_architecture() ARCHITECTURE_TILEGX
+#  error "Missing ARCH_TUPLE for TILEGX"
 #elif defined(__cris__)
 #  define native_architecture() ARCHITECTURE_CRIS
+#  error "Missing ARCH_TUPLE for CRIS"
 #else
 #error "Please register your architecture here!"
 #endif
index 1ef0b26a25b0d6eb091507e7c0281e53be484398..b4c476eff27df9858698ad4f52e66210b6696bb7 100644 (file)
@@ -378,6 +378,30 @@ int strv_push(char ***l, char *value) {
         return 0;
 }
 
+int strv_push_prepend(char ***l, char *value) {
+        char **c;
+        unsigned n, i;
+
+        if (!value)
+                return 0;
+
+        n = strv_length(*l);
+        c = new(char*, n + 2);
+        if (!c)
+                return -ENOMEM;
+
+        for (i = 0; i < n; i++)
+                c[i+1] = (*l)[i];
+
+        c[0] = value;
+        c[n+1] = NULL;
+
+        free(*l);
+        *l = c;
+
+        return 0;
+}
+
 int strv_consume(char ***l, char *value) {
         int r;
 
@@ -388,6 +412,16 @@ int strv_consume(char ***l, char *value) {
         return r;
 }
 
+int strv_consume_prepend(char ***l, char *value) {
+        int r;
+
+        r = strv_push_prepend(l, value);
+        if (r < 0)
+                free(value);
+
+        return r;
+}
+
 int strv_extend(char ***l, const char *value) {
         char *v;
 
index e26ab828d2598922a1a2b2148d5d8624ea142988..3034073d3288e5a28871769b4561b4d53d70837c 100644 (file)
@@ -41,7 +41,9 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
 int strv_extend(char ***l, const char *value);
 int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
 int strv_push(char ***l, char *value);
+int strv_push_prepend(char ***l, char *value);
 int strv_consume(char ***l, char *value);
+int strv_consume_prepend(char ***l, char *value);
 
 char **strv_remove(char **l, const char *s);
 char **strv_uniq(char **l);
index 9b5a47ab6fb6d3735937945c156d7a526171adfe..a1c8baf237f12999d65268e6e6973283def34783 100644 (file)
@@ -5226,8 +5226,8 @@ int get_home_dir(char **_h) {
         assert(_h);
 
         /* Take the user specified one */
-        e = getenv("HOME");
-        if (e) {
+        e = secure_getenv("HOME");
+        if (e && path_is_absolute(e)) {
                 h = strdup(e);
                 if (!h)
                         return -ENOMEM;
diff --git a/src/systemd/sd-path.h b/src/systemd/sd-path.h
new file mode 100644 (file)
index 0000000..e238c0c
--- /dev/null
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdpathhfoo
+#define foosdpathhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+enum {
+        /* Temporary files */
+        SD_PATH_TEMPORARY = 0x0ULL,
+        SD_PATH_TEMPORARY_LARGE,
+
+        /* Vendor supplied data */
+        SD_PATH_SYSTEM_BINARIES,
+        SD_PATH_SYSTEM_INCLUDE,
+        SD_PATH_SYSTEM_LIBRARY_PRIVATE,
+        SD_PATH_SYSTEM_LIBRARY_ARCH,
+        SD_PATH_SYSTEM_SHARED,
+        SD_PATH_SYSTEM_CONFIGURATION_FACTORY,
+        SD_PATH_SYSTEM_STATE_FACTORY,
+
+        /* System configuration, runtime, state, ... */
+        SD_PATH_SYSTEM_CONFIGURATION,
+        SD_PATH_SYSTEM_RUNTIME,
+        SD_PATH_SYSTEM_RUNTIME_LOGS,
+        SD_PATH_SYSTEM_STATE_PRIVATE,
+        SD_PATH_SYSTEM_STATE_LOGS,
+        SD_PATH_SYSTEM_STATE_CACHE,
+        SD_PATH_SYSTEM_STATE_SPOOL,
+
+        /* Vendor supplied data */
+        SD_PATH_USER_BINARIES,
+        SD_PATH_USER_LIBRARY_PRIVATE,
+        SD_PATH_USER_LIBRARY_ARCH,
+        SD_PATH_USER_SHARED,
+
+        /* User configuration, state, runtime ... */
+        SD_PATH_USER_CONFIGURATION, /* takes both actual configuration (like /etc) and state (like /var/lib) */
+        SD_PATH_USER_RUNTIME,
+        SD_PATH_USER_STATE_CACHE,
+
+        /* User resources */
+        SD_PATH_USER, /* $HOME itself */
+        SD_PATH_USER_DOCUMENTS,
+        SD_PATH_USER_MUSIC,
+        SD_PATH_USER_PICTURES,
+        SD_PATH_USER_VIDEOS,
+        SD_PATH_USER_DOWNLOAD,
+        SD_PATH_USER_PUBLIC,
+        SD_PATH_USER_TEMPLATES,
+        SD_PATH_USER_DESKTOP,
+
+        /* Search paths */
+        SD_PATH_SEARCH_BINARIES,
+        SD_PATH_SEARCH_LIBRARY_PRIVATE,
+        SD_PATH_SEARCH_LIBRARY_ARCH,
+        SD_PATH_SEARCH_SHARED,
+        SD_PATH_SEARCH_CONFIGURATION_FACTORY,
+        SD_PATH_SEARCH_STATE_FACTORY,
+        SD_PATH_SEARCH_CONFIGURATION,
+
+        _SD_PATH_MAX,
+};
+
+int sd_path_home(uint64_t type, const char *suffix, char **path);
+int sd_path_search(uint64_t type, const char *suffix, char ***paths);
+
+#endif