diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-03-30 09:32:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-30 09:32:13 +0200 |
commit | 1b07fd3c228fcbbe71653e378c696cf6a8323a32 (patch) | |
tree | 1bcb9b4f0a0aac3fc31805666a5a7a9a02ab2e8b /src | |
parent | 0362953e9a5e472b5cd66687e79a957b37e35e09 (diff) | |
parent | 3739c2fdfc9d01cd5dd5f349fdc5cea1561517f1 (diff) | |
download | systemd-1b07fd3c228fcbbe71653e378c696cf6a8323a32.tar.gz |
Merge pull request #27041 from poettering/fdstore-dump
add ability to show contents of service fdstore + teach systemd-notify passing fds into the fdstore
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze/analyze-fdstore.c | 110 | ||||
-rw-r--r-- | src/analyze/analyze-fdstore.h | 5 | ||||
-rw-r--r-- | src/analyze/analyze.c | 9 | ||||
-rw-r--r-- | src/analyze/meson.build | 1 | ||||
-rw-r--r-- | src/basic/devnum-util.c | 2 | ||||
-rw-r--r-- | src/basic/devnum-util.h | 4 | ||||
-rw-r--r-- | src/basic/fd-util.c | 13 | ||||
-rw-r--r-- | src/basic/fd-util.h | 2 | ||||
-rw-r--r-- | src/basic/missing_fcntl.h | 9 | ||||
-rw-r--r-- | src/basic/stat-util.c | 23 | ||||
-rw-r--r-- | src/basic/stat-util.h | 2 | ||||
-rw-r--r-- | src/basic/terminal-util.c | 2 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 9 | ||||
-rw-r--r-- | src/core/dbus-service.c | 74 | ||||
-rw-r--r-- | src/core/dbus-service.h | 1 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-common-errors.c | 2 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-common-errors.h | 2 | ||||
-rw-r--r-- | src/libsystemd/sd-device/device-util.c | 2 | ||||
-rw-r--r-- | src/notify/notify.c | 109 | ||||
-rw-r--r-- | src/shared/fdset.c | 143 | ||||
-rw-r--r-- | src/shared/fdset.h | 3 | ||||
-rw-r--r-- | src/shared/format-table.c | 49 | ||||
-rw-r--r-- | src/shared/format-table.h | 4 | ||||
-rw-r--r-- | src/udev/udevadm-lock.c | 4 |
24 files changed, 509 insertions, 75 deletions
diff --git a/src/analyze/analyze-fdstore.c b/src/analyze/analyze-fdstore.c new file mode 100644 index 0000000000..13db7f5088 --- /dev/null +++ b/src/analyze/analyze-fdstore.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze-fdstore.h" +#include "analyze.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "fd-util.h" +#include "format-table.h" + +static int dump_fdstore(sd_bus *bus, const char *arg) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(table_unrefp) Table *table = NULL; + _cleanup_free_ char *unit = NULL; + int r; + + assert(bus); + assert(arg); + + r = unit_name_mangle_with_suffix(arg, NULL, UNIT_NAME_MANGLE_GLOB, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to mangle name '%s': %m", arg); + + r = bus_call_method( + bus, + bus_systemd_mgr, + "DumpUnitFileDescriptorStore", + &error, + &reply, + "s", unit); + if (r < 0) + return log_error_errno(r, "Failed to call DumpUnitFileDescriptorStore: %s", + bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, 'a', "(suuutuusu)"); + if (r < 0) + return bus_log_parse_error(r); + + table = table_new("fdname", "type", "devno", "inode", "rdevno", "path", "flags"); + if (!table) + return log_oom(); + + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); + + (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100); + + for (;;) { + uint32_t mode, major, minor, rmajor, rminor, flags; + const char *fdname, *path; + uint64_t inode; + + r = sd_bus_message_read( + reply, + "(suuutuusu)", + &fdname, + &mode, + &major, &minor, + &inode, + &rmajor, &rminor, + &path, + &flags); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + r = table_add_many( + table, + TABLE_STRING, fdname, + TABLE_MODE_INODE_TYPE, mode, + TABLE_DEVNUM, makedev(major, minor), + TABLE_UINT64, inode, + TABLE_DEVNUM, makedev(rmajor, rminor), + TABLE_PATH, path, + TABLE_STRING, accmode_to_string(flags)); + if (r < 0) + return table_log_add_error(r); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && table_get_rows(table) <= 0) + log_info("No file descriptors in fdstore of '%s'.", unit); + else { + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */true); + if (r < 0) + return log_error_errno(r, "Failed to output table: %m"); + } + + return EXIT_SUCCESS; +} + +int verb_fdstore(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + STRV_FOREACH(arg, strv_skip(argv, 1)) { + r = dump_fdstore(bus, *arg); + if (r < 0) + return r; + } + + return EXIT_SUCCESS; +} diff --git a/src/analyze/analyze-fdstore.h b/src/analyze/analyze-fdstore.h new file mode 100644 index 0000000000..0b990db395 --- /dev/null +++ b/src/analyze/analyze-fdstore.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-bus.h" + +int verb_fdstore(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 12c0bd653f..0246da4b45 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -17,11 +17,13 @@ #include "analyze-calendar.h" #include "analyze-capability.h" #include "analyze-cat-config.h" +#include "analyze-compare-versions.h" #include "analyze-condition.h" #include "analyze-critical-chain.h" #include "analyze-dot.h" #include "analyze-dump.h" #include "analyze-exit-status.h" +#include "analyze-fdstore.h" #include "analyze-filesystems.h" #include "analyze-inspect-elf.h" #include "analyze-log-control.h" @@ -36,7 +38,6 @@ #include "analyze-timestamp.h" #include "analyze-unit-files.h" #include "analyze-unit-paths.h" -#include "analyze-compare-versions.h" #include "analyze-verify.h" #include "build.h" #include "bus-error.h" @@ -229,6 +230,7 @@ static int help(int argc, char *argv[], void *userdata) { " security [UNIT...] Analyze security of unit\n" " inspect-elf FILE... Parse and print ELF package metadata\n" " malloc [D-BUS SERVICE...] Dump malloc stats of a D-Bus service\n" + " fdstore SERVICE... Show file descriptor store contents of service\n" "\nOptions:\n" " --recursive-errors=MODE Control which units are verified\n" " --offline=BOOL Perform a security review on unit file(s)\n" @@ -531,9 +533,9 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --offline= is only supported for security right now."); - if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot")) + if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --json= is only supported for security, inspect-elf, and plot right now."); + "Option --json= is only supported for security, inspect-elf, plot, and fdstore right now."); if (arg_threshold != 100 && !streq_ptr(argv[optind], "security")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -620,6 +622,7 @@ static int run(int argc, char *argv[]) { { "security", VERB_ANY, VERB_ANY, 0, verb_security }, { "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection }, { "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc }, + { "fdstore", 2, VERB_ANY, 0, verb_fdstore }, {} }; diff --git a/src/analyze/meson.build b/src/analyze/meson.build index ac40600a6d..695089a0be 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -11,6 +11,7 @@ systemd_analyze_sources = files( 'analyze-dot.c', 'analyze-dump.c', 'analyze-exit-status.c', + 'analyze-fdstore.c', 'analyze-filesystems.c', 'analyze-inspect-elf.c', 'analyze-log-control.c', diff --git a/src/basic/devnum-util.c b/src/basic/devnum-util.c index de02255eef..f82e13bdb0 100644 --- a/src/basic/devnum-util.c +++ b/src/basic/devnum-util.c @@ -83,7 +83,7 @@ int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) { assert(ret); - if (major(devnum) == 0 && minor(devnum) == 0) + if (devnum_is_zero(devnum)) /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in * /dev/block/ and /dev/char/, hence we handle them specially here. */ return device_path_make_inaccessible(mode, ret); diff --git a/src/basic/devnum-util.h b/src/basic/devnum-util.h index 38aa4efa87..e109de9913 100644 --- a/src/basic/devnum-util.h +++ b/src/basic/devnum-util.h @@ -50,3 +50,7 @@ static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) { } #define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {}) + +static inline bool devnum_is_zero(dev_t d) { + return major(d) == 0 && minor(d) == 0; +} diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index b58a080d90..3cc4f44bcd 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -940,3 +940,16 @@ int dir_fd_is_root(int dir_fd) { */ return statx_inode_same(&st.sx, &pst.sx) && statx_mount_same(&st.nsx, &pst.nsx); } + +const char *accmode_to_string(int flags) { + switch (flags & O_ACCMODE) { + case O_RDONLY: + return "ro"; + case O_WRONLY: + return "wo"; + case O_RDWR: + return "rw"; + default: + return NULL; + } +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index a6353cf48e..91f3d7fe9d 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -115,3 +115,5 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f #define FORMAT_PROC_FD_PATH(fd) \ format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd)) + +const char *accmode_to_string(int flags); diff --git a/src/basic/missing_fcntl.h b/src/basic/missing_fcntl.h index 00937d2af0..79e95a8f6f 100644 --- a/src/basic/missing_fcntl.h +++ b/src/basic/missing_fcntl.h @@ -58,3 +58,12 @@ #ifndef O_TMPFILE #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #endif + +/* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS + * mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway — glibc does not mask + * it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves. */ +#if O_LARGEFILE != 0 +#define RAW_O_LARGEFILE O_LARGEFILE +#else +#define RAW_O_LARGEFILE 0100000 +#endif diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 6eaa3da459..2b09ab63b2 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -471,3 +471,26 @@ int inode_compare_func(const struct stat *a, const struct stat *b) { } DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free); + +const char* inode_type_to_string(mode_t m) { + + /* Returns a short string for the inode type. We use the same name as the underlying macros for each + * inode type. */ + + switch (m & S_IFMT) { + case S_IFREG: + return "reg"; + case S_IFDIR: + return "dir"; + case S_IFCHR: + return "chr"; + case S_IFBLK: + return "blk"; + case S_IFIFO: + return "fifo"; + case S_IFSOCK: + return "sock"; + } + + return NULL; +} diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 24684d5794..726d0644aa 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -101,3 +101,5 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s void inode_hash_func(const struct stat *q, struct siphash *state); int inode_compare_func(const struct stat *a, const struct stat *b); extern const struct hash_ops inode_hash_ops; + +const char* inode_type_to_string(mode_t m); diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index d3aa5c27e2..796c262acf 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -979,7 +979,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d) { &ttynr) != 1) return -EIO; - if (major(ttynr) == 0 && minor(ttynr) == 0) + if (devnum_is_zero(ttynr)) return -ENXIO; if (d) diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index c88ef93443..2cb9efc14e 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2840,6 +2840,10 @@ static int method_set_show_status(sd_bus_message *message, void *userdata, sd_bu return sd_bus_reply_method_return(message, NULL); } +static int method_dump_unit_descriptor_store(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_generic_unit_operation(message, userdata, error, bus_service_method_dump_file_descriptor_store, 0); +} + const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -3385,6 +3389,11 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_RESULT("a(us)", users), method_get_dynamic_users, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("DumpUnitFileDescriptorStore", + SD_BUS_ARGS("s", name), + SD_BUS_RESULT("a(suuutuusu)", entries), + method_dump_unit_descriptor_store, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL_WITH_ARGS("UnitNew", SD_BUS_ARGS("s", id, "o", unit), diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index ddfffe80fd..742893370f 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -4,6 +4,7 @@ #include "alloc-util.h" #include "async.h" +#include "bus-common-errors.h" #include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-execute.h" @@ -16,6 +17,7 @@ #include "fd-util.h" #include "fileio.h" #include "locale-util.h" +#include "missing_fcntl.h" #include "mount-util.h" #include "open-file.h" #include "parse-util.h" @@ -218,6 +220,72 @@ int bus_service_method_mount_image(sd_bus_message *message, void *userdata, sd_b return bus_service_method_mount(message, userdata, error, true); } +int bus_service_method_dump_file_descriptor_store(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Service *s = ASSERT_PTR(userdata); + int r; + + assert(message); + + r = mac_selinux_unit_access_check(UNIT(s), message, "status", error); + if (r < 0) + return r; + + if (s->n_fd_store_max == 0 && s->n_fd_store == 0) + return sd_bus_error_setf(error, BUS_ERROR_FILE_DESCRIPTOR_STORE_DISABLED, "File descriptor store not enabled for %s.", UNIT(s)->id); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(suuutuusu)"); + if (r < 0) + return r; + + LIST_FOREACH(fd_store, i, s->fd_store) { + _cleanup_free_ char *path = NULL; + struct stat st; + int flags; + + if (fstat(i->fd, &st) < 0) { + log_debug_errno(errno, "Failed to stat() file descriptor entry '%s', skipping.", strna(i->fdname)); + continue; + } + + flags = fcntl(i->fd, F_GETFL); + if (flags < 0) { + log_debug_errno(errno, "Failed to issue F_GETFL on file descriptor entry '%s', skipping.", strna(i->fdname)); + continue; + } + + /* glibc implies O_LARGEFILE everywhere on 64bit off_t builds, but forgets to hide it away on + * F_GETFL, but provides no definition to check for that. Let's mask the flag away manually, + * to not confuse clients. */ + flags &= ~RAW_O_LARGEFILE; + + (void) fd_get_path(i->fd, &path); + + r = sd_bus_message_append( + reply, + "(suuutuusu)", + i->fdname, + (uint32_t) st.st_mode, + (uint32_t) major(st.st_dev), (uint32_t) minor(st.st_dev), + (uint64_t) st.st_ino, + (uint32_t) major(st.st_rdev), (uint32_t) minor(st.st_rdev), + path, + (uint32_t) flags); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), @@ -292,6 +360,12 @@ const sd_bus_vtable bus_service_vtable[] = { bus_service_method_mount_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("DumpFileDescriptorStore", + SD_BUS_NO_ARGS, + SD_BUS_ARGS("a(suuutuusu)", entries), + bus_service_method_dump_file_descriptor_store, + SD_BUS_VTABLE_UNPRIVILEGED), + /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_ratelimit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index 9a05465802..aea6cf77f3 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -12,3 +12,4 @@ int bus_service_set_property(Unit *u, const char *name, sd_bus_message *i, UnitW int bus_service_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_service_method_mount_image(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_service_commit_properties(Unit *u); +int bus_service_method_dump_file_descriptor_store(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 0eaedec87c..df26fd75cd 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -32,6 +32,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH), SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH), SD_BUS_ERROR_MAP(BUS_ERROR_DISK_FULL, ENOSPC), + SD_BUS_ERROR_MAP(BUS_ERROR_FILE_DESCRIPTOR_STORE_DISABLED, + EHOSTDOWN), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index b6c2e93ea5..3a0eef49ef 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -32,6 +32,8 @@ #define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy" #define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive" #define BUS_ERROR_FREEZE_CANCELLED "org.freedesktop.systemd1.FreezeCancelled" +#define BUS_ERROR_FILE_DESCRIPTOR_STORE_DISABLED \ + "org.freedesktop.systemd1.FileDescriptorStoreDisabled" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" diff --git a/src/libsystemd/sd-device/device-util.c b/src/libsystemd/sd-device/device-util.c index 47c5d98896..529eff2fd1 100644 --- a/src/libsystemd/sd-device/device-util.c +++ b/src/libsystemd/sd-device/device-util.c @@ -15,7 +15,7 @@ int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) { assert(ret); - if (major(devnum) == 0 && minor(devnum) == 0) + if (devnum_is_zero(devnum)) return device_path_make_inaccessible(mode, ret); r = device_new_from_mode_and_devnum(&dev, mode, devnum); diff --git a/src/notify/notify.c b/src/notify/notify.c index 7320ffebde..8489b83873 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -11,6 +11,8 @@ #include "alloc-util.h" #include "build.h" #include "env-util.h" +#include "fd-util.h" +#include "fdset.h" #include "format-util.h" #include "log.h" #include "main-func.h" @@ -33,9 +35,13 @@ static gid_t arg_gid = GID_INVALID; static bool arg_no_block = false; static char **arg_env = NULL; static char **arg_exec = NULL; +static FDSet *arg_fds = NULL; +static char *arg_fdname = NULL; STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep); +STATIC_DESTRUCTOR_REGISTER(arg_fdname, freep); static int help(void) { _cleanup_free_ char *link = NULL; @@ -60,6 +66,8 @@ static int help(void) { " --booted Check if the system was booted up with systemd\n" " --no-block Do not wait until operation finished\n" " --exec Execute command line separated by ';' once done\n" + " --fd=FD Pass specified file descriptor with along with message\n" + " --fdname=NAME Name to assign to passed file descriptor(s)\n" "\nSee the %s for details.\n", program_invocation_short_name, program_invocation_short_name, @@ -103,6 +111,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_UID, ARG_NO_BLOCK, ARG_EXEC, + ARG_FD, + ARG_FDNAME, }; static const struct option options[] = { @@ -117,9 +127,12 @@ static int parse_argv(int argc, char *argv[]) { { "uid", required_argument, NULL, ARG_UID }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "exec", no_argument, NULL, ARG_EXEC }, + { "fd", required_argument, NULL, ARG_FD }, + { "fdname", required_argument, NULL, ARG_FDNAME }, {} }; + _cleanup_(fdset_freep) FDSet *passed = NULL; bool do_exec = false; int c, r, n_env; @@ -198,6 +211,60 @@ static int parse_argv(int argc, char *argv[]) { do_exec = true; break; + case ARG_FD: { + _cleanup_close_ int owned_fd = -EBADF; + int fdnr; + + r = safe_atoi(optarg, &fdnr); + if (r < 0) + return log_error_errno(r, "Failed to parse file descriptor: %s", optarg); + if (fdnr < 0) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "File descriptor can't be negative: %i", fdnr); + + if (!passed) { + /* Take possession of all passed fds */ + r = fdset_new_fill(&passed); + if (r < 0) + return log_error_errno(r, "Failed to take possession of passed file descriptors: %m"); + + r = fdset_cloexec(passed, true); + if (r < 0) + return log_error_errno(r, "Failed to enable O_CLOEXEC for passed file descriptors: %m"); + } + + if (fdnr < 3) { + /* For stdin/stdout/stderr we want to keep the fd, too, hence make a copy */ + owned_fd = fcntl(fdnr, F_DUPFD_CLOEXEC, 3); + if (owned_fd < 0) + return log_error_errno(errno, "Failed to duplicate file descriptor: %m"); + } else { + /* Otherwise, move the fd over */ + owned_fd = fdset_remove(passed, fdnr); + if (owned_fd < 0) + return log_error_errno(owned_fd, "Specified file descriptor '%i' not passed or specified more than once: %m", fdnr); + } + + if (!arg_fds) { + arg_fds = fdset_new(); + if (!arg_fds) + return log_oom(); + } + + r = fdset_consume(arg_fds, TAKE_FD(owned_fd)); + if (r < 0) + return log_error_errno(r, "Failed to add file descriptor to set: %m"); + break; + } + + case ARG_FDNAME: + if (!fdname_is_valid(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File descriptor name invalid: %s", optarg); + + if (free_and_strdup(&arg_fdname, optarg) < 0) + return log_oom(); + + break; + case '?': return -EINVAL; @@ -212,11 +279,15 @@ static int parse_argv(int argc, char *argv[]) { !arg_reloading && !arg_status && !arg_pid && - !arg_booted) { + !arg_booted && + fdset_isempty(arg_fds)) { help(); return -EINVAL; } + if (arg_fdname && fdset_isempty(arg_fds)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= set, refusing."); + if (do_exec) { int i; @@ -243,13 +314,16 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); } + if (!fdset_isempty(passed)) + log_warning("Warning: %u more file descriptors passed than referenced with --fd=.", fdset_size(passed)); + return 1; } static int run(int argc, char* argv[]) { - _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL, *monotonic_usec = NULL; + _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL, *monotonic_usec = NULL, *fdn = NULL; _cleanup_strv_free_ char **final_env = NULL; - char* our_env[7]; + char* our_env[9]; size_t i = 0; pid_t source_pid; int r; @@ -302,6 +376,18 @@ static int run(int argc, char* argv[]) { our_env[i++] = cpid; } + if (!fdset_isempty(arg_fds)) { + our_env[i++] = (char*) "FDSTORE=1"; + + if (arg_fdname) { + fdn = strjoin("FDNAME=", arg_fdname); + if (!fdn) + return log_oom(); + + our_env[i++] = fdn; + } + } + our_env[i++] = NULL; final_env = strv_env_merge(our_env, arg_env); @@ -338,13 +424,28 @@ static int run(int argc, char* argv[]) { * or the service manager itself */ source_pid = 0; } - r = sd_pid_notify(source_pid, false, n); + + if (fdset_isempty(arg_fds)) + r = sd_pid_notify(source_pid, /* unset_environment= */ false, n); + else { + _cleanup_free_ int *a = NULL; + int k; + + k = fdset_to_array(arg_fds, &a); + if (k < 0) + return log_error_errno(k, "Failed to convert file descriptor set to array: %m"); + + r = sd_pid_notify_with_fds(source_pid, /* unset_environment= */ false, n, a, k); + + } if (r < 0) return log_error_errno(r, "Failed to notify init system: %m"); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "No status data could be sent: $NOTIFY_SOCKET was not set"); + arg_fds = fdset_free(arg_fds); /* Close before we execute anything */ + if (!arg_no_block) { r = sd_notify_barrier(0, 5 * USEC_PER_SEC); if (r < 0) diff --git a/src/shared/fdset.c b/src/shared/fdset.c index cfa12a509e..d816a3e4ef 100644 --- a/src/shared/fdset.c +++ b/src/shared/fdset.c @@ -15,6 +15,7 @@ #include "parse-util.h" #include "path-util.h" #include "set.h" +#include "stat-util.h" #define MAKE_SET(s) ((Set*) s) #define MAKE_FDSET(s) ((FDSet*) s) @@ -23,27 +24,29 @@ FDSet *fdset_new(void) { return MAKE_FDSET(set_new(NULL)); } -int fdset_new_array(FDSet **ret, const int *fds, size_t n_fds) { - size_t i; - FDSet *s; +static inline void fdset_shallow_freep(FDSet **s) { + /* Destroys the set, but does not free the fds inside, like fdset_free()! */ + set_free(MAKE_SET(*ASSERT_PTR(s))); +} + +int fdset_new_array(FDSet **ret, const int fds[], size_t n_fds) { + _cleanup_(fdset_shallow_freep) FDSet *s = NULL; int r; assert(ret); + assert(fds || n_fds == 0); s = fdset_new(); if (!s) return -ENOMEM; - for (i = 0; i < n_fds; i++) { - + for (size_t i = 0; i < n_fds; i++) { r = fdset_put(s, fds[i]); - if (r < 0) { - set_free(MAKE_SET(s)); + if (r < 0) return r; - } } - *ret = s; + *ret = TAKE_PTR(s); return 0; } @@ -77,8 +80,22 @@ int fdset_put(FDSet *s, int fd) { return set_put(MAKE_SET(s), FD_TO_PTR(fd)); } +int fdset_consume(FDSet *s, int fd) { + int r; + + assert(s); + assert(fd >= 0); + + r = fdset_put(s, fd); + if (r < 0) + safe_close(fd); + + return r; +} + int fdset_put_dup(FDSet *s, int fd) { - int copy, r; + _cleanup_close_ int copy = -EBADF; + int r; assert(s); assert(fd >= 0); @@ -88,12 +105,10 @@ int fdset_put_dup(FDSet *s, int fd) { return -errno; r = fdset_put(s, copy); - if (r < 0) { - safe_close(copy); + if (r < 0) return r; - } - return copy; + return TAKE_FD(copy); } bool fdset_contains(FDSet *s, int fd) { @@ -110,53 +125,46 @@ int fdset_remove(FDSet *s, int fd) { return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT; } -int fdset_new_fill(FDSet **_s) { +int fdset_new_fill(FDSet **ret) { + _cleanup_(fdset_shallow_freep) FDSet *s = NULL; _cleanup_closedir_ DIR *d = NULL; - int r = 0; - FDSet *s; + int r; - assert(_s); + assert(ret); - /* Creates an fdset and fills in all currently open file - * descriptors. */ + /* Creates an fdset and fills in all currently open file descriptors. */ d = opendir("/proc/self/fd"); - if (!d) + if (!d) { + if (errno == ENOENT && proc_mounted() == 0) + return -ENOSYS; + return -errno; + } s = fdset_new(); - if (!s) { - r = -ENOMEM; - goto finish; - } + if (!s) + return -ENOMEM; FOREACH_DIRENT(de, d, return -errno) { int fd = -EBADF; r = safe_atoi(de->d_name, &fd); if (r < 0) - goto finish; + return r; if (fd < 3) continue; - if (fd == dirfd(d)) continue; r = fdset_put(s, fd); if (r < 0) - goto finish; + return r; } - r = 0; - *_s = TAKE_PTR(s); - -finish: - /* We won't close the fds here! */ - if (s) - set_free(MAKE_SET(s)); - - return r; + *ret = TAKE_PTR(s); + return 0; } int fdset_cloexec(FDSet *fds, bool b) { @@ -174,53 +182,66 @@ int fdset_cloexec(FDSet *fds, bool b) { return 0; } -int fdset_new_listen_fds(FDSet **_s, bool unset) { +int fdset_new_listen_fds(FDSet **ret, bool unset) { + _cleanup_(fdset_shallow_freep) FDSet *s = NULL; int n, fd, r; - FDSet *s; - assert(_s); + assert(ret); /* Creates an fdset and fills in all passed file descriptors */ s = fdset_new(); - if (!s) { - r = -ENOMEM; - goto fail; - } + if (!s) + return -ENOMEM; n = sd_listen_fds(unset); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { r = fdset_put(s, fd); if (r < 0) - goto fail; + return r; } - *_s = s; + *ret = TAKE_PTR(s); return 0; - -fail: - if (s) - set_free(MAKE_SET(s)); - - return r; } -int fdset_close_others(FDSet *fds) { +int fdset_to_array(FDSet *fds, int **ret) { + unsigned j = 0, m; void *e; - int *a = NULL; - size_t j = 0, m; + int *a; - m = fdset_size(fds); + assert(ret); - if (m > 0) { - a = newa(int, m); - SET_FOREACH(e, MAKE_SET(fds)) - a[j++] = PTR_TO_FD(e); + m = fdset_size(fds); + if (m > INT_MAX) /* We want to be able to return an "int" */ + return -ENOMEM; + if (m == 0) { + *ret = NULL; /* suppress array allocation if empty */ + return 0; } + a = new(int, m); + if (!a) + return -ENOMEM; + + SET_FOREACH(e, MAKE_SET(fds)) + a[j++] = PTR_TO_FD(e); + assert(j == m); - return close_all_fds(a, j); + *ret = TAKE_PTR(a); + return (int) m; +} + +int fdset_close_others(FDSet *fds) { + _cleanup_free_ int *a = NULL; + int n; + + n = fdset_to_array(fds, &a); + if (n < 0) + return n; + + return close_all_fds(a, n); } unsigned fdset_size(FDSet *fds) { diff --git a/src/shared/fdset.h b/src/shared/fdset.h index 39d15ee4aa..9700cdbbca 100644 --- a/src/shared/fdset.h +++ b/src/shared/fdset.h @@ -13,6 +13,7 @@ FDSet* fdset_new(void); FDSet* fdset_free(FDSet *s); int fdset_put(FDSet *s, int fd); +int fdset_consume(FDSet *s, int fd); int fdset_put_dup(FDSet *s, int fd); bool fdset_contains(FDSet *s, int fd); @@ -24,6 +25,8 @@ int fdset_new_listen_fds(FDSet **ret, bool unset); int fdset_cloexec(FDSet *fds, bool b); +int fdset_to_array(FDSet *fds, int **ret); + int fdset_close_others(FDSet *fds); unsigned fdset_size(FDSet *fds); diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 351a5ede11..204e8b68b6 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -7,6 +7,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "devnum-util.h" #include "fd-util.h" #include "fileio.h" #include "format-table.h" @@ -24,6 +25,7 @@ #include "process-util.h" #include "signal-util.h" #include "sort-util.h" +#include "stat-util.h" #include "string-util.h" #include "strxcpyx.h" #include "terminal-util.h" @@ -107,6 +109,7 @@ typedef struct TableData { gid_t gid; pid_t pid; mode_t mode; + dev_t devnum; /* … add more here as we start supporting more cell data types … */ }; } TableData; @@ -348,8 +351,12 @@ static size_t table_data_size(TableDataType type, const void *data) { return sizeof(pid_t); case TABLE_MODE: + case TABLE_MODE_INODE_TYPE: return sizeof(mode_t); + case TABLE_DEVNUM: + return sizeof(dev_t); + default: assert_not_reached(); } @@ -867,6 +874,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { gid_t gid; pid_t pid; mode_t mode; + dev_t devnum; } buffer; switch (type) { @@ -1022,10 +1030,16 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { break; case TABLE_MODE: + case TABLE_MODE_INODE_TYPE: buffer.mode = va_arg(ap, mode_t); data = &buffer.mode; break; + case TABLE_DEVNUM: + buffer.devnum = va_arg(ap, dev_t); + data = &buffer.devnum; + break; + case TABLE_SET_MINIMUM_WIDTH: { size_t w = va_arg(ap, size_t); @@ -1273,6 +1287,8 @@ int table_hide_column_from_display_internal(Table *t, ...) { } static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t index_b) { + int r; + assert(a); assert(b); @@ -1377,8 +1393,16 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t return CMP(a->pid, b->pid); case TABLE_MODE: + case TABLE_MODE_INODE_TYPE: return CMP(a->mode, b->mode); + case TABLE_DEVNUM: + r = CMP(major(a->devnum), major(b->devnum)); + if (r != 0) + return r; + + return CMP(minor(a->devnum), minor(b->devnum)); + default: ; } @@ -1882,6 +1906,22 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas break; } + case TABLE_MODE_INODE_TYPE: + + if (d->mode == MODE_INVALID) + return table_ersatz_string(t); + + return inode_type_to_string(d->mode); + + case TABLE_DEVNUM: + if (devnum_is_zero(d->devnum)) + return table_ersatz_string(t); + + if (asprintf(&d->formatted, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d->devnum)) < 0) + return NULL; + + break; + default: assert_not_reached(); } @@ -2696,11 +2736,20 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { return json_variant_new_integer(ret, d->int_val); case TABLE_MODE: + case TABLE_MODE_INODE_TYPE: if (d->mode == MODE_INVALID) return json_variant_new_null(ret); return json_variant_new_unsigned(ret, d->mode); + case TABLE_DEVNUM: + if (devnum_is_zero(d->devnum)) + return json_variant_new_null(ret); + + return json_build(ret, JSON_BUILD_ARRAY( + JSON_BUILD_UNSIGNED(major(d->devnum)), + JSON_BUILD_UNSIGNED(minor(d->devnum)))); + default: return -EINVAL; } diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 5a2b366b59..148418c70f 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -51,7 +51,9 @@ typedef enum TableDataType { TABLE_GID, TABLE_PID, TABLE_SIGNAL, - TABLE_MODE, /* as in UNIX file mode (mode_t), in typical octal output */ + TABLE_MODE, /* as in UNIX file mode (mode_t), in typical octal output */ + TABLE_MODE_INODE_TYPE, /* also mode_t, but displays only the inode type as string */ + TABLE_DEVNUM, /* a dev_t, displayed in the usual major:minor way */ _TABLE_DATA_TYPE_MAX, /* The following are not really data types, but commands for table_add_cell_many() to make changes to diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c index d19e7561f8..9a4a8adbc3 100644 --- a/src/udev/udevadm-lock.c +++ b/src/udev/udevadm-lock.c @@ -334,11 +334,9 @@ int lock_main(int argc, char *argv[], void *userdata) { if (fd < 0) return fd; - r = fdset_put(fds, fd); + r = fdset_consume(fds, TAKE_FD(fd)); if (r < 0) return log_oom(); - - TAKE_FD(fd); } } |