mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
Merge pull request #1438 from poettering/rfkill-rework
An rfkill rework among other things
This commit is contained in:
commit
e46831f0f1
@ -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 \
|
||||
|
@ -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
|
||||
|
12
NEWS
12
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:
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
@ -380,6 +381,14 @@
|
||||
For SOCK_RAW the port is the IP protocol.</para></listitem>
|
||||
</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
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -118,6 +118,7 @@ struct Socket {
|
||||
|
||||
bool accept;
|
||||
bool remove_on_stop;
|
||||
bool writable;
|
||||
|
||||
/* Socket options */
|
||||
bool keep_alive;
|
||||
|
@ -19,21 +19,275 @@
|
||||
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"
|
||||
|
||||
#define EXIT_USEC (5 * USEC_PER_SEC)
|
||||
|
||||
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",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
|
||||
|
||||
static int find_device(
|
||||
struct udev *udev,
|
||||
const struct rfkill_event *event,
|
||||
struct udev_device **ret) {
|
||||
|
||||
_cleanup_free_ char *sysname = NULL;
|
||||
struct udev_device *device;
|
||||
const char *name;
|
||||
|
||||
assert(udev);
|
||||
assert(event);
|
||||
assert(ret);
|
||||
|
||||
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_debug("Device has no name, ignoring.");
|
||||
udev_device_unref(device);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
log_debug("Operating on rfkill device '%s'.", name);
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
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)
|
||||
return log_oom();
|
||||
|
||||
state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
|
||||
} else
|
||||
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;
|
||||
|
||||
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_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;
|
||||
_cleanup_close_ int rfkill_fd = -1;
|
||||
bool ready = false;
|
||||
int r, n;
|
||||
|
||||
if (argc != 3) {
|
||||
log_error("This program requires two arguments.");
|
||||
if (argc > 1) {
|
||||
log_error("This program requires no arguments.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -43,100 +297,124 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
umask(0022);
|
||||
|
||||
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");
|
||||
return EXIT_FAILURE;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
udev = udev_new();
|
||||
if (!udev) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
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;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
name = udev_device_get_sysattr_value(device, "name");
|
||||
if (!name) {
|
||||
log_error("rfkill device has no name? Ignoring device.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
escaped_type = cescape(type);
|
||||
if (!escaped_type) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
path_id = udev_device_get_property_value(device, "ID_PATH");
|
||||
if (path_id) {
|
||||
escaped_path_id = cescape(path_id);
|
||||
if (!escaped_path_id) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
saved = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", escaped_type, NULL);
|
||||
} else
|
||||
saved = strjoin("/var/lib/systemd/rfkill/", escaped_type, NULL);
|
||||
|
||||
if (!saved) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (streq(argv[1], "load")) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
if (!shall_restore_state())
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
r = udev_device_set_sysattr_value(device, "soft", value);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to write 'soft' attribute on rfkill device, ignoring: %m");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} else if (streq(argv[1], "save")) {
|
||||
const char *value;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} else {
|
||||
log_error("Unknown verb %s.", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
rfkill_fd = SD_LISTEN_FDS_START;
|
||||
|
||||
r = fd_nonblock(rfkill_fd, 1);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
for (;;) {
|
||||
struct rfkill_event event;
|
||||
const char *type;
|
||||
ssize_t l;
|
||||
|
||||
l = read(rfkill_fd, &event, sizeof(event));
|
||||
if (l < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (l != RFKILL_EVENT_SIZE_V1) {
|
||||
log_error("Read event structure of invalid size.");
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
2
units/.gitignore
vendored
2
units/.gitignore
vendored
@ -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
|
||||
|
@ -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
|
19
units/systemd-rfkill.socket
Normal file
19
units/systemd-rfkill.socket
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user