Bug Summary

File:.build-ci/../plugins/solidigm/solidigm-internal-logs.c
Warning:line 513, column 23
Access to field 'cfg' results in a dereference of a null pointer (loaded from variable 'ilog')

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 solidigm-internal-logs.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/solidigm/solidigm-internal-logs.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023 Solidigm.
4 *
5 * Authors: leonardo.da.cunha@solidigm.com
6 * shankaralingegowda.singonahalli@solidigm.com
7 * haro.panosyan@solidigm.com
8 */
9
10#include <fcntl.h>
11#include <errno(*__errno_location ()).h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <inttypes.h>
16#include <time.h>
17
18#include <libnvme.h>
19
20#include "common.h"
21#include "nvme-cmds.h"
22#include "nvme-print.h"
23#include "nvme.h"
24#include "plugin.h"
25
26#include "solidigm-util.h"
27
28#define DWORD_SIZE4 4
29#define LOG_FILE_PERMISSION0644 0644
30#define ATMOS_MODEL_PREFIX"SOLIDIGM SB5" "SOLIDIGM SB5"
31
32enum log_type {
33 NLOG = 0,
34 EVENTLOG = 1,
35 ASSERTLOG = 2,
36 HIT,
37 CIT,
38 ALL,
39 EXTENDED
40};
41
42#pragma pack(push, internal_logs, 1)
43struct version {
44 __u16 major;
45 __u16 minor;
46};
47
48struct event_dump_instance {
49 __u32 numeventdumps;
50 __u32 coresize;
51 __u32 coreoffset;
52 __u32 eventidoffset[16];
53 __u8 eventIdValidity[16];
54};
55
56struct commom_header {
57 struct version ver;
58 __u32 header_size;
59 __u32 log_size;
60 __u32 numcores;
61};
62
63struct event_dump_header {
64 struct commom_header header;
65 __u32 eventidsize;
66 struct event_dump_instance edumps[0];
67};
68
69struct assert_dump_core {
70 __u32 coreoffset;
71 __u32 assertsize;
72 __u8 assertdumptype;
73 __u8 assertvalid;
74 __u8 reserved[2];
75};
76
77struct assert_dump_header {
78 struct commom_header header;
79 struct assert_dump_core core[];
80};
81
82struct nlog_dump_header_common {
83 struct version ver;
84 __u32 logselect;
85 __u32 totalnlogs;
86 __u32 nlognum;
87 char nlogname[4];
88 __u32 nlogbytesize;
89 __u32 nlogprimarybuffsize;
90 __u32 tickspersecond;
91 __u32 corecount;
92};
93
94struct nlog_dump_header3_0 {
95 struct nlog_dump_header_common common;
96 __u32 nlogpausestatus;
97 __u32 selectoffsetref;
98 __u32 selectnlogpause;
99 __u32 selectaddedoffset;
100 __u32 nlogbufnum;
101 __u32 nlogbufnummax;
102};
103
104struct nlog_dump_header4_0 {
105 struct nlog_dump_header_common common;
106 __u64 nlogpausestatus;
107 __u32 selectoffsetref;
108 __u32 selectnlogpause;
109 __u32 selectaddedoffset;
110 __u32 nlogbufnum;
111 __u32 nlogbufnummax;
112 __u32 coreselected;
113 __u32 reserved[2];
114};
115
116struct nlog_dump_header4_1 {
117 struct nlog_dump_header_common common;
118 __u64 nlogpausestatus;
119 __u32 selectoffsetref;
120 __u32 selectnlogpause;
121 __u32 selectaddedoffset;
122 __u32 nlogbufnum;
123 __u32 nlogbufnummax;
124 __u32 coreselected;
125 __u32 lpaPointer1High;
126 __u32 lpaPointer1Low;
127 __u32 lpaPointer2High;
128 __u32 lpaPointer2Low;
129};
130
131#pragma pack(pop, internal_logs)
132
133struct config {
134 char *out_dir;
135 char *type;
136 bool_Bool verbose;
137};
138
139struct ilog {
140 struct config *cfg;
141 int count;
142 struct nvme_id_ctrl id_ctrl;
143};
144
145static void print_nlog_header(__u8 *buffer)
146{
147 struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *) buffer;
148
149 if (nlog_header->ver.major >= 3) {
150 printf("Version Major %u\n", nlog_header->ver.major);
151 printf("Version Minor %u\n", nlog_header->ver.minor);
152 printf("Log_select %u\n", nlog_header->logselect);
153 printf("totalnlogs %u\n", nlog_header->totalnlogs);
154 printf("nlognum %u\n", nlog_header->nlognum);
155 printf("nlogname %c%c%c%c\n", nlog_header->nlogname[3], nlog_header->nlogname[2],
156 nlog_header->nlogname[1], nlog_header->nlogname[0]);
157 printf("nlogbytesize %u\n", nlog_header->nlogbytesize);
158 printf("nlogprimarybuffsize %u\n", nlog_header->nlogprimarybuffsize);
159 printf("tickspersecond %u\n", nlog_header->tickspersecond);
160 printf("corecount %u\n", nlog_header->corecount);
161 }
162 if (nlog_header->ver.major >= 4) {
163 struct nlog_dump_header4_0 *nlog_header = (struct nlog_dump_header4_0 *) buffer;
164
165 printf("nlogpausestatus %"PRIu64"l" "u""\n", (uint64_t)nlog_header->nlogpausestatus);
166 printf("selectoffsetref %u\n", nlog_header->selectoffsetref);
167 printf("selectnlogpause %u\n", nlog_header->selectnlogpause);
168 printf("selectaddedoffset %u\n", nlog_header->selectaddedoffset);
169 printf("nlogbufnum %u\n", nlog_header->nlogbufnum);
170 printf("nlogbufnummax %u\n", nlog_header->nlogbufnummax);
171 printf("coreselected %u\n\n", nlog_header->coreselected);
172 }
173}
174
175#define INTERNAL_LOG_MAX_BYTE_TRANSFER4096 4096
176#define INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4) (INTERNAL_LOG_MAX_BYTE_TRANSFER4096 / 4)
177
178static int cmd_dump_repeat(struct libnvme_passthru_cmd *cmd, __u32 total_dw_size,
179 int out_fd, struct libnvme_transport_handle *hdl, bool_Bool force_max_transfer)
180{
181 int err = 0;
182
183 while (total_dw_size > 0) {
184 size_t dword_tfer = min(INTERNAL_LOG_MAX_DWORD_TRANSFER, total_dw_size)(((4096 / 4)) > (total_dw_size) ? (total_dw_size) : ((4096
/ 4)))
;
185
186 cmd->cdw10 = force_max_transfer ? INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4) : dword_tfer;
187 cmd->data_len = dword_tfer * 4;
188 err = libnvme_exec_admin_passthru(hdl, cmd);
189 if (err)
190 return err;
191
192 if (out_fd > 0) {
193 err = write(out_fd, (const void *)(uintptr_t)cmd->addr, cmd->data_len);
194 if (err < 0) {
195 perror("write failure");
196 return err;
197 }
198 err = 0;
199 }
200 total_dw_size -= dword_tfer;
201 cmd->cdw13 += dword_tfer;
202 }
203 return err;
204}
205
206static int write_header(__u8 *buf, int fd, size_t amnt)
207{
208 if (write(fd, buf, amnt) < 0)
209 return 1;
210 return 0;
211}
212
213static int read_header(struct libnvme_passthru_cmd *cmd, struct libnvme_transport_handle *hdl)
214{
215 memset((void *)(uintptr_t)cmd->addr, 0, INTERNAL_LOG_MAX_BYTE_TRANSFER4096);
216 return cmd_dump_repeat(cmd, INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4), -1, hdl, false0);
217}
218
219static int get_serial_number(char *str, struct libnvme_transport_handle *hdl)
220{
221 struct nvme_id_ctrl ctrl = {0};
222 int err;
223
224 err = nvme_identify_ctrl(hdl, &ctrl);
225 if (err)
226 return err;
227
228 /* Remove trailing spaces */
229 for (int i = sizeof(ctrl.sn) - 1; i && ctrl.sn[i] == ' '; i--)
230 ctrl.sn[i] = '\0';
231 sprintf(str, "%-.*s", (int)sizeof(ctrl.sn), ctrl.sn);
232 return err;
233}
234
235static int ilog_dump_assert_logs(struct libnvme_transport_handle *hdl, struct ilog *ilog)
236{
237 __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096];
238 __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096];
239 __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0);
240 char file_name[] = "AssertLog.bin";
241 struct assert_dump_header *ad = (struct assert_dump_header *) head_buf;
242 struct libnvme_passthru_cmd cmd = {
243 .opcode = 0xd2,
244 .nsid = NVME_NSID_ALL,
245 .addr = (__u64)(void *)head_buf,
246 .cdw12 = ASSERTLOG,
247 .cdw13 = 0,
248 };
249 int output, err;
250
251 err = read_header(&cmd, hdl);
252 if (err)
253 return err;
254
255 if (asprintf(&file_path, "%.*s/%s",
256 (int) (sizeof(file_path) - sizeof(file_name) - 1),
257 ilog->cfg->out_dir, file_name) < 0)
258 return -errno(*__errno_location ());
259 output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644);
260 if (output < 0)
261 return -errno(*__errno_location ());
262 err = write_header((__u8 *)ad, output, ad->header.header_size * DWORD_SIZE4);
263 if (err) {
264 perror("write failure");
265 close(output);
266 return err;
267 }
268 cmd.addr = (__u64)(void *)buf;
269
270 if (ilog->cfg->verbose) {
271 printf("Assert Log, cores: %d log size: %d header size: %d\n", ad->header.numcores,
272 ad->header.log_size * DWORD_SIZE4, ad->header.header_size * DWORD_SIZE4);
273 for (__u32 i = 0; i < ad->header.numcores; i++)
274 printf("core %d assert size: %d\n", i, ad->core[i].assertsize * DWORD_SIZE4);
275 }
276
277 for (__u32 i = 0; i < ad->header.numcores; i++) {
278 if (!ad->core[i].assertvalid)
279 continue;
280 cmd.cdw13 = ad->core[i].coreoffset;
281 err = cmd_dump_repeat(&cmd, ad->core[i].assertsize, output,
282 hdl, false0);
283 if (err) {
284 close(output);
285 return err;
286 }
287 }
288 close(output);
289 printf("Successfully wrote Assert to %s\n", file_path);
290 return err;
291}
292
293static int ilog_dump_event_logs(struct libnvme_transport_handle *hdl, struct ilog *ilog)
294{
295 __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096];
296 __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096];
297 __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0);
298 struct event_dump_header *ehdr = (struct event_dump_header *) head_buf;
299 struct libnvme_passthru_cmd cmd = {
300 .opcode = 0xd2,
301 .nsid = NVME_NSID_ALL,
302 .addr = (__u64)(void *)head_buf,
303 .cdw12 = EVENTLOG,
304 .cdw13 = 0,
305 };
306 int output;
307 int core_num, err;
308
309 err = read_header(&cmd, hdl);
310 if (err)
311 return err;
312 if (asprintf(&file_path, "%s/EventLog.bin", ilog->cfg->out_dir))
313 return -errno(*__errno_location ());
314 output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644);
315 if (output < 0)
316 return -errno(*__errno_location ());
317 err = write_header(head_buf, output, INTERNAL_LOG_MAX_BYTE_TRANSFER4096);
318
319 core_num = ehdr->header.numcores;
320
321 if (err) {
322 close(output);
323 return err;
324 }
325 cmd.addr = (__u64)(void *)buf;
326
327 if (ilog->cfg->verbose)
328 printf("Event Log, cores: %d log size: %d\n", core_num, ehdr->header.log_size * 4);
329
330 for (__u32 j = 0; j < core_num; j++) {
331 if (ilog->cfg->verbose) {
332 for (int k = 0 ; k < 16; k++) {
333 printf("core: %d event: %d ", j, k);
334 printf("validity: %d ", ehdr->edumps[j].eventIdValidity[k]);
335 printf("offset: %d\n", ehdr->edumps[j].eventidoffset[k]);
336 }
337 }
338 cmd.cdw13 = ehdr->edumps[j].coreoffset;
339 err = cmd_dump_repeat(&cmd, ehdr->edumps[j].coresize,
340 output, hdl, false0);
341 if (err) {
342 close(output);
343 return err;
344 }
345 }
346 close(output);
347 printf("Successfully wrote Events to %s\n", file_path);
348 return err;
349}
350
351static size_t get_nlog_header_size(struct nlog_dump_header_common *nlog_header)
352{
353 switch (nlog_header->ver.major) {
354 case 3:
355 return sizeof(struct nlog_dump_header3_0);
356 case 4:
357 if (nlog_header->ver.minor == 0)
358 return sizeof(struct nlog_dump_header4_0);
359 return sizeof(struct nlog_dump_header4_1);
360 default:
361 return INTERNAL_LOG_MAX_BYTE_TRANSFER4096;
362 }
363
364}
365
366/* dumps nlogs from specified core or all cores when core = -1 */
367static int ilog_dump_nlogs(struct libnvme_transport_handle *hdl, struct ilog *ilog, int core)
368{
369 int err = 0;
370 __u32 count, core_num;
371 __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096];
372 __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0);
373 struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *)buf;
374 struct libnvme_passthru_cmd cmd = {
375 .opcode = 0xd2,
376 .nsid = NVME_NSID_ALL,
377 .addr = (__u64)(void *)buf
378 };
379
380 struct dump_select {
381 union {
382 struct {
383 __u32 selectLog : 3;
384 __u32 selectCore : 2;
385 __u32 selectNlog : 8;
386 };
387 __u32 raw;
388 };
389 } log_select;
390 int output;
391 bool_Bool is_open = false0;
392 size_t header_size = 0;
393
394 log_select.selectCore = core < 0 ? 0 : core;
395 do {
396 log_select.selectNlog = 0;
397 do {
398 cmd.cdw13 = 0;
399 cmd.cdw12 = log_select.raw;
400 err = read_header(&cmd, hdl);
401 if (err) {
402 if (is_open)
403 close(output);
404 return err;
405 }
406 count = nlog_header->totalnlogs;
407 core_num = core < 0 ? nlog_header->corecount : 0;
408 if (!header_size) {
409 if (asprintf(&file_path, "%s/NLog.bin", ilog->cfg->out_dir) >= 0) {
410 output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC,open((file_path), (01 | 0100 | 01000), 0644)
411 LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644);
412 if (output < 0)
413 return -errno(*__errno_location ());
414 } else
415 return -errno(*__errno_location ());
416 header_size = get_nlog_header_size(nlog_header);
417 is_open = true1;
418 }
419 err = write_header(buf, output, header_size);
420 if (err)
421 break;
422 if (ilog->cfg->verbose)
423 print_nlog_header(buf);
424 cmd.cdw13 = 0x400;
425 err = cmd_dump_repeat(&cmd, nlog_header->nlogbytesize / 4,
426 output, hdl, true1);
427 if (err)
428 break;
429 } while (++log_select.selectNlog < count);
430 if (err)
431 break;
432 } while (++log_select.selectCore < core_num);
433 if (is_open) {
434 close(output);
435 printf("Successfully wrote Nlog to %s\n", file_path);
436 }
437 return err;
438}
439
440int ensure_dir(const char *parent_dir_name, const char *name)
441{
442 __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0);
443 struct stat sb;
444
445 if (asprintf(&file_path, "%s/%s", parent_dir_name, name) < 0)
446 return -errno(*__errno_location ());
447
448 if (!(stat(file_path, &sb) == 0 && S_ISDIR(sb.st_mode)((((sb.st_mode)) & 0170000) == (0040000)))) {
449 if (mkdir(file_path, 777) != 0) {
450 perror(file_path);
451 return -errno(*__errno_location ());
452 }
453 }
454
455 return 0;
456}
457
458struct log {
459 __u8 id;
460 const char *desc;
461 size_t buffer_size;
462 __u8 *buffer;
463};
464
465static int log_save(struct log *log, const char *parent_dir_name, const char *subdir_name,
466 const char *file_name, __u8 *buffer, size_t buf_size)
467{
468 __cleanup_fd__attribute__((cleanup(cleanup_fd))) int output = -1;
469 __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0);
470 size_t bytes_remaining = 0;
471
472 ensure_dir(parent_dir_name, subdir_name);
473
474 if (asprintf(&file_path, "%s/%s/%s", parent_dir_name, subdir_name, file_name) < 0)
475 return -errno(*__errno_location ());
476
477 output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644);
478 if (output < 0)
479 return -errno(*__errno_location ());
480
481 bytes_remaining = buf_size;
482
483 while (bytes_remaining) {
484 ssize_t bytes_written = write(output, buffer, bytes_remaining);
485
486 if (bytes_written < 0)
487 return -errno(*__errno_location ());
488
489 bytes_remaining -= bytes_written;
490 buffer += bytes_written;
491 }
492 printf("Successfully wrote %s to %s\n", log->desc, file_path);
493 return 0;
494}
495
496static int ilog_dump_identify_page(struct libnvme_transport_handle *hdl,
497 struct ilog *ilog, struct log *cns, __u32 nsid)
498{
499 __u8 data[NVME_IDENTIFY_DATA_SIZE];
500 __u8 *buff = cns->buffer ? cns->buffer : data;
6
Assuming field 'buffer' is null
7
'?' condition is false
501 __cleanup_free__attribute__((cleanup(freep))) char *filename = NULL((void*)0);
502 int err;
503
504 err = nvme_identify(hdl, nsid, NVME_CSI_NVM, cns->id, buff,
505 sizeof(data));
506 if (err)
8
Assuming 'err' is 0
9
Taking false branch
507 return err;
508
509 if (asprintf(&filename, "cntid_0_cns_%d_nsid_%d_nvmsetid_0_csi_0.bin",
10
Assuming the condition is false
11
Taking false branch
510 cns->id, nsid) < 0)
511 return -errno(*__errno_location ());
512
513 return log_save(cns, ilog->cfg->out_dir, "identify", filename, buff,
12
Access to field 'cfg' results in a dereference of a null pointer (loaded from variable 'ilog')
514 sizeof(data));
515}
516
517static int ilog_ensure_dump_id_ctrl(struct libnvme_transport_handle *hdl,
518 struct ilog *ilog)
519{
520 static bool_Bool first = true1;
521 static int err;
522 struct log idctrl = {NVME_IDENTIFY_CNS_CTRL, "Id Controller Data", sizeof(ilog->id_ctrl),
523 (__u8 *) &ilog->id_ctrl};
524
525 if (!first
2.1
'first' is true
)
3
Taking false branch
526 return err;
527
528 first = false0;
529 err = ilog_dump_identify_page(hdl, ilog, &idctrl, 0);
4
Passing 'ilog' via 2nd parameter 'ilog'
5
Calling 'ilog_dump_identify_page'
530
531 if (err == 0)
532 ilog->count++;
533
534 return err;
535}
536
537static bool_Bool is_atmos(struct libnvme_transport_handle *hdl, struct ilog *ilog)
538{
539 ilog_ensure_dump_id_ctrl(hdl, ilog);
1
Passing value via 2nd parameter 'ilog'
2
Calling 'ilog_ensure_dump_id_ctrl'
540 return !strncmp(ilog->id_ctrl.mn, ATMOS_MODEL_PREFIX"SOLIDIGM SB5", sizeof(ATMOS_MODEL_PREFIX"SOLIDIGM SB5") - 1);
541}
542
543static enum nvme_telemetry_da get_max_da(struct libnvme_transport_handle *hdl,
544 struct ilog *ilog, enum log_type ttype)
545{
546 enum nvme_telemetry_da max_da = NVME_TELEMETRY_DA_1;
547
548 ilog_ensure_dump_id_ctrl(hdl, ilog);
549
550 if (is_atmos(hdl, ilog) && ttype != EXTENDED)
551 return NVME_TELEMETRY_DA_3;
552
553 if (ilog->id_ctrl.lpa & 0x8)
554 max_da = NVME_TELEMETRY_DA_3;
555 if (ilog->id_ctrl.lpa & 0x40)
556 max_da = NVME_TELEMETRY_DA_4;
557 return max_da;
558}
559
560static int ilog_dump_telemetry(struct libnvme_transport_handle *hdl, struct ilog *ilog, enum log_type ttype)
561{
562 int err = 0;
563 enum nvme_telemetry_da da;
564 size_t mdts;
565 const char *file_name;
566 struct nvme_feat_host_behavior prev = {0};
567 bool_Bool host_behavior_changed = false0;
568 struct libnvme_passthru_cmd cmd;
569 struct log log = {0};
570
571 err = ilog_ensure_dump_id_ctrl(hdl, ilog);
572 if (err)
573 return err;
574
575 da = get_max_da(hdl, ilog, ttype);
576 mdts = ilog->id_ctrl.mdts;
577
578 if (da == 4) {
579 nvme_init_get_features_host_behavior(&cmd, 0, &prev);
580 int err = libnvme_exec_admin_passthru(hdl, &cmd);
581
582 if (!err && !prev.etdas) {
583 struct nvme_feat_host_behavior da4_enable = prev;
584
585 da4_enable.etdas = 1;
586 nvme_init_set_features_host_behavior(&cmd, 0, &da4_enable);
587 libnvme_exec_admin_passthru(hdl, &cmd);
588 host_behavior_changed = true1;
589 }
590 }
591
592 switch (ttype) {
593 case HIT:
594 case ALL:
595 case EXTENDED:
596 file_name = "lid_0x07_lsp_0x01_lsi_0x0000.bin";
597 log.desc = "Host Initiated Telemetry";
598 err = sldgm_dynamic_telemetry(hdl, true1, false0, false0, mdts,
599 da, (struct nvme_telemetry_log **) &log.buffer,
600 &log.buffer_size);
601 break;
602 case CIT:
603 file_name = "lid_0x08_lsp_0x00_lsi_0x0000.bin";
604 log.desc = "Controller Initiated Telemetry";
605 err = sldgm_dynamic_telemetry(hdl, false0, true1, true1, mdts,
606 da, (struct nvme_telemetry_log **) &log.buffer,
607 &log.buffer_size);
608 break;
609 default:
610 return -EINVAL22;
611 }
612
613 if (host_behavior_changed) {
614 nvme_init_set_features_host_behavior(&cmd, 0, &prev);
615 libnvme_exec_admin_passthru(hdl, &cmd);
616 }
617
618 if (err)
619 return err;
620
621 err = log_save(&log, ilog->cfg->out_dir, "log_pages", file_name, log.buffer,
622 log.buffer_size);
623 return err;
624}
625
626static int ilog_dump_identify_pages(struct libnvme_transport_handle *hdl, struct ilog *ilog)
627{
628 struct nvme_ns_list ns_attached_list;
629 struct nvme_ns_list ns_allocated_list;
630 __u32 j = 0;
631
632 struct log identify_base_list[] = {
633 {NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, "Id Active Namespace ID list",
634 sizeof(ns_attached_list), (__u8 *) &ns_attached_list},
635 {NVME_IDENTIFY_CNS_NVMSET_LIST, "Id NVM Set List"},
636 {NVME_IDENTIFY_CNS_CSI_CTRL, "Id I/O Command Set specific"},
637 {NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, "Id Allocated Namespace ID list",
638 sizeof(ns_allocated_list), (__u8 *) &ns_allocated_list},
639 {NVME_IDENTIFY_CNS_CTRL_LIST, "Id Controller List"}
640 };
641 struct log identify_ns_required_list[] = {
642 {NVME_IDENTIFY_CNS_NS, "Id Namespace data"},
643 {NVME_IDENTIFY_CNS_NS_DESC_LIST, "Id Namespace Id Descriptor list"},
644 {NVME_IDENTIFY_CNS_CSI_NS, "Id Namespace ID I/O Command Set specific"},
645 {NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS,
646 "I/O Command Set Independent Identify Namespace Data"},
647 {NVME_IDENTIFY_CNS_NS_CTRL_LIST, "Id Namespace Id Controller List"},
648 };
649
650 struct log allocated = {NVME_IDENTIFY_CNS_ALLOCATED_NS, "Allocated Namespace Data",
651 NVME_IDENTIFY_DATA_SIZE, NULL((void*)0)};
652
653 ilog_ensure_dump_id_ctrl(hdl, ilog);
654
655 for (int i = 0; i < ARRAY_SIZE(identify_base_list)(sizeof(identify_base_list) / sizeof((identify_base_list)[0])
)
; i++) {
656 int err = ilog_dump_identify_page(hdl, ilog, &identify_base_list[i], 0);
657
658 if (err == 0)
659 ilog->count++;
660 }
661
662 while (ns_attached_list.ns[j]) {
663 for (int i = 0; i < ARRAY_SIZE(identify_ns_required_list)(sizeof(identify_ns_required_list) / sizeof((identify_ns_required_list
)[0]))
; i++) {
664 int err = ilog_dump_identify_page(hdl, ilog, &identify_ns_required_list[i],
665 ns_attached_list.ns[j]);
666
667 if (err == 0)
668 ilog->count++;
669 }
670 j++;
671 }
672
673 j = 0;
674 while (ns_allocated_list.ns[j]) {
675 int err = ilog_dump_identify_page(hdl, ilog, &allocated, ns_allocated_list.ns[j]);
676
677 if (err == 0)
678 ilog->count++;
679 j++;
680 }
681
682 return 0;
683}
684
685static int ilog_dump_log_page(struct libnvme_transport_handle *hdl, struct ilog *ilog, struct log *lp, __u32 nsid)
686{
687 __u8 *buff = lp->buffer;
688 __cleanup_free__attribute__((cleanup(freep))) char *filename = NULL((void*)0);
689
690 int err;
691 if (!lp->buffer_size)
692 return -EINVAL22;
693 if (!buff) {
694 buff = libnvme_alloc(lp->buffer_size);
695 if (!buff)
696 return -ENOMEM12;
697 }
698 err = nvme_get_nsid_log(hdl, 0, 0, lp->id, buff, lp->buffer_size);
699 if (err)
700 return err;
701
702 if (asprintf(&filename, "lid_0x%02x_lsp_0x00_lsi_0x0000.bin", lp->id) < 0)
703 return -errno(*__errno_location ());
704
705 return log_save(lp, ilog->cfg->out_dir, "log_pages", filename, buff, lp->buffer_size);
706}
707
708static int ilog_dump_no_lsp_log_pages(struct libnvme_transport_handle *hdl, struct ilog *ilog)
709{
710 struct lba_status_info {
711 __u32 lslplen;
712 __u32 nlslne;
713 __u32 estulb;
714 __u16 rsvd;
715 __u16 lsgc;
716 } lba_status = {};
717 __u64 num_entries = 0;
718 struct log log_page_dependent_list[] = {
719 {NVME_LOG_LID_LBA_STATUS},
720 {NVME_LOG_LID_ENDURANCE_GRP_EVT},
721 };
722 struct log log_page_base_list[] = {
723 {NVME_LOG_LID_SUPPORTED_LOG_PAGES, NULL((void*)0), sizeof(struct nvme_supported_log_pages)},
724 {NVME_LOG_LID_ERROR, NULL((void*)0),
725 (ilog->id_ctrl.elpe + 1) * sizeof(struct nvme_error_log_page)},
726 {NVME_LOG_LID_SMART, NULL((void*)0), sizeof(struct nvme_smart_log)},
727 {NVME_LOG_LID_FW_SLOT, NULL((void*)0), sizeof(struct nvme_firmware_slot)},
728 {NVME_LOG_LID_CHANGED_NS, NULL((void*)0), sizeof(struct nvme_ns_list)},
729 {NVME_LOG_LID_CMD_EFFECTS, NULL((void*)0), sizeof(struct nvme_cmd_effects_log)},
730 {NVME_LOG_LID_DEVICE_SELF_TEST, NULL((void*)0), sizeof(struct nvme_self_test_log)},
731 {NVME_LOG_LID_LBA_STATUS, NULL((void*)0), sizeof(lba_status), (__u8 *) &lba_status},
732 {NVME_LOG_LID_ENDURANCE_GRP_EVT, NULL((void*)0), sizeof(num_entries), (__u8 *) &num_entries},
733 {NVME_LOG_LID_FID_SUPPORTED_EFFECTS, NULL((void*)0),
734 sizeof(struct nvme_fid_supported_effects_log)},
735 {NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, NULL((void*)0),
736 sizeof(struct nvme_mi_cmd_supported_effects_log)},
737 {NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN, NULL((void*)0), 512},
738 {NVME_LOG_LID_PHY_RX_EOM, NULL((void*)0), 512},
739 {NVME_LOG_LID_SANITIZE, NULL((void*)0), sizeof(struct nvme_sanitize_log_page)},
740 {0xC0, "OCP or VU SMART / Health Information Extended", 512},
741 {0xC1, "OCP Error Recovery or VU Latency Reads", 512},
742 {0xC2, "OCP Firmware Activation History or VU Latency Writes", 4096},
743 {0xC3, "OCP Latency Monitor", 512},
744 {0xC4, "OCP Device Capabilities or VU Endurance Manager Statistics", 4096},
745 {0xC5, "OCP Unsupported Requirements or VU Tempeture Statistics", 4096},
746 {0xC7, "OCP TCG Configuration", 512},
747 {0xCA, "SMART Attributes", 512},
748 {0xd5, "Tempeture Statistics", 512},
749 {0xfe, "Latency Outlier", 8192},
750 };
751
752 for (int i = 0; i < ARRAY_SIZE(log_page_base_list)(sizeof(log_page_base_list) / sizeof((log_page_base_list)[0])
)
; i++) {
753 log_page_base_list[i].desc = log_page_base_list[i].desc ?
754 log_page_base_list[i].desc :
755 nvme_log_to_string(log_page_base_list[i].id);
756 if (!ilog_dump_log_page(hdl, ilog, &log_page_base_list[i], 0))
757 ilog->count++;
758 }
759
760 /* if needed, patch logs based on retrieved log size */
761 if (lba_status.lslplen > sizeof(lba_status))
762 log_page_dependent_list[0].buffer_size = lba_status.lslplen;
763 if (num_entries)
764 log_page_dependent_list[1].buffer_size = sizeof(num_entries) +
765 (num_entries * sizeof(__u16));
766
767 for (int i = 0; i < ARRAY_SIZE(log_page_dependent_list)(sizeof(log_page_dependent_list) / sizeof((log_page_dependent_list
)[0]))
; i++) {
768 log_page_dependent_list[i].desc = log_page_dependent_list[i].desc ?
769 log_page_dependent_list[i].desc :
770 nvme_log_to_string(log_page_dependent_list[i].id);
771 ilog_dump_log_page(hdl, ilog, &log_page_dependent_list[i], 0);
772 }
773
774 return 0;
775}
776
777static int ilog_dump_pel(struct libnvme_transport_handle *hdl, struct ilog *ilog)
778{
779 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_persistent_event_log *pevent = NULL((void*)0);
780 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = {0};
781 void *pevent_log_full;
782 struct log lp = {
783 NVME_LOG_LID_PERSISTENT_EVENT,
784 nvme_log_to_string(NVME_LOG_LID_PERSISTENT_EVENT)
785 };
786 int err;
787
788 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX,
789 pevent, sizeof(*pevent));
790 if (err)
791 return err;
792
793
794 pevent = libnvme_alloc(sizeof(*pevent));
795 if (!pevent)
796 return -ENOMEM12;
797
798 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_EST_CTX_AND_READ,
799 pevent, sizeof(*pevent));
800 if (err)
801 return err;
802
803 lp.buffer_size = le64_to_cpu(pevent->tll);
804 pevent_log_full = libnvme_alloc_huge(lp.buffer_size, &mh);
805 if (!pevent_log_full)
806 return -ENOMEM12;
807
808 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_READ,
809 pevent_log_full, lp.buffer_size);
810 if (err)
811 return err;
812
813 ilog->count++;
814
815 err = log_save(&lp, ilog->cfg->out_dir, "log_pages", "lid_0x0d_lsp_0x00_lsi_0x0000.bin",
816 pevent_log_full, lp.buffer_size);
817
818 nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX,
819 pevent, sizeof(*pevent));
820
821 return err;
822}
823
824int solidigm_get_internal_log(int argc, char **argv, struct command *acmd,
825 struct plugin *plugin)
826{
827 char sn_prefix[sizeof(((struct nvme_id_ctrl *)0)->sn)+1];
828 char date_str[sizeof("-YYYYMMDDHHMMSS")];
829 __cleanup_free__attribute__((cleanup(freep))) char *full_folder = NULL((void*)0);
830 __cleanup_free__attribute__((cleanup(freep))) char *unique_folder = NULL((void*)0);
831 __cleanup_free__attribute__((cleanup(freep))) char *zip_name = NULL((void*)0);
832 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
833 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
834 char *initial_folder;
835 char *output_path;
836 struct ilog ilog = {0};
837 int err;
838 enum log_type log_type = ALL;
839 char type_ALL[] = "ALL";
840 time_t current_time;
841 DIR *dir;
842
843 const char *desc = "Get Debug Firmware Logs and save them.";
844 const char *type = "Log type; Defaults to ALL.";
845 const char *out_dir = "Output directory; defaults to current working directory.";
846
847 struct config cfg = {
848 .out_dir = ".",
849 .type = type_ALL,
850 };
851
852 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED"
, CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd',
"DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } }
853 OPT_STRING("type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED", &cfg.type, type),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED"
, CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd',
"DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } }
854 OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED"
, CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd',
"DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } }
;
855
856 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
857 if (err)
858 return err;
859 ilog.cfg = &cfg;
860
861 for (char *p = cfg.type; *p; ++p)
862 *p = toupper(*p)(__extension__ ({ int __res; if (sizeof (*p) > 1) { if (__builtin_constant_p
(*p)) { int __c = (*p); __res = __c < -128 || __c > 255
? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper
(*p); } else __res = (*__ctype_toupper_loc ())[(int) (*p)]; __res
; }))
;
863
864 if (!strcmp(cfg.type, "ALL"))
865 log_type = ALL;
866 else if (!strcmp(cfg.type, "HIT"))
867 log_type = HIT;
868 else if (!strcmp(cfg.type, "CIT"))
869 log_type = CIT;
870 else if (!strcmp(cfg.type, "NLOG"))
871 log_type = NLOG;
872 else if (!strcmp(cfg.type, "ASSERT"))
873 log_type = ASSERTLOG;
874 else if (!strcmp(cfg.type, "EVENT"))
875 log_type = EVENTLOG;
876 else if (!strcmp(cfg.type, "EXTENDED"))
877 log_type = EXTENDED;
878 else {
879 fprintf(stderrstderr, "Invalid log type: %s\n", cfg.type);
880 return -EINVAL22;
881 }
882
883 dir = opendir(cfg.out_dir);
884 if (dir)
885 closedir(dir);
886 else {
887 perror(cfg.out_dir);
888 return -errno(*__errno_location ());
889 }
890
891 initial_folder = cfg.out_dir;
892
893 err = get_serial_number(sn_prefix, hdl);
894 if (err)
895 return err;
896
897 current_time = time(NULL((void*)0));
898 strftime(date_str, sizeof(date_str), "-%Y%m%d%H%M%S", localtime(&current_time));
899 if (asprintf(&unique_folder, "%s%s", sn_prefix, date_str) < 0)
900 return -errno(*__errno_location ());
901 if (asprintf(&full_folder, "%s/%s", cfg.out_dir, unique_folder) < 0)
902 return -errno(*__errno_location ());
903
904 if (mkdir(full_folder, 0755) != 0) {
905 perror("mkdir");
906 return -errno(*__errno_location ());
907 }
908
909 cfg.out_dir = full_folder;
910 output_path = full_folder;
911
912 /* Retrieve first logs that records actions to retrieve other logs */
913 if (log_type == ALL || log_type == HIT || log_type == EXTENDED) {
914 err = ilog_dump_telemetry(hdl, &ilog, log_type);
915 if (err == 0)
916 ilog.count++;
917 else if (err < 0)
918 perror("Error retrieving Host Initiated Telemetry");
919 }
920 if (log_type == ALL || log_type == NLOG || log_type == EXTENDED) {
921 err = ilog_dump_nlogs(hdl, &ilog, -1);
922 if (err == 0)
923 ilog.count++;
924 else if (err < 0)
925 perror("Error retrieving Nlog");
926 }
927 if (log_type == ALL || log_type == CIT || log_type == EXTENDED) {
928 err = ilog_dump_telemetry(hdl, &ilog, CIT);
929 if (err == 0)
930 ilog.count++;
931 else if (err < 0)
932 perror("Error retrieving Controller Initiated Telemetry");
933 }
934 if (log_type == ALL || log_type == ASSERTLOG || log_type == EXTENDED) {
935 err = ilog_dump_assert_logs(hdl, &ilog);
936 if (err == 0)
937 ilog.count++;
938 else if (err < 0)
939 perror("Error retrieving Assert log");
940 }
941 if (log_type == ALL || log_type == EVENTLOG || log_type == EXTENDED) {
942 err = ilog_dump_event_logs(hdl, &ilog);
943 if (err == 0)
944 ilog.count++;
945 else if (err < 0)
946 perror("Error retrieving Event log");
947 }
948 if (log_type == ALL || log_type == EXTENDED) {
949 err = ilog_dump_identify_pages(hdl, &ilog);
950 if (err < 0)
951 perror("Error retrieving Identify pages");
952
953 err = ilog_dump_pel(hdl, &ilog);
954 if (err < 0)
955 perror("Error retrieving Persistent Event Log page");
956
957 err = ilog_dump_no_lsp_log_pages(hdl, &ilog);
958 if (err < 0)
959 perror("Error retrieving no LSP Log pages");
960 }
961
962 if (ilog.count > 0) {
963 int ret_cmd;
964 __cleanup_free__attribute__((cleanup(freep))) char *cmd = NULL((void*)0);
965 char *quiet = nvme_args.verbose ? "" : " -q";
966
967 if (asprintf(&zip_name, "%s.zip", unique_folder) < 0)
968 return -errno(*__errno_location ());
969
970 if (asprintf(&cmd, "cd \"%s\" && zip -MM -r \"../%s\" ./* %s", cfg.out_dir,
971 zip_name, quiet) < 0) {
972 err = errno(*__errno_location ());
973 perror("Can't allocate string for zip command");
974 goto out;
975 }
976 printf("Compressing logs to %s\n", zip_name);
977 ret_cmd = system(cmd);
978 if (ret_cmd)
979 perror(cmd);
980 else {
981 output_path = zip_name;
982 if (asprintf(&cmd, "rm -rf %s", cfg.out_dir) < 0) {
983 err = errno(*__errno_location ());
984 perror("Can't allocate string for cleanup");
985 goto out;
986 }
987 if (system(cmd) != 0)
988 perror("Failed removing logs folder");
989 }
990 }
991
992out:
993 if (ilog.count == 0) {
994 if (err > 0)
995 nvme_show_status(err);
996
997 } else if ((ilog.count > 0) || cfg.verbose)
998 printf("Total: %d log files in %s/%s\n", ilog.count, initial_folder, output_path);
999
1000 return err;
1001}