Merge pull request #27041 from poettering/fdstore-dump
add ability to show contents of service fdstore + teach systemd-notify passing fds into the fdstore
This commit is contained in:
@@ -136,10 +136,6 @@ Features:
|
||||
always end in a colon. This requires updating our udev rules, as well as
|
||||
checking if the various hwdb files are fine with that.
|
||||
|
||||
* Add a bus API to enumerate contents of the fdstore of a service,
|
||||
handle/display similar to querying the process tree. Should probably just an
|
||||
array of inode/devnum of fd, plus fd_get_name() data
|
||||
|
||||
* mount /tmp/ and /var/tmp with a uidmap applied that blocks out "nobody" user
|
||||
among other things such as dynamic uid ranges for containers and so on. That
|
||||
way noone can create files there with these uids and we enforce they are only
|
||||
|
||||
@@ -273,6 +273,8 @@ node /org/freedesktop/systemd1 {
|
||||
LookupDynamicUserByUID(in u uid,
|
||||
out s name);
|
||||
GetDynamicUsers(out a(us) users);
|
||||
DumpUnitFileDescriptorStore(in s name,
|
||||
out a(suuutuusu) entries);
|
||||
signals:
|
||||
UnitNew(s id,
|
||||
o unit);
|
||||
@@ -974,6 +976,8 @@ node /org/freedesktop/systemd1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetDynamicUsers()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="DumpUnitFileDescriptorStore()"/>
|
||||
|
||||
<variablelist class="dbus-signal" generated="True" extra-ref="UnitNew"/>
|
||||
|
||||
<variablelist class="dbus-signal" generated="True" extra-ref="UnitRemoved"/>
|
||||
@@ -1531,6 +1535,11 @@ node /org/freedesktop/systemd1 {
|
||||
<ulink url="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface">New Control Group
|
||||
Interface</ulink> for more information how to make use of this functionality for resource control
|
||||
purposes.</para>
|
||||
|
||||
<para><function>DumpUnitFileDescriptorStore()</function> returns an array with information about the
|
||||
file descriptors currently in the file descriptor store of the specified unit. This call is equivalent
|
||||
to <function>DumpFileDescriptorStore()</function> on the
|
||||
<interfacename>org.freedesktop.systemd1.Service</interfacename>. For further details, see below.</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
@@ -2548,6 +2557,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
in b read_only,
|
||||
in b mkdir,
|
||||
in a(ss) options);
|
||||
DumpFileDescriptorStore(out a(suuutuusu) entries);
|
||||
GetProcesses(out a(sus) processes);
|
||||
AttachProcesses(in s subcgroup,
|
||||
in au pids);
|
||||
@@ -3742,6 +3752,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="MountImage()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="DumpFileDescriptorStore()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
|
||||
@@ -4393,6 +4405,16 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
directly on the Manager object has the advantage of not requiring a <function>GetUnit()</function> call
|
||||
to get the unit object for a specific unit name. Calling the methods on the Manager object is hence a round
|
||||
trip optimization.</para>
|
||||
|
||||
<para><function>DumpFileDescriptorStore()</function> returns an array with information about the file
|
||||
descriptors currently in the file descriptor store of the service. Each entry consists of a file
|
||||
descriptor name (i.e. the <varname>FDNAME=</varname> field), the file descriptor inode type and access
|
||||
mode as integer (i.e. a <type>mode_t</type> value, flags such as <constant>S_IFREG</constant>,
|
||||
<constant>S_IRUSR</constant>, …), the major and minor numbers of the device number of the file system
|
||||
backing the inode of the file descriptor, the inode number, the major and minor numbers of the device
|
||||
number if this refers to a character or block device node, a file system path pointing to the inode,
|
||||
and the file descriptor flags (i.e. <constant>O_RDWR</constant>, <constant>O_RDONLY</constant>,
|
||||
…).</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
|
||||
+36
-1
@@ -156,6 +156,12 @@
|
||||
<arg choice="plain">malloc</arg>
|
||||
<arg choice="opt" rep="repeat"><replaceable>D-BUS SERVICE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">fdstore</arg>
|
||||
<arg choice="opt" rep="repeat"><replaceable>UNIT</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@@ -803,8 +809,37 @@ $ systemd-analyze verify /tmp/source:alias.service
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze fdstore <optional><replaceable>UNIT</replaceable>...</optional></command></title>
|
||||
|
||||
<para>Lists the current contents of the specified service unit's file descriptor store. This shows
|
||||
names, inode types, device numbers, inode numbers, paths and open modes of the open file
|
||||
descriptors. The specified units must have <varname>FileDescriptorStoreMax=</varname> enabled, see
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
|
||||
<example>
|
||||
<title>Table output</title>
|
||||
<programlisting>$ systemd-analyze fdstore systemd-journald.service
|
||||
FDNAME TYPE DEVNO INODE RDEVNO PATH FLAGS
|
||||
stored sock 0:8 4218620 - socket:[4218620] ro
|
||||
stored sock 0:8 4213198 - socket:[4213198] ro
|
||||
stored sock 0:8 4213190 - socket:[4213190] ro
|
||||
…</programlisting>
|
||||
</example>
|
||||
|
||||
<para>Note: the "DEVNO" column refers to the major/minor numbers of the device node backing the file
|
||||
system the file descriptor's inode is on. The "RDEVNO" column refers to the major/minor numbers of the
|
||||
device node itself if the file descriptor refers to one. Compare with corresponding
|
||||
<varname>.st_dev</varname> and <varname>.st_rdev</varname> fields in <type>struct stat</type> (see
|
||||
<citerefentry
|
||||
project='man-pages'><refentrytitle>stat</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
|
||||
details). The listed inode numbers in the "INODE" column are on the file system indicated by
|
||||
"DEVNO".</para>
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@@ -191,6 +191,29 @@
|
||||
escaped as <literal>\;</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--fd=</option></term>
|
||||
|
||||
<listitem><para>Send a file descriptor along with the notification message. This is useful when
|
||||
invoked in services that have the <varname>FileDescriptorStoreMax=</varname> setting enabled, see
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details. The specified file descriptor must be passed to <command>systemd-notify</command> when
|
||||
invoked. This option may be used multiple times to pass multiple file descriptors in a single
|
||||
notification message.</para>
|
||||
|
||||
<para>To use this functionality from a <command>bash</command> shell, use an expression like the following:</para>
|
||||
<programlisting>systemd-notify --fd=4 --fd=5 4</some/file 5</some/other/file</programlisting></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--fdname=</option></term>
|
||||
|
||||
<listitem><para>Set a name to assign to the file descriptors passed via <option>--fd=</option> (see
|
||||
above). This controls the <literal>FDNAME=</literal> field. This setting may only be specified once,
|
||||
and applies to all file descriptors passed. Invoke this tool multiple times in case multiple file
|
||||
descriptors with different file descriptor names shall be submitted.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
+12
-1
@@ -1141,7 +1141,18 @@
|
||||
fully stopped and no job is queued or being executed for it. If this option is used,
|
||||
<varname>NotifyAccess=</varname> (see above) should be set to open access to the notification socket
|
||||
provided by systemd. If <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
|
||||
<option>main</option>.</para></listitem>
|
||||
<option>main</option>.</para>
|
||||
|
||||
<para>The <command>fdstore</command> command of
|
||||
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
may be used to list the current contents of a service's file descriptor store.</para>
|
||||
|
||||
<para>Note that the service manager will only pass file descriptors contained in the file descriptor
|
||||
store to the service's own processes, never to other clients via IPC or similar. However, it does
|
||||
allow unprivileged clients to query the list of currently open file descriptors of a
|
||||
service. Sensitive data may hence be safely placed inside the referenced files, but should not be
|
||||
attached to the metadata (e.g. included in filenames) of the stored file
|
||||
descriptors.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
+105
-4
@@ -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)
|
||||
|
||||
+82
-61
@@ -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;
|
||||
|
||||
assert(ret);
|
||||
|
||||
m = fdset_size(fds);
|
||||
|
||||
if (m > 0) {
|
||||
a = newa(int, m);
|
||||
SET_FOREACH(e, MAKE_SET(fds))
|
||||
a[j++] = PTR_TO_FD(e);
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,5 +48,32 @@ assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
|
||||
|
||||
rm /tmp/syncfifo1 /tmp/syncfifo2
|
||||
|
||||
MYSCRIPT="/tmp/myscript$RANDOM.sh"
|
||||
cat >> "$MYSCRIPT" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
set -o pipefail
|
||||
N="/tmp/$RANDOM"
|
||||
echo $RANDOM > "$N"
|
||||
systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N"
|
||||
rm "$N"
|
||||
systemd-notify --ready
|
||||
exec sleep infinity
|
||||
EOF
|
||||
|
||||
chmod +x "$MYSCRIPT"
|
||||
|
||||
MYUNIT="myunit$RANDOM.service"
|
||||
systemd-run -u "$MYUNIT" -p Type=notify -p StandardOutput=journal+console -p StandardError=journal+console -p FileDescriptorStoreMax=7 "$MYSCRIPT"
|
||||
|
||||
test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2
|
||||
systemd-analyze fdstore "$MYUNIT" --json=short
|
||||
systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]'
|
||||
|
||||
systemctl stop "$MYUNIT"
|
||||
rm "$MYSCRIPT"
|
||||
|
||||
touch /testok
|
||||
rm /failed
|
||||
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user