17419d6e4SChuck Tuffli /*
27419d6e4SChuck Tuffli * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
37419d6e4SChuck Tuffli *
47419d6e4SChuck Tuffli * Permission to use, copy, modify, and distribute this software for any
57419d6e4SChuck Tuffli * purpose with or without fee is hereby granted, provided that the above
67419d6e4SChuck Tuffli * copyright notice and this permission notice appear in all copies.
77419d6e4SChuck Tuffli *
87419d6e4SChuck Tuffli * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
97419d6e4SChuck Tuffli * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
107419d6e4SChuck Tuffli * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
117419d6e4SChuck Tuffli * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
127419d6e4SChuck Tuffli * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
137419d6e4SChuck Tuffli * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
147419d6e4SChuck Tuffli * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
157419d6e4SChuck Tuffli */
167419d6e4SChuck Tuffli
177419d6e4SChuck Tuffli #include <stdio.h>
187419d6e4SChuck Tuffli #include <stdlib.h>
197419d6e4SChuck Tuffli #include <getopt.h>
207419d6e4SChuck Tuffli #include <stdbool.h>
217419d6e4SChuck Tuffli #include <string.h>
227419d6e4SChuck Tuffli #ifdef LIBXO
237419d6e4SChuck Tuffli #include <libxo/xo.h>
247419d6e4SChuck Tuffli #endif
257419d6e4SChuck Tuffli
267419d6e4SChuck Tuffli #include "libsmart.h"
277419d6e4SChuck Tuffli
287419d6e4SChuck Tuffli #define SMART_NAME "smart"
29*346be36eSChuck Tuffli #define SMART_VERSION "1.0.3"
307419d6e4SChuck Tuffli
317419d6e4SChuck Tuffli extern bool do_debug;
327419d6e4SChuck Tuffli
337419d6e4SChuck Tuffli static const char *pn;
347419d6e4SChuck Tuffli bool do_debug = false;
357419d6e4SChuck Tuffli static int debugset = 0;
367419d6e4SChuck Tuffli
377419d6e4SChuck Tuffli static struct option opts[] = {
387419d6e4SChuck Tuffli { "help", no_argument, NULL, 'h' },
397419d6e4SChuck Tuffli { "threshold", no_argument, NULL, 't' },
407419d6e4SChuck Tuffli { "hex", no_argument, NULL, 'x' },
417419d6e4SChuck Tuffli { "attribute", required_argument, NULL, 'a' },
427419d6e4SChuck Tuffli { "info", no_argument, NULL, 'i' },
437419d6e4SChuck Tuffli { "version", no_argument, NULL, 'v' },
447419d6e4SChuck Tuffli { "decode", no_argument, NULL, 'd' },
457419d6e4SChuck Tuffli { "no-decode", no_argument, NULL, 'D' },
467419d6e4SChuck Tuffli { "debug", no_argument, &debugset, 1 },
477419d6e4SChuck Tuffli { NULL, 0, NULL, 0 }
487419d6e4SChuck Tuffli };
497419d6e4SChuck Tuffli
507419d6e4SChuck Tuffli static void
usage(const char * name)517419d6e4SChuck Tuffli usage(const char *name)
527419d6e4SChuck Tuffli {
537419d6e4SChuck Tuffli printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
547419d6e4SChuck Tuffli printf("\t-h, --help\n");
557419d6e4SChuck Tuffli printf("\t-t, --threshold : also print out the threshold values\n");
567419d6e4SChuck Tuffli printf("\t-x, --hex : print the values out in hexadecimal\n");
577419d6e4SChuck Tuffli printf("\t-a, --attribute : print specified attribute(s)\n");
587419d6e4SChuck Tuffli printf("\t-i, --info : print general device information\n");
597419d6e4SChuck Tuffli printf("\t-d, --decode: decode the attribute IDs\n");
607419d6e4SChuck Tuffli printf("\t-D, --no-decode: don't decode the attribute IDs\n");
617419d6e4SChuck Tuffli printf("\t-v, --version : print the version and copyright\n");
627419d6e4SChuck Tuffli printf("\t --debug : output diagnostic information\n");
637419d6e4SChuck Tuffli }
647419d6e4SChuck Tuffli
657419d6e4SChuck Tuffli /*
667419d6e4SChuck Tuffli * Convert string to an integer
677419d6e4SChuck Tuffli *
687419d6e4SChuck Tuffli * Returns -1 on error, converted value otherwise
697419d6e4SChuck Tuffli */
707419d6e4SChuck Tuffli static int32_t
get_val(char * attr,char ** next)717419d6e4SChuck Tuffli get_val(char *attr, char **next)
727419d6e4SChuck Tuffli {
737419d6e4SChuck Tuffli char *sep = NULL;
747419d6e4SChuck Tuffli long val;
757419d6e4SChuck Tuffli
767419d6e4SChuck Tuffli *next = NULL;
777419d6e4SChuck Tuffli
787419d6e4SChuck Tuffli val = strtol(attr, &sep, 0);
797419d6e4SChuck Tuffli if ((val == 0) && (errno != 0)) {
807419d6e4SChuck Tuffli printf("Error parsing attribute %s", attr);
817419d6e4SChuck Tuffli switch (errno) {
827419d6e4SChuck Tuffli case EINVAL:
837419d6e4SChuck Tuffli printf(" (not a number?)\n");
847419d6e4SChuck Tuffli break;
857419d6e4SChuck Tuffli case ERANGE:
867419d6e4SChuck Tuffli printf(" (value out of range)\n");
877419d6e4SChuck Tuffli break;
887419d6e4SChuck Tuffli default:
897419d6e4SChuck Tuffli printf("\n");
907419d6e4SChuck Tuffli }
917419d6e4SChuck Tuffli return -1;
927419d6e4SChuck Tuffli }
937419d6e4SChuck Tuffli
947419d6e4SChuck Tuffli if (val > INT32_MAX) {
957419d6e4SChuck Tuffli printf("Attribute value %ld too big\n", val);
967419d6e4SChuck Tuffli return -1;
977419d6e4SChuck Tuffli }
987419d6e4SChuck Tuffli
997419d6e4SChuck Tuffli *next = sep;
1007419d6e4SChuck Tuffli return ((int32_t)val);
1017419d6e4SChuck Tuffli }
1027419d6e4SChuck Tuffli
1037419d6e4SChuck Tuffli /*
1047419d6e4SChuck Tuffli * Create a match specification from the given attribute
1057419d6e4SChuck Tuffli *
1067419d6e4SChuck Tuffli * Attribute format is
1077419d6e4SChuck Tuffli * <Page ID>:<Attribute ID>
1087419d6e4SChuck Tuffli * where page and attribute IDs are integers. If the page ID is missing,
1097419d6e4SChuck Tuffli * match the specified attribute ID on any page (i.e. -1). Valid forms are
1107419d6e4SChuck Tuffli * <int>:<int>
1117419d6e4SChuck Tuffli * :<int>
1127419d6e4SChuck Tuffli * <int>
1137419d6e4SChuck Tuffli *
1147419d6e4SChuck Tuffli * Returns 0 on success
1157419d6e4SChuck Tuffli */
1167419d6e4SChuck Tuffli static int
add_match(smart_matches_t ** matches,char * attr)1177419d6e4SChuck Tuffli add_match(smart_matches_t **matches, char *attr)
1187419d6e4SChuck Tuffli {
1197419d6e4SChuck Tuffli char *next;
1207419d6e4SChuck Tuffli int32_t page = -1, id;
1217419d6e4SChuck Tuffli uint32_t count = 0;
1227419d6e4SChuck Tuffli
1237419d6e4SChuck Tuffli id = get_val(attr, &next);
1247419d6e4SChuck Tuffli if (id < 0)
1257419d6e4SChuck Tuffli return id;
1267419d6e4SChuck Tuffli
1277419d6e4SChuck Tuffli if (*next == ':') {
1287419d6e4SChuck Tuffli page = id;
1297419d6e4SChuck Tuffli id = get_val(next + 1, &next);
1307419d6e4SChuck Tuffli if (id < 0)
1317419d6e4SChuck Tuffli return id;
1327419d6e4SChuck Tuffli }
1337419d6e4SChuck Tuffli
1347419d6e4SChuck Tuffli if (*matches == NULL) {
1357419d6e4SChuck Tuffli *matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
1367419d6e4SChuck Tuffli if (*matches == NULL)
1377419d6e4SChuck Tuffli return ENOMEM;
1387419d6e4SChuck Tuffli } else {
1397419d6e4SChuck Tuffli void *tmp;
1407419d6e4SChuck Tuffli
1417419d6e4SChuck Tuffli count = (*matches)->count;
1427419d6e4SChuck Tuffli tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
1437419d6e4SChuck Tuffli if (tmp == NULL)
1447419d6e4SChuck Tuffli return ENOMEM;
1457419d6e4SChuck Tuffli *matches = tmp;
1467419d6e4SChuck Tuffli }
1477419d6e4SChuck Tuffli
1487419d6e4SChuck Tuffli (*matches)->m[count].page = page;
1497419d6e4SChuck Tuffli (*matches)->m[count].id = id;
1507419d6e4SChuck Tuffli (*matches)->count++;
1517419d6e4SChuck Tuffli return 0;
1527419d6e4SChuck Tuffli }
1537419d6e4SChuck Tuffli
1547419d6e4SChuck Tuffli /*
1557419d6e4SChuck Tuffli * Parse the comma separated list of attributes to match
1567419d6e4SChuck Tuffli *
1577419d6e4SChuck Tuffli * Caller frees memory allocated for the smart_matches_t pointer.
1587419d6e4SChuck Tuffli *
1597419d6e4SChuck Tuffli * Returns 0 on success
1607419d6e4SChuck Tuffli */
1617419d6e4SChuck Tuffli static int
parse_matches(smart_matches_t ** matches,char * attr)1627419d6e4SChuck Tuffli parse_matches(smart_matches_t **matches, char *attr)
1637419d6e4SChuck Tuffli {
1647419d6e4SChuck Tuffli int res;
1657419d6e4SChuck Tuffli
1667419d6e4SChuck Tuffli if (attr[0] == '\0')
1677419d6e4SChuck Tuffli return -1;
1687419d6e4SChuck Tuffli
1697419d6e4SChuck Tuffli while (*attr != '\0') {
1707419d6e4SChuck Tuffli char *next;
1717419d6e4SChuck Tuffli size_t len;
1727419d6e4SChuck Tuffli
1737419d6e4SChuck Tuffli if ((next = strchr(attr, ',')) == NULL) {
1747419d6e4SChuck Tuffli len = strlen(attr);
1757419d6e4SChuck Tuffli next = attr + len;
1767419d6e4SChuck Tuffli } else {
1777419d6e4SChuck Tuffli len = next - attr;
1787419d6e4SChuck Tuffli next++;
1797419d6e4SChuck Tuffli }
1807419d6e4SChuck Tuffli
1817419d6e4SChuck Tuffli if (len == 0) {
1827419d6e4SChuck Tuffli printf("Malformed attribute %s\n", attr);
1837419d6e4SChuck Tuffli return -1;
1847419d6e4SChuck Tuffli }
1857419d6e4SChuck Tuffli
1867419d6e4SChuck Tuffli res = add_match(matches, attr);
1877419d6e4SChuck Tuffli if (res)
1887419d6e4SChuck Tuffli return res;
1897419d6e4SChuck Tuffli
1907419d6e4SChuck Tuffli attr = next;
1917419d6e4SChuck Tuffli }
1927419d6e4SChuck Tuffli
1937419d6e4SChuck Tuffli return 0;
1947419d6e4SChuck Tuffli }
1957419d6e4SChuck Tuffli
1967419d6e4SChuck Tuffli int
main(int argc,char * argv[])1977419d6e4SChuck Tuffli main(int argc, char *argv[])
1987419d6e4SChuck Tuffli {
1997419d6e4SChuck Tuffli smart_h h;
2007419d6e4SChuck Tuffli smart_map_t *sm = NULL;
2017419d6e4SChuck Tuffli char *devname = NULL;
2027419d6e4SChuck Tuffli int ch;
2037419d6e4SChuck Tuffli bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
2047419d6e4SChuck Tuffli do_descr;
2057419d6e4SChuck Tuffli smart_matches_t *matches = NULL;
2067419d6e4SChuck Tuffli int rc = EXIT_SUCCESS;
2077419d6e4SChuck Tuffli
2087419d6e4SChuck Tuffli /*
2097419d6e4SChuck Tuffli * By default, keep the original behavior (output numbers only) if
2107419d6e4SChuck Tuffli * invoked as smart. Otherwise, default to printing the human-friendly
2117419d6e4SChuck Tuffli * text descriptions.
2127419d6e4SChuck Tuffli */
2137419d6e4SChuck Tuffli pn = getprogname();
2147419d6e4SChuck Tuffli if (strcmp(pn, SMART_NAME) == 0)
2157419d6e4SChuck Tuffli do_descr = false;
2167419d6e4SChuck Tuffli else
2177419d6e4SChuck Tuffli do_descr = true;
2187419d6e4SChuck Tuffli
2197419d6e4SChuck Tuffli #ifdef LIBXO
2207419d6e4SChuck Tuffli argc = xo_parse_args(argc, argv);
2217419d6e4SChuck Tuffli #endif
2227419d6e4SChuck Tuffli
2237419d6e4SChuck Tuffli while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
2247419d6e4SChuck Tuffli switch (ch) {
2257419d6e4SChuck Tuffli case 'h':
2267419d6e4SChuck Tuffli usage(pn);
2277419d6e4SChuck Tuffli #ifdef LIBXO
2287419d6e4SChuck Tuffli xo_finish();
2297419d6e4SChuck Tuffli #endif
2307419d6e4SChuck Tuffli return EXIT_SUCCESS;
2317419d6e4SChuck Tuffli break;
2327419d6e4SChuck Tuffli case 't':
2337419d6e4SChuck Tuffli do_thresh = true;
2347419d6e4SChuck Tuffli break;
2357419d6e4SChuck Tuffli case 'x':
2367419d6e4SChuck Tuffli do_hex = true;
2377419d6e4SChuck Tuffli break;
2387419d6e4SChuck Tuffli case 'a':
2397419d6e4SChuck Tuffli if (parse_matches(&matches, optarg)) {
2407419d6e4SChuck Tuffli usage(pn);
2417419d6e4SChuck Tuffli return EXIT_FAILURE;
2427419d6e4SChuck Tuffli }
2437419d6e4SChuck Tuffli break;
2447419d6e4SChuck Tuffli case 'i':
2457419d6e4SChuck Tuffli do_info = true;
2467419d6e4SChuck Tuffli break;
2477419d6e4SChuck Tuffli case 'd':
2487419d6e4SChuck Tuffli do_descr = true;
2497419d6e4SChuck Tuffli break;
2507419d6e4SChuck Tuffli case 'D':
2517419d6e4SChuck Tuffli do_descr = false;
2527419d6e4SChuck Tuffli break;
2537419d6e4SChuck Tuffli case 'v':
2547419d6e4SChuck Tuffli do_version = true;
2557419d6e4SChuck Tuffli break;
2567419d6e4SChuck Tuffli case 0:
2577419d6e4SChuck Tuffli if (debugset)
2587419d6e4SChuck Tuffli do_debug = true;
2597419d6e4SChuck Tuffli break;
2607419d6e4SChuck Tuffli default:
2617419d6e4SChuck Tuffli usage(pn);
2627419d6e4SChuck Tuffli #ifdef LIBXO
2637419d6e4SChuck Tuffli xo_finish();
2647419d6e4SChuck Tuffli #endif
2657419d6e4SChuck Tuffli return EXIT_FAILURE;
2667419d6e4SChuck Tuffli }
2677419d6e4SChuck Tuffli }
2687419d6e4SChuck Tuffli
2697419d6e4SChuck Tuffli if (do_version) {
2707419d6e4SChuck Tuffli printf("%s, version %s\n", pn, SMART_VERSION);
2717419d6e4SChuck Tuffli printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
2727419d6e4SChuck Tuffli "This is free software; see the source for copying conditions.\n");
2737419d6e4SChuck Tuffli return EXIT_SUCCESS;
2747419d6e4SChuck Tuffli }
2757419d6e4SChuck Tuffli
2767419d6e4SChuck Tuffli argc -= optind;
2777419d6e4SChuck Tuffli argv += optind;
2787419d6e4SChuck Tuffli
2797419d6e4SChuck Tuffli devname = argv[0];
2807419d6e4SChuck Tuffli
2817419d6e4SChuck Tuffli if (!devname) {
2827419d6e4SChuck Tuffli printf("no device specified\n");
2837419d6e4SChuck Tuffli usage(pn);
2847419d6e4SChuck Tuffli #ifdef LIBXO
2857419d6e4SChuck Tuffli xo_finish();
2867419d6e4SChuck Tuffli #endif
2877419d6e4SChuck Tuffli return EXIT_FAILURE;
2887419d6e4SChuck Tuffli }
2897419d6e4SChuck Tuffli
2907419d6e4SChuck Tuffli h = smart_open(SMART_PROTO_AUTO, argv[0]);
2917419d6e4SChuck Tuffli
2927419d6e4SChuck Tuffli if (h == NULL) {
2937419d6e4SChuck Tuffli printf("device open failed %s\n", argv[0]);
2947419d6e4SChuck Tuffli #ifdef LIBXO
2957419d6e4SChuck Tuffli xo_finish();
2967419d6e4SChuck Tuffli #endif
2977419d6e4SChuck Tuffli return EXIT_FAILURE;
2987419d6e4SChuck Tuffli }
2997419d6e4SChuck Tuffli
3007419d6e4SChuck Tuffli #ifdef LIBXO
3017419d6e4SChuck Tuffli xo_open_container("drive");
3027419d6e4SChuck Tuffli #endif
3037419d6e4SChuck Tuffli
3047419d6e4SChuck Tuffli if (do_info) {
3057419d6e4SChuck Tuffli smart_print_device_info(h);
3067419d6e4SChuck Tuffli }
3077419d6e4SChuck Tuffli
3087419d6e4SChuck Tuffli if (smart_supported(h)) {
3097419d6e4SChuck Tuffli sm = smart_read(h);
3107419d6e4SChuck Tuffli
3117419d6e4SChuck Tuffli if (sm) {
3127419d6e4SChuck Tuffli uint32_t flags = 0;
3137419d6e4SChuck Tuffli
3147419d6e4SChuck Tuffli if (do_hex)
3157419d6e4SChuck Tuffli flags |= SMART_OPEN_F_HEX;
3167419d6e4SChuck Tuffli if (do_thresh)
3177419d6e4SChuck Tuffli flags |= SMART_OPEN_F_THRESH;
3187419d6e4SChuck Tuffli if (do_descr)
3197419d6e4SChuck Tuffli flags |= SMART_OPEN_F_DESCR;
3207419d6e4SChuck Tuffli
3217419d6e4SChuck Tuffli smart_print(h, sm, matches, flags);
3227419d6e4SChuck Tuffli
3237419d6e4SChuck Tuffli smart_free(sm);
3247419d6e4SChuck Tuffli }
3257419d6e4SChuck Tuffli } else {
3267419d6e4SChuck Tuffli rc = EXIT_FAILURE;
3277419d6e4SChuck Tuffli }
3287419d6e4SChuck Tuffli #ifdef LIBXO
3297419d6e4SChuck Tuffli xo_finish();
3307419d6e4SChuck Tuffli #endif
3317419d6e4SChuck Tuffli smart_close(h);
3327419d6e4SChuck Tuffli
3337419d6e4SChuck Tuffli return rc;
3347419d6e4SChuck Tuffli }
335