Hello!
I’ve been studying power management for a bit and noticed “Plug acpi shutdown
event” on the TODO list.
This is a sketch of a patch I was working on to power off the system. Upfront,
there are problems with it that I recognize right away:
- I hardcoded the command to power off to be exclusively that of Debian
(/sbin). Maybe the right approach is to pass an option so any command can be
executed instead;
- The thread creation inside of the SCI callback is a bit flaky. Maybe a worker
during initialisation could deal with it, but I am not sure what is the best
way to go;
- Notice I did a simple fixed event handler, which is enough for me to test on
QEMU and maybe desktops, but on most laptops this won’t do I think. I know that
Linux solved this but I haven’t checked that yet;
- Maybe adding a new file for this is a bit excessive, but I figured that the
context around that might keep growing, I just separated things for my sanity.
In that sense, acpi_power_button_init maybe shouldn’t even exist;
- The RB_HALT seemed a bit sketchy to me as it is just a fallback in case the
specified shutdown command fails. And that got me wondering: why not go
directly at that? But I guess we are giving each distribution the liberty to do
what they desire at shutdown that way. If there is another reason, please let
me know.
I made this code on a single live sitting, so it might not be the most
reasonable, but feedback is more than welcome, and I would like to keep working
on it if the idea is more or less on the right track.
Regards!
Marcos Magueta.
PS: I noticed I forgot to update the license date on the new file, but I am not
sure we even want to keep it separated anyway, so I am leaving it.
diff --git a/acpi/Makefile b/acpi/Makefile
index 6e91e98..4413b49 100644
--- a/acpi/Makefile
+++ b/acpi/Makefile
@@ -22,7 +22,7 @@ PORTDIR = $(srcdir)/port
SRCS = main.c netfs_impl.c acpi.c \
acpifs.c ncache.c options.c func_files.c acpi-ops.c \
- acpiServer.c startup_notifyServer.c
+ power_button.c acpiServer.c startup_notifyServer.c
MIGSRCS =
OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS)))
diff --git a/acpi/main.c b/acpi/main.c
index a62c070..de51139 100644
--- a/acpi/main.c
+++ b/acpi/main.c
@@ -40,6 +40,7 @@
#include <pthread.h>
#include <acpi/acpi_init.h>
#include <acpifs.h>
+#include "power_button.h"
/* Libnetfs stuff */
int netfs_maxsymlinks = 0;
@@ -205,6 +206,7 @@ main (int argc, char **argv)
/* Enable ACPI mode of machine */
acpi_init ();
+ acpi_power_button_init ();
if (next_task != MACH_PORT_NULL)
machdev_trivfs_server_startup (bootstrap);
diff --git a/acpi/power_button.c b/acpi/power_button.c
new file mode 100644
index 0000000..2979f03
--- /dev/null
+++ b/acpi/power_button.c
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <<a rel="nofollow"
href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.
+*/
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <spawn.h>
+#include <stdbool.h>
+#include <sys/reboot.h>
+#include <sys/wait.h>
+
+#define __KERNEL__
+#include <acpi/acpi.h>
+
+#include "power_button.h"
+
+#define POWEROFF_CMD "/sbin/poweroff"
+
+static pthread_mutex_t power_lock = PTHREAD_MUTEX_INITIALIZER;
+static bool power_pending;
+
+static void
+power_done (void)
+{
+ pthread_mutex_lock (&power_lock);
+ power_pending = false;
+ pthread_mutex_unlock (&power_lock);
+}
+
+static void *
+power_off (void *arg)
+{
+ char *argv[] = { POWEROFF_CMD, NULL };
+ char *envp[] = { NULL };
+ posix_spawn_file_actions_t actions;
+ pid_t pid;
+ int status;
+ int fd;
+ error_t err;
+
+ error (0, 0, "power button pressed, running %s", POWEROFF_CMD);
+
+ err = posix_spawn_file_actions_init (&actions);
+
+ for (fd = 0; !err && fd < 3; fd++)
+ err = posix_spawn_file_actions_addopen (&actions, fd, "/dev/null",
+ fd ? O_WRONLY : O_RDONLY, 0);
+ if (!err)
+ err = posix_spawn (&pid, POWEROFF_CMD, &actions, NULL, argv, envp);
+ posix_spawn_file_actions_destroy (&actions);
+
+ if (err)
+ error (0, err, "cannot run %s", POWEROFF_CMD);
+ else if (waitpid (pid, &status, 0) != pid)
+ error (0, errno, "waiting for %s", POWEROFF_CMD);
+ else if (WIFEXITED (status) && WEXITSTATUS (status) == 0)
+ {
+ power_done ();
+ return NULL;
+ }
+ else
+ error (0, 0, "%s failed with status %#x", POWEROFF_CMD, status);
+
+ error (0, 0, "halting directly");
+ reboot (RB_HALT);
+ error (0, errno, "reboot");
+
+ power_done ();
+ return NULL;
+}
+
+static u32
+power_button_handler (void *context)
+{
+ pthread_t t;
+ int err;
+
+ pthread_mutex_lock (&power_lock);
+ if (power_pending)
+ {
+ pthread_mutex_unlock (&power_lock);
+ return ACPI_INTERRUPT_HANDLED;
+ }
+ power_pending = true;
+ pthread_mutex_unlock (&power_lock);
+
+ err = pthread_create (&t, NULL, power_off, NULL);
+ if (err)
+ {
+ error (0, err, "creating power off thread");
+ power_done ();
+ }
+ else
+ pthread_detach (t);
+
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+void
+acpi_power_button_init (void)
+{
+ acpi_status status;
+
+ if (acpi_gbl_reduced_hardware
+ || (acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON))
+ return;
+
+ status = acpi_install_fixed_event_handler (ACPI_EVENT_POWER_BUTTON,
+ power_button_handler, NULL);
+ if (ACPI_FAILURE (status))
+ error (0, 0, "cannot install power button handler: %s",
+ acpi_format_exception (status));
+}
diff --git a/acpi/power_button.h b/acpi/power_button.h
new file mode 100644
index 0000000..eda7566
--- /dev/null
+++ b/acpi/power_button.h
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <<a rel="nofollow"
href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.
+*/
+
+#ifndef POWER_BUTTON_H
+#define POWER_BUTTON_H
+
+/* Power the system off when the ACPI power button is pressed. */
+void acpi_power_button_init (void);
+
+#endif // POWER_BUTTON_H