From cf9ec37fab9e1db9a334091f60ce049943ad1b85 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 18 Jun 2009 16:02:14 -0400 Subject: [PATCH] Add API to check/repair Linux MD RAID arrays --- src/Makefile.am | 5 + src/devkit-disks-device.c | 134 ++++++++++++++++++ src/devkit-disks-device.h | 4 + src/devkit-disks-polkit-action-lookup.c | 8 ++ src/job-linux-md-check.c | 185 +++++++++++++++++++++++++ src/org.freedesktop.DeviceKit.Disks.Device.xml | 46 +++++- 6 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 src/job-linux-md-check.c diff --git a/src/Makefile.am b/src/Makefile.am index f6346eb..1a604d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,6 +94,7 @@ libexec_PROGRAMS += devkit-disks-helper-mkfs \ devkit-disks-helper-ata-smart-collect \ devkit-disks-helper-ata-smart-selftest \ devkit-disks-helper-drive-detach \ + devkit-disks-helper-linux-md-check \ $(NULL) libexec_SCRIPTS = devkit-disks-helper-change-luks-password @@ -142,6 +143,10 @@ devkit_disks_helper_drive_detach_SOURCES = job-shared.h job-drive-detach.c devkit_disks_helper_drive_detach_CPPFLAGS = $(AM_CPPFLAGS) $(LIBUDEV_CFLAGS) $(SGUTILS_CFLAGS) devkit_disks_helper_drive_detach_LDADD = $(LIBUDEV_LIBS) $(SGUTILS_LIBS) $(GLIB_LIBS) +devkit_disks_helper_linux_md_check_SOURCES = job-shared.h job-linux-md-check.c +devkit_disks_helper_linux_md_check_CPPFLAGS = $(AM_CPPFLAGS) +devkit_disks_helper_linux_md_check_LDADD = $(GLIB_LIBS) + # TODO: move to udev udevhelperdir = $(slashlibdir)/udev udevhelper_PROGRAMS = devkit-disks-part-id devkit-disks-dm-export devkit-disks-probe-ata-smart diff --git a/src/devkit-disks-device.c b/src/devkit-disks-device.c index 097015d..814c533 100644 --- a/src/devkit-disks-device.c +++ b/src/devkit-disks-device.c @@ -8197,6 +8197,140 @@ devkit_disks_device_linux_md_stop (DevkitDisksDevice *device, /*--------------------------------------------------------------------------------------------------------------*/ static void +linux_md_check_completed_cb (DBusGMethodInvocation *context, + DevkitDisksDevice *device, + gboolean job_was_cancelled, + int status, + const char *stderr, + const char *stdout, + gpointer user_data) +{ + if (WEXITSTATUS (status) == 0 && !job_was_cancelled) { + guint64 num_errors; + + num_errors = sysfs_get_uint64 (device->priv->native_path, "md/mismatch_cnt"); + + dbus_g_method_return (context, num_errors); + } else { + if (job_was_cancelled) { + throw_error (context, + DEVKIT_DISKS_ERROR_CANCELLED, + "Job was cancelled"); + } else { + throw_error (context, + DEVKIT_DISKS_ERROR_FAILED, + "Error checking array: helper exited with exit code %d: %s", + WEXITSTATUS (status), stderr); + } + } +} + +static void +devkit_disks_device_linux_md_check_authorized_cb (DevkitDisksDaemon *daemon, + DevkitDisksDevice *device, + DBusGMethodInvocation *context, + const gchar *action_id, + guint num_user_data, + gpointer *user_data_elements) +{ + gchar **options = user_data_elements[0]; + gchar *filename; + int n, m; + char *argv[128]; + const gchar *job_name; + + filename = NULL; + + if (!device->priv->device_is_linux_md) { + throw_error (context, DEVKIT_DISKS_ERROR_FAILED, + "Device is not a Linux md drive"); + goto out; + } + + if (g_strcmp0 (device->priv->linux_md_sync_action, "idle") != 0) { + throw_error (context, DEVKIT_DISKS_ERROR_FAILED, + "Array is not idle"); + goto out; + } + + n = 0; + argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-linux-md-check"; + argv[n++] = device->priv->device_file; + argv[n++] = device->priv->native_path; + for (m = 0; options[m] != NULL; m++) { + if (n >= (int) sizeof (argv) - 1) { + throw_error (context, + DEVKIT_DISKS_ERROR_FAILED, + "Too many options"); + goto out; + } + /* the helper will validate each option */ + argv[n++] = (char *) options[m]; + } + argv[n++] = NULL; + + job_name = "LinuxMdCheck"; + for (n = 0; options != NULL && options[n] != NULL; n++) + if (strcmp (options[n], "repair") == 0) + job_name = "LinuxMdRepair"; + + if (!job_new (context, + job_name, + TRUE, + device, + argv, + NULL, + linux_md_check_completed_cb, + NULL, + NULL)) { + goto out; + } + + out: + ; +} + +gboolean +devkit_disks_device_linux_md_check (DevkitDisksDevice *device, + char **options, + DBusGMethodInvocation *context) +{ + guint n; + const gchar *job_name; + + job_name = "LinuxMdCheck"; + for (n = 0; options != NULL && options[n] != NULL; n++) + if (strcmp (options[n], "repair") == 0) + job_name = "LinuxMdRepair"; + + if (!device->priv->device_is_linux_md) { + throw_error (context, DEVKIT_DISKS_ERROR_FAILED, + "Device is not a Linux md drive"); + goto out; + } + + if (g_strcmp0 (device->priv->linux_md_sync_action, "idle") != 0) { + throw_error (context, DEVKIT_DISKS_ERROR_FAILED, + "Array is not idle"); + goto out; + } + + devkit_disks_daemon_local_check_auth (device->priv->daemon, + device, + "org.freedesktop.devicekit.disks.linux-md", + job_name, + devkit_disks_device_linux_md_check_authorized_cb, + context, + 1, + g_strdupv (options), g_strfreev); + + out: + return TRUE; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +static void linux_md_add_component_completed_cb (DBusGMethodInvocation *context, DevkitDisksDevice *device, gboolean job_was_cancelled, diff --git a/src/devkit-disks-device.h b/src/devkit-disks-device.h index e39479d..2700f69 100644 --- a/src/devkit-disks-device.h +++ b/src/devkit-disks-device.h @@ -162,6 +162,10 @@ gboolean devkit_disks_device_linux_md_stop (DevkitDisksDevice *device, char **options, DBusGMethodInvocation *context); +gboolean devkit_disks_device_linux_md_check (DevkitDisksDevice *device, + char **options, + DBusGMethodInvocation *context); + gboolean devkit_disks_device_linux_md_add_component (DevkitDisksDevice *device, char *component, char **options, diff --git a/src/devkit-disks-polkit-action-lookup.c b/src/devkit-disks-polkit-action-lookup.c index 0d1be52..b0c6fcb 100644 --- a/src/devkit-disks-polkit-action-lookup.c +++ b/src/devkit-disks-polkit-action-lookup.c @@ -118,6 +118,14 @@ static const Map map[] = { N_("Authentication is required to stop a Software RAID device") }, { + "LinuxMdCheck", + N_("Authentication is required to check a Software RAID device") + }, + { + "LinuxMdRepair", + N_("Authentication is required to repair a Software RAID device") + }, + { "LinuxMdAddComponent", N_("Authentication is required to add a new component to a Software RAID device") }, diff --git a/src/job-linux-md-check.c b/src/job-linux-md-check.c new file mode 100644 index 0000000..a343653 --- /dev/null +++ b/src/job-linux-md-check.c @@ -0,0 +1,185 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "job-shared.h" + +static gboolean +sysfs_put_string (const gchar *sysfs_path, + const gchar *file, + const gchar *value) +{ + FILE *f; + gchar *filename; + gboolean ret; + + ret = FALSE; + filename = NULL; + + filename = g_build_filename (sysfs_path, file, NULL); + f = fopen (filename, "w"); + if (f == NULL) { + g_printerr ("error opening %s for writing: %m\n", filename); + goto out; + } else { + if (fputs (value, f) == EOF) { + g_printerr ("error writing '%s' to %s: %m\n", value, filename); + fclose (f); + goto out; + } + fclose (f); + } + + ret = TRUE; + out: + g_free (filename); + return ret; +} + +static char * +sysfs_get_string (const gchar *sysfs_path, + const gchar *file) +{ + gchar *result; + gchar *filename; + + result = NULL; + filename = g_build_filename (sysfs_path, file, NULL); + if (!g_file_get_contents (filename, &result, NULL, NULL)) { + result = g_strdup (""); + } + g_free (filename); + + return result; +} + +static gboolean cancelled = FALSE; + +static void +sigterm_handler (int signum) +{ + cancelled = TRUE; +} + +int +main (int argc, char **argv) +{ + gint ret; + const gchar *device; + const gchar *sysfs_path; + gchar *sync_action; + gboolean repair; + gchar **options; + gint n; + + ret = 1; + repair = FALSE; + sync_action = NULL; + + if (argc < 3) { + g_printerr ("wrong usage\n"); + goto out; + } + device = argv[1]; + sysfs_path = argv[2]; + options = argv + 3; + + for (n = 0; options[n] != NULL; n++) { + if (strcmp (options[n], "repair") == 0) { + repair = TRUE; + } else { + g_printerr ("option %s not supported\n", options[n]); + goto out; + } + } + + g_print ("device = '%s'\n", device); + g_print ("repair = %d\n", repair); + + sync_action = sysfs_get_string (sysfs_path, "md/sync_action"); + g_strstrip (sync_action); + if (g_strcmp0 (sync_action, "idle") != 0) { + g_printerr ("device %s is not idle\n", device); + goto out; + } + + /* if the user cancels, catch that and make the array idle */ + signal (SIGTERM, sigterm_handler); + + if (!sysfs_put_string (sysfs_path, "md/sync_action", repair ? "repair" : "check")) { + goto out; + } + + g_print ("devkit-disks-helper-progress: 0\n"); + while (!cancelled) { + guint64 done; + guint64 remaining; + gchar *s; + + sleep (2); + + sync_action = sysfs_get_string (sysfs_path, "md/sync_action"); + g_strstrip (sync_action); + if (g_strcmp0 (sync_action, "idle") == 0) { + break; + } + g_free (sync_action); + sync_action = NULL; + + s = g_strstrip (sysfs_get_string (sysfs_path, "md/sync_completed")); + if (sscanf (s, "%" G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "", &done, &remaining) == 2) { + g_print ("devkit-disks-helper-progress: %d\n", (gint) (100L * done / remaining)); + } else { + g_printerr ("Cannot parse md/sync_completed: '%s'", s); + goto out; + } + g_free (s); + } + + if (cancelled) { + sysfs_put_string (sysfs_path, "md/sync_action", "idle"); + goto out; + } + + ret = 0; + +out: + g_free (sync_action); + return ret; +} diff --git a/src/org.freedesktop.DeviceKit.Disks.Device.xml b/src/org.freedesktop.DeviceKit.Disks.Device.xml index d8dd5c3..bd4585c 100644 --- a/src/org.freedesktop.DeviceKit.Disks.Device.xml +++ b/src/org.freedesktop.DeviceKit.Disks.Device.xml @@ -899,7 +899,51 @@ if the caller lacks the appropriate PolicyKit authorization - component to add is busy + if the operation failed + if the job was cancelled + + + + + + + + + + + Use the repair option to fix any problems encountered. + + + + + + Number of mismatched sectors/pages found (or fixed if the repair option is used). + + + + + + + Checks a Linux md RAID array and returns the number of + sectors/page with errors found/fixed. This can only be done if the + property + linux-md-sync-action + is idle. + + + + The caller will need the following PolicyKit authorization: + + + org.freedesktop.devicekit.disks.linux-md + + Needed to configured Linux md Software RAID devices. + + + + + + if the caller lacks the appropriate PolicyKit authorization if the operation failed if the job was cancelled -- 2.7.4