summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-03-30 09:32:13 +0200
committerGitHub <noreply@github.com>2023-03-30 09:32:13 +0200
commit1b07fd3c228fcbbe71653e378c696cf6a8323a32 (patch)
tree1bcb9b4f0a0aac3fc31805666a5a7a9a02ab2e8b /src
parent0362953e9a5e472b5cd66687e79a957b37e35e09 (diff)
parent3739c2fdfc9d01cd5dd5f349fdc5cea1561517f1 (diff)
downloadsystemd-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.c110
-rw-r--r--src/analyze/analyze-fdstore.h5
-rw-r--r--src/analyze/analyze.c9
-rw-r--r--src/analyze/meson.build1
-rw-r--r--src/basic/devnum-util.c2
-rw-r--r--src/basic/devnum-util.h4
-rw-r--r--src/basic/fd-util.c13
-rw-r--r--src/basic/fd-util.h2
-rw-r--r--src/basic/missing_fcntl.h9
-rw-r--r--src/basic/stat-util.c23
-rw-r--r--src/basic/stat-util.h2
-rw-r--r--src/basic/terminal-util.c2
-rw-r--r--src/core/dbus-manager.c9
-rw-r--r--src/core/dbus-service.c74
-rw-r--r--src/core/dbus-service.h1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c2
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h2
-rw-r--r--src/libsystemd/sd-device/device-util.c2
-rw-r--r--src/notify/notify.c109
-rw-r--r--src/shared/fdset.c143
-rw-r--r--src/shared/fdset.h3
-rw-r--r--src/shared/format-table.c49
-rw-r--r--src/shared/format-table.h4
-rw-r--r--src/udev/udevadm-lock.c4
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);
}
}