summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile-man.am15
-rw-r--r--Makefile.am7
-rw-r--r--NEWS12
-rw-r--r--man/systemd-rfkill.service.xml (renamed from man/systemd-rfkill@.service.xml)18
-rw-r--r--man/systemd.service.xml24
-rw-r--r--man/systemd.socket.xml25
-rw-r--r--rules/99-systemd.rules.in3
-rw-r--r--src/core/dbus-socket.c1
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/socket.c8
-rw-r--r--src/core/socket.h1
-rw-r--r--src/rfkill/rfkill.c432
-rw-r--r--src/systemctl/systemctl.c8
-rw-r--r--units/.gitignore2
-rw-r--r--units/systemd-rfkill.service.in (renamed from units/systemd-rfkill@.service.in)16
-rw-r--r--units/systemd-rfkill.socket19
16 files changed, 464 insertions, 128 deletions
diff --git a/Makefile-man.am b/Makefile-man.am
index 49586fe04c..14c2b7d57e 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -1807,11 +1807,16 @@ endif
if ENABLE_RFKILL
MANPAGES += \
- man/systemd-rfkill@.service.8
+ man/systemd-rfkill.service.8
MANPAGES_ALIAS += \
- man/systemd-rfkill.8
-man/systemd-rfkill.8: man/systemd-rfkill@.service.8
-man/systemd-rfkill.html: man/systemd-rfkill@.service.html
+ man/systemd-rfkill.8 \
+ man/systemd-rfkill.socket.8
+man/systemd-rfkill.8: man/systemd-rfkill.service.8
+man/systemd-rfkill.socket.8: man/systemd-rfkill.service.8
+man/systemd-rfkill.html: man/systemd-rfkill.service.html
+ $(html-alias)
+
+man/systemd-rfkill.socket.html: man/systemd-rfkill.service.html
$(html-alias)
endif
@@ -2362,7 +2367,7 @@ EXTRA_DIST += \
man/systemd-random-seed.service.xml \
man/systemd-remount-fs.service.xml \
man/systemd-resolved.service.xml \
- man/systemd-rfkill@.service.xml \
+ man/systemd-rfkill.service.xml \
man/systemd-run.xml \
man/systemd-sleep.conf.xml \
man/systemd-socket-proxyd.xml \
diff --git a/Makefile.am b/Makefile.am
index 4ea66cf813..30989bfe8e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4463,7 +4463,10 @@ rootlibexec_PROGRAMS += \
systemd-rfkill
nodist_systemunit_DATA += \
- units/systemd-rfkill@.service
+ units/systemd-rfkill.service
+
+dist_systemunit_DATA += \
+ units/systemd-rfkill.socket
systemd_rfkill_SOURCES = \
src/rfkill/rfkill.c
@@ -4473,7 +4476,7 @@ systemd_rfkill_LDADD = \
endif
EXTRA_DIST += \
- units/systemd-rfkill@.service.in
+ units/systemd-rfkill.service.in
# ------------------------------------------------------------------------------
if HAVE_LIBCRYPTSETUP
diff --git a/NEWS b/NEWS
index 4edc17a1e7..249c5f6a64 100644
--- a/NEWS
+++ b/NEWS
@@ -126,6 +126,18 @@ CHANGES WITH 227:
file is now also downloaded, if it is available and stored
next to the image file.
+ * Units of type ".socket" gained a new boolean setting
+ Writable= which is only useful in conjunction with
+ ListenSpecial=. If true, enables opening the specified
+ special file in O_RDWR mode rather than O_RDONLY mode.
+
+ * systemd-rfkill has been reworked to become a singleton
+ service that is activated through /dev/rfkill on each rfkill
+ state change and saves the settings to disk. This way,
+ systemd-rfkill is now compatible with devices that exist
+ only intermittendly, and even restores state if the previous
+ system shutdown was abrupt rather than clean.
+
* Galician, Turkish and Korean translations were added.
Contributions from:
diff --git a/man/systemd-rfkill@.service.xml b/man/systemd-rfkill.service.xml
index 709b09d818..f464842700 100644
--- a/man/systemd-rfkill@.service.xml
+++ b/man/systemd-rfkill.service.xml
@@ -19,10 +19,10 @@
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
-<refentry id="systemd-rfkill@.service" conditional='ENABLE_RFKILL'>
+<refentry id="systemd-rfkill.service" conditional='ENABLE_RFKILL'>
<refentryinfo>
- <title>systemd-rfkill@.service</title>
+ <title>systemd-rfkill.service</title>
<productname>systemd</productname>
<authorgroup>
@@ -36,27 +36,29 @@
</refentryinfo>
<refmeta>
- <refentrytitle>systemd-rfkill@.service</refentrytitle>
+ <refentrytitle>systemd-rfkill.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
- <refname>systemd-rfkill@.service</refname>
+ <refname>systemd-rfkill.service</refname>
+ <refname>systemd-rfkill.socket</refname>
<refname>systemd-rfkill</refname>
- <refpurpose>Load and save the RF kill switch state at boot and shutdown</refpurpose>
+ <refpurpose>Load and save the RF kill switch state at boot and change</refpurpose>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-rfkill@.service</filename></para>
+ <para><filename>systemd-rfkill.service</filename></para>
+ <para><filename>systemd-rfkill.socket</filename></para>
<para><filename>/usr/lib/systemd/systemd-rfkill</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><filename>systemd-rfkill@.service</filename> is a service
+ <para><filename>systemd-rfkill.service</filename> is a service
that restores the RF kill switch state at early boot and saves it
- at shutdown. On disk, the RF kill switch state is stored in
+ on each change. On disk, the RF kill switch state is stored in
<filename>/var/lib/systemd/rfkill/</filename>.</para>
</refsect1>
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index eeb98198ab..897ea464d9 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -905,21 +905,23 @@
<varlistentry>
<term><varname>USBFunctionDescriptors=</varname></term>
- <listitem><para>Configure the location of the file containing
- <ulink url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">functionfs</ulink>
- descriptors. This is is used only when a socket with
- <varname>ListenUSBFunction=</varname> configured wants to
- activate this service. Content of this file is writen to
- <filename>ep0</filename> file after it is opened. This is
- required for socket activation using
- <varname>ListenUSBFunction=</varname> (i.e. for passing all
- functionfs endpoints to the service).</para></listitem>
+ <listitem><para>Configure the location of a file containing
+ <ulink
+ url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB
+ FunctionFS</ulink> descriptors, for implementation of USB
+ gadget functions. This is is used only in conjunction with a
+ socket unit with <varname>ListenUSBFunction=</varname>
+ configured. The contents of this file is written to the
+ <filename>ep0</filename> file after it is
+ opened.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>USBFunctionStrings=</varname></term>
- <listitem><para>Configure the location of file containing FunctionFS strings.
- Behavior is similar to <varname>USBFunctionDescriptors</varname>.</para></listitem>
+ <listitem><para>Configure the location of a file containing
+ USB FunctionFS strings. Behavior is similar to
+ <varname>USBFunctionDescriptors=</varname>
+ above.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index bb75b69cea..212764075d 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -262,14 +262,15 @@
<varlistentry>
<term><varname>ListenUSBFunction=</varname></term>
- <listitem><para>Specifies a
- <ulink url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">functionfs</ulink>
- endpoint location to
- listen on. This expects an absolute file system path as the
- argument. Behavior otherwise is very similar to the
- <varname>ListenFIFO=</varname> directive above. Use this to
- open functionfs endpoint <filename>ep0</filename>. When using
- this option, activated service has to have the
+ <listitem><para>Specifies a <ulink
+ url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB
+ FunctionFS</ulink> endpoint location to listen on, for
+ implementation of USB gadget functions. This expects an
+ absolute file system path as the argument. Behavior otherwise
+ is very similar to the <varname>ListenFIFO=</varname>
+ directive above. Use this to open FunctionFS endpoint
+ <filename>ep0</filename>. When using this option, the
+ activated service has to have the
<varname>USBFunctionDescriptors=</varname> and
<varname>USBFunctionStrings=</varname> options set.
</para></listitem>
@@ -381,6 +382,14 @@
</varlistentry>
<varlistentry>
+ <term><varname>Writable=</varname></term>
+ <listitem><para>Takes a boolean argument. May only be used in
+ conjunction with <varname>ListenSpecial=</varname>. If true,
+ the specified special file is opened in read-write mode, if
+ false in read-only mode. Defaults to false.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>MaxConnections=</varname></term>
<listitem><para>The maximum number of connections to
simultaneously run services instances for, when
diff --git a/rules/99-systemd.rules.in b/rules/99-systemd.rules.in
index 10b90b8133..5c2cda51ec 100644
--- a/rules/99-systemd.rules.in
+++ b/rules/99-systemd.rules.in
@@ -57,7 +57,8 @@ SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="pa
# Pull in rfkill save/restore for all rfkill devices
-SUBSYSTEM=="rfkill", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/rfkill/devices/%k", ENV{SYSTEMD_WANTS}+="systemd-rfkill@$name.service"
+SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id"
+SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket"
# Asynchronously mount file systems implemented by these modules as soon as they are loaded.
SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 86732e2a45..4611ad5f86 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -95,6 +95,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_interval), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 27159aa4c7..2333926e7d 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -261,6 +261,7 @@ Socket.SocketGroup, config_parse_unit_string_printf, 0,
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
+Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)
Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time)
diff --git a/src/core/socket.c b/src/core/socket.c
index a0b5bba1b9..3250e7efc6 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -507,6 +507,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sPassSecurity: %s\n"
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
+ "%sWritable: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
@@ -523,6 +524,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(s->pass_sec),
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
+ prefix, yes_no(s->writable),
prefix, yes_no(s->selinux_context_from_net));
if (s->control_pid > 0)
@@ -1031,14 +1033,14 @@ fail:
return r;
}
-static int special_address_create(const char *path) {
+static int special_address_create(const char *path, bool writable) {
_cleanup_close_ int fd = -1;
struct stat st;
int r;
assert(path);
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+ fd = open(path, (writable ? O_RDWR : O_RDONLY)|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
if (fd < 0)
return -errno;
@@ -1280,7 +1282,7 @@ static int socket_open_fds(Socket *s) {
case SOCKET_SPECIAL:
- p->fd = special_address_create(p->path);
+ p->fd = special_address_create(p->path, s->writable);
if (p->fd < 0) {
r = p->fd;
goto rollback;
diff --git a/src/core/socket.h b/src/core/socket.h
index aee0d34a27..d20dc8d81a 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -118,6 +118,7 @@ struct Socket {
bool accept;
bool remove_on_stop;
+ bool writable;
/* Socket options */
bool keep_alive;
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index 904dec6bfc..d8a2f3694e 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -19,124 +19,402 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "mkdir.h"
-#include "fileio.h"
+#include <linux/rfkill.h>
+#include <poll.h>
+
#include "libudev.h"
+#include "sd-daemon.h"
+
+#include "fileio.h"
+#include "mkdir.h"
#include "udev-util.h"
+#include "util.h"
-int main(int argc, char *argv[]) {
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_udev_device_unref_ struct udev_device *device = NULL;
- _cleanup_free_ char *saved = NULL, *escaped_type = NULL, *escaped_path_id = NULL;
- const char *name, *type, *path_id;
- int r;
+#define EXIT_USEC (5 * USEC_PER_SEC)
- if (argc != 3) {
- log_error("This program requires two arguments.");
- return EXIT_FAILURE;
- }
+static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
+ [RFKILL_TYPE_ALL] = "all",
+ [RFKILL_TYPE_WLAN] = "wlan",
+ [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
+ [RFKILL_TYPE_UWB] = "uwb",
+ [RFKILL_TYPE_WIMAX] = "wimax",
+ [RFKILL_TYPE_WWAN] = "wwan",
+ [RFKILL_TYPE_GPS] = "gps",
+ [RFKILL_TYPE_FM] "fm",
+ [RFKILL_TYPE_NFC] "nfc",
+};
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
- umask(0022);
+static int find_device(
+ struct udev *udev,
+ const struct rfkill_event *event,
+ struct udev_device **ret) {
- r = mkdir_p("/var/lib/systemd/rfkill", 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create rfkill directory: %m");
- return EXIT_FAILURE;
- }
+ _cleanup_free_ char *sysname = NULL;
+ struct udev_device *device;
+ const char *name;
- udev = udev_new();
- if (!udev) {
- log_oom();
- return EXIT_FAILURE;
- }
+ assert(udev);
+ assert(event);
+ assert(ret);
- device = udev_device_new_from_subsystem_sysname(udev, "rfkill", argv[2]);
- if (!device) {
- log_debug_errno(errno, "Failed to get rfkill device '%s', ignoring: %m", argv[2]);
- return EXIT_SUCCESS;
- }
+ if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
+ return log_oom();
+
+ device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
+ if (!device)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
name = udev_device_get_sysattr_value(device, "name");
if (!name) {
- log_error("rfkill device has no name? Ignoring device.");
- return EXIT_SUCCESS;
+ log_debug("Device has no name, ignoring.");
+ udev_device_unref(device);
+ return -ENOENT;
}
log_debug("Operating on rfkill device '%s'.", name);
- type = udev_device_get_sysattr_value(device, "type");
- if (!type) {
- log_error("rfkill device has no type? Ignoring device.");
- return EXIT_SUCCESS;
+ *ret = device;
+ return 0;
+}
+
+static int wait_for_initialized(
+ struct udev *udev,
+ struct udev_device *device,
+ struct udev_device **ret) {
+
+ _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
+ struct udev_device *d;
+ const char *sysname;
+ int watch_fd, r;
+
+ assert(udev);
+ assert(device);
+ assert(ret);
+
+ if (udev_device_get_is_initialized(device) != 0) {
+ *ret = udev_device_ref(device);
+ return 0;
}
- escaped_type = cescape(type);
- if (!escaped_type) {
- log_oom();
- return EXIT_FAILURE;
+ assert_se(sysname = udev_device_get_sysname(device));
+
+ /* Wait until the device is initialized, so that we can get
+ * access to the ID_PATH property */
+
+ monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!monitor)
+ return log_error_errno(errno, "Failed to acquire monitor: %m");
+
+ r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
+
+ r = udev_monitor_enable_receiving(monitor);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable udev receiving: %m");
+
+ watch_fd = udev_monitor_get_fd(monitor);
+ if (watch_fd < 0)
+ return log_error_errno(watch_fd, "Failed to get watch fd: %m");
+
+ /* Check again, maybe things changed */
+ d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
+ if (!d)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
+
+ if (udev_device_get_is_initialized(d) != 0) {
+ *ret = d;
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_udev_device_unref_ struct udev_device *t = NULL;
+
+ r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
+ if (r == -EINTR)
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch udev monitor: %m");
+
+ t = udev_monitor_receive_device(monitor);
+ if (!t)
+ continue;
+
+ if (streq_ptr(udev_device_get_sysname(device), sysname)) {
+ *ret = udev_device_ref(t);
+ return 0;
+ }
}
+}
+
+static int determine_state_file(
+ struct udev *udev,
+ const struct rfkill_event *event,
+ struct udev_device *d,
+ char **ret) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ const char *path_id, *type;
+ char *state_file;
+ int r;
+
+ assert(event);
+ assert(d);
+ assert(ret);
+
+ r = wait_for_initialized(udev, d, &device);
+ if (r < 0)
+ return r;
+
+ assert_se(type = rfkill_type_to_string(event->type));
path_id = udev_device_get_property_value(device, "ID_PATH");
if (path_id) {
+ _cleanup_free_ char *escaped_path_id = NULL;
+
escaped_path_id = cescape(path_id);
- if (!escaped_path_id) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!escaped_path_id)
+ return log_oom();
- saved = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", escaped_type, NULL);
+ state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
} else
- saved = strjoin("/var/lib/systemd/rfkill/", escaped_type, NULL);
+ state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL);
+
+ if (!state_file)
+ return log_oom();
+
+ *ret = state_file;
+ return 0;
+}
+
+static int load_state(
+ int rfkill_fd,
+ struct udev *udev,
+ const struct rfkill_event *event) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ _cleanup_free_ char *state_file = NULL, *value = NULL;
+ struct rfkill_event we;
+ ssize_t l;
+ int b, r;
- if (!saved) {
- log_oom();
+ assert(rfkill_fd >= 0);
+ assert(udev);
+ assert(event);
+
+ if (!shall_restore_state())
+ return 0;
+
+ r = find_device(udev, event, &device);
+ if (r < 0)
+ return r;
+
+ r = determine_state_file(udev, event, device, &state_file);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(state_file, &value);
+ if (r == -ENOENT) {
+ /* No state file? Then save the current state */
+
+ r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write state file %s: %m", state_file);
+
+ log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read state file %s: %m", state_file);
+
+ b = parse_boolean(value);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
+
+ we = (struct rfkill_event) {
+ .op = RFKILL_OP_CHANGE,
+ .idx = event->idx,
+ .soft = b,
+ };
+
+ l = write(rfkill_fd, &we, sizeof(we));
+ if (l < 0)
+ return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
+ if (l != sizeof(we)) {
+ log_error("Couldn't write rfkill event structure, too short.");
+ return -EIO;
+ }
+
+ log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
+ return 0;
+}
+
+static int save_state(
+ int rfkill_fd,
+ struct udev *udev,
+ const struct rfkill_event *event) {
+
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ _cleanup_free_ char *state_file = NULL;
+ int r;
+
+ assert(rfkill_fd >= 0);
+ assert(udev);
+ assert(event);
+
+ r = find_device(udev, event, &device);
+ if (r < 0)
+ return r;
+
+ r = determine_state_file(udev, event, device, &state_file);
+ if (r < 0)
+ return r;
+
+ r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write state file %s: %m", state_file);
+
+ log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_close_ int rfkill_fd = -1;
+ bool ready = false;
+ int r, n;
+
+ if (argc > 1) {
+ log_error("This program requires no arguments.");
return EXIT_FAILURE;
}
- if (streq(argv[1], "load")) {
- _cleanup_free_ char *value = NULL;
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
- if (!shall_restore_state())
- return EXIT_SUCCESS;
+ umask(0022);
- r = read_one_line_file(saved, &value);
- if (r == -ENOENT)
- return EXIT_SUCCESS;
- if (r < 0) {
- log_error_errno(r, "Failed to read %s: %m", saved);
- return EXIT_FAILURE;
+ udev = udev_new();
+ if (!udev) {
+ r = log_oom();
+ goto finish;
+ }
+
+ r = mkdir_p("/var/lib/systemd/rfkill", 0755);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create rfkill directory: %m");
+ goto finish;
+ }
+
+ n = sd_listen_fds(false);
+ if (n < 0) {
+ r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
+ goto finish;
+ }
+ if (n > 1) {
+ log_error("Got too many file descriptors.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (n == 0) {
+ rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (rfkill_fd < 0) {
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
+ r = 0;
+ goto finish;
+ }
+
+ r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
+ goto finish;
}
+ } else {
+ rfkill_fd = SD_LISTEN_FDS_START;
- r = udev_device_set_sysattr_value(device, "soft", value);
+ r = fd_nonblock(rfkill_fd, 1);
if (r < 0) {
- log_debug_errno(r, "Failed to write 'soft' attribute on rfkill device, ignoring: %m");
- return EXIT_SUCCESS;
+ log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
+ goto finish;
}
+ }
+
+ for (;;) {
+ struct rfkill_event event;
+ const char *type;
+ ssize_t l;
- } else if (streq(argv[1], "save")) {
- const char *value;
+ l = read(rfkill_fd, &event, sizeof(event));
+ if (l < 0) {
+ if (errno == EAGAIN) {
- value = udev_device_get_sysattr_value(device, "soft");
- if (!value) {
- log_debug_errno(r, "Failed to read system attribute, ignoring device: %m");
- return EXIT_SUCCESS;
+ if (!ready) {
+ /* Notify manager that we are
+ * now finished with
+ * processing whatever was
+ * queued */
+ (void) sd_notify(false, "READY=1");
+ ready = true;
+ }
+
+ /* Hang around for a bit, maybe there's more coming */
+
+ r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
+ if (r == -EINTR)
+ continue;
+ if (r < 0) {
+ log_error_errno(r, "Failed to poll() on device: %m");
+ goto finish;
+ }
+ if (r > 0)
+ continue;
+
+ log_debug("All events read and idle, exiting.");
+ break;
+ }
+
+ log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
}
- r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
- if (r < 0) {
- log_error_errno(r, "Failed to write %s: %m", saved);
- return EXIT_FAILURE;
+ if (l != RFKILL_EVENT_SIZE_V1) {
+ log_error("Read event structure of invalid size.");
+ r = -EIO;
+ goto finish;
}
- } else {
- log_error("Unknown verb %s.", argv[1]);
- return EXIT_FAILURE;
+ type = rfkill_type_to_string(event.type);
+ if (!type) {
+ log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
+ continue;
+ }
+
+ switch (event.op) {
+
+ case RFKILL_OP_ADD:
+ log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
+ (void) load_state(rfkill_fd, udev, &event);
+ break;
+
+ case RFKILL_OP_DEL:
+ log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
+ break;
+
+ case RFKILL_OP_CHANGE:
+ log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
+ (void) save_state(rfkill_fd, udev, &event);
+ break;
+
+ default:
+ log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
+ break;
+ }
}
- return EXIT_SUCCESS;
+ r = 0;
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 0615dd6aa5..420a246be1 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -6648,7 +6648,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
if (unit_type_from_string(type) >= 0) {
- if (strv_push(&arg_types, type))
+ if (strv_push(&arg_types, type) < 0)
return log_oom();
type = NULL;
continue;
@@ -6658,7 +6658,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
* load states, but let's support this
* in --types= too for compatibility
* with old versions */
- if (unit_load_state_from_string(optarg) >= 0) {
+ if (unit_load_state_from_string(type) >= 0) {
if (strv_push(&arg_states, type) < 0)
return log_oom();
type = NULL;
@@ -6871,8 +6871,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return 0;
}
- if (strv_consume(&arg_states, s) < 0)
+ if (strv_push(&arg_states, s) < 0)
return log_oom();
+
+ s = NULL;
}
break;
}
diff --git a/units/.gitignore b/units/.gitignore
index 049371884a..883f51f73c 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -59,7 +59,7 @@
/systemd-resolved.service
/systemd-resolved.service.m4
/systemd-hibernate-resume@.service
-/systemd-rfkill@.service
+/systemd-rfkill.service
/systemd-suspend.service
/systemd-sysctl.service
/systemd-sysusers.service
diff --git a/units/systemd-rfkill@.service.in b/units/systemd-rfkill.service.in
index e53bf5fbba..780a19b996 100644
--- a/units/systemd-rfkill@.service.in
+++ b/units/systemd-rfkill.service.in
@@ -6,18 +6,16 @@
# (at your option) any later version.
[Unit]
-Description=Load/Save RF Kill Switch Status of %I
-Documentation=man:systemd-rfkill@.service(8)
+Description=Load/Save RF Kill Switch Status
+Documentation=man:systemd-rfkill.service(8)
DefaultDependencies=no
-BindsTo=sys-subsystem-rfkill-devices-%i.device
RequiresMountsFor=/var/lib/systemd/rfkill
+BindsTo=sys-devices-virtual-misc-rfkill.device
Conflicts=shutdown.target
-After=systemd-remount-fs.service
-Before=sysinit.target shutdown.target
+After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service
+Before=shutdown.target
[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=@rootlibexecdir@/systemd-rfkill load %I
-ExecStop=@rootlibexecdir@/systemd-rfkill save %I
+Type=notify
+ExecStart=@rootlibexecdir@/systemd-rfkill
TimeoutSec=30s
diff --git a/units/systemd-rfkill.socket b/units/systemd-rfkill.socket
new file mode 100644
index 0000000000..20ae2f8adb
--- /dev/null
+++ b/units/systemd-rfkill.socket
@@ -0,0 +1,19 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Load/Save RF Kill Switch Status /dev/rfkill Watch
+Documentation=man:systemd-rfkill.socket(8)
+DefaultDependencies=no
+BindsTo=sys-devices-virtual-misc-rfkill.device
+After=sys-devices-virtual-misc-rfkill.device
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Socket]
+ListenSpecial=/dev/rfkill
+Writable=yes