|
Packit |
dd4ba5 |
#include <stdio.h>
|
|
Packit |
dd4ba5 |
#include <stdlib.h>
|
|
Packit |
dd4ba5 |
#include <stdbool.h>
|
|
Packit |
dd4ba5 |
#include <string.h>
|
|
Packit |
dd4ba5 |
#include <sys/types.h>
|
|
Packit |
dd4ba5 |
#include <sys/stat.h>
|
|
Packit |
dd4ba5 |
#include <fcntl.h>
|
|
Packit |
dd4ba5 |
#include <unistd.h>
|
|
Packit |
dd4ba5 |
#include <errno.h>
|
|
Packit |
dd4ba5 |
#include "nvme-models.h"
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static char *_fmt1 = "/sys/class/nvme/nvme%d/device/subsystem_vendor";
|
|
Packit |
dd4ba5 |
static char *_fmt2 = "/sys/class/nvme/nvme%d/device/subsystem_device";
|
|
Packit |
dd4ba5 |
static char *_fmt3 = "/sys/class/nvme/nvme%d/device/vendor";
|
|
Packit |
dd4ba5 |
static char *_fmt4 = "/sys/class/nvme/nvme%d/device/device";
|
|
Packit |
dd4ba5 |
static char *_fmt5 = "/sys/class/nvme/nvme%d/device/class";
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static char fmt1[78];
|
|
Packit |
dd4ba5 |
static char fmt2[78];
|
|
Packit |
dd4ba5 |
static char fmt3[78];
|
|
Packit |
dd4ba5 |
static char fmt4[78];
|
|
Packit |
dd4ba5 |
static char fmt5[78];
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static char *device_top;
|
|
Packit |
dd4ba5 |
static char *device_mid;
|
|
Packit |
dd4ba5 |
static char *device_final;
|
|
Packit |
dd4ba5 |
static char *class_top;
|
|
Packit |
dd4ba5 |
static char *class_mid;
|
|
Packit |
dd4ba5 |
static char *class_final;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static void free_all(void)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
free(device_top);
|
|
Packit |
dd4ba5 |
device_top = NULL;
|
|
Packit |
dd4ba5 |
free(device_mid);
|
|
Packit |
dd4ba5 |
device_mid = NULL;
|
|
Packit |
dd4ba5 |
free(device_final);
|
|
Packit |
dd4ba5 |
device_final = NULL;
|
|
Packit |
dd4ba5 |
free(class_top);
|
|
Packit |
dd4ba5 |
class_top = NULL;
|
|
Packit |
dd4ba5 |
free(class_mid);
|
|
Packit |
dd4ba5 |
class_mid = NULL;
|
|
Packit |
dd4ba5 |
free(class_final);
|
|
Packit |
dd4ba5 |
class_final = NULL;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static char *find_data(char *data)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
while (*data != '\0') {
|
|
Packit |
dd4ba5 |
if (*data >= '0' && *data <= '9')
|
|
Packit |
dd4ba5 |
return data;
|
|
Packit |
dd4ba5 |
data++;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
return NULL;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static char *locate_info(char *data, bool is_inner, bool is_class)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
char *orig = data;
|
|
Packit |
dd4ba5 |
char *locate = find_data(data);
|
|
Packit |
dd4ba5 |
if (!data)
|
|
Packit |
dd4ba5 |
return orig;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
if (is_class)
|
|
Packit |
dd4ba5 |
return locate + 4;
|
|
Packit |
dd4ba5 |
if (!is_inner)
|
|
Packit |
dd4ba5 |
/* 4 to get over the number, 2 for spaces */
|
|
Packit |
dd4ba5 |
return locate + 4 + 2;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
/* Inner data, has "sub_ven(space)sub_dev(space)(space)string */
|
|
Packit |
dd4ba5 |
return locate + 4 + 1 + 4 + 2;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static void format_and_print(char *save)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
if (!class_mid)
|
|
Packit |
dd4ba5 |
snprintf(save, 1024, "%s %s %s",
|
|
Packit |
dd4ba5 |
locate_info(device_top, false, false),
|
|
Packit |
dd4ba5 |
locate_info(device_mid, false, false),
|
|
Packit |
dd4ba5 |
locate_info(device_final, true, false));
|
|
Packit |
dd4ba5 |
else
|
|
Packit |
dd4ba5 |
snprintf(save, 1024, "%s: %s %s %s",
|
|
Packit |
dd4ba5 |
locate_info(class_mid, false, true),
|
|
Packit |
dd4ba5 |
locate_info(device_top, false, false),
|
|
Packit |
dd4ba5 |
locate_info(device_mid, false, false),
|
|
Packit |
dd4ba5 |
locate_info(device_final, true, false));
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static void format_all(char *save, char *vendor, char *device)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
if (device_top && device_mid && device_final)
|
|
Packit |
dd4ba5 |
format_and_print(save);
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
else if (device_top && !device_mid && class_mid)
|
|
Packit |
dd4ba5 |
snprintf(save, 1024, "%s: %s Device %s",
|
|
Packit |
dd4ba5 |
locate_info(class_mid, false, true),
|
|
Packit |
dd4ba5 |
locate_info(device_top, false, false),
|
|
Packit |
dd4ba5 |
device);
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
else if (!device_top && class_mid)
|
|
Packit |
dd4ba5 |
snprintf(save, 1024, "%s: Vendor %s Device %s",
|
|
Packit |
dd4ba5 |
locate_info(class_mid, false, true),
|
|
Packit |
dd4ba5 |
vendor,
|
|
Packit |
dd4ba5 |
device);
|
|
Packit |
dd4ba5 |
else
|
|
Packit |
dd4ba5 |
snprintf(save, 1024, "Unknown device");
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static int is_final_match(char *line, char *search)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
return !memcmp(&line[2], search, 2);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static int is_inner_sub_vendev(char *line, char *search, char *search2)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
char combine[10];
|
|
Packit |
dd4ba5 |
snprintf(combine, sizeof(combine), "%s %s", &search[2], &search2[2]);
|
|
Packit |
dd4ba5 |
if (line[0] != '\t' && line[1] != '\t')
|
|
Packit |
dd4ba5 |
return 0;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
return !memcmp(combine, &line[2], 9);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static int is_mid_level_match(char *line, char *device, bool class)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
if (!class)
|
|
Packit |
dd4ba5 |
return !memcmp(&line[1], &device[2], 4);
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
return !memcmp(&line[1], device, 2);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static inline bool is_comment(char *line)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
return line[0] == '#';
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static int is_top_level_match(char *line, const char* device, bool class)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
if (line[0] == '\t')
|
|
Packit |
dd4ba5 |
return false;
|
|
Packit |
dd4ba5 |
if (line[0] == '#')
|
|
Packit |
dd4ba5 |
return false;
|
|
Packit |
dd4ba5 |
if (!class)
|
|
Packit |
dd4ba5 |
return !memcmp(line, &device[2], 4);
|
|
Packit |
dd4ba5 |
if (line[0] != 'C')
|
|
Packit |
dd4ba5 |
return false;
|
|
Packit |
dd4ba5 |
/* Skipping C(SPACE) 0x */
|
|
Packit |
dd4ba5 |
return !memcmp(&line[2], &device[2], 2);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static inline int is_tab(char *line)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
return line[0] == '\t';
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static inline int is_class_info(char *line)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
return !memcmp(line, "# C class", 9);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static void parse_vendor_device(char **line, FILE *file,
|
|
Packit |
dd4ba5 |
char *device, char *subdev,
|
|
Packit |
dd4ba5 |
char *subven)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
bool device_single_found = false;
|
|
Packit |
dd4ba5 |
size_t amnt = 1024;
|
|
Packit |
dd4ba5 |
size_t found = 0;
|
|
Packit |
dd4ba5 |
char *newline;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
while ((found = getline(line, &amnt, file)) != -1) {
|
|
Packit |
dd4ba5 |
newline = *line;
|
|
Packit |
dd4ba5 |
if (is_comment(newline))
|
|
Packit |
dd4ba5 |
continue;
|
|
Packit |
dd4ba5 |
if (!is_tab(newline))
|
|
Packit |
dd4ba5 |
return;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
newline[found - 1] = '\0';
|
|
Packit |
dd4ba5 |
if (!device_single_found && is_mid_level_match(newline, device, false)) {
|
|
Packit |
dd4ba5 |
device_single_found = true;
|
|
Packit |
dd4ba5 |
device_mid = strdup(newline);
|
|
Packit |
dd4ba5 |
continue;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
if (device_single_found && is_inner_sub_vendev(newline, subven, subdev)) {
|
|
Packit |
dd4ba5 |
device_final = strdup(newline);
|
|
Packit |
dd4ba5 |
break;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static void pull_class_info(char **_newline, FILE *file, char *class)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
size_t amnt;
|
|
Packit |
dd4ba5 |
size_t size = 1024;
|
|
Packit |
dd4ba5 |
bool top_found = false;
|
|
Packit |
dd4ba5 |
bool mid_found = false;
|
|
Packit |
dd4ba5 |
char *newline;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
while ((amnt = getline(_newline, &size, file)) != -1) {
|
|
Packit |
dd4ba5 |
newline = *_newline;
|
|
Packit |
dd4ba5 |
newline[amnt - 1] = '\0';
|
|
Packit |
dd4ba5 |
if (!top_found && is_top_level_match(newline, class, true)) {
|
|
Packit |
dd4ba5 |
class_top = strdup(newline);
|
|
Packit |
dd4ba5 |
top_found = true;
|
|
Packit |
dd4ba5 |
continue;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
if (!mid_found && top_found &&
|
|
Packit |
dd4ba5 |
is_mid_level_match(newline, &class[4], true)) {
|
|
Packit |
dd4ba5 |
class_mid = strdup(newline);
|
|
Packit |
dd4ba5 |
mid_found = true;
|
|
Packit |
dd4ba5 |
continue;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
if (top_found && mid_found &&
|
|
Packit |
dd4ba5 |
is_final_match(newline, &class[6])) {
|
|
Packit |
dd4ba5 |
class_final = strdup(newline);
|
|
Packit |
dd4ba5 |
break;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static int read_sys_node(char *where, char *save, size_t savesz)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
char *new;
|
|
Packit |
dd4ba5 |
int fd, ret = 0;
|
|
Packit |
dd4ba5 |
fd = open(where, O_RDONLY);
|
|
Packit |
dd4ba5 |
if (fd < 0) {
|
|
Packit |
dd4ba5 |
fprintf(stderr, "Failed to open %s with errno %s\n",
|
|
Packit |
dd4ba5 |
where, strerror(errno));
|
|
Packit |
dd4ba5 |
return 1;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
/* -1 so we can safely use strstr below */
|
|
Packit |
dd4ba5 |
if(!read(fd, save, savesz - 1))
|
|
Packit |
dd4ba5 |
ret = 1;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
new = strstr(save, "\n");
|
|
Packit |
dd4ba5 |
if (new)
|
|
Packit |
dd4ba5 |
new[0] = '\0';
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
close(fd);
|
|
Packit |
dd4ba5 |
return ret;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
static FILE *open_pci_ids(void)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
int i;
|
|
Packit |
dd4ba5 |
char *pci_ids_path;
|
|
Packit |
dd4ba5 |
FILE *fp;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
const char* pci_ids[] = {
|
|
Packit |
dd4ba5 |
"/usr/share/hwdata/pci.ids", /* RHEL */
|
|
Packit |
dd4ba5 |
"/usr/share/pci.ids", /* SLES */
|
|
Packit |
dd4ba5 |
"/usr/share/misc/pci.ids", /* Ubuntu */
|
|
Packit |
dd4ba5 |
NULL
|
|
Packit |
dd4ba5 |
};
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
/* First check if user gave pci ids in environment */
|
|
Packit |
dd4ba5 |
if ((pci_ids_path = getenv("PCI_IDS_PATH")) != NULL) {
|
|
Packit |
dd4ba5 |
if ((fp = fopen(pci_ids_path, "r")) != NULL) {
|
|
Packit |
dd4ba5 |
return fp;
|
|
Packit |
dd4ba5 |
} else {
|
|
Packit |
dd4ba5 |
/* fail if user provided environment variable but could not open */
|
|
Packit |
dd4ba5 |
perror(pci_ids_path);
|
|
Packit |
dd4ba5 |
return NULL;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
/* NO environment, check in predefined places */
|
|
Packit |
dd4ba5 |
for (i = 0; pci_ids[i] != NULL; i++) {
|
|
Packit |
dd4ba5 |
if ((fp = fopen(pci_ids[i], "r")) != NULL)
|
|
Packit |
dd4ba5 |
return fp;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
fprintf(stderr, "Could not find pci.ids file\n");
|
|
Packit |
dd4ba5 |
return NULL;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
char *nvme_product_name(int id)
|
|
Packit |
dd4ba5 |
{
|
|
Packit |
dd4ba5 |
char *line = NULL;
|
|
Packit |
dd4ba5 |
ssize_t amnt;
|
|
Packit |
dd4ba5 |
char vendor[7] = { 0 };
|
|
Packit |
dd4ba5 |
char device[7] = { 0 };
|
|
Packit |
dd4ba5 |
char sub_device[7] = { 0 };
|
|
Packit |
dd4ba5 |
char sub_vendor[7] = { 0 };
|
|
Packit |
dd4ba5 |
char class[13] = { 0 };
|
|
Packit |
dd4ba5 |
size_t size = 1024;
|
|
Packit |
dd4ba5 |
char ret;
|
|
Packit |
dd4ba5 |
FILE *file = open_pci_ids();
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
if (!file)
|
|
Packit |
dd4ba5 |
goto error1;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
snprintf(fmt1, 78, _fmt1, id);
|
|
Packit |
dd4ba5 |
snprintf(fmt2, 78, _fmt2, id);
|
|
Packit |
dd4ba5 |
snprintf(fmt3, 78, _fmt3, id);
|
|
Packit |
dd4ba5 |
snprintf(fmt4, 78, _fmt4, id);
|
|
Packit |
dd4ba5 |
snprintf(fmt5, 78, _fmt5, id);
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
ret = read_sys_node(fmt1, sub_vendor, 7);
|
|
Packit |
dd4ba5 |
ret |= read_sys_node(fmt2, sub_device, 7);
|
|
Packit |
dd4ba5 |
ret |= read_sys_node(fmt3, vendor, 7);
|
|
Packit |
dd4ba5 |
ret |= read_sys_node(fmt4, device, 7);
|
|
Packit |
dd4ba5 |
ret |= read_sys_node(fmt5, class, 13);
|
|
Packit |
dd4ba5 |
if (ret)
|
|
Packit |
dd4ba5 |
goto error0;
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
line = malloc(1024);
|
|
Packit |
dd4ba5 |
if (!line) {
|
|
Packit |
dd4ba5 |
fprintf(stderr, "malloc: %s\n", strerror(errno));
|
|
Packit |
dd4ba5 |
goto error0;
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
|
|
Packit |
dd4ba5 |
while ((amnt = getline(&line, &size, file)) != -1) {
|
|
Packit |
dd4ba5 |
if (is_comment(line) && !is_class_info(line))
|
|
Packit |
dd4ba5 |
continue;
|
|
Packit |
dd4ba5 |
if (is_top_level_match(line, vendor, false)) {
|
|
Packit |
dd4ba5 |
line[amnt - 1] = '\0';
|
|
Packit |
dd4ba5 |
device_top = strdup(line);
|
|
Packit |
dd4ba5 |
parse_vendor_device(&line, file,
|
|
Packit |
dd4ba5 |
device,
|
|
Packit |
dd4ba5 |
sub_device,
|
|
Packit |
dd4ba5 |
sub_vendor);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
if (is_class_info(line))
|
|
Packit |
dd4ba5 |
pull_class_info(&line, file, class);
|
|
Packit |
dd4ba5 |
}
|
|
Packit |
dd4ba5 |
fclose(file);
|
|
Packit |
dd4ba5 |
format_all(line, vendor, device);
|
|
Packit |
dd4ba5 |
free_all();
|
|
Packit |
dd4ba5 |
return line;
|
|
Packit |
dd4ba5 |
error0:
|
|
Packit |
dd4ba5 |
fclose(file);
|
|
Packit |
dd4ba5 |
error1:
|
|
Packit |
dd4ba5 |
return !line ? strdup("NULL") : strdup("Unknown Device");
|
|
Packit |
dd4ba5 |
}
|