rfkill.c revision c004493cdefc1f43a3956ca529e8070f8d70be56
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann This file is part of systemd.
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann Copyright 2013 Lennart Poettering
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann systemd is free software; you can redistribute it and/or modify it
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann under the terms of the GNU Lesser General Public License as published by
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann the Free Software Foundation; either version 2.1 of the License, or
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann (at your option) any later version.
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann systemd is distributed in the hope that it will be useful, but
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann WITHOUT ANY WARRANTY; without even the implied warranty of
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann Lesser General Public License for more details.
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann You should have received a copy of the GNU Lesser General Public License
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann along with systemd; If not, see <http://www.gnu.org/licenses/>.
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
279da1e3f99b9c767a69849b5445e3cfd8d83376David HerrmannDEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann name = udev_device_get_sysattr_value(device, "name");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Operating on rfkill device '%s'.", name);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (udev_device_get_is_initialized(device) != 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert_se(sysname = udev_device_get_sysname(device));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Wait until the device is initialized, so that we can get
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * access to the ID_PATH property */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann monitor = udev_monitor_new_from_netlink(udev, "udev");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(errno, "Failed to acquire monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to enable udev receiving: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(watch_fd, "Failed to get watch fd: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Check again, maybe things changed */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (udev_device_get_is_initialized(d) != 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *t = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to watch udev monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (streq_ptr(udev_device_get_sysname(device), sysname)) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert_se(type = rfkill_type_to_string(event->type));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann path_id = udev_device_get_property_value(device, "ID_PATH");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_free_ char *state_file = NULL, *value = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = determine_state_file(udev, event, device, &state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* No state file? Then save the current state */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to write state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to read state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (l != sizeof(we)) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("Couldn't write rfkill event structure, too short.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = determine_state_file(udev, event, device, &state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to write state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_unref_ struct udev *udev = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann bool ready = false;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("This program requires no arguments.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = mkdir_p("/var/lib/systemd/rfkill", 0755);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to create rfkill directory: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (n == 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Notify manager that we are
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * now finished with
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * processing whatever was
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Hang around for a bit, maybe there's more coming */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to poll() on device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("All events read and idle, exiting.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("Read event structure of invalid size.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);