Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137816296
D31809.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
D31809.diff
View Options
diff --git a/tests/sys/Makefile b/tests/sys/Makefile
--- a/tests/sys/Makefile
+++ b/tests/sys/Makefile
@@ -28,6 +28,7 @@
TESTS_SUBDIRS+= netpfil
TESTS_SUBDIRS+= opencrypto
TESTS_SUBDIRS+= posixshm
+TESTS_SUBDIRS+= ses
TESTS_SUBDIRS+= sys
TESTS_SUBDIRS+= vfs
TESTS_SUBDIRS+= vm
diff --git a/tests/sys/ses/Makefile b/tests/sys/ses/Makefile
new file mode 100644
--- /dev/null
+++ b/tests/sys/ses/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/ses
+
+ATF_TESTS_C+= destructive
+ATF_TESTS_C+= nondestructive
+
+# Some tests cases alter enclosure state, so they can't run concurrently.
+TEST_METADATA.destructive+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/ses/common.h b/tests/sys/ses/common.h
new file mode 100644
--- /dev/null
+++ b/tests/sys/ses/common.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+typedef bool(*ses_cb)(const char *devname, int fd);
+
+// Run a test function on every available ses device
+static void
+for_each_ses_dev(ses_cb cb, int oflags)
+{
+ glob_t g;
+ int r;
+ unsigned i;
+ bool tested = false;
+
+ g.gl_pathc = 0;
+ g.gl_pathv = NULL;
+ g.gl_offs = 0;
+
+ r = glob("/dev/ses*", GLOB_NOSORT, NULL, &g);
+ ATF_REQUIRE_EQ(r, 0);
+
+ for(i = 0; i < g.gl_pathc; i++) {
+ int fd;
+
+ fd = open(g.gl_pathv[i], oflags);
+ ATF_REQUIRE(fd >= 0);
+ tested |= cb(g.gl_pathv[i], fd);
+ close(fd);
+ }
+
+ if (!tested)
+ atf_tc_skip("No supported devices found");
+
+ globfree(&g);
+}
diff --git a/tests/sys/ses/destructive.c b/tests/sys/ses/destructive.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/ses/destructive.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Tests that alter an enclosure's state */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cam/scsi/scsi_enc.h>
+
+#include "common.h"
+
+// Run a test function on just one ses device
+static void
+for_one_ses_dev(ses_cb cb)
+{
+ glob_t g;
+ int fd, r;
+
+ g.gl_pathc = 0;
+ g.gl_pathv = NULL;
+ g.gl_offs = 0;
+
+ r = glob("/dev/ses*", GLOB_NOSORT, NULL, &g);
+ ATF_REQUIRE_EQ(r, 0);
+ if (g.gl_pathc == 0)
+ atf_tc_skip("No ses devices found");
+
+ fd = open(g.gl_pathv[0], O_RDWR);
+ ATF_REQUIRE(fd >= 0);
+ cb(g.gl_pathv[0], fd);
+ close(fd);
+
+ globfree(&g);
+}
+
+static bool do_setelmstat(const char *devname __unused, int fd) {
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+
+ /* Set the IDENT bit for every disk slot */
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_ctrl_dev_slot *cslot;
+ struct ses_status_dev_slot *sslot;
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+
+ cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
+ sslot = (struct ses_status_dev_slot*)&elmstat.cstat[0];
+
+ ses_ctrl_common_set_select(&cslot->common, 1);
+ ses_ctrl_dev_slot_set_rqst_ident(cslot, 1);
+ r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ }
+ }
+
+ /* Check the IDENT bit for every disk slot */
+ last_elm_type = -1;
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_status_dev_slot *sslot;
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ int i;
+
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+
+ sslot = (struct ses_status_dev_slot*)&elmstat.cstat[0];
+
+ for (i = 0; i < 10; i++) {
+ r = ioctl(fd, ENCIOC_GETELMSTAT,
+ (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ if (0 == ses_status_dev_slot_get_ident(sslot)) {
+ /* Needs more time to take effect */
+ usleep(100000);
+ }
+ }
+ ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0);
+
+ }
+ }
+
+ free(map);
+ return (true);
+}
+
+/*
+ * sg_ses doesn't provide "dump and restore" functionality. The closest is to
+ * dump status page 2, then manually edit the file to set every individual
+ * element select bit, then load the entire file. But that is much too hard.
+ * Instead, we'll just clear every ident bit.
+ */
+static bool
+do_setelmstat_cleanup(const char *devname __unused, int fd __unused) {
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+
+ /* Clear the IDENT bit for every disk slot */
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_ctrl_dev_slot *cslot;
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
+
+ ses_ctrl_common_set_select(&cslot->common, 1);
+ ses_ctrl_dev_slot_set_rqst_ident(cslot, 0);
+ r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
+ }
+ }
+
+ return(true);
+}
+
+
+ATF_TC_WITH_CLEANUP(setelmstat);
+ATF_TC_HEAD(setelmstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(setelmstat, tc)
+{
+ for_one_ses_dev(do_setelmstat);
+}
+ATF_TC_CLEANUP(setelmstat, tc)
+{
+ for_one_ses_dev(do_setelmstat_cleanup);
+}
+
+
+static bool do_setencstat(const char *devname __unused, int fd) {
+ unsigned char encstat;
+ int r, i;
+ bool worked = false;
+
+ /*
+ * SES provides no way to read the current setting of the enclosure
+ * control page common status bits. So we'll blindly set CRIT.
+ */
+ encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT;
+ r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
+ ATF_REQUIRE_EQ(r, 0);
+
+ /* Check that the status has changed */
+ for (i = 0; i < 10; i++) {
+ r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat);
+ ATF_REQUIRE_EQ(r, 0);
+ if (encstat & SES_CTRL_PAGE_CRIT_MASK) {
+ worked = true;
+ break;
+ }
+ usleep(100000);
+ }
+ if (!worked) {
+ /* Some enclosures don't support setting the enclosure status */
+ return (false);
+ } else
+ return (true);
+}
+
+static bool do_setencstat_cleanup(const char *devname __unused, int fd) {
+ unsigned char encstat;
+
+ /*
+ * SES provides no way to read the current setting of the enclosure
+ * control page common status bits. So we don't know what they were
+ * set to before the test. We'll blindly clear all bits.
+ */
+ encstat = 0;
+ ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
+ return (true);
+}
+
+ATF_TC_WITH_CLEANUP(setencstat);
+ATF_TC_HEAD(setencstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(setencstat, tc)
+{
+ for_each_ses_dev(do_setencstat, O_RDWR);
+}
+ATF_TC_CLEANUP(setencstat, tc)
+{
+ for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /*
+ * Untested ioctls:
+ *
+ * * ENCIOC_INIT because SES doesn't need it and I don't have any
+ * SAF-TE devices.
+ *
+ * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally
+ * used for stuff like firmware updates
+ */
+ ATF_TP_ADD_TC(tp, setelmstat);
+ ATF_TP_ADD_TC(tp, setencstat);
+ // TODO ENCIOC_SETELMSTAT
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/ses/nondestructive.c b/tests/sys/ses/nondestructive.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/ses/nondestructive.c
@@ -0,0 +1,636 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Basic smoke test of the ioctl interface */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cam/scsi/scsi_enc.h>
+
+#include "common.h"
+
+static bool do_getelmdesc(const char *devname, int fd) {
+ regex_t re;
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ char *actual;
+ unsigned nobj;
+ unsigned elm_idx = 0;
+ int r;
+
+ actual = calloc(UINT16_MAX, sizeof(char));
+ ATF_REQUIRE(actual != NULL);
+ r = regcomp(&re, "(Overall|Element [0-9]+) descriptor: ", REG_EXTENDED);
+ ATF_REQUIRE_EQ(r, 0);
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p7 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ regmatch_t matches[1];
+ encioc_elm_desc_t e_desc;
+ char *expected;
+ size_t elen;
+
+ if (regexec(&re, line, 1, matches, 0) == REG_NOMATCH) {
+ continue;
+ }
+
+ expected = &line[matches[0].rm_eo];
+ /* Remove trailing newline */
+ elen = strnlen(expected, sizeof(line) - matches[0].rm_eo);
+ expected[elen - 1] = '\0';
+ /*
+ * Zero the result string. XXX we wouldn't have to do this if
+ * the kernel would nul-terminate the result.
+ */
+ memset(actual, 0, UINT16_MAX);
+ e_desc.elm_idx = elm_idx;
+ e_desc.elm_desc_len = UINT16_MAX;
+ e_desc.elm_desc_str = actual;
+ r = ioctl(fd, ENCIOC_GETELMDESC, (caddr_t) &e_desc);
+ ATF_REQUIRE_EQ(r, 0);
+ if (0 == strcmp("<empty>", expected)) {
+ /* sg_ses replaces "" with "<empty>" */
+ ATF_CHECK_STREQ("", actual);
+ } else
+ ATF_CHECK_STREQ(expected, actual);
+ elm_idx++;
+ }
+
+ r = pclose(pipe);
+ regfree(&re);
+ free(actual);
+ if (r != 0) {
+ /* Probably an SGPIO device */
+
+ return (false);
+ } else {
+ ATF_CHECK_EQ_MSG(nobj, elm_idx,
+ "Did not find the expected number of element "
+ "descriptors in sg_ses's output");
+ return (true);
+ }
+}
+
+ATF_TC(getelmdesc);
+ATF_TC_HEAD(getelmdesc, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMDESC's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmdesc, tc)
+{
+ for_each_ses_dev(do_getelmdesc, O_RDONLY);
+}
+
+static bool do_getelmdevnames(const char *devname __unused, int fd) {
+ encioc_element_t *map;
+ unsigned nobj;
+ const size_t namesize = 128;
+ int r;
+ char *namebuf;
+ unsigned elm_idx;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ namebuf = calloc(namesize, sizeof(char));
+ ATF_REQUIRE(namebuf != NULL);
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+ ATF_REQUIRE_EQ(r, 0);
+
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ /*
+ * devnames should be present if:
+ * * The element is of type Device Slot or Array Device Slot
+ * * It isn't an Overall Element
+ * * The element's status is not "Not Installed"
+ */
+ encioc_elm_status_t e_status;
+ encioc_elm_devnames_t elmdn;
+
+ memset(&e_status, 0, sizeof(e_status));
+ e_status.elm_idx = elm_idx;
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
+ ATF_REQUIRE_EQ(r, 0);
+
+ memset(&elmdn, 0, sizeof(elmdn));
+ elmdn.elm_idx = elm_idx;
+ elmdn.elm_names_size = namesize;
+ elmdn.elm_devnames = namebuf;
+ namebuf[0] = '\0';
+ r = ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t) &elmdn);
+ if (e_status.cstat[0] != SES_OBJSTAT_UNSUPPORTED &&
+ e_status.cstat[0] != SES_OBJSTAT_NOTINSTALLED &&
+ (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV))
+ {
+ ATF_CHECK_EQ_MSG(r, 0, "devnames not found. This could be due to a buggy ses driver, buggy ses controller, dead HDD, or an ATA HDD in a SAS slot");
+ } else {
+ ATF_CHECK(r != 0);
+ }
+
+ if (r == 0) {
+ size_t z = 0;
+ int da = 0, ada = 0, pass = 0, nvd = 0;
+ int nvme = 0, unknown = 0;
+
+ while(elmdn.elm_devnames[z] != '\0') {
+ size_t e;
+ char *s;
+
+ if (elmdn.elm_devnames[z] == ',')
+ z++; /* Skip the comma */
+ s = elmdn.elm_devnames + z;
+ e = strcspn(s, "0123456789");
+ if (0 == strncmp("da", s, e))
+ da++;
+ else if (0 == strncmp("ada", s, e))
+ ada++;
+ else if (0 == strncmp("pass", s, e))
+ pass++;
+ else if (0 == strncmp("nvd", s, e))
+ nvd++;
+ else if (0 == strncmp("nvme", s, e))
+ nvme++;
+ else
+ unknown++;
+ z += strcspn(elmdn.elm_devnames + z, ",");
+ }
+ /* There should be one pass dev for each non-pass dev */
+ ATF_CHECK_EQ(pass, da + ada + nvd + nvme);
+ ATF_CHECK_EQ_MSG(0, unknown,
+ "Unknown device names %s", elmdn.elm_devnames);
+ }
+ }
+ free(map);
+ free(namebuf);
+
+ return (true);
+}
+
+ATF_TC(getelmdevnames);
+ATF_TC_HEAD(getelmdevnames, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMDEVNAMES's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmdevnames, tc)
+{
+ for_each_ses_dev(do_getelmdevnames, O_RDONLY);
+}
+
+static int
+elm_type_name2int(const char *name) {
+ const char *elm_type_names[] = ELM_TYPE_NAMES;
+ int i;
+
+ for (i = 0; i <= ELMTYP_LAST; i++) {
+ /* sg_ses uses different case than ses(4) */
+ if (0 == strcasecmp(name, elm_type_names[i]))
+ return i;
+ }
+ return (-1);
+}
+
+static bool do_getelmmap(const char *devname, int fd) {
+ encioc_element_t *map;
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ unsigned elm_idx = 0;
+ unsigned nobj, subenc_id;
+ int r, elm_type;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+ ATF_REQUIRE_EQ(r, 0);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ char elm_type_name[80];
+ int i, num_elm;
+
+ r = sscanf(line,
+ " Element type: %[a-zA-Z0-9_ /], subenclosure id: %d",
+ elm_type_name, &subenc_id);
+ if (r == 2) {
+ elm_type = elm_type_name2int(elm_type_name);
+ continue;
+ } else {
+ r = sscanf(line,
+ " Element type: vendor specific [0x%x], subenclosure id: %d",
+ &elm_type, &subenc_id);
+ if (r == 2)
+ continue;
+ }
+ r = sscanf(line, " number of possible elements: %d",
+ &num_elm);
+ if (r != 1)
+ continue;
+
+ /* Skip the Overall elements */
+ elm_idx++;
+ for (i = 0; i < num_elm; i++, elm_idx++) {
+ ATF_CHECK_EQ(map[elm_idx].elm_idx, elm_idx);
+ ATF_CHECK_EQ(map[elm_idx].elm_subenc_id, subenc_id);
+ ATF_CHECK_EQ((int)map[elm_idx].elm_type, elm_type);
+ }
+ }
+
+ free(map);
+ r = pclose(pipe);
+ if (r != 0) {
+ /* Probably an SGPIO device */
+ return (false);
+ } else {
+ ATF_CHECK_EQ_MSG(nobj, elm_idx,
+ "Did not find the expected number of element "
+ "descriptors in sg_ses's output");
+ return (true);
+ }
+}
+
+ATF_TC(getelmmap);
+ATF_TC_HEAD(getelmmap, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMMAP's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmmap, tc)
+{
+ for_each_ses_dev(do_getelmmap, O_RDONLY);
+}
+
+static bool do_getelmstat(const char *devname, int fd) {
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r, elm_subidx;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+
+ for (elm_idx = 0; elm_idx < nobj; elm_subidx++, elm_idx++) {
+ encioc_elm_status_t e_status;
+ FILE *pipe;
+ char cmd[256];
+ uint32_t status;
+ int pr;
+
+ if (last_elm_type != map[elm_idx].elm_type)
+ elm_subidx = -1;
+ last_elm_type = map[elm_idx].elm_type;
+
+ snprintf(cmd, sizeof(cmd),
+ "sg_ses -Hp2 --index=_%d,%d --get=0:7:32 %s",
+ map[elm_idx].elm_type, elm_subidx, devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ r = fscanf(pipe, "0x%x", &status);
+ pr = pclose(pipe);
+ if (pr != 0) {
+ /* Probably an SGPIO device */
+ free(map);
+ return (false);
+ }
+ ATF_REQUIRE_EQ(r, 1);
+
+ memset(&e_status, 0, sizeof(e_status));
+ e_status.elm_idx = map[elm_idx].elm_idx;
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
+ ATF_REQUIRE_EQ(r, 0);
+
+ // Compare the common status field
+ ATF_CHECK_EQ(e_status.cstat[0], status >> 24);
+ /*
+ * Ignore the other fields, because some have values that can
+ * change frequently (voltage, temperature, etc)
+ */
+ }
+ free(map);
+
+ return (true);
+}
+
+ATF_TC(getelmstat);
+ATF_TC_HEAD(getelmstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMSTAT's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmstat, tc)
+{
+ for_each_ses_dev(do_getelmstat, O_RDONLY);
+}
+
+static bool do_getencid(const char *devname, int fd) {
+ encioc_string_t stri;
+ FILE *pipe;
+ char cmd[256];
+ char encid[32];
+ char line[256];
+ char sg_encid[32];
+ int r, sg_ses_r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ sg_encid[0] = '\0';
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ const char *f = " enclosure logical identifier (hex): %s";
+
+ if (1 == fscanf(pipe, f, sg_encid))
+ break;
+ }
+ sg_ses_r = pclose(pipe);
+
+ stri.bufsiz = sizeof(encid);
+ stri.buf = &encid[0];
+ r = ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri);
+ ATF_REQUIRE_EQ(r, 0);
+ if (sg_ses_r == 0) {
+ ATF_REQUIRE(sg_encid[0] != '\0');
+ ATF_CHECK_STREQ(sg_encid, (char*)stri.buf);
+ return (true);
+ } else {
+ /* Probably SGPIO; sg_ses unsupported */
+ return (false);
+ }
+}
+
+ATF_TC(getencid);
+ATF_TC_HEAD(getencid, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCID's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getencid, tc)
+{
+ for_each_ses_dev(do_getencid, O_RDONLY);
+}
+
+static bool do_getencname(const char *devname, int fd) {
+ encioc_string_t stri;
+ FILE *pipe;
+ char cmd[256];
+ char encname[32];
+ char line[256];
+ int r;
+
+ snprintf(cmd, sizeof(cmd), "sg_inq -o %s | awk '"
+ "/Vendor identification/ {vi=$NF} "
+ "/Product identification/ {pi=$NF} "
+ "/Product revision level/ {prl=$NF} "
+ "END {printf(vi \" \" pi \" \" prl)}'", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ ATF_REQUIRE(NULL != fgets(line, sizeof(line), pipe));
+ pclose(pipe);
+
+ stri.bufsiz = sizeof(encname);
+ stri.buf = &encname[0];
+ r = ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri);
+ ATF_REQUIRE_EQ(r, 0);
+ if (strlen(line) < 3) {
+ // Probably an SGPIO device, INQUIRY unsupported
+ return (false);
+ } else {
+ ATF_CHECK_STREQ(line, (char*)stri.buf);
+ return (true);
+ }
+}
+
+ATF_TC(getencname);
+ATF_TC_HEAD(getencname, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCNAME's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_inq");
+}
+ATF_TC_BODY(getencname, tc)
+{
+ for_each_ses_dev(do_getencname, O_RDONLY);
+}
+
+static bool do_getencstat(const char *devname, int fd) {
+ FILE *pipe;
+ char cmd[256];
+ unsigned char e, estat, invop, info, noncrit, crit, unrecov;
+ int r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p2 %s "
+ "| grep 'INVOP='",
+ devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ r = fscanf(pipe,
+ " INVOP=%hhu, INFO=%hhu, NON-CRIT=%hhu, CRIT=%hhu, UNRECOV=%hhu",
+ &invop, &info, &noncrit, &crit, &unrecov);
+ pclose(pipe);
+ if (r != 5) {
+ /* Probably on SGPIO device */
+ return (false);
+ } else {
+ r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat);
+ ATF_REQUIRE_EQ(r, 0);
+ /* Exclude the info bit because it changes frequently */
+ e = (invop << 4) | (noncrit << 2) | (crit << 1) | unrecov;
+ ATF_CHECK_EQ(estat & ~0x08, e);
+ return (true);
+ }
+}
+
+ATF_TC(getencstat);
+ATF_TC_HEAD(getencstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCSTAT's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getencstat, tc)
+{
+ for_each_ses_dev(do_getencstat, O_RDONLY);
+}
+
+static bool do_getnelm(const char *devname, int fd) {
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ unsigned nobj, expected = 0;
+ int r, sg_ses_r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ unsigned nelm;
+
+ if (1 == fscanf(pipe, " number of possible elements: %u",
+ &nelm))
+ {
+ expected += 1 + nelm; // +1 for the Overall element
+ }
+ }
+ sg_ses_r = pclose(pipe);
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+ if (sg_ses_r == 0) {
+ ATF_CHECK_EQ(expected, nobj);
+ return (true);
+ } else {
+ /* Probably SGPIO, sg_ses unsupported */
+ return (false);
+ }
+}
+
+ATF_TC(getnelm);
+ATF_TC_HEAD(getnelm, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETNELM's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getnelm, tc)
+{
+ for_each_ses_dev(do_getnelm, O_RDONLY);
+}
+
+static bool do_getstring(const char *devname, int fd) {
+ FILE *pipe;
+ char cmd[256];
+ char *sg_ses_buf, *ses_buf;
+ ssize_t sg_ses_count;
+ encioc_string_t str_in;
+ int r;
+
+ sg_ses_buf = malloc(65535);
+ ATF_REQUIRE(sg_ses_buf != NULL);
+ ses_buf = malloc(65535);
+ ATF_REQUIRE(ses_buf != NULL);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p4 -rr %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ sg_ses_count = fread(sg_ses_buf, 1, 65535, pipe);
+ r = pclose(pipe);
+ if (r != 0) {
+ // This SES device does not support the STRINGIN diagnostic page
+ return (false);
+ }
+ ATF_REQUIRE(sg_ses_count > 0);
+
+ str_in.bufsiz = 65535;
+ str_in.buf = ses_buf;
+ r = ioctl(fd, ENCIOC_GETSTRING, (caddr_t) &str_in);
+ ATF_REQUIRE_EQ(r, 0);
+ ATF_CHECK_EQ(sg_ses_count, (ssize_t)str_in.bufsiz);
+ ATF_CHECK_EQ(0, memcmp(sg_ses_buf, ses_buf, str_in.bufsiz));
+
+ free(ses_buf);
+ free(sg_ses_buf);
+
+ return (true);
+}
+
+ATF_TC(getstring);
+ATF_TC_HEAD(getstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETSTRING's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getstring, tc)
+{
+ atf_tc_expect_fail("Bug 258188 ENCIO_GETSTRING does not set the string's returned size");
+ for_each_ses_dev(do_getstring, O_RDWR);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /*
+ * Untested ioctls:
+ *
+ * * ENCIOC_GETTEXT because it was never implemented
+ *
+ */
+ ATF_TP_ADD_TC(tp, getelmdesc);
+ ATF_TP_ADD_TC(tp, getelmdevnames);
+ ATF_TP_ADD_TC(tp, getelmmap);
+ ATF_TP_ADD_TC(tp, getelmstat);
+ ATF_TP_ADD_TC(tp, getencid);
+ ATF_TP_ADD_TC(tp, getencname);
+ ATF_TP_ADD_TC(tp, getencstat);
+ ATF_TP_ADD_TC(tp, getnelm);
+ ATF_TP_ADD_TC(tp, getstring);
+
+ return (atf_no_error());
+}
diff --git a/usr.sbin/sesutil/sesutil.c b/usr.sbin/sesutil/sesutil.c
--- a/usr.sbin/sesutil/sesutil.c
+++ b/usr.sbin/sesutil/sesutil.c
@@ -484,6 +484,7 @@
memset(&e_desc, 0, sizeof(e_desc));
e_desc.elm_idx = e_ptr[j].elm_idx;
e_desc.elm_desc_len = UINT16_MAX;
+ /* XXX memory leak! */
e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char));
if (e_desc.elm_desc_str == NULL) {
close(fd);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 27, 2:22 AM (15 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26220166
Default Alt Text
D31809.diff (28 KB)
Attached To
Mode
D31809: Add tests for ses(4)
Attached
Detach File
Event Timeline
Log In to Comment