rfkill.c revision c004493cdefc1f43a3956ca529e8070f8d70be56
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann/***
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann This file is part of systemd.
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann Copyright 2013 Lennart Poettering
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
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
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
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 Herrmann***/
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include <linux/rfkill.h>
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include <poll.h>
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "libudev.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "sd-daemon.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "escape.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "fd-util.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "fileio.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "io-util.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "mkdir.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "string-util.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "udev-util.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#include "util.h"
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann#define EXIT_USEC (5 * USEC_PER_SEC)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_ALL] = "all",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_WLAN] = "wlan",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_UWB] = "uwb",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_WIMAX] = "wimax",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_WWAN] = "wwan",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_GPS] = "gps",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_FM] = "fm",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann [RFKILL_TYPE_NFC] = "nfc",
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann};
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David HerrmannDEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic int find_device(
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev *udev,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const struct rfkill_event *event,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device **ret) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_free_ char *sysname = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device *device;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const char *name;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
7566e26721ee95d6fc864e9e6654fb61bd3cd603Zbigniew Jędrzejewski-Szmek assert(udev);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(ret);
7566e26721ee95d6fc864e9e6654fb61bd3cd603Zbigniew Jędrzejewski-Szmek
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_oom();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!device)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann name = udev_device_get_sysattr_value(device, "name");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!name) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Device has no name, ignoring.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann udev_device_unref(device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return -ENOENT;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Operating on rfkill device '%s'.", name);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann *ret = device;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic int wait_for_initialized(
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev *udev,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device *device,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device **ret) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device *d;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const char *sysname;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int watch_fd, r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(udev);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(ret);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (udev_device_get_is_initialized(device) != 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann *ret = udev_device_ref(device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert_se(sysname = udev_device_get_sysname(device));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Wait until the device is initialized, so that we can get
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * access to the ID_PATH property */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann monitor = udev_monitor_new_from_netlink(udev, "udev");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!monitor)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(errno, "Failed to acquire monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = udev_monitor_enable_receiving(monitor);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to enable udev receiving: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann watch_fd = udev_monitor_get_fd(monitor);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (watch_fd < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(watch_fd, "Failed to get watch fd: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Check again, maybe things changed */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!d)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (udev_device_get_is_initialized(d) != 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann *ret = d;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann for (;;) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *t = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r == -EINTR)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann continue;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to watch udev monitor: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann t = udev_monitor_receive_device(monitor);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!t)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann continue;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (streq_ptr(udev_device_get_sysname(device), sysname)) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann *ret = udev_device_ref(t);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic int determine_state_file(
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev *udev,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const struct rfkill_event *event,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev_device *d,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann char **ret) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const char *path_id, *type;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann char *state_file;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(d);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(ret);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = wait_for_initialized(udev, d, &device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert_se(type = rfkill_type_to_string(event->type));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann path_id = udev_device_get_property_value(device, "ID_PATH");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (path_id) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_free_ char *escaped_path_id = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann escaped_path_id = cescape(path_id);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!escaped_path_id)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_oom();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann } else
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!state_file)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_oom();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann *ret = state_file;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic int load_state(
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int rfkill_fd,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev *udev,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const struct rfkill_event *event) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_free_ char *state_file = NULL, *value = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct rfkill_event we;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann ssize_t l;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int b, r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(rfkill_fd >= 0);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(udev);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!shall_restore_state())
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = find_device(udev, event, &device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = determine_state_file(udev, event, device, &state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = read_one_line_file(state_file, &value);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r == -ENOENT) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* No state file? Then save the current state */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to write state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to read state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann b = parse_boolean(value);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (b < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann we = (struct rfkill_event) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann .op = RFKILL_OP_CHANGE,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann .idx = event->idx,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann .soft = b,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann };
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann l = write(rfkill_fd, &we, sizeof(we));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (l < 0)
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 return -EIO;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannstatic int save_state(
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int rfkill_fd,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct udev *udev,
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const struct rfkill_event *event) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_device_unref_ struct udev_device *device = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_free_ char *state_file = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(rfkill_fd >= 0);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(udev);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann assert(event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = find_device(udev, event, &device);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = determine_state_file(udev, event, device, &state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return log_error_errno(r, "Failed to write state file %s: %m", state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannint main(int argc, char *argv[]) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_udev_unref_ struct udev *udev = NULL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann _cleanup_close_ int rfkill_fd = -1;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann bool ready = false;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann int r, n;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (argc > 1) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("This program requires no arguments.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return EXIT_FAILURE;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_set_target(LOG_TARGET_AUTO);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_parse_environment();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_open();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann umask(0022);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann udev = udev_new();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!udev) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = log_oom();
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = mkdir_p("/var/lib/systemd/rfkill", 0755);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to create rfkill directory: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann n = sd_listen_fds(false);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (n < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (n > 1) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("Got too many file descriptors.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = -EINVAL;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (n == 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (rfkill_fd < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (errno == ENOENT) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann } else {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann rfkill_fd = SD_LISTEN_FDS_START;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = fd_nonblock(rfkill_fd, 1);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann for (;;) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann struct rfkill_event event;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann const char *type;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann ssize_t l;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann l = read(rfkill_fd, &event, sizeof(event));
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (l < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (errno == EAGAIN) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!ready) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Notify manager that we are
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * now finished with
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * processing whatever was
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann * queued */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann (void) sd_notify(false, "READY=1");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann ready = true;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann /* Hang around for a bit, maybe there's more coming */
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r == -EINTR)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann continue;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r < 0) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(r, "Failed to poll() on device: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (r > 0)
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann continue;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("All events read and idle, exiting.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann break;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (l != RFKILL_EVENT_SIZE_V1) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_error("Read event structure of invalid size.");
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = -EIO;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann goto finish;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann type = rfkill_type_to_string(event.type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann if (!type) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann continue;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann switch (event.op) {
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann case RFKILL_OP_ADD:
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann (void) load_state(rfkill_fd, udev, &event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann break;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann case RFKILL_OP_DEL:
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann break;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann case RFKILL_OP_CHANGE:
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann (void) save_state(rfkill_fd, udev, &event);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann break;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann default:
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann break;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann }
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann r = 0;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmannfinish:
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann}
279da1e3f99b9c767a69849b5445e3cfd8d83376David Herrmann