summaryrefslogtreecommitdiffstats
path: root/src/battery-check/battery-check.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/battery-check/battery-check.c')
-rw-r--r--src/battery-check/battery-check.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/battery-check/battery-check.c b/src/battery-check/battery-check.c
new file mode 100644
index 0000000..03628c8
--- /dev/null
+++ b/src/battery-check/battery-check.c
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "sd-messages.h"
+
+#include "battery-util.h"
+#include "build.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "glyph-util.h"
+#include "io-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "plymouth-util.h"
+#include "pretty-print.h"
+#include "proc-cmdline.h"
+#include "socket-util.h"
+#include "terminal-util.h"
+#include "time-util.h"
+
+#define BATTERY_LOW_MESSAGE \
+ "Battery level critically low. Please connect your charger or the system will power off in 10 seconds."
+#define BATTERY_RESTORED_MESSAGE \
+ "A.C. power restored, continuing."
+
+static bool arg_doit = true;
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-battery-check", "8", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s\n\n"
+ "%sCheck battery level to see whether there's enough charge.%s\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ "\nSee the %s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ link);
+
+ return 0;
+}
+
+static int plymouth_send_message(const char *mode, const char *message) {
+ _cleanup_free_ char *plymouth_message = NULL;
+ int c, r;
+
+ assert(mode);
+ assert(message);
+
+ c = asprintf(&plymouth_message,
+ "C\x02%c%s%c"
+ "M\x02%c%s%c",
+ (int) strlen(mode) + 1, mode, '\x00',
+ (int) strlen(message) + 1, message, '\x00');
+ if (c < 0)
+ return log_oom();
+
+ /* We set SOCK_NONBLOCK here so that we rather drop the message than wait for plymouth */
+ r = plymouth_send_raw(plymouth_message, c, SOCK_NONBLOCK);
+ if (r < 0)
+ return log_full_errno(ERRNO_IS_NO_PLYMOUTH(r) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to communicate with plymouth: %m");
+
+ return 0;
+}
+
+static int parse_argv(int argc, char * argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s takes no argument.",
+ program_invocation_short_name);
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_free_ char *plymouth_message = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ log_setup();
+
+ r = proc_cmdline_get_bool("systemd.battery-check", PROC_CMDLINE_STRIP_RD_PREFIX|PROC_CMDLINE_TRUE_WHEN_MISSING, &arg_doit);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse systemd.battery-check= kernel command line option, ignoring: %m");
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (!arg_doit) {
+ log_info("Checking battery status and AC power existence is disabled by the kernel command line, skipping execution.");
+ return 0;
+ }
+
+ r = battery_is_discharging_and_low();
+ if (r < 0) {
+ log_warning_errno(r, "Failed to check battery status, ignoring: %m");
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+ log_struct(LOG_EMERG,
+ LOG_MESSAGE("%s " BATTERY_LOW_MESSAGE, special_glyph(SPECIAL_GLYPH_LOW_BATTERY)),
+ "MESSAGE_ID=" SD_MESSAGE_BATTERY_LOW_WARNING_STR);
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ log_warning_errno(fd, "Failed to open console, ignoring: %m");
+ else
+ dprintf(fd, ANSI_HIGHLIGHT_RED "%s " BATTERY_LOW_MESSAGE ANSI_NORMAL "\n",
+ special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY, /* force_utf = */ false));
+
+ if (asprintf(&plymouth_message, "%s " BATTERY_LOW_MESSAGE,
+ special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY, /* force_utf = */ true)) < 0)
+ return log_oom();
+
+ (void) plymouth_send_message("shutdown", plymouth_message);
+
+ usleep_safe(10 * USEC_PER_SEC);
+
+ r = battery_is_discharging_and_low();
+ if (r < 0)
+ return log_warning_errno(r, "Failed to check battery status, assuming not charged yet, powering off: %m");
+ if (r > 0) {
+ log_struct(LOG_EMERG,
+ LOG_MESSAGE("Battery level critically low, powering off."),
+ "MESSAGE_ID=" SD_MESSAGE_BATTERY_LOW_POWEROFF_STR);
+ return r;
+ }
+
+ log_info(BATTERY_RESTORED_MESSAGE);
+ if (fd >= 0)
+ dprintf(fd, BATTERY_RESTORED_MESSAGE "\n");
+ (void) plymouth_send_message("boot-up", BATTERY_RESTORED_MESSAGE);
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);