mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-10-31 07:51:08 +03:00
318 lines
7.6 KiB
C
318 lines
7.6 KiB
C
/*
|
|
* sysfs_bus.c
|
|
*
|
|
* Generic bus utility functions for libsysfs
|
|
*
|
|
* Copyright (C) IBM Corp. 2003-2005
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
#include "libsysfs.h"
|
|
#include "sysfs.h"
|
|
|
|
static void sysfs_close_dev(void *dev)
|
|
{
|
|
sysfs_close_device((struct sysfs_device *)dev);
|
|
}
|
|
|
|
static void sysfs_close_drv(void *drv)
|
|
{
|
|
sysfs_close_driver((struct sysfs_driver *)drv);
|
|
}
|
|
|
|
/*
|
|
* compares names.
|
|
* @a: name looked for
|
|
* @b: sysfs_device comparing being compared
|
|
* returns 1 if a==b->name or 0 not equal
|
|
*/
|
|
static int name_equal(void *a, void *b)
|
|
{
|
|
if (!a || !b)
|
|
return 0;
|
|
|
|
if (strcmp(((char *)a), ((struct sysfs_device *)b)->name) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sysfs_close_bus: close single bus
|
|
* @bus: bus structure
|
|
*/
|
|
void sysfs_close_bus(struct sysfs_bus *bus)
|
|
{
|
|
if (bus) {
|
|
if (bus->attrlist)
|
|
dlist_destroy(bus->attrlist);
|
|
if (bus->devices)
|
|
dlist_destroy(bus->devices);
|
|
if (bus->drivers)
|
|
dlist_destroy(bus->drivers);
|
|
free(bus);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* alloc_bus: mallocs new bus structure
|
|
* returns sysfs_bus_bus struct or NULL
|
|
*/
|
|
static struct sysfs_bus *alloc_bus(void)
|
|
{
|
|
return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus));
|
|
}
|
|
|
|
/**
|
|
* sysfs_get_bus_devices: gets all devices for bus
|
|
* @bus: bus to get devices for
|
|
* returns dlist of devices with success and NULL with failure
|
|
*/
|
|
struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus)
|
|
{
|
|
struct sysfs_device *dev;
|
|
struct dlist *linklist;
|
|
char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
|
|
char target[SYSFS_PATH_MAX];
|
|
char *curlink;
|
|
|
|
if (!bus) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
memset(path, 0, SYSFS_PATH_MAX);
|
|
safestrcpy(path, bus->path);
|
|
safestrcat(path, "/");
|
|
safestrcat(path, SYSFS_DEVICES_NAME);
|
|
|
|
linklist = read_dir_links(path);
|
|
if (linklist) {
|
|
dlist_for_each_data(linklist, curlink, char) {
|
|
if (bus->devices) {
|
|
dev = (struct sysfs_device *)
|
|
dlist_find_custom(bus->devices,
|
|
(void *)curlink, name_equal);
|
|
if (dev)
|
|
continue;
|
|
}
|
|
safestrcpy(devpath, path);
|
|
safestrcat(devpath, "/");
|
|
safestrcat(devpath, curlink);
|
|
if (sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
|
|
dprintf("Error getting link - %s\n", devpath);
|
|
continue;
|
|
}
|
|
dev = sysfs_open_device_path(target);
|
|
if (!dev) {
|
|
dprintf("Error opening device at %s\n",
|
|
target);
|
|
continue;
|
|
}
|
|
if (!bus->devices)
|
|
bus->devices = dlist_new_with_delete
|
|
(sizeof(struct sysfs_device),
|
|
sysfs_close_dev);
|
|
dlist_unshift_sorted(bus->devices, dev, sort_list);
|
|
}
|
|
sysfs_close_list(linklist);
|
|
}
|
|
return (bus->devices);
|
|
}
|
|
|
|
/**
|
|
* sysfs_get_bus_drivers: gets all drivers for bus
|
|
* @bus: bus to get devices for
|
|
* returns dlist of devices with success and NULL with failure
|
|
*/
|
|
struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus)
|
|
{
|
|
struct sysfs_driver *drv;
|
|
struct dlist *dirlist;
|
|
char path[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX];
|
|
char *curdir;
|
|
|
|
if (!bus) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
memset(path, 0, SYSFS_PATH_MAX);
|
|
safestrcpy(path, bus->path);
|
|
safestrcat(path, "/");
|
|
safestrcat(path, SYSFS_DRIVERS_NAME);
|
|
|
|
dirlist = read_dir_subdirs(path);
|
|
if (dirlist) {
|
|
dlist_for_each_data(dirlist, curdir, char) {
|
|
if (bus->drivers) {
|
|
drv = (struct sysfs_driver *)
|
|
dlist_find_custom(bus->drivers,
|
|
(void *)curdir, name_equal);
|
|
if (drv)
|
|
continue;
|
|
}
|
|
safestrcpy(drvpath, path);
|
|
safestrcat(drvpath, "/");
|
|
safestrcat(drvpath, curdir);
|
|
drv = sysfs_open_driver_path(drvpath);
|
|
if (!drv) {
|
|
dprintf("Error opening driver at %s\n",
|
|
drvpath);
|
|
continue;
|
|
}
|
|
if (!bus->drivers)
|
|
bus->drivers = dlist_new_with_delete
|
|
(sizeof(struct sysfs_driver),
|
|
sysfs_close_drv);
|
|
dlist_unshift_sorted(bus->drivers, drv, sort_list);
|
|
}
|
|
sysfs_close_list(dirlist);
|
|
}
|
|
return (bus->drivers);
|
|
}
|
|
|
|
/**
|
|
* sysfs_open_bus: opens specific bus and all its devices on system
|
|
* returns sysfs_bus structure with success or NULL with error.
|
|
*/
|
|
struct sysfs_bus *sysfs_open_bus(const char *name)
|
|
{
|
|
struct sysfs_bus *bus;
|
|
char buspath[SYSFS_PATH_MAX];
|
|
|
|
if (!name) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
memset(buspath, 0, SYSFS_PATH_MAX);
|
|
if (sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) {
|
|
dprintf("Sysfs not supported on this system\n");
|
|
return NULL;
|
|
}
|
|
|
|
safestrcat(buspath, "/");
|
|
safestrcat(buspath, SYSFS_BUS_NAME);
|
|
safestrcat(buspath, "/");
|
|
safestrcat(buspath, name);
|
|
if (sysfs_path_is_dir(buspath)) {
|
|
dprintf("Invalid path to bus: %s\n", buspath);
|
|
return NULL;
|
|
}
|
|
bus = alloc_bus();
|
|
if (!bus) {
|
|
dprintf("calloc failed\n");
|
|
return NULL;
|
|
}
|
|
safestrcpy(bus->name, name);
|
|
safestrcpy(bus->path, buspath);
|
|
if (sysfs_remove_trailing_slash(bus->path)) {
|
|
dprintf("Incorrect path to bus %s\n", bus->path);
|
|
sysfs_close_bus(bus);
|
|
return NULL;
|
|
}
|
|
|
|
return bus;
|
|
}
|
|
|
|
/**
|
|
* sysfs_get_bus_device: Get specific device on bus using device's id
|
|
* @bus: bus to find device on
|
|
* @id: bus_id for device
|
|
* returns struct sysfs_device reference or NULL if not found.
|
|
*/
|
|
struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus,
|
|
const char *id)
|
|
{
|
|
struct sysfs_device *dev = NULL;
|
|
char devpath[SYSFS_PATH_MAX], target[SYSFS_PATH_MAX];
|
|
|
|
if (!bus || !id) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (bus->devices) {
|
|
dev = (struct sysfs_device *)dlist_find_custom
|
|
(bus->devices, (void *)id, name_equal);
|
|
if (dev)
|
|
return dev;
|
|
}
|
|
safestrcpy(devpath, bus->path);
|
|
safestrcat(devpath, "/");
|
|
safestrcat(devpath, SYSFS_DEVICES_NAME);
|
|
safestrcat(devpath, "/");
|
|
safestrcat(devpath, id);
|
|
if (sysfs_path_is_link(devpath)) {
|
|
dprintf("No such device %s on bus %s?\n", id, bus->name);
|
|
return NULL;
|
|
}
|
|
if (!sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
|
|
dev = sysfs_open_device_path(target);
|
|
if (!dev) {
|
|
dprintf("Error opening device at %s\n", target);
|
|
return NULL;
|
|
}
|
|
if (!bus->devices)
|
|
bus->devices = dlist_new_with_delete
|
|
(sizeof(struct sysfs_device),
|
|
sysfs_close_dev);
|
|
dlist_unshift_sorted(bus->devices, dev, sort_list);
|
|
}
|
|
return dev;
|
|
}
|
|
|
|
/**
|
|
* sysfs_get_bus_driver: Get specific driver on bus using driver name
|
|
* @bus: bus to find driver on
|
|
* @drvname: name of driver
|
|
* returns struct sysfs_driver reference or NULL if not found.
|
|
*/
|
|
struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus,
|
|
const char *drvname)
|
|
{
|
|
struct sysfs_driver *drv;
|
|
char drvpath[SYSFS_PATH_MAX];
|
|
|
|
if (!bus || !drvname) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (bus->drivers) {
|
|
drv = (struct sysfs_driver *)dlist_find_custom
|
|
(bus->drivers, (void *)drvname, name_equal);
|
|
if (drv)
|
|
return drv;
|
|
}
|
|
safestrcpy(drvpath, bus->path);
|
|
safestrcat(drvpath, "/");
|
|
safestrcat(drvpath, SYSFS_DRIVERS_NAME);
|
|
safestrcat(drvpath, "/");
|
|
safestrcat(drvpath, drvname);
|
|
drv = sysfs_open_driver_path(drvpath);
|
|
if (!drv) {
|
|
dprintf("Error opening driver at %s\n", drvpath);
|
|
return NULL;
|
|
}
|
|
if (!bus->drivers)
|
|
bus->drivers = dlist_new_with_delete
|
|
(sizeof(struct sysfs_driver),
|
|
sysfs_close_drv);
|
|
dlist_unshift_sorted(bus->drivers, drv, sort_list);
|
|
return drv;
|
|
}
|
|
|