]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vpick: add new tool "systemd-vpick" which exposes vpick on the command line
authorLennart Poettering <lennart@poettering.net>
Fri, 3 Mar 2023 17:26:45 +0000 (18:26 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 3 Jan 2024 17:38:46 +0000 (18:38 +0100)
Usecase:

    $ du $(systemd-vpick /srv/myimages.v/foo___.raw)

In order to determine size of newest image in /srv/myimages.v/

meson.build
src/vpick/meson.build [new file with mode: 0644]
src/vpick/vpick-tool.c [new file with mode: 0644]

index d1433e3dbc750f0ae043acef7a845e71483622b7..d2d255391d523f009976acbb287a4e0f71ae4fd1 100644 (file)
@@ -2230,6 +2230,7 @@ subdir('src/vconsole')
 subdir('src/veritysetup')
 subdir('src/vmspawn')
 subdir('src/volatile-root')
+subdir('src/vpick')
 subdir('src/xdg-autostart-generator')
 
 subdir('src/systemd')
diff --git a/src/vpick/meson.build b/src/vpick/meson.build
new file mode 100644 (file)
index 0000000..a8c14cb
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        executable_template + {
+                'name' : 'systemd-vpick',
+                'public' : true,
+                'sources' : files('vpick-tool.c'),
+        },
+]
diff --git a/src/vpick/vpick-tool.c b/src/vpick/vpick-tool.c
new file mode 100644 (file)
index 0000000..0ae8fe3
--- /dev/null
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "architecture.h"
+#include "build.h"
+#include "format-table.h"
+#include "fs-util.h"
+#include "main-func.h"
+#include "path-util.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "vpick.h"
+
+static char *arg_filter_basename = NULL;
+static char *arg_filter_version = NULL;
+static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID;
+static char *arg_filter_suffix = NULL;
+static uint32_t arg_filter_type_mask = 0;
+static enum {
+        PRINT_PATH,
+        PRINT_FILENAME,
+        PRINT_VERSION,
+        PRINT_TYPE,
+        PRINT_ARCHITECTURE,
+        PRINT_TRIES,
+        PRINT_ALL,
+        _PRINT_INVALID = -EINVAL,
+} arg_print = _PRINT_INVALID;
+static PickFlags arg_flags = PICK_ARCHITECTURE|PICK_TRIES;
+
+STATIC_DESTRUCTOR_REGISTER(arg_filter_basename, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_version, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_suffix, freep);
+
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-vpick", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] PATH...\n"
+               "\n%5$sPick entry from versioned directory.%6$s\n\n"
+               "  -h --help            Show this help\n"
+               "     --version         Show package version\n"
+               "\n%3$sLookup Keys:%4$s\n"
+               "  -B --basename=BASENAME\n"
+               "                       Look for specified basename\n"
+               "  -V VERSION           Look for specified version\n"
+               "  -A ARCH              Look for specified architecture\n"
+               "  -S --suffix=SUFFIX   Look for specified suffix\n"
+               "  -t --type=TYPE       Look for specified inode type\n"
+               "\n%3$sOutput:%4$s\n"
+               "  -p --print=filename  Print selected filename rather than path\n"
+               "  -p --print=version   Print selected version rather than path\n"
+               "  -p --print=type      Print selected inode type rather than path\n"
+               "  -p --print=arch      Print selected architecture rather than path\n"
+               "  -p --print=tries     Print selected tries left/tries done rather than path\n"
+               "  -p --print=all       Print all of the above\n"
+               "     --resolve=yes     Canonicalize the result path\n"
+               "\nSee the %2$s for details.\n",
+               program_invocation_short_name,
+               link,
+               ansi_underline(), ansi_normal(),
+               ansi_highlight(), ansi_normal());
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_RESOLVE,
+        };
+
+        static const struct option options[] = {
+                { "help",         no_argument,       NULL, 'h'          },
+                { "version",      no_argument,       NULL, ARG_VERSION  },
+                { "basename",     required_argument, NULL, 'B'          },
+                { "suffix",       required_argument, NULL, 'S'          },
+                { "type",         required_argument, NULL, 't'          },
+                { "print",        required_argument, NULL, 'p'          },
+                { "resolve",      required_argument, NULL, ARG_RESOLVE  },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hB:V:A:S:t:p:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'B':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_basename, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'V':
+                        if (!version_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_version, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'A':
+                        if (streq(optarg, "native"))
+                                arg_filter_architecture = native_architecture();
+                        else if (streq(optarg, "secondary")) {
+#ifdef ARCHITECTURE_SECONDARY
+                                arg_filter_architecture = ARCHITECTURE_SECONDARY;
+#else
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Local architecture has no secondary architecture.");
+#endif
+                        } else if (streq(optarg, "uname"))
+                                arg_filter_architecture = uname_architecture();
+                        else if (streq(optarg, "auto"))
+                                arg_filter_architecture = _ARCHITECTURE_INVALID;
+                        else {
+                                arg_filter_architecture = architecture_from_string(optarg);
+                                if (arg_filter_architecture < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", optarg);
+                        }
+                        break;
+
+                case 'S':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_suffix, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 't':
+                        if (isempty(optarg))
+                                arg_filter_type_mask = 0;
+                        else {
+                                mode_t m;
+
+                                m = inode_type_from_string(optarg);
+                                if (m == MODE_INVALID)
+                                        return log_error_errno(m, "Unknown inode type: %s", optarg);
+
+                                arg_filter_type_mask |= UINT32_C(1) << IFTODT(m);
+                        }
+
+                        break;
+
+                case 'p':
+                        if (streq(optarg, "path"))
+                                arg_print = PRINT_PATH;
+                        else if (streq(optarg, "filename"))
+                                arg_print = PRINT_FILENAME;
+                        else if (streq(optarg, "version"))
+                                arg_print = PRINT_VERSION;
+                        else if (streq(optarg, "type"))
+                                arg_print = PRINT_TYPE;
+                        else if (STR_IN_SET(optarg, "arch", "architecture"))
+                                arg_print = PRINT_ARCHITECTURE;
+                        else if (streq(optarg, "tries"))
+                                arg_print = PRINT_TRIES;
+                        else if (streq(optarg, "all"))
+                                arg_print = PRINT_ALL;
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --print= argument: %s", optarg);
+
+                        break;
+
+                case ARG_RESOLVE:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --resolve= value: %m");
+
+                        SET_FLAG(arg_flags, PICK_RESOLVE, r);
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        if (arg_print < 0)
+                arg_print = PRINT_PATH;
+
+        return 1;
+}
+
+static int run(int argc, char *argv[]) {
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (optind >= argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path to resolve must be specified.");
+
+        for (int i = optind; i < argc; i++) {
+                _cleanup_free_ char *p = NULL;
+                r = path_make_absolute_cwd(argv[i], &p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make path '%s' absolute: %m", argv[i]);
+
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              p,
+                              &(PickFilter) {
+                                      .basename = arg_filter_basename,
+                                      .version = arg_filter_version,
+                                      .architecture = arg_filter_architecture,
+                                      .suffix = arg_filter_suffix,
+                                      .type_mask = arg_filter_type_mask,
+                              },
+                              arg_flags,
+                              &result);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pick version for '%s': %m", p);
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching version for '%s' found.", p);
+
+                switch (arg_print) {
+
+                case PRINT_PATH:
+                        fputs(result.path, stdout);
+                        if (result.st.st_mode != MODE_INVALID && S_ISDIR(result.st.st_mode) && !endswith(result.path, "/"))
+                                fputc('/', stdout);
+                        fputc('\n', stdout);
+                        break;
+
+                case PRINT_FILENAME: {
+                        _cleanup_free_ char *fname = NULL;
+
+                        r = path_extract_filename(result.path, &fname);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract filename from path '%s': %m", result.path);
+
+                        puts(fname);
+                        break;
+                }
+
+                case PRINT_VERSION:
+                        if (!result.version)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No version information discovered.");
+
+                        puts(result.version);
+                        break;
+
+                case PRINT_TYPE:
+                        if (result.st.st_mode == MODE_INVALID)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No inode type information discovered.");
+
+                        puts(inode_type_to_string(result.st.st_mode));
+                        break;
+
+                case PRINT_ARCHITECTURE:
+                        if (result.architecture < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No architecture information discovered.");
+
+                        puts(architecture_to_string(result.architecture));
+                        break;
+
+                case PRINT_TRIES:
+                        if (result.tries_left == UINT_MAX)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No tries left/tries done information discovered.");
+
+                        printf("+%u-%u", result.tries_left, result.tries_done);
+                        break;
+
+                case PRINT_ALL: {
+                        _cleanup_(table_unrefp) Table *t = NULL;
+
+                        t = table_new_vertical();
+                        if (!t)
+                                return log_oom();
+
+                        table_set_ersatz_string(t, TABLE_ERSATZ_NA);
+
+                        r = table_add_many(
+                                        t,
+                                        TABLE_FIELD, "Path",
+                                        TABLE_PATH, result.path,
+                                        TABLE_FIELD, "Version",
+                                        TABLE_STRING, result.version,
+                                        TABLE_FIELD, "Type",
+                                        TABLE_STRING, result.st.st_mode == MODE_INVALID ? NULL : inode_type_to_string(result.st.st_mode),
+                                        TABLE_FIELD, "Architecture",
+                                        TABLE_STRING, result.architecture < 0 ? NULL : architecture_to_string(result.architecture));
+                        if (r < 0)
+                                return table_log_add_error(r);
+
+                        if (result.tries_left != UINT_MAX) {
+                                r = table_add_many(
+                                                t,
+                                                TABLE_FIELD, "Tries left",
+                                                TABLE_UINT, result.tries_left,
+                                                TABLE_FIELD, "Tries done",
+                                                TABLE_UINT, result.tries_done);
+                                if (r < 0)
+                                        return table_log_add_error(r);
+                        }
+
+                        r = table_print(t, stdout);
+                        if (r < 0)
+                                return table_log_print_error(r);
+
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);