LCOV - code coverage report
Current view: top level - rfkill - rfkill.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 188 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 12 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 222 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <fcntl.h>
       4                 :            : #include <linux/rfkill.h>
       5                 :            : #include <poll.h>
       6                 :            : #include <sys/stat.h>
       7                 :            : #include <sys/types.h>
       8                 :            : #include <unistd.h>
       9                 :            : 
      10                 :            : #include "sd-daemon.h"
      11                 :            : #include "sd-device.h"
      12                 :            : 
      13                 :            : #include "alloc-util.h"
      14                 :            : #include "device-util.h"
      15                 :            : #include "escape.h"
      16                 :            : #include "fd-util.h"
      17                 :            : #include "fileio.h"
      18                 :            : #include "io-util.h"
      19                 :            : #include "main-func.h"
      20                 :            : #include "mkdir.h"
      21                 :            : #include "parse-util.h"
      22                 :            : #include "proc-cmdline.h"
      23                 :            : #include "string-table.h"
      24                 :            : #include "string-util.h"
      25                 :            : #include "udev-util.h"
      26                 :            : #include "util.h"
      27                 :            : #include "list.h"
      28                 :            : 
      29                 :            : /* Note that any write is delayed until exit and the rfkill state will not be
      30                 :            :  * stored for rfkill indices that disappear after a change. */
      31                 :            : #define EXIT_USEC (5 * USEC_PER_SEC)
      32                 :            : 
      33                 :            : typedef struct write_queue_item {
      34                 :            :         LIST_FIELDS(struct write_queue_item, queue);
      35                 :            :         int rfkill_idx;
      36                 :            :         char *file;
      37                 :            :         int state;
      38                 :            : } write_queue_item;
      39                 :            : 
      40                 :            : typedef struct Context {
      41                 :            :         LIST_HEAD(write_queue_item, write_queue);
      42                 :            :         int rfkill_fd;
      43                 :            : } Context;
      44                 :            : 
      45                 :          0 : static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
      46         [ #  # ]:          0 :         if (!item)
      47                 :          0 :                 return NULL;
      48                 :            : 
      49                 :          0 :         free(item->file);
      50                 :          0 :         return mfree(item);
      51                 :            : }
      52                 :            : 
      53                 :            : static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
      54                 :            :         [RFKILL_TYPE_ALL] = "all",
      55                 :            :         [RFKILL_TYPE_WLAN] = "wlan",
      56                 :            :         [RFKILL_TYPE_BLUETOOTH] = "bluetooth",
      57                 :            :         [RFKILL_TYPE_UWB] = "uwb",
      58                 :            :         [RFKILL_TYPE_WIMAX] = "wimax",
      59                 :            :         [RFKILL_TYPE_WWAN] = "wwan",
      60                 :            :         [RFKILL_TYPE_GPS] = "gps",
      61                 :            :         [RFKILL_TYPE_FM] = "fm",
      62                 :            :         [RFKILL_TYPE_NFC] = "nfc",
      63                 :            : };
      64                 :            : 
      65   [ #  #  #  # ]:          0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
      66                 :            : 
      67                 :          0 : static int find_device(
      68                 :            :                 const struct rfkill_event *event,
      69                 :            :                 sd_device **ret) {
      70                 :          0 :         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
      71                 :          0 :         _cleanup_free_ char *sysname = NULL;
      72                 :            :         const char *name;
      73                 :            :         int r;
      74                 :            : 
      75         [ #  # ]:          0 :         assert(event);
      76         [ #  # ]:          0 :         assert(ret);
      77                 :            : 
      78         [ #  # ]:          0 :         if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
      79                 :          0 :                 return log_oom();
      80                 :            : 
      81                 :          0 :         r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
      82         [ #  # ]:          0 :         if (r < 0)
      83   [ #  #  #  #  :          0 :                 return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
                   #  # ]
      84                 :            :                                       "Failed to open device '%s': %m", sysname);
      85                 :            : 
      86                 :          0 :         r = sd_device_get_sysattr_value(device, "name", &name);
      87         [ #  # ]:          0 :         if (r < 0)
      88   [ #  #  #  #  :          0 :                 return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
                   #  # ]
      89                 :            : 
      90   [ #  #  #  #  :          0 :         log_device_debug(device, "Operating on rfkill device '%s'.", name);
                   #  # ]
      91                 :            : 
      92                 :          0 :         *ret = TAKE_PTR(device);
      93                 :          0 :         return 0;
      94                 :            : }
      95                 :            : 
      96                 :          0 : static int determine_state_file(
      97                 :            :                 const struct rfkill_event *event,
      98                 :            :                 char **ret) {
      99                 :            : 
     100                 :          0 :         _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
     101                 :            :         const char *path_id, *type;
     102                 :            :         char *state_file;
     103                 :            :         int r;
     104                 :            : 
     105         [ #  # ]:          0 :         assert(event);
     106         [ #  # ]:          0 :         assert(ret);
     107                 :            : 
     108                 :          0 :         r = find_device(event, &d);
     109         [ #  # ]:          0 :         if (r < 0)
     110                 :          0 :                 return r;
     111                 :            : 
     112                 :          0 :         r = device_wait_for_initialization(d, "rfkill", USEC_INFINITY, &device);
     113         [ #  # ]:          0 :         if (r < 0)
     114                 :          0 :                 return r;
     115                 :            : 
     116         [ #  # ]:          0 :         assert_se(type = rfkill_type_to_string(event->type));
     117                 :            : 
     118         [ #  # ]:          0 :         if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
     119         [ #  # ]:          0 :                 _cleanup_free_ char *escaped_path_id = NULL;
     120                 :            : 
     121                 :          0 :                 escaped_path_id = cescape(path_id);
     122         [ #  # ]:          0 :                 if (!escaped_path_id)
     123                 :          0 :                         return log_oom();
     124                 :            : 
     125                 :          0 :                 state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type);
     126                 :            :         } else
     127                 :          0 :                 state_file = strjoin("/var/lib/systemd/rfkill/", type);
     128                 :            : 
     129         [ #  # ]:          0 :         if (!state_file)
     130                 :          0 :                 return log_oom();
     131                 :            : 
     132                 :          0 :         *ret = state_file;
     133                 :          0 :         return 0;
     134                 :            : }
     135                 :            : 
     136                 :          0 : static int load_state(Context *c, const struct rfkill_event *event) {
     137                 :          0 :         _cleanup_free_ char *state_file = NULL, *value = NULL;
     138                 :            :         struct rfkill_event we;
     139                 :            :         ssize_t l;
     140                 :            :         int b, r;
     141                 :            : 
     142         [ #  # ]:          0 :         assert(c);
     143         [ #  # ]:          0 :         assert(c->rfkill_fd >= 0);
     144         [ #  # ]:          0 :         assert(event);
     145                 :            : 
     146         [ #  # ]:          0 :         if (shall_restore_state() == 0)
     147                 :          0 :                 return 0;
     148                 :            : 
     149                 :          0 :         r = determine_state_file(event, &state_file);
     150         [ #  # ]:          0 :         if (r < 0)
     151                 :          0 :                 return r;
     152                 :            : 
     153                 :          0 :         r = read_one_line_file(state_file, &value);
     154   [ #  #  #  # ]:          0 :         if (IN_SET(r, -ENOENT, 0)) {
     155                 :            :                 /* No state file or it's truncated? Then save the current state */
     156                 :            : 
     157                 :          0 :                 r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
     158         [ #  # ]:          0 :                 if (r < 0)
     159         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to write state file %s: %m", state_file);
     160                 :            : 
     161         [ #  # ]:          0 :                 log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file);
     162                 :          0 :                 return 0;
     163                 :            :         }
     164         [ #  # ]:          0 :         if (r < 0)
     165         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to read state file %s: %m", state_file);
     166                 :            : 
     167                 :          0 :         b = parse_boolean(value);
     168         [ #  # ]:          0 :         if (b < 0)
     169         [ #  # ]:          0 :                 return log_error_errno(b, "Failed to parse state file %s: %m", state_file);
     170                 :            : 
     171                 :          0 :         we = (struct rfkill_event) {
     172                 :            :                 .op = RFKILL_OP_CHANGE,
     173                 :          0 :                 .idx = event->idx,
     174                 :            :                 .soft = b,
     175                 :            :         };
     176                 :            : 
     177                 :          0 :         l = write(c->rfkill_fd, &we, sizeof(we));
     178         [ #  # ]:          0 :         if (l < 0)
     179         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
     180         [ #  # ]:          0 :         if (l != sizeof(we))
     181         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     182                 :            :                                        "Couldn't write rfkill event structure, too short.");
     183                 :            : 
     184         [ #  # ]:          0 :         log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
     185                 :          0 :         return 0;
     186                 :            : }
     187                 :            : 
     188                 :          0 : static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
     189                 :            :         struct write_queue_item *item, *tmp;
     190                 :            : 
     191         [ #  # ]:          0 :         assert(c);
     192                 :            : 
     193         [ #  # ]:          0 :         LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
     194   [ #  #  #  #  :          0 :                 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
                   #  # ]
     195         [ #  # ]:          0 :                         log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
     196   [ #  #  #  #  :          0 :                         LIST_REMOVE(queue, c->write_queue, item);
             #  #  #  # ]
     197                 :          0 :                         write_queue_item_free(item);
     198                 :            :                 }
     199                 :            :         }
     200                 :          0 : }
     201                 :            : 
     202                 :          0 : static int save_state_queue(Context *c, const struct rfkill_event *event) {
     203                 :          0 :         _cleanup_free_ char *state_file = NULL;
     204                 :            :         struct write_queue_item *item;
     205                 :            :         int r;
     206                 :            : 
     207         [ #  # ]:          0 :         assert(c);
     208         [ #  # ]:          0 :         assert(c->rfkill_fd >= 0);
     209         [ #  # ]:          0 :         assert(event);
     210                 :            : 
     211                 :          0 :         r = determine_state_file(event, &state_file);
     212         [ #  # ]:          0 :         if (r < 0)
     213                 :          0 :                 return r;
     214                 :            : 
     215                 :          0 :         save_state_queue_remove(c, event->idx, state_file);
     216                 :            : 
     217                 :          0 :         item = new0(struct write_queue_item, 1);
     218         [ #  # ]:          0 :         if (!item)
     219                 :          0 :                 return -ENOMEM;
     220                 :            : 
     221                 :          0 :         item->file = TAKE_PTR(state_file);
     222                 :          0 :         item->rfkill_idx = event->idx;
     223                 :          0 :         item->state = event->soft;
     224                 :            : 
     225   [ #  #  #  #  :          0 :         LIST_APPEND(queue, c->write_queue, item);
          #  #  #  #  #  
                #  #  # ]
     226                 :            : 
     227                 :          0 :         return 0;
     228                 :            : }
     229                 :            : 
     230                 :          0 : static int save_state_cancel(Context *c, const struct rfkill_event *event) {
     231                 :          0 :         _cleanup_free_ char *state_file = NULL;
     232                 :            :         int r;
     233                 :            : 
     234         [ #  # ]:          0 :         assert(c);
     235         [ #  # ]:          0 :         assert(c->rfkill_fd >= 0);
     236         [ #  # ]:          0 :         assert(event);
     237                 :            : 
     238                 :          0 :         r = determine_state_file(event, &state_file);
     239                 :          0 :         save_state_queue_remove(c, event->idx, state_file);
     240         [ #  # ]:          0 :         if (r < 0)
     241                 :          0 :                 return r;
     242                 :            : 
     243                 :          0 :         return 0;
     244                 :            : }
     245                 :            : 
     246                 :          0 : static int save_state_write_one(struct write_queue_item *item) {
     247                 :            :         int r;
     248                 :            : 
     249                 :          0 :         r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
     250         [ #  # ]:          0 :         if (r < 0)
     251         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to write state file %s: %m", item->file);
     252                 :            : 
     253         [ #  # ]:          0 :         log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
     254                 :          0 :         return 0;
     255                 :            : }
     256                 :            : 
     257                 :          0 : static void context_save_and_clear(Context *c) {
     258                 :            :         struct write_queue_item *i;
     259                 :            : 
     260         [ #  # ]:          0 :         assert(c);
     261                 :            : 
     262         [ #  # ]:          0 :         while ((i = c->write_queue)) {
     263   [ #  #  #  #  :          0 :                 LIST_REMOVE(queue, c->write_queue, i);
             #  #  #  # ]
     264                 :          0 :                 (void) save_state_write_one(i);
     265                 :          0 :                 write_queue_item_free(i);
     266                 :            :         }
     267                 :            : 
     268                 :          0 :         safe_close(c->rfkill_fd);
     269                 :          0 : }
     270                 :            : 
     271                 :          0 : static int run(int argc, char *argv[]) {
     272                 :          0 :         _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -1 };
     273                 :          0 :         bool ready = false;
     274                 :            :         int r, n;
     275                 :            : 
     276         [ #  # ]:          0 :         if (argc > 1)
     277         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
     278                 :            : 
     279                 :          0 :         log_setup_service();
     280                 :            : 
     281                 :          0 :         umask(0022);
     282                 :            : 
     283                 :          0 :         n = sd_listen_fds(false);
     284         [ #  # ]:          0 :         if (n < 0)
     285         [ #  # ]:          0 :                 return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
     286         [ #  # ]:          0 :         if (n > 1)
     287         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
     288                 :            : 
     289         [ #  # ]:          0 :         if (n == 0) {
     290                 :          0 :                 c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
     291         [ #  # ]:          0 :                 if (c.rfkill_fd < 0) {
     292         [ #  # ]:          0 :                         if (errno == ENOENT) {
     293         [ #  # ]:          0 :                                 log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
     294                 :          0 :                                 return 0;
     295                 :            :                         }
     296                 :            : 
     297         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
     298                 :            :                 }
     299                 :            :         } else {
     300                 :          0 :                 c.rfkill_fd = SD_LISTEN_FDS_START;
     301                 :            : 
     302                 :          0 :                 r = fd_nonblock(c.rfkill_fd, 1);
     303         [ #  # ]:          0 :                 if (r < 0)
     304         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
     305                 :            :         }
     306                 :            : 
     307                 :          0 :         for (;;) {
     308                 :            :                 struct rfkill_event event;
     309                 :            :                 const char *type;
     310                 :            :                 ssize_t l;
     311                 :            : 
     312                 :          0 :                 l = read(c.rfkill_fd, &event, sizeof(event));
     313         [ #  # ]:          0 :                 if (l < 0) {
     314         [ #  # ]:          0 :                         if (errno == EAGAIN) {
     315                 :            : 
     316         [ #  # ]:          0 :                                 if (!ready) {
     317                 :            :                                         /* Notify manager that we are
     318                 :            :                                          * now finished with
     319                 :            :                                          * processing whatever was
     320                 :            :                                          * queued */
     321                 :          0 :                                         (void) sd_notify(false, "READY=1");
     322                 :          0 :                                         ready = true;
     323                 :            :                                 }
     324                 :            : 
     325                 :            :                                 /* Hang around for a bit, maybe there's more coming */
     326                 :            : 
     327                 :          0 :                                 r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
     328         [ #  # ]:          0 :                                 if (r == -EINTR)
     329                 :          0 :                                         continue;
     330         [ #  # ]:          0 :                                 if (r < 0)
     331         [ #  # ]:          0 :                                         return log_error_errno(r, "Failed to poll() on device: %m");
     332         [ #  # ]:          0 :                                 if (r > 0)
     333                 :          0 :                                         continue;
     334                 :            : 
     335         [ #  # ]:          0 :                                 log_debug("All events read and idle, exiting.");
     336                 :          0 :                                 break;
     337                 :            :                         }
     338                 :            : 
     339         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
     340                 :            :                 }
     341                 :            : 
     342         [ #  # ]:          0 :                 if (l != RFKILL_EVENT_SIZE_V1)
     343         [ #  # ]:          0 :                         return log_error_errno(SYNTHETIC_ERRNO(EIO), "Read event structure of invalid size.");
     344                 :            : 
     345                 :          0 :                 type = rfkill_type_to_string(event.type);
     346         [ #  # ]:          0 :                 if (!type) {
     347         [ #  # ]:          0 :                         log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type);
     348                 :          0 :                         continue;
     349                 :            :                 }
     350                 :            : 
     351   [ #  #  #  # ]:          0 :                 switch (event.op) {
     352                 :            : 
     353                 :          0 :                 case RFKILL_OP_ADD:
     354         [ #  # ]:          0 :                         log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
     355                 :          0 :                         (void) load_state(&c, &event);
     356                 :          0 :                         break;
     357                 :            : 
     358                 :          0 :                 case RFKILL_OP_DEL:
     359         [ #  # ]:          0 :                         log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
     360                 :          0 :                         (void) save_state_cancel(&c, &event);
     361                 :          0 :                         break;
     362                 :            : 
     363                 :          0 :                 case RFKILL_OP_CHANGE:
     364         [ #  # ]:          0 :                         log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
     365                 :          0 :                         (void) save_state_queue(&c, &event);
     366                 :          0 :                         break;
     367                 :            : 
     368                 :          0 :                 default:
     369         [ #  # ]:          0 :                         log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type);
     370                 :          0 :                         break;
     371                 :            :                 }
     372                 :            :         }
     373                 :            : 
     374                 :          0 :         return 0;
     375                 :            : }
     376                 :            : 
     377                 :          0 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14