LCOV - code coverage report
Current view: top level - login - logind-brightness.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 119 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "bus-util.h"
       4             : #include "device-util.h"
       5             : #include "hash-funcs.h"
       6             : #include "logind-brightness.h"
       7             : #include "logind.h"
       8             : #include "process-util.h"
       9             : #include "stdio-util.h"
      10             : 
      11             : /* Brightness and LED devices tend to be very slow to write to (often being I2C and such). Writes to the
      12             :  * sysfs attributes are synchronous, and hence will freeze our process on access. We can't really have that,
      13             :  * hence we add some complexity: whenever we need to write to the brightness attribute, we do so in a forked
      14             :  * off process, which terminates when it is done. Watching that process allows us to watch completion of the
      15             :  * write operation.
      16             :  *
      17             :  * To make this even more complex: clients are likely to send us many write requests in a short time-frame
      18             :  * (because they implement reactive brightness sliders on screen). Let's coalesce writes to make this
      19             :  * efficient: whenever we get requests to change brightness while we are still writing to the brightness
      20             :  * attribute, let's remember the request and restart a new one when the initial operation finished. When we
      21             :  * get another request while one is ongoing and one is pending we'll replace the pending one with the new
      22             :  * one.
      23             :  *
      24             :  * The bus messages are answered when the first write operation finishes that started either due to the
      25             :  * request or due to a later request that overrode the requested one.
      26             :  *
      27             :  * Yes, this is complex, but I don't see an easier way if we want to be both efficient and still support
      28             :  * completion notification. */
      29             : 
      30             : typedef struct BrightnessWriter {
      31             :         Manager *manager;
      32             : 
      33             :         sd_device *device;
      34             :         char *path;
      35             : 
      36             :         pid_t child;
      37             : 
      38             :         uint32_t brightness;
      39             :         bool again;
      40             : 
      41             :         Set *current_messages;
      42             :         Set *pending_messages;
      43             : 
      44             :         sd_event_source* child_event_source;
      45             : } BrightnessWriter;
      46             : 
      47           0 : static void brightness_writer_free(BrightnessWriter *w) {
      48           0 :         if (!w)
      49           0 :                 return;
      50             : 
      51           0 :         if (w->manager && w->path)
      52           0 :                 (void) hashmap_remove_value(w->manager->brightness_writers, w->path, w);
      53             : 
      54           0 :         sd_device_unref(w->device);
      55           0 :         free(w->path);
      56             : 
      57           0 :         set_free(w->current_messages);
      58           0 :         set_free(w->pending_messages);
      59             : 
      60           0 :         w->child_event_source = sd_event_source_unref(w->child_event_source);
      61             : 
      62           0 :         free(w);
      63             : }
      64             : 
      65           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(BrightnessWriter*, brightness_writer_free);
      66             : 
      67           0 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
      68             :                 brightness_writer_hash_ops,
      69             :                 char,
      70             :                 string_hash_func,
      71             :                 string_compare_func,
      72             :                 BrightnessWriter,
      73             :                 brightness_writer_free);
      74             : 
      75           0 : static void brightness_writer_reply(BrightnessWriter *w, int error) {
      76             :         int r;
      77             : 
      78           0 :         assert(w);
      79             : 
      80           0 :         for (;;) {
      81           0 :                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
      82             : 
      83           0 :                 m = set_steal_first(w->current_messages);
      84           0 :                 if (!m)
      85           0 :                         break;
      86             : 
      87           0 :                 if (error == 0)
      88           0 :                         r = sd_bus_reply_method_return(m, NULL);
      89             :                 else
      90           0 :                         r = sd_bus_reply_method_errnof(m, error, "Failed to write to brightness device: %m");
      91           0 :                 if (r < 0)
      92           0 :                         log_warning_errno(r, "Failed to send method reply, ignoring: %m");
      93             :         }
      94           0 : }
      95             : 
      96             : static int brightness_writer_fork(BrightnessWriter *w);
      97             : 
      98           0 : static int on_brightness_writer_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
      99           0 :         BrightnessWriter *w = userdata;
     100             :         int r;
     101             : 
     102           0 :         assert(s);
     103           0 :         assert(si);
     104           0 :         assert(w);
     105             : 
     106           0 :         assert(si->si_pid == w->child);
     107           0 :         w->child = 0;
     108           0 :         w->child_event_source = sd_event_source_unref(w->child_event_source);
     109             : 
     110           0 :         brightness_writer_reply(w,
     111           0 :                                 si->si_code == CLD_EXITED &&
     112           0 :                                 si->si_status == EXIT_SUCCESS ? 0 : -EPROTO);
     113             : 
     114           0 :         if (w->again) {
     115             :                 /* Another request to change the brightness has been queued. Act on it, but make the pending
     116             :                  * messages the current ones. */
     117           0 :                 w->again = false;
     118           0 :                 set_free(w->current_messages);
     119           0 :                 w->current_messages = TAKE_PTR(w->pending_messages);
     120             : 
     121           0 :                 r = brightness_writer_fork(w);
     122           0 :                 if (r >= 0)
     123           0 :                         return 0;
     124             : 
     125           0 :                 brightness_writer_reply(w, r);
     126             :         }
     127             : 
     128           0 :         brightness_writer_free(w);
     129           0 :         return 0;
     130             : }
     131             : 
     132           0 : static int brightness_writer_fork(BrightnessWriter *w) {
     133             :         int r;
     134             : 
     135           0 :         assert(w);
     136           0 :         assert(w->manager);
     137           0 :         assert(w->child == 0);
     138           0 :         assert(!w->child_event_source);
     139             : 
     140           0 :         r = safe_fork("(sd-bright)", FORK_DEATHSIG|FORK_NULL_STDIO|FORK_CLOSE_ALL_FDS|FORK_LOG, &w->child);
     141           0 :         if (r < 0)
     142           0 :                 return r;
     143           0 :         if (r == 0) {
     144             :                 char brs[DECIMAL_STR_MAX(uint32_t)+1];
     145             : 
     146             :                 /* Child */
     147           0 :                 xsprintf(brs, "%" PRIu32, w->brightness);
     148             : 
     149           0 :                 r = sd_device_set_sysattr_value(w->device, "brightness", brs);
     150           0 :                 if (r < 0) {
     151           0 :                         log_device_error_errno(w->device, r, "Failed to write brightness to device: %m");
     152           0 :                         _exit(EXIT_FAILURE);
     153             :                 }
     154             : 
     155           0 :                 _exit(EXIT_SUCCESS);
     156             :         }
     157             : 
     158           0 :         r = sd_event_add_child(w->manager->event, &w->child_event_source, w->child, WEXITED, on_brightness_writer_exit, w);
     159           0 :         if (r < 0)
     160           0 :                 return log_error_errno(r, "Failed to watch brightness writer child " PID_FMT ": %m", w->child);
     161             : 
     162           0 :         return 0;
     163             : }
     164             : 
     165           0 : static int set_add_message(Set **set, sd_bus_message *message) {
     166             :         int r;
     167             : 
     168           0 :         assert(set);
     169             : 
     170           0 :         if (!message)
     171           0 :                 return 0;
     172             : 
     173           0 :         r = sd_bus_message_get_expect_reply(message);
     174           0 :         if (r <= 0)
     175           0 :                 return r;
     176             : 
     177           0 :         r = set_ensure_allocated(set, &bus_message_hash_ops);
     178           0 :         if (r < 0)
     179           0 :                 return r;
     180             : 
     181           0 :         r = set_put(*set, message);
     182           0 :         if (r < 0)
     183           0 :                 return r;
     184             : 
     185           0 :         sd_bus_message_ref(message);
     186           0 :         return 1;
     187             : }
     188             : 
     189           0 : int manager_write_brightness(
     190             :                 Manager *m,
     191             :                 sd_device *device,
     192             :                 uint32_t brightness,
     193             :                 sd_bus_message *message) {
     194             : 
     195           0 :         _cleanup_(brightness_writer_freep) BrightnessWriter *w = NULL;
     196             :         BrightnessWriter *existing;
     197             :         const char *path;
     198             :         int r;
     199             : 
     200           0 :         assert(m);
     201           0 :         assert(device);
     202             : 
     203           0 :         r = sd_device_get_syspath(device, &path);
     204           0 :         if (r < 0)
     205           0 :                 return log_device_error_errno(device, r, "Failed to get sysfs path for brightness device: %m");
     206             : 
     207           0 :         existing = hashmap_get(m->brightness_writers, path);
     208           0 :         if (existing) {
     209             :                 /* There's already a writer for this device. Let's update it with the new brightness, and add
     210             :                  * our message to the set of message to reply when done. */
     211             : 
     212           0 :                 r = set_add_message(&existing->pending_messages, message);
     213           0 :                 if (r < 0)
     214           0 :                         return log_error_errno(r, "Failed to add message to set: %m");
     215             : 
     216             :                 /* We overide any previously requested brightness here: we coalesce writes, and the newest
     217             :                  * requested brightness is the one we'll put into effect. */
     218           0 :                 existing->brightness = brightness;
     219           0 :                 existing->again = true; /* request another iteration of the writer when the current one is
     220             :                                          * complete */
     221           0 :                 return 0;
     222             :         }
     223             : 
     224           0 :         r = hashmap_ensure_allocated(&m->brightness_writers, &brightness_writer_hash_ops);
     225           0 :         if (r < 0)
     226           0 :                 return log_oom();
     227             : 
     228           0 :         w = new(BrightnessWriter, 1);
     229           0 :         if (!w)
     230           0 :                 return log_oom();
     231             : 
     232           0 :         *w = (BrightnessWriter) {
     233           0 :                 .device = sd_device_ref(device),
     234           0 :                 .path = strdup(path),
     235             :                 .brightness = brightness,
     236             :         };
     237             : 
     238           0 :         if (!w->path)
     239           0 :                 return log_oom();
     240             : 
     241           0 :         r = hashmap_put(m->brightness_writers, w->path, w);
     242           0 :         if (r < 0)
     243           0 :                 return log_error_errno(r, "Failed to add brightness writer to hashmap: %m");
     244           0 :         w->manager = m;
     245             : 
     246           0 :         r = set_add_message(&w->current_messages, message);
     247           0 :         if (r < 0)
     248           0 :                 return log_error_errno(r, "Failed to add message to set: %m");
     249             : 
     250           0 :         r = brightness_writer_fork(w);
     251           0 :         if (r < 0)
     252           0 :                 return r;
     253             : 
     254           0 :         TAKE_PTR(w);
     255           0 :         return 0;
     256             : }

Generated by: LCOV version 1.14