* New method lsm_scsi_disk_paths_of_vpd83() takes VPD83 ID to find out
all matching scsi disk paths(format:/dev/sd[a-z]+).
Signed-off-by: Gris Ge <***@redhat.com>
---
c_binding/Makefile.am | 8 +-
c_binding/include/libstoragemgmt/Makefile.am | 1 +
c_binding/include/libstoragemgmt/libstoragemgmt.h | 1 +
.../include/libstoragemgmt/libstoragemgmt_scsi.h | 55 +++
c_binding/lsm_scsi.c | 419 +++++++++++++++++++++
configure.ac | 1 +
6 files changed, 482 insertions(+), 3 deletions(-)
create mode 100644 c_binding/include/libstoragemgmt/libstoragemgmt_scsi.h
create mode 100644 c_binding/lsm_scsi.c
diff --git a/c_binding/Makefile.am b/c_binding/Makefile.am
index e4b8e1b..18da493 100644
--- a/c_binding/Makefile.am
+++ b/c_binding/Makefile.am
@@ -3,13 +3,15 @@ SUBDIRS = include
AM_CPPFLAGS = -I$(top_srcdir)/c_binding/include \
-I$(top_builddir)/c_binding/include \
-***@srcdir@/c_binding/include \
- $(LIBXML_CFLAGS) $(LIBGLIB_CFLAGS)
+ $(LIBXML_CFLAGS) $(LIBGLIB_CFLAGS) \
+ $(LIBUDEV_CFLAGS)
lib_LTLIBRARIES = libstoragemgmt.la
-libstoragemgmt_la_LIBADD=$(LIBXML_LIBS) $(YAJL_LIBS) $(LIBGLIB_LIBS)
+libstoragemgmt_la_LIBADD=$(LIBXML_LIBS) $(YAJL_LIBS) $(LIBGLIB_LIBS) \
+ $(LIBUDEV_LIBS)
libstoragemgmt_la_LDFLAGS= -version-info $(LIBSM_LIBTOOL_VERSION)
libstoragemgmt_la_SOURCES= \
lsm_mgmt.cpp lsm_datatypes.hpp lsm_datatypes.cpp lsm_convert.hpp \
lsm_convert.cpp lsm_ipc.hpp lsm_ipc.cpp lsm_plugin_ipc.hpp \
- lsm_plugin_ipc.cpp util/qparams.c util/qparams.h
+ lsm_plugin_ipc.cpp util/qparams.c util/qparams.h lsm_scsi.c
diff --git a/c_binding/include/libstoragemgmt/Makefile.am b/c_binding/include/libstoragemgmt/Makefile.am
index 2a947f6..f7b8dbd 100644
--- a/c_binding/include/libstoragemgmt/Makefile.am
+++ b/c_binding/include/libstoragemgmt/Makefile.am
@@ -23,6 +23,7 @@ lsminc_HEADERS = \
libstoragemgmt_targetport.h \
libstoragemgmt_types.h \
libstoragemgmt_version.h \
+ libstoragemgmt_scsi.h \
libstoragemgmt_volumes.h
diff --git a/c_binding/include/libstoragemgmt/libstoragemgmt.h b/c_binding/include/libstoragemgmt/libstoragemgmt.h
index b902bd8..8450434 100644
--- a/c_binding/include/libstoragemgmt/libstoragemgmt.h
+++ b/c_binding/include/libstoragemgmt/libstoragemgmt.h
@@ -34,6 +34,7 @@
#include "libstoragemgmt_systems.h"
#include "libstoragemgmt_targetport.h"
#include "libstoragemgmt_volumes.h"
+#include "libstoragemgmt_scsi.h"
/*! \mainpage libStorageMgmt
diff --git a/c_binding/include/libstoragemgmt/libstoragemgmt_scsi.h b/c_binding/include/libstoragemgmt/libstoragemgmt_scsi.h
new file mode 100644
index 0000000..a229692
--- /dev/null
+++ b/c_binding/include/libstoragemgmt/libstoragemgmt_scsi.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * This library 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.
+ *
+ * This library 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 this library; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <***@redhat.com>
+ *
+ */
+
+#ifndef LIBSTORAGEMGMT_SCSI_H
+#define LIBSTORAGEMGMT_SCSI_H
+
+#include "libstoragemgmt_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Find out the scsi disk paths of given SCSI VPD page 0x83 NAA ID.
+ * New in version 1.3.
+ * @param[in] vpd83 String. The VPD83 ID retrieved from LSM volume or disk.
+ * for supported strip sizes.
+ * @param[out] sd_path_list
+ * Output pointer of lsm_string_list. The format of scsi
+ * disk path will be "/dev/sd[a-z]+".
+ * NULL if no found or got error.
+ * Memory should be freed by lsm_string_list_free().
+ * @param[out] lsm_err
+ * Output pointer of lsm_error. Error message could be
+ * retrived via lsm_error_message_get(). Memory should be
+ * freed by lsm_error_free().
+ * @return LSM_ERR_OK on success.
+ * LSM_ERR_INVALID_ARGUMENT when any argument is NULL.
+ * LSM_ERR_NO_MEMORY when no memory.
+ * LSM_ERR_LIB_BUG when something unexpected happens.
+ */
+int LSM_DLL_EXPORT lsm_scsi_disk_paths_of_vpd83(const char *vpd83,
+ lsm_string_list **sd_path_list,
+ lsm_error **lsm_err);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIBSTORAGEMGMT_SCSI_H */
diff --git a/c_binding/lsm_scsi.c b/c_binding/lsm_scsi.c
new file mode 100644
index 0000000..a488f48
--- /dev/null
+++ b/c_binding/lsm_scsi.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * This library 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 3 of the License, or (at your option) any later version.
+ *
+ * This library 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 this library; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <***@redhat.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libudev.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "libstoragemgmt/libstoragemgmt.h"
+#include "libstoragemgmt/libstoragemgmt_error.h"
+#include "libstoragemgmt/libstoragemgmt_plug_interface.h"
+
+#define _MAX_VPD83_LEN 0xff + 4
+#define _MAX_NAA_ID_LEN 33 /* Max one is 6h IEEE Registered Extended ID */
+#define _SYS_BLOCK_PATH "/sys/block"
+
+#define _T10_VPD83_NAA_235_ID_LEN 8
+#define _T10_VPD83_NAA_6_ID_LEN 16
+#define _T10_VPD83_PAGE_CODE 0x83
+#define _T10_VPD83_DESIGNATOR_TYPE_NAA 0x3
+#define _T10_VPD83_NAA_TYPE_2 0x2
+#define _T10_VPD83_NAA_TYPE_3 0x3
+#define _T10_VPD83_NAA_TYPE_5 0x5
+#define _T10_VPD83_NAA_TYPE_6 0x6
+
+#define _LSM_ERR_MSG_LEN 255
+
+#pragma pack(1)
+/*
+ * Table 589 — Device Identification VPD page
+ */
+struct t10_vpd83_header {
+ uint8_t dev_type : 5;
+ /* ^ PERIPHERAL DEVICE TYPE */
+ uint8_t qualifier : 3;
+ /* PERIPHERAL QUALIFIER */
+ uint8_t page_code;
+ uint8_t page_len_msb;
+ uint8_t page_len_lsb;
+};
+
+/*
+ * Table 590 — Designation descriptor
+ */
+struct t10_vpd83_id_header {
+ uint8_t code_set : 4;
+ uint8_t protocol_id : 4;
+ uint8_t designator_type : 4;
+ uint8_t association : 2;
+ uint8_t reserved_1 : 1;
+ uint8_t piv : 1;
+ uint8_t reserved_2;
+ uint8_t len;
+};
+
+struct t10_vpd83_naa_header {
+ uint8_t data_msb : 4;
+ uint8_t naa_type : 4;
+};
+
+#pragma pack()
+
+static char _lsm_err_msg[_LSM_ERR_MSG_LEN];
+
+#define _lsm_err_msg_clear() memset(_lsm_err_msg, 0, _LSM_ERR_MSG_LEN)
+
+#define _lsm_err_msg_set(format, ...) \
+ snprintf(_lsm_err_msg, _LSM_ERR_MSG_LEN, format, ##__VA_ARGS__)
+
+#define _lsm_string_list_foreach(l, i, d) \
+ for(i = 0; \
+ (l != NULL) && (i < lsm_string_list_size(l)) && \
+ (d = lsm_string_list_elem_get(l, i)); \
+ ++i)
+
+static void _be_raw_to_hex(uint8_t *raw, size_t len, char *out);
+static int _sysfs_vpd83_naa_of_sd_name(const char *sd_name, char **vpd83);
+
+/*
+ * Input big-endian uint8_t array, output has hex string.
+ * No check on output memory size or bundery, make sure before invoke.
+ */
+static void _be_raw_to_hex(uint8_t *raw, size_t len, char *out)
+{
+ size_t i = 0;
+
+ for (; i < len; ++i) {
+ snprintf(out + (i * 2), 3, "%02x", raw[i]);
+ }
+ out[len * 2] = 0;
+
+}
+
+static int _sysfs_read_file(const char *sys_fs_path, uint8_t *buff,
+ ssize_t *size, size_t max_size)
+{
+ int fd = -1;
+
+ *size = 0;
+ memset(buff, 0, max_size);
+
+ fd = open(sys_fs_path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return LSM_ERR_NO_SUPPORT;
+
+ _lsm_err_msg_set("_sysfs_read_file(): Failed to open %s, error: %d, "
+ "%s\n", sys_fs_path, errno, strerror(errno));
+ return LSM_ERR_PLUGIN_BUG;
+ }
+ *size = read(fd, buff, max_size);
+ close(fd);
+
+ if (*size < 0) {
+ _lsm_err_msg_set("Failed to read %s, error: %d, %s\n", sys_fs_path,
+ errno, strerror(errno));
+ return LSM_ERR_PLUGIN_BUG;
+ }
+ return LSM_ERR_OK;
+}
+
+/*
+ * Parse /sys/block/sda/device/vpd_pg83 for VPD83 NAA ID.
+ * When no such sysfs file found, return LSM_ERR_NO_SUPPORT.
+ * When no NAA ID found, return LSM_ERR_OK and vpd83 as NULL.
+ * Return LSM_ERR_NO_MEMORY or LSM_ERR_NO_SUPPORT or LSM_ERR_PLUGIN_BUG
+ */
+static int _sysfs_vpd83_naa_of_sd_name(const char *sd_name, char **vpd83)
+{
+ char *sysfs_path = NULL;
+ ssize_t readed_size = 0;
+ uint8_t *end_p = NULL;
+ uint8_t *p = NULL;
+ struct t10_vpd83_header *vpd83_header = NULL;
+ uint32_t vpd83_len = 0;
+ struct t10_vpd83_id_header *id_header = NULL;
+ struct t10_vpd83_naa_header *naa_header = NULL;
+ int rc = LSM_ERR_OK;
+ uint8_t buff[_MAX_VPD83_LEN];
+ char vpd83_naa_id[_MAX_NAA_ID_LEN];
+
+ memset(vpd83_naa_id, 0, _MAX_NAA_ID_LEN);
+ *vpd83 = NULL;
+
+ if (sd_name == NULL) {
+ _lsm_err_msg_set("_sysfs_vpd83_naa_of_sd_name(): "
+ "Input sd_name argument is NULL");
+ rc = LSM_ERR_PLUGIN_BUG;
+ goto out;
+ }
+
+ sysfs_path = (char *) malloc(strlen("/sys/block//device/vpd_pg83") +
+ strlen(sd_name) + 1 /* trailing \0 */);
+ if (sysfs_path == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ sprintf(sysfs_path, "/sys/block/%s/device/vpd_pg83", sd_name);
+
+ rc = _sysfs_read_file(sysfs_path, buff, &readed_size, _MAX_VPD83_LEN);
+ free(sysfs_path);
+
+ if (rc != LSM_ERR_OK)
+ goto out;
+
+ /* Return NULL and LSM_ERR_OK when got invalid sysfs file */
+ /* Readed size is smaller than VPD83 header */
+ if (readed_size < sizeof(struct t10_vpd83_header))
+ goto out;
+
+ vpd83_header = (struct t10_vpd83_header*) buff;
+
+ /* Incorrect page code */
+ if (vpd83_header->page_code != _T10_VPD83_PAGE_CODE)
+ goto out;
+
+ vpd83_len = (((uint32_t) vpd83_header->page_len_msb) << 8) +
+ vpd83_header->page_len_lsb + sizeof(struct t10_vpd83_header);
+
+ end_p = buff + vpd83_len - 1;
+ p = buff + sizeof(struct t10_vpd83_header);
+
+ while(p <= end_p) {
+ /* Corrupted data: facing data end */
+ if (p + sizeof(struct t10_vpd83_id_header) > end_p)
+ goto out;
+
+ id_header = (struct t10_vpd83_id_header *) p;
+ /* Skip non-NAA ID */
+ if (id_header->designator_type != _T10_VPD83_DESIGNATOR_TYPE_NAA)
+ goto next_one;
+
+ naa_header = (struct t10_vpd83_naa_header *)
+ ((uint8_t *)id_header + sizeof(struct t10_vpd83_id_header));
+
+ switch(naa_header->naa_type) {
+ case _T10_VPD83_NAA_TYPE_2:
+ case _T10_VPD83_NAA_TYPE_3:
+ case _T10_VPD83_NAA_TYPE_5:
+ _be_raw_to_hex((uint8_t *) naa_header, _T10_VPD83_NAA_235_ID_LEN,
+ vpd83_naa_id);
+ break;
+ case _T10_VPD83_NAA_TYPE_6:
+ _be_raw_to_hex((uint8_t *) naa_header, _T10_VPD83_NAA_6_ID_LEN,
+ vpd83_naa_id);
+ break;
+ default:
+ /* Skip for Unknown NAA ID type */
+ goto next_one;
+ }
+ /* Quit when found first NAA ID */
+ if (vpd83_naa_id[0] != 0)
+ break;
+
+ next_one:
+ p = (uint8_t *) id_header + id_header->len +
+ sizeof(struct t10_vpd83_id_header);
+ continue;
+ }
+
+ if (vpd83_naa_id[0] == 0)
+ goto out;
+
+ *vpd83 = strdup(vpd83_naa_id);
+ if (*vpd83 == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ out:
+ return rc;
+}
+
+/*
+ * Try to parse /sys/block/sda/device/vpd_pg83 for VPD83 NAA ID frist.
+ * This sysfs file is missing in some older kenrel(like RHEL6), we use udev
+ * ID_WWN_WITH_EXTENSION property instead.
+ */
+static int _udev_vpd83_of_sd_name(const char *sd_name, char **vpd83)
+{
+ struct udev *udev = NULL;
+ struct udev_device *sd_udev = NULL;
+ int rc = LSM_ERR_OK;
+ char *sys_path = NULL;
+ const char *wwn = NULL;
+
+ *vpd83 = NULL;
+
+ sys_path = (char *) malloc(strlen("/sys/block/") + strlen(sd_name) +
+ 1 /* trailing \0 */);
+ if (sys_path == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ sprintf(sys_path, "/sys/block/%s", sd_name);
+
+ udev = udev_new();
+ if (udev == NULL) {
+ rc = LSM_ERR_NO_MEMORY;
+ goto out;
+ }
+
+ sd_udev = udev_device_new_from_syspath(udev, sys_path);
+ if (sd_udev == NULL) {
+ rc = LSM_ERR_NO_SUPPORT;
+ goto out;
+ }
+ wwn = udev_device_get_property_value(sd_udev, "ID_WWN_WITH_EXTENSION");
+ if (wwn == NULL)
+ goto out;
+
+ if (strncmp(wwn, "0x", strlen("0x")) == 0)
+ wwn += strlen("0x");
+
+ *vpd83 = strdup(wwn);
+ if (*vpd83 == NULL)
+ rc = LSM_ERR_NO_MEMORY;
+
+ out:
+ if (udev != NULL)
+ udev_unref(udev);
+
+ if (sd_udev != NULL)
+ udev_device_unref(sd_udev);
+
+ return rc;
+}
+
+static int _sysfs_get_all_sd_names(lsm_string_list **sd_name_list)
+{
+ DIR *dir = NULL;
+ struct dirent *dp = NULL;
+
+ *sd_name_list = lsm_string_list_alloc(0 /* no pre-allocation */);
+ if (*sd_name_list == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ dir = opendir(_SYS_BLOCK_PATH);
+ if (dir == NULL) {
+ lsm_string_list_free(*sd_name_list);
+ *sd_name_list = NULL;
+ _lsm_err_msg_set("Cannot optn %s", _SYS_BLOCK_PATH);
+ return LSM_ERR_PLUGIN_BUG;
+ }
+
+ do {
+ if ((dp = readdir(dir)) != NULL) {
+ if (strncmp(dp->d_name, "sd", strlen("sd")) != 0)
+ continue;
+ if (lsm_string_list_append(*sd_name_list, dp->d_name) != 0)
+ return LSM_ERR_NO_MEMORY;
+ }
+ } while(dp != NULL);
+
+ closedir(dir);
+
+ return LSM_ERR_OK;
+}
+
+int lsm_scsi_disk_paths_of_vpd83(const char *vpd83,
+ lsm_string_list **sd_path_list,
+ lsm_error **lsm_err)
+{
+ int rc = LSM_ERR_OK;
+ lsm_string_list *sd_name_list = NULL;
+ uint32_t i = 0;
+ const char *sd_name = NULL;
+ char *tmp_vpd83 = NULL;
+ char *sd_path = NULL;
+ bool sysfs_support = true;
+
+ _lsm_err_msg_clear();
+
+ if ((vpd83 == NULL) || (sd_path_list == NULL) || (lsm_err == NULL)) {
+ if (sd_path_list != NULL)
+ *sd_path_list = NULL;
+
+ if (lsm_err != NULL)
+ *lsm_err = NULL;
+
+ return LSM_ERR_INVALID_ARGUMENT;
+ }
+
+ *lsm_err = NULL;
+ *sd_path_list = lsm_string_list_alloc(0 /* no pre-allocation */);
+ if (*sd_path_list == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ rc = _sysfs_get_all_sd_names(&sd_name_list);
+ if (rc != LSM_ERR_OK)
+ goto out;
+
+ _lsm_string_list_foreach(sd_name_list, i, sd_name) {
+ if (sd_name == NULL)
+ continue;
+ if (sysfs_support == true) {
+ rc = _sysfs_vpd83_naa_of_sd_name(sd_name, &tmp_vpd83);
+ if (rc == LSM_ERR_NO_SUPPORT)
+ sysfs_support = false;
+ else if (rc != LSM_ERR_OK)
+ break;
+ }
+ /* Try udev way if got NO_SUPPORT from sysfs way. */
+ if (sysfs_support == false) {
+ rc = _udev_vpd83_of_sd_name(sd_name, &tmp_vpd83);
+ if (rc != LSM_ERR_OK)
+ break;
+ }
+ if (tmp_vpd83 == NULL)
+ continue;
+ if (strcmp(vpd83, tmp_vpd83) == 0) {
+ sd_path = (char *) malloc(strlen("/dev/") + strlen(sd_name) +
+ 1 /* trailing \0 */);
+ if (sd_path == NULL)
+ return LSM_ERR_NO_MEMORY;
+
+ sprintf(sd_path, "/dev/%s", sd_name);
+
+ if (lsm_string_list_append(*sd_path_list, sd_path) != 0)
+ return LSM_ERR_NO_MEMORY;
+ free(sd_path);
+ sd_path = NULL;
+ }
+ free(tmp_vpd83);
+ tmp_vpd83 = NULL;
+ }
+
+ out:
+ if (sd_name_list != NULL)
+ lsm_string_list_free(sd_name_list);
+
+ if (rc != LSM_ERR_OK) {
+ *lsm_err = LSM_ERROR_CREATE_PLUGIN_MSG(rc, _lsm_err_msg);
+
+ if (*sd_path_list != NULL)
+ lsm_string_list_free(*sd_path_list);
+
+ *sd_path_list = NULL;
+ }
+ return rc;
+}
diff --git a/configure.ac b/configure.ac
index beae390..908b3cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -162,6 +162,7 @@ AM_PATH_PYTHON([2.6], [], AC_MSG_ERROR([Python interpreter 2.6 or 2.7 required])
AC_PYTHON_MODULE([pywbem], [Required])
AC_PYTHON_MODULE([M2Crypto], [Required])
AC_PYTHON_MODULE([argparse], [Required])
+PKG_CHECK_MODULES([LIBUDEV], [libudev])
dnl ==========================================================================
dnl Check for libmicrohttpd and json-c as it is needed for REST API daemon
--
1.8.3.1