From 1734ba34a10acfc2cee6058dc796464c649434d3 Mon Sep 17 00:00:00 2001
From: "Samantha N. Bueno" <sbueno+anaconda@redhat.com>
Date: Mon, 11 May 2015 09:15:01 -0400
Subject: [PATCH 2/3] Add the s390 plugin functions.

This includes the header, api, and source for all s390-related
functions, as well as the Python bindings.
---
 src/lib/plugin_apis/s390.api        |  78 ++++++++
 src/plugins/s390.c                  | 389 ++++++++++++++++++++++++++++++++++++
 src/plugins/s390.h                  |  79 ++++++++
 src/python/gi/overrides/BlockDev.py |  50 +++++
 4 files changed, 596 insertions(+)
 create mode 100644 src/lib/plugin_apis/s390.api
 create mode 100644 src/plugins/s390.c
 create mode 100644 src/plugins/s390.h

diff --git a/src/lib/plugin_apis/s390.api b/src/lib/plugin_apis/s390.api
new file mode 100644
index 0000000..82d2b1b
--- /dev/null
+++ b/src/lib/plugin_apis/s390.api
@@ -0,0 +1,78 @@
+#include <glib.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <string.h>
+#include <utils.h>
+#include <asm/dasd.h>
+#include <s390utils/vtoc.h>
+
+
+typedef enum {
+    BD_S390_ERROR_DEVICE,
+    BD_S390_ERROR_FORMAT_FAILED,
+    BD_S390_ERROR_PARSE,
+} BDS390Error;
+
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, whether it needs dasdfmt run on it
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_is_Ldl:
+ * @dasd: dasd to check, whether it is LDL formatted
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd is LDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error);
diff --git a/src/plugins/s390.c b/src/plugins/s390.c
new file mode 100644
index 0000000..1d70608
--- /dev/null
+++ b/src/plugins/s390.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2015  Red Hat, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Samantha N. Bueno <sbueno@redhat.com>
+ */
+
+#include <glib.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <string.h>
+#include <utils.h>
+#include <asm/dasd.h>
+#include <s390utils/vtoc.h>
+
+#include "s390.h"
+
+
+/**
+ *  * SECTION: s390
+ *   * @short_description: plugin for operations with s390
+ *    * @title: s390
+ *     * @include: s390.h
+ *      *
+ *       * A plugin for operations with s390 devices.
+ *         */
+
+/**
+ *  * bd_s390_error_quark: (skip)
+ *   */
+GQuark bd_s390_error_quark (void) {
+    return g_quark_from_static_string ("g-bd-s390-error-quark");
+}
+
+
+/**
+ * check: (skip)
+ */
+gboolean check() {
+    GError *error = NULL;
+    /* dasdfmt doesn't return version info */
+    gboolean ret = bd_utils_check_util_version ("dasdfmt", NULL, NULL, NULL, &error);
+    if (!ret && error) {
+        g_warning("Cannot load the s390 plugin: %s" , error->message);
+        g_clear_error (&error);
+    }
+    return ret;
+}
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error) {
+    gint rc = 0;
+    gchar *cmd = NULL;
+    gchar *failed = NULL;
+
+    g_strdup_printf ("/sbin/dasdfmt -y -d cdl -b 4096 /dev/%s", dasd);
+    rc = system(cmd);
+
+    /* rc is 0 on successful dasdfmt */
+    if (rc) {
+        failed = g_strdup_printf ("dasdfmt failed: %d", rc);
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_FORMAT_FAILED, failed);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, given as device id
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error) {
+    gchar **status = NULL;
+    gchar *path = NULL;
+    GIOStatus rc = 0;
+    GIOChannel *fd = NULL;
+
+    g_clear_error (error);
+
+    path = g_strdup_printf ("/sys/bus/ccw/drivers/dasd-eckd/%s/status", dasd);
+    fd = g_io_channel_new_file (path, "r+", error);
+    if (!fd)
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking status of device %s; device may not exist, or status can not be read.", dasd);
+        return FALSE;
+
+    rc = g_io_channel_read_line(fd, status, NULL, NULL, error);
+    if (rc != G_IO_STATUS_NORMAL) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking status of device %s.", dasd);
+        return FALSE;
+    }
+
+    if (g_ascii_strncasecmp (*status, "unformatted", 11) == 0) {
+        g_warning ("%s (/dev/%s) status is %s, needs dasdfmt.", dasd, dasd, *status);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online, given as device id
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error) {
+    gint rc;
+    gchar **online = NULL;
+    gchar *cmd = NULL;
+    gchar *path = NULL;
+    GIOChannel *fd = NULL;
+    GIOStatus status = 0;
+
+    g_clear_error (error);
+
+    path = g_strdup_printf ("/sys/bus/ccw/drivers/dasd-eckd/%s/online", dasd);
+    fd = g_io_channel_new_file (path, "r+", error);
+    if (!fd) {
+        /* DASD might be in device ignore list; try to rm it */
+        cmd = g_strdup_printf ("/usr/sbin/dasd_cio_free -d %s", dasd);
+        rc = system(cmd);
+
+        /* rc is 0 on successful dasd_cio_free */
+        if (rc) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not free device %s.", dasd);
+            return FALSE;
+        }
+        /* fd is NULL at this point, so try to open it */
+        fd = g_io_channel_new_file (path, "r+", error);
+        if (!fd) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not open device %s even after removing it from the device ignore list.", dasd);
+            return FALSE;
+        }
+    }
+
+    /* check whether our DASD is online; if not, set it */
+    status = g_io_channel_read_line (fd, online, NULL, NULL, error);
+    if (status != G_IO_STATUS_NORMAL) {
+        /* there was some error checking the 'online' status */
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking if device %s is online", dasd);
+        return FALSE;
+    }
+    if (g_ascii_strncasecmp ((const gchar *)*online, "1", 1) == 0) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "DASD device %s is already online.", dasd);
+        return FALSE;
+    }
+    else {
+        status = g_io_channel_write_chars (fd, "1", 1, NULL, error);
+    }
+
+    status = g_io_channel_shutdown(fd, TRUE, error);
+
+    if (status != G_IO_STATUS_NORMAL) {
+        g_warning ("Could not set DASD device %s online", dasd);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * bd_s390_dasd_read_vlabel:
+ * @dasd_info:
+ * @fd: a file descriptor for the open DASD device
+ * @blksize:
+ * @vlabel:
+ *
+ * Returns:
+ */
+static int bd_s390_dasd_read_vlabel(dasd_information2_t *dasd_info, int fd, int blksize, volume_label_t *vlabel) {
+    gint rc;
+    unsigned long vlabel_start;
+    vlabel_start = dasd_info->label_block * blksize;
+
+    memset(vlabel, 0, sizeof(volume_label_t));
+
+    if (lseek(fd, vlabel_start, SEEK_SET) < 0)
+        return 2;
+
+    rc = read(fd, vlabel, sizeof(volume_label_t));
+    if (rc != sizeof(volume_label_t))
+        return 1;
+
+    return 0;
+}
+
+/**
+ * bd_s390_dasd_is_ldl:
+ * @dasd: dasd to check, whether it is LDL formatted
+ *
+ * Returns: whether a dasd is LDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd, GError **error) {
+    gchar *devname = NULL;
+    gint f, ret, blksize;
+    dasd_information2_t dasd_info;
+    volume_label_t vlabel;
+
+    memset(&dasd_info, 0, sizeof(dasd_info));
+    devname = g_strdup_printf("/dev/%s", dasd);
+
+    g_clear_error (error);
+
+    if ((f = open(devname, O_RDONLY)) == -1)
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Unable to open device %s", devname);
+        return FALSE;
+    if (ioctl(f, BLKSSZGET, &blksize) != 0) {
+        close(f);
+        return FALSE;
+    }
+
+    if (ioctl(f, BIODASDINFO2, &dasd_info) != 0) {
+        close(f);
+        return FALSE;
+    }
+
+    ret = bd_s390_dasd_read_vlabel(&dasd_info, f, blksize, &vlabel);
+    close(f);
+    if (ret == 2)
+        return FALSE;
+    if (dasd_info.format == DASD_FORMAT_CDL) /* VOL1, CDL */
+        return FALSE;
+    else {
+        return TRUE;
+    }
+}
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error) {
+
+    gchar *tok = NULL;
+    gchar *tmptok = NULL;
+    gchar *prepend = NULL;
+    gchar *ret = NULL;
+    gchar *lcdev = NULL;
+
+    g_clear_error (error);
+
+    /* first make sure we're not being played */
+    if ((dev == NULL) || (!*dev))
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a device number or the number is invalid");
+
+    /* convert everything to lowercase */
+    lcdev = g_ascii_strdown (dev, strlen(dev));
+
+    /* we only care about the last piece of the device number, since
+     * that's the only part which will need formatting */
+    tok = g_strrstr(lcdev, ".");
+
+    if (tok) {
+        tmptok = &tok[1]; /* want to ignore the delimiter char */
+
+        /* calculate if/how much to pad tok with */
+        prepend = g_strnfill((4 - strlen(tmptok)), '0');
+    }
+    else {
+        tmptok = lcdev;
+    }
+
+    /* combine it all together */
+    if (prepend == NULL) {
+        ret = g_strdup_printf("0.0.%s", tmptok);
+    }
+    else {
+        ret = g_strdup_printf("0.0.%s%s", prepend, tmptok);
+    }
+    return ret;
+}
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error) {
+    gchar *fullwwpn = NULL;
+    gchar *lcwwpn = NULL;
+
+    g_clear_error (error);
+
+    /* first make sure we're not being played */
+    if ((wwpn == NULL) || (!*wwpn)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a WWPN or the WWPN is invalid");
+    }
+
+    /* convert everything to lowercase */
+    lcwwpn = g_ascii_strdown (wwpn, strlen(wwpn));
+
+    if (strncmp(lcwwpn, "0x", 2) == 0) {
+        /* user entered a properly formatted wwpn */
+        fullwwpn = g_strdup_printf("%s", lcwwpn);
+    }
+    else {
+        fullwwpn = g_strdup_printf("0x%s", lcwwpn);
+    }
+    return fullwwpn;
+}
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error) {
+    gchar *lclun = NULL;
+    gchar *tmplun = NULL;
+    gchar *fulllun = NULL;
+    gchar *prepend = NULL;
+    gchar *append = NULL;
+
+    g_clear_error (error);
+
+    /* first make sure we're not being played */
+    if ((lun == NULL) || (!*lun)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a LUN or the LUN is invalid");
+    }
+
+    /* convert everything to lowercase */
+    lclun = g_ascii_strdown (lun, strlen(lun));
+
+    if ((g_str_has_prefix(lclun, "0x")) && (strlen(lclun) == 18)) {
+        /* user entered a properly formatted lun */
+        fulllun = g_strdup_printf ("%s", lclun);
+    }
+    else {
+        /* we need to mangle the input to make it proper. ugh. */
+        if (g_str_has_prefix(lclun, "0x")) {
+            /* this may seem odd, but it makes the math easier a ways down */
+            tmplun = &lclun[2];
+        }
+        else {
+            tmplun = lclun;
+        }
+
+        if (strlen(tmplun) < 4) {
+            /* check if/how many zeros we pad to the left */
+            prepend = g_strnfill((4 - strlen(tmplun)), '0');
+            /* check if/how many zeros we pad to the right */
+            append = g_strnfill((16 - (strlen(tmplun) + strlen(prepend))), '0');
+        }
+        else {
+            /* didn't need to pad anything on the left; so just check if/how
+             * many zeros we pad to the right */
+            append = g_strnfill((16 - (strlen(tmplun))), '0');
+        }
+
+        /* now combine everything together */
+        if (prepend == NULL) {
+            fulllun = g_strdup_printf ("0x%s%s", tmplun, append);
+        }
+        else {
+            fulllun = g_strdup_printf ("0x%s%s%s", prepend, tmplun, append);
+        }
+    }
+    return fulllun;
+}
diff --git a/src/plugins/s390.h b/src/plugins/s390.h
new file mode 100644
index 0000000..9f041a8
--- /dev/null
+++ b/src/plugins/s390.h
@@ -0,0 +1,79 @@
+#include <glib.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <string.h>
+#include <utils.h>
+#include <asm/dasd.h>
+#include <s390utils/vtoc.h>
+
+
+GQuark bd_s390_error_quark (void);
+#define BD_S390_ERROR bd_s390_error_quark ()
+typedef enum {
+    BD_S390_ERROR_DEVICE,
+    BD_S390_ERROR_FORMAT_FAILED,
+    BD_S390_ERROR_DASDFMT,
+} BDS390Error;
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, whether it needs dasdfmt run on it
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error);
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_is_cdl:
+ * @dasd: dasd to check, whether it is CDL formatted
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd is CDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error);
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
index 5e47f03..404e5a8 100644
--- a/src/python/gi/overrides/BlockDev.py
+++ b/src/python/gi/overrides/BlockDev.py
@@ -45,6 +45,7 @@ bd_plugins = { "lvm": BlockDev.Plugin.LVM,
                "swap": BlockDev.Plugin.SWAP,
                "mdraid": BlockDev.Plugin.MDRAID,
                "mpath": BlockDev.Plugin.MPATH,
+               "s390": BlockDev.Plugin.S390,
 }
 
 _init = BlockDev.init
