]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/path-util.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / basic / path-util.c
index 103962330599a19949e2d77eba701544925876f4..0015667ac0954d24ae2c0acfbce142aaa20455d9 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <string.h>
-#include <unistd.h>
 #include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
 #include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/statvfs.h>
+#include <unistd.h>
 
-#include "macro.h"
-#include "util.h"
+#include "fd-util.h"
+#include "fileio.h"
 #include "log.h"
-#include "strv.h"
-#include "path-util.h"
+#include "macro.h"
 #include "missing.h"
-#include "fileio.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
 
 bool path_is_absolute(const char *p) {
         return p[0] == '/';
@@ -84,20 +87,25 @@ int path_get_parent(const char *path, char **_r) {
         return 0;
 }
 
-char **path_split_and_make_absolute(const char *p) {
+int path_split_and_make_absolute(const char *p, char ***ret) {
         char **l;
+        int r;
+
         assert(p);
+        assert(ret);
 
         l = strv_split(p, ":");
         if (!l)
-                return NULL;
+                return -ENOMEM;
 
-        if (!path_strv_make_absolute_cwd(l)) {
+        r = path_strv_make_absolute_cwd(l);
+        if (r < 0) {
                 strv_free(l);
-                return NULL;
+                return r;
         }
 
-        return l;
+        *ret = l;
+        return r;
 }
 
 char *path_make_absolute(const char *p, const char *prefix) {
@@ -112,22 +120,31 @@ char *path_make_absolute(const char *p, const char *prefix) {
         return strjoin(prefix, "/", p, NULL);
 }
 
-char *path_make_absolute_cwd(const char *p) {
-        _cleanup_free_ char *cwd = NULL;
+int path_make_absolute_cwd(const char *p, char **ret) {
+        char *c;
 
         assert(p);
+        assert(ret);
 
         /* Similar to path_make_absolute(), but prefixes with the
          * current working directory. */
 
         if (path_is_absolute(p))
-                return strdup(p);
+                c = strdup(p);
+        else {
+                _cleanup_free_ char *cwd = NULL;
 
-        cwd = get_current_dir_name();
-        if (!cwd)
-                return NULL;
+                cwd = get_current_dir_name();
+                if (!cwd)
+                        return -errno;
+
+                c = strjoin(cwd, "/", p, NULL);
+        }
+        if (!c)
+                return -ENOMEM;
 
-        return strjoin(cwd, "/", p, NULL);
+        *ret = c;
+        return 0;
 }
 
 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
@@ -215,8 +232,9 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
         return 0;
 }
 
-char **path_strv_make_absolute_cwd(char **l) {
+int path_strv_make_absolute_cwd(char **l) {
         char **s;
+        int r;
 
         /* Goes through every item in the string list and makes it
          * absolute. This works in place and won't rollback any
@@ -225,15 +243,15 @@ char **path_strv_make_absolute_cwd(char **l) {
         STRV_FOREACH(s, l) {
                 char *t;
 
-                t = path_make_absolute_cwd(*s);
-                if (!t)
-                        return NULL;
+                r = path_make_absolute_cwd(*s, &t);
+                if (r < 0)
+                        return r;
 
                 free(*s);
                 *s = t;
         }
 
-        return l;
+        return 0;
 }
 
 char **path_strv_resolve(char **l, const char *prefix) {
@@ -411,7 +429,7 @@ int path_compare(const char *a, const char *b) {
          * Which one is sorted before the other does not really matter.
          * Here a relative path is ordered before an absolute path. */
         d = (a[0] == '/') - (b[0] == '/');
-        if (d)
+        if (d != 0)
                 return d;
 
         for (;;) {
@@ -434,12 +452,12 @@ int path_compare(const char *a, const char *b) {
 
                 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
                 d = memcmp(a, b, MIN(j, k));
-                if (d)
+                if (d != 0)
                         return (d > 0) - (d < 0); /* sign of d */
 
                 /* Sort "/foo/a" before "/foo/aaa" */
                 d = (j > k) - (j < k);  /* sign of (j - k) */
-                if (d)
+                if (d != 0)
                         return d;
 
                 a += j;
@@ -698,7 +716,6 @@ int path_is_os_tree(const char *path) {
         /* We use /usr/lib/os-release as flag file if something is an OS */
         p = strjoina(path, "/usr/lib/os-release");
         r = access(p, F_OK);
-
         if (r >= 0)
                 return 1;
 
@@ -709,56 +726,66 @@ int path_is_os_tree(const char *path) {
         return r >= 0;
 }
 
-int find_binary(const char *name, bool local, char **filename) {
+int find_binary(const char *name, char **ret) {
+        int last_error, r;
+        const char *p;
+
         assert(name);
 
         if (is_path(name)) {
-                if (local && access(name, X_OK) < 0)
+                if (access(name, X_OK) < 0)
                         return -errno;
 
-                if (filename) {
-                        char *p;
-
-                        p = path_make_absolute_cwd(name);
-                        if (!p)
-                                return -ENOMEM;
-
-                        *filename = p;
+                if (ret) {
+                        r = path_make_absolute_cwd(name, ret);
+                        if (r < 0)
+                                return r;
                 }
 
                 return 0;
-        } else {
-                const char *path;
-                const char *word, *state;
-                size_t l;
-
-                /**
-                 * Plain getenv, not secure_getenv, because we want
-                 * to actually allow the user to pick the binary.
-                 */
-                path = getenv("PATH");
-                if (!path)
-                        path = DEFAULT_PATH;
-
-                FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
-                        _cleanup_free_ char *p = NULL;
-
-                        if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
-                                return -ENOMEM;
+        }
 
-                        if (access(p, X_OK) < 0)
-                                continue;
+        /**
+         * Plain getenv, not secure_getenv, because we want
+         * to actually allow the user to pick the binary.
+         */
+        p = getenv("PATH");
+        if (!p)
+                p = DEFAULT_PATH;
+
+        last_error = -ENOENT;
+
+        for (;;) {
+                _cleanup_free_ char *j = NULL, *element = NULL;
+
+                r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (!path_is_absolute(element))
+                        continue;
+
+                j = strjoin(element, "/", name, NULL);
+                if (!j)
+                        return -ENOMEM;
+
+                if (access(j, X_OK) >= 0) {
+                        /* Found it! */
 
-                        if (filename) {
-                                *filename = path_kill_slashes(p);
-                                p = NULL;
+                        if (ret) {
+                                *ret = path_kill_slashes(j);
+                                j = NULL;
                         }
 
                         return 0;
                 }
 
-                return -ENOENT;
+                last_error = -errno;
         }
+
+        return last_error;
 }
 
 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
@@ -800,7 +827,9 @@ static int binary_is_good(const char *binary) {
         _cleanup_free_ char *p = NULL, *d = NULL;
         int r;
 
-        r = find_binary(binary, true, &p);
+        r = find_binary(binary, &p);
+        if (r == -ENOENT)
+                return 0;
         if (r < 0)
                 return r;
 
@@ -808,28 +837,38 @@ static int binary_is_good(const char *binary) {
          * fsck */
 
         r = readlink_malloc(p, &d);
-        if (r >= 0 &&
-            (path_equal(d, "/bin/true") ||
-             path_equal(d, "/usr/bin/true") ||
-             path_equal(d, "/dev/null")))
-                return -ENOENT;
+        if (r == -EINVAL) /* not a symlink */
+                return 1;
+        if (r < 0)
+                return r;
 
-        return 0;
+        return !path_equal(d, "true") &&
+               !path_equal(d, "/bin/true") &&
+               !path_equal(d, "/usr/bin/true") &&
+               !path_equal(d, "/dev/null");
 }
 
 int fsck_exists(const char *fstype) {
         const char *checker;
 
-        checker = strjoina("fsck.", fstype);
+        assert(fstype);
 
+        if (streq(fstype, "auto"))
+                return -EINVAL;
+
+        checker = strjoina("fsck.", fstype);
         return binary_is_good(checker);
 }
 
 int mkfs_exists(const char *fstype) {
         const char *mkfs;
 
-        mkfs = strjoina("mkfs.", fstype);
+        assert(fstype);
 
+        if (streq(fstype, "auto"))
+                return -EINVAL;
+
+        mkfs = strjoina("mkfs.", fstype);
         return binary_is_good(mkfs);
 }
 
@@ -866,3 +905,35 @@ char *prefix_root(const char *root, const char *path) {
         strcpy(p, path);
         return n;
 }
+
+int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
+        char *p;
+        int r;
+
+        /*
+         * This function is intended to be used in command line
+         * parsers, to handle paths that are passed in. It makes the
+         * path absolute, and reduces it to NULL if omitted or
+         * root (the latter optionally).
+         *
+         * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
+         * SUCCESS! Hence, do not pass in uninitialized pointers.
+         */
+
+        if (isempty(path)) {
+                *arg = mfree(*arg);
+                return 0;
+        }
+
+        r = path_make_absolute_cwd(path, &p);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
+
+        path_kill_slashes(p);
+        if (suppress_root && path_equal(p, "/"))
+                p = mfree(p);
+
+        free(*arg);
+        *arg = p;
+        return 0;
+}