Blame usr/sysfs.c

Packit eace71
/*
Packit eace71
 * This is from udev-121's udev_sysfs.c
Packit eace71
 *
Packit eace71
 * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
Packit eace71
 *
Packit eace71
 *	This program is free software; you can redistribute it and/or modify it
Packit eace71
 *	under the terms of the GNU General Public License as published by the
Packit eace71
 *	Free Software Foundation version 2 of the License.
Packit eace71
 *
Packit eace71
 *	This program is distributed in the hope that it will be useful, but
Packit eace71
 *	WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit eace71
 *	General Public License for more details.
Packit eace71
 *
Packit eace71
 *	You should have received a copy of the GNU General Public License along
Packit eace71
 *	with this program; if not, write to the Free Software Foundation, Inc.,
Packit eace71
 *	51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Packit eace71
 *
Packit eace71
 */
Packit eace71
Packit eace71
Packit eace71
#include <stdlib.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <stddef.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <fcntl.h>
Packit eace71
#include <ctype.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <limits.h>
Packit eace71
#include <inttypes.h>
Packit eace71
#include <sys/stat.h>
Packit eace71
Packit eace71
#include "log.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
#include "sysfs.h"
Packit eace71
Packit eace71
/*
Packit eace71
 * We take this file from udev so we want to make as few changes are possible,
Packit eace71
 * so we can maintain patches.
Packit eace71
 *
Packit eace71
 * This is from udev_utils_string.c
Packit eace71
 */
Packit eace71
static void remove_trailing_chars(char *path, char c)
Packit eace71
{
Packit eace71
	size_t len;
Packit eace71
Packit eace71
	len = strlen(path);
Packit eace71
	while (len > 0 && path[len-1] == c)
Packit eace71
		path[--len] = '\0';
Packit eace71
}
Packit eace71
Packit eace71
/* this converts udevs logging to ours. */
Packit eace71
#define dbg(format, arg...)						\
Packit eace71
	do {								\
Packit eace71
		log_debug(3, "%s: " format, __FUNCTION__,## arg);	\
Packit eace71
	} while (0)
Packit eace71
Packit eace71
/*
Packit eace71
 * Begin udev_sysfs.c code
Packit eace71
 */
Packit eace71
char sysfs_path[PATH_SIZE];
Packit eace71
Packit eace71
/* device cache */
Packit eace71
static LIST_HEAD(dev_list);
Packit eace71
Packit eace71
int sysfs_init(void)
Packit eace71
{
Packit eace71
	const char *env;
Packit eace71
Packit eace71
	env = getenv("SYSFS_PATH");
Packit eace71
	if (env) {
Packit eace71
		strlcpy(sysfs_path, env, sizeof(sysfs_path));
Packit eace71
		remove_trailing_chars(sysfs_path, '/');
Packit eace71
	} else
Packit eace71
		strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
Packit eace71
	dbg("sysfs_path='%s'", sysfs_path);
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&dev_list);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
void sysfs_cleanup(void)
Packit eace71
{
Packit eace71
	struct sysfs_device *dev_loop;
Packit eace71
	struct sysfs_device *dev_temp;
Packit eace71
Packit eace71
	list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) {
Packit eace71
		list_del_init(&dev_loop->node);
Packit eace71
		free(dev_loop);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
Packit eace71
			     const char *subsystem, const char *driver)
Packit eace71
{
Packit eace71
	char *pos;
Packit eace71
Packit eace71
	strlcpy(dev->devpath, devpath, sizeof(dev->devpath));
Packit eace71
	if (subsystem != NULL)
Packit eace71
		strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
Packit eace71
	if (driver != NULL)
Packit eace71
		strlcpy(dev->driver, driver, sizeof(dev->driver));
Packit eace71
Packit eace71
	/* set kernel name */
Packit eace71
	pos = strrchr(dev->devpath, '/');
Packit eace71
	if (pos == NULL)
Packit eace71
		return;
Packit eace71
	strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel));
