aboutsummaryrefslogtreecommitdiff
path: root/smart.c
diff options
context:
space:
mode:
Diffstat (limited to 'smart.c')
-rw-r--r--smart.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/smart.c b/smart.c
new file mode 100644
index 000000000000..8b543fea1819
--- /dev/null
+++ b/smart.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#ifdef LIBXO
+#include <libxo/xo.h>
+#endif
+
+#include "libsmart.h"
+
+#define SMART_NAME "smart"
+#define SMART_VERSION "1.0.2"
+
+extern bool do_debug;
+
+static const char *pn;
+bool do_debug = false;
+static int debugset = 0;
+
+static struct option opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "threshold", no_argument, NULL, 't' },
+ { "hex", no_argument, NULL, 'x' },
+ { "attribute", required_argument, NULL, 'a' },
+ { "info", no_argument, NULL, 'i' },
+ { "version", no_argument, NULL, 'v' },
+ { "decode", no_argument, NULL, 'd' },
+ { "no-decode", no_argument, NULL, 'D' },
+ { "debug", no_argument, &debugset, 1 },
+ { NULL, 0, NULL, 0 }
+};
+
+static void
+usage(const char *name)
+{
+ printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
+ printf("\t-h, --help\n");
+ printf("\t-t, --threshold : also print out the threshold values\n");
+ printf("\t-x, --hex : print the values out in hexadecimal\n");
+ printf("\t-a, --attribute : print specified attribute(s)\n");
+ printf("\t-i, --info : print general device information\n");
+ printf("\t-d, --decode: decode the attribute IDs\n");
+ printf("\t-D, --no-decode: don't decode the attribute IDs\n");
+ printf("\t-v, --version : print the version and copyright\n");
+ printf("\t --debug : output diagnostic information\n");
+}
+
+/*
+ * Convert string to an integer
+ *
+ * Returns -1 on error, converted value otherwise
+ */
+static int32_t
+get_val(char *attr, char **next)
+{
+ char *sep = NULL;
+ long val;
+
+ *next = NULL;
+
+ val = strtol(attr, &sep, 0);
+ if ((val == 0) && (errno != 0)) {
+ printf("Error parsing attribute %s", attr);
+ switch (errno) {
+ case EINVAL:
+ printf(" (not a number?)\n");
+ break;
+ case ERANGE:
+ printf(" (value out of range)\n");
+ break;
+ default:
+ printf("\n");
+ }
+ return -1;
+ }
+
+ if (val > INT32_MAX) {
+ printf("Attribute value %ld too big\n", val);
+ return -1;
+ }
+
+ *next = sep;
+ return ((int32_t)val);
+}
+
+/*
+ * Create a match specification from the given attribute
+ *
+ * Attribute format is
+ * <Page ID>:<Attribute ID>
+ * where page and attribute IDs are integers. If the page ID is missing,
+ * match the specified attribute ID on any page (i.e. -1). Valid forms are
+ * <int>:<int>
+ * :<int>
+ * <int>
+ *
+ * Returns 0 on success
+ */
+static int
+add_match(smart_matches_t **matches, char *attr)
+{
+ char *next;
+ int32_t page = -1, id;
+ uint32_t count = 0;
+
+ id = get_val(attr, &next);
+ if (id < 0)
+ return id;
+
+ if (*next == ':') {
+ page = id;
+ id = get_val(next + 1, &next);
+ if (id < 0)
+ return id;
+ }
+
+ if (*matches == NULL) {
+ *matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
+ if (*matches == NULL)
+ return ENOMEM;
+ } else {
+ void *tmp;
+
+ count = (*matches)->count;
+ tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
+ if (tmp == NULL)
+ return ENOMEM;
+ *matches = tmp;
+ }
+
+ (*matches)->m[count].page = page;
+ (*matches)->m[count].id = id;
+ (*matches)->count++;
+ return 0;
+}
+
+/*
+ * Parse the comma separated list of attributes to match
+ *
+ * Caller frees memory allocated for the smart_matches_t pointer.
+ *
+ * Returns 0 on success
+ */
+static int
+parse_matches(smart_matches_t **matches, char *attr)
+{
+ int res;
+
+ if (attr[0] == '\0')
+ return -1;
+
+ while (*attr != '\0') {
+ char *next;
+ size_t len;
+
+ if ((next = strchr(attr, ',')) == NULL) {
+ len = strlen(attr);
+ next = attr + len;
+ } else {
+ len = next - attr;
+ next++;
+ }
+
+ if (len == 0) {
+ printf("Malformed attribute %s\n", attr);
+ return -1;
+ }
+
+ res = add_match(matches, attr);
+ if (res)
+ return res;
+
+ attr = next;
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ smart_h h;
+ smart_map_t *sm = NULL;
+ char *devname = NULL;
+ int ch;
+ bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
+ do_descr;
+ smart_matches_t *matches = NULL;
+ int rc = EXIT_SUCCESS;
+
+ /*
+ * By default, keep the original behavior (output numbers only) if
+ * invoked as smart. Otherwise, default to printing the human-friendly
+ * text descriptions.
+ */
+ pn = getprogname();
+ if (strcmp(pn, SMART_NAME) == 0)
+ do_descr = false;
+ else
+ do_descr = true;
+
+#ifdef LIBXO
+ argc = xo_parse_args(argc, argv);
+#endif
+
+ while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
+ switch (ch) {
+ case 'h':
+ usage(pn);
+#ifdef LIBXO
+ xo_finish();
+#endif
+ return EXIT_SUCCESS;
+ break;
+ case 't':
+ do_thresh = true;
+ break;
+ case 'x':
+ do_hex = true;
+ break;
+ case 'a':
+ if (parse_matches(&matches, optarg)) {
+ usage(pn);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'i':
+ do_info = true;
+ break;
+ case 'd':
+ do_descr = true;
+ break;
+ case 'D':
+ do_descr = false;
+ break;
+ case 'v':
+ do_version = true;
+ break;
+ case 0:
+ if (debugset)
+ do_debug = true;
+ break;
+ default:
+ usage(pn);
+#ifdef LIBXO
+ xo_finish();
+#endif
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (do_version) {
+ printf("%s, version %s\n", pn, SMART_VERSION);
+ printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
+ "This is free software; see the source for copying conditions.\n");
+ return EXIT_SUCCESS;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ devname = argv[0];
+
+ if (!devname) {
+ printf("no device specified\n");
+ usage(pn);
+#ifdef LIBXO
+ xo_finish();
+#endif
+ return EXIT_FAILURE;
+ }
+
+ h = smart_open(SMART_PROTO_AUTO, argv[0]);
+
+ if (h == NULL) {
+ printf("device open failed %s\n", argv[0]);
+#ifdef LIBXO
+ xo_finish();
+#endif
+ return EXIT_FAILURE;
+ }
+
+#ifdef LIBXO
+ xo_open_container("drive");
+#endif
+
+ if (do_info) {
+ smart_print_device_info(h);
+ }
+
+ if (smart_supported(h)) {
+ sm = smart_read(h);
+
+ if (sm) {
+ uint32_t flags = 0;
+
+ if (do_hex)
+ flags |= SMART_OPEN_F_HEX;
+ if (do_thresh)
+ flags |= SMART_OPEN_F_THRESH;
+ if (do_descr)
+ flags |= SMART_OPEN_F_DESCR;
+
+ smart_print(h, sm, matches, flags);
+
+ smart_free(sm);
+ }
+ } else {
+ rc = EXIT_FAILURE;
+ }
+#ifdef LIBXO
+ xo_finish();
+#endif
+ smart_close(h);
+
+ return rc;
+}