#include "nvmectl.h"
typedef int (*cmd_t)(int ac, char **av, const char *id, int fd);
static cmd_t parsecmd(int ac, char **av, int *globokp);
static int cmd_info(int ac, char **av, const char *id, int fd);
static int cmd_errors(int ac, char **av, const char *id, int fd);
static void usage(int rc);
int VerboseOpt;
int
main(int ac, char **av)
{
int rc = 0;
int ch;
int nvmei;
int globok;
int i;
cmd_t cmd;
while ((ch = getopt(ac, av, "v")) != -1) {
switch(ch) {
case 'v':
++VerboseOpt;
break;
default:
usage(1);
break;
}
}
ac -= optind;
av += optind;
for (nvmei = ac; nvmei > 0; --nvmei) {
if (strncmp(av[nvmei - 1], "nvme", 4) != 0)
break;
}
if (nvmei == 0)
usage(1);
globok = 0;
cmd = parsecmd(nvmei, av, &globok);
if (nvmei == ac && globok) {
i = 0;
for (;;) {
char *path;
int fd;
if (i)
printf("\n");
asprintf(&path, "/dev/nvme%d", i);
fd = open(path, O_RDWR);
free(path);
if (fd < 0)
break;
rc += cmd(nvmei, av, path + 5, fd);
close(fd);
++i;
}
} else if (nvmei == ac && !globok) {
fprintf(stderr, "must specify nvmeX device for command\n");
} else {
for (i = nvmei; i < ac; ++i) {
char *path;
int fd;
if (i != nvmei)
printf("\n");
asprintf(&path, "/dev/%s", av[i]);
fd = open(path, O_RDWR);
if (fd < 0) {
fprintf(stderr, "open \"%s\": %s\n",
path,
strerror(errno));
} else {
rc += cmd(nvmei, av, path + 5, fd);
close(fd);
}
free(path);
}
}
return (rc ? 1 : 0);
}
static
cmd_t
parsecmd(int ac, char **av, int *globokp)
{
if (ac == 0)
usage(1);
if (strcmp(av[0], "info") == 0) {
*globokp = 1;
return cmd_info;
}
if (strcmp(av[0], "errors") == 0) {
*globokp = 1;
return cmd_errors;
}
fprintf(stderr, "Command %s not recognized\n", av[0]);
usage(1);
return NULL;
}
static
int
cmd_info(int ac __unused, char **av __unused, const char *id, int fd)
{
nvme_getlog_ioctl_t ioc;
nvme_log_smart_data_t *smart;
int count;
int i;
bzero(&ioc, sizeof(ioc));
ioc.lid = NVME_LID_SMART;
ioc.ret_size = sizeof(ioc.info.logsmart);
if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) {
fprintf(stderr, "ioctl failed: %s\n", strerror(errno));
return 1;
}
if (NVME_COMQ_STATUS_CODE_GET(ioc.status)) {
fprintf(stderr, "%s: type %d code 0x%02x\n",
id,
NVME_COMQ_STATUS_TYPE_GET(ioc.status),
NVME_COMQ_STATUS_CODE_GET(ioc.status));
return 1;
}
printf("%s:\n", id);
smart = &ioc.info.logsmart;
printf("\tcrit_flags:\t");
if (smart->crit_flags) {
if (smart->crit_flags & NVME_SMART_CRF_RES80)
printf(" 80");
if (smart->crit_flags & NVME_SMART_CRF_RES40)
printf(" 40");
if (smart->crit_flags & NVME_SMART_CRF_RES20)
printf(" 20");
if (smart->crit_flags & NVME_SMART_CRF_VOLTL_BKUP_FAIL)
printf(" MEM_BACKUP_FAILED");
if (smart->crit_flags & NVME_SMART_CRF_MEDIA_RO)
printf(" MEDIA_RDONLY");
if (smart->crit_flags & NVME_SMART_CRF_UNRELIABLE)
printf(" MEDIA_UNRELIABLE");
if (smart->crit_flags & NVME_SMART_CRF_ABOVE_THRESH)
printf(" TOO_HOT");
if (smart->crit_flags & NVME_SMART_CRF_BELOW_THRESH)
printf(" TOO_COLD");
} else {
printf("none\n");
}
printf("\tcomp_temp:\t%dC\n",
(int)(smart->comp_temp1 + (smart->comp_temp2 << 8)) - 273);
printf("\tLIFE_LEFT:\t%d%% (%d%% used)\n",
100 - (int)smart->rated_life,
(int)smart->rated_life);
printf("\tread_bytes:\t%s\n",
format_number(smart->read_count[0] * 512000));
printf("\twrite_bytes:\t%s\n",
format_number(smart->write_count[0] * 512000));
printf("\tread_cmds:\t%s\n",
format_number(smart->read_cmds[0]));
printf("\twrite_cmds:\t%s\n",
format_number(smart->write_cmds[0]));
printf("\tbusy_time:\t%ld min (%1.2f hrs)\n",
smart->busy_time[0],
(double)smart->busy_time[0] / 60.0);
printf("\tpowon_hours:\t%ld\n", smart->powon_hours[0]);
printf("\tpower_cyc:\t%ld\n", smart->power_cycles[0]);
printf("\tunsafe_shut:\t%ld\n", smart->unsafe_shutdowns[0]);
printf("\tUNRECOV_ERR:\t%ld", smart->unrecoverable_errors[0]);
if (smart->unrecoverable_errors[0])
printf("\t*******WARNING*******");
printf("\n");
printf("\terr_log_ent:\t%ld\n", smart->error_log_entries[0]);
printf("\twarn_temp_time:\t%d min (%1.2f hrs)\n",
smart->warn_comp_temp_time,
(double)smart->warn_comp_temp_time / 60.0);
printf("\tcrit_temp_time:\t%d min (%1.2f hrs)\n",
smart->crit_comp_temp_time,
(double)smart->crit_comp_temp_time / 60.0);
printf("\ttemp_sensors:\t");
for (i = count = 0; i < 8; ++i) {
if (smart->temp_sensors[i]) {
if (count)
printf(" ");
printf("%dC", smart->temp_sensors[i] - 273);
++count;
}
}
if (count == 0)
printf("none");
printf("\n");
return 0;
}
static
int
cmd_errors(int ac __unused, char **av __unused, const char *id, int fd)
{
nvme_getlog_ioctl_t ioc;
nvme_log_error_data_t *errs;
int i;
bzero(&ioc, sizeof(ioc));
ioc.lid = NVME_LID_ERROR;
ioc.ret_size = sizeof(ioc.info.logsmart);
if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) {
fprintf(stderr, "ioctl failed: %s\n", strerror(errno));
return 1;
}
if (NVME_COMQ_STATUS_CODE_GET(ioc.status)) {
fprintf(stderr, "%s: type %d code 0x%02x\n",
id,
NVME_COMQ_STATUS_TYPE_GET(ioc.status),
NVME_COMQ_STATUS_CODE_GET(ioc.status));
return 1;
}
printf("%s:\n", id);
errs = &ioc.info.logerr[0];
for (i = 0; i < 64; ++i) {
if (errs->error_count == 0 && errs->subq_id == 0 &&
errs->cmd_id == 0 && errs->status == 0 &&
errs->param == 0 && errs->nsid == 0 &&
errs->vendor == 0 && errs->csi == 0 && errs->lba == 0)
continue;
if (errs->param || errs->vendor || errs->csi) {
printf("\t%2d cnt=%-3ld subq=%-2d cmdi=%-3d "
"status=%d,0x%02x parm=%04x nsid=%-3d vend=%d "
"csi=0x%lx lba=%ld",
i, errs->error_count,
(int16_t)errs->subq_id,
(int16_t)errs->cmd_id,
NVME_COMQ_STATUS_TYPE_GET(errs->status),
NVME_COMQ_STATUS_CODE_GET(errs->status),
errs->param, errs->nsid,
errs->vendor,
errs->csi, errs->lba);
} else {
printf("\t%2d cnt=%-3ld subq=%-2d cmdi=%-3d "
"status=%d,0x%02x nsid=%-3d lba=%ld",
i, errs->error_count,
(int16_t)errs->subq_id,
(int16_t)errs->cmd_id,
NVME_COMQ_STATUS_TYPE_GET(errs->status),
NVME_COMQ_STATUS_CODE_GET(errs->status),
errs->nsid,
errs->lba);
}
if (errs->status & NVME_COMQ_STATUS_DNR)
printf(" DNR");
printf(" %s\n", status_to_str(errs->status));
++errs;
}
return 0;
}
static
void
usage(int rc)
{
fprintf(stderr,
"nvmectl [-v] cmd [nvme0,1,2...]\n"
"\tinfo\n"
"\terrors\n"
);
exit(rc);
}