Packit eace71
	dbg("kernel='%s'", dev->kernel);
Packit eace71
Packit eace71
	/* some devices have '!' in their name, change that to '/' */
Packit eace71
	pos = dev->kernel;
Packit eace71
	while (pos[0] != '\0') {
Packit eace71
		if (pos[0] == '!')
Packit eace71
			pos[0] = '/';
Packit eace71
		pos++;
Packit eace71
	}
Packit eace71
Packit eace71
	/* get kernel number */
Packit eace71
	pos = &dev->kernel[strlen(dev->kernel)];
Packit eace71
	while (isdigit(pos[-1]))
Packit eace71
		pos--;
Packit eace71
	strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number));
Packit eace71
	dbg("kernel_number='%s'", dev->kernel_number);
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_resolve_link(char *devpath, size_t size)
Packit eace71
{
Packit eace71
	char link_path[PATH_SIZE];
Packit eace71
	char link_target[PATH_SIZE];
Packit eace71
	int len;
Packit eace71
	int i;
Packit eace71
	int back;
Packit eace71
Packit eace71
	strlcpy(link_path, sysfs_path, sizeof(link_path));
Packit eace71
	strlcat(link_path, devpath, sizeof(link_path));
Packit eace71
	len = readlink(link_path, link_target, sizeof(link_target) - 1);
Packit eace71
	if (len <= 0)
Packit eace71
		return -1;
Packit eace71
	link_target[len] = '\0';
Packit eace71
	dbg("path link '%s' points to '%s'", devpath, link_target);
Packit eace71
Packit eace71
	for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
Packit eace71
		;
Packit eace71
	dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
Packit eace71
	for (i = 0; i <= back; i++) {
Packit eace71
		char *pos = strrchr(devpath, '/');
Packit eace71
Packit eace71
		if (pos == NULL)
Packit eace71
			return -1;
Packit eace71
		pos[0] = '\0';
Packit eace71
	}
Packit eace71
	dbg("after moving back '%s'", devpath);
Packit eace71
	strlcat(devpath, "/", size);
Packit eace71
	strlcat(devpath, &link_target[back * 3], size);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
struct sysfs_device *sysfs_device_get(const char *devpath)
Packit eace71
{
Packit eace71
	char path[PATH_SIZE];
Packit eace71
	char devpath_real[PATH_SIZE];
Packit eace71
	struct sysfs_device *dev;
Packit eace71
	struct sysfs_device *dev_loop;
Packit eace71
	struct stat statbuf;
Packit eace71
	char link_path[PATH_SIZE];
Packit eace71
	char link_target[PATH_SIZE];
Packit eace71
	int len;
Packit eace71
	char *pos;
Packit eace71
Packit eace71
	/* we handle only these devpathes */
Packit eace71
	if (devpath != NULL &&
Packit eace71
	    strncmp(devpath, "/devices/", 9) != 0 &&
Packit eace71
	    strncmp(devpath, "/subsystem/", 11) != 0 &&
Packit eace71
	    strncmp(devpath, "/module/", 8) != 0 &&
Packit eace71
	    strncmp(devpath, "/bus/", 5) != 0 &&
Packit eace71
	    strncmp(devpath, "/class/", 7) != 0 &&
Packit eace71
	    strncmp(devpath, "/block/", 7) != 0)
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	dbg("open '%s'", devpath);
Packit eace71
	strlcpy(devpath_real, devpath, sizeof(devpath_real));
Packit eace71
	remove_trailing_chars(devpath_real, '/');
Packit eace71
	if (devpath[0] == '\0' )
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	/* look for device already in cache (we never put an untranslated path in the cache) */
Packit eace71
	list_for_each_entry(dev_loop, &dev_list, node) {
Packit eace71
		if (strcmp(dev_loop->devpath, devpath_real) == 0) {
Packit eace71
			dbg("found in cache '%s'", dev_loop->devpath);
Packit eace71
			return dev_loop;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	/* if we got a link, resolve it to the real device */
Packit eace71
	strlcpy(path, sysfs_path, sizeof(path));
Packit eace71
	strlcat(path, devpath_real, sizeof(path));
Packit eace71
	if (lstat(path, &statbuf) != 0) {
Packit eace71
		dbg("stat '%s' failed: %s", path, strerror(errno));
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
	if (S_ISLNK(statbuf.st_mode)) {
Packit eace71
		if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0)
Packit eace71
			return NULL;
Packit eace71
Packit eace71
		/* now look for device in cache after path translation */
Packit eace71
		list_for_each_entry(dev_loop, &dev_list, node) {
Packit eace71
			if (strcmp(dev_loop->devpath, devpath_real) == 0) {
Packit eace71
				dbg("found in cache '%s'", dev_loop->devpath);
Packit eace71
				return dev_loop;
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	/* it is a new device */
Packit eace71
	dbg("new uncached device '%s'", devpath_real);
Packit eace71
	dev = malloc(sizeof(struct sysfs_device));
Packit eace71
	if (dev == NULL)
Packit eace71
		return NULL;
Packit eace71
	memset(dev, 0x00, sizeof(struct sysfs_device));
Packit eace71
Packit eace71
	sysfs_device_set_values(dev, devpath_real, NULL, NULL);
Packit eace71
Packit eace71
	/* get subsystem name */
Packit eace71
	strlcpy(link_path, sysfs_path, sizeof(link_path));
Packit eace71
	strlcat(link_path, dev->devpath, sizeof(link_path));
Packit eace71
	strlcat(link_path, "/subsystem", sizeof(link_path));
Packit eace71
	len = readlink(link_path, link_target, sizeof(link_target) - 1);
Packit eace71
	if (len > 0) {
Packit eace71
		/* get subsystem from "subsystem" link */
Packit eace71
		link_target[len] = '\0';
Packit eace71
		dbg("subsystem link '%s' points to '%s'", link_path, link_target);
Packit eace71
		pos = strrchr(link_target, '/');
Packit eace71
		if (pos != NULL)
Packit eace71
			strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
Packit eace71
	} else if (strstr(dev->devpath, "/drivers/") != NULL) {
Packit eace71
		strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem));
Packit eace71
	} else if (strncmp(dev->devpath, "/module/", 8) == 0) {
Packit eace71
		strlcpy(dev->subsystem, "module", sizeof(dev->subsystem));
Packit eace71
	} else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) {
Packit eace71
		pos = strrchr(dev->devpath, '/');
Packit eace71
		if (pos == &dev->devpath[10])
Packit eace71
			strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
Packit eace71
	} else if (strncmp(dev->devpath, "/class/", 7) == 0) {
Packit eace71
		pos = strrchr(dev->devpath, '/');
Packit eace71
		if (pos == &dev->devpath[6])
Packit eace71
			strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
Packit eace71
	} else if (strncmp(dev->devpath, "/bus/", 5) == 0) {
Packit eace71
		pos = strrchr(dev->devpath, '/');
Packit eace71
		if (pos == &dev->devpath[4])
Packit eace71
			strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
Packit eace71
	}
Packit eace71
Packit eace71
	/* get driver name */
Packit eace71
	strlcpy(link_path, sysfs_path, sizeof(link_path));
Packit eace71
	strlcat(link_path, dev->devpath, sizeof(link_path));
Packit eace71
	strlcat(link_path, "/driver", sizeof(link_path));
Packit eace71
	len = readlink(link_path, link_target, sizeof(link_target) - 1);
Packit eace71
	if (len > 0) {
Packit eace71
		link_target[len] = '\0';
Packit eace71
		dbg("driver link '%s' points to '%s'", link_path, link_target);
Packit eace71
		pos = strrchr(link_target, '/');
Packit eace71
		if (pos != NULL)
Packit eace71
			strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
Packit eace71
	}
Packit eace71
Packit eace71
	dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'", dev->devpath, dev->subsystem, dev->driver);
Packit eace71
	list_add(&dev->node, &dev_list);
Packit eace71
Packit eace71
	return dev;
Packit eace71
}
Packit eace71
Packit eace71
struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
Packit eace71
{
Packit eace71
	char parent_devpath[PATH_SIZE];
Packit eace71
	char *pos;
Packit eace71
Packit eace71
	dbg("open '%s'", dev->devpath);
Packit eace71
Packit eace71
	/* look if we already know the parent */
Packit eace71
	if (dev->parent != NULL)
Packit eace71
		return dev->parent;
Packit eace71
Packit eace71
	strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
Packit eace71
	dbg("'%s'", parent_devpath);
Packit eace71
Packit eace71
	/* strip last element */
Packit eace71
	pos = strrchr(parent_devpath, '/');
Packit eace71
	if (pos == NULL || pos == parent_devpath)
Packit eace71
		return NULL;
Packit eace71
	pos[0] = '\0';
Packit eace71
Packit eace71
	if (strncmp(parent_devpath, "/class", 6) == 0) {
Packit eace71
		pos = strrchr(parent_devpath, '/');
Packit eace71
		if (pos == &parent_devpath[6] || pos == parent_devpath) {
Packit eace71
			dbg("/class top level, look for device link");
Packit eace71
			goto device_link;
Packit eace71
		}
Packit eace71
	}
Packit eace71
	if (strcmp(parent_devpath, "/block") == 0) {
Packit eace71
		dbg("/block top level, look for device link");
Packit eace71
		goto device_link;
Packit eace71
	}
Packit eace71
Packit eace71
	/* are we at the top level? */
Packit eace71
	pos = strrchr(parent_devpath, '/');
Packit eace71
	if (pos == NULL || pos == parent_devpath)
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	/* get parent and remember it */
Packit eace71
	dev->parent = sysfs_device_get(parent_devpath);
Packit eace71
	return dev->parent;
Packit eace71
Packit eace71
device_link:
Packit eace71
	strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
Packit eace71
	strlcat(parent_devpath, "/device", sizeof(parent_devpath));
Packit eace71
	if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0)
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	/* get parent and remember it */
Packit eace71
	dev->parent = sysfs_device_get(parent_devpath);
Packit eace71
	return dev->parent;
Packit eace71
}
Packit eace71
Packit eace71
struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem)
Packit eace71
{
Packit eace71
	struct sysfs_device *dev_parent;
Packit eace71
Packit eace71
	dev_parent = sysfs_device_get_parent(dev);
Packit eace71
	while (dev_parent != NULL) {
Packit eace71
		if (strcmp(dev_parent->subsystem, subsystem) == 0)
Packit eace71
			return dev_parent;
Packit eace71
		dev_parent = sysfs_device_get_parent(dev_parent);
Packit eace71
	}
Packit eace71
	return NULL;
Packit eace71
}
Packit eace71
Packit eace71
char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
Packit eace71
{
Packit eace71
	char path_full[PATH_SIZE];
Packit eace71
	char value[NAME_SIZE] = { '\0', };
Packit eace71
	struct stat statbuf;
Packit eace71
	int fd;
Packit eace71
	ssize_t size;
Packit eace71
	size_t sysfs_len;
Packit eace71
Packit eace71
	dbg("open '%s'/'%s'", devpath, attr_name);
Packit eace71
	sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
Packit eace71
	if(sysfs_len >= sizeof(path_full))
Packit eace71
		sysfs_len = sizeof(path_full) - 1;
Packit eace71
	strlcat(path_full, devpath, sizeof(path_full));
Packit eace71
	strlcat(path_full, "/", sizeof(path_full));
Packit eace71
	strlcat(path_full, attr_name, sizeof(path_full));
Packit eace71
Packit eace71
	if (lstat(path_full, &statbuf) != 0) {
Packit eace71
		dbg("stat '%s' failed: %s", path_full, strerror(errno));
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
Packit eace71
	if (S_ISLNK(statbuf.st_mode)) {
Packit eace71
		/* links return the last element of the target path */
Packit eace71
		char link_target[PATH_SIZE];
Packit eace71
		int len;
Packit eace71
		const char *pos;
Packit eace71
Packit eace71
		len = readlink(path_full, link_target, sizeof(link_target) - 1);
Packit eace71
		if (len > 0) {
Packit eace71
			link_target[len] = '\0';
Packit eace71
			pos = strrchr(link_target, '/');
Packit eace71
			if (pos != NULL) {
Packit eace71
				dbg("cache '%s' with link value '%s'", path_full, value);
Packit eace71
				strlcpy(value, &pos[1], NAME_SIZE);
Packit eace71
			}
Packit eace71
		}
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
Packit eace71
	/* skip directories */
Packit eace71
	if (S_ISDIR(statbuf.st_mode))
Packit eace71
		goto out;
Packit eace71
Packit eace71
	/* skip non-readable files */
Packit eace71
	if ((statbuf.st_mode & S_IRUSR) == 0)
Packit eace71
		goto out;
Packit eace71
Packit eace71
	/* read attribute value */
Packit eace71
	fd = open(path_full, O_RDONLY);
Packit eace71
	if (fd < 0) {
Packit eace71
		dbg("attribute '%s' can not be opened", path_full);
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
	size = read(fd, value, sizeof(value));
Packit eace71
	close(fd);
Packit eace71
	if (size < 0)
Packit eace71
		goto out;
Packit eace71
	if (size == sizeof(value))
Packit eace71
		goto out;
Packit eace71
Packit eace71
	/* got a valid value, store and return it */
Packit eace71
	value[size] = '\0';
Packit eace71
	remove_trailing_chars(value, '\n');
Packit eace71
Packit eace71
out:
Packit eace71
	if (value[0] == '\0')
Packit eace71
		return NULL;
Packit eace71
	return strdup(value);
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len, const char *subsystem, const char *id)
Packit eace71
{
Packit eace71
	size_t sysfs_len;
Packit eace71
	char path_full[PATH_SIZE];
Packit eace71
	char *path;
Packit eace71
	struct stat statbuf;
Packit eace71
Packit eace71
	sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
Packit eace71
	path = &path_full[sysfs_len];
Packit eace71
Packit eace71
	if (strcmp(subsystem, "subsystem") == 0) {
Packit eace71
		strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
Packit eace71
		strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
		if (stat(path_full, &statbuf) == 0)
Packit eace71
			goto found;
Packit eace71
Packit eace71
		strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
Packit eace71
		strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
		if (stat(path_full, &statbuf) == 0)
Packit eace71
			goto found;
Packit eace71
		goto out;
Packit eace71
Packit eace71
		strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
Packit eace71
		strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
		if (stat(path_full, &statbuf) == 0)
Packit eace71
			goto found;
Packit eace71
	}
Packit eace71
Packit eace71
	if (strcmp(subsystem, "module") == 0) {
Packit eace71
		strlcpy(path, "/module/", sizeof(path_full) - sysfs_len);
Packit eace71
		strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
		if (stat(path_full, &statbuf) == 0)
Packit eace71
			goto found;
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
Packit eace71
	if (strcmp(subsystem, "drivers") == 0) {
Packit eace71
		char subsys[NAME_SIZE];
Packit eace71
		char *driver;
Packit eace71
Packit eace71
		strlcpy(subsys, id, sizeof(subsys));
Packit eace71
		driver = strchr(subsys, ':');
Packit eace71
		if (driver != NULL) {
Packit eace71
			driver[0] = '\0';
Packit eace71
			driver = &driver[1];
Packit eace71
			strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, subsys, sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, driver, sizeof(path_full) - sysfs_len);
Packit eace71
			if (stat(path_full, &statbuf) == 0)
Packit eace71
				goto found;
Packit eace71
Packit eace71
			strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, subsys, sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
Packit eace71
			strlcat(path, driver, sizeof(path_full) - sysfs_len);
Packit eace71
			if (stat(path_full, &statbuf) == 0)
Packit eace71
				goto found;
Packit eace71
		}
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
Packit eace71
	strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
	if (stat(path_full, &statbuf) == 0)
Packit eace71
		goto found;
Packit eace71
Packit eace71
	strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
	if (stat(path_full, &statbuf) == 0)
Packit eace71
		goto found;
Packit eace71
Packit eace71
	strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, "/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
	if (stat(path_full, &statbuf) == 0)
Packit eace71
		goto found;
Packit eace71
Packit eace71
	strlcpy(path, "/firmware/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, "/", sizeof(path_full) - sysfs_len);
Packit eace71
	strlcat(path, id, sizeof(path_full) - sysfs_len);
Packit eace71
	if (stat(path_full, &statbuf) == 0)
Packit eace71
		goto found;
Packit eace71
Packit eace71
out:
Packit eace71
	return 0;
Packit eace71
found:
Packit eace71
	if (S_ISLNK(statbuf.st_mode))
Packit eace71
		sysfs_resolve_link(path, sizeof(path_full) - sysfs_len);
Packit eace71
	strlcpy(devpath_full, path, len);
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
Packit eace71
char *sysfs_get_value(const char *id, char *subsys, char *param)
Packit eace71
{
Packit eace71
	char devpath[PATH_SIZE];
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath),
Packit eace71
					       subsys, id)) {
Packit eace71
		log_debug(3, "Could not lookup devpath for %s %s",
Packit eace71
			  subsys, id);
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
Packit eace71
	sysfs_value = sysfs_attr_get_value(devpath, param);
Packit eace71
	if (!sysfs_value) {
Packit eace71
		log_debug(3, "Could not read attr %s on path %s",
Packit eace71
			  param, devpath);
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!strncmp(sysfs_value, "<NULL>", 6) ||
Packit eace71
	    !strncmp(sysfs_value, "(null)", 6)) {
Packit eace71
		free(sysfs_value);
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
Packit eace71
	return sysfs_value;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_uint(char *id, char *subsys, char *param,
Packit eace71
		   unsigned int *value)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	*value = -1;
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value)
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	errno = 0;
Packit eace71
	*value = strtoul(sysfs_value, NULL, 0);
Packit eace71
	free(sysfs_value);
Packit eace71
	if (errno)
Packit eace71
		return errno;
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_int(const char *id, char *subsys, char *param, int *value)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	*value = -1;
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value)
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	*value = atoi(sysfs_value);
Packit eace71
	free(sysfs_value);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_str(char *id, char *subsys, char *param, char *value,
Packit eace71
		  int value_size)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
	int len;
Packit eace71
Packit eace71
	value[0] = '\0';
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value || !strlen(sysfs_value))
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	len = strlen(sysfs_value);
Packit eace71
	if (len && (sysfs_value[len - 1] == '\n'))
Packit eace71
		sysfs_value[len - 1] = '\0';
Packit eace71
	strncpy(value, sysfs_value, value_size);
Packit eace71
	value[value_size - 1] = '\0';
Packit eace71
	free(sysfs_value);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_uint64(char *id, char *subsys, char *param, uint64_t *value)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	*value = -1;
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value)
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	if (sscanf(sysfs_value, "%" PRIu64 "\n", value) != 1) {
Packit eace71
		free(sysfs_value);
Packit eace71
		return EINVAL;
Packit eace71
	}
Packit eace71
	free(sysfs_value);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_uint8(char *id, char *subsys, char *param,
Packit eace71
		    uint8_t *value)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	*value = -1;
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value)
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	*value = (uint8_t)atoi(sysfs_value);
Packit eace71
	free(sysfs_value);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_get_uint16(char *id, char *subsys, char *param,
Packit eace71
		     uint16_t *value)
Packit eace71
{
Packit eace71
	char *sysfs_value;
Packit eace71
Packit eace71
	*value = -1;
Packit eace71
	sysfs_value = sysfs_get_value(id, subsys, param);
Packit eace71
	if (!sysfs_value)
Packit eace71
		return EIO;
Packit eace71
Packit eace71
	*value = (uint16_t)atoi(sysfs_value);
Packit eace71
	free(sysfs_value);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int sysfs_set_param(char *id, char *subsys, char *attr_name,
Packit eace71
		    char *write_buf, ssize_t buf_size)
Packit eace71
{
Packit eace71
	struct stat statbuf;
Packit eace71
	char devpath[PATH_SIZE];
Packit eace71
	size_t sysfs_len;
Packit eace71
	char path_full[PATH_SIZE];
Packit eace71
	int rc = 0, fd;
Packit eace71
Packit eace71
	if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath),
Packit eace71
					       subsys, id)) {
Packit eace71
		log_debug(3, "Could not lookup devpath for %s %s",
Packit eace71
			  subsys, id);
Packit eace71
		return EIO;
Packit eace71
	}
Packit eace71
Packit eace71
	sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
Packit eace71
	if(sysfs_len >= sizeof(path_full))
Packit eace71
		sysfs_len = sizeof(path_full) - 1;
Packit eace71
	strlcat(path_full, devpath, sizeof(path_full));
Packit eace71
	strlcat(path_full, "/", sizeof(path_full));
Packit eace71
	strlcat(path_full, attr_name, sizeof(path_full));
Packit eace71
Packit eace71
	if (lstat(path_full, &statbuf)) {
Packit eace71
		log_debug(3, "Could not stat %s", path_full);
Packit eace71
		return errno;
Packit eace71
	}
Packit eace71
Packit eace71
	if ((statbuf.st_mode & S_IWUSR) == 0) {
Packit eace71
		log_error("Could not write to %s. Invalid permissions.",
Packit eace71
			  path_full);
Packit eace71
		return EACCES;
Packit eace71
	}
Packit eace71
Packit eace71
	fd = open(path_full, O_WRONLY);
Packit eace71
	if (fd < 0) {
Packit eace71
		log_error("Could not open %s err %d", path_full, errno);
Packit eace71
		return errno;
Packit eace71
	}
Packit eace71
Packit eace71
	if (write(fd, write_buf, buf_size) == -1)
Packit eace71
		rc = errno;
Packit eace71
	close(fd);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
char *sysfs_get_uevent_field(const char *path, const char *field)
Packit eace71
{
Packit eace71
	char *uevent_path = NULL;
Packit eace71
	FILE *f = NULL;
Packit eace71
	char *line, buffer[1024];
Packit eace71
	char *ff, *d;
Packit eace71
	char *out = NULL;
Packit eace71
Packit eace71
	uevent_path = calloc(1, PATH_MAX);
Packit eace71
	if (!uevent_path)
Packit eace71
		return NULL;
Packit eace71
	snprintf(uevent_path, PATH_MAX, "%s/uevent", path);
Packit eace71
Packit eace71
	f = fopen(uevent_path, "r");
Packit eace71
	if (!f)
Packit eace71
		goto out;
Packit eace71
	while ((line = fgets(buffer, sizeof (buffer), f))) {
Packit eace71
		ff = strtok(line, "=");
Packit eace71
		d = strtok(NULL, "\n");
Packit eace71
		if (strcmp(ff, field))
Packit eace71
			continue;
Packit eace71
		out = strdup(d);
Packit eace71
		break;
Packit eace71
	}
Packit eace71
	fclose(f);
Packit eace71
out:
Packit eace71
	free(uevent_path);
Packit eace71
	return out;
Packit eace71
}
Packit eace71
Packit eace71
char *sysfs_get_uevent_devtype(const char *path)
Packit eace71
{
Packit eace71
	return sysfs_get_uevent_field(path, "DEVTYPE");
Packit eace71
}
Packit eace71
Packit eace71
char *sysfs_get_uevent_devname(const char *path)
Packit eace71
{
Packit eace71
	return sysfs_get_uevent_field(path, "DEVNAME");
Packit eace71
}