]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/backlight/backlight.c
device-util: Declare iterator variables inline
[thirdparty/systemd.git] / src / backlight / backlight.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
3731acf1 2
ca78ad1d
ZJS
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <unistd.h>
6
9aadd281 7#include "sd-device.h"
b4bbcaa9 8
b5efdb8a 9#include "alloc-util.h"
8437c059 10#include "device-util.h"
4f5dd394 11#include "escape.h"
3731acf1 12#include "fileio.h"
1ddae15a 13#include "main-func.h"
4f5dd394 14#include "mkdir.h"
4e731273 15#include "parse-util.h"
b23728ec 16#include "pretty-print.h"
542bb9be 17#include "process-util.h"
2bfa8466 18#include "reboot-util.h"
07630cea 19#include "string-util.h"
9aadd281 20#include "strv.h"
542bb9be 21#include "terminal-util.h"
3731acf1 22
e0504dd0
YW
23#define PCI_CLASS_GRAPHICS_CARD 0x30000
24
b23728ec
P
25static int help(void) {
26 _cleanup_free_ char *link = NULL;
27 int r;
28
29 r = terminal_urlify_man("systemd-backlight", "8", &link);
30 if (r < 0)
31 return log_oom();
32
33 printf("%s save [backlight|leds]:DEVICE\n"
34 "%s load [backlight|leds]:DEVICE\n"
35 "\n%sSave and restore backlight brightness at shutdown and boot.%s\n\n"
36 " save Save current brightness\n"
37 " load Set brightness to be the previously saved value\n"
bc556335
DDM
38 "\nSee the %s for details.\n",
39 program_invocation_short_name,
40 program_invocation_short_name,
41 ansi_highlight(),
42 ansi_normal(),
43 link);
b23728ec
P
44
45 return 0;
46}
47
e0504dd0
YW
48static int has_multiple_graphics_cards(void) {
49 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
e0504dd0
YW
50 bool found = false;
51 int r;
52
53 r = sd_device_enumerator_new(&e);
54 if (r < 0)
55 return r;
56
57 r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
58 if (r < 0)
59 return r;
60
61 /* class is an unsigned number, let's validate the value later. */
62 r = sd_device_enumerator_add_match_sysattr(e, "class", NULL, /* match = */ true);
63 if (r < 0)
64 return r;
65
66 FOREACH_DEVICE(e, dev) {
67 const char *s;
68 unsigned long c;
69
70 if (sd_device_get_sysattr_value(dev, "class", &s) < 0)
71 continue;
72
73 if (safe_atolu(s, &c) < 0)
74 continue;
75
76 if (c != PCI_CLASS_GRAPHICS_CARD)
77 continue;
78
79 if (found)
80 return true; /* This is the second device. */
81
82 found = true; /* Found the first device. */
83 }
84
85 return false;
86}
87
9aadd281
YW
88static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
89 const char *subsystem, *sysname, *value;
90 sd_device *parent;
91 int r;
0f4ba83c
LP
92
93 assert(device);
9aadd281 94 assert(ret);
0f4ba83c 95
9aadd281
YW
96 r = sd_device_get_parent(device, &parent);
97 if (r < 0)
98 return r;
0f4ba83c 99
9aadd281
YW
100 r = sd_device_get_subsystem(parent, &subsystem);
101 if (r < 0)
102 return r;
0f4ba83c 103
9aadd281
YW
104 r = sd_device_get_sysname(parent, &sysname);
105 if (r < 0)
106 return r;
0f4ba83c
LP
107
108 if (streq(subsystem, "drm")) {
109 const char *c;
110
111 c = startswith(sysname, "card");
112 if (!c)
9aadd281 113 return -ENODATA;
0f4ba83c 114
938d2699 115 c += strspn(c, DIGITS);
85ff4150 116 if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
0f4ba83c 117 /* A connector DRM device, let's ignore all but LVDS and eDP! */
d17d66f0 118 return -EOPNOTSUPP;
0f4ba83c 119
9aadd281
YW
120 } else if (streq(subsystem, "pci") &&
121 sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
afa8ffae 122 unsigned long class;
0f4ba83c 123
9aadd281
YW
124 r = safe_atolu(value, &class);
125 if (r < 0)
126 return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
127 value, subsystem, sysname);
0f4ba83c 128
9aadd281 129 /* Graphics card */
e0504dd0 130 if (class == PCI_CLASS_GRAPHICS_CARD) {
e8596ca5 131 *ret = parent;
9aadd281 132 return 0;
0f4ba83c
LP
133 }
134
9aadd281 135 } else if (streq(subsystem, "platform")) {
e8596ca5 136 *ret = parent;
9aadd281
YW
137 return 0;
138 }
0f4ba83c 139
9aadd281 140 return find_pci_or_platform_parent(parent, ret);
0f4ba83c
LP
141}
142
9aadd281
YW
143static int same_device(sd_device *a, sd_device *b) {
144 const char *a_val, *b_val;
145 int r;
146
0f4ba83c
LP
147 assert(a);
148 assert(b);
149
9aadd281
YW
150 r = sd_device_get_subsystem(a, &a_val);
151 if (r < 0)
152 return r;
153
154 r = sd_device_get_subsystem(b, &b_val);
155 if (r < 0)
156 return r;
0f4ba83c 157
403660c5 158 if (!streq(a_val, b_val))
0f4ba83c
LP
159 return false;
160
9aadd281
YW
161 r = sd_device_get_sysname(a, &a_val);
162 if (r < 0)
163 return r;
164
165 r = sd_device_get_sysname(b, &b_val);
166 if (r < 0)
167 return r;
168
403660c5 169 return streq(a_val, b_val);
0f4ba83c
LP
170}
171
9aadd281
YW
172static int validate_device(sd_device *device) {
173 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
b2f77b5e 174 const char *v, *sysname, *subsystem;
a1af8372 175 sd_device *parent;
0f4ba83c
LP
176 int r;
177
0f4ba83c
LP
178 assert(device);
179
b77c9299
YW
180 /* Verify whether we should actually care for a specific backlight device. For backlight devices
181 * there might be multiple ways to access the same control: "firmware" (i.e. ACPI), "platform"
182 * (i.e. via the machine's EC) and "raw" (via the graphics card). In general we should prefer
183 * "firmware" (i.e. ACPI) or "platform" access over "raw" access, in order not to confuse the
184 * BIOS/EC, and compatibility with possible low-level hotkey handling of screen brightness. The
185 * kernel will already make sure to expose only one of "firmware" and "platform" for the same
186 * device to userspace. However, we still need to make sure that we use "raw" only if no
187 * "firmware" or "platform" device for the same device exists. */
0f4ba83c 188
b2f77b5e
YW
189 r = sd_device_get_sysname(device, &sysname);
190 if (r < 0)
191 return log_device_debug_errno(device, r, "Failed to get sysname: %m");
192
9aadd281
YW
193 r = sd_device_get_subsystem(device, &subsystem);
194 if (r < 0)
b2f77b5e 195 return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
9aadd281 196 if (!streq(subsystem, "backlight"))
0f4ba83c
LP
197 return true;
198
9aadd281
YW
199 r = sd_device_get_sysattr_value(device, "type", &v);
200 if (r < 0)
b2f77b5e 201 return log_device_debug_errno(device, r, "Failed to read 'type' sysattr: %m");
9aadd281 202 if (!streq(v, "raw"))
0f4ba83c
LP
203 return true;
204
9aadd281
YW
205 r = find_pci_or_platform_parent(device, &parent);
206 if (r < 0)
b2f77b5e 207 return log_device_debug_errno(device, r, "Failed to find PCI or platform parent: %m");
0f4ba83c 208
9aadd281
YW
209 r = sd_device_get_subsystem(parent, &subsystem);
210 if (r < 0)
b2f77b5e
YW
211 return log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
212
213 if (DEBUG_LOGGING) {
214 const char *s = NULL;
215
216 (void) sd_device_get_syspath(parent, &s);
217 log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
218 }
0f4ba83c 219
9aadd281
YW
220 r = sd_device_enumerator_new(&enumerate);
221 if (r < 0)
b2f77b5e 222 return log_oom_debug();
0f4ba83c 223
9aadd281 224 r = sd_device_enumerator_allow_uninitialized(enumerate);
0f4ba83c 225 if (r < 0)
b2f77b5e 226 return log_debug_errno(r, "Failed to allow uninitialized devices: %m");
0f4ba83c 227
b2f77b5e 228 r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", /* match = */ true);
0f4ba83c 229 if (r < 0)
b2f77b5e 230 return log_debug_errno(r, "Failed to add subsystem match: %m");
0f4ba83c 231
f8ff4b60
YW
232 r = sd_device_enumerator_add_nomatch_sysname(enumerate, sysname);
233 if (r < 0)
234 return log_debug_errno(r, "Failed to add sysname unmatch: %m");
235
236 r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "platform", /* match = */ true);
237 if (r < 0)
238 return log_debug_errno(r, "Failed to add sysattr match: %m");
239
240 r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "firmware", /* match = */ true);
241 if (r < 0)
242 return log_debug_errno(r, "Failed to add sysattr match: %m");
243
e0504dd0
YW
244 if (streq(subsystem, "pci")) {
245 r = has_multiple_graphics_cards();
246 if (r < 0)
247 return log_debug_errno(r, "Failed to check if the system has multiple graphics cards: %m");
248 if (r > 0) {
249 /* If the system has multiple graphics cards, then we cannot associate platform
250 * devices on non-PCI bus (especially WMI bus) with PCI devices. Let's ignore all
251 * backlight devices that do not have the same parent PCI device. */
252 log_debug("Found multiple graphics cards on PCI bus. "
253 "Skipping to associate platform backlight devices on non-PCI bus.");
254
255 r = sd_device_enumerator_add_match_parent(enumerate, parent);
256 if (r < 0)
257 return log_debug_errno(r, "Failed to add parent match: %m");
258 }
259 }
260
8437c059 261 FOREACH_DEVICE(enumerate, other) {
9aadd281 262 const char *other_subsystem;
e8596ca5 263 sd_device *other_parent;
0f4ba83c 264
b77c9299
YW
265 /* OK, so there's another backlight device, and it's a platform or firmware device.
266 * Let's see if we can verify it belongs to the same device as ours. */
b2f77b5e
YW
267 r = find_pci_or_platform_parent(other, &other_parent);
268 if (r < 0) {
269 log_device_debug_errno(other, r, "Failed to get PCI or platform parent, ignoring: %m");
0f4ba83c 270 continue;
b2f77b5e 271 }
0f4ba83c 272
ba6c9b79 273 if (same_device(parent, other_parent) > 0) {
9aadd281 274 /* Both have the same PCI parent, that means we are out. */
b2f77b5e
YW
275 if (DEBUG_LOGGING) {
276 const char *other_sysname = NULL, *other_type = NULL;
277
278 (void) sd_device_get_sysname(other, &other_sysname);
279 (void) sd_device_get_sysattr_value(other, "type", &other_type);
280 log_device_debug(device,
281 "Found another %s backlight device %s on the same PCI, skipping.",
282 strna(other_type), strna(other_sysname));
283 }
0f4ba83c
LP
284 return false;
285 }
286
b2f77b5e
YW
287 r = sd_device_get_subsystem(other_parent, &other_subsystem);
288 if (r < 0) {
289 log_device_debug_errno(other_parent, r, "Failed to get subsystem, ignoring: %m");
9aadd281 290 continue;
b2f77b5e 291 }
9aadd281
YW
292
293 if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
9aadd281 294 /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
b2f77b5e
YW
295 if (DEBUG_LOGGING) {
296 const char *other_sysname = NULL, *other_type = NULL;
297
298 (void) sd_device_get_sysname(other, &other_sysname);
299 (void) sd_device_get_sysattr_value(other, "type", &other_type);
300 log_device_debug(device,
301 "Found another %s backlight device %s, which has higher precedence, skipping.",
302 strna(other_type), strna(other_sysname));
303 }
0f4ba83c
LP
304 return false;
305 }
306 }
307
308 return true;
309}
310
9aadd281 311static int get_max_brightness(sd_device *device, unsigned *ret) {
c0391616 312 const char *s;
9aadd281 313 int r;
7b909d74 314
9aadd281
YW
315 assert(device);
316 assert(ret);
317
c0391616 318 r = sd_device_get_sysattr_value(device, "max_brightness", &s);
9aadd281 319 if (r < 0)
87a9a197 320 return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
7b909d74 321
c0391616 322 r = safe_atou(s, ret);
9aadd281 323 if (r < 0)
c0391616 324 return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", s);
7b909d74 325
9aadd281 326 return 0;
3cadce7d
TB
327}
328
3bacb7e7
YW
329static int clamp_brightness(sd_device *device, bool saved, unsigned max_brightness, unsigned *brightness) {
330 unsigned new_brightness, min_brightness;
4cd2b2cf 331 const char *subsystem;
3e44d24d 332 int r;
3cadce7d 333
3bacb7e7
YW
334 assert(device);
335 assert(brightness);
7b909d74 336
b77c9299
YW
337 /* Some systems turn the backlight all the way off at the lowest levels. This clamps the saved
338 * brightness to at least 1 or 5% of max_brightness in case of 'backlight' subsystem. This
339 * avoids preserving an unreadably dim screen, which would otherwise force the user to disable
340 * state restoration. */
341
9aadd281
YW
342 r = sd_device_get_subsystem(device, &subsystem);
343 if (r < 0)
87a9a197 344 return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
9aadd281
YW
345
346 if (streq(subsystem, "backlight"))
4cd2b2cf
DT
347 min_brightness = MAX(1U, max_brightness/20);
348 else
349 min_brightness = 0;
350
3bacb7e7
YW
351 new_brightness = CLAMP(*brightness, min_brightness, max_brightness);
352 if (new_brightness != *brightness)
353 log_device_info(device, "%s brightness %u is %s to %u.",
354 saved ? "Saved" : "Current",
355 *brightness,
356 new_brightness > *brightness ?
87a9a197 357 "too low; increasing" : "too high; decreasing",
3bacb7e7 358 new_brightness);
9aadd281 359
3bacb7e7 360 *brightness = new_brightness;
9aadd281 361 return 0;
7b909d74
JT
362}
363
9aadd281 364static bool shall_clamp(sd_device *d) {
4432b941
SR
365 const char *s;
366 int r;
367
368 assert(d);
369
9aadd281 370 r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
87a9a197 371 if (r < 0) {
06d98bdc
YW
372 if (r != -ENOENT)
373 log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
4432b941 374 return true;
87a9a197 375 }
4432b941
SR
376
377 r = parse_boolean(s);
378 if (r < 0) {
87a9a197 379 log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
4432b941
SR
380 return true;
381 }
382
383 return r;
384}
385
3bacb7e7
YW
386static int read_brightness(sd_device *device, unsigned max_brightness, unsigned *ret_brightness) {
387 const char *subsystem, *value;
388 unsigned brightness;
437b9a7f
YW
389 int r;
390
391 assert(device);
3bacb7e7 392 assert(ret_brightness);
437b9a7f
YW
393
394 r = sd_device_get_subsystem(device, &subsystem);
395 if (r < 0)
396 return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
397
398 if (streq(subsystem, "backlight")) {
3bacb7e7
YW
399 r = sd_device_get_sysattr_value(device, "actual_brightness", &value);
400 if (r == -ENOENT) {
401 log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute, "
402 "fall back to use 'brightness' attribute: %m");
403 goto use_brightness;
404 }
405 if (r < 0)
437b9a7f
YW
406 return log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute: %m");
407
3bacb7e7
YW
408 r = safe_atou(value, &brightness);
409 if (r < 0) {
410 log_device_debug_errno(device, r, "Failed to parse 'actual_brightness' attribute, "
411 "fall back to use 'brightness' attribute: %s", value);
412 goto use_brightness;
413 }
414
415 if (brightness > max_brightness) {
416 log_device_debug(device, "actual_brightness=%u is larger than max_brightness=%u, "
417 "fall back to use 'brightness' attribute", brightness, max_brightness);
418 goto use_brightness;
419 }
420
8dc1ad04 421 log_device_debug(device, "Current actual_brightness is %u", brightness);
3bacb7e7
YW
422 *ret_brightness = brightness;
423 return 0;
437b9a7f
YW
424 }
425
3bacb7e7
YW
426use_brightness:
427 r = sd_device_get_sysattr_value(device, "brightness", &value);
437b9a7f
YW
428 if (r < 0)
429 return log_device_debug_errno(device, r, "Failed to read 'brightness' attribute: %m");
430
3bacb7e7
YW
431 r = safe_atou(value, &brightness);
432 if (r < 0)
433 return log_device_debug_errno(device, r, "Failed to parse 'brightness' attribute: %s", value);
434
435 if (brightness > max_brightness)
436 return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
437 "brightness=%u is larger than max_brightness=%u",
438 brightness, max_brightness);
439
8dc1ad04 440 log_device_debug(device, "Current brightness is %u", brightness);
3bacb7e7 441 *ret_brightness = brightness;
437b9a7f
YW
442 return 0;
443}
444
1ddae15a 445static int run(int argc, char *argv[]) {
9aadd281 446 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
3e44d24d
LP
447 _cleanup_free_ char *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
448 const char *sysname, *path_id, *ss, *saved;
3bacb7e7 449 unsigned max_brightness, brightness;
3731acf1
LP
450 int r;
451
d2acb93d 452 log_setup();
daa227a3 453
542bb9be 454 if (argv_looks_like_help(argc, argv))
b23728ec
P
455 return help();
456
74f1bb5c
YW
457 if (argc != 3)
458 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires two arguments.");
3731acf1 459
7a9737bc
YW
460 if (!STR_IN_SET(argv[1], "load", "save"))
461 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]);
462
3731acf1
LP
463 umask(0022);
464
ef5bfcf6 465 r = mkdir_p("/var/lib/systemd/backlight", 0755);
1ddae15a
YW
466 if (r < 0)
467 return log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
3731acf1 468
0f4ba83c 469 sysname = strchr(argv[2], ':');
74f1bb5c
YW
470 if (!sysname)
471 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Requires a subsystem and sysname pair specifying a backlight device.");
0f4ba83c 472
2f82562b 473 ss = strndupa_safe(argv[2], sysname - argv[2]);
0f4ba83c
LP
474
475 sysname++;
476
74f1bb5c
YW
477 if (!STR_IN_SET(ss, "backlight", "leds"))
478 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a backlight or LED device: '%s:%s'", ss, sysname);
0f4ba83c 479
9aadd281 480 r = sd_device_new_from_subsystem_sysname(&device, ss, sysname);
f0f65087
YW
481 if (r < 0) {
482 bool ignore = r == -ENODEV;
483
484 /* Some drivers, e.g. for AMD GPU, removes acpi backlight device soon after it is added.
485 * See issue #21997. */
486 log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
487 "Failed to get backlight or LED device '%s:%s'%s: %m",
488 ss, sysname, ignore ? ", ignoring" : "");
489 return ignore ? 0 : r;
490 }
3731acf1 491
b77c9299
YW
492 /* If max_brightness is 0, then there is no actual backlight device. This happens on desktops
493 * with Asus mainboards that load the eeepc-wmi module. */
1ddae15a
YW
494 if (get_max_brightness(device, &max_brightness) < 0)
495 return 0;
3cadce7d 496
c0391616
ZJS
497 if (max_brightness == 0) {
498 log_device_warning(device, "Maximum brightness is 0, ignoring device.");
499 return 0;
500 }
501
502 log_device_debug(device, "Maximum brightness is %u", max_brightness);
503
be3f52f4 504 escaped_ss = cescape(ss);
1ddae15a
YW
505 if (!escaped_ss)
506 return log_oom();
be3f52f4
LP
507
508 escaped_sysname = cescape(sysname);
1ddae15a
YW
509 if (!escaped_sysname)
510 return log_oom();
be3f52f4 511
9aadd281 512 if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
be3f52f4 513 escaped_path_id = cescape(path_id);
1ddae15a
YW
514 if (!escaped_path_id)
515 return log_oom();
be3f52f4 516
3e44d24d 517 saved = strjoina("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname);
be3f52f4 518 } else
3e44d24d 519 saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname);
3731acf1 520
b77c9299
YW
521 /* If there are multiple conflicting backlight devices, then their probing at boot-time might
522 * happen in any order. This means the validity checking of the device then is not reliable,
523 * since it might not see other devices conflicting with a specific backlight. To deal with
524 * this, we will actively delete backlight state files at shutdown (where device probing should
525 * be complete), so that the validity check at boot time doesn't have to be reliable. */
0f4ba83c 526
b76388e1 527 if (streq(argv[1], "load")) {
3731acf1 528 _cleanup_free_ char *value = NULL;
4432b941 529 bool clamp;
3731acf1 530
934ae16b 531 if (shall_restore_state() == 0)
1ddae15a 532 return 0;
b76388e1 533
9aadd281 534 if (validate_device(device) == 0)
1ddae15a 535 return 0;
0f4ba83c 536
4432b941
SR
537 clamp = shall_clamp(device);
538
3731acf1 539 r = read_one_line_file(saved, &value);
3bacb7e7
YW
540 if (r < 0 && r != -ENOENT)
541 return log_error_errno(r, "Failed to read %s: %m", saved);
542 if (r > 0) {
543 r = safe_atou(value, &brightness);
544 if (r < 0) {
8dc1ad04
YW
545 log_warning_errno(r, "Failed to parse saved brightness '%s', removing %s.",
546 value, saved);
3bacb7e7
YW
547 (void) unlink(saved);
548 } else {
8dc1ad04 549 log_debug("Using saved brightness %u.", brightness);
3bacb7e7
YW
550 if (clamp)
551 (void) clamp_brightness(device, true, max_brightness, &brightness);
552
553 /* Do not fall back to read current brightness below. */
554 r = 1;
555 }
556 }
557 if (r <= 0) {
558 /* Fallback to clamping current brightness or exit early if clamping is not
559 * supported/enabled. */
4432b941 560 if (!clamp)
1ddae15a 561 return 0;
3731acf1 562
3bacb7e7 563 r = read_brightness(device, max_brightness, &brightness);
1ddae15a 564 if (r < 0)
437b9a7f 565 return log_device_error_errno(device, r, "Failed to read current brightness: %m");
4432b941 566
3bacb7e7
YW
567 (void) clamp_brightness(device, false, max_brightness, &brightness);
568 }
7b909d74 569
3bacb7e7 570 r = sd_device_set_sysattr_valuef(device, "brightness", "%u", brightness);
1ddae15a
YW
571 if (r < 0)
572 return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
3731acf1
LP
573
574 } else if (streq(argv[1], "save")) {
9aadd281 575 if (validate_device(device) == 0) {
6990fb6b 576 (void) unlink(saved);
1ddae15a 577 return 0;
0f4ba83c
LP
578 }
579
3bacb7e7 580 r = read_brightness(device, max_brightness, &brightness);
1ddae15a 581 if (r < 0)
437b9a7f 582 return log_device_error_errno(device, r, "Failed to read current brightness: %m");
3731acf1 583
3bacb7e7 584 r = write_string_filef(saved, WRITE_STRING_FILE_CREATE, "%u", brightness);
1ddae15a
YW
585 if (r < 0)
586 return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
3731acf1 587
74f1bb5c 588 } else
04499a70 589 assert_not_reached();
3731acf1 590
1ddae15a 591 return 0;
3731acf1 592}
1ddae15a
YW
593
594DEFINE_MAIN_FUNCTION(run);