1 /*
2 * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <getopt.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #ifdef LIBXO
23 #include <libxo/xo.h>
24 #endif
25
26 #include "libsmart.h"
27
28 #define SMART_NAME "smart"
29 #define SMART_VERSION "1.0.3"
30
31 extern bool do_debug;
32
33 static const char *pn;
34 bool do_debug = false;
35 static int debugset = 0;
36
37 static struct option opts[] = {
38 { "help", no_argument, NULL, 'h' },
39 { "threshold", no_argument, NULL, 't' },
40 { "hex", no_argument, NULL, 'x' },
41 { "attribute", required_argument, NULL, 'a' },
42 { "info", no_argument, NULL, 'i' },
43 { "version", no_argument, NULL, 'v' },
44 { "decode", no_argument, NULL, 'd' },
45 { "no-decode", no_argument, NULL, 'D' },
46 { "debug", no_argument, &debugset, 1 },
47 { NULL, 0, NULL, 0 }
48 };
49
50 static void
usage(const char * name)51 usage(const char *name)
52 {
53 printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
54 printf("\t-h, --help\n");
55 printf("\t-t, --threshold : also print out the threshold values\n");
56 printf("\t-x, --hex : print the values out in hexadecimal\n");
57 printf("\t-a, --attribute : print specified attribute(s)\n");
58 printf("\t-i, --info : print general device information\n");
59 printf("\t-d, --decode: decode the attribute IDs\n");
60 printf("\t-D, --no-decode: don't decode the attribute IDs\n");
61 printf("\t-v, --version : print the version and copyright\n");
62 printf("\t --debug : output diagnostic information\n");
63 }
64
65 /*
66 * Convert string to an integer
67 *
68 * Returns -1 on error, converted value otherwise
69 */
70 static int32_t
get_val(char * attr,char ** next)71 get_val(char *attr, char **next)
72 {
73 char *sep = NULL;
74 long val;
75
76 *next = NULL;
77
78 val = strtol(attr, &sep, 0);
79 if ((val == 0) && (errno != 0)) {
80 printf("Error parsing attribute %s", attr);
81 switch (errno) {
82 case EINVAL:
83 printf(" (not a number?)\n");
84 break;
85 case ERANGE:
86 printf(" (value out of range)\n");
87 break;
88 default:
89 printf("\n");
90 }
91 return -1;
92 }
93
94 if (val > INT32_MAX) {
95 printf("Attribute value %ld too big\n", val);
96 return -1;
97 }
98
99 *next = sep;
100 return ((int32_t)val);
101 }
102
103 /*
104 * Create a match specification from the given attribute
105 *
106 * Attribute format is
107 * <Page ID>:<Attribute ID>
108 * where page and attribute IDs are integers. If the page ID is missing,
109 * match the specified attribute ID on any page (i.e. -1). Valid forms are
110 * <int>:<int>
111 * :<int>
112 * <int>
113 *
114 * Returns 0 on success
115 */
116 static int
add_match(smart_matches_t ** matches,char * attr)117 add_match(smart_matches_t **matches, char *attr)
118 {
119 char *next;
120 int32_t page = -1, id;
121 uint32_t count = 0;
122
123 id = get_val(attr, &next);
124 if (id < 0)
125 return id;
126
127 if (*next == ':') {
128 page = id;
129 id = get_val(next + 1, &next);
130 if (id < 0)
131 return id;
132 }
133
134 if (*matches == NULL) {
135 *matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
136 if (*matches == NULL)
137 return ENOMEM;
138 } else {
139 void *tmp;
140
141 count = (*matches)->count;
142 tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
143 if (tmp == NULL)
144 return ENOMEM;
145 *matches = tmp;
146 }
147
148 (*matches)->m[count].page = page;
149 (*matches)->m[count].id = id;
150 (*matches)->count++;
151 return 0;
152 }
153
154 /*
155 * Parse the comma separated list of attributes to match
156 *
157 * Caller frees memory allocated for the smart_matches_t pointer.
158 *
159 * Returns 0 on success
160 */
161 static int
parse_matches(smart_matches_t ** matches,char * attr)162 parse_matches(smart_matches_t **matches, char *attr)
163 {
164 int res;
165
166 if (attr[0] == '\0')
167 return -1;
168
169 while (*attr != '\0') {
170 char *next;
171 size_t len;
172
173 if ((next = strchr(attr, ',')) == NULL) {
174 len = strlen(attr);
175 next = attr + len;
176 } else {
177 len = next - attr;
178 next++;
179 }
180
181 if (len == 0) {
182 printf("Malformed attribute %s\n", attr);
183 return -1;
184 }
185
186 res = add_match(matches, attr);
187 if (res)
188 return res;
189
190 attr = next;
191 }
192
193 return 0;
194 }
195
196 int
main(int argc,char * argv[])197 main(int argc, char *argv[])
198 {
199 smart_h h;
200 smart_map_t *sm = NULL;
201 char *devname = NULL;
202 int ch;
203 bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
204 do_descr;
205 smart_matches_t *matches = NULL;
206 int rc = EXIT_SUCCESS;
207
208 /*
209 * By default, keep the original behavior (output numbers only) if
210 * invoked as smart. Otherwise, default to printing the human-friendly
211 * text descriptions.
212 */
213 pn = getprogname();
214 if (strcmp(pn, SMART_NAME) == 0)
215 do_descr = false;
216 else
217 do_descr = true;
218
219 #ifdef LIBXO
220 argc = xo_parse_args(argc, argv);
221 #endif
222
223 while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
224 switch (ch) {
225 case 'h':
226 usage(pn);
227 #ifdef LIBXO
228 xo_finish();
229 #endif
230 return EXIT_SUCCESS;
231 break;
232 case 't':
233 do_thresh = true;
234 break;
235 case 'x':
236 do_hex = true;
237 break;
238 case 'a':
239 if (parse_matches(&matches, optarg)) {
240 usage(pn);
241 return EXIT_FAILURE;
242 }
243 break;
244 case 'i':
245 do_info = true;
246 break;
247 case 'd':
248 do_descr = true;
249 break;
250 case 'D':
251 do_descr = false;
252 break;
253 case 'v':
254 do_version = true;
255 break;
256 case 0:
257 if (debugset)
258 do_debug = true;
259 break;
260 default:
261 usage(pn);
262 #ifdef LIBXO
263 xo_finish();
264 #endif
265 return EXIT_FAILURE;
266 }
267 }
268
269 if (do_version) {
270 printf("%s, version %s\n", pn, SMART_VERSION);
271 printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
272 "This is free software; see the source for copying conditions.\n");
273 return EXIT_SUCCESS;
274 }
275
276 argc -= optind;
277 argv += optind;
278
279 devname = argv[0];
280
281 if (!devname) {
282 printf("no device specified\n");
283 usage(pn);
284 #ifdef LIBXO
285 xo_finish();
286 #endif
287 return EXIT_FAILURE;
288 }
289
290 h = smart_open(SMART_PROTO_AUTO, argv[0]);
291
292 if (h == NULL) {
293 printf("device open failed %s\n", argv[0]);
294 #ifdef LIBXO
295 xo_finish();
296 #endif
297 return EXIT_FAILURE;
298 }
299
300 #ifdef LIBXO
301 xo_open_container("drive");
302 #endif
303
304 if (do_info) {
305 smart_print_device_info(h);
306 }
307
308 if (smart_supported(h)) {
309 sm = smart_read(h);
310
311 if (sm) {
312 uint32_t flags = 0;
313
314 if (do_hex)
315 flags |= SMART_OPEN_F_HEX;
316 if (do_thresh)
317 flags |= SMART_OPEN_F_THRESH;
318 if (do_descr)
319 flags |= SMART_OPEN_F_DESCR;
320
321 smart_print(h, sm, matches, flags);
322
323 smart_free(sm);
324 }
325 } else {
326 rc = EXIT_FAILURE;
327 }
328 #ifdef LIBXO
329 xo_finish();
330 #endif
331 smart_close(h);
332
333 return rc;
334 }
335