Bug Summary

File:.build-ci/../plugins/netapp/netapp-nvme.c
Warning:line 812, column 2
Value stored to 'err' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name netapp-nvme.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../plugins/netapp/netapp-nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2018 NetApp, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <stdio.h>
18#include <dirent.h>
19#include <sys/stat.h>
20#include <stdlib.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <errno(*__errno_location ()).h>
24#include <string.h>
25#include <libgen.h>
26
27#include <libnvme.h>
28
29#include "common.h"
30#include "nvme-cmds.h"
31#include "nvme.h"
32#include "util/suffix.h"
33
34#define CREATE_CMD
35#include "netapp-nvme.h"
36
37#define ONTAP_C2_LOG_ID0xC2 0xC2
38#define ONTAP_C2_LOG_SIZE4096 4096
39#define ONTAP_LABEL_LEN260 260
40#define ONTAP_NS_PATHLEN525 525
41
42enum {
43 NNORMAL,
44 NJSON,
45 NCOLUMN,
46};
47
48enum {
49 ONTAP_C2_LOG_SUPPORTED_LSP = 0x0,
50 ONTAP_C2_LOG_NSINFO_LSP = 0x1,
51 ONTAP_C2_LOG_PLATFORM_LSP = 0x2,
52};
53
54enum {
55 ONTAP_VSERVER_NAME_TLV = 0x11,
56 ONTAP_VOLUME_NAME_TLV = 0x12,
57 ONTAP_NS_NAME_TLV = 0x13,
58 ONTAP_NS_PATH_TLV = 0x14,
59};
60
61static const char *dev_path = "/dev/";
62
63struct smdevice_info {
64 unsigned int nsid;
65 struct nvme_id_ctrl ctrl;
66 struct nvme_id_ns ns;
67 char dev[265];
68};
69
70struct ontapdevice_info {
71 unsigned int nsid;
72 struct nvme_id_ctrl ctrl;
73 struct nvme_id_ns ns;
74 unsigned char uuid[NVME_UUID_LEN16];
75 unsigned char log_data[ONTAP_C2_LOG_SIZE4096];
76 char dev[265];
77};
78
79#define ARRAY_LABEL_LEN60 60
80#define VOLUME_LABEL_LEN60 60
81
82/*
83 * Format of the string isn't tightly controlled yet. For now, squash UCS-2 into
84 * ASCII. dst buffer must be at least count + 1 bytes long
85 */
86static void netapp_convert_string(char *dst, char *src, unsigned int count)
87{
88 int i;
89
90 if (!dst || !src || !count)
91 return;
92
93 memset(dst, 0, count + 1);
94 for (i = 0; i < count; i++)
95 dst[i] = src[i * 2 + 1];
96 /* the json routines won't accept empty strings */
97 if (strlen(dst) == 0 && count)
98 dst[0] = ' ';
99}
100
101static void netapp_nguid_to_str(char *str, __u8 *nguid)
102{
103 int i;
104
105 memset(str, 0, 33);
106 for (i = 0; i < 16; i++)
107 str += sprintf(str, "%02x", nguid[i]);
108}
109
110static void netapp_get_ns_size(char *size, unsigned long long *lba,
111 struct nvme_id_ns *ns)
112{
113 __u8 lba_index;
114
115 nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &lba_index);
116 *lba = 1ULL << ns->lbaf[lba_index].ds;
117 double nsze = le64_to_cpu(ns->nsze) * (*lba);
118 const char *s_suffix = suffix_si_get(&nsze);
119
120 sprintf(size, "%.2f%sB", nsze, s_suffix);
121}
122
123static void netapp_get_ns_attrs(char *size, char *used, char *blk_size,
124 char *version, unsigned long long *lba,
125 struct nvme_id_ctrl *ctrl, struct nvme_id_ns *ns)
126{
127 __u8 lba_index;
128
129 nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &lba_index);
130 *lba = 1ULL << ns->lbaf[lba_index].ds;
131
132 /* get the namespace size */
133 double nsze = le64_to_cpu(ns->nsze) * (*lba);
134 const char *s_suffix = suffix_si_get(&nsze);
135
136 sprintf(size, "%.2f%sB", nsze, s_suffix);
137
138 /* get the namespace utilization */
139 double nuse = le64_to_cpu(ns->nuse) * (*lba);
140 const char *u_suffix = suffix_si_get(&nuse);
141
142 sprintf(used, "%.2f%sB", nuse, u_suffix);
143
144 /* get the namespace block size */
145 long long addr = 1LL << ns->lbaf[lba_index].ds;
146 const char *l_suffix = suffix_binary_get(&addr);
147
148 sprintf(blk_size, "%u%sB", (unsigned int)addr, l_suffix);
149
150 /* get the firmware version */
151 int i, max = sizeof(ctrl->fr);
152
153 memcpy(version, ctrl->fr, max);
154 version[max] = '\0';
155 /* strip trailing whitespaces */
156 for (i = max - 1; i >= 0 && version[i] == ' '; i--)
157 version[i] = '\0';
158}
159
160static void ontap_get_subsysname(char *subnqn, char *subsysname,
161 struct nvme_id_ctrl *ctrl)
162{
163 char *subname;
164 int i, len = sizeof(ctrl->subnqn);
165
166 /* get the target NQN */
167 memcpy(subnqn, ctrl->subnqn, len);
168 subnqn[len] = '\0';
169
170 /* strip trailing whitespaces */
171 for (i = len - 1; i >= 0 && subnqn[i] == ' '; i--)
172 subnqn[i] = '\0';
173
174 /* get the subsysname from the target NQN */
175 subname = strrchr(subnqn, '.');
176 if (subname) {
177 subname++;
178 len = strlen(subname);
179 memcpy(subsysname, subname, len);
180 subsysname[len] = '\0';
181 } else
182 fprintf(stderrstderr, "Unable to fetch ONTAP subsystem name\n");
183}
184
185static void ontap_labels_to_str(char *dst, char *src, int count)
186{
187 int i;
188
189 memset(dst, 0, ONTAP_LABEL_LEN260);
190 for (i = 0; i < count; i++) {
191 if (src[i] >= '!' && src[i] <= '~')
192 dst[i] = src[i];
193 else
194 break;
195 }
196 dst[i] = '\0';
197}
198
199static void netapp_get_ontap_labels(char *vsname, char *nspath,
200 unsigned char *log_data)
201{
202 int lsp, tlv, label_len;
203 char *vserver_name, *volume_name, *namespace_name, *namespace_path;
204 char vol_name[ONTAP_LABEL_LEN260], ns_name[ONTAP_LABEL_LEN260];
205 char ns_path[ONTAP_LABEL_LEN260];
206 bool_Bool nspath_tlv_available = false0;
207 const char *ontap_vol = "/vol/";
208 int i, j;
209
210 /* get the lsp */
211 lsp = (*(__u8 *)&log_data[16]) & 0x0F;
212 if (lsp != ONTAP_C2_LOG_NSINFO_LSP)
213 /* lsp not related to nsinfo */
214 return;
215
216 /* get the vserver name tlv */
217 tlv = *(__u8 *)&log_data[32];
218 if (tlv == ONTAP_VSERVER_NAME_TLV) {
219 label_len = (*(__u16 *)&log_data[34]) * 4;
220 vserver_name = (char *)&log_data[36];
221 ontap_labels_to_str(vsname, vserver_name, label_len);
222 } else {
223 /* not the expected vserver tlv */
224 fprintf(stderrstderr, "Unable to fetch ONTAP vserver name\n");
225 return;
226 }
227
228 i = 36 + label_len;
229 j = i + 2;
230 /* get the volume name tlv */
231 tlv = *(__u8 *)&log_data[i];
232 if (tlv == ONTAP_VOLUME_NAME_TLV) {
233 label_len = (*(__u16 *)&log_data[j]) * 4;
234 volume_name = (char *)&log_data[j + 2];
235 ontap_labels_to_str(vol_name, volume_name, label_len);
236 } else {
237 /* not the expected volume tlv */
238 fprintf(stderrstderr, "Unable to fetch ONTAP volume name\n");
239 return;
240 }
241
242 i += 4 + label_len;
243 j += 4 + label_len;
244 /* get the namespace name tlv */
245 tlv = *(__u8 *)&log_data[i];
246 if (tlv == ONTAP_NS_NAME_TLV) {
247 label_len = (*(__u16 *)&log_data[j]) * 4;
248 namespace_name = (char *)&log_data[j + 2];
249 ontap_labels_to_str(ns_name, namespace_name, label_len);
250 } else {
251 /* not the expected namespace tlv */
252 fprintf(stderrstderr, "Unable to fetch ONTAP namespace name\n");
253 return;
254 }
255
256 i += 4 + label_len;
257 j += 4 + label_len;
258 /* get the namespace path tlv if available */
259 tlv = *(__u8 *)&log_data[i];
260 if (tlv == ONTAP_NS_PATH_TLV) {
261 nspath_tlv_available = true1;
262 label_len = (*(__u16 *)&log_data[j]) * 4;
263 namespace_path = (char *)&log_data[j + 2];
264 ontap_labels_to_str(ns_path, namespace_path, label_len);
265 }
266
267 if (nspath_tlv_available) {
268 /* set nspath from the corresponding ns_path string */
269 snprintf(nspath, ONTAP_NS_PATHLEN525, "%s", ns_path);
270 } else {
271 /* set nspath by concatenating ontap_vol with ns_name */
272 snprintf(nspath, ONTAP_NS_PATHLEN525, "%s%s%s%s", ontap_vol,
273 vol_name, "/", ns_name);
274 }
275}
276
277static void netapp_smdevice_json(struct json_object *devices, char *devname,
278 char *arrayname, char *volname, int nsid, char *nguid,
279 char *ctrl, char *astate, char *version, unsigned long long lba,
280 unsigned long long nsze, unsigned long long nuse)
281{
282 struct json_object *device_attrs;
283 unsigned long long ns_size = nsze * lba;
284
285 device_attrs = json_create_object()json_object_new_object();
286 json_object_add_value_string(device_attrs, "Device", devname);
287 json_object_add_value_string(device_attrs, "Array_Name", arrayname);
288 json_object_add_value_string(device_attrs, "Volume_Name", volname);
289 json_object_add_value_int(device_attrs, "NSID", nsid)json_object_object_add(device_attrs, "NSID", json_object_new_int
(nsid))
;
290 json_object_add_value_string(device_attrs, "Volume_ID", nguid);
291 json_object_add_value_string(device_attrs, "Controller", ctrl);
292 json_object_add_value_string(device_attrs, "Access_State", astate);
293 json_object_add_value_uint64(device_attrs, "LBA_Size", lba)json_object_object_add(device_attrs, "LBA_Size", json_object_new_uint64
(lba))
;
294 json_object_add_value_uint64(device_attrs, "Namespace_Size", ns_size)json_object_object_add(device_attrs, "Namespace_Size", json_object_new_uint64
(ns_size))
;
295 json_object_add_value_string(device_attrs, "Version", version);
296
297 json_array_add_value_object(devices, device_attrs)json_object_array_add(devices, device_attrs);
298}
299
300static void netapp_ontapdevice_json(struct json_object *devices, char *devname,
301 char *vsname, char *subsysname, char *nspath, int nsid,
302 char *uuid, unsigned long long lba, char *version,
303 unsigned long long nsze, unsigned long long nuse)
304{
305 struct json_object *device_attrs;
306 unsigned long long ns_size = nsze * lba;
307 unsigned long long used_size = nuse * lba;
308
309 device_attrs = json_create_object()json_object_new_object();
310 json_object_add_value_string(device_attrs, "Device", devname);
311 json_object_add_value_string(device_attrs, "Vserver", vsname);
312 json_object_add_value_string(device_attrs, "Subsystem", subsysname);
313 json_object_add_value_string(device_attrs, "Namespace_Path", nspath);
314 json_object_add_value_int(device_attrs, "NSID", nsid)json_object_object_add(device_attrs, "NSID", json_object_new_int
(nsid))
;
315 json_object_add_value_string(device_attrs, "UUID", uuid);
316 json_object_add_value_uint64(device_attrs, "LBA_Size", lba)json_object_object_add(device_attrs, "LBA_Size", json_object_new_uint64
(lba))
;
317 json_object_add_value_uint64(device_attrs, "Namespace_Size", ns_size)json_object_object_add(device_attrs, "Namespace_Size", json_object_new_uint64
(ns_size))
;
318 json_object_add_value_uint64(device_attrs, "UsedBytes", used_size)json_object_object_add(device_attrs, "UsedBytes", json_object_new_uint64
(used_size))
;
319 json_object_add_value_string(device_attrs, "Version", version);
320
321 json_array_add_value_object(devices, device_attrs)json_object_array_add(devices, device_attrs);
322}
323
324static void netapp_smdevices_print_verbose(struct smdevice_info *devices,
325 int count, int format, const char *devname)
326{
327 int i, slta;
328 char array_label[ARRAY_LABEL_LEN60 / 2 + 1];
329 char volume_label[VOLUME_LABEL_LEN60 / 2 + 1];
330 char nguid_str[33];
331 unsigned long long lba;
332 char size[128], used[128];
333 char blk_size[128], version[9];
334
335 char *formatstr = NULL((void*)0);
336 char basestr[] =
337 "%s, Array Name %s, Volume Name %s, NSID %d, Volume ID %s, "
338 "Controller %c, Access State %s, Size %s, Format %s, Version %s\n";
339 char columnstr[] =
340 "%-16s %-30s %-30s %4d %32s %c %-12s %-9s %-9s %-9s\n";
341
342 if (format == NNORMAL)
343 formatstr = basestr;
344 else if (format == NCOLUMN) {
345 /* print column headers and change the output string */
346 printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s %-9s %-9s\n",
347 "Device", "Array Name", "Volume Name", "NSID",
348 "Volume ID", "Ctrl", "Access State", " Size",
349 "Format", "Version");
350 printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s %-9s %-9s\n",
351 "----------------", "------------------------------",
352 "------------------------------", "----",
353 "--------------------------------", "----",
354 "------------", "---------",
355 "---------", "---------");
356 formatstr = columnstr;
357 }
358
359 for (i = 0; i < count; i++) {
360 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
361 /* found the device, fetch info for that alone */
362 netapp_get_ns_attrs(size, used, blk_size, version,
363 &lba, &devices[i].ctrl, &devices[i].ns);
364 netapp_convert_string(array_label,
365 (char *)&devices[i].ctrl.vs[20],
366 ARRAY_LABEL_LEN60 / 2);
367 slta = devices[i].ctrl.vs[0] & 0x1;
368 netapp_convert_string(volume_label,
369 (char *)devices[i].ns.vs,
370 VOLUME_LABEL_LEN60 / 2);
371 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
372
373 printf(formatstr, devices[i].dev, array_label,
374 volume_label, devices[i].nsid,
375 nguid_str,
376 slta ? 'A' : 'B', "unknown", size,
377 blk_size, version);
378 return;
379 }
380 }
381
382 for (i = 0; i < count; i++) {
383 /* fetch info and print for all devices */
384 netapp_get_ns_attrs(size, used, blk_size, version,
385 &lba, &devices[i].ctrl, &devices[i].ns);
386 netapp_convert_string(array_label,
387 (char *)&devices[i].ctrl.vs[20],
388 ARRAY_LABEL_LEN60 / 2);
389 slta = devices[i].ctrl.vs[0] & 0x1;
390 netapp_convert_string(volume_label,
391 (char *)devices[i].ns.vs,
392 VOLUME_LABEL_LEN60 / 2);
393 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
394
395 printf(formatstr, devices[i].dev, array_label,
396 volume_label, devices[i].nsid,
397 nguid_str,
398 slta ? 'A' : 'B', "unknown", size,
399 blk_size, version);
400 }
401}
402
403static void netapp_smdevices_print_regular(struct smdevice_info *devices,
404 int count, int format, const char *devname)
405{
406 int i, slta;
407 char array_label[ARRAY_LABEL_LEN60 / 2 + 1];
408 char volume_label[VOLUME_LABEL_LEN60 / 2 + 1];
409 char nguid_str[33];
410 unsigned long long lba;
411 char size[128];
412
413 char *formatstr = NULL((void*)0);
414 char basestr[] =
415 "%s, Array Name %s, Volume Name %s, NSID %d, Volume ID %s, Controller %c, Access State %s, %s\n";
416 char columnstr[] = "%-16s %-30s %-30s %4d %32s %c %-12s %9s\n";
417
418 if (format == NNORMAL)
419 formatstr = basestr;
420 else if (format == NCOLUMN) {
421 /* print column headers and change the output string */
422 printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
423 "Device", "Array Name", "Volume Name", "NSID",
424 "Volume ID", "Ctrl", "Access State", " Size");
425 printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
426 "----------------", "------------------------------",
427 "------------------------------", "----",
428 "--------------------------------", "----",
429 "------------", "---------");
430 formatstr = columnstr;
431 }
432
433 for (i = 0; i < count; i++) {
434 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
435 /* found the device, fetch info for that alone */
436 netapp_get_ns_size(size, &lba, &devices[i].ns);
437 netapp_convert_string(array_label,
438 (char *)&devices[i].ctrl.vs[20],
439 ARRAY_LABEL_LEN60 / 2);
440 slta = devices[i].ctrl.vs[0] & 0x1;
441 netapp_convert_string(volume_label,
442 (char *)devices[i].ns.vs,
443 VOLUME_LABEL_LEN60 / 2);
444 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
445
446 printf(formatstr, devices[i].dev, array_label,
447 volume_label, devices[i].nsid,
448 nguid_str,
449 slta ? 'A' : 'B', "unknown", size);
450 return;
451 }
452 }
453
454 for (i = 0; i < count; i++) {
455 /* fetch info for all devices */
456 netapp_get_ns_size(size, &lba, &devices[i].ns);
457 netapp_convert_string(array_label,
458 (char *)&devices[i].ctrl.vs[20],
459 ARRAY_LABEL_LEN60 / 2);
460 slta = devices[i].ctrl.vs[0] & 0x1;
461 netapp_convert_string(volume_label, (char *)devices[i].ns.vs,
462 VOLUME_LABEL_LEN60 / 2);
463 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
464
465 printf(formatstr, devices[i].dev, array_label,
466 volume_label, devices[i].nsid, nguid_str,
467 slta ? 'A' : 'B', "unknown", size);
468 }
469}
470
471static void netapp_smdevices_print_json(struct smdevice_info *devices,
472 int count, const char *devname)
473{
474 struct json_object *root = NULL((void*)0);
475 struct json_object *json_devices = NULL((void*)0);
476 int i, slta;
477 char array_label[ARRAY_LABEL_LEN60 / 2 + 1];
478 char volume_label[VOLUME_LABEL_LEN60 / 2 + 1];
479 char nguid_str[33];
480 unsigned long long lba;
481 char size[128], used[128];
482 char blk_size[128], version[9];
483
484 /* prepare for the json output */
485 root = json_create_object()json_object_new_object();
486 json_devices = json_create_array()json_object_new_array();
487
488 for (i = 0; i < count; i++) {
489 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
490 /* found the device, fetch info for that alone */
491 netapp_get_ns_attrs(size, used, blk_size, version,
492 &lba, &devices[i].ctrl, &devices[i].ns);
493 netapp_convert_string(array_label,
494 (char *)&devices[i].ctrl.vs[20],
495 ARRAY_LABEL_LEN60 / 2);
496 slta = devices[i].ctrl.vs[0] & 0x1;
497 netapp_convert_string(volume_label,
498 (char *)devices[i].ns.vs,
499 VOLUME_LABEL_LEN60 / 2);
500 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
501 netapp_smdevice_json(json_devices, devices[i].dev,
502 array_label, volume_label,
503 devices[i].nsid, nguid_str,
504 slta ? "A" : "B", "unknown",
505 version, lba,
506 le64_to_cpu(devices[i].ns.nsze),
507 le64_to_cpu(devices[i].ns.nuse));
508 goto out;
509 }
510 }
511
512 for (i = 0; i < count; i++) {
513 /* fetch info for all devices */
514 netapp_get_ns_attrs(size, used, blk_size, version,
515 &lba, &devices[i].ctrl, &devices[i].ns);
516 netapp_convert_string(array_label,
517 (char *)&devices[i].ctrl.vs[20],
518 ARRAY_LABEL_LEN60 / 2);
519 slta = devices[i].ctrl.vs[0] & 0x1;
520 netapp_convert_string(volume_label,
521 (char *)devices[i].ns.vs,
522 VOLUME_LABEL_LEN60 / 2);
523 netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
524 netapp_smdevice_json(json_devices, devices[i].dev,
525 array_label, volume_label, devices[i].nsid,
526 nguid_str, slta ? "A" : "B", "unknown",
527 version, lba,
528 le64_to_cpu(devices[i].ns.nsze),
529 le64_to_cpu(devices[i].ns.nuse));
530 }
531
532out:
533 /* complete the json output */
534 json_object_add_value_array(root, "SMdevices", json_devices)json_object_object_add(root, "SMdevices", json_devices);
535 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
536 printf("\n");
537 json_free_object(root)json_object_put(root);
538}
539
540static void netapp_ontapdevices_print_verbose(struct ontapdevice_info *devices,
541 int count, int format, const char *devname)
542{
543 char vsname[ONTAP_LABEL_LEN260] = " ";
544 char nspath[ONTAP_NS_PATHLEN525] = " ";
545 unsigned long long lba;
546 char size[128], used[128];
547 char blk_size[128], version[9];
548 char uuid_str[37] = " ";
549 char subnqn[257], subsysname[65];
550 int i;
551
552 char *formatstr = NULL((void*)0);
553 char basestr[] =
554 "%s, Vserver %s, Subsystem %s, Namespace Path %s, NSID %d, "
555 "UUID %s, Size %s, Used %s, Format %s, Version %s\n";
556 char columnstr[] = "%-16s %-25s %-25s %-50s %-4d %-38s %-9s %-9s %-9s %-9s\n";
557
558 if (format == NNORMAL)
559 formatstr = basestr;
560 else if (format == NCOLUMN) {
561 printf("%-16s %-25s %-25s %-50s %-4s %-38s %-9s %-9s %-9s %-9s\n",
562 "Device", "Vserver", "Subsystem", "Namespace Path",
563 "NSID", "UUID", "Size", "Used",
564 "Format", "Version");
565 printf("%-16s %-25s %-25s %-50s %-4s %-38s %-9s %-9s %-9s %-9s\n",
566 "----------------", "-------------------------",
567 "-------------------------",
568 "--------------------------------------------------",
569 "----", "--------------------------------------",
570 "---------", "---------", "---------", "---------");
571 formatstr = columnstr;
572 }
573
574 for (i = 0; i < count; i++) {
575 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
576 /* found the device, fetch and print for that alone */
577 netapp_get_ns_attrs(size, used, blk_size, version,
578 &lba, &devices[i].ctrl, &devices[i].ns);
579 ontap_get_subsysname(subnqn, subsysname,
580 &devices[i].ctrl);
581 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
582 netapp_get_ontap_labels(vsname, nspath,
583 devices[i].log_data);
584
585 printf(formatstr, devices[i].dev, vsname, subsysname,
586 nspath, devices[i].nsid, uuid_str,
587 size, used, blk_size, version);
588 return;
589 }
590 }
591
592 for (i = 0; i < count; i++) {
593 /* fetch info and print for all devices */
594 netapp_get_ns_attrs(size, used, blk_size, version,
595 &lba, &devices[i].ctrl, &devices[i].ns);
596 ontap_get_subsysname(subnqn, subsysname,
597 &devices[i].ctrl);
598 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
599 netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
600
601 printf(formatstr, devices[i].dev, vsname, subsysname,
602 nspath, devices[i].nsid, uuid_str,
603 size, used, blk_size, version);
604 }
605}
606
607static void netapp_ontapdevices_print_regular(struct ontapdevice_info *devices,
608 int count, int format, const char *devname)
609{
610 char vsname[ONTAP_LABEL_LEN260] = " ";
611 char nspath[ONTAP_NS_PATHLEN525] = " ";
612 unsigned long long lba;
613 char size[128];
614 char uuid_str[37] = " ";
615 char subnqn[257], subsysname[65];
616 int i;
617
618 char *formatstr = NULL((void*)0);
619 char basestr[] =
620 "%s, Vserver %s, Subsystem %s, Namespace Path %s, NSID %d, UUID %s, %s\n";
621 char columnstr[] = "%-16s %-25s %-25s %-50s %-4d %-38s %-9s\n";
622
623 if (format == NNORMAL)
624 formatstr = basestr;
625 else if (format == NCOLUMN) {
626 printf("%-16s %-25s %-25s %-50s %-4s %-38s %-9s\n",
627 "Device", "Vserver", "Subsystem", "Namespace Path",
628 "NSID", "UUID", "Size");
629 printf("%-16s %-25s %-25s %-50s %-4s %-38s %-9s\n",
630 "----------------", "-------------------------",
631 "-------------------------",
632 "--------------------------------------------------",
633 "----", "--------------------------------------",
634 "---------");
635 formatstr = columnstr;
636 }
637
638 for (i = 0; i < count; i++) {
639 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
640 /* found the device, fetch and print for that alone */
641 netapp_get_ns_size(size, &lba, &devices[i].ns);
642 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
643 netapp_get_ontap_labels(vsname, nspath,
644 devices[i].log_data);
645 ontap_get_subsysname(subnqn, subsysname,
646 &devices[i].ctrl);
647
648 printf(formatstr, devices[i].dev, vsname, subsysname,
649 nspath, devices[i].nsid, uuid_str, size);
650 return;
651 }
652 }
653
654 for (i = 0; i < count; i++) {
655 /* fetch info and print for all devices */
656 netapp_get_ns_size(size, &lba, &devices[i].ns);
657 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
658 netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
659 ontap_get_subsysname(subnqn, subsysname, &devices[i].ctrl);
660
661 printf(formatstr, devices[i].dev, vsname, subsysname,
662 nspath, devices[i].nsid, uuid_str, size);
663 }
664}
665
666static void netapp_ontapdevices_print_json(struct ontapdevice_info *devices,
667 int count, const char *devname)
668{
669 struct json_object *root = NULL((void*)0);
670 struct json_object *json_devices = NULL((void*)0);
671 char vsname[ONTAP_LABEL_LEN260] = " ";
672 char nspath[ONTAP_NS_PATHLEN525] = " ";
673 unsigned long long lba;
674 char size[128], used[128];
675 char blk_size[128], version[9];
676 char uuid_str[37] = " ";
677 char subnqn[257], subsysname[65];
678 int i;
679
680 /* prepare for the json output */
681 root = json_create_object()json_object_new_object();
682 json_devices = json_create_array()json_object_new_array();
683
684 for (i = 0; i < count; i++) {
685 if (devname && !strcmp(devname, basename__xpg_basename(devices[i].dev))) {
686 /* found the device, fetch info for that alone */
687 netapp_get_ns_attrs(size, used, blk_size, version,
688 &lba, &devices[i].ctrl, &devices[i].ns);
689 ontap_get_subsysname(subnqn, subsysname,
690 &devices[i].ctrl);
691 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
692 netapp_get_ontap_labels(vsname, nspath,
693 devices[i].log_data);
694
695 netapp_ontapdevice_json(json_devices, devices[i].dev,
696 vsname, subsysname, nspath,
697 devices[i].nsid, uuid_str, lba, version,
698 le64_to_cpu(devices[i].ns.nsze),
699 le64_to_cpu(devices[i].ns.nuse));
700 goto out;
701 }
702 }
703
704 for (i = 0; i < count; i++) {
705 /* fetch info for all devices */
706 netapp_get_ns_attrs(size, used, blk_size, version,
707 &lba, &devices[i].ctrl, &devices[i].ns);
708 ontap_get_subsysname(subnqn, subsysname,
709 &devices[i].ctrl);
710 libnvme_uuid_to_string(devices[i].uuid, uuid_str);
711 netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
712
713 netapp_ontapdevice_json(json_devices, devices[i].dev,
714 vsname, subsysname, nspath,
715 devices[i].nsid, uuid_str, lba, version,
716 le64_to_cpu(devices[i].ns.nsze),
717 le64_to_cpu(devices[i].ns.nuse));
718 }
719
720out:
721 /* complete the json output */
722 json_object_add_value_array(root, "ONTAPdevices", json_devices)json_object_object_add(root, "ONTAPdevices", json_devices);
723 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
724 printf("\n");
725 json_free_object(root)json_object_put(root);
726}
727
728static int nvme_get_ontap_c2_log(struct libnvme_transport_handle *hdl, __u32 nsid, void *buf, __u32 buflen)
729{
730 struct libnvme_passthru_cmd get_log;
731 int err;
732
733 memset(buf, 0, buflen);
734 memset(&get_log, 0, sizeof(struct libnvme_passthru_cmd));
735
736 get_log.opcode = nvme_admin_get_log_page;
737 get_log.nsid = nsid;
738 get_log.addr = (__u64)(uintptr_t)buf;
739 get_log.data_len = buflen;
740
741 __u32 numd = (get_log.data_len >> 2) - 1;
742 __u32 numdu = numd >> 16;
743 __u32 numdl = numd & 0xFFFF;
744
745 get_log.cdw10 = ONTAP_C2_LOG_ID0xC2 | (numdl << 16);
746 get_log.cdw10 |= ONTAP_C2_LOG_NSINFO_LSP << 8;
747 get_log.cdw11 = numdu;
748
749 err = libnvme_exec_admin_passthru(hdl, &get_log);
750 if (err) {
751 fprintf(stderrstderr, "ioctl error %0x\n", err);
752 return 1;
753 }
754
755 return 0;
756}
757
758static int netapp_smdevices_get_info(struct libnvme_transport_handle *hdl,
759 struct smdevice_info *item,
760 const char *dev)
761{
762 int err;
763
764 err = nvme_identify_ctrl(hdl, &item->ctrl);
765 if (err) {
766 fprintf(stderrstderr,
767 "Identify Controller failed to %s (%s)\n", dev,
768 err < 0 ? libnvme_strerror(-err) :
769 libnvme_status_to_string(err, false0));
770 return 0;
771 }
772
773 if (strncmp("NetApp E-Series", item->ctrl.mn, 15) != 0)
774 return 0; /* not the right model of controller */
775
776 err = libnvme_get_nsid(hdl, &item->nsid);
777 if (err)
778 return err;
779
780 err = nvme_identify_ns(hdl, item->nsid, &item->ns);
781 if (err) {
782 fprintf(stderrstderr,
783 "Unable to identify namespace for %s (%s)\n",
784 dev, err < 0 ? libnvme_strerror(-err) :
785 libnvme_status_to_string(err, false0));
786 return 0;
787 }
788 strncpy(item->dev, dev, sizeof(item->dev) - 1);
789
790 return 1;
791}
792
793static int netapp_ontapdevices_get_info(struct libnvme_transport_handle *hdl,
794 struct ontapdevice_info *item,
795 const char *dev)
796{
797 void *nsdescs;
798 int err;
799
800 err = nvme_identify_ctrl(hdl, &item->ctrl);
801 if (err) {
802 fprintf(stderrstderr, "Identify Controller failed to %s (%s)\n",
803 dev, err < 0 ? libnvme_strerror(-err) :
804 libnvme_status_to_string(err, false0));
805 return 0;
806 }
807
808 if (strncmp("NetApp ONTAP Controller", item->ctrl.mn, 23) != 0)
809 /* not the right controller model */
810 return 0;
811
812 err = libnvme_get_nsid(hdl, &item->nsid);
Value stored to 'err' is never read
813
814 err = nvme_identify_ns(hdl, item->nsid, &item->ns);
815 if (err) {
816 fprintf(stderrstderr, "Unable to identify namespace for %s (%s)\n",
817 dev, err < 0 ? libnvme_strerror(-err) :
818 libnvme_status_to_string(err, false0));
819 return 0;
820 }
821
822 nsdescs = libnvme_alloc(0x1000);
823 if (!nsdescs) {
824 fprintf(stderrstderr, "Cannot allocate controller list payload\n");
825 return 0;
826 }
827
828 memset(nsdescs, 0, 0x1000);
829
830 err = nvme_identify_ns_descs_list(hdl, item->nsid, nsdescs);
831 if (err) {
832 fprintf(stderrstderr, "Unable to identify namespace descriptor for %s (%s)\n",
833 dev, err < 0 ? libnvme_strerror(-err) :
834 libnvme_status_to_string(err, false0));
835 libnvme_free(nsdescs);
836 return 0;
837 }
838
839 memcpy(item->uuid, nsdescs + sizeof(struct nvme_ns_id_desc), sizeof(item->uuid));
840 libnvme_free(nsdescs);
841
842 err = nvme_get_ontap_c2_log(hdl, item->nsid, item->log_data, ONTAP_C2_LOG_SIZE4096);
843 if (err) {
844 fprintf(stderrstderr, "Unable to get log page data for %s (%s)\n",
845 dev, err < 0 ? libnvme_strerror(-err) :
846 libnvme_status_to_string(err, false0));
847 return 0;
848 }
849
850 strncpy(item->dev, dev, sizeof(item->dev) - 1);
851
852 return 1;
853}
854
855static int netapp_nvme_filter(const struct dirent *d)
856{
857 char path[264];
858 struct stat bd;
859 int ctrl, ns, partition;
860
861 if (d->d_name[0] == '.')
862 return 0;
863
864 if (strstr(d->d_name, "nvme")) {
865 snprintf(path, sizeof(path), "%s%s", dev_path, d->d_name);
866 if (stat(path, &bd))
867 return 0;
868 if (sscanf(d->d_name, "nvme%dn%d", &ctrl, &ns) != 2)
869 return 0;
870 if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &partition) == 3)
871 return 0;
872 return 1;
873 }
874 return 0;
875}
876
877static int netapp_output_format(char *format)
878{
879 if (!format)
880 return -EINVAL22;
881 if (!strcmp(format, "normal"))
882 return NNORMAL;
883#ifdef CONFIG_JSONC
884 if (!strcmp(format, "json"))
885 return NJSON;
886#endif /* CONFIG_JSONC */
887 if (!strcmp(format, "column"))
888 return NCOLUMN;
889 return -EINVAL22;
890}
891
892/* handler for 'nvme netapp smdevices' */
893static int netapp_smdevices(int argc, char **argv, struct command *acmd,
894 struct plugin *plugin)
895{
896 const char *desc = "Display information about E-Series volumes.";
897 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = libnvme_create_global_ctx(stdoutstdout, LIBNVME_DEFAULT_LOGLEVELLIBNVME_LOG_WARN);
898 struct dirent **devices;
899 int num, i, ret, fmt;
900 struct smdevice_info *smdevices;
901 char path[264];
902 char *devname = NULL((void*)0);
903 int num_smdevices = 0;
904 struct libnvme_transport_handle *hdl;
905
906 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
907
908 if (!ctx)
909 return -ENOMEM12;
910
911 ret = argconfig_parse(argc, argv, desc, opts);
912 if (ret < 0)
913 return ret;
914
915 fmt = netapp_output_format(nvme_args.output_format);
916 if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
917 fprintf(stderrstderr, "Unrecognized output format: %s\n",
918 nvme_args.output_format);
919 return -EINVAL22;
920 }
921
922 num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
923 if (num <= 0) {
924 fprintf(stderrstderr, "No smdevices detected\n");
925 return num;
926 }
927
928 if (optind < argc)
929 devname = basename__xpg_basename(argv[optind++]);
930
931 if (devname) {
932 int subsys_num, nsid;
933 struct stat st;
934 char path[512];
935
936 if (sscanf(devname, "nvme%dn%d", &subsys_num, &nsid) != 2) {
937 fprintf(stderrstderr, "Invalid device name %s\n", devname);
938 return -EINVAL22;
939 }
940
941 sprintf(path, "/dev/%s", devname);
942 if (stat(path, &st) != 0) {
943 fprintf(stderrstderr, "%s does not exist\n", path);
944 return -EINVAL22;
945 }
946 }
947
948 smdevices = calloc(num, sizeof(*smdevices));
949 if (!smdevices) {
950 fprintf(stderrstderr, "Unable to allocate memory for devices\n");
951 return -ENOMEM12;
952 }
953
954 for (i = 0; i < num; i++) {
955 snprintf(path, sizeof(path), "%s%s", dev_path,
956 devices[i]->d_name);
957 ret = libnvme_open(ctx, path, &hdl);
958 if (ret) {
959 fprintf(stderrstderr, "Unable to open %s: %s\n", path,
960 libnvme_strerror(-ret));
961 continue;
962 }
963
964 num_smdevices += netapp_smdevices_get_info(hdl,
965 &smdevices[num_smdevices], path);
966 libnvme_close(hdl);
967 }
968
969 if (num_smdevices) {
970 if (fmt == NNORMAL || fmt == NCOLUMN) {
971 if (nvme_args.verbose)
972 netapp_smdevices_print_verbose(smdevices,
973 num_smdevices, fmt, devname);
974 else
975 netapp_smdevices_print_regular(smdevices,
976 num_smdevices, fmt, devname);
977 }
978 else if (fmt == NJSON)
979 netapp_smdevices_print_json(smdevices,
980 num_smdevices, devname);
981 } else
982 fprintf(stderrstderr, "No smdevices detected\n");
983
984 for (i = 0; i < num; i++)
985 free(devices[i]);
986 free(devices);
987 free(smdevices);
988 return 0;
989}
990
991/* handler for 'nvme netapp ontapdevices' */
992static int netapp_ontapdevices(int argc, char **argv, struct command *acmd,
993 struct plugin *plugin)
994{
995 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = libnvme_create_global_ctx(stdoutstdout, LIBNVME_DEFAULT_LOGLEVELLIBNVME_LOG_WARN);
996 const char *desc = "Display information about ONTAP devices.";
997 struct dirent **devices;
998 int num, i, ret, fmt;
999 struct ontapdevice_info *ontapdevices;
1000 char path[264];
1001 char *devname = NULL((void*)0);
1002 int num_ontapdevices = 0;
1003 struct libnvme_transport_handle *hdl;
1004
1005 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1006
1007 if (!ctx)
1008 return -ENOMEM12;
1009
1010 ret = argconfig_parse(argc, argv, desc, opts);
1011 if (ret < 0)
1012 return ret;
1013
1014 fmt = netapp_output_format(nvme_args.output_format);
1015 if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
1016 fprintf(stderrstderr, "Unrecognized output format: %s\n",
1017 nvme_args.output_format);
1018 return -EINVAL22;
1019 }
1020
1021 if (optind < argc)
1022 devname = basename__xpg_basename(argv[optind++]);
1023
1024 if (devname) {
1025 int subsys_num, nsid;
1026 struct stat st;
1027 char path[512];
1028
1029 if (sscanf(devname, "nvme%dn%d", &subsys_num, &nsid) != 2) {
1030 fprintf(stderrstderr, "Invalid device name %s\n", devname);
1031 return -EINVAL22;
1032 }
1033
1034 sprintf(path, "/dev/%s", devname);
1035 if (stat(path, &st) != 0) {
1036 fprintf(stderrstderr, "%s does not exist\n", path);
1037 return -EINVAL22;
1038 }
1039 }
1040
1041 num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
1042 if (num <= 0) {
1043 fprintf(stderrstderr, "No ontapdevices detected\n");
1044 return num;
1045 }
1046
1047 ontapdevices = calloc(num, sizeof(*ontapdevices));
1048 if (!ontapdevices) {
1049 fprintf(stderrstderr, "Unable to allocate memory for devices\n");
1050 return -ENOMEM12;
1051 }
1052
1053 for (i = 0; i < num; i++) {
1054 snprintf(path, sizeof(path), "%s%s", dev_path,
1055 devices[i]->d_name);
1056 ret = libnvme_open(ctx, path, &hdl);
1057 if (ret) {
1058 fprintf(stderrstderr, "Unable to open %s: %s\n", path,
1059 libnvme_strerror(-ret));
1060 continue;
1061 }
1062
1063 num_ontapdevices += netapp_ontapdevices_get_info(hdl,
1064 &ontapdevices[num_ontapdevices], path);
1065
1066 libnvme_close(hdl);
1067 }
1068
1069 if (num_ontapdevices) {
1070 if (fmt == NNORMAL || fmt == NCOLUMN) {
1071 if (nvme_args.verbose)
1072 netapp_ontapdevices_print_verbose(ontapdevices,
1073 num_ontapdevices, fmt, devname);
1074 else
1075 netapp_ontapdevices_print_regular(ontapdevices,
1076 num_ontapdevices, fmt, devname);
1077 }
1078 else if (fmt == NJSON)
1079 netapp_ontapdevices_print_json(ontapdevices,
1080 num_ontapdevices, devname);
1081 } else
1082 fprintf(stderrstderr, "No ontapdevices detected\n");
1083
1084 for (i = 0; i < num; i++)
1085 free(devices[i]);
1086 free(devices);
1087 free(ontapdevices);
1088 return 0;
1089}