@@ -258,6 +259,48 @@ def swap_swapon(device, priority=-1):
 __all__.append("swap_swapon")
 
 
+_s390_dasd_format = BlockDev.s390_dasd_format
+@override(BlockDev.s390_dasd_format)
+def s390_dasd_format(dasd):
+    return _s390_dasd_format(dasd)
+__all__.append("s390_dasd_format")
+
+_s390_dasd_needs_format = BlockDev.s390_dasd_needs_format
+@override(BlockDev.s390_dasd_needs_format)
+def s390_dasd_needs_format(dasd):
+    return _s390_dasd_needs_format(dasd)
+__all__.append("s390_dasd_needs_format")
+
+_s390_dasd_online = BlockDev.s390_dasd_online
+@override(BlockDev.s390_dasd_online)
+def s390_dasd_online(dasd):
+    return _s390_dasd_online(dasd)
+__all__.append("s390_dasd_online")
+
+_s390_dasd_is_ldl = BlockDev.s390_dasd_is_ldl
+@override(BlockDev.s390_dasd_is_ldl)
+def s390_dasd_is_ldl(dasd):
+    return _s390_dasd_is_ldl(dasd)
+__all__.append("s390_dasd_is_ldl")
+
+_s390_sanitize_dev_input = BlockDev.s390_sanitize_dev_input
+@override(BlockDev.s390_sanitize_dev_input)
+def s390_sanitize_dev_input(dev):
+    return _s390_sanitize_dev_input(dev)
+__all__.append("s390_sanitize_dev_input")
+
+_s390_zfcp_sanitize_wwpn_input = BlockDev.s390_zfcp_sanitize_wwpn_input
+@override(BlockDev.s390_zfcp_sanitize_wwpn_input)
+def s390_zfcp_sanitize_wwpn_input(wwpn):
+    return _s390_zfcp_sanitize_wwpn_input(wwpn)
+__all__.append("s390_zfcp_sanitize_wwpn_input")
+
+_s390_zfcp_sanitize_lun_input = BlockDev.s390_zfcp_sanitize_lun_input
+@override(BlockDev.s390_zfcp_sanitize_lun_input)
+def s390_zfcp_sanitize_lun_input(lun):
+    return _s390_zfcp_sanitize_lun_input(lun)
+__all__.append("s390_zfcp_sanitize_lun_input")
+
 ## defined in this overrides only!
 def plugin_specs_from_names(plugin_names):
     ret = []
@@ -424,6 +467,10 @@ class SwapError(BlockDevError):
     pass
 __all__.append("SwapError")
 
+class S390Error(BlockDevError):
+    pass
+__all__.append("S390Error")
+
 class UtilsError(BlockDevError):
     pass
 __all__.append("UtilsError")
@@ -458,5 +505,8 @@ __all__.append("mpath")
 swap = ErrorProxy("swap", BlockDev, [(GLib.Error, SwapError)], [not_implemented_rule])
 __all__.append("swap")
 
+s390 = ErrorProxy("s390", BlockDev, [(GLib.Error, S390Error)], [not_implemented_rule])
+__all__.append("s390")
+
 utils = ErrorProxy("utils", BlockDev, [(GLib.Error, UtilsError)])
 __all__.append("utils")
-- 
2.4.0

