#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ftw.h>
#include <string.h>
#include <thread.h>
#include <synch.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/modctl.h>
#include <strings.h>
#include <libdevinfo.h>
#include "libdevid.h"
int
devid_get(int fd, ddi_devid_t *devidp)
{
int len = 0;
dev_t dev;
struct stat statb;
ddi_devid_t mydevid;
if (fstat(fd, &statb) != 0)
return (-1);
if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode))
return (-1);
dev = statb.st_rdev;
if (modctl(MODSIZEOF_DEVID, dev, &len) != 0)
return (-1);
if ((mydevid = (ddi_devid_t)malloc(len)) == NULL)
return (-1);
if (modctl(MODGETDEVID, dev, len, mydevid) != 0) {
free(mydevid);
return (-1);
}
*devidp = mydevid;
return (0);
}
int
devid_get_minor_name(int fd, char **minor_namep)
{
int len = 0;
dev_t dev;
int spectype;
char *myminor_name;
struct stat statb;
if (fstat(fd, &statb) != 0)
return (-1);
if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode))
return (-1);
spectype = statb.st_mode & S_IFMT;
dev = statb.st_rdev;
if (modctl(MODSIZEOF_MINORNAME, dev, spectype, &len) != 0)
return (-1);
if ((myminor_name = (char *)malloc(len)) == NULL)
return (-1);
if (modctl(MODGETMINORNAME, dev, spectype, len, myminor_name) != 0) {
free(myminor_name);
return (-1);
}
*minor_namep = myminor_name;
return (0);
}
char *
devid_str_from_path(const char *path)
{
int fd;
ddi_devid_t devid;
char *minor, *ret = NULL;
if ((fd = open(path, O_RDONLY)) < 0)
return (NULL);
if (devid_get(fd, &devid) == 0) {
if (devid_get_minor_name(fd, &minor) != 0)
minor = NULL;
ret = devid_str_encode(devid, minor);
if (minor != NULL)
devid_str_free(minor);
devid_free(devid);
}
(void) close(fd);
return (ret);
}
struct nmlist {
struct nmlist *nl_next;
char *nl_devname;
dev_t nl_dev;
};
struct nmlist *
nmlist_add(struct nmlist **nlhp, char *path)
{
struct stat statb;
dev_t dev;
struct nmlist *nl;
if ((stat(path, &statb) == 0) &&
(S_ISCHR(statb.st_mode) || S_ISBLK(statb.st_mode)))
dev = statb.st_rdev;
else
dev = NODEV;
for (; (nl = *nlhp) != NULL; nlhp = &nl->nl_next)
;
if ((nl = malloc(sizeof (*nl))) == NULL)
return (NULL);
if ((nl->nl_devname = strdup(path)) == NULL) {
free(nl);
return (NULL);
}
nl->nl_next = NULL;
nl->nl_dev = dev;
*nlhp = nl;
return (nl);
}
struct devlink_cbinfo {
struct nmlist **cbi_nlhp;
char *cbi_search_path;
int cbi_error;
};
static int
devlink_callback(di_devlink_t dl, void *arg)
{
struct devlink_cbinfo *cbip = (struct devlink_cbinfo *)arg;
char *devpath = (char *)di_devlink_path(dl);
if (strncmp(devpath, cbip->cbi_search_path,
strlen(cbip->cbi_search_path)) == 0) {
if (nmlist_add(cbip->cbi_nlhp, devpath) == NULL) {
cbip->cbi_error = 1;
return (DI_WALK_TERMINATE);
}
}
return (DI_WALK_CONTINUE);
}
int devid_deviceid_to_nmlist_link = DI_PRIMARY_LINK;
#define DEVICEID_NMLIST_SLINK 1
int devid_deviceid_to_nmlist_flg = 0;
static di_devlink_handle_t devid_deviceid_to_nmlist_dlh = NULL;
#define DEVICEID_NMLIST_NRETRY 10
int
devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name,
devid_nmlist_t **retlist)
{
char *cp;
int dev;
char *paths = NULL;
char *path;
int lens;
di_devlink_handle_t dlh = NULL;
int ret = -1;
struct devlink_cbinfo cbi;
struct nmlist *nlh = NULL;
struct nmlist *nl;
devid_nmlist_t *rl;
int nret;
int nagain = 0;
int err = 0;
*retlist = NULL;
if ((strcmp(search_path, "/devices") == 0) ||
(strncmp(search_path, "/devices/", 9) == 0))
dev = 0;
else if ((strcmp(search_path, "/dev") == 0) ||
(strncmp(search_path, "/dev/", 5) == 0))
dev = 1;
else {
errno = EINVAL;
return (-1);
}
again: if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, NULL) != 0)
goto out;
if ((paths = (char *)malloc(lens)) == NULL)
goto out;
if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, paths) != 0) {
if ((errno == EAGAIN) && (nagain++ < DEVICEID_NMLIST_NRETRY)) {
free(paths);
paths = NULL;
goto again;
}
goto out;
}
if (dev) {
dlh = devid_deviceid_to_nmlist_dlh;
if (dlh &&
!(devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK)) {
(void) di_devlink_fini(&dlh);
dlh = devid_deviceid_to_nmlist_dlh = NULL;
}
if ((dlh == NULL) &&
((dlh = di_devlink_init(NULL, 0)) == NULL))
goto out;
}
for (path = paths; *path; path += strlen(path) + 1) {
if (dev) {
cbi.cbi_nlhp = &nlh;
cbi.cbi_search_path = search_path;
cbi.cbi_error = 0;
(void) di_devlink_walk(dlh, NULL, path,
devid_deviceid_to_nmlist_link,
(void *)&cbi, devlink_callback);
if (cbi.cbi_error)
goto out;
} else {
cp = malloc(strlen("/devices") + strlen(path) + 1);
(void) strcpy(cp, "/devices");
(void) strcat(cp, path);
if (strncmp(cp, search_path,
strlen(search_path)) == 0) {
if (nmlist_add(&nlh, cp) == NULL) {
free(cp);
goto out;
}
}
free(cp);
}
}
for (nl = nlh, nret = 0; nl; nl = nl->nl_next)
nret++;
if (nret == 0) {
err = ENODEV;
goto out;
}
if ((*retlist = calloc(nret + 1, sizeof (devid_nmlist_t))) == NULL) {
err = ENOMEM;
goto out;
}
for (nl = nlh, rl = *retlist; nl; nl = nl->nl_next, rl++) {
rl->devname = nl->nl_devname;
rl->dev = nl->nl_dev;
}
rl->devname = NULL;
rl->dev = NODEV;
ret = 0;
out:
while ((nl = nlh) != NULL) {
nlh = nl->nl_next;
free(nl);
}
if (paths)
free(paths);
if (dlh) {
if ((ret == 0) &&
(devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK))
devid_deviceid_to_nmlist_dlh = dlh;
else
(void) di_devlink_fini(&dlh);
}
if (ret && *retlist)
free(*retlist);
if (ret && err != 0)
errno = err;
return (ret);
}
void
devid_free_nmlist(devid_nmlist_t *list)
{
devid_nmlist_t *p = list;
if (list == NULL)
return;
while (p->devname != NULL) {
free(p->devname);
p++;
}
free(list);
}