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

Reply via email to