1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <sys/socket.h>
8 #include "battery-util.h"
10 #include "constants.h"
11 #include "errno-util.h"
12 #include "glyph-util.h"
16 #include "main-func.h"
17 #include "parse-util.h"
18 #include "pretty-print.h"
19 #include "proc-cmdline.h"
20 #include "socket-util.h"
21 #include "terminal-util.h"
22 #include "time-util.h"
24 #define BATTERY_LOW_MESSAGE \
25 "Battery level critically low. Please connect your charger or the system will power off in 10 seconds."
26 #define BATTERY_RESTORED_MESSAGE \
27 "A.C. power restored, continuing."
29 static bool arg_doit
= true;
31 static int help(void) {
32 _cleanup_free_
char *link
= NULL
;
35 r
= terminal_urlify_man("systemd-battery-check", "8", &link
);
40 "%sCheck battery level to see whether there's enough charge.%s\n\n"
41 " -h --help Show this help\n"
42 " --version Show package version\n"
43 "\nSee the %s for details.\n",
44 program_invocation_short_name
,
52 static bool ERRNO_IS_NO_PLYMOUTH(int r
) {
53 return IN_SET(abs(r
), EAGAIN
, ENOENT
) || ERRNO_IS_DISCONNECT(r
);
56 static int plymouth_send_message(const char *mode
, const char *message
) {
57 static const union sockaddr_union sa
= PLYMOUTH_SOCKET
;
58 _cleanup_free_
char *plymouth_message
= NULL
;
59 _cleanup_close_
int fd
= -EBADF
;
65 c
= asprintf(&plymouth_message
,
68 (int) strlen(mode
) + 1, mode
, '\x00',
69 (int) strlen(message
) + 1, message
, '\x00');
73 /* We set SOCK_NONBLOCK here so that we rather drop the
74 * message than wait for plymouth */
75 fd
= socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
77 return log_warning_errno(errno
, "socket() failed: %m");
79 if (connect(fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
80 return log_full_errno(ERRNO_IS_NO_PLYMOUTH(errno
) ? LOG_DEBUG
: LOG_WARNING
, errno
,
81 "Failed to connect to plymouth: %m");
83 r
= loop_write(fd
, plymouth_message
, c
, /* do_poll = */ false);
85 return log_full_errno(ERRNO_IS_NO_PLYMOUTH(r
) ? LOG_DEBUG
: LOG_WARNING
, r
,
86 "Failed to write to plymouth: %m");
91 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
96 if (streq(key
, "systemd.battery-check")) {
98 r
= value
? parse_boolean(value
) : 1;
100 log_warning_errno(r
, "Failed to parse %s switch, ignoring: %s", key
, value
);
108 static int parse_argv(int argc
, char * argv
[]) {
114 static const struct option options
[] = {
115 { "help", no_argument
, NULL
, 'h' },
116 { "version", no_argument
, NULL
, ARG_VERSION
},
125 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
139 assert_not_reached();
143 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
144 "%s takes no argument.",
145 program_invocation_short_name
);
149 static int run(int argc
, char *argv
[]) {
150 _cleanup_free_
char *plymouth_message
= NULL
;
151 _cleanup_close_
int fd
= -EBADF
;
156 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
158 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
160 r
= parse_argv(argc
, argv
);
165 log_info("Checking battery status and AC power existence is disabled by the kernel command line, skipping execution.");
169 r
= battery_is_discharging_and_low();
171 log_warning_errno(r
, "Failed to check battery status, ignoring: %m");
177 log_emergency("%s " BATTERY_LOW_MESSAGE
, special_glyph(SPECIAL_GLYPH_LOW_BATTERY
));
179 fd
= open_terminal("/dev/console", O_WRONLY
|O_NOCTTY
|O_CLOEXEC
);
181 log_warning_errno(fd
, "Failed to open console, ignoring: %m");
183 dprintf(fd
, ANSI_HIGHLIGHT_RED
"%s " BATTERY_LOW_MESSAGE ANSI_NORMAL
"\n",
184 special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY
, /* force_utf = */ false));
186 if (asprintf(&plymouth_message
, "%s " BATTERY_LOW_MESSAGE
,
187 special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY
, /* force_utf = */ true)) < 0)
190 (void) plymouth_send_message("shutdown", plymouth_message
);
192 usleep_safe(10 * USEC_PER_SEC
);
194 r
= battery_is_discharging_and_low();
196 return log_warning_errno(r
, "Failed to check battery status, assuming not charged yet, powering off: %m");
198 log_emergency("Battery level critically low, powering off.");
202 log_info(BATTERY_RESTORED_MESSAGE
);
204 dprintf(fd
, BATTERY_RESTORED_MESSAGE
"\n");
205 (void) plymouth_send_message("boot-up", BATTERY_RESTORED_MESSAGE
);
210 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);