diff --git a/20010-logind-add-SetBrightness-bus-call-for-setting-bright.patch b/20010-logind-add-SetBrightness-bus-call-for-setting-bright.patch new file mode 100644 index 0000000000000000000000000000000000000000..c068110187bbca0a85684c27e732c0be3e230251 --- /dev/null +++ b/20010-logind-add-SetBrightness-bus-call-for-setting-bright.patch @@ -0,0 +1,573 @@ +From b88b7b05b4540e69ab31ef526690890a900b424d Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sun, 28 Apr 2019 11:07:56 +0200 +Subject: [PATCH] logind: add SetBrightness() bus call for setting brightness + of leds/backlight devices associated with a seat + +This augments the drm/input device management by adding a single method +call for setting the brightness of an "leds" or "backlight" kernel class +device. + +This method call requires no privileges to call, but a caller can only +change the brightness on sessions that are currently active, and they +must own the session. + +This does not do enumeration of such class devices, feature or range +probing, chnage notification; it doesn't help associating graphics or +input devices with their backlight or leds devices. For all that clients +should go directly to udev/sysfs. The SetBrightness() call is just for +executing the actual change operation, that is otherwise privileged. + +Example line: + + busctl call org.freedesktop.login1 /org/freedesktop/login1/session/self org.freedesktop.login1.Session SetBrightness ssu "backlight" "intel_backlight" 200 + +The parameter the SetBrightness() call takes are the kernel subsystem +(i.e. "leds" or "backlight"), the device name, and the brightness +value. + +On some hw setting the brightness is slow, and implementation and write +access to the sysfs knobs exposes this slowness. Due to this we'll fork +off a writer process in the background so that logind doesn't have to +block. Moreover, write requestes are coalesced: when a write request is +enqueued while one is already being executed it is queued. When another +write reques is then enqueued the earlier one is replaced by the newer +one, so that only one queued write request per device remains at any +time. Method replies are sent as soon as the first write request that +happens after the request was received is completed. + +It is recommended that bus clients turn off the "expect_reply" flag on +the dbus messages they send though, that relieves logind from sending +completion notification and is particularly a good idea if clients +implement reactive UI sliders that send a quick secession of write +requests. + +Replaces: #12413 +--- + src/libsystemd/sd-bus/bus-common-errors.c | 1 + + src/libsystemd/sd-bus/bus-common-errors.h | 1 + + src/login/logind-brightness.c | 256 ++++++++++++++++++++++ + src/login/logind-brightness.h | 9 + + src/login/logind-session-dbus.c | 54 +++++ + src/login/logind.c | 3 +- + src/login/logind.h | 1 + + src/login/meson.build | 28 +-- + src/login/org.freedesktop.login1.conf | 4 + + src/shared/bus-util.c | 10 + + src/shared/bus-util.h | 2 + + 11 files changed, 355 insertions(+), 14 deletions(-) + create mode 100644 src/login/logind-brightness.c + create mode 100644 src/login/logind-brightness.h + +diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c +index ff0790b..7ceb436 100644 +--- a/src/libsystemd/sd-bus/bus-common-errors.c ++++ b/src/libsystemd/sd-bus/bus-common-errors.c +@@ -53,6 +53,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { + SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), + SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY), ++ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_YOUR_DEVICE, EPERM), + + SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_NTP_SUPPORT, EOPNOTSUPP), +diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h +index 77da78d..11d46f1 100644 +--- a/src/libsystemd/sd-bus/bus-common-errors.h ++++ b/src/libsystemd/sd-bus/bus-common-errors.h +@@ -55,6 +55,7 @@ + #define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress" + #define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported" + #define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy" ++#define BUS_ERROR_NOT_YOUR_DEVICE "org.freedesktop.login1.NotYourDevice" + + #define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled" + #define BUS_ERROR_NO_NTP_SUPPORT "org.freedesktop.timedate1.NoNTPSupport" +diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c +new file mode 100644 +index 0000000..8dfa97d +--- /dev/null ++++ b/src/login/logind-brightness.c +@@ -0,0 +1,256 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "bus-util.h" ++#include "device-util.h" ++#include "hash-funcs.h" ++#include "logind-brightness.h" ++#include "logind.h" ++#include "process-util.h" ++#include "stdio-util.h" ++ ++/* Brightness and LED devices tend to be very slow to write to (often being I2C and such). Writes to the ++ * sysfs attributes are synchronous, and hence will freeze our process on access. We can't really have that, ++ * hence we add some complexity: whenever we need to write to the brightness attribute, we do so in a forked ++ * off process, which terminates when it is done. Watching that process allows us to watch completion of the ++ * write operation. ++ * ++ * To make this even more complex: clients are likely to send us many write requests in a short time-frame ++ * (because they implement reactive brightness sliders on screen). Let's coalesce writes to make this ++ * efficient: whenever we get requests to change brightness while we are still writing to the brightness ++ * attribute, let's remember the request and restart a new one when the initial operation finished. When we ++ * get another request while one is ongoing and one is pending we'll replace the pending one with the new ++ * one. ++ * ++ * The bus messages are answered when the first write operation finishes that started either due to the ++ * request or due to a later request that overrode the requested one. ++ * ++ * Yes, this is complex, but I don't see an easier way if we want to be both efficient and still support ++ * completion notification. */ ++ ++typedef struct BrightnessWriter { ++ Manager *manager; ++ ++ sd_device *device; ++ char *path; ++ ++ pid_t child; ++ ++ uint32_t brightness; ++ bool again; ++ ++ Set *current_messages; ++ Set *pending_messages; ++ ++ sd_event_source* child_event_source; ++} BrightnessWriter; ++ ++static void brightness_writer_free(BrightnessWriter *w) { ++ if (!w) ++ return; ++ ++ if (w->manager && w->path) ++ (void) hashmap_remove_value(w->manager->brightness_writers, w->path, w); ++ ++ sd_device_unref(w->device); ++ free(w->path); ++ ++ set_free(w->current_messages); ++ set_free(w->pending_messages); ++ ++ w->child_event_source = sd_event_source_unref(w->child_event_source); ++ ++ free(w); ++} ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(BrightnessWriter*, brightness_writer_free); ++ ++DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( ++ brightness_writer_hash_ops, ++ char, ++ string_hash_func, ++ string_compare_func, ++ BrightnessWriter, ++ brightness_writer_free); ++ ++static void brightness_writer_reply(BrightnessWriter *w, int error) { ++ int r; ++ ++ assert(w); ++ ++ for (;;) { ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; ++ ++ m = set_steal_first(w->current_messages); ++ if (!m) ++ break; ++ ++ if (error == 0) ++ r = sd_bus_reply_method_return(m, NULL); ++ else ++ r = sd_bus_reply_method_errnof(m, error, "Failed to write to brightness device: %m"); ++ if (r < 0) ++ log_warning_errno(r, "Failed to send method reply, ignoring: %m"); ++ } ++} ++ ++static int brightness_writer_fork(BrightnessWriter *w); ++ ++static int on_brightness_writer_exit(sd_event_source *s, const siginfo_t *si, void *userdata) { ++ BrightnessWriter *w = userdata; ++ int r; ++ ++ assert(s); ++ assert(si); ++ assert(w); ++ ++ assert(si->si_pid == w->child); ++ w->child = 0; ++ w->child_event_source = sd_event_source_unref(w->child_event_source); ++ ++ brightness_writer_reply(w, ++ si->si_code == CLD_EXITED && ++ si->si_status == EXIT_SUCCESS ? 0 : -EPROTO); ++ ++ if (w->again) { ++ /* Another request to change the brightness has been queued. Act on it, but make the pending ++ * messages the current ones. */ ++ w->again = false; ++ set_free(w->current_messages); ++ w->current_messages = TAKE_PTR(w->pending_messages); ++ ++ r = brightness_writer_fork(w); ++ if (r >= 0) ++ return 0; ++ ++ brightness_writer_reply(w, r); ++ } ++ ++ brightness_writer_free(w); ++ return 0; ++} ++ ++static int brightness_writer_fork(BrightnessWriter *w) { ++ int r; ++ ++ assert(w); ++ assert(w->manager); ++ assert(w->child == 0); ++ assert(!w->child_event_source); ++ ++ r = safe_fork("(sd-bright)", FORK_DEATHSIG|FORK_NULL_STDIO|FORK_CLOSE_ALL_FDS|FORK_LOG, &w->child); ++ if (r < 0) ++ return r; ++ if (r == 0) { ++ char brs[DECIMAL_STR_MAX(uint32_t)+1]; ++ ++ /* Child */ ++ xsprintf(brs, "%" PRIu32, w->brightness); ++ ++ r = sd_device_set_sysattr_value(w->device, "brightness", brs); ++ if (r < 0) { ++ log_device_error_errno(w->device, r, "Failed to write brightness to device: %m"); ++ _exit(EXIT_FAILURE); ++ } ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ r = sd_event_add_child(w->manager->event, &w->child_event_source, w->child, WEXITED, on_brightness_writer_exit, w); ++ if (r < 0) ++ return log_error_errno(r, "Failed to watch brightness writer child " PID_FMT ": %m", w->child); ++ ++ return 0; ++} ++ ++static int set_add_message(Set **set, sd_bus_message *message) { ++ int r; ++ ++ assert(set); ++ ++ if (!message) ++ return 0; ++ ++ r = sd_bus_message_get_expect_reply(message); ++ if (r <= 0) ++ return r; ++ ++ r = set_ensure_allocated(set, &bus_message_hash_ops); ++ if (r < 0) ++ return r; ++ ++ r = set_put(*set, message); ++ if (r < 0) ++ return r; ++ ++ sd_bus_message_ref(message); ++ return 1; ++} ++ ++int manager_write_brightness( ++ Manager *m, ++ sd_device *device, ++ uint32_t brightness, ++ sd_bus_message *message) { ++ ++ _cleanup_(brightness_writer_freep) BrightnessWriter *w = NULL; ++ BrightnessWriter *existing; ++ const char *path; ++ int r; ++ ++ assert(m); ++ assert(device); ++ ++ r = sd_device_get_syspath(device, &path); ++ if (r < 0) ++ return log_device_error_errno(device, r, "Failed to get sysfs path for brightness device: %m"); ++ ++ existing = hashmap_get(m->brightness_writers, path); ++ if (existing) { ++ /* There's already a writer for this device. Let's update it with the new brightness, and add ++ * our message to the set of message to reply when done. */ ++ ++ r = set_add_message(&existing->pending_messages, message); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add message to set: %m"); ++ ++ /* We overide any previously requested brightness here: we coalesce writes, and the newest ++ * requested brightness is the one we'll put into effect. */ ++ existing->brightness = brightness; ++ existing->again = true; /* request another iteration of the writer when the current one is ++ * complete */ ++ return 0; ++ } ++ ++ r = hashmap_ensure_allocated(&m->brightness_writers, &brightness_writer_hash_ops); ++ if (r < 0) ++ return log_oom(); ++ ++ w = new(BrightnessWriter, 1); ++ if (!w) ++ return log_oom(); ++ ++ *w = (BrightnessWriter) { ++ .device = sd_device_ref(device), ++ .path = strdup(path), ++ .brightness = brightness, ++ }; ++ ++ if (!w->path) ++ return log_oom(); ++ ++ r = hashmap_put(m->brightness_writers, w->path, w); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add brightness writer to hashmap: %m"); ++ w->manager = m; ++ ++ r = set_add_message(&w->current_messages, message); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add message to set: %m"); ++ ++ r = brightness_writer_fork(w); ++ if (r < 0) ++ return r; ++ ++ TAKE_PTR(w); ++ return 0; ++} +diff --git a/src/login/logind-brightness.h b/src/login/logind-brightness.h +new file mode 100644 +index 0000000..b22ee37 +--- /dev/null ++++ b/src/login/logind-brightness.h +@@ -0,0 +1,9 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++#pragma once ++ ++#include "sd-bus.h" ++#include "sd-device.h" ++ ++#include "logind.h" ++ ++int manager_write_brightness(Manager *m, sd_device *device, uint32_t brightness, sd_bus_message *message); +diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c +index c935959..dd21f8c 100644 +--- a/src/login/logind-session-dbus.c ++++ b/src/login/logind-session-dbus.c +@@ -8,10 +8,12 @@ + #include "bus-label.h" + #include "bus-util.h" + #include "fd-util.h" ++#include "logind-brightness.h" + #include "logind-session-device.h" + #include "logind-session.h" + #include "logind.h" + #include "signal-util.h" ++#include "path-util.h" + #include "strv.h" + #include "user-util.h" + #include "util.h" +@@ -508,6 +510,57 @@ static int method_pause_device_complete(sd_bus_message *message, void *userdata, + return sd_bus_reply_method_return(message, NULL); + } + ++static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; ++ _cleanup_(sd_device_unrefp) sd_device *d = NULL; ++ const char *subsystem, *name, *seat; ++ Session *s = userdata; ++ uint32_t brightness; ++ uid_t uid; ++ int r; ++ ++ assert(message); ++ assert(s); ++ ++ r = sd_bus_message_read(message, "ssu", &subsystem, &name, &brightness); ++ if (r < 0) ++ return r; ++ ++ if (!STR_IN_SET(subsystem, "backlight", "leds")) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Subsystem type %s not supported, must be one of 'backlight' or 'leds'.", subsystem); ++ if (!filename_is_valid(name)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not a valid device name %s, refusing.", name); ++ ++ if (!s->seat) ++ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing."); ++ if (s->seat->active != s) ++ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing."); ++ ++ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_creds_get_euid(creds, &uid); ++ if (r < 0) ++ return r; ++ ++ if (uid != 0 && uid != s->user->uid) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness."); ++ ++ r = sd_device_new_from_subsystem_sysname(&d, subsystem, name); ++ if (r < 0) ++ return sd_bus_error_set_errnof(error, r, "Failed to open device %s:%s: %m", subsystem, name); ++ ++ if (sd_device_get_property_value(d, "ID_SEAT", &seat) >= 0 && !streq_ptr(seat, s->seat->id)) ++ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Device %s:%s does not belong to your seat %s, refusing.", subsystem, name, s->seat->id); ++ ++ r = manager_write_brightness(s->manager, d, brightness, message); ++ if (r < 0) ++ return r; ++ ++ return 1; ++} ++ + const sd_bus_vtable session_vtable[] = { + SD_BUS_VTABLE_START(0), + +@@ -548,6 +601,7 @@ const sd_bus_vtable session_vtable[] = { + SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetBrightness", "ssu", NULL, method_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetDisplay", "s", NULL, method_set_display, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("PauseDevice", "uus", 0), +diff --git a/src/login/logind.c b/src/login/logind.c +index bb1d3f3..33bcf3d 100644 +--- a/src/login/logind.c ++++ b/src/login/logind.c +@@ -117,6 +117,7 @@ static Manager* manager_unref(Manager *m) { + hashmap_free(m->users); + hashmap_free(m->inhibitors); + hashmap_free(m->buttons); ++ hashmap_free(m->brightness_writers); + + hashmap_free(m->user_units); + hashmap_free(m->session_units); +@@ -1254,7 +1255,7 @@ int main(int argc, char *argv[]) { + (void) mkdir_label("/run/systemd/users", 0755); + (void) mkdir_label("/run/systemd/sessions", 0755); + +- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, -1) >= 0); ++ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, SIGCHLD, -1) >= 0); + + r = manager_new(&m); + if (r < 0) { +diff --git a/src/login/logind.h b/src/login/logind.h +index 395306c..fbd25d2 100644 +--- a/src/login/logind.h ++++ b/src/login/logind.h +@@ -30,6 +30,7 @@ struct Manager { + Hashmap *users; + Hashmap *inhibitors; + Hashmap *buttons; ++ Hashmap *brightness_writers; + + LIST_HEAD(Seat, seat_gc_queue); + LIST_HEAD(Session, session_gc_queue); +diff --git a/src/login/meson.build b/src/login/meson.build +index 4326a45..069a95e 100644 +--- a/src/login/meson.build ++++ b/src/login/meson.build +@@ -12,29 +12,31 @@ logind_gperf_c = custom_target( + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + + liblogind_core_sources = files(''' ++ logind-acl.h ++ logind-action.c ++ logind-action.h ++ logind-brightness.c ++ logind-brightness.h ++ logind-button.c ++ logind-button.h + logind-core.c ++ logind-dbus.c + logind-device.c + logind-device.h +- logind-button.c +- logind-button.h +- logind-action.c +- logind-action.h ++ logind-inhibit.c ++ logind-inhibit.h ++ logind-seat-dbus.c + logind-seat.c + logind-seat.h +- logind-session.c +- logind-session.h ++ logind-session-dbus.c + logind-session-device.c + logind-session-device.h ++ logind-session.c ++ logind-session.h ++ logind-user-dbus.c + logind-user.c + logind-user.h +- logind-inhibit.c +- logind-inhibit.h +- logind-dbus.c +- logind-session-dbus.c +- logind-seat-dbus.c +- logind-user-dbus.c + logind-utmp.c +- logind-acl.h + '''.split()) + + liblogind_core_sources += [logind_gperf_c] +diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf +index b780ae0..71e1a6b 100644 +--- a/src/login/org.freedesktop.login1.conf ++++ b/src/login/org.freedesktop.login1.conf +@@ -306,6 +306,10 @@ + send_interface="org.freedesktop.login1.Session" + send_member="PauseDeviceComplete"/> + ++ ++ + +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index ff0e800..7e2d3e6 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -1940,3 +1940,13 @@ int bus_reply_pair_array(sd_bus_message *m, char **l) { + + return sd_bus_send(NULL, reply, NULL); + } ++ ++static void bus_message_unref_wrapper(void *m) { ++ sd_bus_message_unref(m); ++} ++ ++const struct hash_ops bus_message_hash_ops = { ++ .hash = trivial_hash_func, ++ .compare = trivial_compare_func, ++ .free_value = bus_message_unref_wrapper, ++}; +diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h +index b400eb8..63b66ab 100644 +--- a/src/shared/bus-util.h ++++ b/src/shared/bus-util.h +@@ -180,3 +180,5 @@ static inline int bus_open_system_watch_bind(sd_bus **ret) { + int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, void *userdata); + + int bus_reply_pair_array(sd_bus_message *m, char **l); ++ ++extern const struct hash_ops bus_message_hash_ops; +-- +2.39.3 + diff --git a/systemd.spec b/systemd.spec index 3360a8825ea8dd1221726a5859b2ce1c81105251..c3b51046102b7b3d090a836b22f1b4a46d873ac5 100644 --- a/systemd.spec +++ b/systemd.spec @@ -1,4 +1,4 @@ -%define anolis_release .0.1 +%define anolis_release .0.2 #global gitcommit 10e465b5321bd53c1fc59ffab27e724535c6bc0f %{?gitcommit:%global gitcommitshort %(c=%{gitcommit}; echo ${c:0:7})} @@ -1105,6 +1105,7 @@ Patch20006: 20006-systemd-Add-sw64.patch Patch20007: 20007-add-seccomp-support-for-sw_64.patch Patch20008: 20008-Fix-unit-test-test-seccomp-support-on-sw_64.patch Patch20009: 20009-core-introduce-cgroup-FullDelegation-FullDelegationD.patch +Patch20010: 20010-logind-add-SetBrightness-bus-call-for-setting-bright.patch # lifsea only patch %if %{defined lifsea_dist} @@ -1832,6 +1833,9 @@ fi %files tests -f .file-list-tests %changelog +* Tue Sep 10 2024 Kaiqiang Wang - 239.82.0.2 +- logind: add SetBrightness() bus call for setting brightness of leds/backlight devices associated with a seat + * Wed Aug 28 2024 Yuanhong Peng - 239-82.0.1 - core: fix a null reference case in load_from_path() - sysctl: Don't pass null directive argument to '%s'