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

           Branch data     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