571 lines
17 KiB
Diff
571 lines
17 KiB
Diff
This seems to be a SUSE specific patch. Here we add the check for unmaintained
|
|
disk like devices to be able to flush and maybe shut them down. Also we add the
|
|
missing sync() system call for the direct halt/reboot systemctl command. Then we
|
|
use the system halt as gfallback if poweroff fails for both the direct poweroff
|
|
systemctl command as well as for the systemd-shutdown utility.
|
|
|
|
---
|
|
Makefile.am | 11 +
|
|
src/core/hdflush.c | 365 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
src/core/hdflush.h | 25 +++
|
|
src/core/shutdown.c | 12 +
|
|
src/systemctl/systemctl.c | 25 ++-
|
|
5 files changed, 429 insertions(+), 9 deletions(-)
|
|
|
|
--- systemd-209/Makefile.am
|
|
+++ systemd-209/Makefile.am 2014-01-28 11:06:56.000000000 +0000
|
|
@@ -1004,7 +1004,9 @@ libsystemd_core_la_SOURCES = \
|
|
src/core/audit-fd.c \
|
|
src/core/audit-fd.h \
|
|
src/core/async.c \
|
|
- src/core/async.h
|
|
+ src/core/async.h \
|
|
+ src/core/hdflush.c \
|
|
+ src/core/hdflush.h
|
|
|
|
if HAVE_KMOD
|
|
libsystemd_core_la_SOURCES += \
|
|
@@ -1522,6 +1524,8 @@ systemd_shutdown_SOURCES = \
|
|
src/core/shutdown.c \
|
|
src/core/mount-setup.c \
|
|
src/core/mount-setup.h \
|
|
+ src/core/hdflush.c \
|
|
+ src/core/hdflush.h \
|
|
src/core/killall.h \
|
|
src/core/killall.c
|
|
|
|
@@ -1818,7 +1822,9 @@ systemd_cgroups_agent_LDADD = \
|
|
|
|
# ------------------------------------------------------------------------------
|
|
systemctl_SOURCES = \
|
|
- src/systemctl/systemctl.c
|
|
+ src/systemctl/systemctl.c \
|
|
+ src/core/hdflush.c \
|
|
+ src/core/hdflush.h
|
|
|
|
systemctl_LDADD = \
|
|
libsystemd-units.la \
|
|
@@ -1826,6 +1832,7 @@ systemctl_LDADD = \
|
|
libsystemd-internal.la \
|
|
libsystemd-logs.la \
|
|
libsystemd-journal-internal.la \
|
|
+ libudev-internal.la \
|
|
libsystemd-shared.la
|
|
|
|
# ------------------------------------------------------------------------------
|
|
--- systemd-209/src/core/hdflush.c
|
|
+++ systemd-209/src/core/hdflush.c 2014-01-28 10:58:56.000000000 +0000
|
|
@@ -0,0 +1,367 @@
|
|
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
+
|
|
+/***
|
|
+ This file is part of systemd.
|
|
+
|
|
+ Copyright 2014 Werner Fink
|
|
+
|
|
+ systemd is free software; you can redistribute it and/or modify it
|
|
+ under the terms of the GNU Lesser General Public License as published by
|
|
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ systemd 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
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
+***/
|
|
+
|
|
+/*
|
|
+ * Find all disks on the system, list out IDE, unmanaged ATA disks, and
|
|
+ * USB sticks flush the cache of those and optional shut them down.
|
|
+ */
|
|
+
|
|
+#include <libudev.h>
|
|
+#include <limits.h>
|
|
+#ifdef LIST_DEBUG
|
|
+# include <stdio.h>
|
|
+#endif
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <errno.h>
|
|
+
|
|
+#include <sys/ioctl.h>
|
|
+#include <linux/hdreg.h>
|
|
+#include <linux/fs.h>
|
|
+#ifdef WORDS_BIGENDIAN
|
|
+# include <byteswap.h>
|
|
+#endif
|
|
+
|
|
+#include "hdflush.h"
|
|
+
|
|
+/* Used in flush_cache_ext(), compare with <linux/hdreg.h> */
|
|
+#define IDBYTES 512
|
|
+#define MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */
|
|
+#define TEST_EXT 0x6000
|
|
+
|
|
+/* Maybe set in list_disks() and used in do_standby_disk() */
|
|
+#define DISK_IS_IDE 0x00000001
|
|
+#define DISK_IS_SATA 0x00000002
|
|
+#define DISK_EXTFLUSH 0x00000004
|
|
+#define DISK_REMOVABLE 0x00000008
|
|
+#define DISK_MANAGED 0x00000010
|
|
+#define DISK_FLUSHONLY 0x00000020
|
|
+
|
|
+struct sysfs {
|
|
+ struct udev *udev;
|
|
+ struct udev_enumerate *num;
|
|
+ struct udev_list_entry *item;
|
|
+ char *devnode;
|
|
+ size_t size;
|
|
+};
|
|
+
|
|
+static int flush_cache_ext(const struct sysfs *sysfs);
|
|
+
|
|
+static struct sysfs * open_sysfs(void)
|
|
+{
|
|
+ static struct sysfs sysfs;
|
|
+ sysfs.udev = udev_new();
|
|
+ if (!sysfs.udev)
|
|
+ goto err;
|
|
+ sysfs.num = udev_enumerate_new(sysfs.udev);
|
|
+ if (!sysfs.num)
|
|
+ goto err;
|
|
+ if (udev_enumerate_add_match_subsystem(sysfs.num, "block") < 0)
|
|
+ goto err;
|
|
+ if (udev_enumerate_add_match_sysname(sysfs.num, "sd?") < 0)
|
|
+ goto err;
|
|
+ if (udev_enumerate_add_match_sysname(sysfs.num, "hd?") < 0)
|
|
+ goto err;
|
|
+ if (udev_enumerate_scan_devices(sysfs.num) < 0)
|
|
+ goto err;
|
|
+ sysfs.item = udev_enumerate_get_list_entry(sysfs.num);
|
|
+ sysfs.devnode = NULL;
|
|
+ sysfs.size = 0;
|
|
+ return &sysfs;
|
|
+err:
|
|
+ if (sysfs.num)
|
|
+ udev_unref(sysfs.udev);
|
|
+ if (sysfs.udev)
|
|
+ udev_unref(sysfs.udev);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void close_sysfs(struct sysfs *sysfs)
|
|
+{
|
|
+ if (sysfs->num)
|
|
+ udev_enumerate_unref(sysfs->num);
|
|
+ if (sysfs->udev)
|
|
+ udev_unref(sysfs->udev);
|
|
+ if (sysfs->devnode)
|
|
+ free(sysfs->devnode);
|
|
+ sysfs->devnode = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+static char *list_disks(struct sysfs *sysfs, unsigned int* flags)
|
|
+{
|
|
+ struct udev_device *device, *parent;
|
|
+ struct udev_list_entry *item;
|
|
+ const char *devnode;
|
|
+ char path[PATH_MAX];
|
|
+
|
|
+ device = NULL;
|
|
+next:
|
|
+ if (device)
|
|
+ udev_device_unref(device);
|
|
+ if (sysfs->devnode)
|
|
+ free(sysfs->devnode);
|
|
+ sysfs->devnode = NULL;
|
|
+ sysfs->size = 0;
|
|
+ *flags = 0;
|
|
+
|
|
+ if (!sysfs->item)
|
|
+ goto empty;
|
|
+ item = sysfs->item;
|
|
+ sysfs->item = udev_list_entry_get_next(sysfs->item);
|
|
+
|
|
+ if (!(device = udev_device_new_from_syspath(sysfs->udev, udev_list_entry_get_name(item))))
|
|
+ goto out;
|
|
+ if (!(devnode = udev_device_get_devnode(device)))
|
|
+ goto out;
|
|
+ if (!(sysfs->devnode = strdup(devnode)))
|
|
+ goto out;
|
|
+
|
|
+ path[0] = '\0';
|
|
+ parent = udev_device_get_parent(device);
|
|
+ if (parent) {
|
|
+ const char *sysname, *devpath;
|
|
+ struct udev_device *disk;
|
|
+ const char *value;
|
|
+ int ret;
|
|
+
|
|
+ sysname = udev_device_get_sysname(parent);
|
|
+ devpath = udev_device_get_devpath(parent);
|
|
+
|
|
+ strcpy(path, "/sys");
|
|
+ strcat(path, devpath);
|
|
+ strcat(path, "/scsi_disk/");
|
|
+ strcat(path, sysname);
|
|
+
|
|
+ disk = udev_device_new_from_syspath(sysfs->udev, path);
|
|
+ if (disk) {
|
|
+ value = udev_device_get_sysattr_value(disk, "manage_start_stop");
|
|
+ udev_device_unref(disk);
|
|
+
|
|
+ if (value && *value != '0') {
|
|
+ *flags = DISK_MANAGED;
|
|
+#ifndef LIST_DEBUG
|
|
+ goto next; /* Device managed by the kernel */
|
|
+#endif
|
|
+ }
|
|
+ }
|
|
+
|
|
+ value = udev_device_get_sysattr_value(device, "size");
|
|
+ if (value && *value)
|
|
+ sysfs->size = (size_t)atoll(value);
|
|
+
|
|
+ value = udev_device_get_sysattr_value(device, "removable");
|
|
+ if (value && *value != '0') {
|
|
+ *flags |= DISK_REMOVABLE;
|
|
+
|
|
+ if ((ret = flush_cache_ext(sysfs))) {
|
|
+ if (ret < 0)
|
|
+ goto next;
|
|
+ *flags |= DISK_EXTFLUSH;
|
|
+ }
|
|
+ goto out; /* Removable disk like USB stick */
|
|
+ }
|
|
+
|
|
+ value = udev_device_get_sysname(device);
|
|
+ if (value && *value == 'h') {
|
|
+ *flags |= DISK_IS_IDE;
|
|
+
|
|
+ if ((ret = flush_cache_ext(sysfs))) {
|
|
+ if (ret < 0)
|
|
+ goto next;
|
|
+ *flags |= DISK_EXTFLUSH;
|
|
+ }
|
|
+ goto out; /* IDE disk found */
|
|
+ }
|
|
+
|
|
+ value = udev_device_get_sysattr_value(parent, "vendor");
|
|
+ if (value && strncmp(value, "ATA", 3) == 0) {
|
|
+ *flags |= (DISK_IS_IDE|DISK_IS_SATA);
|
|
+
|
|
+ if ((ret = flush_cache_ext(sysfs))) {
|
|
+ if (ret < 0)
|
|
+ goto next;
|
|
+ *flags |= DISK_EXTFLUSH;
|
|
+ }
|
|
+ goto out; /* SATA disk to shutdown */
|
|
+ }
|
|
+ goto next;
|
|
+ }
|
|
+out:
|
|
+ udev_device_unref(device);
|
|
+empty:
|
|
+ return sysfs->devnode;
|
|
+}
|
|
+#ifndef LIST_DEBUG
|
|
+/*
|
|
+ * Check IDE/(S)ATA hard disk identity for
|
|
+ * the FLUSH CACHE EXT bit set.
|
|
+ */
|
|
+static int flush_cache_ext(const struct sysfs *sysfs)
|
|
+{
|
|
+#ifndef WIN_IDENTIFY
|
|
+#define WIN_IDENTIFY 0xEC
|
|
+#endif
|
|
+ unsigned char args[4+IDBYTES];
|
|
+ unsigned short *id = (unsigned short*)(&args[4]);
|
|
+ int fd = -1, ret = 0;
|
|
+
|
|
+ if (sysfs->size < (1<<28))
|
|
+ goto out; /* small disk */
|
|
+
|
|
+ if ((fd = open(sysfs->devnode, O_RDONLY|O_NONBLOCK|O_CLOEXEC)) < 0)
|
|
+ goto out;
|
|
+
|
|
+ memset(&args[0], 0, sizeof(args));
|
|
+ args[0] = WIN_IDENTIFY;
|
|
+ args[3] = 1;
|
|
+ if (ioctl(fd, HDIO_DRIVE_CMD, &args))
|
|
+ goto out;
|
|
+#ifdef WORDS_BIGENDIAN
|
|
+# if 0
|
|
+ {
|
|
+ const unsigned short *end = id + IDBYTES/2;
|
|
+ const unsigned short *from = id;
|
|
+ unsigned short *to = id;
|
|
+
|
|
+ while (from < end)
|
|
+ *to++ = bswap_16(*from++);
|
|
+ }
|
|
+# else
|
|
+ id[83] = bswap_16(id[83]);
|
|
+# endif
|
|
+#endif
|
|
+ if ((id[83] & MASK_EXT) == TEST_EXT)
|
|
+ ret = 1;
|
|
+out:
|
|
+ if (fd >= 0)
|
|
+ close(fd);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Put an IDE/SCSI/SATA disk in standby mode.
|
|
+ * Code stolen from hdparm.c
|
|
+ */
|
|
+static int do_standby_disk(struct sysfs *sysfs, unsigned int flags)
|
|
+{
|
|
+#ifndef WIN_STANDBYNOW1
|
|
+#define WIN_STANDBYNOW1 0xE0
|
|
+#endif
|
|
+#ifndef WIN_STANDBYNOW2
|
|
+#define WIN_STANDBYNOW2 0x94
|
|
+#endif
|
|
+#ifndef WIN_FLUSH_CACHE_EXT
|
|
+#define WIN_FLUSH_CACHE_EXT 0xEA
|
|
+#endif
|
|
+#ifndef WIN_FLUSH_CACHE
|
|
+#define WIN_FLUSH_CACHE 0xE7
|
|
+#endif
|
|
+ unsigned char flush1[4] = {WIN_FLUSH_CACHE_EXT,0,0,0};
|
|
+ unsigned char flush2[4] = {WIN_FLUSH_CACHE,0,0,0};
|
|
+ unsigned char stdby1[4] = {WIN_STANDBYNOW1,0,0,0};
|
|
+ unsigned char stdby2[4] = {WIN_STANDBYNOW2,0,0,0};
|
|
+ int fd, ret;
|
|
+
|
|
+ if ((fd = open(sysfs->devnode, O_RDWR|O_NONBLOCK|O_CLOEXEC)) < 0)
|
|
+ return -1;
|
|
+
|
|
+ switch (flags & DISK_EXTFLUSH) {
|
|
+ case DISK_EXTFLUSH:
|
|
+ if ((ret = ioctl(fd, HDIO_DRIVE_CMD, &flush1)) == 0)
|
|
+ break;
|
|
+ /* Extend flush rejected, try standard flush */
|
|
+ default:
|
|
+ ret = ioctl(fd, HDIO_DRIVE_CMD, &flush2) &&
|
|
+ ioctl(fd, BLKFLSBUF);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((flags & DISK_FLUSHONLY) == 0x0) {
|
|
+ ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) &&
|
|
+ ioctl(fd, HDIO_DRIVE_CMD, &stdby2);
|
|
+ }
|
|
+
|
|
+ close(fd);
|
|
+
|
|
+ if (ret)
|
|
+ return -1;
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+#ifdef LIST_DEBUG
|
|
+int main()
|
|
+{
|
|
+ char *disk;
|
|
+ unsigned int flags;
|
|
+ struct sysfs *sysfs = open_sysfs();
|
|
+ if (!sysfs)
|
|
+ goto err;
|
|
+ while ((disk = list_disks(sysfs, &flags)))
|
|
+ fprintf(stdout, "%s\n", sysfs->devnode);
|
|
+ close_sysfs(sysfs);
|
|
+err:
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+/*
|
|
+ * List all disks and put them in standby mode.
|
|
+ * This has the side-effect of flushing the writecache,
|
|
+ * which is exactly what we want on poweroff.
|
|
+ */
|
|
+void hddown(void)
|
|
+{
|
|
+ struct sysfs *sysfs;
|
|
+ unsigned int flags;
|
|
+ char *disk;
|
|
+
|
|
+ if (!(sysfs = open_sysfs()))
|
|
+ return;
|
|
+
|
|
+ while ((disk = list_disks(sysfs, &flags)))
|
|
+ do_standby_disk(sysfs, flags);
|
|
+
|
|
+ close_sysfs(sysfs);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * List all disks and cause them to flush their buffers.
|
|
+ */
|
|
+void hdflush(void)
|
|
+{
|
|
+ struct sysfs *sysfs;
|
|
+ unsigned int flags;
|
|
+ char *disk;
|
|
+
|
|
+ if (!(sysfs = open_sysfs()))
|
|
+ return;
|
|
+
|
|
+ while ((disk = list_disks(sysfs, &flags)))
|
|
+ do_standby_disk(sysfs, (flags|DISK_FLUSHONLY));
|
|
+
|
|
+ close_sysfs(sysfs);
|
|
+}
|
|
+#endif
|
|
--- systemd-209/src/core/hdflush.h
|
|
+++ systemd-209/src/core/hdflush.h 2014-01-28 11:00:08.000000000 +0000
|
|
@@ -0,0 +1,25 @@
|
|
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
+
|
|
+#pragma once
|
|
+
|
|
+/***
|
|
+ This file is part of systemd.
|
|
+
|
|
+ Copyright 2014 Werner Fink
|
|
+
|
|
+ systemd is free software; you can redistribute it and/or modify it
|
|
+ under the terms of the GNU Lesser General Public License as published by
|
|
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ systemd 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
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public License
|
|
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
+***/
|
|
+
|
|
+void hdflush(void);
|
|
+void hddown(void);
|
|
--- systemd-209/src/core/shutdown.c
|
|
+++ systemd-209/src/core/shutdown.c 2014-02-28 11:17:22.000000000 +0000
|
|
@@ -40,6 +40,7 @@
|
|
#include "missing.h"
|
|
#include "log.h"
|
|
#include "fileio.h"
|
|
+#include "hdflush.h"
|
|
#include "umount.h"
|
|
#include "util.h"
|
|
#include "mkdir.h"
|
|
@@ -225,7 +226,8 @@ int main(int argc, char *argv[]) {
|
|
_cleanup_free_ char *cgroup = NULL;
|
|
char *arguments[3];
|
|
unsigned retries;
|
|
- int cmd, r;
|
|
+ unsigned cmd;
|
|
+ int r;
|
|
|
|
log_parse_environment();
|
|
r = parse_argv(argc, argv);
|
|
@@ -388,8 +390,13 @@ int main(int argc, char *argv[]) {
|
|
* on reboot(), but the file systems need to be synce'd
|
|
* explicitly in advance. So let's do this here, but not
|
|
* needlessly slow down containers. */
|
|
- if (!in_container)
|
|
+ if (!in_container) {
|
|
sync();
|
|
+ if (cmd == RB_POWER_OFF || cmd == RB_HALT_SYSTEM)
|
|
+ hddown();
|
|
+ else
|
|
+ hdflush();
|
|
+ }
|
|
|
|
switch (cmd) {
|
|
|
|
@@ -449,6 +456,10 @@ int main(int argc, char *argv[]) {
|
|
}
|
|
|
|
reboot(cmd);
|
|
+
|
|
+ if (cmd == RB_POWER_OFF)
|
|
+ reboot(RB_HALT_SYSTEM);
|
|
+
|
|
if (errno == EPERM && in_container) {
|
|
/* If we are in a container, and we lacked
|
|
* CAP_SYS_BOOT just exit, this will kill our
|
|
--- systemd-209/src/systemctl/systemctl.c
|
|
+++ systemd-209/src/systemctl/systemctl.c 2014-02-28 11:19:35.000000000 +0000
|
|
@@ -67,6 +67,7 @@
|
|
#include "logs-show.h"
|
|
#include "socket-util.h"
|
|
#include "fileio.h"
|
|
+#include "hdflush.h"
|
|
#include "env-util.h"
|
|
#include "bus-util.h"
|
|
#include "bus-message.h"
|
|
@@ -93,6 +94,7 @@ static bool arg_no_pager = false;
|
|
static bool arg_no_wtmp = false;
|
|
static bool arg_no_wall = false;
|
|
static bool arg_no_reload = false;
|
|
+static bool arg_no_sync = false;
|
|
static bool arg_show_types = false;
|
|
static bool arg_ignore_inhibitors = false;
|
|
static bool arg_dry = false;
|
|
@@ -5566,6 +5568,7 @@ static int halt_parse_argv(int argc, cha
|
|
{ "reboot", no_argument, NULL, ARG_REBOOT },
|
|
{ "force", no_argument, NULL, 'f' },
|
|
{ "wtmp-only", no_argument, NULL, 'w' },
|
|
+ { "no-sync", no_argument, NULL, 'n' },
|
|
{ "no-wtmp", no_argument, NULL, 'd' },
|
|
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
|
|
{}
|
|
@@ -5617,10 +5620,13 @@ static int halt_parse_argv(int argc, cha
|
|
|
|
case 'i':
|
|
case 'h':
|
|
- case 'n':
|
|
/* Compatibility nops */
|
|
break;
|
|
|
|
+ case 'n':
|
|
+ arg_no_sync = true;
|
|
+ break;
|
|
+
|
|
case '?':
|
|
return -EINVAL;
|
|
|
|
@@ -6266,16 +6272,24 @@ static int halt_now(enum action a) {
|
|
* point on... */
|
|
reboot(RB_ENABLE_CAD);
|
|
|
|
- switch (a) {
|
|
+ if (!arg_no_sync)
|
|
+ sync();
|
|
|
|
- case ACTION_HALT:
|
|
- log_info("Halting.");
|
|
- reboot(RB_HALT_SYSTEM);
|
|
- return -errno;
|
|
+ if (a == ACTION_POWEROFF || a == ACTION_HALT)
|
|
+ hddown();
|
|
+ else
|
|
+ hdflush();
|
|
+
|
|
+ switch (a) {
|
|
|
|
case ACTION_POWEROFF:
|
|
log_info("Powering off.");
|
|
reboot(RB_POWER_OFF);
|
|
+ /* Fall through */
|
|
+
|
|
+ case ACTION_HALT:
|
|
+ log_info("Halting.");
|
|
+ reboot(RB_HALT_SYSTEM);
|
|
return -errno;
|
|
|
|
case ACTION_REBOOT: {
|