diff options
author | Daniel Mack <github@zonque.org> | 2015-10-01 16:35:43 +0200 |
---|---|---|
committer | Daniel Mack <github@zonque.org> | 2015-10-01 16:35:43 +0200 |
commit | e46831f0f1e2ef0a6cd333b639af1327745ccb5b (patch) | |
tree | b7178058d4d278763c4c294b0c87a7674404e3f0 | |
parent | 8f4cc914724c77cc75d9ffc31e339012be6d2dc1 (diff) | |
parent | 91d0d699121f9cf29e3ba45380ce503b8ea505fe (diff) |
Merge pull request #1438 from poettering/rfkill-rework
An rfkill rework among other things
-rw-r--r-- | Makefile-man.am | 15 | ||||
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | man/systemd-rfkill.service.xml (renamed from man/systemd-rfkill@.service.xml) | 18 | ||||
-rw-r--r-- | man/systemd.service.xml | 24 | ||||
-rw-r--r-- | man/systemd.socket.xml | 25 | ||||
-rw-r--r-- | rules/99-systemd.rules.in | 3 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 1 | ||||
-rw-r--r-- | src/core/socket.c | 8 | ||||
-rw-r--r-- | src/core/socket.h | 1 | ||||
-rw-r--r-- | src/rfkill/rfkill.c | 432 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 8 | ||||
-rw-r--r-- | units/.gitignore | 2 | ||||
-rw-r--r-- | units/systemd-rfkill.service.in (renamed from units/systemd-rfkill@.service.in) | 16 | ||||
-rw-r--r-- | units/systemd-rfkill.socket | 19 |
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 @@ -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 |