replace libsysfs
We never used any of the libsysfs convenience features. Here we replace it completely with 300 lines of code, which are much simpler and a bit faster cause udev(d) does not open any syfs file for a simple event which does not need any parent device information. Signed-off-by: Kay Sievers <kay.sievers@suse.de>
This commit is contained in:
@@ -58,7 +58,6 @@ PROGRAMS = \
|
||||
|
||||
HEADERS = \
|
||||
udev.h \
|
||||
udev_utils.h \
|
||||
udev_rules.h \
|
||||
logging.h \
|
||||
udev_libc_wrapper.h \
|
||||
@@ -66,12 +65,12 @@ HEADERS = \
|
||||
list.h
|
||||
|
||||
UDEV_OBJS = \
|
||||
udev_event.o \
|
||||
udev_device.o \
|
||||
udev_config.o \
|
||||
udev_add.o \
|
||||
udev_remove.o \
|
||||
udev_db.o \
|
||||
udev_sysfs.o \
|
||||
udev_rules.o \
|
||||
udev_rules_parse.o \
|
||||
udev_utils.o \
|
||||
@@ -90,15 +89,6 @@ MAN_PAGES = \
|
||||
udevinfo.8 \
|
||||
udevstart.8
|
||||
|
||||
SYSFS_OBJS = \
|
||||
libsysfs/sysfs_class.o \
|
||||
libsysfs/sysfs_device.o \
|
||||
libsysfs/sysfs_dir.o \
|
||||
libsysfs/sysfs_driver.o \
|
||||
libsysfs/sysfs_utils.o \
|
||||
libsysfs/dlist.o
|
||||
LIBSYSFS = libsysfs/libsysfs.a
|
||||
|
||||
# config files automatically generated
|
||||
GEN_CONFIGS = \
|
||||
$(LOCAL_CFG_DIR)/udev.conf
|
||||
@@ -144,10 +134,6 @@ LDFLAGS = -Wl,-warn-common
|
||||
OPTFLAGS = -Os
|
||||
CFLAGS += $(OPTFLAGS)
|
||||
|
||||
# include our local copy of libsysfs
|
||||
CFLAGS += -I$(PWD)/libsysfs/sysfs \
|
||||
-I$(PWD)/libsysfs
|
||||
|
||||
ifeq ($(strip $(USE_LOG)),true)
|
||||
CFLAGS += -DUSE_LOG
|
||||
endif
|
||||
@@ -200,7 +186,6 @@ all: $(PROGRAMS) $(MAN_PAGES)
|
||||
STRIPCMD="$(STRIPCMD)" \
|
||||
LIB_OBJS="$(LIB_OBJS)" \
|
||||
LIBUDEV="$(PWD)/$(LIBUDEV)" \
|
||||
LIBSYSFS="$(PWD)/$(LIBSYSFS)" \
|
||||
QUIET="$(QUIET)" \
|
||||
-C $$target $@ || exit 1; \
|
||||
done;
|
||||
@@ -211,28 +196,21 @@ all: $(PROGRAMS) $(MAN_PAGES)
|
||||
.SUFFIXES:
|
||||
|
||||
# build the objects
|
||||
%.o: %.c $(HOST_PROGS) $(GEN_HEADERS)
|
||||
%.o: %.c $(HOST_PROGS) $(HEADERS) $(GEN_HEADERS)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
# "Static Pattern Rule" to build all programs
|
||||
$(PROGRAMS): %: $(HOST_PROGS) $(HEADERS) $(GEN_HEADERS) $(LIBSYSFS) $(LIBUDEV) %.o
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(PROGRAMS): %: $(HOST_PROGS) $(HEADERS) $(GEN_HEADERS) $(LIBUDEV) %.o
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(STRIPCMD),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
$(UDEV_OBJS):
|
||||
$(LIBUDEV): $(HOST_PROGS) $(HEADERS) $(GEN_HEADERS) $(UDEV_OBJS)
|
||||
@rm -f $@
|
||||
$(QUIET) $(AR) cq $@ $(UDEV_OBJS)
|
||||
$(QUIET) $(RANLIB) $@
|
||||
|
||||
$(SYSFS_OBJS):
|
||||
$(LIBSYSFS): $(HOST_PROGS) $(SYSFS_OBJS)
|
||||
@rm -f $@
|
||||
$(QUIET) $(AR) cq $@ $(SYSFS_OBJS)
|
||||
$(QUIET) $(RANLIB) $@
|
||||
|
||||
# generate config files
|
||||
$(GEN_CONFIGS):
|
||||
sed -e "s:@udevdir@:$(udevdir):" -e "s:@configdir@:$(configdir):" < $@.in > $@
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
udev 080
|
||||
========
|
||||
Complete removal of libsysfs, replaced by simple helper functions
|
||||
which are much simpler and a bit faster. The udev daemon operatesentirely
|
||||
on event parameters and does not use sysfs for simple rules anymore.
|
||||
Please report any new bugs/problems, that may be caused by this big
|
||||
change. They will be fixed immediately.
|
||||
|
||||
The enumeration format character '%e' is deprecated and will be
|
||||
removed sometimes from a future udev version. It never worked correctly
|
||||
outside of udevstart, so we can't use it with the new parallel
|
||||
@@ -10,6 +16,8 @@ MODALIAS and $modalias is not needed and will be removed from one of
|
||||
the next udev versions, replace it in all rules with ENV{MODALIAS} or
|
||||
the sysfs "modalias" value.
|
||||
|
||||
Thanks a lot to Marco for all his help on finding and fixing bugs.
|
||||
|
||||
udev 079
|
||||
========
|
||||
Let scsi_id request libata drive serial numbers from page 0x80.
|
||||
|
||||
@@ -361,13 +361,6 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>%b</option>, <option>$id</option></term>
|
||||
<listitem>
|
||||
<para>The kernel bus id for this device.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>%n</option>, <option>$number</option></term>
|
||||
<listitem>
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>udevtest <filename>device-path</filename> <replaceable>subsystem</replaceable></command>
|
||||
<command>udevtest <filename>device-path</filename></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(STRIPCMD),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
@@ -24,8 +25,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/hdreg.h>
|
||||
|
||||
#include "../../logging.h"
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
#ifdef USE_LOG
|
||||
void log_message(int priority, const char *format, ...)
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "../../logging.h"
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
/*
|
||||
* Taken from the cdrom.h kernel include file.
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "../../logging.h"
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
#ifdef USE_LOG
|
||||
void log_message(int priority, const char *format, ...)
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../../logging.h"
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
#ifdef USE_LOG
|
||||
void log_message(int priority, const char *format, ...)
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
#define FIRMWARE_PATH "/lib/firmware"
|
||||
#define PATH_SIZE 256
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -32,7 +32,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../list.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
#include "run_directory.h"
|
||||
|
||||
static int exec_program(const char *filename, const char *subsystem)
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../list.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
#include "run_directory.h"
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../list.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
#include "run_directory.h"
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ INSTALL_DATA = ${INSTALL} -m 644
|
||||
INSTALL_SCRIPT = ${INSTALL_PROGRAM}
|
||||
|
||||
# be able to run without udev
|
||||
LIBSYSFS = -lsysfs
|
||||
CROSS =
|
||||
QUIET =
|
||||
CC = $(CROSS)gcc
|
||||
@@ -47,7 +46,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
+146
-292
@@ -32,37 +32,20 @@
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sysfs/libsysfs.h>
|
||||
#include "scsi_id_version.h"
|
||||
|
||||
#include "../../udev.h"
|
||||
#include "scsi_id.h"
|
||||
#include "scsi_id_version.h"
|
||||
|
||||
#ifndef SCSI_ID_VERSION
|
||||
#warning No version
|
||||
#define SCSI_ID_VERSION "unknown"
|
||||
#endif
|
||||
/* temporary names for mknod */
|
||||
#define TMP_DIR "/dev"
|
||||
#define TMP_PREFIX "tmp-scsi"
|
||||
|
||||
/*
|
||||
* temporary names for mknod.
|
||||
*/
|
||||
#define TMP_DIR "/dev"
|
||||
#define TMP_PREFIX "tmp-scsi"
|
||||
|
||||
/*
|
||||
* XXX Note the 'e' (send output to stderr in all cases), and 'c' (callout)
|
||||
* options are not supported, but other code is still left in place for
|
||||
* now.
|
||||
*/
|
||||
static const char short_options[] = "abd:f:gip:s:uvVx";
|
||||
/*
|
||||
* Just duplicate per dev options.
|
||||
*/
|
||||
static const char dev_short_options[] = "bgp:";
|
||||
|
||||
char sysfs_mnt_path[SYSFS_PATH_MAX];
|
||||
|
||||
static int all_good;
|
||||
static int always_info;
|
||||
static char *default_callout;
|
||||
static int dev_specified;
|
||||
static int sys_specified;
|
||||
static char config_file[MAX_NAME_LEN] = SCSI_ID_CONFIG_FILE;
|
||||
@@ -78,28 +61,30 @@ static char model_str[64];
|
||||
static char revision_str[16];
|
||||
static char type_str[16];
|
||||
|
||||
void log_message (int level, const char *format, ...)
|
||||
#ifdef USE_LOG
|
||||
void log_message(int priority, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_list args;
|
||||
static int udev_log = -1;
|
||||
|
||||
if (!debug && level == LOG_DEBUG)
|
||||
if (udev_log == -1) {
|
||||
const char *value;
|
||||
|
||||
value = getenv("UDEV_LOG");
|
||||
if (value)
|
||||
udev_log = log_priority(value);
|
||||
else
|
||||
udev_log = LOG_ERR;
|
||||
}
|
||||
|
||||
if (priority > udev_log)
|
||||
return;
|
||||
|
||||
va_start (args, format);
|
||||
if (!hotplug_mode || use_stderr) {
|
||||
vfprintf(stderr, format, args);
|
||||
} else {
|
||||
static int logging_init = 0;
|
||||
if (!logging_init) {
|
||||
openlog ("scsi_id", LOG_PID, LOG_DAEMON);
|
||||
logging_init = 1;
|
||||
}
|
||||
|
||||
vsyslog(level, format, args);
|
||||
}
|
||||
va_end (args);
|
||||
return;
|
||||
va_start(args, format);
|
||||
vsyslog(priority, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void set_str(char *to, const char *from, size_t count)
|
||||
{
|
||||
@@ -171,68 +156,35 @@ static void set_type(char *to, const char *from, size_t len)
|
||||
to[len-1] = '\0';
|
||||
}
|
||||
|
||||
static int get_major_minor(struct sysfs_class_device *class_dev, int *maj,
|
||||
int *min)
|
||||
static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
|
||||
{
|
||||
struct sysfs_attribute *dev_attr;
|
||||
unsigned int maj, min;
|
||||
const char *attr;
|
||||
|
||||
dev_attr = sysfs_get_classdev_attr(class_dev, "dev");
|
||||
if (!dev_attr) {
|
||||
/*
|
||||
* XXX This happens a lot, since sg has no dev attr.
|
||||
* And now sysfsutils does not set a meaningful errno
|
||||
* value. Someday change this back to a LOG_WARNING.
|
||||
* And if sysfsutils changes, check for ENOENT and handle
|
||||
* it separately.
|
||||
*/
|
||||
log_message(LOG_DEBUG, "%s: could not get dev attribute: %s\n",
|
||||
class_dev->name, strerror(errno));
|
||||
dbg("%s", devpath);
|
||||
attr = sysfs_attr_get_value(devpath, "dev");
|
||||
if (attr == NULL) {
|
||||
dbg("%s: could not get dev attribute: %s", devpath, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
dprintf("dev value %s", dev_attr->value); /* value has a trailing \n */
|
||||
if (sscanf(dev_attr->value, "%u:%u", maj, min) != 2) {
|
||||
log_message(LOG_WARNING, "%s: invalid dev major/minor\n",
|
||||
class_dev->name);
|
||||
dbg("dev value %s", attr);
|
||||
if (sscanf(attr, "%u:%u", &maj, &min) != 2) {
|
||||
err("%s: invalid dev major/minor", devpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_tmp_dev(struct sysfs_class_device *class_dev, char *tmpdev,
|
||||
int dev_type)
|
||||
{
|
||||
int maj, min;
|
||||
|
||||
dprintf("(%s)\n", class_dev->name);
|
||||
|
||||
if (get_major_minor(class_dev, &maj, &min))
|
||||
return -1;
|
||||
snprintf(tmpdev, MAX_NAME_LEN, "%s/%s-maj%d-min%d-%u",
|
||||
TMP_DIR, TMP_PREFIX, maj, min, getpid());
|
||||
|
||||
dprintf("tmpdev '%s'\n", tmpdev);
|
||||
|
||||
dbg("tmpdev '%s'", tmpdev);
|
||||
if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) {
|
||||
log_message(LOG_WARNING, "mknod failed: %s\n", strerror(errno));
|
||||
err("mknod failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int has_sysfs_prefix(const char *path, const char *prefix)
|
||||
{
|
||||
char match[MAX_NAME_LEN];
|
||||
|
||||
strncpy(match, sysfs_mnt_path, MAX_NAME_LEN);
|
||||
strncat(match, prefix, MAX_NAME_LEN);
|
||||
if (strncmp(path, match, strlen(match)) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_value:
|
||||
*
|
||||
@@ -289,8 +241,8 @@ static int argc_count(char *opts)
|
||||
*
|
||||
* vendor and model can end in '\n'.
|
||||
*/
|
||||
static int get_file_options(char *vendor, char *model, int *argc,
|
||||
char ***newargv)
|
||||
static int get_file_options(const char *vendor, const char *model,
|
||||
int *argc, char ***newargv)
|
||||
{
|
||||
char *buffer;
|
||||
FILE *fd;
|
||||
@@ -301,15 +253,14 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
int c;
|
||||
int retval = 0;
|
||||
|
||||
dprintf("vendor='%s'; model='%s'\n", vendor, model);
|
||||
dbg("vendor='%s'; model='%s'\n", vendor, model);
|
||||
fd = fopen(config_file, "r");
|
||||
if (fd == NULL) {
|
||||
dprintf("can't open %s\n", config_file);
|
||||
dbg("can't open %s\n", config_file);
|
||||
if (errno == ENOENT) {
|
||||
return 1;
|
||||
} else {
|
||||
log_message(LOG_WARNING, "can't open %s: %s\n",
|
||||
config_file, strerror(errno));
|
||||
err("can't open %s: %s", config_file, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -321,7 +272,7 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
*/
|
||||
buffer = malloc(MAX_BUFFER_LEN);
|
||||
if (!buffer) {
|
||||
log_message(LOG_WARNING, "Can't allocate memory.\n");
|
||||
err("Can't allocate memory.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -335,29 +286,22 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
break;
|
||||
lineno++;
|
||||
if (buf[strlen(buffer) - 1] != '\n') {
|
||||
log_message(LOG_WARNING,
|
||||
"Config file line %d too long.\n", lineno);
|
||||
info("Config file line %d too long.\n", lineno);
|
||||
break;
|
||||
}
|
||||
|
||||
while (isspace(*buf))
|
||||
buf++;
|
||||
|
||||
/* blank or all whitespace line */
|
||||
if (*buf == '\0')
|
||||
/*
|
||||
* blank or all whitespace line
|
||||
*/
|
||||
continue;
|
||||
|
||||
/* comment line */
|
||||
if (*buf == '#')
|
||||
/*
|
||||
* comment line
|
||||
*/
|
||||
continue;
|
||||
|
||||
#ifdef LOTS
|
||||
dprintf("lineno %d: '%s'\n", lineno, buf);
|
||||
#endif
|
||||
dbg("lineno %d: '%s'\n", lineno, buf);
|
||||
str1 = strsep(&buf, "=");
|
||||
if (str1 && strcasecmp(str1, "VENDOR") == 0) {
|
||||
str1 = get_value(&buf);
|
||||
@@ -387,22 +331,20 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
}
|
||||
options_in = str1;
|
||||
}
|
||||
dprintf("config file line %d:"
|
||||
dbg("config file line %d:"
|
||||
" vendor '%s'; model '%s'; options '%s'\n",
|
||||
lineno, vendor_in, model_in, options_in);
|
||||
/*
|
||||
* Only allow: [vendor=foo[,model=bar]]options=stuff
|
||||
*/
|
||||
if (!options_in || (!vendor_in && model_in)) {
|
||||
log_message(LOG_WARNING,
|
||||
"Error parsing config file line %d '%s'\n",
|
||||
lineno, buffer);
|
||||
info("Error parsing config file line %d '%s'", lineno, buffer);
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
if (vendor == NULL) {
|
||||
if (vendor_in == NULL) {
|
||||
dprintf("matched global option\n");
|
||||
dbg("matched global option\n");
|
||||
break;
|
||||
}
|
||||
} else if ((vendor_in && strncmp(vendor, vendor_in,
|
||||
@@ -416,10 +358,10 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
* give a partial match (that is FOO
|
||||
* matches FOOBAR).
|
||||
*/
|
||||
dprintf("matched vendor/model\n");
|
||||
dbg("matched vendor/model\n");
|
||||
break;
|
||||
} else {
|
||||
dprintf("no match\n");
|
||||
dbg("no match\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,8 +376,7 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
c = argc_count(buffer) + 2;
|
||||
*newargv = calloc(c, sizeof(**newargv));
|
||||
if (!*newargv) {
|
||||
log_message(LOG_WARNING,
|
||||
"Can't allocate memory.\n");
|
||||
err("Can't allocate memory.");
|
||||
retval = -1;
|
||||
} else {
|
||||
*argc = c;
|
||||
@@ -450,9 +391,7 @@ static int get_file_options(char *vendor, char *model, int *argc,
|
||||
(*newargv)[c] = strsep(&buffer, " ");
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* No matches.
|
||||
*/
|
||||
/* No matches */
|
||||
retval = 1;
|
||||
}
|
||||
}
|
||||
@@ -480,9 +419,9 @@ static int set_options(int argc, char **argv, const char *short_opts,
|
||||
break;
|
||||
|
||||
if (optarg)
|
||||
dprintf("option '%c' arg '%s'\n", option, optarg);
|
||||
dbg("option '%c' arg '%s'\n", option, optarg);
|
||||
else
|
||||
dprintf("option '%c'\n", option);
|
||||
dbg("option '%c'\n", option);
|
||||
|
||||
switch (option) {
|
||||
case 'a':
|
||||
@@ -492,10 +431,6 @@ static int set_options(int argc, char **argv, const char *short_opts,
|
||||
all_good = 0;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
default_callout = optarg;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
dev_specified = 1;
|
||||
strncpy(maj_min_dev, optarg, MAX_NAME_LEN);
|
||||
@@ -525,16 +460,15 @@ static int set_options(int argc, char **argv, const char *short_opts,
|
||||
} else if (strcmp(optarg, "pre-spc3-83") == 0) {
|
||||
default_page_code = PAGE_83_PRE_SPC3;
|
||||
} else {
|
||||
log_message(LOG_WARNING,
|
||||
"Unknown page code '%s'\n", optarg);
|
||||
info("Unknown page code '%s'", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
sys_specified = 1;
|
||||
strncpy(target, sysfs_mnt_path, MAX_NAME_LEN);
|
||||
strncat(target, optarg, MAX_NAME_LEN);
|
||||
strncpy(target, optarg, MAX_NAME_LEN);
|
||||
target[MAX_NAME_LEN-1] = '\0';
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
@@ -550,71 +484,58 @@ static int set_options(int argc, char **argv, const char *short_opts,
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
log_message(LOG_WARNING, "scsi_id version: %s\n",
|
||||
SCSI_ID_VERSION);
|
||||
info("scsi_id version: %s\n", SCSI_ID_VERSION);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_message(LOG_WARNING,
|
||||
"Unknown or bad option '%c' (0x%x)\n",
|
||||
option, option);
|
||||
info("Unknown or bad option '%c' (0x%x)", option, option);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int per_dev_options(struct sysfs_device *scsi_dev, int *good_bad,
|
||||
int *page_code, char *callout)
|
||||
static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code)
|
||||
{
|
||||
int retval;
|
||||
int newargc;
|
||||
char **newargv = NULL;
|
||||
struct sysfs_attribute *vendor, *model, *type;
|
||||
const char *vendor, *model, *type;
|
||||
int option;
|
||||
|
||||
*good_bad = all_good;
|
||||
*page_code = default_page_code;
|
||||
if (default_callout && (callout != default_callout))
|
||||
strncpy(callout, default_callout, MAX_NAME_LEN);
|
||||
else
|
||||
callout[0] = '\0';
|
||||
|
||||
vendor = sysfs_get_device_attr(scsi_dev, "vendor");
|
||||
vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
|
||||
if (!vendor) {
|
||||
log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get vendor attribute", dev_scsi->devpath);
|
||||
return -1;
|
||||
}
|
||||
set_str(vendor_str, vendor->value, sizeof(vendor_str)-1);
|
||||
set_str(vendor_str, vendor, sizeof(vendor_str)-1);
|
||||
|
||||
model = sysfs_get_device_attr(scsi_dev, "model");
|
||||
model = sysfs_attr_get_value(dev_scsi->devpath, "model");
|
||||
if (!model) {
|
||||
log_message(LOG_WARNING, "%s: cannot get model attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get model attribute\n", dev_scsi->devpath);
|
||||
return -1;
|
||||
}
|
||||
set_str(model_str, model->value, sizeof(model_str)-1);
|
||||
set_str(model_str, model, sizeof(model_str)-1);
|
||||
|
||||
type = sysfs_get_device_attr(scsi_dev, "type");
|
||||
type = sysfs_attr_get_value(dev_scsi->devpath, "type");
|
||||
if (!type) {
|
||||
log_message(LOG_WARNING, "%s: cannot get type attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get type attribute", dev_scsi->devpath);
|
||||
return -1;
|
||||
}
|
||||
set_type(type_str, type->value, sizeof(type_str));
|
||||
set_type(type_str, type, sizeof(type_str));
|
||||
|
||||
type = sysfs_get_device_attr(scsi_dev, "rev");
|
||||
type = sysfs_attr_get_value(dev_scsi->devpath, "rev");
|
||||
if (!type) {
|
||||
log_message(LOG_WARNING, "%s: cannot get type attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get type attribute\n", dev_scsi->devpath);
|
||||
return -1;
|
||||
}
|
||||
set_str(revision_str, type->value, sizeof(revision_str)-1);
|
||||
set_str(revision_str, type, sizeof(revision_str)-1);
|
||||
|
||||
retval = get_file_options(vendor->value, model->value, &newargc,
|
||||
&newargv);
|
||||
retval = get_file_options(vendor, model, &newargc, &newargv);
|
||||
|
||||
optind = 1; /* reset this global extern */
|
||||
while (retval == 0) {
|
||||
@@ -623,19 +544,15 @@ static int per_dev_options(struct sysfs_device *scsi_dev, int *good_bad,
|
||||
break;
|
||||
|
||||
if (optarg)
|
||||
dprintf("option '%c' arg '%s'\n", option, optarg);
|
||||
dbg("option '%c' arg '%s'\n", option, optarg);
|
||||
else
|
||||
dprintf("option '%c'\n", option);
|
||||
dbg("option '%c'\n", option);
|
||||
|
||||
switch (option) {
|
||||
case 'b':
|
||||
*good_bad = 0;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
strncpy(callout, default_callout, MAX_NAME_LEN);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
*good_bad = 1;
|
||||
break;
|
||||
@@ -648,16 +565,13 @@ static int per_dev_options(struct sysfs_device *scsi_dev, int *good_bad,
|
||||
} else if (strcmp(optarg, "pre-spc3-83") == 0) {
|
||||
*page_code = PAGE_83_PRE_SPC3;
|
||||
} else {
|
||||
log_message(LOG_WARNING,
|
||||
"Unknown page code '%s'\n", optarg);
|
||||
info("Unknown page code '%s'", optarg);
|
||||
retval = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_message(LOG_WARNING,
|
||||
"Unknown or bad option '%c' (0x%x)\n",
|
||||
option, option);
|
||||
info("Unknown or bad option '%c' (0x%x)", option, option);
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
@@ -702,129 +616,62 @@ static void format_serial(char *serial)
|
||||
* memory etc. return 2, and return 1 for expected cases (like broken
|
||||
* device found) that do not print an id.
|
||||
*/
|
||||
static int scsi_id(const char *target_path, char *maj_min_dev)
|
||||
static int scsi_id(const char *devpath, char *maj_min_dev)
|
||||
{
|
||||
int retval;
|
||||
int dev_type = 0;
|
||||
char *serial, *unaligned_buf;
|
||||
struct sysfs_class_device *class_dev; /* of target_path */
|
||||
struct sysfs_class_device *class_dev_parent; /* for partitions */
|
||||
struct sysfs_device *scsi_dev; /* the scsi_device */
|
||||
struct sysfs_device *dev;
|
||||
struct sysfs_device *dev_scsi;
|
||||
int good_dev;
|
||||
int page_code;
|
||||
char callout[MAX_NAME_LEN];
|
||||
|
||||
dprintf("target_path %s\n", target_path);
|
||||
dbg("devpath %s\n", devpath);
|
||||
|
||||
/*
|
||||
* Ugly: depend on the sysfs path to tell us whether this is a
|
||||
* block or char device. This should probably be encoded in the
|
||||
* "dev" along with the major/minor.
|
||||
*/
|
||||
if (has_sysfs_prefix(target_path, "/block")) {
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL) {
|
||||
err("unable to access '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(dev->subsystem, "block") == 0)
|
||||
dev_type = S_IFBLK;
|
||||
} else if (has_sysfs_prefix(target_path, "/class")) {
|
||||
else
|
||||
dev_type = S_IFCHR;
|
||||
} else {
|
||||
if (!hotplug_mode) {
|
||||
log_message(LOG_WARNING,
|
||||
"Non block or class device '%s'\n",
|
||||
target_path);
|
||||
return 1;
|
||||
} else {
|
||||
/*
|
||||
* Expected in some cases.
|
||||
*/
|
||||
dprintf("Non block or class device\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class_dev = sysfs_open_class_device_path(target_path);
|
||||
if (!class_dev) {
|
||||
log_message(LOG_WARNING, "open class %s failed: %s\n",
|
||||
target_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
dprintf("class_dev 0x%p; class_dev_parent 0x%p\n", class_dev,
|
||||
class_dev_parent);
|
||||
if (class_dev_parent) {
|
||||
scsi_dev = sysfs_get_classdev_device(class_dev_parent);
|
||||
} else {
|
||||
scsi_dev = sysfs_get_classdev_device(class_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The close of scsi_dev will close class_dev or class_dev_parent.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We assume we are called after the device is completely ready,
|
||||
* so we don't have to loop here like udev. (And we are usually
|
||||
* called via udev.)
|
||||
*/
|
||||
if (!scsi_dev) {
|
||||
/*
|
||||
* errno is not set if we can't find the device link, so
|
||||
* don't print it out here.
|
||||
*/
|
||||
log_message(LOG_WARNING, "Cannot find sysfs device associated with %s\n",
|
||||
target_path);
|
||||
/* get scsi parent device */
|
||||
dev_scsi = sysfs_device_get_parent(dev);
|
||||
if (dev_scsi == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allow only scsi devices.
|
||||
*
|
||||
* Other block devices can support SG IO, but only ide-cd does, so
|
||||
* for now, don't bother with anything else.
|
||||
*/
|
||||
if (strcmp(scsi_dev->bus, "scsi") != 0) {
|
||||
if (hotplug_mode)
|
||||
/*
|
||||
* Expected in some cases.
|
||||
*/
|
||||
dprintf("%s is not a scsi device\n", target_path);
|
||||
else
|
||||
log_message(LOG_WARNING, "%s is not a scsi device\n",
|
||||
target_path);
|
||||
/* allow only scsi devices */
|
||||
if (strcmp(dev_scsi->subsystem, "scsi") != 0) {
|
||||
info("%s is not a scsi device", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mknod a temp dev to communicate with the device.
|
||||
*/
|
||||
if (!dev_specified && create_tmp_dev(class_dev, maj_min_dev,
|
||||
dev_type)) {
|
||||
dprintf("create_tmp_dev failed\n");
|
||||
/* mknod a temp dev to communicate with the device */
|
||||
if (!dev_specified && create_tmp_dev(dev->devpath, maj_min_dev, dev_type)) {
|
||||
dbg("create_tmp_dev failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get any per device (vendor + model) options from the config
|
||||
* file.
|
||||
*/
|
||||
retval = per_dev_options(scsi_dev, &good_dev, &page_code, callout);
|
||||
dprintf("per dev options: good %d; page code 0x%x; callout '%s'\n",
|
||||
good_dev, page_code, callout);
|
||||
/* get per device (vendor + model) options from the config file */
|
||||
retval = per_dev_options(dev_scsi, &good_dev, &page_code);
|
||||
dbg("per dev options: good %d; page code 0x%x", good_dev, page_code);
|
||||
|
||||
#define ALIGN 512
|
||||
unaligned_buf = malloc(MAX_SERIAL_LEN + ALIGN);
|
||||
serial = (char*) (((unsigned long) unaligned_buf + (ALIGN - 1))
|
||||
& ~(ALIGN - 1));
|
||||
dprintf("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial);
|
||||
dbg("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial);
|
||||
#undef ALIGN
|
||||
|
||||
if (!good_dev) {
|
||||
retval = 1;
|
||||
} else if (callout[0] != '\0') {
|
||||
/*
|
||||
* XXX Disabled for now ('c' is not in any options[]).
|
||||
*/
|
||||
retval = 1;
|
||||
} else if (scsi_get_serial(scsi_dev, maj_min_dev, page_code,
|
||||
} else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code,
|
||||
serial, MAX_SERIAL_LEN)) {
|
||||
retval = always_info?0:1;
|
||||
} else {
|
||||
@@ -844,13 +691,12 @@ static int scsi_id(const char *target_path, char *maj_min_dev)
|
||||
if (reformat_serial)
|
||||
format_serial(serial);
|
||||
if (display_bus_id)
|
||||
printf("%s: ", scsi_dev->name);
|
||||
printf("%s: ", dev_scsi->kernel_name);
|
||||
printf("%s\n", serial);
|
||||
}
|
||||
dprintf("%s\n", serial);
|
||||
dbg("%s\n", serial);
|
||||
retval = 0;
|
||||
}
|
||||
sysfs_close_device(scsi_dev);
|
||||
|
||||
if (!dev_specified)
|
||||
unlink(maj_min_dev);
|
||||
@@ -860,32 +706,31 @@ static int scsi_id(const char *target_path, char *maj_min_dev)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int retval;
|
||||
char *devpath;
|
||||
char target_path[MAX_NAME_LEN];
|
||||
int retval = 0;
|
||||
char devpath[MAX_NAME_LEN];
|
||||
char maj_min_dev[MAX_NAME_LEN];
|
||||
int newargc;
|
||||
const char *env;
|
||||
char **newargv;
|
||||
|
||||
if (getenv("DEBUG"))
|
||||
debug++;
|
||||
logging_init("scsi_id");
|
||||
sysfs_init();
|
||||
dbg("argc is %d\n", argc);
|
||||
|
||||
dprintf("argc is %d\n", argc);
|
||||
if (sysfs_get_mnt_path(sysfs_mnt_path, MAX_NAME_LEN)) {
|
||||
log_message(LOG_WARNING, "sysfs_get_mnt_path failed: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
/* sysfs path can be overridden for testing */
|
||||
env = getenv("SYSFS_PATH");
|
||||
if (env) {
|
||||
strncpy(sysfs_path, env, sizeof(sysfs_path));
|
||||
sysfs_path[sizeof(sysfs_path)-1] = '\0';
|
||||
} else
|
||||
strcpy(sysfs_path, "/sys");
|
||||
|
||||
devpath = getenv("DEVPATH");
|
||||
if (devpath) {
|
||||
/*
|
||||
* This implies that we were invoked via udev or hotplug.
|
||||
*/
|
||||
env = getenv("DEVPATH");
|
||||
if (env) {
|
||||
hotplug_mode = 1;
|
||||
sys_specified = 1;
|
||||
strncpy(target_path, sysfs_mnt_path, MAX_NAME_LEN);
|
||||
strncat(target_path, devpath, MAX_NAME_LEN);
|
||||
strncpy(devpath, env, MAX_NAME_LEN);
|
||||
devpath[sizeof(devpath)-1] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -894,26 +739,35 @@ int main(int argc, char **argv)
|
||||
newargv = NULL;
|
||||
retval = get_file_options(NULL, NULL, &newargc, &newargv);
|
||||
if (retval < 0) {
|
||||
exit(1);
|
||||
} else if (newargv && (retval == 0)) {
|
||||
if (set_options(newargc, newargv, short_options, target_path,
|
||||
maj_min_dev) < 0)
|
||||
exit(1);
|
||||
retval = 1;
|
||||
goto exit;
|
||||
}
|
||||
if (newargv && (retval == 0)) {
|
||||
if (set_options(newargc, newargv, short_options, devpath,
|
||||
maj_min_dev) < 0) {
|
||||
retval = 2;
|
||||
goto exit;
|
||||
}
|
||||
free(newargv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get command line options (overriding any config file or DEVPATH
|
||||
* settings).
|
||||
*/
|
||||
if (set_options(argc, argv, short_options, target_path,
|
||||
maj_min_dev) < 0)
|
||||
if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0)
|
||||
exit(1);
|
||||
|
||||
if (!sys_specified) {
|
||||
log_message(LOG_WARNING, "-s must be specified\n");
|
||||
exit(1);
|
||||
info("-s must be specified\n");
|
||||
retval = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = scsi_id(target_path, maj_min_dev);
|
||||
exit(retval);
|
||||
retval = scsi_id(devpath, maj_min_dev);
|
||||
|
||||
exit:
|
||||
sysfs_cleanup();
|
||||
logging_close();
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
#define dprintf(format, arg...) \
|
||||
log_message(LOG_DEBUG, "%s: " format, __FUNCTION__ , ## arg)
|
||||
|
||||
#define MAX_NAME_LEN 72
|
||||
|
||||
/*
|
||||
@@ -44,15 +41,8 @@
|
||||
*/
|
||||
#define MAX_BUFFER_LEN 256
|
||||
|
||||
extern int scsi_get_serial (struct sysfs_device *scsi_dev, const char
|
||||
*devname, int page_code, char *serial, int
|
||||
len);
|
||||
extern void log_message (int level, const char *format, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
#ifndef u8
|
||||
typedef unsigned char u8;
|
||||
#endif
|
||||
extern int scsi_get_serial (struct sysfs_device *dev_scsi, const char *devname,
|
||||
int page_code, char *serial, int len);
|
||||
|
||||
/*
|
||||
* Page code values.
|
||||
|
||||
+111
-147
@@ -30,11 +30,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <linux/compiler.h> /* need __user when built via klibc */
|
||||
#include <scsi/sg.h>
|
||||
#include <sysfs/libsysfs.h>
|
||||
#include "scsi_id.h"
|
||||
|
||||
#include "../../udev.h"
|
||||
#include "scsi.h"
|
||||
#include "scsi_id.h"
|
||||
#include "scsi_id_version.h"
|
||||
|
||||
/*
|
||||
* A priority based list of id, naa, and binary/ascii for the identifier
|
||||
@@ -75,23 +76,21 @@ static const char hex_str[]="0123456789abcdef";
|
||||
* are used here.
|
||||
*/
|
||||
|
||||
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
|
||||
|
||||
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
|
||||
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
|
||||
|
||||
#define DRIVER_TIMEOUT 0x06
|
||||
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
|
||||
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
|
||||
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
|
||||
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
|
||||
#define DRIVER_TIMEOUT 0x06
|
||||
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
|
||||
|
||||
/* The following "category" function returns one of the following */
|
||||
#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
|
||||
#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
|
||||
#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
|
||||
#define SG_ERR_CAT_TIMEOUT 3
|
||||
#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
|
||||
#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
|
||||
#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
|
||||
#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
|
||||
#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
|
||||
#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
|
||||
#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
|
||||
#define SG_ERR_CAT_TIMEOUT 3
|
||||
#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
|
||||
#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
|
||||
#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
|
||||
#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
|
||||
|
||||
static int sg_err_category_new(int scsi_status, int msg_status, int
|
||||
host_status, int driver_status, const
|
||||
@@ -157,7 +156,7 @@ static int sg_err_category3(struct sg_io_hdr *hp)
|
||||
hp->sbp, hp->sb_len_wr);
|
||||
}
|
||||
|
||||
static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
static int scsi_dump_sense(struct sysfs_device *dev_scsi, struct sg_io_hdr *io)
|
||||
{
|
||||
unsigned char *sense_buffer;
|
||||
int s;
|
||||
@@ -184,12 +183,11 @@ static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
* we'll retry the command.
|
||||
*/
|
||||
|
||||
dprintf("got check condition\n");
|
||||
dbg("got check condition\n");
|
||||
|
||||
sb_len = io->sb_len_wr;
|
||||
if (sb_len < 1) {
|
||||
log_message(LOG_WARNING, "%s: sense buffer empty\n",
|
||||
scsi_dev->name);
|
||||
info("%s: sense buffer empty", dev_scsi->kernel_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -203,10 +201,8 @@ static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
*/
|
||||
s = sense_buffer[7] + 8;
|
||||
if (sb_len < s) {
|
||||
log_message(LOG_WARNING,
|
||||
"%s: sense buffer too small %d bytes,"
|
||||
" %d bytes too short\n", scsi_dev->name,
|
||||
sb_len, s - sb_len);
|
||||
info("%s: sense buffer too small %d bytes, %d bytes too short",
|
||||
dev_scsi->kernel_name, sb_len, s - sb_len);
|
||||
return -1;
|
||||
}
|
||||
if ((code == 0x0) || (code == 0x1)) {
|
||||
@@ -216,9 +212,8 @@ static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
/*
|
||||
* Possible?
|
||||
*/
|
||||
log_message(LOG_WARNING, "%s: sense result too"
|
||||
" small %d bytes\n",
|
||||
scsi_dev->name, s);
|
||||
info("%s: sense result too" " small %d bytes",
|
||||
dev_scsi->kernel_name, s);
|
||||
return -1;
|
||||
}
|
||||
asc = sense_buffer[12];
|
||||
@@ -229,72 +224,64 @@ static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
asc = sense_buffer[2];
|
||||
ascq = sense_buffer[3];
|
||||
} else {
|
||||
log_message(LOG_WARNING,
|
||||
"%s: invalid sense code 0x%x\n",
|
||||
scsi_dev->name, code);
|
||||
info("%s: invalid sense code 0x%x",
|
||||
dev_scsi->kernel_name, code);
|
||||
return -1;
|
||||
}
|
||||
log_message(LOG_WARNING,
|
||||
"%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
|
||||
scsi_dev->name, sense_key, asc, ascq);
|
||||
info("%s: sense key 0x%x ASC 0x%x ASCQ 0x%x",
|
||||
dev_scsi->kernel_name, sense_key, asc, ascq);
|
||||
} else {
|
||||
if (sb_len < 4) {
|
||||
log_message(LOG_WARNING,
|
||||
"%s: sense buffer too small %d bytes, %d bytes too short\n",
|
||||
scsi_dev->name, sb_len, 4 - sb_len);
|
||||
info("%s: sense buffer too small %d bytes, %d bytes too short",
|
||||
dev_scsi->kernel_name, sb_len, 4 - sb_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sense_buffer[0] < 15)
|
||||
log_message(LOG_WARNING, "%s: old sense key: 0x%x\n",
|
||||
scsi_dev->name, sense_buffer[0] & 0x0f);
|
||||
info("%s: old sense key: 0x%x", dev_scsi->kernel_name, sense_buffer[0] & 0x0f);
|
||||
else
|
||||
log_message(LOG_WARNING, "%s: sense = %2x %2x\n",
|
||||
scsi_dev->name, sense_buffer[0],
|
||||
sense_buffer[2]);
|
||||
log_message(LOG_WARNING,
|
||||
"%s: non-extended sense class %d code 0x%0x\n",
|
||||
scsi_dev->name, sense_class, code);
|
||||
info("%s: sense = %2x %2x",
|
||||
dev_scsi->kernel_name, sense_buffer[0], sense_buffer[2]);
|
||||
info("%s: non-extended sense class %d code 0x%0x",
|
||||
dev_scsi->kernel_name, sense_class, code);
|
||||
|
||||
}
|
||||
|
||||
#ifdef DUMP_SENSE
|
||||
for (i = 0, j = 0; (i < s) && (j < 254); i++) {
|
||||
dprintf("i %d, j %d\n", i, j);
|
||||
dbg("i %d, j %d\n", i, j);
|
||||
out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
|
||||
out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
|
||||
out_buffer[j++] = ' ';
|
||||
}
|
||||
out_buffer[j] = '\0';
|
||||
log_message(LOG_WARNING, "%s: sense dump:\n", scsi_dev->name);
|
||||
log_message(LOG_WARNING, "%s: %s\n", scsi_dev->name, out_buffer);
|
||||
info("%s: sense dump:", dev_scsi->kernel_name);
|
||||
info("%s: %s", dev_scsi->kernel_name, out_buffer);
|
||||
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
|
||||
static int scsi_dump(struct sysfs_device *dev_scsi, struct sg_io_hdr *io)
|
||||
{
|
||||
if (!io->status && !io->host_status && !io->msg_status &&
|
||||
!io->driver_status) {
|
||||
/*
|
||||
* Impossible, should not be called.
|
||||
*/
|
||||
log_message(LOG_WARNING, "%s: called with no error\n",
|
||||
__FUNCTION__);
|
||||
info("%s: called with no error", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_message(LOG_WARNING, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
|
||||
scsi_dev->name, io->driver_status, io->host_status,
|
||||
io->msg_status, io->status);
|
||||
info("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x",
|
||||
dev_scsi->kernel_name, io->driver_status, io->host_status, io->msg_status, io->status);
|
||||
if (io->status == SCSI_CHECK_CONDITION)
|
||||
return scsi_dump_sense(scsi_dev, io);
|
||||
return scsi_dump_sense(dev_scsi, io);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
static int scsi_inquiry(struct sysfs_device *dev_scsi, int fd,
|
||||
unsigned char evpd, unsigned char page,
|
||||
unsigned char *buf, unsigned int buflen)
|
||||
{
|
||||
@@ -306,12 +293,12 @@ static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
int retry = 3; /* rather random */
|
||||
|
||||
if (buflen > SCSI_INQ_BUFF_LEN) {
|
||||
log_message(LOG_WARNING, "buflen %d too long\n", buflen);
|
||||
info("buflen %d too long", buflen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
resend:
|
||||
dprintf("%s evpd %d, page 0x%x\n", scsi_dev->name, evpd, page);
|
||||
dbg("%s evpd %d, page 0x%x\n", dev_scsi->kernel_name, evpd, page);
|
||||
|
||||
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
|
||||
io_hdr.interface_id = 'S';
|
||||
@@ -325,8 +312,7 @@ resend:
|
||||
io_hdr.timeout = DEF_TIMEOUT;
|
||||
|
||||
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
|
||||
log_message(LOG_WARNING, "%s: ioctl failed: %s\n",
|
||||
scsi_dev->name, strerror(errno));
|
||||
info("%s: ioctl failed: %s", dev_scsi->kernel_name, strerror(errno));
|
||||
retval = -1;
|
||||
goto error;
|
||||
}
|
||||
@@ -343,14 +329,14 @@ resend:
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = scsi_dump(scsi_dev, &io_hdr);
|
||||
retval = scsi_dump(dev_scsi, &io_hdr);
|
||||
}
|
||||
|
||||
if (!retval) {
|
||||
retval = buflen;
|
||||
} else if (retval > 0) {
|
||||
if (--retry > 0) {
|
||||
dprintf("%s: Retrying ...\n", scsi_dev->name);
|
||||
dbg("%s: Retrying ...\n", dev_scsi->kernel_name);
|
||||
goto resend;
|
||||
}
|
||||
retval = -1;
|
||||
@@ -358,33 +344,30 @@ resend:
|
||||
|
||||
error:
|
||||
if (retval < 0)
|
||||
log_message(LOG_WARNING,
|
||||
"%s: Unable to get INQUIRY vpd %d page 0x%x.\n",
|
||||
scsi_dev->name, evpd, page);
|
||||
info("%s: Unable to get INQUIRY vpd %d page 0x%x.",
|
||||
dev_scsi->kernel_name, evpd, page);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Get list of supported EVPD pages */
|
||||
static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
static int do_scsi_page0_inquiry(struct sysfs_device *dev_scsi, int fd,
|
||||
unsigned char *buffer, unsigned int len)
|
||||
{
|
||||
int retval;
|
||||
struct sysfs_attribute *vendor;
|
||||
const char *vendor;
|
||||
|
||||
memset(buffer, 0, len);
|
||||
retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
|
||||
retval = scsi_inquiry(dev_scsi, fd, 1, 0x0, buffer, len);
|
||||
if (retval < 0)
|
||||
return 1;
|
||||
|
||||
if (buffer[1] != 0) {
|
||||
log_message(LOG_WARNING, "%s: page 0 not available.\n",
|
||||
scsi_dev->name);
|
||||
info("%s: page 0 not available.", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
if (buffer[3] > len) {
|
||||
log_message(LOG_WARNING, "%s: page 0 buffer too long %d\n",
|
||||
scsi_dev->name, buffer[3]);
|
||||
info("%s: page 0 buffer too long %d", dev_scsi->kernel_name, buffer[3]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -400,17 +383,13 @@ static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
* If the vendor id appears in the page assume the page is
|
||||
* invalid.
|
||||
*/
|
||||
vendor = sysfs_get_device_attr(scsi_dev, "vendor");
|
||||
vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
|
||||
if (!vendor) {
|
||||
log_message(LOG_WARNING,
|
||||
"%s: cannot get model attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get model attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
if (!strncmp((char *)&buffer[VENDOR_LENGTH], vendor->value,
|
||||
VENDOR_LENGTH)) {
|
||||
log_message(LOG_WARNING, "%s: invalid page0 data\n",
|
||||
scsi_dev->name);
|
||||
if (!strncmp((char *)&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
|
||||
info("%s: invalid page0 data", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -421,46 +400,35 @@ static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
* The caller checks that serial is long enough to include the vendor +
|
||||
* model.
|
||||
*/
|
||||
static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
|
||||
static int prepend_vendor_model(struct sysfs_device *dev_scsi, char *serial)
|
||||
{
|
||||
struct sysfs_attribute *attr;
|
||||
const char *attr;
|
||||
int ind;
|
||||
|
||||
attr = sysfs_get_device_attr(scsi_dev, "vendor");
|
||||
attr = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
|
||||
if (!attr) {
|
||||
log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get vendor attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
strncpy(serial, attr->value, VENDOR_LENGTH);
|
||||
strncpy(serial, attr, VENDOR_LENGTH);
|
||||
ind = strlen(serial) - 1;
|
||||
/*
|
||||
* Remove sysfs added newlines.
|
||||
*/
|
||||
if (serial[ind] == '\n')
|
||||
serial[ind] = '\0';
|
||||
|
||||
attr = sysfs_get_device_attr(scsi_dev, "model");
|
||||
attr = sysfs_attr_get_value(dev_scsi->devpath, "model");
|
||||
if (!attr) {
|
||||
log_message(LOG_WARNING, "%s: cannot get model attribute\n",
|
||||
scsi_dev->name);
|
||||
info("%s: cannot get model attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
strncat(serial, attr->value, MODEL_LENGTH);
|
||||
strncat(serial, attr, MODEL_LENGTH);
|
||||
ind = strlen(serial) - 1;
|
||||
if (serial[ind] == '\n')
|
||||
serial[ind] = '\0';
|
||||
else
|
||||
ind++;
|
||||
ind++;
|
||||
|
||||
/*
|
||||
* This is not a complete check, since we are using strncat/cpy
|
||||
* above, ind will never be too large.
|
||||
*/
|
||||
if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
|
||||
log_message(LOG_WARNING, "%s: expected length %d, got length %d\n",
|
||||
scsi_dev->name, (VENDOR_LENGTH + MODEL_LENGTH),
|
||||
ind);
|
||||
info("%s: expected length %d, got length %d",
|
||||
dev_scsi->kernel_name, (VENDOR_LENGTH + MODEL_LENGTH), ind);
|
||||
return 1;
|
||||
}
|
||||
return ind;
|
||||
@@ -470,7 +438,7 @@ static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
|
||||
* check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
|
||||
* serial number.
|
||||
**/
|
||||
static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
|
||||
static int check_fill_0x83_id(struct sysfs_device *dev_scsi,
|
||||
unsigned char *page_83,
|
||||
const struct scsi_id_search_values
|
||||
*id_search, char *serial, int max_len)
|
||||
@@ -517,8 +485,8 @@ static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
|
||||
len += VENDOR_LENGTH + MODEL_LENGTH;
|
||||
|
||||
if (max_len < len) {
|
||||
log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
|
||||
scsi_dev->name, max_len, len);
|
||||
info("%s: length %d too short - need %d",
|
||||
dev_scsi->kernel_name, max_len, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -531,8 +499,8 @@ static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
|
||||
* included in the identifier.
|
||||
*/
|
||||
if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
|
||||
if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
|
||||
dprintf("prepend failed\n");
|
||||
if (prepend_vendor_model(dev_scsi, &serial[1]) < 0) {
|
||||
dbg("prepend failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -559,7 +527,7 @@ static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
|
||||
}
|
||||
|
||||
/* Extract the raw binary from VPD 0x83 pre-SPC devices */
|
||||
static int check_fill_0x83_prespc3(struct sysfs_device *scsi_dev,
|
||||
static int check_fill_0x83_prespc3(struct sysfs_device *dev_scsi,
|
||||
unsigned char *page_83,
|
||||
const struct scsi_id_search_values
|
||||
*id_search, char *serial, int max_len)
|
||||
@@ -574,13 +542,13 @@ static int check_fill_0x83_prespc3(struct sysfs_device *scsi_dev,
|
||||
serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4];
|
||||
serial[j++] = hex_str[ page_83[4+i] & 0x0f];
|
||||
}
|
||||
dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
|
||||
dbg("using pre-spc3-83 for %s.\n", dev_scsi->kernel_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Get device identification VPD page */
|
||||
static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
static int do_scsi_page83_inquiry(struct sysfs_device *dev_scsi, int fd,
|
||||
char *serial, int len)
|
||||
{
|
||||
int retval;
|
||||
@@ -588,14 +556,13 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
unsigned char page_83[SCSI_INQ_BUFF_LEN];
|
||||
|
||||
memset(page_83, 0, SCSI_INQ_BUFF_LEN);
|
||||
retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83,
|
||||
retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_83, page_83,
|
||||
SCSI_INQ_BUFF_LEN);
|
||||
if (retval < 0)
|
||||
return 1;
|
||||
|
||||
if (page_83[1] != PAGE_83) {
|
||||
log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
|
||||
scsi_dev->name);
|
||||
info("%s: Invalid page 0x83", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -629,7 +596,7 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
*/
|
||||
|
||||
if (page_83[6] != 0)
|
||||
return check_fill_0x83_prespc3(scsi_dev, page_83,
|
||||
return check_fill_0x83_prespc3(dev_scsi, page_83,
|
||||
id_search_list, serial, len);
|
||||
|
||||
/*
|
||||
@@ -643,21 +610,21 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
* one or a small number of descriptors.
|
||||
*/
|
||||
for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) {
|
||||
retval = check_fill_0x83_id(scsi_dev, &page_83[j],
|
||||
retval = check_fill_0x83_id(dev_scsi, &page_83[j],
|
||||
&id_search_list[id_ind],
|
||||
serial, len);
|
||||
dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
|
||||
dbg("%s id desc %d/%d/%d\n", dev_scsi->kernel_name,
|
||||
id_search_list[id_ind].id_type,
|
||||
id_search_list[id_ind].naa_type,
|
||||
id_search_list[id_ind].code_set);
|
||||
if (!retval) {
|
||||
dprintf(" used\n");
|
||||
dbg(" used\n");
|
||||
return retval;
|
||||
} else if (retval < 0) {
|
||||
dprintf(" failed\n");
|
||||
dbg(" failed\n");
|
||||
return retval;
|
||||
} else {
|
||||
dprintf(" not used\n");
|
||||
dbg(" not used\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,7 +638,7 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
* Return the hard coded error code value 2 if the page 83 reply is not
|
||||
* conformant to the SCSI-2 format.
|
||||
*/
|
||||
static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *dev_scsi, int fd,
|
||||
char *serial, int len)
|
||||
{
|
||||
int retval;
|
||||
@@ -679,12 +646,12 @@ static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
unsigned char page_83[SCSI_INQ_BUFF_LEN];
|
||||
|
||||
memset(page_83, 0, SCSI_INQ_BUFF_LEN);
|
||||
retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
|
||||
retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
|
||||
if (retval < 0)
|
||||
return 1;
|
||||
|
||||
if (page_83[1] != PAGE_83) {
|
||||
log_message(LOG_WARNING, "%s: Invalid page 0x83\n", scsi_dev->name);
|
||||
info("%s: Invalid page 0x83", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
@@ -728,12 +695,12 @@ static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
serial[j++] = hex_str[page_83[i] & 0x0f];
|
||||
i++;
|
||||
}
|
||||
dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
|
||||
dbg("using pre-spc3-83 for %s.\n", dev_scsi->kernel_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get unit serial number VPD page */
|
||||
static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
static int do_scsi_page80_inquiry(struct sysfs_device *dev_scsi, int fd,
|
||||
char *serial, int max_len)
|
||||
{
|
||||
int retval;
|
||||
@@ -743,20 +710,19 @@ static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
unsigned char buf[SCSI_INQ_BUFF_LEN];
|
||||
|
||||
memset(buf, 0, SCSI_INQ_BUFF_LEN);
|
||||
retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
|
||||
retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (buf[1] != PAGE_80) {
|
||||
log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
|
||||
scsi_dev->name);
|
||||
info("%s: Invalid page 0x80", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
|
||||
if (max_len < len) {
|
||||
log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
|
||||
scsi_dev->name, max_len, len);
|
||||
info("%s: length %d too short - need %d",
|
||||
dev_scsi->kernel_name, max_len, len);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
@@ -764,7 +730,7 @@ static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
* specific type where we prepend '0' + vendor + model.
|
||||
*/
|
||||
serial[0] = 'S';
|
||||
ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
|
||||
ser_ind = prepend_vendor_model(dev_scsi, &serial[1]);
|
||||
if (ser_ind < 0)
|
||||
return 1;
|
||||
len = buf[3];
|
||||
@@ -773,7 +739,7 @@ static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
int scsi_get_serial (struct sysfs_device *dev_scsi, const char *devname,
|
||||
int page_code, char *serial, int len)
|
||||
{
|
||||
unsigned char page0[SCSI_INQ_BUFF_LEN];
|
||||
@@ -782,16 +748,16 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
int retval;
|
||||
|
||||
memset(serial, 0, len);
|
||||
dprintf("opening %s\n", devname);
|
||||
dbg("opening %s\n", devname);
|
||||
fd = open(devname, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
|
||||
scsi_dev->name, devname, strerror(errno));
|
||||
info("%s: cannot open %s: %s",
|
||||
dev_scsi->kernel_name, devname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (page_code == PAGE_80) {
|
||||
if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
|
||||
if (do_scsi_page80_inquiry(dev_scsi, fd, serial, len)) {
|
||||
retval = 1;
|
||||
goto completed;
|
||||
} else {
|
||||
@@ -799,7 +765,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
goto completed;
|
||||
}
|
||||
} else if (page_code == PAGE_83) {
|
||||
if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
|
||||
if (do_scsi_page83_inquiry(dev_scsi, fd, serial, len)) {
|
||||
retval = 1;
|
||||
goto completed;
|
||||
} else {
|
||||
@@ -807,7 +773,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
goto completed;
|
||||
}
|
||||
} else if (page_code == PAGE_83_PRE_SPC3) {
|
||||
retval = do_scsi_page83_prespc3_inquiry(scsi_dev, fd, serial, len);
|
||||
retval = do_scsi_page83_prespc3_inquiry(dev_scsi, fd, serial, len);
|
||||
if (retval) {
|
||||
/*
|
||||
* Fallback to servicing a SPC-2/3 compliant page 83
|
||||
@@ -815,7 +781,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
* conform to pre-SPC3 expectations.
|
||||
*/
|
||||
if (retval == 2) {
|
||||
if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
|
||||
if (do_scsi_page83_inquiry(dev_scsi, fd, serial, len)) {
|
||||
retval = 1;
|
||||
goto completed;
|
||||
} else {
|
||||
@@ -832,8 +798,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
goto completed;
|
||||
}
|
||||
} else if (page_code != 0x00) {
|
||||
log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
|
||||
scsi_dev->name, page_code);
|
||||
info("%s: unsupported page code 0x%d", dev_scsi->kernel_name, page_code);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -841,7 +806,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
* Get page 0, the page of the pages. By default, try from best to
|
||||
* worst of supported pages: 0x83 then 0x80.
|
||||
*/
|
||||
if (do_scsi_page0_inquiry(scsi_dev, fd, page0, SCSI_INQ_BUFF_LEN)) {
|
||||
if (do_scsi_page0_inquiry(dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) {
|
||||
/*
|
||||
* Don't try anything else. Black list if a specific page
|
||||
* should be used for this vendor+model, or maybe have an
|
||||
@@ -851,11 +816,11 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
goto completed;
|
||||
}
|
||||
|
||||
dprintf("%s: Checking page0\n", scsi_dev->name);
|
||||
dbg("%s: Checking page0\n", dev_scsi->kernel_name);
|
||||
|
||||
for (ind = 4; ind <= page0[3] + 3; ind++)
|
||||
if (page0[ind] == PAGE_83)
|
||||
if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
|
||||
if (!do_scsi_page83_inquiry(dev_scsi, fd, serial,
|
||||
len)) {
|
||||
/*
|
||||
* Success
|
||||
@@ -866,7 +831,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
|
||||
for (ind = 4; ind <= page0[3] + 3; ind++)
|
||||
if (page0[ind] == PAGE_80)
|
||||
if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
|
||||
if (!do_scsi_page80_inquiry(dev_scsi, fd, serial,
|
||||
len)) {
|
||||
/*
|
||||
* Success
|
||||
@@ -877,7 +842,6 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
|
||||
retval = 1;
|
||||
completed:
|
||||
if (close(fd) < 0)
|
||||
log_message(LOG_WARNING, "%s: close failed: %s\n",
|
||||
scsi_dev->name, strerror(errno));
|
||||
info("%s: close failed: %s", dev_scsi->kernel_name, strerror(errno));
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ all: $(PROG) $(MAN_PAGES)
|
||||
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
+111
-117
@@ -21,12 +21,10 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <../../libsysfs/sysfs/libsysfs.h>
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
|
||||
#define MAX_NAME_LEN 72
|
||||
#define MAX_SERIAL_LEN 256
|
||||
#define MAX_NAME_LEN 72
|
||||
#define MAX_SERIAL_LEN 256
|
||||
#define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
||||
|
||||
#ifdef USE_LOG
|
||||
@@ -54,7 +52,6 @@ void log_message(int priority, const char *format, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
char sysfs_mnt_path[SYSFS_PATH_MAX];
|
||||
static char vendor_str[64];
|
||||
static char model_str[64];
|
||||
static char serial_str[MAX_SERIAL_LEN];
|
||||
@@ -245,182 +242,176 @@ static void set_scsi_type(char *to, const char *from, int count)
|
||||
* 6.) If the device supplies a serial number, this number
|
||||
* is concatenated with the identification with an underscore '_'.
|
||||
*/
|
||||
static int usb_id(const char *target_path)
|
||||
static int usb_id(const char *devpath)
|
||||
{
|
||||
struct sysfs_class_device *class_dev; /* of target_path */
|
||||
struct sysfs_class_device *class_dev_parent; /* for partitions */
|
||||
struct sysfs_device *scsi_dev; /* the scsi_device */
|
||||
struct sysfs_device *target_dev;
|
||||
struct sysfs_device *host_dev, *interface_dev, *usb_dev;
|
||||
struct sysfs_attribute *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
|
||||
struct sysfs_attribute *usb_model = NULL, *usb_vendor = NULL, *usb_rev, *usb_serial;
|
||||
struct sysfs_attribute *if_class, *if_subclass;
|
||||
struct sysfs_device *dev;
|
||||
struct sysfs_device *dev_scsi;
|
||||
struct sysfs_device *dev_target;
|
||||
struct sysfs_device *dev_host, *dev_interface, *dev_usb;
|
||||
const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
|
||||
const char *usb_model = NULL, *usb_vendor = NULL, *usb_rev, *usb_serial;
|
||||
const char *if_class, *if_subclass;
|
||||
int if_class_num;
|
||||
int protocol = 0;
|
||||
|
||||
class_dev = sysfs_open_class_device_path(target_path);
|
||||
if (!class_dev) {
|
||||
info("open class %s failed: %s", target_path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
if (class_dev_parent) {
|
||||
scsi_dev = sysfs_get_classdev_device(class_dev_parent);
|
||||
} else {
|
||||
scsi_dev = sysfs_get_classdev_device(class_dev);
|
||||
}
|
||||
dbg("devpath %s\n", devpath);
|
||||
|
||||
/*
|
||||
* The close of scsi_dev will close class_dev or class_dev_parent.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We assume we are called after the device is completely ready,
|
||||
* so we don't have to loop here like udev. (And we are usually
|
||||
* called via udev.)
|
||||
*/
|
||||
if (!scsi_dev) {
|
||||
/*
|
||||
* errno is not set if we can't find the device link, so
|
||||
* don't print it out here.
|
||||
*/
|
||||
info("Cannot find sysfs device associated with %s", target_path);
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL) {
|
||||
err("unable to access '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow only scsi devices.
|
||||
*
|
||||
* Other block devices can support SG IO, but only ide-cd does, so
|
||||
* for now, don't bother with anything else.
|
||||
*/
|
||||
if (strcmp(scsi_dev->bus, "scsi") != 0) {
|
||||
info("%s is not a scsi device", target_path);
|
||||
/* get scsi parent device */
|
||||
dev_scsi = sysfs_device_get_parent(dev);
|
||||
if (dev_scsi == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
/* allow only scsi devices */
|
||||
if (strcmp(dev_scsi->subsystem, "scsi") != 0) {
|
||||
info("%s is not a scsi device", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* target directory */
|
||||
target_dev = sysfs_get_device_parent(scsi_dev);
|
||||
dev_target = sysfs_device_get_parent(dev_scsi);
|
||||
if (dev_target == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* host directory */
|
||||
host_dev = sysfs_get_device_parent(target_dev);
|
||||
dev_host = sysfs_device_get_parent(dev_target);
|
||||
if (dev_host == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* usb interface directory */
|
||||
interface_dev = sysfs_get_device_parent(host_dev);
|
||||
dev_interface = sysfs_device_get_parent(dev_host);
|
||||
if (dev_interface == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* usb device directory */
|
||||
usb_dev = sysfs_get_device_parent(interface_dev);
|
||||
|
||||
if (strcmp(interface_dev->bus, "usb") != 0) {
|
||||
info("%s is not an usb device", target_path);
|
||||
dev_usb = sysfs_device_get_parent(dev_interface);
|
||||
if (dev_usb == NULL) {
|
||||
err("unable to access parent device of '%s'", devpath);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(dev_interface->subsystem, "usb") != 0) {
|
||||
info("%s is not an usb device", devpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if_class = sysfs_get_device_attr(interface_dev, "bInterfaceClass");
|
||||
if_class = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceClass");
|
||||
if (!if_class) {
|
||||
info("%s: cannot get bInterfaceClass attribute", interface_dev->name);
|
||||
info("%s: cannot get bInterfaceClass attribute", dev_interface->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
if_class_num = strtoul(if_class->value, NULL, 16);
|
||||
if_class_num = strtoul(if_class, NULL, 16);
|
||||
if (if_class_num != 8) {
|
||||
set_usb_iftype(type_str, if_class->value, sizeof(type_str) - 1);
|
||||
set_usb_iftype(type_str, if_class, sizeof(type_str)-1);
|
||||
protocol = 0;
|
||||
} else {
|
||||
if_subclass = sysfs_get_device_attr(interface_dev,
|
||||
"bInterfaceSubClass");
|
||||
protocol = set_usb_ifsubtype(type_str, if_subclass->value,
|
||||
sizeof(type_str) -1 );
|
||||
if_subclass = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceSubClass");
|
||||
protocol = set_usb_ifsubtype(type_str, if_subclass, sizeof(type_str)-1);
|
||||
}
|
||||
|
||||
if (!use_usb_info && protocol == 6) {
|
||||
/* Generic SPC-2 device */
|
||||
scsi_vendor = sysfs_get_device_attr(scsi_dev, "vendor");
|
||||
scsi_vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
|
||||
if (!scsi_vendor) {
|
||||
info("%s: cannot get SCSI vendor attribute", scsi_dev->name);
|
||||
info("%s: cannot get SCSI vendor attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
set_str(vendor_str, scsi_vendor->value, sizeof(vendor_str)-1);
|
||||
set_str(vendor_str, scsi_vendor, sizeof(vendor_str)-1);
|
||||
|
||||
scsi_model = sysfs_get_device_attr(scsi_dev, "model");
|
||||
scsi_model = sysfs_attr_get_value(dev_scsi->devpath, "model");
|
||||
if (!scsi_model) {
|
||||
info("%s: cannot get SCSI model attribute", scsi_dev->name);
|
||||
info("%s: cannot get SCSI model attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
set_str(model_str, scsi_model->value, sizeof(model_str)-1);
|
||||
set_str(model_str, scsi_model, sizeof(model_str)-1);
|
||||
|
||||
scsi_type = sysfs_get_device_attr(scsi_dev, "type");
|
||||
scsi_type = sysfs_attr_get_value(dev_scsi->devpath, "type");
|
||||
if (!scsi_type) {
|
||||
info("%s: cannot get SCSI type attribute", scsi_dev->name);
|
||||
info("%s: cannot get SCSI type attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
set_scsi_type(type_str, scsi_type->value, sizeof(type_str)-1);
|
||||
set_scsi_type(type_str, scsi_type, sizeof(type_str)-1);
|
||||
|
||||
scsi_rev = sysfs_get_device_attr(scsi_dev, "rev");
|
||||
scsi_rev = sysfs_attr_get_value(dev_scsi->devpath, "rev");
|
||||
if (!scsi_rev) {
|
||||
info("%s: cannot get SCSI revision attribute", scsi_dev->name);
|
||||
info("%s: cannot get SCSI revision attribute", dev_scsi->kernel_name);
|
||||
return 1;
|
||||
}
|
||||
set_str(revision_str, scsi_rev->value, sizeof(revision_str)-1);
|
||||
set_str(revision_str, scsi_rev, sizeof(revision_str)-1);
|
||||
|
||||
}
|
||||
|
||||
/* Fallback to USB vendor & device */
|
||||
if (vendor_str[0] == '\0') {
|
||||
if (!use_num_info)
|
||||
if (!(usb_vendor = sysfs_get_device_attr(usb_dev, "manufacturer")))
|
||||
if (!(usb_vendor = sysfs_attr_get_value(dev_usb->devpath, "manufacturer")))
|
||||
dbg("No USB vendor string found, using idVendor");
|
||||
|
||||
if (!usb_vendor) {
|
||||
if (!(usb_vendor = sysfs_get_device_attr(usb_dev, "idVendor"))) {
|
||||
if (!(usb_vendor = sysfs_attr_get_value(dev_usb->devpath, "idVendor"))) {
|
||||
dbg("No USB vendor information available\n");
|
||||
sprintf(vendor_str,"0000");
|
||||
}
|
||||
}
|
||||
set_str(vendor_str,usb_vendor->value, sizeof(vendor_str) - 1);
|
||||
set_str(vendor_str,usb_vendor, sizeof(vendor_str) - 1);
|
||||
}
|
||||
|
||||
if (model_str[0] == '\0') {
|
||||
if (!use_num_info)
|
||||
if (!(usb_model = sysfs_get_device_attr(usb_dev, "product")))
|
||||
if (!(usb_model = sysfs_attr_get_value(dev_usb->devpath, "product")))
|
||||
dbg("No USB model string found, using idProduct");
|
||||
|
||||
if (!usb_model) {
|
||||
if (!(usb_model = sysfs_get_device_attr(usb_dev, "idProduct"))) {
|
||||
dbg("No USB model information available\n");
|
||||
sprintf(model_str,"0000");
|
||||
}
|
||||
if (!(usb_model = sysfs_attr_get_value(dev_usb->devpath, "idProduct")))
|
||||
dbg("No USB model information available\n"); sprintf(model_str,"0000");
|
||||
}
|
||||
set_str(model_str, usb_model->value, sizeof(model_str) - 1);
|
||||
set_str(model_str, usb_model, sizeof(model_str) - 1);
|
||||
}
|
||||
|
||||
if (revision_str[0] == '\0') {
|
||||
usb_rev = sysfs_get_device_attr(usb_dev, "bcdDevice");
|
||||
if (usb_rev) {
|
||||
set_str(revision_str, usb_rev->value,
|
||||
sizeof(revision_str) - 1);
|
||||
}
|
||||
usb_rev = sysfs_attr_get_value(dev_usb->devpath, "bcdDevice");
|
||||
if (usb_rev)
|
||||
set_str(revision_str, usb_rev, sizeof(revision_str)-1);
|
||||
}
|
||||
|
||||
if (serial_str[0] == '\0') {
|
||||
usb_serial = sysfs_get_device_attr(usb_dev, "serial");
|
||||
if (usb_serial) {
|
||||
set_str(serial_str, usb_serial->value,
|
||||
sizeof(serial_str) - 1);
|
||||
}
|
||||
usb_serial = sysfs_attr_get_value(dev_usb->devpath, "serial");
|
||||
if (usb_serial)
|
||||
set_str(serial_str, usb_serial, sizeof(serial_str)-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int retval;
|
||||
char *devpath;
|
||||
char target_path[MAX_NAME_LEN];
|
||||
int retval = 0;
|
||||
const char *env;
|
||||
char devpath[MAX_NAME_LEN];
|
||||
int option;
|
||||
|
||||
logging_init("usb_id");
|
||||
sysfs_init();
|
||||
|
||||
dbg("argc is %d", argc);
|
||||
if (sysfs_get_mnt_path(sysfs_mnt_path, MAX_NAME_LEN)) {
|
||||
info("sysfs_get_mnt_path failed: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* sysfs path can be overridden for testing */
|
||||
env = getenv("SYSFS_PATH");
|
||||
if (env) {
|
||||
strlcpy(sysfs_path, env, sizeof(sysfs_path));
|
||||
remove_trailing_chars(sysfs_path, '/');
|
||||
} else
|
||||
strcpy(sysfs_path, "/sys");
|
||||
|
||||
while ((option = getopt(argc, argv, "dnux")) != -1 ) {
|
||||
if (optarg)
|
||||
@@ -433,14 +424,14 @@ int main(int argc, char **argv)
|
||||
debug = 1;
|
||||
break;
|
||||
case 'n':
|
||||
use_num_info=1;
|
||||
use_usb_info=1;
|
||||
use_num_info = 1;
|
||||
use_usb_info = 1;
|
||||
break;
|
||||
case 'u':
|
||||
use_usb_info=1;
|
||||
use_usb_info = 1;
|
||||
break;
|
||||
case 'x':
|
||||
export=1;
|
||||
export = 1;
|
||||
break;
|
||||
default:
|
||||
info("Unknown or bad option '%c' (0x%x)", option, option);
|
||||
@@ -449,20 +440,19 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
devpath = getenv("DEVPATH");
|
||||
if (devpath) {
|
||||
strncpy(target_path, sysfs_mnt_path, MAX_NAME_LEN);
|
||||
strncat(target_path, devpath, MAX_NAME_LEN);
|
||||
} else {
|
||||
env = getenv("DEVPATH");
|
||||
if (env != NULL)
|
||||
strlcpy(devpath, env, sizeof(devpath));
|
||||
else {
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "No device specified\n");
|
||||
exit(1);
|
||||
retval = 1;
|
||||
goto exit;
|
||||
}
|
||||
devpath = argv[optind];
|
||||
strncpy(target_path, devpath, MAX_NAME_LEN);
|
||||
strlcpy(devpath, argv[optind], sizeof(devpath));
|
||||
}
|
||||
|
||||
retval = usb_id(target_path);
|
||||
retval = usb_id(devpath);
|
||||
|
||||
if (retval == 0) {
|
||||
if (export) {
|
||||
@@ -488,5 +478,9 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(retval);
|
||||
|
||||
exit:
|
||||
sysfs_cleanup();
|
||||
logging_close();
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ $(LIBVOLUME_ID):
|
||||
$(MAKE) -C libvolume_id
|
||||
|
||||
$(PROG): %: $(HEADERS) %.o $(LIBVOLUME_ID)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(LIBVOLUME_ID) -o $@ $(LIBUDEV) $(LIBSYSFS) $(LIB_OBJS)
|
||||
$(QUIET) $(LD) $(LDFLAGS) $@.o $(LIBVOLUME_ID) -o $@ $(LIBUDEV) $(LIB_OBJS)
|
||||
ifneq ($(strip $(STRIPCMD)),)
|
||||
$(QUIET) $(STRIPCMD) $@
|
||||
endif
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
#include <ctype.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "../../udev_utils.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../udev.h"
|
||||
#include "libvolume_id/volume_id.h"
|
||||
|
||||
#define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
||||
|
||||
-441
@@ -1,441 +0,0 @@
|
||||
|
||||
GNU Lesser Public License
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to
|
||||
share and change it. By contrast, the GNU General Public Licenses are
|
||||
intended to guarantee your freedom to share and change free software--to
|
||||
make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some specially
|
||||
designated software packages--typically libraries--of the Free Software
|
||||
Foundation and other authors who decide to use it. You can use it too, but
|
||||
we suggest you first think carefully about whether this license or the
|
||||
ordinary General Public License is the better strategy to use in any
|
||||
particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use, not
|
||||
price. Our General Public Licenses are designed to make sure that you have
|
||||
the freedom to distribute copies of free software (and charge for this
|
||||
service if you wish); that you receive source code or can get it if you
|
||||
want it; that you can change the software and use pieces of it in new free
|
||||
programs; and that you are informed that you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for you
|
||||
if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis or
|
||||
for a fee, you must give the recipients all the rights that we gave you.
|
||||
You must make sure that they, too, receive or can get the source code. If
|
||||
you link other code with the library, you must provide complete object
|
||||
files to the recipients, so that they can relink them with the library
|
||||
after making changes to the library and recompiling it. And you must show
|
||||
them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that there is
|
||||
no warranty for the free library. Also, if the library is modified by
|
||||
someone else and passed on, the recipients should know that what they have
|
||||
is not the original version, so that the original author's reputation will
|
||||
not be affected by problems that might be introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of any
|
||||
free program. We wish to make sure that a company cannot effectively
|
||||
restrict the users of a free program by obtaining a restrictive license
|
||||
from a patent holder. Therefore, we insist that any patent license
|
||||
obtained for a version of the library must be consistent with the full
|
||||
freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License. This license, the GNU Lesser General Public
|
||||
License, applies to certain designated libraries, and is quite different
|
||||
from the ordinary General Public License. We use this license for certain
|
||||
libraries in order to permit linking those libraries into non-free
|
||||
programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using a
|
||||
shared library, the combination of the two is legally speaking a combined
|
||||
work, a derivative of the original library. The ordinary General Public
|
||||
License therefore permits such linking only if the entire combination fits
|
||||
its criteria of freedom. The Lesser General Public License permits more
|
||||
lax criteria for linking other code with the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it does
|
||||
Less to protect the user's freedom than the ordinary General Public
|
||||
License. It also provides other free software developers Less of an
|
||||
advantage over competing non-free programs. These disadvantages are the
|
||||
reason we use the ordinary General Public License for many libraries.
|
||||
However, the Lesser license provides advantages in certain special
|
||||
circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to encourage
|
||||
the widest possible use of a certain library, so that it becomes a
|
||||
de-facto standard. To achieve this, non-free programs must be allowed to
|
||||
use the library. A more frequent case is that a free library does the same
|
||||
job as widely used non-free libraries. In this case, there is little to
|
||||
gain by limiting the free library to free software only, so we use the
|
||||
Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of free
|
||||
software. For example, permission to use the GNU C Library in non-free
|
||||
programs enables many more people to use the whole GNU operating system,
|
||||
as well as its variant, the GNU/Linux operating system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is linked
|
||||
with the Library has the freedom and the wherewithal to run that program
|
||||
using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a "work
|
||||
based on the library" and a "work that uses the library". The former
|
||||
contains code derived from the library, whereas the latter must be
|
||||
combined with the library in order to run.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other program
|
||||
which contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Lesser General
|
||||
Public License (also called "this License"). Each licensee is addressed as
|
||||
"you".
|
||||
|
||||
A "library" means a collection of software functions and/or data prepared
|
||||
so as to be conveniently linked with application programs (which use some
|
||||
of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work which
|
||||
has been distributed under these terms. A "work based on the Library"
|
||||
means either the Library or any derivative work under copyright law: that
|
||||
is to say, a work containing the Library or a portion of it, either
|
||||
verbatim or with modifications and/or translated straightforwardly into
|
||||
another language. (Hereinafter, translation is included without limitation
|
||||
in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for making
|
||||
modifications to it. For a library, complete source code means all the
|
||||
source code for all modules it contains, plus any associated interface
|
||||
definition files, plus the scripts used to control compilation and
|
||||
installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of running a
|
||||
program using the Library is not restricted, and output from such a
|
||||
program is covered only if its contents constitute a work based on the
|
||||
Library (independent of the use of the Library in a tool for writing it).
|
||||
Whether that is true depends on what the Library does and what the program
|
||||
that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's complete
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the notices
|
||||
that refer to this License and to the absence of any warranty; and
|
||||
distribute a copy of this License along with the Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you
|
||||
may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion of it,
|
||||
thus forming a work based on the Library, and copy and distribute such
|
||||
modifications or work under the terms of Section 1 above, provided that
|
||||
you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices stating
|
||||
that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no charge to
|
||||
all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a table
|
||||
of data to be supplied by an application program that uses the facility,
|
||||
other than as an argument passed when the facility is invoked, then you
|
||||
must make a good faith effort to ensure that, in the event an
|
||||
application does not supply such function or table, the facility still
|
||||
operates, and performs whatever part of its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has a
|
||||
purpose that is entirely well-defined independent of the application.
|
||||
Therefore, Subsection 2d requires that any application-supplied function
|
||||
or table used by this function must be optional: if the application does
|
||||
not supply it, the square root function must still compute square
|
||||
roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library, and
|
||||
can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based on
|
||||
the Library, the distribution of the whole must be on the terms of this
|
||||
License, whose permissions for other licensees extend to the entire
|
||||
whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of a
|
||||
storage or distribution medium does not bring the other work under the
|
||||
scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so that
|
||||
they refer to the ordinary GNU General Public License, version 2, instead
|
||||
of to this License. (If a newer version than version 2 of the ordinary GNU
|
||||
General Public License has appeared, then you can specify that version
|
||||
instead if you wish.) Do not make any other change in these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for that
|
||||
copy, so the ordinary GNU General Public License applies to all subsequent
|
||||
copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of the
|
||||
Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or derivative of
|
||||
it, under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you accompany it with the complete
|
||||
corresponding machine-readable source code, which must be distributed
|
||||
under the terms of Sections 1 and 2 above on a medium customarily used for
|
||||
software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy from a
|
||||
designated place, then offering equivalent access to copy the source code
|
||||
from the same place satisfies the requirement to distribute the source
|
||||
code, even though third parties are not compelled to copy the source along
|
||||
with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the Library,
|
||||
but is designed to work with the Library by being compiled or linked with
|
||||
it, is called a "work that uses the Library". Such a work, in isolation,
|
||||
is not a derivative work of the Library, and therefore falls outside the
|
||||
scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library creates
|
||||
an executable that is a derivative of the Library (because it contains
|
||||
portions of the Library), rather than a "work that uses the library". The
|
||||
executable is therefore covered by this License. Section 6 states terms
|
||||
for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file that
|
||||
is part of the Library, the object code for the work may be a derivative
|
||||
work of the Library even though the source code is not. Whether this is
|
||||
true is especially significant if the work can be linked without the
|
||||
Library, or if the work is itself a library. The threshold for this to be
|
||||
true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data structure
|
||||
layouts and accessors, and small macros and small inline functions (ten
|
||||
lines or less in length), then the use of the object file is unrestricted,
|
||||
regardless of whether it is legally a derivative work. (Executables
|
||||
containing this object code plus portions of the Library will still fall
|
||||
under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||
the object code for the work under the terms of Section 6. Any executables
|
||||
containing that work also fall under Section 6, whether or not they are
|
||||
linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or link a
|
||||
"work that uses the Library" with the Library to produce a work containing
|
||||
portions of the Library, and distribute that work under terms of your
|
||||
choice, provided that the terms permit modification of the work for the
|
||||
customer's own use and reverse engineering for debugging such
|
||||
modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the Library
|
||||
is used in it and that the Library and its use are covered by this
|
||||
License. You must supply a copy of this License. If the work during
|
||||
execution displays copyright notices, you must include the copyright
|
||||
notice for the Library among them, as well as a reference directing the
|
||||
user to the copy of this License. Also, you must do one of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding machine-readable
|
||||
source code for the Library including whatever changes were used in the
|
||||
work (which must be distributed under Sections 1 and 2 above); and, if
|
||||
the work is an executable linked with the Library, with the complete
|
||||
machine-readable "work that uses the Library", as object code and/or
|
||||
source code, so that the user can modify the Library and then relink to
|
||||
produce a modified executable containing the modified Library. (It is
|
||||
understood that the user who changes the contents of definitions files
|
||||
in the Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the Library.
|
||||
A suitable mechanism is one that (1) uses at run time a copy of the
|
||||
library already present on the user's computer system, rather than
|
||||
copying library functions into the executable, and (2) will operate
|
||||
properly with a modified version of the library, if the user installs
|
||||
one, as long as the modified version is interface-compatible with the
|
||||
version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at least three
|
||||
years, to give the same user the materials specified in Subsection 6a,
|
||||
above, for a charge no more than the cost of performing this
|
||||
distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy from a
|
||||
designated place, offer equivalent access to copy the above specified
|
||||
materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these materials
|
||||
or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the Library"
|
||||
must include any data and utility programs needed for reproducing the
|
||||
executable from it. However, as a special exception, the materials to be
|
||||
distributed need not include anything that is normally distributed (in
|
||||
either source or binary form) with the major components (compiler, kernel,
|
||||
and so on) of the operating system on which the executable runs, unless
|
||||
that component itself accompanies the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license restrictions
|
||||
of other proprietary libraries that do not normally accompany the
|
||||
operating system. Such a contradiction means you cannot use both them and
|
||||
the Library together in an executable that you distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the Library
|
||||
side-by-side in a single library together with other library facilities
|
||||
not covered by this License, and distribute such a combined library,
|
||||
provided that the separate distribution of the work based on the Library
|
||||
and of the other library facilities is otherwise permitted, and provided
|
||||
that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based on
|
||||
the Library, uncombined with any other library facilities. This must be
|
||||
distributed under the terms of the Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact that part
|
||||
of it is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute the
|
||||
Library except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense, link with, or distribute the
|
||||
Library is void, and will automatically terminate your rights under this
|
||||
License. However, parties who have received copies, or rights, from you
|
||||
under this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not signed
|
||||
it. However, nothing else grants you permission to modify or distribute
|
||||
the Library or its derivative works. These actions are prohibited by law
|
||||
if you do not accept this License. Therefore, by modifying or distributing
|
||||
the Library (or any work based on the Library), you indicate your
|
||||
acceptance of this License to do so, and all its terms and conditions for
|
||||
copying, distributing or modifying the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the original
|
||||
licensor to copy, distribute, link with or modify the Library subject to
|
||||
these terms and conditions. You may not impose any further restrictions on
|
||||
the recipients' exercise of the rights granted herein. You are not
|
||||
responsible for enforcing compliance by third parties with this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot distribute
|
||||
so as to satisfy simultaneously your obligations under this License and
|
||||
any other pertinent obligations, then as a consequence you may not
|
||||
distribute the Library at all. For example, if a patent license would not
|
||||
permit royalty-free redistribution of the Library by all those who receive
|
||||
copies directly or indirectly through you, then the only way you could
|
||||
satisfy both it and this License would be to refrain entirely from
|
||||
distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any such
|
||||
claims; this section has the sole purpose of protecting the integrity of
|
||||
the free software distribution system which is implemented by public
|
||||
license practices. Many people have made generous contributions to the
|
||||
wide range of software distributed through that system in reliance on
|
||||
consistent application of that system; it is up to the author/donor to
|
||||
decide if he or she is willing to distribute software through any other
|
||||
system and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a
|
||||
consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in certain
|
||||
countries either by patents or by copyrighted interfaces, the original
|
||||
copyright holder who places the Library under this License may add an
|
||||
explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new versions
|
||||
of the Lesser General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail
|
||||
to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Library does not specify a license version
|
||||
number, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these, write
|
||||
to the author to ask for permission. For software which is copyrighted by
|
||||
the Free Software Foundation, write to the Free Software Foundation; we
|
||||
sometimes make exceptions for this. Our decision will be guided by the two
|
||||
goals of preserving the free status of all derivatives of our free
|
||||
software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE
|
||||
LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT
|
||||
LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES
|
||||
SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE
|
||||
WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
@@ -1,621 +0,0 @@
|
||||
/*
|
||||
* dlist.c
|
||||
*
|
||||
* Copyright (C) 2003 Eric J Bohm
|
||||
*
|
||||
* 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 021110307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Double linked list implementation.
|
||||
|
||||
* You allocate the data and give dlist the pointer.
|
||||
* If your data is complex set the dlist->del_func to a an appropriate
|
||||
* delete function. Otherwise dlist will just use free.
|
||||
|
||||
*/
|
||||
#include "stdlib.h"
|
||||
#include "dlist.h"
|
||||
|
||||
/*
|
||||
* Return pointer to node at marker.
|
||||
* else null if no nodes.
|
||||
*/
|
||||
|
||||
inline void *dlist_mark(Dlist *list)
|
||||
{
|
||||
if(list->marker!=NULL)
|
||||
return(list->marker->data);
|
||||
else
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set marker to start.
|
||||
*/
|
||||
|
||||
inline void dlist_start(Dlist *list)
|
||||
{
|
||||
list->marker=list->head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set marker to end.
|
||||
*/
|
||||
|
||||
inline void dlist_end(Dlist *list)
|
||||
{
|
||||
list->marker=list->head;
|
||||
}
|
||||
|
||||
/* internal use function
|
||||
* quickie inline to consolidate the marker movement logic
|
||||
* in one place
|
||||
*
|
||||
* when direction true it moves marker after
|
||||
* when direction false it moves marker before.
|
||||
* return pointer to data at new marker
|
||||
* if nowhere to move the marker in desired direction return null
|
||||
*/
|
||||
inline void *_dlist_mark_move(Dlist *list,int direction)
|
||||
{
|
||||
if(direction)
|
||||
{
|
||||
if( list->marker && list->marker->next!=NULL)
|
||||
list->marker=list->marker->next;
|
||||
else
|
||||
return(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( list->marker && list->marker->prev!=NULL)
|
||||
list->marker=list->marker->prev;
|
||||
else
|
||||
return(NULL);
|
||||
}
|
||||
if(list->marker!=list->head)
|
||||
return(list->marker->data);
|
||||
else
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new linked list to store nodes of datasize.
|
||||
* return null if list cannot be created.
|
||||
*/
|
||||
Dlist *dlist_new(size_t datasize)
|
||||
{
|
||||
Dlist *list=NULL;
|
||||
if((list=malloc(sizeof(Dlist))))
|
||||
{
|
||||
list->marker=NULL;
|
||||
list->count=0L;
|
||||
list->data_size=datasize;
|
||||
list->del_func=free;
|
||||
list->head=&(list->headnode);
|
||||
list->head->prev=NULL;
|
||||
list->head->next=NULL;
|
||||
list->head->data=NULL;
|
||||
}
|
||||
return(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new linked list to store nodes of datasize set list
|
||||
* data node delete function to the passed in del_func
|
||||
* return null if list cannot be created.
|
||||
*/
|
||||
Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*))
|
||||
{
|
||||
Dlist *list=NULL;
|
||||
list=dlist_new(datasize);
|
||||
if(list!=NULL)
|
||||
list->del_func=del_func;
|
||||
return(list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove marker node from list
|
||||
* call data_delete function on data if registered.
|
||||
* otherwise call free.
|
||||
* when direction true it moves marker after
|
||||
* when direction false it moves marker before.
|
||||
* free marker node
|
||||
* return nothing.
|
||||
*/
|
||||
void dlist_delete(Dlist *list,int direction)
|
||||
{
|
||||
if((list->marker != list->head)&&(list->marker!=NULL))
|
||||
{
|
||||
DL_node *corpse;
|
||||
corpse=list->marker;
|
||||
_dlist_mark_move(list,direction);
|
||||
if(list->head->next==corpse)
|
||||
list->head->next=corpse->next;
|
||||
if(list->head->prev==corpse)
|
||||
list->head->prev=corpse->prev;
|
||||
if(corpse->prev!=NULL) //should be impossible
|
||||
corpse->prev->next=corpse->next;
|
||||
if(corpse->next!=NULL) //should be impossible
|
||||
corpse->next->prev=corpse->prev;
|
||||
list->del_func(corpse->data);
|
||||
list->count--;
|
||||
free(corpse);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert node containing data at marker.
|
||||
* If direction true it inserts after.
|
||||
* If direction false it inserts before.
|
||||
* move marker to inserted node
|
||||
* return pointer to inserted node
|
||||
*/
|
||||
void *dlist_insert(Dlist *list,void *data,int direction)
|
||||
{
|
||||
DL_node *new_node=NULL;
|
||||
if(list==NULL || data==NULL)
|
||||
return(NULL);
|
||||
if(list->marker==NULL) //in case the marker ends up unset
|
||||
list->marker=list->head;
|
||||
if((new_node=malloc(sizeof(DL_node))))
|
||||
{
|
||||
new_node->data=data;
|
||||
new_node->prev=NULL;
|
||||
new_node->next=NULL;
|
||||
list->count++;
|
||||
if(list->head->next==NULL) //no l
|
||||
{
|
||||
list->head->next=list->head->prev=new_node;
|
||||
new_node->prev=list->head;
|
||||
new_node->next=list->head;
|
||||
}
|
||||
else if(direction)
|
||||
{
|
||||
new_node->next=list->marker->next;
|
||||
new_node->prev=list->marker;
|
||||
list->marker->next->prev=new_node;
|
||||
list->marker->next=new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_node->prev=list->marker->prev;
|
||||
new_node->next=list->marker;
|
||||
list->marker->prev->next=new_node;
|
||||
list->marker->prev=new_node;
|
||||
}
|
||||
list->marker=new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
return(list->marker->data);
|
||||
}
|
||||
|
||||
/* internal use only
|
||||
* Insert dl_node at marker.
|
||||
* If direction true it inserts after.
|
||||
* If direction false it inserts before.
|
||||
* move marker to inserted node
|
||||
* return pointer to inserted node
|
||||
*/
|
||||
void *_dlist_insert_dlnode(struct dlist *list,struct dl_node *new_node,int direction)
|
||||
{
|
||||
if(list==NULL || new_node==NULL)
|
||||
return(NULL);
|
||||
if(list->marker==NULL) //in case the marker ends up unset
|
||||
list->marker=list->head;
|
||||
list->count++;
|
||||
if(list->head->next==NULL)
|
||||
{
|
||||
list->head->next=list->head->prev=new_node;
|
||||
new_node->prev=list->head;
|
||||
new_node->next=list->head;
|
||||
}
|
||||
else if(direction)
|
||||
{
|
||||
new_node->next=list->marker->next;
|
||||
new_node->prev=list->marker;
|
||||
list->marker->next->prev=new_node;
|
||||
list->marker->next=new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_node->prev=list->marker->prev;
|
||||
new_node->next=list->marker;
|
||||
list->marker->prev->next=new_node;
|
||||
list->marker->prev=new_node;
|
||||
}
|
||||
list->marker=new_node;
|
||||
return(list->marker);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Remove DL_node from list without deallocating data.
|
||||
* if marker == killme .
|
||||
* when direction true it moves marker after
|
||||
* when direction false it moves marker before.
|
||||
* to previous if there is no next.
|
||||
*/
|
||||
void *_dlist_remove(Dlist *list,DL_node *killme,int direction)
|
||||
{
|
||||
if(killme!=NULL)
|
||||
{
|
||||
void *killer_data=killme->data;
|
||||
// take care of head and marker pointers.
|
||||
if(list->marker==killme)
|
||||
_dlist_mark_move(list,direction);
|
||||
if(killme ==list->head->next)
|
||||
list->head->next=killme->next;
|
||||
if(killme==list->head->prev)
|
||||
list->head->prev=killme->prev;
|
||||
// remove from list
|
||||
if(killme->prev !=NULL)
|
||||
killme->prev->next=killme->next;
|
||||
if(killme->next !=NULL)
|
||||
killme->next->prev=killme->prev;
|
||||
list->count--;
|
||||
free(killme);
|
||||
return(killer_data);
|
||||
}
|
||||
else
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* move dl_node from source to dest
|
||||
* if marker == target .
|
||||
* when direction true it moves marker after
|
||||
* when direction false it moves marker before.
|
||||
* to previous if there is no next.
|
||||
*/
|
||||
void dlist_move(struct dlist *source, struct dlist *dest, struct dl_node *target,int direction)
|
||||
{
|
||||
|
||||
if(target!=NULL)
|
||||
{
|
||||
if(target==source->head)
|
||||
{
|
||||
//not even going to try
|
||||
}
|
||||
else
|
||||
{
|
||||
// take care of head and marker pointers.
|
||||
if(source->marker==target)
|
||||
_dlist_mark_move(source,direction);
|
||||
if(target ==source->head->next)
|
||||
source->head->next=target->next;
|
||||
if(target==source->head->prev)
|
||||
source->head->prev=target->prev;
|
||||
// remove from list
|
||||
if(source->count==1)
|
||||
{
|
||||
target->prev=NULL;
|
||||
target->next=NULL;
|
||||
source->head->next=NULL;
|
||||
source->head->prev=NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(target->prev !=NULL)
|
||||
target->prev->next=target->next;
|
||||
if(target->next !=NULL)
|
||||
target->next->prev=target->prev;
|
||||
target->prev=NULL;
|
||||
target->next=NULL;
|
||||
}
|
||||
source->count--;
|
||||
_dlist_insert_dlnode(dest,target,direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Insert node containing data after end.
|
||||
*/
|
||||
void dlist_push(Dlist *list,void *data)
|
||||
{
|
||||
list->marker=list->head->prev;
|
||||
dlist_insert(list,data,1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert node containing data at start.
|
||||
*/
|
||||
|
||||
void dlist_unshift(Dlist *list,void *data)
|
||||
|
||||
{
|
||||
list->marker=list->head->next;
|
||||
dlist_insert(list,data,0);
|
||||
}
|
||||
|
||||
void dlist_unshift_sorted(Dlist *list, void *data,
|
||||
int (*sorter)(void *new_elem, void *old_elem))
|
||||
{
|
||||
if (list->count == 0)
|
||||
dlist_unshift(list, data);
|
||||
else {
|
||||
list->marker=list->head->next;
|
||||
dlist_insert_sorted(list, data, sorter);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove end node from list.
|
||||
* Return pointer to data in removed node.
|
||||
* Null if no nodes.
|
||||
*/
|
||||
|
||||
void *dlist_pop(Dlist *list)
|
||||
{
|
||||
return(_dlist_remove(list,list->head->prev,0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove start node from list.
|
||||
* Return pointer to data in removed node.
|
||||
* Null if no nodes.
|
||||
*/
|
||||
|
||||
void *dlist_shift(Dlist *list)
|
||||
{
|
||||
return(_dlist_remove(list,list->head->next,1));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* destroy the list freeing all memory
|
||||
*/
|
||||
|
||||
|
||||
void dlist_destroy(Dlist *list)
|
||||
{
|
||||
if(list !=NULL)
|
||||
{
|
||||
dlist_start(list);
|
||||
dlist_next(list);
|
||||
while (dlist_mark(list)) {
|
||||
dlist_delete(list,1);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return void pointer to list_data element matching comp function criteria
|
||||
* else null
|
||||
* Does not move the marker.
|
||||
*/
|
||||
|
||||
void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *))
|
||||
{
|
||||
/* test the comp function on each node */
|
||||
struct dl_node *nodepointer;
|
||||
dlist_for_each_nomark(list,nodepointer)
|
||||
if(comp(target,nodepointer->data))
|
||||
return(nodepointer->data);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the node_operation function to each data node in the list
|
||||
*/
|
||||
void dlist_transform(struct dlist *list, void (*node_operation)(void *))
|
||||
{
|
||||
struct dl_node *nodepointer;
|
||||
dlist_for_each_nomark(list,nodepointer)
|
||||
node_operation(nodepointer->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* insert new into list in sorted order
|
||||
* sorter function in form int sorter(new,ith)
|
||||
* must return 1 for when new should go before ith
|
||||
* else 0
|
||||
* return pointer to inserted node
|
||||
* NOTE: assumes list is already sorted
|
||||
*/
|
||||
void *dlist_insert_sorted(struct dlist *list, void *new, int (*sorter)(void *, void *))
|
||||
{
|
||||
for(dlist_start(list),dlist_next(list); \
|
||||
list->marker!=list->head && !sorter(new,list->marker->data);dlist_next(list));
|
||||
return(dlist_insert_before(list,new));
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: internal use only
|
||||
*/
|
||||
int _dlist_merge(struct dlist *listsource, struct dlist *listdest, unsigned int passcount, int (*compare)(void *, void *))
|
||||
{
|
||||
|
||||
struct dl_node *l1head;
|
||||
struct dl_node *l2head;
|
||||
struct dl_node *target;
|
||||
unsigned int l1count=0;
|
||||
unsigned int l2count=0;
|
||||
unsigned int mergecount=0;
|
||||
while(listsource->count>0)
|
||||
{
|
||||
l1head=listsource->head->next;
|
||||
l2head=l1head;
|
||||
while((l1count<passcount)&&(l2head!=listsource->head))
|
||||
{
|
||||
l2head=l2head->next;
|
||||
l1count++;
|
||||
}
|
||||
// so now we have two lists to merge
|
||||
|
||||
if(l2head==listsource->head)
|
||||
{// l2count
|
||||
l2count=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
l2count=passcount;
|
||||
}
|
||||
while(l1count>0 || l2count>0)
|
||||
{
|
||||
mergecount++;
|
||||
if((l2count>0)&&(l1count>0))
|
||||
{
|
||||
// we have things to merge
|
||||
int result=compare(l1head->data,l2head->data);
|
||||
if(result>0)
|
||||
{
|
||||
// move from l2
|
||||
target=l2head;
|
||||
l2head=l2head->next;
|
||||
dlist_move(listsource,listdest,target,1);
|
||||
l2count--;
|
||||
if(l2head==listsource->head)
|
||||
l2count=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// move from l1
|
||||
target=l1head;
|
||||
l1head=l1head->next;
|
||||
dlist_move(listsource,listdest,target,1);
|
||||
l1count--;
|
||||
}
|
||||
}
|
||||
else if(l1count>0)
|
||||
{
|
||||
// only have l1 to work with
|
||||
while(l1count>0)
|
||||
{
|
||||
target=l1head;
|
||||
l1head=l1head->next;
|
||||
dlist_move(listsource,listdest,target,1);
|
||||
l1count--;
|
||||
}
|
||||
}
|
||||
else if(l2count>0)
|
||||
{
|
||||
// only have l2 to work with
|
||||
while(l2count>0)
|
||||
{
|
||||
if(l2head==listsource->head)
|
||||
{
|
||||
l2count=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
target=l2head;
|
||||
l2head=l2head->next;
|
||||
dlist_move(listsource,listdest,target,1);
|
||||
l2count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //nothing left and this should be unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
return(mergecount);
|
||||
}
|
||||
|
||||
/**
|
||||
* mergesort the list based on compare
|
||||
* compare function in form int sorter(void * a,void * b)
|
||||
* must return >0 for a after b
|
||||
* must return <0 for a before b
|
||||
* else 0
|
||||
|
||||
* NOTE: mergesort changes the mark pointer
|
||||
*/
|
||||
void dlist_sort_custom(struct dlist *list, int (*compare)(void *, void *))
|
||||
{
|
||||
|
||||
struct dlist *listsource, *listdest, *swap;
|
||||
struct dlist *templist;
|
||||
unsigned int passcount = 1;
|
||||
unsigned int mergecount = 1;
|
||||
|
||||
dlist_start(list);
|
||||
templist = dlist_new(list->data_size);
|
||||
|
||||
// do nothing if there isn't anything to sort
|
||||
listsource = list;
|
||||
listdest = templist;
|
||||
if(listsource->count<2)
|
||||
{ //nothing to do
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(mergecount>0)
|
||||
{
|
||||
mergecount=_dlist_merge(listsource, listdest, passcount, compare);
|
||||
if(mergecount>1)
|
||||
{
|
||||
passcount=passcount*2;
|
||||
//start new pass
|
||||
swap=listsource;
|
||||
listsource=listdest;
|
||||
listdest=swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
// now put the input list pointers right
|
||||
// list pointers = newlist pointers
|
||||
// including the forward and next nodes prev and back pointers
|
||||
if(list->count==0)
|
||||
{//copy
|
||||
list->marker = listdest->marker;
|
||||
list->count = listdest->count;
|
||||
list->data_size = listdest->data_size;
|
||||
list->del_func = listdest->del_func;
|
||||
list->head->prev = listdest->head->prev;
|
||||
list->head->next = listdest->head->next;
|
||||
list->head->data = listdest->head->data;
|
||||
list->head->next->prev=list->head;
|
||||
list->head->prev->next=list->head;
|
||||
templist->head->next=NULL;
|
||||
templist->head->prev=NULL;
|
||||
templist->count=0;
|
||||
}
|
||||
else
|
||||
{// no need to copy
|
||||
|
||||
}
|
||||
|
||||
dlist_destroy(templist);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* internal use function
|
||||
swaps elements a and b
|
||||
No sense in juggling node pointers when we can just swap the data pointers
|
||||
*/
|
||||
|
||||
void _dlist_swap(struct dlist *list, struct dl_node *a, struct dl_node *b)
|
||||
{
|
||||
|
||||
void *swap=a->data;
|
||||
a->data=b->data;
|
||||
b->data=swap;
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* sysfs.h
|
||||
*
|
||||
* Internal Header Definitions 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
|
||||
*
|
||||
*/
|
||||
#ifndef _SYSFS_H_
|
||||
#define _SYSFS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define safestrcpy(to, from) strncpy(to, from, sizeof(to)-1)
|
||||
#define safestrcat(to, from) strncat(to, from, sizeof(to) - strlen(to)-1)
|
||||
|
||||
#define safestrcpymax(to, from, max) \
|
||||
do { \
|
||||
to[max-1] = '\0'; \
|
||||
strncpy(to, from, max-1); \
|
||||
} while (0)
|
||||
|
||||
#define safestrcatmax(to, from, max) \
|
||||
do { \
|
||||
to[max-1] = '\0'; \
|
||||
strncat(to, from, max - strlen(to)-1); \
|
||||
} while (0)
|
||||
|
||||
extern struct sysfs_attribute *get_attribute(void *dev, const char *name);
|
||||
extern struct dlist *read_dir_subdirs(const char *path);
|
||||
extern struct dlist *read_dir_links(const char *path);
|
||||
extern struct dlist *get_attributes_list(void *dev);
|
||||
|
||||
/* Debugging */
|
||||
#ifdef DEBUG
|
||||
#include "../logging.h"
|
||||
#define dprintf(format, arg...) dbg(format, ## arg)
|
||||
#else
|
||||
#define dprintf(format, arg...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _SYSFS_H_ */
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* dlist.h
|
||||
*
|
||||
* Copyright (C) 2003 Eric J Bohm
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef _DLIST_H_
|
||||
#define _DLIST_H_
|
||||
|
||||
/* Double linked list header.
|
||||
|
||||
* navigate your list with DLIST_PREV and DLIST_NEXT. These are macros
|
||||
* so function call overhead is minimized.
|
||||
|
||||
* Supports perl style push, pop, shift, unshift list semantics.
|
||||
|
||||
* You allocate the data and give dlist the pointer. If your data is
|
||||
* complex set the dlist->del_func to a an appropriate delete using
|
||||
* dlist_new_with_delete. Your delete function must match
|
||||
(void * )(del(void *)
|
||||
*Otherwise dlist will just use free.
|
||||
|
||||
* NOTE: The small amount of pain involved in doing that allows us to
|
||||
* avoid copy in copy out semantics.
|
||||
|
||||
* Dlist uses an internal mark pointer to keep track of where you are
|
||||
* in the list.
|
||||
|
||||
* insert and delete take a directional parameter. Where direction
|
||||
* corresponds to the direction in which you want the list to go.
|
||||
* true direction corresponded to progressing forward in the last
|
||||
* false to regressing in the list.
|
||||
* so a dlist_insert(yourlist,item,1) will insert it after the mark
|
||||
* so a dlist_insert(yourlist,item,0) will insert it before the mark
|
||||
* any insert will move the mark to the new node regardless of the direction.
|
||||
|
||||
* Just use the dlist_(insert|delete)_(before|after) macros if you do not want
|
||||
* to think about it.
|
||||
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct dl_node {
|
||||
struct dl_node *prev;
|
||||
struct dl_node *next;
|
||||
void *data;
|
||||
} DL_node;
|
||||
|
||||
typedef struct dlist {
|
||||
DL_node *marker;
|
||||
unsigned long count;
|
||||
size_t data_size;
|
||||
void (*del_func)(void *);
|
||||
DL_node headnode;
|
||||
DL_node *head;
|
||||
} Dlist;
|
||||
|
||||
Dlist *dlist_new(size_t datasize);
|
||||
Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*));
|
||||
void *_dlist_mark_move(Dlist *list,int direction);
|
||||
void *dlist_mark(Dlist *);
|
||||
void dlist_start(Dlist *);
|
||||
void dlist_end(Dlist *);
|
||||
void dlist_move(struct dlist *source, struct dlist *dest, struct dl_node *target,int direction);
|
||||
void *dlist_insert(Dlist *,void *,int) ;
|
||||
|
||||
void *dlist_insert_sorted(struct dlist *list, void *new_elem, int (*sorter)(void *, void *));
|
||||
|
||||
void dlist_delete(Dlist *,int);
|
||||
|
||||
void dlist_push(Dlist *,void *);
|
||||
|
||||
void dlist_unshift(Dlist *,void *);
|
||||
void dlist_unshift_sorted(Dlist *,void *,int (*sorter)(void *, void *));
|
||||
|
||||
void *dlist_pop(Dlist *);
|
||||
|
||||
void *dlist_shift(Dlist *);
|
||||
|
||||
void dlist_destroy(Dlist *);
|
||||
|
||||
int _dlist_merge(struct dlist *listsource, struct dlist *listdest, unsigned int passcount, int (*compare)(void *, void *));
|
||||
|
||||
void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *));
|
||||
|
||||
void dlist_sort_custom(struct dlist *list, int (*compare)(void *, void *));
|
||||
|
||||
|
||||
void _dlist_swap(struct dlist *list, struct dl_node *a, struct dl_node *b);
|
||||
|
||||
void dlist_transform(struct dlist *list, void (*node_operation)(void *));
|
||||
|
||||
|
||||
/*
|
||||
* _dlist_remove is for internal use only
|
||||
* _dlist_mark_move is for internal use only
|
||||
*/
|
||||
void *_dlist_remove(struct dlist *,struct dl_node *,int );
|
||||
void *_dlist_insert_dlnode(struct dlist *list,struct dl_node *new_node,int direction);
|
||||
|
||||
#define dlist_prev(A) _dlist_mark_move((A),0)
|
||||
#define dlist_next(A) _dlist_mark_move((A),1)
|
||||
|
||||
#define dlist_insert_before(A,B) dlist_insert((A),(B),0)
|
||||
#define dlist_insert_after(A,B) dlist_insert((A),(B),1)
|
||||
|
||||
#define dlist_delete_before(A) dlist_delete((A),0)
|
||||
#define dlist_delete_after(A) dlist_delete((A),1)
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates the mark from start to end
|
||||
* list: the dlist pointer, use dlist_mark(list) to get iterator
|
||||
*/
|
||||
#define dlist_for_each(list) \
|
||||
for(dlist_start(list),dlist_next(list); \
|
||||
(list)->marker!=(list)->head;dlist_next(list))
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates the mark from end to start
|
||||
* list: the dlist pointer, use dlist_mark(list) to get iterator
|
||||
*/
|
||||
#define dlist_for_each_rev(list) \
|
||||
for(dlist_end(list),dlist_prev(list); \
|
||||
(list)->marker!=(list)->head;dlist_prev(list))
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates through the list without moving mark
|
||||
* list: the dlist_pointer
|
||||
* iterator: dl_node pointer to iterate
|
||||
*/
|
||||
#define dlist_for_each_nomark(list,iterator) \
|
||||
for((iterator)=(list)->head->next; (iterator)!=(list)->head; \
|
||||
(iterator)=(iterator)->next)
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates through the list without moving mark
|
||||
* in reverse
|
||||
* list: the dlist_pointer
|
||||
* iterator: dl_node pointer to iterate
|
||||
*/
|
||||
#define dlist_for_each_nomark_rev(list,iterator) \
|
||||
for((iterator)=(list)->head->prev; (iterator)!=(list)->head; \
|
||||
(iterator)=(iterator)->prev)
|
||||
/**
|
||||
* provide for loop header which iterates through the list providing a
|
||||
* data iterator
|
||||
* list: the dlist pointer
|
||||
* data_iterator: the pointer of type datatype to iterate
|
||||
* datatype: actual type of the contents in the dl_node->data
|
||||
*/
|
||||
|
||||
#define dlist_for_each_data(list,data_iterator,datatype) \
|
||||
for(dlist_start(list), (data_iterator)=(datatype *) dlist_next(list); \
|
||||
(list)->marker!=(list)->head;(data_iterator)=(datatype *) dlist_next(list))
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates through the list providing a
|
||||
* data iterator in reverse
|
||||
* list: the dlist pointer
|
||||
* data_iterator: the pointer of type datatype to iterate
|
||||
* datatype: actual type of the contents in the dl_node->data
|
||||
*/
|
||||
#define dlist_for_each_data_rev(list,data_iterator,datatype) \
|
||||
for(dlist_end(list), (data_iterator)=(datatype *) dlist_prev(list); \
|
||||
(list)->marker!=(list)->head;(data_iterator)=(datatype *) dlist_prev(list))
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates through the list providing a
|
||||
* data iterator without moving the mark
|
||||
* list: the dlist pointer
|
||||
* iterator: the dl_node pointer to iterate
|
||||
* data_iterator: the pointer of type datatype to iterate
|
||||
* datatype: actual type of the contents in the dl_node->data
|
||||
*/
|
||||
|
||||
#define dlist_for_each_data_nomark(list,iterator,data_iterator,datatype) \
|
||||
for((iterator)=(list)->head->next, (data_iterator)=(datatype *) (iterator)->data; \
|
||||
(iterator)!=(list)->head;(iterator)=(iterator)->next,(data_iterator)=(datatype *) (iterator))
|
||||
|
||||
/**
|
||||
* provide for loop header which iterates through the list providing a
|
||||
* data iterator in reverse without moving the mark
|
||||
* list: the dlist pointer
|
||||
* iterator: the dl_node pointer to iterate
|
||||
* data_iterator: the pointer of type datatype to iterate
|
||||
* datatype: actual type of the contents in the dl_node->data
|
||||
*/
|
||||
#define dlist_for_each_data_nomark_rev(list,iterator, data_iterator,datatype) \
|
||||
for((iterator)=(list)->head->prev, (data_iterator)=(datatype *) (iterator)->data; \
|
||||
(iterator)!=(list)->head;(iterator)=(iterator)->prev,(data_iterator)=(datatype *) (iterator))
|
||||
|
||||
#endif /* _DLIST_H_ */
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* libsysfs.h
|
||||
*
|
||||
* Header Definitions for libsysfs
|
||||
*
|
||||
* Copyright (C) IBM Corp. 2004-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
|
||||
*
|
||||
*/
|
||||
#ifndef _LIBSYSFS_H_
|
||||
#define _LIBSYSFS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include "dlist.h"
|
||||
|
||||
#define SYSFS_FSTYPE_NAME "sysfs"
|
||||
#define SYSFS_PROC_MNTS "/proc/mounts"
|
||||
#define SYSFS_BUS_NAME "bus"
|
||||
#define SYSFS_CLASS_NAME "class"
|
||||
#define SYSFS_BLOCK_NAME "block"
|
||||
#define SYSFS_DEVICES_NAME "devices"
|
||||
#define SYSFS_DRIVERS_NAME "drivers"
|
||||
#define SYSFS_MODULE_NAME "module"
|
||||
#define SYSFS_NAME_ATTRIBUTE "name"
|
||||
#define SYSFS_UNKNOWN "unknown"
|
||||
#define SYSFS_PATH_ENV "SYSFS_PATH"
|
||||
|
||||
#define SYSFS_PATH_MAX 256
|
||||
#define SYSFS_NAME_LEN 64
|
||||
#define SYSFS_BUS_ID_SIZE 32
|
||||
|
||||
/* mount path for sysfs, can be overridden by exporting SYSFS_PATH */
|
||||
#define SYSFS_MNT_PATH "/sys"
|
||||
|
||||
enum sysfs_attribute_method {
|
||||
SYSFS_METHOD_SHOW = 0x01, /* attr can be read by user */
|
||||
SYSFS_METHOD_STORE = 0x02, /* attr can be changed by user */
|
||||
};
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* 1. We have the statically allocated "name" as the first element of all
|
||||
* the structures. This feature is used in the "sorter" function for dlists
|
||||
* 2. As is the case with attrlist
|
||||
* 3. As is the case with path
|
||||
*/
|
||||
struct sysfs_attribute {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
char *value;
|
||||
unsigned short len; /* value length */
|
||||
enum sysfs_attribute_method method; /* show and store */
|
||||
};
|
||||
|
||||
struct sysfs_driver {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *attrlist;
|
||||
char bus[SYSFS_NAME_LEN];
|
||||
|
||||
/* Private: for internal use only */
|
||||
struct dlist *devices;
|
||||
};
|
||||
|
||||
struct sysfs_device {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *attrlist;
|
||||
char bus_id[SYSFS_NAME_LEN];
|
||||
char bus[SYSFS_NAME_LEN];
|
||||
char driver_name[SYSFS_NAME_LEN];
|
||||
|
||||
/* Private: for internal use only */
|
||||
struct sysfs_device *parent;
|
||||
/* NOTE - we still don't populate this */
|
||||
struct dlist *children;
|
||||
};
|
||||
|
||||
/* NOTE: not used as of now */
|
||||
struct sysfs_bus {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *attrlist;
|
||||
|
||||
/* Private: for internal use only */
|
||||
struct dlist *drivers;
|
||||
struct dlist *devices;
|
||||
};
|
||||
|
||||
struct sysfs_class_device {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *attrlist;
|
||||
char classname[SYSFS_NAME_LEN];
|
||||
|
||||
/* Private: for internal use only */
|
||||
struct sysfs_class_device *parent;
|
||||
struct sysfs_device *sysdevice; /* NULL if virtual */
|
||||
};
|
||||
|
||||
/* NOTE: not used as of now */
|
||||
struct sysfs_class {
|
||||
char name[SYSFS_NAME_LEN];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *attrlist;
|
||||
|
||||
/* Private: for internal use only */
|
||||
struct dlist *devices;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
extern int sysfs_get_mnt_path(char *mnt_path, size_t len);
|
||||
extern int sysfs_remove_trailing_slash(char *path);
|
||||
extern int sysfs_get_name_from_path(const char *path, char *name, size_t len);
|
||||
extern int sysfs_path_is_dir(const char *path);
|
||||
extern int sysfs_path_is_link(const char *path);
|
||||
extern int sysfs_path_is_file(const char *path);
|
||||
extern int sysfs_get_link(const char *path, char *target, size_t len);
|
||||
extern struct dlist *sysfs_open_directory_list(const char *path);
|
||||
extern void sysfs_close_list(struct dlist *list);
|
||||
|
||||
/* sysfs directory and file access */
|
||||
extern void sysfs_close_attribute(struct sysfs_attribute *sysattr);
|
||||
extern struct sysfs_attribute *sysfs_open_attribute(const char *path);
|
||||
extern int sysfs_read_attribute(struct sysfs_attribute *sysattr);
|
||||
extern int sysfs_write_attribute(struct sysfs_attribute *sysattr,
|
||||
const char *new_value, size_t len);
|
||||
|
||||
/* sysfs driver access */
|
||||
extern void sysfs_close_driver(struct sysfs_driver *driver);
|
||||
extern struct sysfs_driver *sysfs_open_driver
|
||||
(const char *bus_name, const char *drv_name);
|
||||
extern struct sysfs_driver *sysfs_open_driver_path(const char *path);
|
||||
extern struct sysfs_attribute *sysfs_get_driver_attr
|
||||
(struct sysfs_driver *drv, const char *name);
|
||||
extern struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *driver);
|
||||
extern struct dlist *sysfs_get_driver_devices(struct sysfs_driver *driver);
|
||||
|
||||
/* generic sysfs device access */
|
||||
extern void sysfs_close_device_tree(struct sysfs_device *device);
|
||||
extern struct sysfs_device *sysfs_open_device_tree(const char *path);
|
||||
extern void sysfs_close_device(struct sysfs_device *dev);
|
||||
extern struct sysfs_device *sysfs_open_device
|
||||
(const char *bus, const char *bus_id);
|
||||
extern struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev);
|
||||
extern struct sysfs_device *sysfs_open_device_path(const char *path);
|
||||
extern int sysfs_get_device_bus(struct sysfs_device *dev);
|
||||
extern struct sysfs_attribute *sysfs_get_device_attr
|
||||
(struct sysfs_device *dev, const char *name);
|
||||
extern struct dlist *sysfs_get_device_attributes
|
||||
(struct sysfs_device *dev);
|
||||
|
||||
/* generic sysfs class access */
|
||||
extern void sysfs_close_class_device(struct sysfs_class_device *dev);
|
||||
extern struct sysfs_class_device *sysfs_open_class_device_path
|
||||
(const char *path);
|
||||
extern struct sysfs_class_device *sysfs_open_class_device
|
||||
(const char *classname, const char *name);
|
||||
extern struct sysfs_class_device *sysfs_get_classdev_parent
|
||||
(struct sysfs_class_device *clsdev);
|
||||
extern struct sysfs_attribute *sysfs_get_classdev_attr
|
||||
(struct sysfs_class_device *clsdev, const char *name);
|
||||
extern struct dlist *sysfs_get_classdev_attributes
|
||||
(struct sysfs_class_device *clsdev);
|
||||
extern struct sysfs_device *sysfs_get_classdev_device
|
||||
(struct sysfs_class_device *clsdev);
|
||||
extern void sysfs_close_class(struct sysfs_class *cls);
|
||||
extern struct sysfs_class *sysfs_open_class(const char *name);
|
||||
extern struct sysfs_class_device *sysfs_get_class_device
|
||||
(struct sysfs_class *cls, const char *name);
|
||||
extern struct dlist *sysfs_get_class_devices(struct sysfs_class *cls);
|
||||
|
||||
/* generic sysfs bus access */
|
||||
extern void sysfs_close_bus(struct sysfs_bus *bus);
|
||||
extern struct sysfs_bus *sysfs_open_bus(const char *name);
|
||||
extern struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus);
|
||||
extern struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus);
|
||||
extern struct sysfs_device *sysfs_get_bus_device
|
||||
(struct sysfs_bus *bus, const char *id);
|
||||
extern struct sysfs_driver *sysfs_get_bus_driver
|
||||
(struct sysfs_bus *bus, const char *drvname);
|
||||
|
||||
/**
|
||||
* sort_list: sorter function to keep list elements sorted in alphabetical
|
||||
* order. Just does a strncmp as you can see :)
|
||||
*
|
||||
* Returns 1 if less than 0 otherwise
|
||||
*
|
||||
* NOTE: We take care to have a statically allocated "name" as the first
|
||||
* lement of all libsysfs structures. Hence, this function will work
|
||||
* AS IS for _ALL_ the lists that have to be sorted.
|
||||
*/
|
||||
static inline int sort_list(void *new_elem, void *old_elem)
|
||||
{
|
||||
return ((strncmp(((struct sysfs_attribute *)new_elem)->name,
|
||||
((struct sysfs_attribute *)old_elem)->name,
|
||||
strlen(((struct sysfs_attribute *)new_elem)->name))) < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBSYSFS_H_ */
|
||||
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -1,557 +0,0 @@
|
||||
/*
|
||||
* sysfs_class.c
|
||||
*
|
||||
* Generic class 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"
|
||||
|
||||
/**
|
||||
* sysfs_close_class_device: closes a single class device.
|
||||
* @dev: class device to close.
|
||||
*/
|
||||
void sysfs_close_class_device(struct sysfs_class_device *dev)
|
||||
{
|
||||
if (dev) {
|
||||
if (dev->parent)
|
||||
sysfs_close_class_device(dev->parent);
|
||||
if (dev->sysdevice)
|
||||
sysfs_close_device(dev->sysdevice);
|
||||
if (dev->attrlist)
|
||||
dlist_destroy(dev->attrlist);
|
||||
free(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void sysfs_close_cls_dev(void *dev)
|
||||
{
|
||||
sysfs_close_class_device((struct sysfs_class_device *)dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_class: close the given class
|
||||
* @cls: sysfs_class to close
|
||||
*/
|
||||
void sysfs_close_class(struct sysfs_class *cls)
|
||||
{
|
||||
if (cls) {
|
||||
if (cls->devices)
|
||||
dlist_destroy(cls->devices);
|
||||
if (cls->attrlist)
|
||||
dlist_destroy(cls->attrlist);
|
||||
free(cls);
|
||||
}
|
||||
}
|
||||
|
||||
static int cdev_name_equal(void *a, void *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
if (strncmp((char *)a, ((struct sysfs_class_device *)b)->name,
|
||||
strlen((char *)a)) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sysfs_class *alloc_class(void)
|
||||
{
|
||||
return (struct sysfs_class *) calloc(1, sizeof(struct sysfs_class));
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_class_device: mallocs and initializes new class device struct.
|
||||
* returns sysfs_class_device or NULL.
|
||||
*/
|
||||
static struct sysfs_class_device *alloc_class_device(void)
|
||||
{
|
||||
struct sysfs_class_device *dev;
|
||||
|
||||
dev = calloc(1, sizeof(struct sysfs_class_device));
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_classdev_classname: Grabs classname from path
|
||||
* @cdev: class device to set
|
||||
* Returns nothing
|
||||
*/
|
||||
static void set_classdev_classname(struct sysfs_class_device *cdev)
|
||||
{
|
||||
char *c, *e;
|
||||
int count = 0;
|
||||
|
||||
c = strstr(cdev->path, SYSFS_CLASS_NAME);
|
||||
if (c == NULL) {
|
||||
c = strstr(cdev->path, SYSFS_BLOCK_NAME);
|
||||
} else {
|
||||
c = strstr(c, "/");
|
||||
}
|
||||
|
||||
if (c == NULL)
|
||||
safestrcpy(cdev->classname, SYSFS_UNKNOWN);
|
||||
else {
|
||||
if (*c == '/')
|
||||
c++;
|
||||
e = c;
|
||||
while (e != NULL && *e != '/' && *e != '\0') {
|
||||
e++;
|
||||
count++;
|
||||
}
|
||||
strncpy(cdev->classname, c, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_class_device_path: Opens and populates class device
|
||||
* @path: path to class device.
|
||||
* returns struct sysfs_class_device with success and NULL with error.
|
||||
*/
|
||||
struct sysfs_class_device *sysfs_open_class_device_path(const char *path)
|
||||
{
|
||||
struct sysfs_class_device *cdev;
|
||||
char temp_path[SYSFS_PATH_MAX];
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Post linux-2.6.14 driver model supports nested classes with
|
||||
* links to the nested hierarchy at /sys/class/xxx/. Check for
|
||||
* a link to the actual class device if a directory isn't found
|
||||
*/
|
||||
if (sysfs_path_is_dir(path)) {
|
||||
dprintf("%s: Directory not found, checking for a link\n", path);
|
||||
if (!sysfs_path_is_link(path)) {
|
||||
if (sysfs_get_link(path, temp_path, SYSFS_PATH_MAX)) {
|
||||
dprintf("Error retrieving link at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
dprintf("%s is not a valid class device path\n", path);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
safestrcpy(temp_path, path);
|
||||
|
||||
cdev = alloc_class_device();
|
||||
if (!cdev) {
|
||||
dprintf("calloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_get_name_from_path(temp_path, cdev->name, SYSFS_NAME_LEN)) {
|
||||
errno = EINVAL;
|
||||
dprintf("Error getting class device name\n");
|
||||
sysfs_close_class_device(cdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
safestrcpy(cdev->path, temp_path);
|
||||
if (sysfs_remove_trailing_slash(cdev->path)) {
|
||||
dprintf("Invalid path to class device %s\n", cdev->path);
|
||||
sysfs_close_class_device(cdev);
|
||||
return NULL;
|
||||
}
|
||||
set_classdev_classname(cdev);
|
||||
|
||||
return cdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_blockdev_parent: Get the parent class device for a "block" subsystem
|
||||
* device if present
|
||||
* @clsdev: block subsystem class device whose parent needs to be found
|
||||
* Returns 0 on success and 1 on error
|
||||
*/
|
||||
static int get_blockdev_parent(struct sysfs_class_device *clsdev)
|
||||
{
|
||||
char parent_path[SYSFS_PATH_MAX];
|
||||
char *c;
|
||||
|
||||
safestrcpy(parent_path, clsdev->path);
|
||||
c = strstr(parent_path, SYSFS_BLOCK_NAME);
|
||||
if (c == NULL) {
|
||||
dprintf("Class device %s does not belong to BLOCK subsystem\n",
|
||||
clsdev->name);
|
||||
return 1;
|
||||
}
|
||||
c += strlen(SYSFS_BLOCK_NAME);
|
||||
if (*c == '/')
|
||||
c++;
|
||||
else
|
||||
goto errout;
|
||||
|
||||
/* validate whether the given class device is a partition or not */
|
||||
if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) {
|
||||
dprintf("%s not a partition\n", clsdev->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
c = strchr(c, '/');
|
||||
if (c == NULL)
|
||||
goto errout;
|
||||
|
||||
*c = '\0';
|
||||
|
||||
clsdev->parent = sysfs_open_class_device_path(parent_path);
|
||||
if (!clsdev->parent) {
|
||||
dprintf("Error opening the parent class device at %s\n",
|
||||
parent_path);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
dprintf("Invalid path %s\n", clsdev->path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_classdev_parent: Retrieves the parent of a class device.
|
||||
* eg., when working with hda1, this function can be used to retrieve the
|
||||
* sysfs_class_device for hda
|
||||
*
|
||||
* @clsdev: class device whose parent details are required.
|
||||
* Returns sysfs_class_device of the parent on success, NULL on failure
|
||||
*/
|
||||
struct sysfs_class_device *sysfs_get_classdev_parent
|
||||
(struct sysfs_class_device *clsdev)
|
||||
{
|
||||
if (!clsdev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (clsdev->parent)
|
||||
return (clsdev->parent);
|
||||
|
||||
/*
|
||||
* As of now, only block devices have a parent child heirarchy in sysfs
|
||||
* We do not know, if, in the future, more classes will have a similar
|
||||
* structure. Hence, we now call a specialized function for block and
|
||||
* later we can add support functions for other subsystems as required.
|
||||
*/
|
||||
if (!(strncmp(clsdev->classname, SYSFS_BLOCK_NAME,
|
||||
sizeof(SYSFS_BLOCK_NAME)))) {
|
||||
if ((get_blockdev_parent(clsdev)) == 0)
|
||||
return (clsdev->parent);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_classdev_path: given the class and a device in the class, return the
|
||||
* absolute path to the device
|
||||
* @classname: name of the class
|
||||
* @clsdev: the class device
|
||||
* @path: buffer to return path
|
||||
* @psize: size of "path"
|
||||
* Returns 0 on SUCCESS or -1 on error
|
||||
*/
|
||||
static int get_classdev_path(const char *classname, const char *clsdev,
|
||||
char *path, size_t len)
|
||||
{
|
||||
if (!classname || !clsdev || !path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (sysfs_get_mnt_path(path, len) != 0) {
|
||||
dprintf("Error getting sysfs mount path\n");
|
||||
return -1;
|
||||
}
|
||||
if (strncmp(classname, SYSFS_BLOCK_NAME,
|
||||
sizeof(SYSFS_BLOCK_NAME)) == 0) {
|
||||
safestrcatmax(path, "/", len);
|
||||
safestrcatmax(path, SYSFS_BLOCK_NAME, len);
|
||||
} else {
|
||||
safestrcatmax(path, "/", len);
|
||||
safestrcatmax(path, SYSFS_CLASS_NAME, len);
|
||||
safestrcatmax(path, "/", len);
|
||||
safestrcatmax(path, classname, len);
|
||||
}
|
||||
safestrcatmax(path, "/", len);
|
||||
safestrcatmax(path, clsdev, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_class_device: Locates a specific class_device and returns it.
|
||||
* Class_device must be closed using sysfs_close_class_device
|
||||
* @classname: Class to search
|
||||
* @name: name of the class_device
|
||||
*
|
||||
* NOTE:
|
||||
* Call sysfs_close_class_device() to close the class device
|
||||
*/
|
||||
struct sysfs_class_device *sysfs_open_class_device
|
||||
(const char *classname, const char *name)
|
||||
{
|
||||
char devpath[SYSFS_PATH_MAX];
|
||||
struct sysfs_class_device *cdev;
|
||||
|
||||
if (!classname || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(devpath, 0, SYSFS_PATH_MAX);
|
||||
if ((get_classdev_path(classname, name, devpath,
|
||||
SYSFS_PATH_MAX)) != 0) {
|
||||
dprintf("Error getting to device %s on class %s\n",
|
||||
name, classname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cdev = sysfs_open_class_device_path(devpath);
|
||||
if (!cdev) {
|
||||
dprintf("Error getting class device %s from class %s\n",
|
||||
name, classname);
|
||||
return NULL;
|
||||
}
|
||||
return cdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_classdev_attr: searches class device's attributes by name
|
||||
* @clsdev: class device to look through
|
||||
* @name: attribute name to get
|
||||
* returns sysfs_attribute reference with success or NULL with error
|
||||
*/
|
||||
struct sysfs_attribute *sysfs_get_classdev_attr
|
||||
(struct sysfs_class_device *clsdev, const char *name)
|
||||
{
|
||||
if (!clsdev || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attribute(clsdev, (char *)name);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_classdev_attributes: gets list of classdev attributes
|
||||
* @clsdev: class device whose attributes list is needed
|
||||
* returns dlist of attributes on success or NULL on error
|
||||
*/
|
||||
struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *clsdev)
|
||||
{
|
||||
if (!clsdev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attributes_list(clsdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_classdev_device: gets the sysfs_device associated with the
|
||||
* given sysfs_class_device
|
||||
* @clsdev: class device whose associated sysfs_device is needed
|
||||
* returns struct sysfs_device * on success or NULL on error
|
||||
*/
|
||||
struct sysfs_device *sysfs_get_classdev_device
|
||||
(struct sysfs_class_device *clsdev)
|
||||
{
|
||||
char linkpath[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
|
||||
|
||||
if (!clsdev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (clsdev->sysdevice)
|
||||
return clsdev->sysdevice;
|
||||
|
||||
memset(linkpath, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(linkpath, clsdev->path);
|
||||
safestrcat(linkpath, "/device");
|
||||
if (!sysfs_path_is_link(linkpath)) {
|
||||
memset(devpath, 0, SYSFS_PATH_MAX);
|
||||
if (!sysfs_get_link(linkpath, devpath, SYSFS_PATH_MAX))
|
||||
clsdev->sysdevice = sysfs_open_device_path(devpath);
|
||||
}
|
||||
return clsdev->sysdevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_class: opens specific class and all its devices on system
|
||||
* returns sysfs_class structure with success or NULL with error.
|
||||
*/
|
||||
struct sysfs_class *sysfs_open_class(const char *name)
|
||||
{
|
||||
struct sysfs_class *cls = NULL;
|
||||
char classpath[SYSFS_PATH_MAX];
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(classpath, 0, SYSFS_PATH_MAX);
|
||||
if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) {
|
||||
dprintf("Sysfs not supported on this system\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We shall now treat "block" also as a class. Hence, check here
|
||||
* if "name" is "block" and proceed accordingly
|
||||
*/
|
||||
if (strcmp(name, SYSFS_BLOCK_NAME) == 0) {
|
||||
safestrcat(classpath, "/");
|
||||
safestrcat(classpath, SYSFS_BLOCK_NAME);
|
||||
} else {
|
||||
safestrcat(classpath, "/");
|
||||
safestrcat(classpath, SYSFS_CLASS_NAME);
|
||||
safestrcat(classpath, "/");
|
||||
safestrcat(classpath, name);
|
||||
}
|
||||
if (sysfs_path_is_dir(classpath)) {
|
||||
dprintf("Class %s not found on the system\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cls = alloc_class();
|
||||
if (cls == NULL) {
|
||||
dprintf("calloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
safestrcpy(cls->name, name);
|
||||
safestrcpy(cls->path, classpath);
|
||||
if ((sysfs_remove_trailing_slash(cls->path)) != 0) {
|
||||
dprintf("Invalid path to class device %s\n", cls->path);
|
||||
sysfs_close_class(cls);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_class_device: get specific class device using the device's id
|
||||
* @cls: sysfs_class to find the device on
|
||||
* @name: name of the class device to look for
|
||||
*
|
||||
* Returns sysfs_class_device * on success and NULL on failure
|
||||
*/
|
||||
struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *cls,
|
||||
const char *name)
|
||||
{
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct sysfs_class_device *cdev = NULL;
|
||||
|
||||
if (!cls || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cls->devices) {
|
||||
cdev = (struct sysfs_class_device *)dlist_find_custom
|
||||
(cls->devices, (void *)name, cdev_name_equal);
|
||||
if (cdev)
|
||||
return cdev;
|
||||
}
|
||||
|
||||
safestrcpy(path, cls->path);
|
||||
safestrcat(path, "/");
|
||||
safestrcat(path, name);
|
||||
cdev = sysfs_open_class_device_path(path);
|
||||
if (!cdev) {
|
||||
dprintf("Error opening class device at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
if (!cls->devices)
|
||||
cls->devices = dlist_new_with_delete
|
||||
(sizeof(struct sysfs_class_device),
|
||||
sysfs_close_cls_dev);
|
||||
|
||||
dlist_unshift_sorted(cls->devices, cdev, sort_list);
|
||||
return cdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class devices to list
|
||||
*/
|
||||
static void add_cdevs_to_classlist(struct sysfs_class *cls, struct dlist *list)
|
||||
{
|
||||
char path[SYSFS_PATH_MAX], *cdev_name;
|
||||
struct sysfs_class_device *cdev = NULL;
|
||||
|
||||
if (cls == NULL || list == NULL)
|
||||
return;
|
||||
|
||||
dlist_for_each_data(list, cdev_name, char) {
|
||||
if (cls->devices) {
|
||||
cdev = (struct sysfs_class_device *)
|
||||
dlist_find_custom(cls->devices,
|
||||
(void *)cdev_name, cdev_name_equal);
|
||||
if (cdev)
|
||||
continue;
|
||||
}
|
||||
safestrcpy(path, cls->path);
|
||||
safestrcat(path, "/");
|
||||
safestrcat(path, cdev_name);
|
||||
cdev = sysfs_open_class_device_path(path);
|
||||
if (cdev) {
|
||||
if (!cls->devices)
|
||||
cls->devices = dlist_new_with_delete
|
||||
(sizeof(struct sysfs_class_device),
|
||||
sysfs_close_cls_dev);
|
||||
dlist_unshift_sorted(cls->devices, cdev,
|
||||
sort_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_class_devices: get all class devices in the given class
|
||||
* @cls: sysfs_class whose devices list is needed
|
||||
*
|
||||
* Returns a dlist of sysfs_class_device * on success and NULL on failure
|
||||
*/
|
||||
struct dlist *sysfs_get_class_devices(struct sysfs_class *cls)
|
||||
{
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct dlist *dirlist, *linklist;
|
||||
|
||||
if (!cls) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Post linux-2.6.14, we have nested classes and links under
|
||||
* /sys/class/xxx/. are also valid class devices
|
||||
*/
|
||||
safestrcpy(path, cls->path);
|
||||
dirlist = read_dir_subdirs(path);
|
||||
if (dirlist) {
|
||||
add_cdevs_to_classlist(cls, dirlist);
|
||||
sysfs_close_list(dirlist);
|
||||
}
|
||||
|
||||
linklist = read_dir_links(path);
|
||||
if (linklist) {
|
||||
add_cdevs_to_classlist(cls, linklist);
|
||||
sysfs_close_list(linklist);
|
||||
}
|
||||
|
||||
return cls->devices;
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* sysfs_device.c
|
||||
*
|
||||
* Generic device 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"
|
||||
|
||||
/**
|
||||
* get_dev_driver: fills in the dev->driver_name field
|
||||
* Returns 0 on SUCCESS and -1 on error
|
||||
*/
|
||||
static int get_dev_driver(struct sysfs_device *dev)
|
||||
{
|
||||
char path[SYSFS_PATH_MAX];
|
||||
char devpath[SYSFS_PATH_MAX];
|
||||
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memset(path, 0, SYSFS_PATH_MAX);
|
||||
memset(devpath, 0, SYSFS_PATH_MAX);
|
||||
safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
|
||||
safestrcatmax(path, "/driver", SYSFS_PATH_MAX);
|
||||
if (!sysfs_path_is_link(path)) {
|
||||
if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
|
||||
if (sysfs_get_name_from_path(devpath,
|
||||
dev->driver_name, SYSFS_NAME_LEN))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_device_bus: retrieves the bus name the device is on, checks path
|
||||
* to bus' link to make sure it has correct device.
|
||||
* @dev: device to get busname.
|
||||
* returns 0 with success and -1 with error.
|
||||
*/
|
||||
int sysfs_get_device_bus(struct sysfs_device *dev)
|
||||
{
|
||||
char devpath[SYSFS_PATH_MAX];
|
||||
char path[SYSFS_PATH_MAX];
|
||||
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(path, 0, SYSFS_PATH_MAX);
|
||||
memset(devpath, 0, SYSFS_PATH_MAX);
|
||||
safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
|
||||
safestrcatmax(path, "/bus", SYSFS_PATH_MAX);
|
||||
if (!sysfs_path_is_link(path)) {
|
||||
if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
|
||||
if (sysfs_get_name_from_path(devpath,
|
||||
dev->bus, SYSFS_NAME_LEN))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_device_tree: closes every device in the supplied tree,
|
||||
* closing children only.
|
||||
* @devroot: device root of tree.
|
||||
*/
|
||||
void sysfs_close_device_tree(struct sysfs_device *devroot)
|
||||
{
|
||||
if (devroot) {
|
||||
if (devroot->children) {
|
||||
struct sysfs_device *child = NULL;
|
||||
|
||||
dlist_for_each_data(devroot->children, child,
|
||||
struct sysfs_device) {
|
||||
sysfs_close_device_tree(child);
|
||||
}
|
||||
}
|
||||
sysfs_close_device(devroot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_device: closes and cleans up a device
|
||||
* @dev = device to clean up
|
||||
*/
|
||||
void sysfs_close_device(struct sysfs_device *dev)
|
||||
{
|
||||
if (dev) {
|
||||
if (dev->parent)
|
||||
sysfs_close_device(dev->parent);
|
||||
if (dev->children && dev->children->count)
|
||||
dlist_destroy(dev->children);
|
||||
if (dev->attrlist)
|
||||
dlist_destroy(dev->attrlist);
|
||||
free(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_device: allocates and initializes device structure
|
||||
* returns struct sysfs_device
|
||||
*/
|
||||
static struct sysfs_device *alloc_device(void)
|
||||
{
|
||||
return (struct sysfs_device *) calloc(1, sizeof(struct sysfs_device));
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_device_path: opens and populates device structure
|
||||
* @path: path to device, this is the /sys/devices/ path
|
||||
* returns sysfs_device structure with success or NULL with error
|
||||
*/
|
||||
struct sysfs_device *sysfs_open_device_path(const char *path)
|
||||
{
|
||||
struct sysfs_device *dev;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_path_is_dir(path)) {
|
||||
dprintf("Incorrect path to device: %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
dev = alloc_device();
|
||||
if (!dev) {
|
||||
dprintf("Error allocating device at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_get_name_from_path(path, dev->bus_id, SYSFS_NAME_LEN)) {
|
||||
errno = EINVAL;
|
||||
dprintf("Error getting device bus_id\n");
|
||||
sysfs_close_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
safestrcpy(dev->path, path);
|
||||
if (sysfs_remove_trailing_slash(dev->path)) {
|
||||
dprintf("Invalid path to device %s\n", dev->path);
|
||||
sysfs_close_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* The "name" attribute no longer exists... return the device's
|
||||
* sysfs representation instead, in the "dev->name" field, which
|
||||
* implies that the dev->name and dev->bus_id contain same data.
|
||||
*/
|
||||
safestrcpy(dev->name, dev->bus_id);
|
||||
|
||||
if (sysfs_get_device_bus(dev))
|
||||
dprintf("Could not get device bus\n");
|
||||
|
||||
if (get_dev_driver(dev)) {
|
||||
dprintf("Could not get device %s's driver\n", dev->bus_id);
|
||||
safestrcpy(dev->driver_name, SYSFS_UNKNOWN);
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_device_attr: searches dev's attributes by name
|
||||
* @dev: device to look through
|
||||
* @name: attribute name to get
|
||||
* returns sysfs_attribute reference with success or NULL with error.
|
||||
*/
|
||||
struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
|
||||
const char *name)
|
||||
{
|
||||
if (!dev || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attribute(dev, (char *)name);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_device_attributes: gets list of device attributes
|
||||
* @dev: device whose attributes list is needed
|
||||
* returns dlist of attributes on success or NULL on error
|
||||
*/
|
||||
struct dlist *sysfs_get_device_attributes(struct sysfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attributes_list(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_device_absolute_path: looks up the bus the device is on, gets
|
||||
* absolute path to the device
|
||||
* @device: device for which path is needed
|
||||
* @path: buffer to store absolute path
|
||||
* @psize: size of "path"
|
||||
* Returns 0 on success -1 on failure
|
||||
*/
|
||||
static int get_device_absolute_path(const char *device, const char *bus,
|
||||
char *path, size_t psize)
|
||||
{
|
||||
char bus_path[SYSFS_PATH_MAX];
|
||||
|
||||
if (!device || !path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(bus_path, 0, SYSFS_PATH_MAX);
|
||||
if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX)) {
|
||||
dprintf ("Sysfs not supported on this system\n");
|
||||
return -1;
|
||||
}
|
||||
safestrcat(bus_path, "/");
|
||||
safestrcat(bus_path, SYSFS_BUS_NAME);
|
||||
safestrcat(bus_path, "/");
|
||||
safestrcat(bus_path, bus);
|
||||
safestrcat(bus_path, "/");
|
||||
safestrcat(bus_path, SYSFS_DEVICES_NAME);
|
||||
safestrcat(bus_path, "/");
|
||||
safestrcat(bus_path, device);
|
||||
/*
|
||||
* We now are at /sys/bus/"bus_name"/devices/"device" which is a link.
|
||||
* Now read this link to reach to the device.
|
||||
*/
|
||||
if (sysfs_get_link(bus_path, path, psize)) {
|
||||
dprintf("Error getting to device %s\n", device);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_device: open a device by id (use the "bus" subsystem)
|
||||
* @bus: bus the device belongs to
|
||||
* @bus_id: bus_id of the device to open - has to be the "bus_id" in
|
||||
* /sys/bus/xxx/devices
|
||||
* returns struct sysfs_device if found, NULL otherwise
|
||||
* NOTE:
|
||||
* 1. Use sysfs_close_device to close the device
|
||||
* 2. Bus the device is on must be supplied
|
||||
* Use sysfs_find_device_bus to get the bus name
|
||||
*/
|
||||
struct sysfs_device *sysfs_open_device(const char *bus, const char *bus_id)
|
||||
{
|
||||
char sysfs_path[SYSFS_PATH_MAX];
|
||||
struct sysfs_device *device;
|
||||
|
||||
if (!bus_id || !bus) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
memset(sysfs_path, 0, SYSFS_PATH_MAX);
|
||||
if (get_device_absolute_path(bus_id, bus, sysfs_path,
|
||||
SYSFS_PATH_MAX)) {
|
||||
dprintf("Error getting to device %s\n", bus_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device = sysfs_open_device_path(sysfs_path);
|
||||
if (!device) {
|
||||
dprintf("Error opening device %s\n", bus_id);
|
||||
return NULL;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_device_parent: opens up given device's parent and returns a
|
||||
* reference to its sysfs_device
|
||||
* @dev: sysfs_device whose parent is requested
|
||||
* Returns sysfs_device of the parent on success and NULL on failure
|
||||
*/
|
||||
struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev)
|
||||
{
|
||||
char ppath[SYSFS_PATH_MAX], dpath[SYSFS_PATH_MAX], *tmp;
|
||||
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dev->parent)
|
||||
return (dev->parent);
|
||||
|
||||
memset(ppath, 0, SYSFS_PATH_MAX);
|
||||
memset(dpath, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(ppath, dev->path);
|
||||
tmp = strrchr(ppath, '/');
|
||||
if (!tmp) {
|
||||
dprintf("Invalid path to device %s\n", ppath);
|
||||
return NULL;
|
||||
}
|
||||
if (*(tmp + 1) == '\0') {
|
||||
*tmp = '\0';
|
||||
tmp = strrchr(tmp, '/');
|
||||
if (tmp == NULL) {
|
||||
dprintf("Invalid path to device %s\n", ppath);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*tmp = '\0';
|
||||
|
||||
/* Make sure we're not at the top of the device tree */
|
||||
sysfs_get_mnt_path(dpath, SYSFS_PATH_MAX);
|
||||
safestrcat(dpath, "/" SYSFS_DEVICES_NAME);
|
||||
if (strcmp(dpath, ppath) == 0) {
|
||||
dprintf("Device at %s does not have a parent\n", dev->path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->parent = sysfs_open_device_path(ppath);
|
||||
if (!dev->parent) {
|
||||
dprintf("Error opening device %s's parent at %s\n",
|
||||
dev->bus_id, ppath);
|
||||
return NULL;
|
||||
}
|
||||
return (dev->parent);
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* sysfs_dir.c
|
||||
*
|
||||
* Directory 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 int sort_char(void *new, void *old)
|
||||
{
|
||||
return ((strncmp((char *)new, (char *)old,
|
||||
strlen((char *)new))) < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_del_name: free function for sysfs_open_subsystem_list
|
||||
* @name: memory area to be freed
|
||||
*/
|
||||
static void sysfs_del_name(void *name)
|
||||
{
|
||||
free(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_del_attribute: routine for dlist integration
|
||||
*/
|
||||
static void sysfs_del_attribute(void *attr)
|
||||
{
|
||||
sysfs_close_attribute((struct sysfs_attribute *)attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* attr_name_equal: compares attributes by name
|
||||
* @a: attribute name for comparison
|
||||
* @b: sysfs_attribute to be compared.
|
||||
* returns 1 if a==b->name or 0 if not equal
|
||||
*/
|
||||
static int attr_name_equal(void *a, void *b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
|
||||
if (strcmp(((char *)a), ((struct sysfs_attribute *)b)->name) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_attribute: closes and cleans up attribute
|
||||
* @sysattr: attribute to close.
|
||||
*/
|
||||
void sysfs_close_attribute(struct sysfs_attribute *sysattr)
|
||||
{
|
||||
if (sysattr) {
|
||||
if (sysattr->value)
|
||||
free(sysattr->value);
|
||||
free(sysattr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_attribute: allocates and initializes attribute structure
|
||||
* returns struct sysfs_attribute with success and NULL with error.
|
||||
*/
|
||||
static struct sysfs_attribute *alloc_attribute(void)
|
||||
{
|
||||
return (struct sysfs_attribute *)
|
||||
calloc(1, sizeof(struct sysfs_attribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_attribute: creates sysfs_attribute structure
|
||||
* @path: path to attribute.
|
||||
* returns sysfs_attribute struct with success and NULL with error.
|
||||
*/
|
||||
struct sysfs_attribute *sysfs_open_attribute(const char *path)
|
||||
{
|
||||
struct sysfs_attribute *sysattr = NULL;
|
||||
struct stat fileinfo;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
sysattr = alloc_attribute();
|
||||
if (!sysattr) {
|
||||
dprintf("Error allocating attribute at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_get_name_from_path(path, sysattr->name,
|
||||
SYSFS_NAME_LEN) != 0) {
|
||||
dprintf("Error retrieving attrib name from path: %s\n", path);
|
||||
sysfs_close_attribute(sysattr);
|
||||
return NULL;
|
||||
}
|
||||
safestrcpy(sysattr->path, path);
|
||||
if ((stat(sysattr->path, &fileinfo)) != 0) {
|
||||
dprintf("Stat failed: No such attribute?\n");
|
||||
sysattr->method = 0;
|
||||
free(sysattr);
|
||||
sysattr = NULL;
|
||||
} else {
|
||||
if (fileinfo.st_mode & S_IRUSR)
|
||||
sysattr->method |= SYSFS_METHOD_SHOW;
|
||||
if (fileinfo.st_mode & S_IWUSR)
|
||||
sysattr->method |= SYSFS_METHOD_STORE;
|
||||
}
|
||||
|
||||
return sysattr;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_read_attribute: reads value from attribute
|
||||
* @sysattr: attribute to read
|
||||
* returns 0 with success and -1 with error.
|
||||
*/
|
||||
int sysfs_read_attribute(struct sysfs_attribute *sysattr)
|
||||
{
|
||||
char *fbuf = NULL;
|
||||
char *vbuf = NULL;
|
||||
ssize_t length = 0;
|
||||
long pgsize = 0;
|
||||
int fd;
|
||||
|
||||
if (!sysattr) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
|
||||
dprintf("Show method not supported for attribute %s\n",
|
||||
sysattr->path);
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
pgsize = getpagesize();
|
||||
fbuf = (char *)calloc(1, pgsize+1);
|
||||
if (!fbuf) {
|
||||
dprintf("calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
|
||||
dprintf("Error reading attribute %s\n", sysattr->path);
|
||||
free(fbuf);
|
||||
return -1;
|
||||
}
|
||||
length = read(fd, fbuf, pgsize);
|
||||
if (length < 0) {
|
||||
dprintf("Error reading from attribute %s\n", sysattr->path);
|
||||
close(fd);
|
||||
free(fbuf);
|
||||
return -1;
|
||||
}
|
||||
if (sysattr->len > 0) {
|
||||
if ((sysattr->len == length) &&
|
||||
(!(strncmp(sysattr->value, fbuf, length)))) {
|
||||
close(fd);
|
||||
free(fbuf);
|
||||
return 0;
|
||||
}
|
||||
free(sysattr->value);
|
||||
}
|
||||
sysattr->len = length;
|
||||
close(fd);
|
||||
vbuf = (char *)realloc(fbuf, length+1);
|
||||
if (!vbuf) {
|
||||
dprintf("realloc failed\n");
|
||||
free(fbuf);
|
||||
return -1;
|
||||
}
|
||||
sysattr->value = vbuf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_write_attribute: write value to the attribute
|
||||
* @sysattr: attribute to write
|
||||
* @new_value: value to write
|
||||
* @len: length of "new_value"
|
||||
* returns 0 with success and -1 with error.
|
||||
*/
|
||||
int sysfs_write_attribute(struct sysfs_attribute *sysattr,
|
||||
const char *new_value, size_t len)
|
||||
{
|
||||
int fd;
|
||||
int length;
|
||||
|
||||
if (!sysattr || !new_value || len == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sysattr->method & SYSFS_METHOD_STORE)) {
|
||||
dprintf ("Store method not supported for attribute %s\n",
|
||||
sysattr->path);
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
if (sysattr->method & SYSFS_METHOD_SHOW) {
|
||||
/*
|
||||
* read attribute again to see if we can get an updated value
|
||||
*/
|
||||
if ((sysfs_read_attribute(sysattr))) {
|
||||
dprintf("Error reading attribute\n");
|
||||
return -1;
|
||||
}
|
||||
if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) {
|
||||
dprintf("Attr %s already has the requested value %s\n",
|
||||
sysattr->name, new_value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* open O_WRONLY since some attributes have no "read" but only
|
||||
* "write" permission
|
||||
*/
|
||||
if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
|
||||
dprintf("Error reading attribute %s\n", sysattr->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = write(fd, new_value, len);
|
||||
if (length < 0) {
|
||||
dprintf("Error writing to the attribute %s - invalid value?\n",
|
||||
sysattr->name);
|
||||
close(fd);
|
||||
return -1;
|
||||
} else if ((unsigned int)length != len) {
|
||||
dprintf("Could not write %zd bytes to attribute %s\n",
|
||||
len, sysattr->name);
|
||||
/*
|
||||
* since we could not write user supplied number of bytes,
|
||||
* restore the old value if one available
|
||||
*/
|
||||
if (sysattr->method & SYSFS_METHOD_SHOW) {
|
||||
length = write(fd, sysattr->value, sysattr->len);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate length that has been copied. Alloc appropriate area
|
||||
* in sysfs_attribute. Verify first if the attribute supports reading
|
||||
* (show method). If it does not, do not bother
|
||||
*/
|
||||
if (sysattr->method & SYSFS_METHOD_SHOW) {
|
||||
if (length != sysattr->len) {
|
||||
sysattr->value = (char *)realloc
|
||||
(sysattr->value, length);
|
||||
sysattr->len = length;
|
||||
safestrcpymax(sysattr->value, new_value, length);
|
||||
} else {
|
||||
/*"length" of the new value is same as old one */
|
||||
safestrcpymax(sysattr->value, new_value, length);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_attribute: open and add attribute at path to given directory
|
||||
* @dev: device whose attribute is to be added
|
||||
* @path: path to attribute
|
||||
* returns pointer to attr added with success and NULL with error.
|
||||
*/
|
||||
static struct sysfs_attribute *add_attribute(void *dev, const char *path)
|
||||
{
|
||||
struct sysfs_attribute *attr;
|
||||
|
||||
attr = sysfs_open_attribute(path);
|
||||
if (!attr) {
|
||||
dprintf("Error opening attribute %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
if (attr->method & SYSFS_METHOD_SHOW) {
|
||||
if (sysfs_read_attribute(attr)) {
|
||||
dprintf("Error reading attribute %s\n", path);
|
||||
sysfs_close_attribute(attr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!((struct sysfs_device *)dev)->attrlist) {
|
||||
((struct sysfs_device *)dev)->attrlist = dlist_new_with_delete
|
||||
(sizeof(struct sysfs_attribute), sysfs_del_attribute);
|
||||
}
|
||||
dlist_unshift_sorted(((struct sysfs_device *)dev)->attrlist,
|
||||
attr, sort_list);
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_attribute - given a sysfs_* struct and a name, return the
|
||||
* sysfs_attribute corresponding to "name"
|
||||
* returns sysfs_attribute on success and NULL on error
|
||||
*/
|
||||
struct sysfs_attribute *get_attribute(void *dev, const char *name)
|
||||
{
|
||||
struct sysfs_attribute *cur = NULL;
|
||||
char path[SYSFS_PATH_MAX];
|
||||
|
||||
if (!dev || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (((struct sysfs_device *)dev)->attrlist) {
|
||||
/* check if attr is already in the list */
|
||||
cur = (struct sysfs_attribute *)dlist_find_custom
|
||||
((((struct sysfs_device *)dev)->attrlist),
|
||||
(void *)name, attr_name_equal);
|
||||
if (cur)
|
||||
return cur;
|
||||
}
|
||||
safestrcpymax(path, ((struct sysfs_device *)dev)->path,
|
||||
SYSFS_PATH_MAX);
|
||||
safestrcatmax(path, "/", SYSFS_PATH_MAX);
|
||||
safestrcatmax(path, name, SYSFS_PATH_MAX);
|
||||
if (!sysfs_path_is_file(path))
|
||||
cur = add_attribute((void *)dev, path);
|
||||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_dir_links: grabs links in a specific directory
|
||||
* @sysdir: sysfs directory to read
|
||||
* returns list of link names with success and NULL with error.
|
||||
*/
|
||||
struct dlist *read_dir_links(const char *path)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
struct dirent *dirent = NULL;
|
||||
char file_path[SYSFS_PATH_MAX], *linkname;
|
||||
struct dlist *linklist = NULL;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
dprintf("Error opening directory %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (0 == strcmp(dirent->d_name, "."))
|
||||
continue;
|
||||
if (0 == strcmp(dirent->d_name, ".."))
|
||||
continue;
|
||||
memset(file_path, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(file_path, path);
|
||||
safestrcat(file_path, "/");
|
||||
safestrcat(file_path, dirent->d_name);
|
||||
if (!sysfs_path_is_link(file_path)) {
|
||||
if (!linklist) {
|
||||
linklist = dlist_new_with_delete
|
||||
(SYSFS_NAME_LEN, sysfs_del_name);
|
||||
if (!linklist) {
|
||||
dprintf("Error creating list\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
linkname = (char *)calloc(1, SYSFS_NAME_LEN);
|
||||
safestrcpymax(linkname, dirent->d_name, SYSFS_NAME_LEN);
|
||||
dlist_unshift_sorted(linklist, linkname, sort_char);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return linklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_dir_subdirs: grabs subdirs in a specific directory
|
||||
* @sysdir: sysfs directory to read
|
||||
* returns list of directory names with success and NULL with error.
|
||||
*/
|
||||
struct dlist *read_dir_subdirs(const char *path)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
struct dirent *dirent = NULL;
|
||||
char file_path[SYSFS_PATH_MAX], *dir_name;
|
||||
struct dlist *dirlist = NULL;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
dprintf("Error opening directory %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (0 == strcmp(dirent->d_name, "."))
|
||||
continue;
|
||||
if (0 == strcmp(dirent->d_name, ".."))
|
||||
continue;
|
||||
memset(file_path, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(file_path, path);
|
||||
safestrcat(file_path, "/");
|
||||
safestrcat(file_path, dirent->d_name);
|
||||
if (!sysfs_path_is_dir(file_path)) {
|
||||
if (!dirlist) {
|
||||
dirlist = dlist_new_with_delete
|
||||
(SYSFS_NAME_LEN, sysfs_del_name);
|
||||
if (!dirlist) {
|
||||
dprintf("Error creating list\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
dir_name = (char *)calloc(1, SYSFS_NAME_LEN);
|
||||
safestrcpymax(dir_name, dirent->d_name, SYSFS_NAME_LEN);
|
||||
dlist_unshift_sorted(dirlist, dir_name, sort_char);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return dirlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_attributes_list: build a list of attributes for the given device
|
||||
* @dev: devices whose attributes list is required
|
||||
* returns dlist of attributes on success and NULL on failure
|
||||
*/
|
||||
struct dlist *get_attributes_list(void *dev)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
struct dirent *dirent = NULL;
|
||||
struct sysfs_attribute *attr = NULL;
|
||||
char file_path[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX];
|
||||
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
memset(path, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(path, ((struct sysfs_device *)dev)->path);
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
dprintf("Error opening directory %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (0 == strcmp(dirent->d_name, "."))
|
||||
continue;
|
||||
if (0 == strcmp(dirent->d_name, ".."))
|
||||
continue;
|
||||
memset(file_path, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(file_path, path);
|
||||
safestrcat(file_path, "/");
|
||||
safestrcat(file_path, dirent->d_name);
|
||||
if (!sysfs_path_is_file(file_path)) {
|
||||
if (((struct sysfs_device *)dev)->attrlist) {
|
||||
/* check if attr is already in the list */
|
||||
attr = (struct sysfs_attribute *)
|
||||
dlist_find_custom
|
||||
((((struct sysfs_device *)dev)->attrlist),
|
||||
(void *)dirent->d_name, attr_name_equal);
|
||||
if (attr)
|
||||
continue;
|
||||
else
|
||||
add_attribute(dev, file_path);
|
||||
} else
|
||||
attr = add_attribute(dev, file_path);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return ((struct sysfs_device *)dev)->attrlist;
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* sysfs_driver.c
|
||||
*
|
||||
* Driver 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_driver_device(void *device)
|
||||
{
|
||||
sysfs_close_device((struct sysfs_device *)device);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_driver: closes driver and deletes device lists too
|
||||
* @driver: driver to close
|
||||
*/
|
||||
void sysfs_close_driver(struct sysfs_driver *driver)
|
||||
{
|
||||
if (driver) {
|
||||
if (driver->devices)
|
||||
dlist_destroy(driver->devices);
|
||||
if (driver->attrlist)
|
||||
dlist_destroy(driver->attrlist);
|
||||
free(driver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_driver: allocates and initializes driver
|
||||
* returns struct sysfs_driver with success and NULL with error.
|
||||
*/
|
||||
static struct sysfs_driver *alloc_driver(void)
|
||||
{
|
||||
return (struct sysfs_driver *)calloc(1, sizeof(struct sysfs_driver));
|
||||
}
|
||||
|
||||
/**
|
||||
* get_driver_bus: gets bus the driver is on
|
||||
* Returns 0 on success and 1 on error
|
||||
*/
|
||||
static int get_driver_bus(struct sysfs_driver *drv)
|
||||
{
|
||||
char drvpath[SYSFS_PATH_MAX], *c = NULL;
|
||||
|
||||
if (!drv) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
safestrcpy(drvpath, drv->path);
|
||||
c = strstr(drvpath, SYSFS_DRIVERS_NAME);
|
||||
if (c == NULL)
|
||||
return 1;
|
||||
*--c = '\0';
|
||||
c = strstr(drvpath, SYSFS_BUS_NAME);
|
||||
if (c == NULL)
|
||||
return 1;
|
||||
c = strstr(c, "/");
|
||||
if (c == NULL)
|
||||
return 1;
|
||||
c++;
|
||||
safestrcpy(drv->bus, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_driver_attr: searches drv's attributes by name
|
||||
* @drv: driver to look through
|
||||
* @name: attribute name to get
|
||||
* returns sysfs_attribute reference with success or NULL with error.
|
||||
*/
|
||||
struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv,
|
||||
const char *name)
|
||||
{
|
||||
if (!drv || !name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attribute(drv, (char *)name);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_driver_attributes: gets list of driver attributes
|
||||
* @dev: driver whose attributes list is needed
|
||||
* returns dlist of attributes on success or NULL on error
|
||||
*/
|
||||
struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *drv)
|
||||
{
|
||||
if (!drv) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return get_attributes_list(drv);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_driver_path: opens and initializes driver structure
|
||||
* @path: path to driver directory
|
||||
* returns struct sysfs_driver with success and NULL with error
|
||||
*/
|
||||
struct sysfs_driver *sysfs_open_driver_path(const char *path)
|
||||
{
|
||||
struct sysfs_driver *driver = NULL;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_path_is_dir(path)) {
|
||||
dprintf("Invalid path to driver: %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
driver = alloc_driver();
|
||||
if (!driver) {
|
||||
dprintf("Error allocating driver at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
if (sysfs_get_name_from_path(path, driver->name, SYSFS_NAME_LEN)) {
|
||||
dprintf("Error getting driver name from path\n");
|
||||
free(driver);
|
||||
return NULL;
|
||||
}
|
||||
safestrcpy(driver->path, path);
|
||||
if (sysfs_remove_trailing_slash(driver->path)) {
|
||||
dprintf("Invalid path to driver %s\n", driver->path);
|
||||
sysfs_close_driver(driver);
|
||||
return NULL;
|
||||
}
|
||||
if (get_driver_bus(driver)) {
|
||||
dprintf("Could not get the bus driver is on\n");
|
||||
sysfs_close_driver(driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_driver_path: looks up the bus the driver is on and builds path to
|
||||
* the driver.
|
||||
* @bus: bus on which to search
|
||||
* @drv: driver to look for
|
||||
* @path: buffer to return path to driver
|
||||
* @psize: size of "path"
|
||||
* Returns 0 on success and -1 on error
|
||||
*/
|
||||
static int get_driver_path(const char *bus, const char *drv,
|
||||
char *path, size_t psize)
|
||||
{
|
||||
if (!bus || !drv || !path || psize == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (sysfs_get_mnt_path(path, psize)) {
|
||||
dprintf("Error getting sysfs mount path\n");
|
||||
return -1;
|
||||
}
|
||||
safestrcatmax(path, "/", psize);
|
||||
safestrcatmax(path, SYSFS_BUS_NAME, psize);
|
||||
safestrcatmax(path, "/", psize);
|
||||
safestrcatmax(path, bus, psize);
|
||||
safestrcatmax(path, "/", psize);
|
||||
safestrcatmax(path, SYSFS_DRIVERS_NAME, psize);
|
||||
safestrcatmax(path, "/", psize);
|
||||
safestrcatmax(path, drv, psize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_driver: open driver by name, given its bus
|
||||
* @bus_name: Name of the bus
|
||||
* @drv_name: Name of the driver
|
||||
* Returns the sysfs_driver reference on success and NULL on failure
|
||||
*/
|
||||
struct sysfs_driver *sysfs_open_driver(const char *bus_name,
|
||||
const char *drv_name)
|
||||
{
|
||||
char path[SYSFS_PATH_MAX];
|
||||
struct sysfs_driver *driver = NULL;
|
||||
|
||||
if (!drv_name || !bus_name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(path, 0, SYSFS_PATH_MAX);
|
||||
if (get_driver_path(bus_name, drv_name, path, SYSFS_PATH_MAX)) {
|
||||
dprintf("Error getting to driver %s\n", drv_name);
|
||||
return NULL;
|
||||
}
|
||||
driver = sysfs_open_driver_path(path);
|
||||
if (!driver) {
|
||||
dprintf("Error opening driver at %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
return driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_driver_devices: gets list of devices that use the driver
|
||||
* @drv: sysfs_driver whose device list is needed
|
||||
* Returns dlist of struct sysfs_device on success and NULL on failure
|
||||
*/
|
||||
struct dlist *sysfs_get_driver_devices(struct sysfs_driver *drv)
|
||||
{
|
||||
char *ln = NULL;
|
||||
struct dlist *linklist = NULL;
|
||||
struct sysfs_device *dev = NULL;
|
||||
|
||||
if (!drv) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
linklist = read_dir_links(drv->path);
|
||||
if (linklist) {
|
||||
dlist_for_each_data(linklist, ln, char) {
|
||||
|
||||
if (!strncmp(ln, SYSFS_MODULE_NAME, strlen(ln)))
|
||||
continue;
|
||||
|
||||
dev = sysfs_open_device(drv->bus, ln);
|
||||
if (!dev) {
|
||||
dprintf("Error opening driver's device\n");
|
||||
sysfs_close_list(linklist);
|
||||
return NULL;
|
||||
}
|
||||
if (!drv->devices) {
|
||||
drv->devices = dlist_new_with_delete
|
||||
(sizeof(struct sysfs_device),
|
||||
sysfs_close_driver_device);
|
||||
if (!drv->devices) {
|
||||
dprintf("Error creating device list\n");
|
||||
sysfs_close_list(linklist);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
dlist_unshift_sorted(drv->devices, dev, sort_list);
|
||||
}
|
||||
sysfs_close_list(linklist);
|
||||
}
|
||||
return drv->devices;
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* sysfs_utils.c
|
||||
*
|
||||
* System 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"
|
||||
|
||||
/**
|
||||
* sysfs_remove_trailing_slash: Removes any trailing '/' in the given path
|
||||
* @path: Path to look for the trailing '/'
|
||||
* Returns 0 on success 1 on error
|
||||
*/
|
||||
int sysfs_remove_trailing_slash(char *path)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = strlen(path);
|
||||
while (len > 0 && path[len-1] == '/')
|
||||
path[--len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs_get_mnt_path: Gets the sysfs mount point.
|
||||
* @mnt_path: place to put "sysfs" mount point
|
||||
* @len: size of mnt_path
|
||||
* returns 0 with success and -1 with error.
|
||||
*/
|
||||
int sysfs_get_mnt_path(char *mnt_path, size_t len)
|
||||
{
|
||||
static char sysfs_path[SYSFS_PATH_MAX] = "";
|
||||
const char *sysfs_path_env;
|
||||
|
||||
/* evaluate only at the first call */
|
||||
if (sysfs_path[0] == '\0') {
|
||||
/* possible overrride of real mount path */
|
||||
sysfs_path_env = getenv(SYSFS_PATH_ENV);
|
||||
if (sysfs_path_env != NULL) {
|
||||
safestrcpymax(mnt_path, sysfs_path_env, len);
|
||||
sysfs_remove_trailing_slash(mnt_path);
|
||||
return 0;
|
||||
}
|
||||
safestrcpymax(mnt_path, SYSFS_MNT_PATH, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_name_from_path: returns last name from a "/" delimited path
|
||||
* @path: path to get name from
|
||||
* @name: where to put name
|
||||
* @len: size of name
|
||||
*/
|
||||
int sysfs_get_name_from_path(const char *path, char *name, size_t len)
|
||||
{
|
||||
char tmp[SYSFS_PATH_MAX];
|
||||
char *n = NULL;
|
||||
|
||||
if (!path || !name || len == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memset(tmp, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(tmp, path);
|
||||
n = strrchr(tmp, '/');
|
||||
if (n == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (*(n+1) == '\0') {
|
||||
*n = '\0';
|
||||
n = strrchr(tmp, '/');
|
||||
if (n == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
safestrcpymax(name, n, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_get_link: returns link source
|
||||
* @path: symbolic link's path
|
||||
* @target: where to put name
|
||||
* @len: size of name
|
||||
*/
|
||||
int sysfs_get_link(const char *path, char *target, size_t len)
|
||||
{
|
||||
char devdir[SYSFS_PATH_MAX];
|
||||
char linkpath[SYSFS_PATH_MAX];
|
||||
char temp_path[SYSFS_PATH_MAX];
|
||||
char *d = NULL, *s = NULL;
|
||||
int slashes = 0, count = 0;
|
||||
|
||||
if (!path || !target || len == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(devdir, 0, SYSFS_PATH_MAX);
|
||||
memset(linkpath, 0, SYSFS_PATH_MAX);
|
||||
memset(temp_path, 0, SYSFS_PATH_MAX);
|
||||
safestrcpy(devdir, path);
|
||||
|
||||
if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
d = linkpath;
|
||||
/*
|
||||
* Three cases here:
|
||||
* 1. relative path => format ../..
|
||||
* 2. absolute path => format /abcd/efgh
|
||||
* 3. relative path _from_ this dir => format abcd/efgh
|
||||
*/
|
||||
switch (*d) {
|
||||
case '.':
|
||||
/*
|
||||
* handle the case where link is of type ./abcd/xxx
|
||||
*/
|
||||
safestrcpy(temp_path, devdir);
|
||||
if (*(d+1) == '/')
|
||||
d += 2;
|
||||
else if (*(d+1) == '.')
|
||||
goto parse_path;
|
||||
s = strrchr(temp_path, '/');
|
||||
if (s != NULL) {
|
||||
*(s+1) = '\0';
|
||||
safestrcat(temp_path, d);
|
||||
} else {
|
||||
safestrcpy(temp_path, d);
|
||||
}
|
||||
safestrcpymax(target, temp_path, len);
|
||||
break;
|
||||
/*
|
||||
* relative path, getting rid of leading "../.."
|
||||
*/
|
||||
parse_path:
|
||||
while (*d == '/' || *d == '.') {
|
||||
if (*d == '/')
|
||||
slashes++;
|
||||
d++;
|
||||
}
|
||||
d--;
|
||||
s = &devdir[strlen(devdir)-1];
|
||||
while (s != NULL && count != (slashes+1)) {
|
||||
s--;
|
||||
if (*s == '/')
|
||||
count++;
|
||||
}
|
||||
safestrcpymax(s, d, (SYSFS_PATH_MAX-strlen(devdir)));
|
||||
safestrcpymax(target, devdir, len);
|
||||
break;
|
||||
case '/':
|
||||
/* absolute path - copy as is */
|
||||
safestrcpymax(target, linkpath, len);
|
||||
break;
|
||||
default:
|
||||
/* relative path from this directory */
|
||||
safestrcpy(temp_path, devdir);
|
||||
s = strrchr(temp_path, '/');
|
||||
if (s != NULL) {
|
||||
*(s+1) = '\0';
|
||||
safestrcat(temp_path, linkpath);
|
||||
} else {
|
||||
safestrcpy(temp_path, linkpath);
|
||||
}
|
||||
safestrcpymax(target, temp_path, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_close_list: generic list free routine
|
||||
* @list: dlist to free
|
||||
* Returns nothing
|
||||
*/
|
||||
void sysfs_close_list(struct dlist *list)
|
||||
{
|
||||
if (list)
|
||||
dlist_destroy(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_open_directory_list: gets a list of all directories under "path"
|
||||
* @path: path to read
|
||||
* Returns a dlist of supported names or NULL no directories (errno is set
|
||||
* in case of error
|
||||
*/
|
||||
struct dlist *sysfs_open_directory_list(const char *path)
|
||||
{
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
return (read_dir_subdirs(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_path_is_dir: Check if the path supplied points to a directory
|
||||
* @path: path to validate
|
||||
* Returns 0 if path points to dir, 1 otherwise
|
||||
*/
|
||||
int sysfs_path_is_dir(const char *path)
|
||||
{
|
||||
struct stat astats;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
if ((lstat(path, &astats)) != 0) {
|
||||
dprintf("stat() failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (S_ISDIR(astats.st_mode))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_path_is_link: Check if the path supplied points to a link
|
||||
* @path: path to validate
|
||||
* Returns 0 if path points to link, 1 otherwise
|
||||
*/
|
||||
int sysfs_path_is_link(const char *path)
|
||||
{
|
||||
struct stat astats;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
if ((lstat(path, &astats)) != 0) {
|
||||
dprintf("stat() failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (S_ISLNK(astats.st_mode))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_path_is_file: Check if the path supplied points to a file
|
||||
* @path: path to validate
|
||||
* Returns 0 if path points to file, 1 otherwise
|
||||
*/
|
||||
int sysfs_path_is_file(const char *path)
|
||||
{
|
||||
struct stat astats;
|
||||
|
||||
if (!path) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
if ((lstat(path, &astats)) != 0) {
|
||||
dprintf("stat() failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (S_ISREG(astats.st_mode))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -23,8 +23,7 @@ make all $MAKEOPTS USE_LOG=false EXTRAS="$EXTRAS" || exit
|
||||
echo -e "\n\n"
|
||||
|
||||
# klibc build
|
||||
[ -z "$KLCC" ] && KLCC=/usr/bin/klcc
|
||||
if [ -e "$KLCC" ]; then
|
||||
if [ -n "$KLCC" -a -e "$KLCC" ]; then
|
||||
echo KLCC: "$KLCC"
|
||||
make clean EXTRAS="$EXTRAS" >/dev/null
|
||||
make all -j4 $MAKEOPTS USE_KLIBC=true DEBUG=true EXTRAS="$EXTRAS" KLCC="$KLCC" || exit
|
||||
|
||||
+29
-22
@@ -243,18 +243,18 @@ EOF
|
||||
desc => "test substitution chars",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "Major:8:minor:3:kernelnumber:3:bus:0:0:0:0" ,
|
||||
exp_name => "Major:8:minor:3:kernelnumber:3" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="Major:%M:minor:%m:kernelnumber:%n:bus:%b"
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="Major:%M:minor:%m:kernelnumber:%n"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
desc => "test substitution chars (with length limit)",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "M8-m3-n3-b0:0-sIBM" ,
|
||||
exp_name => "M8-m3-n3-bsd-sIBM" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="M%M-m%m-n%n-b%3b-s%3s{vendor}"
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="M%M-m%m-n%n-b%2k-s%3s{vendor}"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -322,9 +322,9 @@ EOF
|
||||
desc => "program result substitution",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "test-0:0:0:0" ,
|
||||
exp_name => "test-3" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", PROGRAM=="/bin/echo -n test-%b", RESULT=="test-0:0*", NAME="%c"
|
||||
BUS=="scsi", PROGRAM=="/bin/echo -n test-%n", RESULT=="test-3*", NAME="%c"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -385,27 +385,27 @@ EOF
|
||||
desc => "test substitution by variable name",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "Major:8-minor:3-kernelnumber:3-bus:0:0:0:0" ,
|
||||
exp_name => "Major:8-minor:3-kernelnumber:3" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="Major:\$major-minor:\$minor-kernelnumber:\$number-bus:\$id"
|
||||
BUS=="scsi", ID=="0:0:0:0", NAME="Major:\$major-minor:\$minor-kernelnumber:\$number"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
desc => "test substitution by variable name 2",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "Major:8-minor:3-kernelnumber:3-bus:0:0:0:0" ,
|
||||
exp_name => "Major:8-minor:3-kernelnumber:3-name:sda3" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="Major:\$major-minor:%m-kernelnumber:\$number-bus:%b"
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="Major:\$major-minor:%m-kernelnumber:\$number-name:\$kernel"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
desc => "test substitution by variable name 3",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "830:0:0:03" ,
|
||||
exp_name => "83sda33" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="%M%m%b%n"
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="%M%m%k%n"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -421,9 +421,9 @@ EOF
|
||||
desc => "test substitution by variable name 5",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda/sda3",
|
||||
exp_name => "8330:0:0:0" ,
|
||||
exp_name => "833sda3" ,
|
||||
rules => <<EOF
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="\$major%m%n\$id"
|
||||
BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="\$major%m%n\$kernel"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -470,11 +470,11 @@ EOF
|
||||
desc => "program and bus type match",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda",
|
||||
exp_name => "scsi-0:0:0:0" ,
|
||||
exp_name => "scsi-sda" ,
|
||||
rules => <<EOF
|
||||
BUS=="usb", PROGRAM=="/bin/echo -n usb-%b", NAME="%c"
|
||||
BUS=="scsi", PROGRAM=="/bin/echo -n scsi-%b", NAME="%c"
|
||||
BUS=="foo", PROGRAM=="/bin/echo -n foo-%b", NAME="%c"
|
||||
BUS=="usb", PROGRAM=="/bin/echo -n usb-%k", NAME="%c"
|
||||
BUS=="scsi", PROGRAM=="/bin/echo -n scsi-%k", NAME="%c"
|
||||
BUS=="foo", PROGRAM=="/bin/echo -n foo-%k", NAME="%c"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -797,7 +797,7 @@ EOF
|
||||
exp_name => "symlink2-ttyUSB0",
|
||||
exp_target => "ttyUSB0",
|
||||
rules => <<EOF
|
||||
KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="symlink1-%n symlink2-%k symlink3-%b"
|
||||
KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="symlink1-%n symlink2-%k"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -918,13 +918,13 @@ KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="major-%M:%m"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
desc => "symlink %b substitution",
|
||||
desc => "symlink %k substitution",
|
||||
subsys => "block",
|
||||
devpath => "/block/sda",
|
||||
exp_name => "symlink-0:0:0:0",
|
||||
exp_name => "symlink-sda",
|
||||
exp_target => "node",
|
||||
rules => <<EOF
|
||||
BUS=="scsi", KERNEL=="sda", NAME="node", SYMLINK+="symlink-%b"
|
||||
BUS=="scsi", KERNEL=="sda", NAME="node", SYMLINK+="symlink-%k"
|
||||
EOF
|
||||
},
|
||||
{
|
||||
@@ -1564,6 +1564,13 @@ sub udev {
|
||||
|
||||
$ENV{DEVPATH} = $devpath;
|
||||
|
||||
open my $fd, "$sysfs$devpath/dev";
|
||||
my $dev = <$fd>;
|
||||
close $fd;
|
||||
$dev =~ m/^(.+):(.+)$/;
|
||||
$ENV{MAJOR} = $1;
|
||||
$ENV{MINOR} = $2;
|
||||
|
||||
# create temporary rules
|
||||
open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules";
|
||||
print CONF $$rules;
|
||||
|
||||
@@ -30,13 +30,8 @@
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "udev_rules.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef USE_LOG
|
||||
void log_message(int priority, const char *format, ...)
|
||||
@@ -65,7 +60,9 @@ static void asmlinkage sig_handler(int signum)
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
struct udevice udev;
|
||||
struct sysfs_device *dev;
|
||||
struct udevice *udev;
|
||||
const char *maj, *min;
|
||||
struct udev_rules rules;
|
||||
const char *action;
|
||||
const char *devpath;
|
||||
@@ -95,7 +92,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
logging_init("udev");
|
||||
if (devnull < 0)
|
||||
err("fatal, could not open /dev/null: %s", strerror(errno));
|
||||
udev_init_config();
|
||||
udev_config_init();
|
||||
dbg("version %s", UDEV_VERSION);
|
||||
|
||||
/* set signal handlers */
|
||||
@@ -114,10 +111,10 @@ int main(int argc, char *argv[], char *envp[])
|
||||
devpath = getenv("DEVPATH");
|
||||
subsystem = getenv("SUBSYSTEM");
|
||||
/* older kernels passed the SUBSYSTEM only as argument */
|
||||
if (!subsystem && argc == 2)
|
||||
if (subsystem == NULL && argc == 2)
|
||||
subsystem = argv[1];
|
||||
|
||||
if (!action || !subsystem || !devpath) {
|
||||
if (action == NULL || subsystem == NULL || devpath == NULL) {
|
||||
err("action, subsystem or devpath missing");
|
||||
goto exit;
|
||||
}
|
||||
@@ -130,25 +127,49 @@ int main(int argc, char *argv[], char *envp[])
|
||||
setenv("UDEV_LOG", priority, 1);
|
||||
}
|
||||
|
||||
udev_init_device(&udev, devpath, subsystem, action);
|
||||
sysfs_init();
|
||||
udev_rules_init(&rules, 0);
|
||||
|
||||
retval = udev_process_event(&rules, &udev);
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL) {
|
||||
info("unable to open '%s'", devpath);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!retval && udev_run && !list_empty(&udev.run_list)) {
|
||||
udev = udev_device_init();
|
||||
if (udev == NULL)
|
||||
goto fail;
|
||||
|
||||
/* override built-in sysfs device */
|
||||
udev->dev = dev;
|
||||
strlcpy(udev->action, action, sizeof(udev->action));
|
||||
|
||||
/* get dev_t from environment, which is needed for "remove" to work, "add" works also from sysfs */
|
||||
maj = getenv("MAJOR");
|
||||
min = getenv("MINOR");
|
||||
if (maj != NULL && min != NULL)
|
||||
udev->devt = makedev(atoi(maj), atoi(min));
|
||||
else
|
||||
udev->devt = udev_device_get_devt(udev);
|
||||
|
||||
retval = udev_device_event(&rules, udev);
|
||||
|
||||
if (!retval && udev_run && !list_empty(&udev->run_list)) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
dbg("executing run list");
|
||||
list_for_each_entry(name_loop, &udev.run_list, node) {
|
||||
list_for_each_entry(name_loop, &udev->run_list, node) {
|
||||
if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0)
|
||||
pass_env_to_socket(&name_loop->name[strlen("socket:")], devpath, action);
|
||||
else
|
||||
run_program(name_loop->name, udev.subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO));
|
||||
run_program(name_loop->name, udev->dev->subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO));
|
||||
}
|
||||
}
|
||||
|
||||
udev_rules_close(&rules);
|
||||
udev_cleanup_device(&udev);
|
||||
udev_device_cleanup(udev);
|
||||
fail:
|
||||
udev_rules_cleanup(&rules);
|
||||
sysfs_cleanup();
|
||||
|
||||
exit:
|
||||
logging_close();
|
||||
|
||||
@@ -24,8 +24,11 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "logging.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev_version.h"
|
||||
|
||||
#define COMMENT_CHARACTER '#'
|
||||
#define PATH_TO_NAME_CHAR '@'
|
||||
@@ -39,24 +42,32 @@
|
||||
#define DEFAULT_PARTITIONS_COUNT 15
|
||||
#define UDEV_ALARM_TIMEOUT 180
|
||||
|
||||
#define UDEV_MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
/* pipes */
|
||||
#define READ_END 0
|
||||
#define WRITE_END 1
|
||||
|
||||
#define DB_DIR ".udev/db"
|
||||
|
||||
struct udev_rules;
|
||||
|
||||
enum device_type {
|
||||
DEV_UNKNOWN,
|
||||
DEV_CLASS,
|
||||
DEV_BLOCK,
|
||||
DEV_NET,
|
||||
DEV_DEVICE,
|
||||
struct sysfs_device {
|
||||
struct list_head node; /* for device cache */
|
||||
char devpath[PATH_SIZE];
|
||||
char subsystem[NAME_SIZE]; /* $class/$bus/"drivers */
|
||||
char kernel_name[NAME_SIZE]; /* device instance name */
|
||||
char kernel_number[NAME_SIZE];
|
||||
char driver[NAME_SIZE]; /* device driver name */
|
||||
};
|
||||
|
||||
struct udevice {
|
||||
char devpath[PATH_SIZE];
|
||||
char subsystem[NAME_SIZE];
|
||||
/* device event */
|
||||
struct sysfs_device *dev;
|
||||
struct sysfs_device dev_local;
|
||||
char action[NAME_SIZE];
|
||||
|
||||
enum device_type type;
|
||||
/* node */
|
||||
char name[PATH_SIZE];
|
||||
struct list_head symlink_list;
|
||||
int symlink_final;
|
||||
@@ -67,42 +78,83 @@ struct udevice {
|
||||
mode_t mode;
|
||||
int mode_final;
|
||||
dev_t devt;
|
||||
|
||||
/* event processing */
|
||||
struct list_head run_list;
|
||||
int run_final;
|
||||
struct list_head env_list;
|
||||
|
||||
char tmp_node[PATH_SIZE];
|
||||
int partitions;
|
||||
int ignore_device;
|
||||
int ignore_remove;
|
||||
char bus_id[NAME_SIZE];
|
||||
char program_result[PATH_SIZE];
|
||||
char kernel_number[NAME_SIZE];
|
||||
char kernel_name[NAME_SIZE];
|
||||
int test_run;
|
||||
};
|
||||
|
||||
extern int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action);
|
||||
extern void udev_cleanup_device(struct udevice *udev);
|
||||
extern dev_t get_devt(struct sysfs_class_device *class_dev);
|
||||
extern int udev_process_event(struct udev_rules *rules, struct udevice *udev);
|
||||
extern int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev);
|
||||
extern int udev_remove_device(struct udevice *udev);
|
||||
extern void udev_init_config(void);
|
||||
extern int udev_start(void);
|
||||
extern int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid);
|
||||
/* udev_config.c */
|
||||
extern char udev_root[PATH_SIZE];
|
||||
extern char udev_config_filename[PATH_SIZE];
|
||||
extern char udev_rules_filename[PATH_SIZE];
|
||||
extern int udev_log_priority;
|
||||
extern int udev_run;
|
||||
extern void udev_config_init(void);
|
||||
|
||||
/* udev_device.c */
|
||||
extern struct udevice *udev_device_init(void);
|
||||
extern void udev_device_cleanup(struct udevice *udev);
|
||||
extern int udev_device_event(struct udev_rules *rules, struct udevice *udev);
|
||||
extern dev_t udev_device_get_devt(struct udevice *udev);
|
||||
|
||||
/* udev_sysfs.c */
|
||||
extern char sysfs_path[PATH_SIZE];
|
||||
extern int sysfs_init(void);
|
||||
extern void sysfs_cleanup(void);
|
||||
extern void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, const char *subsystem);
|
||||
extern struct sysfs_device *sysfs_device_get(const char *devpath);
|
||||
extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev);
|
||||
extern char *sysfs_attr_get_value(const char *devpath, const char *attr_name);
|
||||
|
||||
/* udev_add.c / udev_remove.c */
|
||||
extern int udev_add_device(struct udevice *udev);
|
||||
extern int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid);
|
||||
extern int udev_remove_device(struct udevice *udev);
|
||||
|
||||
/* udev_db.c */
|
||||
extern int udev_db_add_device(struct udevice *dev);
|
||||
extern int udev_db_delete_device(struct udevice *dev);
|
||||
extern int udev_db_get_device(struct udevice *udev, const char *devpath);
|
||||
extern int udev_db_lookup_name(const char *name, char *devpath, size_t len);
|
||||
extern int udev_db_get_all_entries(struct list_head *name_list);
|
||||
|
||||
extern char sysfs_path[PATH_SIZE];
|
||||
extern char udev_root[PATH_SIZE];
|
||||
extern char udev_config_filename[PATH_SIZE];
|
||||
extern char udev_rules_filename[PATH_SIZE];
|
||||
extern int udev_log_priority;
|
||||
extern int udev_run;
|
||||
/* udev_utils.c */
|
||||
struct name_entry {
|
||||
struct list_head node;
|
||||
char name[PATH_SIZE];
|
||||
};
|
||||
extern int log_priority(const char *priority);
|
||||
extern int name_list_add(struct list_head *name_list, const char *name, int sort);
|
||||
extern int name_list_key_add(struct list_head *name_list, const char *key, const char *value);
|
||||
extern void name_list_cleanup(struct list_head *name_list);
|
||||
extern int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix);
|
||||
|
||||
/* udev_utils_string.c */
|
||||
extern int strcmp_pattern(const char *p, const char *s);
|
||||
extern int string_is_true(const char *str);
|
||||
extern void remove_trailing_chars(char *path, char c);
|
||||
extern int utf8_encoded_valid_unichar(const char *str);
|
||||
extern int replace_untrusted_chars(char *str);
|
||||
|
||||
/* udev_utils_file.c */
|
||||
extern int create_path(const char *path);
|
||||
extern int delete_path(const char *path);
|
||||
extern int file_map(const char *filename, char **buf, size_t *bufsize);
|
||||
extern void file_unmap(void *buf, size_t bufsize);
|
||||
extern int unlink_secure(const char *filename);
|
||||
extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur);
|
||||
|
||||
/* udev_utils_run.c */
|
||||
extern int pass_env_to_socket(const char *name, const char *devpath, const char *action);
|
||||
extern int run_program(const char *command, const char *subsystem,
|
||||
char *result, size_t ressize, size_t *reslen, int log);
|
||||
|
||||
#endif
|
||||
|
||||
+21
-33
@@ -22,6 +22,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -29,16 +30,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <grp.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/sockios.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
#include "udev_rules.h"
|
||||
#include "udev_selinux.h"
|
||||
|
||||
@@ -48,25 +43,18 @@ int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mo
|
||||
struct stat stats;
|
||||
int retval = 0;
|
||||
|
||||
switch (udev->type) {
|
||||
case DEV_BLOCK:
|
||||
if (major(devt) != 0 && strcmp(udev->dev->subsystem, "block") == 0)
|
||||
mode |= S_IFBLK;
|
||||
break;
|
||||
case DEV_CLASS:
|
||||
else
|
||||
mode |= S_IFCHR;
|
||||
break;
|
||||
default:
|
||||
dbg("unknown node type %c\n", udev->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (stat(file, &stats) != 0)
|
||||
goto create;
|
||||
|
||||
/* preserve node with already correct numbers, to not change the inode number */
|
||||
/* preserve node with already correct numbers, to prevent changing the inode number */
|
||||
if ((stats.st_mode & S_IFMT) == (mode & S_IFMT) && (stats.st_rdev == devt)) {
|
||||
info("preserve file '%s', cause it has correct dev_t", file);
|
||||
selinux_setfilecon(file, udev->kernel_name, stats.st_mode);
|
||||
selinux_setfilecon(file, udev->dev->kernel_name, stats.st_mode);
|
||||
goto perms;
|
||||
}
|
||||
|
||||
@@ -76,7 +64,7 @@ int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mo
|
||||
dbg("already present file '%s' unlinked", file);
|
||||
|
||||
create:
|
||||
selinux_setfscreatecon(file, udev->kernel_name, mode);
|
||||
selinux_setfscreatecon(file, udev->dev->kernel_name, mode);
|
||||
retval = mknod(file, mode, devt);
|
||||
selinux_resetfscreatecon();
|
||||
if (retval != 0) {
|
||||
@@ -105,7 +93,7 @@ exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int create_node(struct udevice *udev, struct sysfs_class_device *class_dev)
|
||||
static int create_node(struct udevice *udev)
|
||||
{
|
||||
char filename[PATH_SIZE];
|
||||
struct name_entry *name_loop;
|
||||
@@ -161,13 +149,13 @@ static int create_node(struct udevice *udev, struct sysfs_class_device *class_de
|
||||
/* create all_partitions if requested */
|
||||
if (udev->partitions) {
|
||||
char partitionname[PATH_SIZE];
|
||||
struct sysfs_attribute *attr;
|
||||
char *attr;
|
||||
int range;
|
||||
|
||||
/* take the maximum registered minor range */
|
||||
attr = sysfs_get_classdev_attr(class_dev, "range");
|
||||
attr = sysfs_attr_get_value(udev->dev->devpath, "range");
|
||||
if (attr) {
|
||||
range = atoi(attr->value);
|
||||
range = atoi(attr);
|
||||
if (range > 1)
|
||||
udev->partitions = range-1;
|
||||
}
|
||||
@@ -247,7 +235,7 @@ static int rename_net_if(struct udevice *udev)
|
||||
struct ifreq ifr;
|
||||
int retval;
|
||||
|
||||
info("changing net interface name from '%s' to '%s'", udev->kernel_name, udev->name);
|
||||
info("changing net interface name from '%s' to '%s'", udev->dev->kernel_name, udev->name);
|
||||
if (udev->test_run)
|
||||
return 0;
|
||||
|
||||
@@ -258,7 +246,7 @@ static int rename_net_if(struct udevice *udev)
|
||||
}
|
||||
|
||||
memset(&ifr, 0x00, sizeof(struct ifreq));
|
||||
strlcpy(ifr.ifr_name, udev->kernel_name, IFNAMSIZ);
|
||||
strlcpy(ifr.ifr_name, udev->dev->kernel_name, IFNAMSIZ);
|
||||
strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ);
|
||||
|
||||
retval = ioctl(sk, SIOCSIFNAME, &ifr);
|
||||
@@ -269,7 +257,7 @@ static int rename_net_if(struct udevice *udev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
|
||||
int udev_add_device(struct udevice *udev)
|
||||
{
|
||||
char *pos;
|
||||
int retval = 0;
|
||||
@@ -277,16 +265,16 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
|
||||
dbg("adding name='%s'", udev->name);
|
||||
selinux_init();
|
||||
|
||||
if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
|
||||
retval = create_node(udev, class_dev);
|
||||
if (major(udev->devt)) {
|
||||
retval = create_node(udev);
|
||||
if (retval != 0)
|
||||
goto exit;
|
||||
|
||||
if (udev_db_add_device(udev) != 0)
|
||||
dbg("udev_db_add_dev failed, remove might not work for custom names");
|
||||
} else if (udev->type == DEV_NET) {
|
||||
} else if (strcmp(udev->dev->subsystem, "net") == 0) {
|
||||
/* look if we want to change the name of the netif */
|
||||
if (strcmp(udev->name, udev->kernel_name) != 0) {
|
||||
if (strcmp(udev->name, udev->dev->kernel_name) != 0) {
|
||||
retval = rename_net_if(udev);
|
||||
if (retval != 0)
|
||||
goto exit;
|
||||
@@ -296,12 +284,12 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
|
||||
* original kernel name sleeps with the fishes and we don't
|
||||
* get an event from the kernel with the new name
|
||||
*/
|
||||
pos = strrchr(udev->devpath, '/');
|
||||
pos = strrchr(udev->dev->devpath, '/');
|
||||
if (pos != NULL) {
|
||||
pos[1] = '\0';
|
||||
strlcat(udev->devpath, udev->name, sizeof(udev->devpath));
|
||||
strlcpy(udev->kernel_name, udev->name, sizeof(udev->kernel_name));
|
||||
setenv("DEVPATH", udev->devpath, 1);
|
||||
strlcat(udev->dev->devpath, udev->name, sizeof(udev->dev->devpath));
|
||||
strlcpy(udev->dev->kernel_name, udev->name, sizeof(udev->dev->kernel_name));
|
||||
setenv("DEVPATH", udev->dev->devpath, 1);
|
||||
setenv("INTERFACE", udev->name, 1);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-9
@@ -28,15 +28,9 @@
|
||||
#include <ctype.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* global variables */
|
||||
char sysfs_path[PATH_SIZE];
|
||||
char udev_root[PATH_SIZE];
|
||||
char udev_config_filename[PATH_SIZE];
|
||||
char udev_rules_filename[PATH_SIZE];
|
||||
@@ -168,7 +162,7 @@ static int parse_config_file(void)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void udev_init_config(void)
|
||||
void udev_config_init(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
@@ -177,7 +171,6 @@ void udev_init_config(void)
|
||||
strcpy(udev_rules_filename, UDEV_RULES_FILE);
|
||||
udev_log_priority = LOG_ERR;
|
||||
udev_run = 1;
|
||||
sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path));
|
||||
|
||||
/* disable RUN key execution */
|
||||
env = getenv("UDEV_RUN");
|
||||
@@ -202,7 +195,6 @@ void udev_init_config(void)
|
||||
if (env)
|
||||
udev_log_priority = log_priority(env);
|
||||
|
||||
dbg("sysfs_path='%s'", sysfs_path);
|
||||
dbg("UDEV_CONFIG_FILE='%s'", udev_config_filename);
|
||||
dbg("udev_root='%s'", udev_root);
|
||||
dbg("udev_rules='%s'", udev_rules_filename);
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
static int devpath_to_db_path(const char *devpath, char *filename, size_t len)
|
||||
@@ -83,21 +79,21 @@ int udev_db_add_device(struct udevice *udev)
|
||||
/* don't write anything if udev created only the node with the
|
||||
* kernel name without any interesting data to remember
|
||||
*/
|
||||
if (strcmp(udev->name, udev->kernel_name) == 0 &&
|
||||
if (strcmp(udev->name, udev->dev->kernel_name) == 0 &&
|
||||
list_empty(&udev->symlink_list) && list_empty(&udev->env_list) &&
|
||||
!udev->partitions && !udev->ignore_remove) {
|
||||
dbg("nothing interesting to store in udevdb, skip");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
devpath_to_db_path(udev->devpath, filename, sizeof(filename));
|
||||
devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename));
|
||||
create_path(filename);
|
||||
f = fopen(filename, "w");
|
||||
if (f == NULL) {
|
||||
err("unable to create db file '%s': %s", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
dbg("storing data for device '%s' in '%s'", udev->devpath, filename);
|
||||
dbg("storing data for device '%s' in '%s'", udev->dev->devpath, filename);
|
||||
|
||||
fprintf(f, "N:%s\n", udev->name);
|
||||
list_for_each_entry(name_loop, &udev->symlink_list, node)
|
||||
@@ -132,7 +128,7 @@ int udev_db_get_device(struct udevice *udev, const char *devpath)
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(udev->devpath, devpath, sizeof(udev->devpath));
|
||||
strlcpy(udev->dev->devpath, devpath, sizeof(udev->dev->devpath));
|
||||
cur = 0;
|
||||
while (cur < bufsize) {
|
||||
count = buf_get_line(buf, bufsize, cur);
|
||||
@@ -196,7 +192,7 @@ int udev_db_delete_device(struct udevice *udev)
|
||||
{
|
||||
char filename[PATH_SIZE];
|
||||
|
||||
devpath_to_db_path(udev->devpath, filename, sizeof(filename));
|
||||
devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename));
|
||||
unlink(filename);
|
||||
|
||||
return 0;
|
||||
|
||||
+76
-60
@@ -26,83 +26,99 @@
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
#include "udev_rules.h"
|
||||
|
||||
|
||||
int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action)
|
||||
struct udevice *udev_device_init(void)
|
||||
{
|
||||
char *pos;
|
||||
struct udevice *udev;
|
||||
|
||||
udev = malloc(sizeof(struct udevice));
|
||||
if (udev == NULL)
|
||||
return NULL;
|
||||
memset(udev, 0x00, sizeof(struct udevice));
|
||||
|
||||
INIT_LIST_HEAD(&udev->symlink_list);
|
||||
INIT_LIST_HEAD(&udev->run_list);
|
||||
INIT_LIST_HEAD(&udev->env_list);
|
||||
|
||||
if (subsystem)
|
||||
strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem));
|
||||
/* set sysfs device to local storage, can be overridden if needed */
|
||||
udev->dev = &udev->dev_local;
|
||||
|
||||
if (action)
|
||||
strlcpy(udev->action, action, sizeof(udev->action));
|
||||
/* default node permissions */
|
||||
udev->mode = 0660;
|
||||
strcpy(udev->owner, "root");
|
||||
strcpy(udev->group, "root");
|
||||
|
||||
if (devpath) {
|
||||
strlcpy(udev->devpath, devpath, sizeof(udev->devpath));
|
||||
remove_trailing_chars(udev->devpath, '/');
|
||||
|
||||
if (strncmp(udev->devpath, "/block/", 7) == 0)
|
||||
udev->type = DEV_BLOCK;
|
||||
else if (strncmp(udev->devpath, "/class/net/", 11) == 0)
|
||||
udev->type = DEV_NET;
|
||||
else if (strncmp(udev->devpath, "/class/", 7) == 0)
|
||||
udev->type = DEV_CLASS;
|
||||
else if (strncmp(udev->devpath, "/devices/", 9) == 0)
|
||||
udev->type = DEV_DEVICE;
|
||||
|
||||
/* get kernel name */
|
||||
pos = strrchr(udev->devpath, '/');
|
||||
if (pos) {
|
||||
strlcpy(udev->kernel_name, &pos[1], sizeof(udev->kernel_name));
|
||||
dbg("kernel_name='%s'", udev->kernel_name);
|
||||
|
||||
/* Some block devices have '!' in their name, change that to '/' */
|
||||
pos = udev->kernel_name;
|
||||
while (pos[0] != '\0') {
|
||||
if (pos[0] == '!')
|
||||
pos[0] = '/';
|
||||
pos++;
|
||||
}
|
||||
|
||||
/* get kernel number */
|
||||
pos = &udev->kernel_name[strlen(udev->kernel_name)];
|
||||
while (isdigit(pos[-1]))
|
||||
pos--;
|
||||
strlcpy(udev->kernel_number, pos, sizeof(udev->kernel_number));
|
||||
dbg("kernel_number='%s'", udev->kernel_number);
|
||||
}
|
||||
}
|
||||
|
||||
if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
|
||||
udev->mode = 0660;
|
||||
strcpy(udev->owner, "root");
|
||||
strcpy(udev->group, "root");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return udev;
|
||||
}
|
||||
|
||||
void udev_cleanup_device(struct udevice *udev)
|
||||
void udev_device_cleanup(struct udevice *udev)
|
||||
{
|
||||
name_list_cleanup(&udev->symlink_list);
|
||||
name_list_cleanup(&udev->run_list);
|
||||
name_list_cleanup(&udev->env_list);
|
||||
free(udev);
|
||||
}
|
||||
|
||||
dev_t udev_device_get_devt(struct udevice *udev)
|
||||
{
|
||||
const char *attr;
|
||||
unsigned int major, minor;
|
||||
|
||||
/* read it from sysfs */
|
||||
attr = sysfs_attr_get_value(udev->dev->devpath, "dev");
|
||||
if (attr != NULL) {
|
||||
if (sscanf(attr, "%u:%u", &major, &minor) == 2)
|
||||
return makedev(major, minor);
|
||||
}
|
||||
return makedev(0, 0);
|
||||
}
|
||||
|
||||
int udev_device_event(struct udev_rules *rules, struct udevice *udev)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* device node or netif */
|
||||
if ((major(udev->devt) != 0 || strcmp(udev->dev->subsystem, "net") == 0) &&
|
||||
strcmp(udev->action, "add") == 0) {
|
||||
dbg("device node or netif add '%s'", udev->dev->devpath);
|
||||
udev_rules_get_name(rules, udev);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
return 0;
|
||||
}
|
||||
/* create node, store in db */
|
||||
if (udev->name[0] != '\0')
|
||||
retval = udev_add_device(udev);
|
||||
else
|
||||
info("device node creation supressed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (major(udev->devt) != 0 && strcmp(udev->action, "remove") == 0) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
udev_rules_get_run(rules, udev);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
return 0;
|
||||
}
|
||||
/* get data from db, remove db-entry, delete node */
|
||||
retval = udev_remove_device(udev);
|
||||
|
||||
/* restore stored persistent data */
|
||||
list_for_each_entry(name_loop, &udev->env_list, node)
|
||||
putenv(name_loop->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* default devices */
|
||||
udev_rules_get_run(rules, udev);
|
||||
if (udev->ignore_device)
|
||||
info("device event will be ignored");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* udev_event.c - udev event process
|
||||
*
|
||||
* Copyright (C) 2004, 2005 Kay Sievers <kay.sievers@vrfy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation version 2 of the License.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_rules.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
dev_t get_devt(struct sysfs_class_device *class_dev)
|
||||
{
|
||||
struct sysfs_attribute *attr = NULL;
|
||||
unsigned int major, minor;
|
||||
char *maj, *min;
|
||||
|
||||
maj = getenv("MAJOR");
|
||||
min = getenv("MINOR");
|
||||
|
||||
if (maj && min) {
|
||||
major = atoi(maj);
|
||||
minor = atoi(min);
|
||||
} else {
|
||||
attr = sysfs_get_classdev_attr(class_dev, "dev");
|
||||
if (attr == NULL)
|
||||
return 0;
|
||||
dbg("dev='%s'", attr->value);
|
||||
|
||||
if (sscanf(attr->value, "%u:%u", &major, &minor) != 2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg("found major=%d, minor=%d", major, minor);
|
||||
return makedev(major, minor);
|
||||
}
|
||||
|
||||
int udev_process_event(struct udev_rules *rules, struct udevice *udev)
|
||||
{
|
||||
int retval;
|
||||
char path[PATH_SIZE];
|
||||
|
||||
if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS || udev->type == DEV_NET) {
|
||||
/* handle device node */
|
||||
if (strcmp(udev->action, "add") == 0) {
|
||||
struct sysfs_class_device *class_dev;
|
||||
|
||||
dbg("node add");
|
||||
snprintf(path, sizeof(path), "%s%s", sysfs_path, udev->devpath);
|
||||
path[sizeof(path)-1] = '\0';
|
||||
class_dev = sysfs_open_class_device_path(path);
|
||||
if (class_dev == NULL) {
|
||||
dbg("open class device failed");
|
||||
return 0;
|
||||
}
|
||||
dbg("opened class_dev->name='%s'", class_dev->name);
|
||||
|
||||
/* get major/minor */
|
||||
if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS)
|
||||
udev->devt = get_devt(class_dev);
|
||||
|
||||
if (udev->type == DEV_NET || udev->devt) {
|
||||
/* name device */
|
||||
udev_rules_get_name(rules, udev, class_dev);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
sysfs_close_class_device(class_dev);
|
||||
return 0;
|
||||
}
|
||||
if (udev->name[0] != '\0') {
|
||||
/* create node, store in db */
|
||||
retval = udev_add_device(udev, class_dev);
|
||||
} else {
|
||||
info("device node creation supressed");
|
||||
}
|
||||
} else {
|
||||
dbg("no dev-file found");
|
||||
udev_rules_get_run(rules, udev, class_dev, NULL);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
sysfs_close_class_device(class_dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sysfs_close_class_device(class_dev);
|
||||
} else if (strcmp(udev->action, "remove") == 0) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
/* get data from db, remove db-entry, delete node */
|
||||
dbg("node remove");
|
||||
retval = udev_remove_device(udev);
|
||||
|
||||
/* restore stored persistent data */
|
||||
list_for_each_entry(name_loop, &udev->env_list, node)
|
||||
putenv(name_loop->name);
|
||||
|
||||
udev_rules_get_run(rules, udev, NULL, NULL);
|
||||
if (udev->ignore_device) {
|
||||
dbg("device event will be ignored");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else if (udev->type == DEV_DEVICE && strcmp(udev->action, "add") == 0) {
|
||||
struct sysfs_device *devices_dev;
|
||||
|
||||
dbg("devices add");
|
||||
snprintf(path, sizeof(path), "%s%s", sysfs_path, udev->devpath);
|
||||
path[sizeof(path)-1] = '\0';
|
||||
devices_dev = sysfs_open_device_path(path);
|
||||
if (!devices_dev) {
|
||||
dbg("devices device unavailable (probably remove has beaten us)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg("devices device opened '%s'", path);
|
||||
udev_rules_get_run(rules, udev, NULL, devices_dev);
|
||||
sysfs_close_device(devices_dev);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
dbg("default handling");
|
||||
udev_rules_get_run(rules, udev, NULL, NULL);
|
||||
if (udev->ignore_device) {
|
||||
info("device event will be ignored");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -27,10 +27,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef __KLIBC__
|
||||
#define __OWN_USERDB_PARSER__
|
||||
|
||||
+4
-8
@@ -28,11 +28,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
|
||||
static int delete_node(struct udevice *udev)
|
||||
{
|
||||
@@ -112,10 +108,10 @@ static int delete_node(struct udevice *udev)
|
||||
*/
|
||||
int udev_remove_device(struct udevice *udev)
|
||||
{
|
||||
if (udev->type != DEV_BLOCK && udev->type != DEV_CLASS)
|
||||
if (major(udev->devt) == 0)
|
||||
return 0;
|
||||
|
||||
if (udev_db_get_device(udev, udev->devpath) == 0) {
|
||||
if (udev_db_get_device(udev, udev->dev->devpath) == 0) {
|
||||
if (udev->ignore_remove) {
|
||||
dbg("remove event for '%s' requested to be ignored by rule", udev->name);
|
||||
return 0;
|
||||
@@ -123,8 +119,8 @@ int udev_remove_device(struct udevice *udev)
|
||||
dbg("remove name='%s'", udev->name);
|
||||
udev_db_delete_device(udev);
|
||||
} else {
|
||||
dbg("'%s' not found in database, using kernel name '%s'", udev->devpath, udev->kernel_name);
|
||||
strlcpy(udev->name, udev->kernel_name, sizeof(udev->name));
|
||||
dbg("'%s' not found in database, using kernel name '%s'", udev->dev->devpath, udev->dev->kernel_name);
|
||||
strlcpy(udev->name, udev->dev->kernel_name, sizeof(udev->name));
|
||||
}
|
||||
|
||||
return delete_node(udev);
|
||||
|
||||
+138
-211
@@ -31,13 +31,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "list.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
#include "udev_rules.h"
|
||||
|
||||
|
||||
@@ -221,26 +215,29 @@ static int import_program_into_env(struct udevice *udev, const char *program)
|
||||
char result[1024];
|
||||
size_t reslen;
|
||||
|
||||
if (run_program(program, udev->subsystem, result, sizeof(result), &reslen, (udev_log_priority >= LOG_INFO)) != 0)
|
||||
if (run_program(program, udev->dev->subsystem, result, sizeof(result), &reslen, (udev_log_priority >= LOG_INFO)) != 0)
|
||||
return -1;
|
||||
return import_keys_into_env(udev, result, reslen);
|
||||
}
|
||||
|
||||
static int import_parent_into_env(struct udevice *udev, struct sysfs_class_device *class_dev, const char *filter)
|
||||
static int import_parent_into_env(struct udevice *udev, const char *filter)
|
||||
{
|
||||
struct sysfs_class_device *parent = sysfs_get_classdev_parent(class_dev);
|
||||
struct sysfs_device *dev_parent;
|
||||
int rc = -1;
|
||||
|
||||
if (parent != NULL) {
|
||||
struct udevice udev_parent;
|
||||
dev_parent = sysfs_device_get_parent(udev->dev);
|
||||
if (dev_parent != NULL) {
|
||||
struct udevice *udev_parent;
|
||||
struct name_entry *name_loop;
|
||||
|
||||
dbg("found parent '%s', get the node name", parent->path);
|
||||
udev_init_device(&udev_parent, NULL, NULL, NULL);
|
||||
dbg("found parent '%s', get the node name", dev_parent->devpath);
|
||||
udev_parent = udev_device_init();
|
||||
if (udev_parent == NULL)
|
||||
return -1;
|
||||
/* import the udev_db of the parent */
|
||||
if (udev_db_get_device(&udev_parent, &parent->path[strlen(sysfs_path)]) == 0) {
|
||||
dbg("import stored parent env '%s'", udev_parent.name);
|
||||
list_for_each_entry(name_loop, &udev_parent.env_list, node) {
|
||||
if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) {
|
||||
dbg("import stored parent env '%s'", udev_parent->name);
|
||||
list_for_each_entry(name_loop, &udev_parent->env_list, node) {
|
||||
char name[NAME_SIZE];
|
||||
char *pos;
|
||||
|
||||
@@ -260,7 +257,7 @@ static int import_parent_into_env(struct udevice *udev, struct sysfs_class_devic
|
||||
rc = 0;
|
||||
} else
|
||||
dbg("parent not found in database");
|
||||
udev_cleanup_device(&udev_parent);
|
||||
udev_device_cleanup(udev_parent);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -291,22 +288,24 @@ static int find_free_number(const char *base, const char *devpath)
|
||||
{
|
||||
char db_devpath[PATH_SIZE];
|
||||
char filename[PATH_SIZE];
|
||||
struct udevice udev_db;
|
||||
struct udevice *udev_db;
|
||||
int num = 0;
|
||||
|
||||
/* check if the device already owns a matching name */
|
||||
udev_init_device(&udev_db, NULL, NULL, NULL);
|
||||
if (udev_db_get_device(&udev_db, devpath) == 0) {
|
||||
udev_db = udev_device_init();
|
||||
if (udev_db == NULL)
|
||||
return -1;
|
||||
if (udev_db_get_device(udev_db, devpath) == 0) {
|
||||
struct name_entry *name_loop;
|
||||
int devnum;
|
||||
|
||||
devnum = match_name_and_get_number(base, udev_db.name);
|
||||
devnum = match_name_and_get_number(base, udev_db->name);
|
||||
if (devnum >= 0) {
|
||||
num = devnum;
|
||||
dbg("device '%s', already has the node '%s' with num %u, use it", devpath, base, num);
|
||||
goto out;
|
||||
}
|
||||
list_for_each_entry(name_loop, &udev_db.symlink_list, node) {
|
||||
list_for_each_entry(name_loop, &udev_db->symlink_list, node) {
|
||||
devnum = match_name_and_get_number(base, name_loop->name);
|
||||
if (devnum >= 0) {
|
||||
num = devnum;
|
||||
@@ -336,45 +335,10 @@ static int find_free_number(const char *base, const char *devpath)
|
||||
}
|
||||
|
||||
out:
|
||||
udev_cleanup_device(&udev_db);
|
||||
udev_device_cleanup(udev_db);
|
||||
return num;
|
||||
}
|
||||
|
||||
static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device,
|
||||
const char *name, char *value, size_t len)
|
||||
{
|
||||
struct sysfs_class_device *class_dev_parent;
|
||||
struct sysfs_attribute *tmpattr;
|
||||
|
||||
dbg("look for device attribute '%s'", name);
|
||||
if (class_dev) {
|
||||
dbg("look for class attribute '%s/%s'", class_dev->path, name);
|
||||
tmpattr = sysfs_get_classdev_attr(class_dev, name);
|
||||
if (tmpattr)
|
||||
goto attr_found;
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
if (class_dev_parent) {
|
||||
tmpattr = sysfs_get_classdev_attr(class_dev_parent, name);
|
||||
if (tmpattr)
|
||||
goto attr_found;
|
||||
}
|
||||
}
|
||||
if (sysfs_device) {
|
||||
dbg("look for devices attribute '%s/%s'", sysfs_device->path, name);
|
||||
tmpattr = sysfs_get_device_attr(sysfs_device, name);
|
||||
if (tmpattr)
|
||||
goto attr_found;
|
||||
}
|
||||
return -1;
|
||||
|
||||
attr_found:
|
||||
strlcpy(value, tmpattr->value, len);
|
||||
remove_trailing_chars(value, '\n');
|
||||
|
||||
dbg("found attribute '%s'", tmpattr->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WAIT_LOOP_PER_SECOND 50
|
||||
static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
|
||||
{
|
||||
@@ -382,7 +346,7 @@ static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
|
||||
struct stat stats;
|
||||
int loop = timeout * WAIT_LOOP_PER_SECOND;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s%s/%s", sysfs_path, udev->devpath, file);
|
||||
snprintf(filename, sizeof(filename), "%s%s/%s", sysfs_path, udev->dev->devpath, file);
|
||||
filename[sizeof(filename)-1] = '\0';
|
||||
dbg("wait %i sec for '%s'", timeout, filename);
|
||||
|
||||
@@ -398,8 +362,7 @@ static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void apply_format(struct udevice *udev, char *string, size_t maxsize,
|
||||
struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
|
||||
static void apply_format(struct udevice *udev, char *string, size_t maxsize)
|
||||
{
|
||||
char temp[PATH_SIZE];
|
||||
char temp2[PATH_SIZE];
|
||||
@@ -408,11 +371,9 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
|
||||
int i;
|
||||
int count;
|
||||
unsigned int next_free_number;
|
||||
struct sysfs_class_device *class_dev_parent;
|
||||
enum subst_type {
|
||||
SUBST_UNKNOWN,
|
||||
SUBST_DEVPATH,
|
||||
SUBST_ID,
|
||||
SUBST_KERNEL_NUMBER,
|
||||
SUBST_KERNEL_NAME,
|
||||
SUBST_MAJOR,
|
||||
@@ -432,7 +393,6 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
|
||||
enum subst_type type;
|
||||
} map[] = {
|
||||
{ .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH },
|
||||
{ .name = "id", .fmt = 'b', .type = SUBST_ID },
|
||||
{ .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
|
||||
{ .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL_NAME },
|
||||
{ .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
|
||||
@@ -502,25 +462,20 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
|
||||
found:
|
||||
attr = get_format_attribute(&tail);
|
||||
strlcpy(temp, tail, sizeof(temp));
|
||||
dbg("format=%i, string='%s', tail='%s', class_dev=%p, sysfs_dev=%p",
|
||||
type ,string, tail, class_dev, sysfs_device);
|
||||
dbg("format=%i, string='%s', tail='%s'", type ,string, tail);
|
||||
|
||||
switch (type) {
|
||||
case SUBST_DEVPATH:
|
||||
strlcat(string, udev->devpath, maxsize);
|
||||
dbg("substitute devpath '%s'", udev->devpath);
|
||||
break;
|
||||
case SUBST_ID:
|
||||
strlcat(string, udev->bus_id, maxsize);
|
||||
dbg("substitute bus_id '%s'", udev->bus_id);
|
||||
strlcat(string, udev->dev->devpath, maxsize);
|
||||
dbg("substitute devpath '%s'", udev->dev->devpath);
|
||||
break;
|
||||
case SUBST_KERNEL_NAME:
|
||||
strlcat(string, udev->kernel_name, maxsize);
|
||||
dbg("substitute kernel name '%s'", udev->kernel_name);
|
||||
strlcat(string, udev->dev->kernel_name, maxsize);
|
||||
dbg("substitute kernel name '%s'", udev->dev->kernel_name);
|
||||
break;
|
||||
case SUBST_KERNEL_NUMBER:
|
||||
strlcat(string, udev->kernel_number, maxsize);
|
||||
dbg("substitute kernel number '%s'", udev->kernel_number);
|
||||
strlcat(string, udev->dev->kernel_number, maxsize);
|
||||
dbg("substitute kernel number '%s'", udev->dev->kernel_number);
|
||||
break;
|
||||
case SUBST_MAJOR:
|
||||
sprintf(temp2, "%d", major(udev->devt));
|
||||
@@ -570,54 +525,59 @@ found:
|
||||
if (attr == NULL) {
|
||||
dbg("missing attribute");
|
||||
break;
|
||||
}
|
||||
if (find_sysfs_attribute(class_dev, sysfs_device, attr, temp2, sizeof(temp2)) != 0) {
|
||||
struct sysfs_device *parent_device;
|
||||
} else {
|
||||
struct sysfs_device *dev_parent;
|
||||
const char *value;
|
||||
|
||||
dbg("sysfs attribute '%s' not found, walk up the physical devices", attr);
|
||||
parent_device = sysfs_get_device_parent(sysfs_device);
|
||||
while (parent_device) {
|
||||
dbg("looking at '%s'", parent_device->path);
|
||||
if (find_sysfs_attribute(NULL, parent_device, attr, temp2, sizeof(temp2)) == 0)
|
||||
dev_parent = udev->dev;
|
||||
do {
|
||||
dbg("looking at '%s'", dev_parent->devpath);
|
||||
value = sysfs_attr_get_value(dev_parent->devpath, attr);
|
||||
if (value != NULL) {
|
||||
strlcpy(temp2, value, sizeof(temp2));
|
||||
break;
|
||||
parent_device = sysfs_get_device_parent(parent_device);
|
||||
}
|
||||
if (!parent_device)
|
||||
break;
|
||||
}
|
||||
dev_parent = sysfs_device_get_parent(dev_parent);
|
||||
} while (dev_parent != NULL);
|
||||
|
||||
/* strip trailing whitespace of sysfs value */
|
||||
i = strlen(temp2);
|
||||
while (i > 0 && isspace(temp2[i-1]))
|
||||
temp2[--i] = '\0';
|
||||
count = replace_untrusted_chars(temp2);
|
||||
if (count)
|
||||
info("%i untrusted character(s) replaced" , count);
|
||||
strlcat(string, temp2, maxsize);
|
||||
dbg("substitute sysfs value '%s'", temp2);
|
||||
}
|
||||
/* strip trailing whitespace of sysfs value */
|
||||
i = strlen(temp2);
|
||||
while (i > 0 && isspace(temp2[i-1]))
|
||||
temp2[--i] = '\0';
|
||||
count = replace_untrusted_chars(temp2);
|
||||
if (count)
|
||||
info("%i untrusted character(s) replaced" , count);
|
||||
strlcat(string, temp2, maxsize);
|
||||
dbg("substitute sysfs value '%s'", temp2);
|
||||
break;
|
||||
case SUBST_ENUM:
|
||||
next_free_number = find_free_number(string, udev->devpath);
|
||||
next_free_number = find_free_number(string, udev->dev->devpath);
|
||||
if (next_free_number > 0) {
|
||||
sprintf(temp2, "%d", next_free_number);
|
||||
strlcat(string, temp2, maxsize);
|
||||
}
|
||||
break;
|
||||
case SUBST_PARENT:
|
||||
if (!class_dev)
|
||||
break;
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
if (class_dev_parent != NULL) {
|
||||
struct udevice udev_parent;
|
||||
{
|
||||
struct sysfs_device *dev_parent;
|
||||
|
||||
dbg("found parent '%s', get the node name", class_dev_parent->path);
|
||||
udev_init_device(&udev_parent, NULL, NULL, NULL);
|
||||
/* lookup the name in the udev_db with the DEVPATH of the parent */
|
||||
if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) {
|
||||
strlcat(string, udev_parent.name, maxsize);
|
||||
dbg("substitute parent node name'%s'", udev_parent.name);
|
||||
} else
|
||||
dbg("parent not found in database");
|
||||
udev_cleanup_device(&udev_parent);
|
||||
dev_parent = sysfs_device_get_parent(udev->dev);
|
||||
if (dev_parent != NULL) {
|
||||
struct udevice *udev_parent;
|
||||
|
||||
dbg("found parent '%s', get the node name", dev_parent->devpath);
|
||||
udev_parent = udev_device_init();
|
||||
if (udev_parent != NULL) {
|
||||
/* lookup the name in the udev_db with the DEVPATH of the parent */
|
||||
if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) {
|
||||
strlcat(string, udev_parent->name, maxsize);
|
||||
dbg("substitute parent node name'%s'", udev_parent->name);
|
||||
} else
|
||||
dbg("parent not found in database");
|
||||
udev_device_cleanup(udev_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SUBST_TEMP_NODE:
|
||||
@@ -636,10 +596,15 @@ found:
|
||||
dbg("substitute udev_root '%s'", udev_root);
|
||||
break;
|
||||
case SUBST_MODALIAS:
|
||||
if (find_sysfs_attribute(NULL, sysfs_device, "modalias", temp2, sizeof(temp2)) != 0)
|
||||
break;
|
||||
strlcat(string, temp2, maxsize);
|
||||
dbg("substitute MODALIAS '%s'", temp2);
|
||||
{
|
||||
const char *value;
|
||||
|
||||
value = sysfs_attr_get_value(udev->dev->devpath, "modalias");
|
||||
if (value != NULL) {
|
||||
strlcat(string, value, maxsize);
|
||||
dbg("substitute MODALIAS '%s'", temp2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SUBST_ENV:
|
||||
if (attr == NULL) {
|
||||
@@ -714,28 +679,28 @@ static int match_key(const char *key_name, struct udev_rule *rule, struct key *k
|
||||
}
|
||||
|
||||
/* match a single rule against a given device and possibly its parent devices */
|
||||
static int match_rule(struct udevice *udev, struct udev_rule *rule,
|
||||
struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
|
||||
static int match_rule(struct udevice *udev, struct udev_rule *rule)
|
||||
{
|
||||
struct sysfs_device *parent_device = sysfs_device;
|
||||
struct sysfs_device *dev_parent;
|
||||
int i;
|
||||
|
||||
if (match_key("ACTION", rule, &rule->action, udev->action))
|
||||
goto exit;
|
||||
|
||||
if (match_key("KERNEL", rule, &rule->kernel_name, udev->kernel_name))
|
||||
if (match_key("KERNEL", rule, &rule->kernel_name, udev->dev->kernel_name))
|
||||
goto exit;
|
||||
|
||||
if (match_key("SUBSYSTEM", rule, &rule->subsystem, udev->subsystem))
|
||||
if (match_key("SUBSYSTEM", rule, &rule->subsystem, udev->dev->subsystem))
|
||||
goto exit;
|
||||
|
||||
if (match_key("DEVPATH", rule, &rule->devpath, udev->devpath))
|
||||
if (match_key("DEVPATH", rule, &rule->devpath, udev->dev->devpath))
|
||||
goto exit;
|
||||
|
||||
if (rule->modalias.operation != KEY_OP_UNSET) {
|
||||
char value[NAME_SIZE];
|
||||
const char *value;
|
||||
|
||||
if (find_sysfs_attribute(NULL, sysfs_device, "modalias", value, sizeof(value)) != 0) {
|
||||
value = sysfs_attr_get_value(udev->dev->devpath, "modalias");
|
||||
if (value == NULL) {
|
||||
dbg("MODALIAS value not found");
|
||||
goto exit;
|
||||
}
|
||||
@@ -777,34 +742,23 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,
|
||||
}
|
||||
|
||||
/* walk up the chain of physical devices and find a match */
|
||||
dev_parent = udev->dev;
|
||||
while (1) {
|
||||
/* check for matching driver */
|
||||
if (rule->driver.operation != KEY_OP_UNSET) {
|
||||
if (parent_device == NULL) {
|
||||
dbg("device has no sysfs_device");
|
||||
goto exit;
|
||||
}
|
||||
if (match_key("DRIVER", rule, &rule->driver, parent_device->driver_name))
|
||||
if (match_key("DRIVER", rule, &rule->driver, dev_parent->driver))
|
||||
goto try_parent;
|
||||
}
|
||||
|
||||
/* check for matching bus value */
|
||||
if (rule->bus.operation != KEY_OP_UNSET) {
|
||||
if (parent_device == NULL) {
|
||||
dbg("device has no sysfs_device");
|
||||
goto exit;
|
||||
}
|
||||
if (match_key("BUS", rule, &rule->bus, parent_device->bus))
|
||||
if (match_key("BUS", rule, &rule->bus, dev_parent->subsystem))
|
||||
goto try_parent;
|
||||
}
|
||||
|
||||
/* check for matching bus id */
|
||||
if (rule->id.operation != KEY_OP_UNSET) {
|
||||
if (parent_device == NULL) {
|
||||
dbg("device has no sysfs_device");
|
||||
goto exit;
|
||||
}
|
||||
if (match_key("ID", rule, &rule->id, parent_device->bus_id))
|
||||
if (match_key("ID", rule, &rule->id, dev_parent->kernel_name))
|
||||
goto try_parent;
|
||||
}
|
||||
|
||||
@@ -815,36 +769,39 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,
|
||||
struct key_pair *pair = &rule->sysfs.keys[i];
|
||||
const char *key_name = key_pair_name(rule, pair);
|
||||
const char *key_value = key_val(rule, &pair->key);
|
||||
char value[VALUE_SIZE];
|
||||
const char *value;
|
||||
char val[VALUE_SIZE];
|
||||
size_t len;
|
||||
|
||||
if (find_sysfs_attribute(class_dev, parent_device, key_name, value, sizeof(value)) != 0)
|
||||
value = sysfs_attr_get_value(dev_parent->devpath, key_name);
|
||||
if (value == NULL)
|
||||
goto try_parent;
|
||||
strlcpy(val, value, sizeof(val));
|
||||
|
||||
/* strip trailing whitespace of value, if not asked to match for it */
|
||||
len = strlen(key_value);
|
||||
if (len && !isspace(key_value[len-1])) {
|
||||
len = strlen(value);
|
||||
while (len > 0 && isspace(value[len-1]))
|
||||
value[--len] = '\0';
|
||||
dbg("removed %zi trailing whitespace chars from '%s'", strlen(value)-len, value);
|
||||
if (len > 0 && !isspace(key_value[len-1])) {
|
||||
len = strlen(val);
|
||||
while (len > 0 && isspace(val[len-1]))
|
||||
val[--len] = '\0';
|
||||
dbg("removed %zi trailing whitespace chars from '%s'", strlen(val)-len, val);
|
||||
}
|
||||
|
||||
if (match_key("SYSFS", rule, &pair->key, value))
|
||||
if (match_key("SYSFS", rule, &pair->key, val))
|
||||
goto try_parent;
|
||||
}
|
||||
dbg("all %i SYSFS keys matched", rule->sysfs.count);
|
||||
}
|
||||
|
||||
/* found matching physical device */
|
||||
/* found matching device */
|
||||
break;
|
||||
try_parent:
|
||||
dbg("try parent sysfs device");
|
||||
parent_device = sysfs_get_device_parent(parent_device);
|
||||
if (parent_device == NULL)
|
||||
dev_parent = sysfs_device_get_parent(dev_parent);
|
||||
if (dev_parent == NULL)
|
||||
goto exit;
|
||||
dbg("look at sysfs_device->path='%s'", parent_device->path);
|
||||
dbg("look at sysfs_device->bus_id='%s'", parent_device->bus_id);
|
||||
dbg("looking at dev_parent->devpath='%s'", dev_parent->devpath);
|
||||
dbg("looking at dev_parent->bus_kernel_name='%s'", dev_parent->kernel_name);
|
||||
}
|
||||
|
||||
/* execute external program */
|
||||
@@ -853,8 +810,8 @@ try_parent:
|
||||
char result[PATH_SIZE];
|
||||
|
||||
strlcpy(program, key_val(rule, &rule->program), sizeof(program));
|
||||
apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
|
||||
if (run_program(program, udev->subsystem, result, sizeof(result), NULL, (udev_log_priority >= LOG_INFO)) != 0) {
|
||||
apply_format(udev, program, sizeof(program));
|
||||
if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL, (udev_log_priority >= LOG_INFO)) != 0) {
|
||||
dbg("PROGRAM is false");
|
||||
udev->program_result[0] = '\0';
|
||||
if (rule->program.operation != KEY_OP_NOMATCH)
|
||||
@@ -886,18 +843,18 @@ try_parent:
|
||||
int rc = -1;
|
||||
|
||||
strlcpy(import, key_val(rule, &rule->import), sizeof(import));
|
||||
apply_format(udev, import, sizeof(import), class_dev, sysfs_device);
|
||||
apply_format(udev, import, sizeof(import));
|
||||
dbg("check for IMPORT import='%s'", import);
|
||||
if (rule->import_type == IMPORT_PROGRAM) {
|
||||
rc = import_program_into_env(udev, import);
|
||||
} else if (rule->import_type == IMPORT_FILE) {
|
||||
dbg("import file import='%s'", import);
|
||||
rc = import_file_into_env(udev, import);
|
||||
} else if (rule->import_type == IMPORT_PARENT && class_dev) {
|
||||
} else if (rule->import_type == IMPORT_PARENT) {
|
||||
dbg("import parent import='%s'", import);
|
||||
rc = import_parent_into_env(udev, class_dev, import);
|
||||
rc = import_parent_into_env(udev, import);
|
||||
}
|
||||
if (rc) {
|
||||
if (rc != 0) {
|
||||
dbg("IMPORT failed");
|
||||
if (rule->import.operation != KEY_OP_NOMATCH)
|
||||
goto exit;
|
||||
@@ -926,35 +883,13 @@ exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct sysfs_class_device *class_dev)
|
||||
int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev)
|
||||
{
|
||||
struct sysfs_class_device *class_dev_parent;
|
||||
struct sysfs_device *sysfs_device = NULL;
|
||||
struct udev_rule *rule;
|
||||
int name_set = 0;
|
||||
|
||||
dbg("class_dev->name='%s'", class_dev->name);
|
||||
|
||||
/* Figure out where the "device"-symlink is at. For char devices this will
|
||||
* always be in the class_dev->path. On block devices, only the main block
|
||||
* device will have the device symlink in it's path. All partition devices
|
||||
* need to look at the symlink in its parent directory.
|
||||
*/
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
if (class_dev_parent != NULL) {
|
||||
dbg("given class device has a parent, use this instead");
|
||||
sysfs_device = sysfs_get_classdev_device(class_dev_parent);
|
||||
} else {
|
||||
sysfs_device = sysfs_get_classdev_device(class_dev);
|
||||
}
|
||||
|
||||
if (sysfs_device) {
|
||||
dbg("found devices device: path='%s', bus_id='%s', bus='%s'",
|
||||
sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus);
|
||||
strlcpy(udev->bus_id, sysfs_device->bus_id, sizeof(udev->bus_id));
|
||||
}
|
||||
|
||||
dbg("udev->kernel_name='%s'", udev->kernel_name);
|
||||
dbg("udev->dev->devpath='%s'", udev->dev->devpath);
|
||||
dbg("udev->dev->kernel_name='%s'", udev->dev->kernel_name);
|
||||
|
||||
/* look for a matching rule to apply */
|
||||
udev_rules_iter_init(rules);
|
||||
@@ -969,10 +904,10 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
}
|
||||
|
||||
dbg("process rule");
|
||||
if (match_rule(udev, rule, class_dev, sysfs_device) == 0) {
|
||||
if (match_rule(udev, rule) == 0) {
|
||||
/* apply options */
|
||||
if (rule->ignore_device) {
|
||||
info("rule applied, '%s' is ignored", udev->kernel_name);
|
||||
info("rule applied, '%s' is ignored", udev->dev->kernel_name);
|
||||
udev->ignore_device = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -981,7 +916,8 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
dbg("remove event should be ignored");
|
||||
}
|
||||
/* apply all_partitions option only at a main block device */
|
||||
if (rule->partitions && udev->type == DEV_BLOCK && udev->kernel_number[0] == '\0') {
|
||||
if (rule->partitions &&
|
||||
strcmp(udev->dev->subsystem, "block") == 0 && udev->dev->kernel_number[0] == '\0') {
|
||||
udev->partitions = rule->partitions;
|
||||
dbg("creation of partition nodes requested");
|
||||
}
|
||||
@@ -991,21 +927,21 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
if (rule->mode_operation == KEY_OP_ASSIGN_FINAL)
|
||||
udev->mode_final = 1;
|
||||
udev->mode = rule->mode;
|
||||
dbg("applied mode=%#o to '%s'", rule->mode, udev->kernel_name);
|
||||
dbg("applied mode=%#o to '%s'", rule->mode, udev->dev->kernel_name);
|
||||
}
|
||||
if (!udev->owner_final && rule->owner.operation != KEY_OP_UNSET) {
|
||||
if (rule->owner.operation == KEY_OP_ASSIGN_FINAL)
|
||||
udev->owner_final = 1;
|
||||
strlcpy(udev->owner, key_val(rule, &rule->owner), sizeof(udev->owner));
|
||||
apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device);
|
||||
dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name);
|
||||
apply_format(udev, udev->owner, sizeof(udev->owner));
|
||||
dbg("applied owner='%s' to '%s'", udev->owner, udev->dev->kernel_name);
|
||||
}
|
||||
if (!udev->group_final && rule->group.operation != KEY_OP_UNSET) {
|
||||
if (rule->group.operation == KEY_OP_ASSIGN_FINAL)
|
||||
udev->group_final = 1;
|
||||
strlcpy(udev->group, key_val(rule, &rule->group), sizeof(udev->group));
|
||||
apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device);
|
||||
dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name);
|
||||
apply_format(udev, udev->group, sizeof(udev->group));
|
||||
dbg("applied group='%s' to '%s'", udev->group, udev->dev->kernel_name);
|
||||
}
|
||||
|
||||
/* collect symlinks */
|
||||
@@ -1021,7 +957,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
name_list_cleanup(&udev->symlink_list);
|
||||
}
|
||||
strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp));
|
||||
apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device);
|
||||
apply_format(udev, temp, sizeof(temp));
|
||||
count = replace_untrusted_chars(temp);
|
||||
if (count)
|
||||
info("%i untrusted character(s) replaced" , count);
|
||||
@@ -1052,13 +988,13 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
int count;
|
||||
name_set = 1;
|
||||
strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name));
|
||||
apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device);
|
||||
apply_format(udev, udev->name, sizeof(udev->name));
|
||||
count = replace_untrusted_chars(udev->name);
|
||||
if (count)
|
||||
info("%i untrusted character(s) replaced", count);
|
||||
|
||||
info("rule applied, '%s' becomes '%s'", udev->kernel_name, udev->name);
|
||||
if (udev->type != DEV_NET)
|
||||
info("rule applied, '%s' becomes '%s'", udev->dev->kernel_name, udev->name);
|
||||
if (strcmp(udev->dev->subsystem, "net") != 0)
|
||||
dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i",
|
||||
udev->name, udev->owner, udev->group, udev->mode, udev->partitions);
|
||||
}
|
||||
@@ -1073,7 +1009,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
name_list_cleanup(&udev->run_list);
|
||||
}
|
||||
strlcpy(program, key_val(rule, &rule->run), sizeof(program));
|
||||
apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
|
||||
apply_format(udev, program, sizeof(program));
|
||||
dbg("add run '%s'", program);
|
||||
name_list_add(&udev->run_list, program, 0);
|
||||
}
|
||||
@@ -1091,7 +1027,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
}
|
||||
|
||||
if (!name_set) {
|
||||
strlcpy(udev->name, udev->kernel_name, sizeof(udev->name));
|
||||
strlcpy(udev->name, udev->dev->kernel_name, sizeof(udev->name));
|
||||
info("no node name set, will use kernel name '%s'", udev->name);
|
||||
}
|
||||
|
||||
@@ -1104,20 +1040,11 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev,
|
||||
struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_dev)
|
||||
int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev)
|
||||
{
|
||||
struct udev_rule *rule;
|
||||
|
||||
if (class_dev && !sysfs_dev)
|
||||
sysfs_dev = sysfs_get_classdev_device(class_dev);
|
||||
if (sysfs_dev) {
|
||||
dbg("found devices device: path='%s', bus_id='%s', bus='%s'",
|
||||
sysfs_dev->path, sysfs_dev->bus_id, sysfs_dev->bus);
|
||||
strlcpy(udev->bus_id, sysfs_dev->bus_id, sizeof(udev->bus_id));
|
||||
}
|
||||
|
||||
dbg("udev->kernel_name='%s'", udev->kernel_name);
|
||||
dbg("udev->kernel_name='%s'", udev->dev->kernel_name);
|
||||
|
||||
/* look for a matching rule to apply */
|
||||
udev_rules_iter_init(rules);
|
||||
@@ -1133,9 +1060,9 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match_rule(udev, rule, class_dev, sysfs_dev) == 0) {
|
||||
if (match_rule(udev, rule) == 0) {
|
||||
if (rule->ignore_device) {
|
||||
info("rule applied, '%s' is ignored", udev->kernel_name);
|
||||
info("rule applied, '%s' is ignored", udev->dev->kernel_name);
|
||||
udev->ignore_device = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1148,7 +1075,7 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev,
|
||||
name_list_cleanup(&udev->run_list);
|
||||
}
|
||||
strlcpy(program, key_val(rule, &rule->run), sizeof(program));
|
||||
apply_format(udev, program, sizeof(program), class_dev, sysfs_dev);
|
||||
apply_format(udev, program, sizeof(program));
|
||||
dbg("add run '%s'", program);
|
||||
name_list_add(&udev->run_list, program, 0);
|
||||
if (rule->run.operation == KEY_OP_ASSIGN_FINAL)
|
||||
|
||||
+4
-8
@@ -22,7 +22,6 @@
|
||||
#ifndef UDEV_RULES_H
|
||||
#define UDEV_RULES_H
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev.h"
|
||||
#include "list.h"
|
||||
|
||||
@@ -104,18 +103,15 @@ struct udev_rules {
|
||||
};
|
||||
|
||||
extern int udev_rules_init(struct udev_rules *rules, int resolve_names);
|
||||
extern void udev_rules_close(struct udev_rules *rules);
|
||||
extern void udev_rules_cleanup(struct udev_rules *rules);
|
||||
|
||||
extern void udev_apply_format(struct udevice *udev, char *string, size_t maxsize,
|
||||
struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device);
|
||||
extern void udev_apply_format(struct udevice *udev, char *string, size_t maxsize);
|
||||
|
||||
extern void udev_rules_iter_init(struct udev_rules *rules);
|
||||
extern struct udev_rule *udev_rules_iter_next(struct udev_rules *rules);
|
||||
extern struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label);
|
||||
|
||||
extern int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev,
|
||||
struct sysfs_class_device *class_dev);
|
||||
extern int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev,
|
||||
struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_dev);
|
||||
extern int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev);
|
||||
extern int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev);
|
||||
|
||||
#endif
|
||||
|
||||
+1
-4
@@ -28,10 +28,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "logging.h"
|
||||
#include "udev_rules.h"
|
||||
|
||||
|
||||
@@ -626,7 +623,7 @@ int udev_rules_init(struct udev_rules *rules, int resolve_names)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void udev_rules_close(struct udev_rules *rules)
|
||||
void udev_rules_cleanup(struct udev_rules *rules)
|
||||
{
|
||||
if (rules->buf) {
|
||||
free(rules->buf);
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "udev.h"
|
||||
#include "udev_selinux.h"
|
||||
#include "logging.h"
|
||||
|
||||
static security_context_t prev_scontext = NULL;
|
||||
|
||||
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* udev_sysfs.c - sysfs access
|
||||
*
|
||||
* Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation version 2 of the License.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "udev.h"
|
||||
|
||||
char sysfs_path[PATH_SIZE];
|
||||
|
||||
/* device cache */
|
||||
static LIST_HEAD(dev_list);
|
||||
|
||||
/* attribute value cache */
|
||||
static LIST_HEAD(attr_list);
|
||||
struct sysfs_attr {
|
||||
struct list_head node;
|
||||
char path[PATH_SIZE];
|
||||
char value[NAME_SIZE];
|
||||
};
|
||||
|
||||
int sysfs_init(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
env = getenv("SYSFS_PATH");
|
||||
if (env) {
|
||||
strlcpy(sysfs_path, env, sizeof(sysfs_path));
|
||||
remove_trailing_chars(sysfs_path, '/');
|
||||
} else
|
||||
strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
|
||||
dbg("sysfs_path='%s'", sysfs_path);
|
||||
|
||||
INIT_LIST_HEAD(&dev_list);
|
||||
INIT_LIST_HEAD(&attr_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sysfs_cleanup(void)
|
||||
{
|
||||
struct sysfs_attr *attr_loop;
|
||||
struct sysfs_attr *attr_temp;
|
||||
struct sysfs_device *dev_loop;
|
||||
struct sysfs_device *dev_temp;
|
||||
|
||||
list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
|
||||
list_del(&attr_loop->node);
|
||||
free(attr_loop);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) {
|
||||
list_del(&dev_loop->node);
|
||||
free(dev_loop);
|
||||
}
|
||||
}
|
||||
|
||||
void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, const char *subsystem)
|
||||
{
|
||||
char *pos;
|
||||
|
||||
strlcpy(dev->devpath, devpath, sizeof(dev->devpath));
|
||||
if (subsystem != NULL)
|
||||
strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
|
||||
|
||||
/* set kernel name */
|
||||
pos = strrchr(dev->devpath, '/');
|
||||
if (pos == NULL)
|
||||
return;
|
||||
|
||||
strlcpy(dev->kernel_name, &pos[1], sizeof(dev->kernel_name));
|
||||
dbg("kernel_name='%s'", dev->kernel_name);
|
||||
|
||||
/* some devices have '!' in their name, change that to '/' */
|
||||
pos = dev->kernel_name;
|
||||
while (pos[0] != '\0') {
|
||||
if (pos[0] == '!')
|
||||
pos[0] = '/';
|
||||
pos++;
|
||||
}
|
||||
|
||||
/* get kernel number */
|
||||
pos = &dev->kernel_name[strlen(dev->kernel_name)];
|
||||
while (isdigit(pos[-1]))
|
||||
pos--;
|
||||
strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number));
|
||||
dbg("kernel_number='%s'", dev->kernel_number);
|
||||
}
|
||||
|
||||
struct sysfs_device *sysfs_device_get(const char *devpath)
|
||||
{
|
||||
char path[PATH_SIZE];
|
||||
char devpath_real[PATH_SIZE];
|
||||
struct sysfs_device *dev;
|
||||
struct sysfs_device *dev_loop;
|
||||
struct stat statbuf;
|
||||
char link_path[PATH_SIZE];
|
||||
char link_target[PATH_SIZE];
|
||||
int len;
|
||||
char *pos;
|
||||
|
||||
dbg("open '%s'", devpath);
|
||||
strlcpy(devpath_real, devpath, sizeof(devpath_real));
|
||||
remove_trailing_chars(devpath_real, '/');
|
||||
|
||||
strlcpy(path, sysfs_path, sizeof(path));
|
||||
strlcat(path, devpath_real, sizeof(path));
|
||||
if (lstat(path, &statbuf) != 0) {
|
||||
dbg("stat '%s' failed: %s", devpath, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if we got a link, resolve it to the real device */
|
||||
if (S_ISLNK(statbuf.st_mode)) {
|
||||
int i;
|
||||
int back;
|
||||
|
||||
len = readlink(path, link_target, sizeof(link_target));
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
link_target[len] = '\0';
|
||||
dbg("devpath link '%s' points to '%s'", path, link_target);
|
||||
|
||||
for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
|
||||
;
|
||||
dbg("base '%s', tail '%s', back %i", devpath_real, &link_target[back * 3], back);
|
||||
for (i = 0; i <= back; i++) {
|
||||
pos = strrchr(devpath_real, '/');
|
||||
if (pos == NULL)
|
||||
return NULL;
|
||||
pos[0] = '\0';
|
||||
}
|
||||
dbg("after moving back '%s'", devpath_real);
|
||||
strlcat(devpath_real, "/", sizeof(devpath_real));
|
||||
strlcat(devpath_real, &link_target[back * 3], sizeof(devpath_real));
|
||||
}
|
||||
|
||||
/* look for device in cache */
|
||||
list_for_each_entry(dev_loop, &dev_list, node) {
|
||||
if (strcmp(dev_loop->devpath, devpath_real) == 0) {
|
||||
dbg("found in cache '%s'", dev_loop->devpath);
|
||||
return dev_loop;
|
||||
}
|
||||
}
|
||||
|
||||
/* new device */
|
||||
dbg("'%s'", devpath_real);
|
||||
dev = malloc(sizeof(struct sysfs_device));
|
||||
if (dev == NULL)
|
||||
return NULL;
|
||||
memset(dev, 0x00, sizeof(struct sysfs_device));
|
||||
|
||||
sysfs_device_set_values(dev, devpath_real, NULL);
|
||||
|
||||
/* get subsystem */
|
||||
if (strncmp(dev->devpath, "/class/", 7) == 0) {
|
||||
strlcpy(dev->subsystem, &dev->devpath[7], sizeof(dev->subsystem));
|
||||
pos = strchr(dev->subsystem, '/');
|
||||
if (pos != NULL)
|
||||
pos[0] = '\0';
|
||||
else
|
||||
dev->subsystem[0] = '\0';
|
||||
} else if (strncmp(devpath, "/block/", 7) == 0) {
|
||||
strlcpy(dev->subsystem, "block", sizeof(dev->subsystem));
|
||||
} else if (strncmp(devpath, "/devices/", 9) == 0) {
|
||||
/* get subsystem from bus name */
|
||||
strlcpy(link_path, sysfs_path, sizeof(link_path));
|
||||
strlcat(link_path, dev->devpath, sizeof(link_path));
|
||||
strlcat(link_path, "/bus", sizeof(link_path));
|
||||
len = readlink(link_path, link_target, sizeof(link_target));
|
||||
if (len > 0) {
|
||||
link_target[len] = '\0';
|
||||
dbg("bus link '%s' points to '%s'", link_path, link_target);
|
||||
pos = strrchr(link_target, '/');
|
||||
if (pos != NULL)
|
||||
strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
|
||||
}
|
||||
|
||||
/* get driver name */
|
||||
strlcpy(link_path, sysfs_path, sizeof(link_path));
|
||||
strlcat(link_path, dev->devpath, sizeof(link_path));
|
||||
strlcat(link_path, "/driver", sizeof(link_path));
|
||||
len = readlink(link_path, link_target, sizeof(link_target));
|
||||
if (len > 0) {
|
||||
link_target[len] = '\0';
|
||||
dbg("driver link '%s' points to '%s'", link_path, link_target);
|
||||
pos = strrchr(link_target, '/');
|
||||
if (pos != NULL)
|
||||
strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
|
||||
}
|
||||
} else if (strncmp(devpath, "/bus/", 5) == 0 && strstr(devpath, "/drivers/")) {
|
||||
strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem));
|
||||
} else if (strncmp(devpath, "/module/", 8) == 0) {
|
||||
strlcpy(dev->subsystem, "module", sizeof(dev->subsystem));
|
||||
}
|
||||
|
||||
dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'", dev->devpath, dev->subsystem, dev->driver);
|
||||
list_add(&dev->node, &dev_list);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
|
||||
{
|
||||
char parent_devpath[PATH_SIZE];
|
||||
char device_link[PATH_SIZE];
|
||||
char device_link_target[PATH_SIZE];
|
||||
char *pos;
|
||||
int i;
|
||||
int len;
|
||||
int back;
|
||||
|
||||
/* requesting a parent is only valid for devices */
|
||||
if ((strncmp(dev->devpath, "/devices/", 9) != 0) &&
|
||||
(strncmp(dev->devpath, "/class/", 7) != 0) &&
|
||||
(strncmp(dev->devpath, "/block/", 7) != 0))
|
||||
return NULL;
|
||||
|
||||
strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
|
||||
dbg("'%s'", parent_devpath);
|
||||
|
||||
/* strip last element */
|
||||
pos = strrchr(parent_devpath, '/');
|
||||
if (pos == NULL || pos == parent_devpath)
|
||||
return NULL;
|
||||
pos[0] = '\0';
|
||||
|
||||
/* are we at the top level */
|
||||
if (strcmp(parent_devpath, "/devices") == 0) {
|
||||
dbg("/devices top level");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* at the top level we may follow the "device" link */
|
||||
if (strcmp(parent_devpath, "/block") == 0) {
|
||||
dbg("/block top level, look for device link");
|
||||
goto device_link;
|
||||
}
|
||||
|
||||
if (strncmp(parent_devpath, "/class", 6) == 0) {
|
||||
pos = strrchr(parent_devpath, '/');
|
||||
if (pos == &parent_devpath[6] || pos == parent_devpath) {
|
||||
dbg("class top level, look for device link");
|
||||
goto device_link;
|
||||
}
|
||||
}
|
||||
return sysfs_device_get(parent_devpath);
|
||||
|
||||
device_link:
|
||||
strlcpy(device_link, sysfs_path, sizeof(device_link));
|
||||
strlcat(device_link, dev->devpath, sizeof(device_link));
|
||||
strlcat(device_link, "/device", sizeof(device_link));
|
||||
len = readlink(device_link, device_link_target, sizeof(device_link_target));
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
device_link_target[len] = '\0';
|
||||
dbg("device link '%s' points to '%s'", device_link, device_link_target);
|
||||
|
||||
for (back = 0; strncmp(&device_link_target[back * 3], "../", 3) == 0; back++)
|
||||
;
|
||||
strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
|
||||
dbg("base='%s', tail='%s', back=%i", parent_devpath, &device_link_target[back * 3], back);
|
||||
for (i = 0; i < back; i++) {
|
||||
pos = strrchr(parent_devpath, '/');
|
||||
if (pos == NULL)
|
||||
return NULL;
|
||||
pos[0] = '\0';
|
||||
}
|
||||
dbg("after moving back '%s'", parent_devpath);
|
||||
strlcat(parent_devpath, "/", sizeof(parent_devpath));
|
||||
strlcat(parent_devpath, &device_link_target[back * 3], sizeof(parent_devpath));
|
||||
return sysfs_device_get(parent_devpath);
|
||||
}
|
||||
|
||||
char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
|
||||
{
|
||||
char path_full[PATH_SIZE];
|
||||
const char *path;
|
||||
char value[NAME_SIZE];
|
||||
struct sysfs_attr *attr_loop;
|
||||
struct sysfs_attr *attr;
|
||||
int fd;
|
||||
ssize_t size;
|
||||
size_t sysfs_len;
|
||||
|
||||
sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
|
||||
path = &path_full[sysfs_len];
|
||||
strlcat(path_full, devpath, sizeof(path_full));
|
||||
strlcat(path_full, "/", sizeof(path_full));
|
||||
strlcat(path_full, attr_name, sizeof(path_full));
|
||||
|
||||
/* look for attribute in cache */
|
||||
list_for_each_entry(attr_loop, &attr_list, node) {
|
||||
if (strcmp(attr_loop->path, path) == 0) {
|
||||
dbg("found in cache '%s'", attr_loop->path);
|
||||
return attr_loop->value;
|
||||
}
|
||||
}
|
||||
|
||||
/* read attribute value */
|
||||
fd = open(path_full, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
size = read(fd, value, sizeof(value));
|
||||
close(fd);
|
||||
if (size < 0)
|
||||
return NULL;
|
||||
if (size == sizeof(value))
|
||||
return NULL;
|
||||
value[size] = '\0';
|
||||
remove_trailing_chars(value, '\n');
|
||||
|
||||
/* store attribute in cache */
|
||||
attr = malloc(sizeof(struct sysfs_attr));
|
||||
if (attr == NULL)
|
||||
return NULL;
|
||||
strlcpy(attr->path, path, sizeof(attr->path));
|
||||
strlcpy(attr->value, value, sizeof(attr->value));
|
||||
dbg("add to cache '%s' '%s'", attr->path, attr->value);
|
||||
list_add(&attr->node, &attr_list);
|
||||
|
||||
return attr->value;
|
||||
}
|
||||
@@ -30,11 +30,7 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
int log_priority(const char *priority)
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* udev_utils.c - generic stuff used by udev
|
||||
*
|
||||
* Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation version 2 of the License.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UDEV_COMMON_H_
|
||||
#define _UDEV_COMMON_H_
|
||||
|
||||
#include "udev.h"
|
||||
#include "list.h"
|
||||
|
||||
#define UDEV_MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
/* pipes */
|
||||
#define READ_END 0
|
||||
#define WRITE_END 1
|
||||
|
||||
struct name_entry {
|
||||
struct list_head node;
|
||||
char name[PATH_SIZE];
|
||||
};
|
||||
|
||||
/* udev_utils.c */
|
||||
extern int log_priority(const char *priority);
|
||||
extern int name_list_add(struct list_head *name_list, const char *name, int sort);
|
||||
extern int name_list_key_add(struct list_head *name_list, const char *key, const char *value);
|
||||
extern void name_list_cleanup(struct list_head *name_list);
|
||||
extern int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix);
|
||||
|
||||
/* udev_utils_string.c */
|
||||
extern int strcmp_pattern(const char *p, const char *s);
|
||||
extern int string_is_true(const char *str);
|
||||
extern void remove_trailing_chars(char *path, char c);
|
||||
extern int utf8_encoded_valid_unichar(const char *str);
|
||||
extern int replace_untrusted_chars(char *str);
|
||||
|
||||
/* udev_utils_file.c */
|
||||
extern int create_path(const char *path);
|
||||
extern int delete_path(const char *path);
|
||||
extern int file_map(const char *filename, char **buf, size_t *bufsize);
|
||||
extern void file_unmap(void *buf, size_t bufsize);
|
||||
extern int unlink_secure(const char *filename);
|
||||
extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur);
|
||||
|
||||
/* udev_utils_run.c */
|
||||
extern int pass_env_to_socket(const char *name, const char *devpath, const char *action);
|
||||
extern int run_program(const char *command, const char *subsystem,
|
||||
char *result, size_t ressize, size_t *reslen, int log);
|
||||
|
||||
#endif
|
||||
@@ -30,11 +30,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
int create_path(const char *path)
|
||||
{
|
||||
|
||||
@@ -31,11 +31,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
|
||||
int pass_env_to_socket(const char *sockname, const char *devpath, const char *action)
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "list.h"
|
||||
|
||||
/* compare string with pattern (like fnmatch(), supports * ? [0-9] [!A-Z]) */
|
||||
int strcmp_pattern(const char *p, const char *s)
|
||||
|
||||
@@ -32,10 +32,7 @@
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "udev.h"
|
||||
#include "udev_version.h"
|
||||
#include "udevd.h"
|
||||
#include "udev_utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* global variables */
|
||||
static int sock = -1;
|
||||
|
||||
@@ -42,16 +42,11 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_version.h"
|
||||
#include "udev_rules.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udevd.h"
|
||||
#include "logging.h"
|
||||
|
||||
struct udev_rules rules;
|
||||
static struct udev_rules rules;
|
||||
static int udevd_sock;
|
||||
static int uevent_netlink_sock;
|
||||
static int inotify_fd;
|
||||
@@ -94,8 +89,7 @@ static void asmlinkage udev_event_sig_handler(int signum)
|
||||
static int udev_event_process(struct uevent_msg *msg)
|
||||
{
|
||||
struct sigaction act;
|
||||
struct udevice udev;
|
||||
struct name_entry *name_loop;
|
||||
struct udevice *udev;
|
||||
int i;
|
||||
int retval;
|
||||
|
||||
@@ -109,27 +103,34 @@ static int udev_event_process(struct uevent_msg *msg)
|
||||
/* trigger timeout to prevent hanging processes */
|
||||
alarm(UDEV_ALARM_TIMEOUT);
|
||||
|
||||
/* reconstruct env from message */
|
||||
/* reconstruct event environment from message */
|
||||
for (i = 0; msg->envp[i]; i++)
|
||||
putenv(msg->envp[i]);
|
||||
|
||||
udev_init_device(&udev, msg->devpath, msg->subsystem, msg->action);
|
||||
retval = udev_process_event(&rules, &udev);
|
||||
udev = udev_device_init();
|
||||
if (udev == NULL)
|
||||
return -1;
|
||||
strlcpy(udev->action, msg->action, sizeof(udev->action));
|
||||
sysfs_device_set_values(udev->dev, msg->devpath, msg->subsystem);
|
||||
udev->devt = msg->devt;
|
||||
|
||||
retval = udev_device_event(&rules, udev);
|
||||
|
||||
/* run programs collected by RUN-key*/
|
||||
if (!retval) {
|
||||
list_for_each_entry(name_loop, &udev.run_list, node) {
|
||||
if (retval == 0) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
list_for_each_entry(name_loop, &udev->run_list, node) {
|
||||
if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0)
|
||||
pass_env_to_socket(&name_loop->name[strlen("socket:")], msg->devpath, msg->action);
|
||||
else
|
||||
if (run_program(name_loop->name, udev.subsystem, NULL, 0, NULL,
|
||||
if (run_program(name_loop->name, udev->dev->subsystem, NULL, 0, NULL,
|
||||
(udev_log_priority >= LOG_INFO)))
|
||||
retval = -1;
|
||||
}
|
||||
}
|
||||
|
||||
udev_cleanup_device(&udev);
|
||||
|
||||
udev_device_cleanup(udev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -819,7 +820,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
if (fd < 0)
|
||||
err("fatal, could not open /dev/null: %s", strerror(errno));
|
||||
|
||||
udev_init_config();
|
||||
udev_config_init();
|
||||
dbg("version %s", UDEV_VERSION);
|
||||
|
||||
if (getuid() != 0) {
|
||||
@@ -859,6 +860,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
}
|
||||
|
||||
/* parse the rules and keep it in memory */
|
||||
sysfs_init();
|
||||
udev_rules_init(&rules, 1);
|
||||
|
||||
if (daemonize) {
|
||||
@@ -1020,7 +1022,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
/* rules changed, set by inotify or a signal*/
|
||||
if (reload_config) {
|
||||
reload_config = 0;
|
||||
udev_rules_close(&rules);
|
||||
udev_rules_cleanup(&rules);
|
||||
udev_rules_init(&rules, 1);
|
||||
}
|
||||
|
||||
@@ -1038,7 +1040,8 @@ int main(int argc, char *argv[], char *envp[])
|
||||
}
|
||||
|
||||
exit:
|
||||
udev_rules_close(&rules);
|
||||
udev_rules_cleanup(&rules);
|
||||
sysfs_cleanup();
|
||||
|
||||
if (signal_pipe[READ_END] > 0)
|
||||
close(signal_pipe[READ_END]);
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define EVENT_QUEUE_DIR ".udev/queue"
|
||||
#define EVENT_FAILED_DIR ".udev/failed"
|
||||
|
||||
/* maximum limit of runnig childs */
|
||||
/* maximum limit of forked childs */
|
||||
#define UDEVD_MAX_CHILDS 64
|
||||
/* start to throttle forking if maximum number of running childs in our session is reached */
|
||||
#define UDEVD_MAX_CHILDS_RUNNING 16
|
||||
|
||||
+117
-143
@@ -21,17 +21,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
#ifdef USE_LOG
|
||||
@@ -48,42 +45,89 @@ void log_message (int priority, const char *format, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void print_all_attributes(struct dlist *attr_list)
|
||||
static void print_all_attributes(const char *devpath)
|
||||
{
|
||||
struct sysfs_attribute *attr;
|
||||
char value[VALUE_SIZE];
|
||||
size_t len;
|
||||
char path[PATH_SIZE];
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
|
||||
dlist_for_each_data(attr_list, attr, struct sysfs_attribute) {
|
||||
if (attr->value == NULL)
|
||||
continue;
|
||||
len = strlcpy(value, attr->value, sizeof(value));
|
||||
if (len >= sizeof(value)) {
|
||||
dbg("attribute value of '%s' too long, skip", attr->name);
|
||||
continue;
|
||||
}
|
||||
strlcpy(path, sysfs_path, sizeof(path));
|
||||
strlcat(path, devpath, sizeof(path));
|
||||
|
||||
/* remove trailing newlines */
|
||||
while (len && value[len-1] == '\n')
|
||||
value[--len] = '\0';
|
||||
/* skip nonprintable attributes */
|
||||
while (len && isprint(value[len-1]))
|
||||
len--;
|
||||
if (len) {
|
||||
dbg("attribute value of '%s' non-printable, skip", attr->name);
|
||||
continue;
|
||||
dir = opendir(path);
|
||||
if (dir != NULL) {
|
||||
for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
|
||||
char *attr_value;
|
||||
char value[NAME_SIZE];
|
||||
size_t len;
|
||||
|
||||
attr_value = sysfs_attr_get_value(devpath, dent->d_name);
|
||||
if (attr_value == NULL)
|
||||
continue;
|
||||
len = strlcpy(value, attr_value, sizeof(value));
|
||||
dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
|
||||
|
||||
/* remove trailing newlines */
|
||||
while (len && value[len-1] == '\n')
|
||||
value[--len] = '\0';
|
||||
|
||||
/* skip nonprintable attributes */
|
||||
while (len && isprint(value[len-1]))
|
||||
len--;
|
||||
if (len) {
|
||||
dbg("attribute value of '%s' non-printable, skip", dent->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
replace_untrusted_chars(value);
|
||||
printf(" SYSFS{%s}==\"%s\"\n", dent->d_name, value);
|
||||
}
|
||||
replace_untrusted_chars(value);
|
||||
printf(" SYSFS{%s}==\"%s\"\n", attr->name, value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int print_device_chain(const char *devpath)
|
||||
{
|
||||
struct sysfs_device *dev;
|
||||
|
||||
printf("\n"
|
||||
"udevinfo starts with the device the node belongs to and then walks up the\n"
|
||||
"device chain, to print for every device found, all possibly useful attributes\n"
|
||||
"in the udev key format.\n"
|
||||
"Only attributes within one device section may be used together in one rule,\n"
|
||||
"to match the device for which the node will be created.\n"
|
||||
"\n");
|
||||
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL)
|
||||
return -1;
|
||||
|
||||
printf(" looking at device '%s':\n", dev->devpath);
|
||||
printf(" KERNEL==\"%s\"\n", dev->kernel_name);
|
||||
printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
|
||||
print_all_attributes(dev->devpath);
|
||||
|
||||
/* walk up the chain of devices */
|
||||
while (1) {
|
||||
dev = sysfs_device_get_parent(dev);
|
||||
if (dev == NULL)
|
||||
break;
|
||||
printf(" looking at device '%s':\n", dev->devpath);
|
||||
printf(" ID==\"%s\"\n", dev->kernel_name);
|
||||
printf(" BUS==\"%s\"\n", dev->subsystem);
|
||||
printf(" DRIVER==\"%s\"\n", dev->driver);
|
||||
|
||||
print_all_attributes(dev->devpath);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_record(struct udevice *udev)
|
||||
{
|
||||
struct name_entry *name_loop;
|
||||
|
||||
printf("P: %s\n", udev->devpath);
|
||||
printf("P: %s\n", udev->dev->devpath);
|
||||
printf("N: %s\n", udev->name);
|
||||
list_for_each_entry(name_loop, &udev->symlink_list, node)
|
||||
printf("S: %s\n", name_loop->name);
|
||||
@@ -91,82 +135,8 @@ static void print_record(struct udevice *udev)
|
||||
printf("E: %s\n", name_loop->name);
|
||||
}
|
||||
|
||||
static int print_device_chain(const char *path)
|
||||
{
|
||||
struct sysfs_class_device *class_dev;
|
||||
struct sysfs_class_device *class_dev_parent;
|
||||
struct sysfs_attribute *attr;
|
||||
struct sysfs_device *sysfs_dev;
|
||||
struct dlist *attr_list;
|
||||
int retval = 0;
|
||||
|
||||
/* get the class dev */
|
||||
class_dev = sysfs_open_class_device_path(path);
|
||||
if (class_dev == NULL) {
|
||||
fprintf(stderr, "couldn't get the class device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
|
||||
"device chain, to print for every device found, all possibly useful attributes\n"
|
||||
"in the udev key format.\n"
|
||||
"Only attributes within one device section may be used together in one rule,\n"
|
||||
"to match the device for which the node will be created.\n"
|
||||
"\n");
|
||||
|
||||
/* look for the 'dev' file */
|
||||
attr = sysfs_get_classdev_attr(class_dev, "dev");
|
||||
if (attr != NULL)
|
||||
printf("device '%s' has major:minor %s", class_dev->path, attr->value);
|
||||
|
||||
/* open sysfs class device directory and print all attributes */
|
||||
printf(" looking at class device '%s':\n", class_dev->path);
|
||||
printf(" KERNEL==\"%s\"\n", class_dev->name);
|
||||
printf(" SUBSYSTEM==\"%s\"\n", class_dev->classname);
|
||||
|
||||
attr_list = sysfs_get_classdev_attributes(class_dev);
|
||||
if (attr_list == NULL) {
|
||||
fprintf(stderr, "couldn't open class device directory\n");
|
||||
retval = -1;
|
||||
goto exit;
|
||||
}
|
||||
print_all_attributes(attr_list);
|
||||
|
||||
/* get the device link (if parent exists look here) */
|
||||
class_dev_parent = sysfs_get_classdev_parent(class_dev);
|
||||
if (class_dev_parent != NULL)
|
||||
sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
|
||||
else
|
||||
sysfs_dev = sysfs_get_classdev_device(class_dev);
|
||||
|
||||
if (sysfs_dev != NULL)
|
||||
printf("follow the \"device\"-link to the physical device:\n");
|
||||
|
||||
/* look the device chain upwards */
|
||||
while (sysfs_dev != NULL) {
|
||||
printf(" looking at the device chain at '%s':\n", sysfs_dev->path);
|
||||
printf(" BUS==\"%s\"\n", sysfs_dev->bus);
|
||||
printf(" ID==\"%s\"\n", sysfs_dev->bus_id);
|
||||
printf(" DRIVER==\"%s\"\n", sysfs_dev->driver_name);
|
||||
|
||||
attr_list = sysfs_get_device_attributes(sysfs_dev);
|
||||
if (attr_list != NULL)
|
||||
print_all_attributes(attr_list);
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
sysfs_dev = sysfs_get_device_parent(sysfs_dev);
|
||||
if (sysfs_dev == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
sysfs_close_class_device(class_dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void export_name_devpath(struct udevice *udev) {
|
||||
printf("%s=%s/%s\n", udev->devpath, udev_root, udev->name);
|
||||
printf("%s=%s/%s\n", udev->dev->devpath, udev_root, udev->name);
|
||||
}
|
||||
|
||||
static void export_record(struct udevice *udev) {
|
||||
@@ -180,12 +150,14 @@ static void export_db(void fnct(struct udevice *udev)) {
|
||||
|
||||
udev_db_get_all_entries(&name_list);
|
||||
list_for_each_entry(name_loop, &name_list, node) {
|
||||
struct udevice udev_db;
|
||||
struct udevice *udev_db;
|
||||
|
||||
udev_init_device(&udev_db, NULL, NULL, NULL);
|
||||
if (udev_db_get_device(&udev_db, name_loop->name) == 0)
|
||||
fnct(&udev_db);
|
||||
udev_cleanup_device(&udev_db);
|
||||
udev_db = udev_device_init();
|
||||
if (udev_db == NULL)
|
||||
continue;
|
||||
if (udev_db_get_device(udev_db, name_loop->name) == 0)
|
||||
fnct(udev_db);
|
||||
udev_device_cleanup(udev_db);
|
||||
}
|
||||
name_list_cleanup(&name_list);
|
||||
}
|
||||
@@ -215,7 +187,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
static const char short_options[] = "aden:p:q:rVh";
|
||||
int option;
|
||||
struct udevice udev;
|
||||
struct udevice *udev;
|
||||
int root = 0;
|
||||
|
||||
enum action_type {
|
||||
@@ -239,12 +211,18 @@ int main(int argc, char *argv[], char *envp[])
|
||||
char temp[PATH_SIZE];
|
||||
struct name_entry *name_loop;
|
||||
char *pos;
|
||||
int retval = 0;
|
||||
int rc = 0;
|
||||
|
||||
logging_init("udevinfo");
|
||||
|
||||
udev_init_config();
|
||||
udev_init_device(&udev, NULL, NULL, NULL);
|
||||
udev_config_init();
|
||||
sysfs_init();
|
||||
|
||||
udev = udev_device_init();
|
||||
if (udev == NULL) {
|
||||
rc = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* get command line options */
|
||||
while (1) {
|
||||
@@ -260,7 +238,11 @@ int main(int argc, char *argv[], char *envp[])
|
||||
break;
|
||||
case 'p':
|
||||
dbg("udev path: %s\n", optarg);
|
||||
strlcpy(path, optarg, sizeof(path));
|
||||
/* remove sysfs mountpoint if not given */
|
||||
if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
|
||||
strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
|
||||
else
|
||||
strlcpy(path, optarg, sizeof(path));
|
||||
break;
|
||||
case 'q':
|
||||
dbg("udev query: %s\n", optarg);
|
||||
@@ -286,7 +268,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "unknown query type\n");
|
||||
retval = 2;
|
||||
rc = 2;
|
||||
goto exit;
|
||||
case 'r':
|
||||
if (action == ACTION_NONE)
|
||||
@@ -331,10 +313,9 @@ int main(int argc, char *argv[], char *envp[])
|
||||
pos = path;
|
||||
}
|
||||
}
|
||||
retval = udev_db_get_device(&udev, pos);
|
||||
if (retval != 0) {
|
||||
if (udev_db_get_device(udev, pos) != 0) {
|
||||
fprintf(stderr, "no record for '%s' in database\n", pos);
|
||||
retval = 3;
|
||||
rc = 3;
|
||||
goto exit;
|
||||
}
|
||||
} else if (name[0] != '\0') {
|
||||
@@ -348,46 +329,45 @@ int main(int argc, char *argv[], char *envp[])
|
||||
} else
|
||||
pos = name;
|
||||
|
||||
retval = udev_db_lookup_name(pos, devpath, sizeof(devpath));
|
||||
if (retval != 0) {
|
||||
if (udev_db_lookup_name(pos, devpath, sizeof(devpath)) != 0) {
|
||||
fprintf(stderr, "no record for '%s' in database\n", pos);
|
||||
retval = 3;
|
||||
rc = 3;
|
||||
goto exit;
|
||||
}
|
||||
udev_db_get_device(&udev, devpath);
|
||||
udev_db_get_device(udev, devpath);
|
||||
} else {
|
||||
fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
|
||||
retval = 4;
|
||||
rc = 4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch(query) {
|
||||
case QUERY_NAME:
|
||||
if (root)
|
||||
printf("%s/%s\n", udev_root, udev.name);
|
||||
printf("%s/%s\n", udev_root, udev->name);
|
||||
else
|
||||
printf("%s\n", udev.name);
|
||||
printf("%s\n", udev->name);
|
||||
break;
|
||||
case QUERY_SYMLINK:
|
||||
if (list_empty(&udev.symlink_list))
|
||||
if (list_empty(&udev->symlink_list))
|
||||
goto exit;
|
||||
if (root)
|
||||
list_for_each_entry(name_loop, &udev.symlink_list, node)
|
||||
list_for_each_entry(name_loop, &udev->symlink_list, node)
|
||||
printf("%s/%s ", udev_root, name_loop->name);
|
||||
else
|
||||
list_for_each_entry(name_loop, &udev.symlink_list, node)
|
||||
list_for_each_entry(name_loop, &udev->symlink_list, node)
|
||||
printf("%s ", name_loop->name);
|
||||
printf("\n");
|
||||
break;
|
||||
case QUERY_PATH:
|
||||
printf("%s\n", udev.devpath);
|
||||
printf("%s\n", udev->dev->devpath);
|
||||
goto exit;
|
||||
case QUERY_ENV:
|
||||
list_for_each_entry(name_loop, &udev.env_list, node)
|
||||
list_for_each_entry(name_loop, &udev->env_list, node)
|
||||
printf("%s\n", name_loop->name);
|
||||
break;
|
||||
case QUERY_ALL:
|
||||
print_record(&udev);
|
||||
print_record(udev);
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
@@ -397,29 +377,23 @@ int main(int argc, char *argv[], char *envp[])
|
||||
case ACTION_ATTRIBUTE_WALK:
|
||||
if (path[0] == '\0') {
|
||||
fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
|
||||
retval = 4;
|
||||
rc = 4;
|
||||
goto exit;
|
||||
} else {
|
||||
if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
|
||||
/* prepend sysfs mountpoint if not given */
|
||||
snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
|
||||
temp[sizeof(temp)-1] = '\0';
|
||||
strlcpy(path, temp, sizeof(temp));
|
||||
}
|
||||
} else
|
||||
print_device_chain(path);
|
||||
}
|
||||
break;
|
||||
case ACTION_ROOT:
|
||||
printf("%s\n", udev_root);
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
retval = 1;
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
udev_cleanup_device(&udev);
|
||||
udev_device_cleanup(udev);
|
||||
sysfs_cleanup();
|
||||
logging_close();
|
||||
return retval;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@
|
||||
|
||||
#include "udev.h"
|
||||
#include "udevd.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
|
||||
static int uevent_netlink_sock;
|
||||
static int udev_monitor_sock;
|
||||
|
||||
+1
-3
@@ -33,9 +33,7 @@
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "udev.h"
|
||||
#include "udev_version.h"
|
||||
#include "udevd.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* global variables */
|
||||
static int sock = -1;
|
||||
@@ -67,7 +65,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
|
||||
logging_init("udevsend");
|
||||
#ifdef USE_LOG
|
||||
udev_init_config();
|
||||
udev_config_init();
|
||||
#endif
|
||||
dbg("version %s", UDEV_VERSION);
|
||||
|
||||
|
||||
+44
-46
@@ -38,14 +38,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev_libc_wrapper.h"
|
||||
#include "udev.h"
|
||||
#include "udev_version.h"
|
||||
#include "logging.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_rules.h"
|
||||
#include "list.h"
|
||||
|
||||
static const char *udev_run_str;
|
||||
static const char *udev_log_str;
|
||||
@@ -68,11 +62,10 @@ void log_message(int priority, const char *format, ...)
|
||||
struct device {
|
||||
struct list_head node;
|
||||
char path[PATH_SIZE];
|
||||
char subsys[NAME_SIZE];
|
||||
};
|
||||
|
||||
/* sort files in lexical order */
|
||||
static int device_list_insert(const char *path, char *subsystem, struct list_head *device_list)
|
||||
static int device_list_insert(const char *path, struct list_head *device_list)
|
||||
{
|
||||
struct device *loop_device;
|
||||
struct device *new_device;
|
||||
@@ -93,9 +86,8 @@ static int device_list_insert(const char *path, char *subsystem, struct list_hea
|
||||
}
|
||||
|
||||
strlcpy(new_device->path, devpath, sizeof(new_device->path));
|
||||
strlcpy(new_device->subsys, subsystem, sizeof(new_device->subsys));
|
||||
list_add_tail(&new_device->node, &loop_device->node);
|
||||
dbg("add '%s' from subsys '%s'", new_device->path, new_device->subsys);
|
||||
dbg("add '%s'" , new_device->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,64 +104,68 @@ static char *first_list[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int add_device(const char *devpath, const char *subsystem)
|
||||
static int add_device(const char *devpath)
|
||||
{
|
||||
struct udevice udev;
|
||||
struct sysfs_class_device *class_dev;
|
||||
char path[PATH_SIZE];
|
||||
struct sysfs_device *dev;
|
||||
struct udevice *udev;
|
||||
|
||||
/* clear and set environment for next event */
|
||||
clearenv();
|
||||
setenv("ACTION", "add", 1);
|
||||
setenv("DEVPATH", devpath, 1);
|
||||
setenv("SUBSYSTEM", subsystem, 1);
|
||||
setenv("UDEV_START", "1", 1);
|
||||
if (udev_log_str)
|
||||
setenv("UDEV_LOG", udev_log_str, 1);
|
||||
if (udev_run_str)
|
||||
setenv("UDEV_RUN", udev_run_str, 1);
|
||||
dbg("add '%s'", devpath);
|
||||
|
||||
snprintf(path, sizeof(path), "%s%s", sysfs_path, devpath);
|
||||
path[sizeof(path)-1] = '\0';
|
||||
class_dev = sysfs_open_class_device_path(path);
|
||||
if (class_dev == NULL) {
|
||||
dbg("sysfs_open_class_device_path failed");
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL)
|
||||
return -1;
|
||||
|
||||
udev = udev_device_init();
|
||||
if (udev == NULL)
|
||||
return -1;
|
||||
|
||||
/* override built-in sysfs device */
|
||||
udev->dev = dev;
|
||||
strcpy(udev->action, "add");
|
||||
udev->devt = udev_device_get_devt(udev);
|
||||
|
||||
if (strcmp(udev->dev->subsystem, "net") != 0) {
|
||||
udev->devt = udev_device_get_devt(udev);
|
||||
if (major(udev->devt) == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
udev_init_device(&udev, &class_dev->path[strlen(sysfs_path)], subsystem, "add");
|
||||
udev.devt = get_devt(class_dev);
|
||||
if (!udev.devt && udev.type != DEV_NET) {
|
||||
dbg("sysfs_open_class_device_path failed");
|
||||
return -1;
|
||||
}
|
||||
udev_rules_get_name(&rules, &udev, class_dev);
|
||||
if (udev.ignore_device) {
|
||||
dbg("add '%s'", udev->dev->devpath);
|
||||
setenv("DEVPATH", udev->dev->devpath, 1);
|
||||
setenv("SUBSYSTEM", udev->dev->subsystem, 1);
|
||||
|
||||
udev_rules_get_name(&rules, udev);
|
||||
if (udev->ignore_device) {
|
||||
dbg("device event will be ignored");
|
||||
goto exit;
|
||||
}
|
||||
if (udev.name[0] == '\0') {
|
||||
if (udev->name[0] == '\0') {
|
||||
dbg("device node creation supressed");
|
||||
goto run;
|
||||
}
|
||||
|
||||
udev_add_device(&udev, class_dev);
|
||||
udev_add_device(udev);
|
||||
run:
|
||||
if (udev_run && !list_empty(&udev.run_list)) {
|
||||
if (udev_run && !list_empty(&udev->run_list)) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
dbg("executing run list");
|
||||
list_for_each_entry(name_loop, &udev.run_list, node) {
|
||||
list_for_each_entry(name_loop, &udev->run_list, node) {
|
||||
if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0)
|
||||
pass_env_to_socket(&name_loop->name[strlen("socket:")], devpath, "add");
|
||||
pass_env_to_socket(&name_loop->name[strlen("socket:")], udev->dev->devpath, "add");
|
||||
else
|
||||
run_program(name_loop->name, udev.subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO));
|
||||
run_program(name_loop->name, udev->dev->subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO));
|
||||
}
|
||||
}
|
||||
exit:
|
||||
sysfs_close_class_device(class_dev);
|
||||
udev_cleanup_device(&udev);
|
||||
udev_device_cleanup(udev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -184,7 +180,7 @@ static void exec_list(struct list_head *device_list)
|
||||
list_for_each_entry_safe(loop_device, tmp_device, device_list, node) {
|
||||
for (i = 0; first_list[i] != NULL; i++) {
|
||||
if (strncmp(loop_device->path, first_list[i], strlen(first_list[i])) == 0) {
|
||||
add_device(loop_device->path, loop_device->subsys);
|
||||
add_device(loop_device->path);
|
||||
list_del(&loop_device->node);
|
||||
free(loop_device);
|
||||
break;
|
||||
@@ -204,14 +200,14 @@ static void exec_list(struct list_head *device_list)
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
add_device(loop_device->path, loop_device->subsys);
|
||||
add_device(loop_device->path);
|
||||
list_del(&loop_device->node);
|
||||
free(loop_device);
|
||||
}
|
||||
|
||||
/* handle the rest of the devices left over, if any */
|
||||
list_for_each_entry_safe(loop_device, tmp_device, device_list, node) {
|
||||
add_device(loop_device->path, loop_device->subsys);
|
||||
add_device(loop_device->path);
|
||||
list_del(&loop_device->node);
|
||||
free(loop_device);
|
||||
}
|
||||
@@ -253,7 +249,7 @@ static void udev_scan_block(struct list_head *device_list)
|
||||
snprintf(dirname, sizeof(dirname), "%s/%s", base, dent->d_name);
|
||||
dirname[sizeof(dirname)-1] = '\0';
|
||||
if (has_devt(dirname))
|
||||
device_list_insert(dirname, "block", device_list);
|
||||
device_list_insert(dirname, device_list);
|
||||
else
|
||||
continue;
|
||||
|
||||
@@ -270,7 +266,7 @@ static void udev_scan_block(struct list_head *device_list)
|
||||
dirname2[sizeof(dirname2)-1] = '\0';
|
||||
|
||||
if (has_devt(dirname2))
|
||||
device_list_insert(dirname2, "block", device_list);
|
||||
device_list_insert(dirname2, device_list);
|
||||
}
|
||||
closedir(dir2);
|
||||
}
|
||||
@@ -313,7 +309,7 @@ static void udev_scan_class(struct list_head *device_list)
|
||||
dirname2[sizeof(dirname2)-1] = '\0';
|
||||
|
||||
if (has_devt(dirname2) || strcmp(dent->d_name, "net") == 0)
|
||||
device_list_insert(dirname2, dent->d_name, device_list);
|
||||
device_list_insert(dirname2, device_list);
|
||||
}
|
||||
closedir(dir2);
|
||||
}
|
||||
@@ -339,7 +335,7 @@ int main(int argc, char *argv[], char *envp[])
|
||||
struct sigaction act;
|
||||
|
||||
logging_init("udevstart");
|
||||
udev_init_config();
|
||||
udev_config_init();
|
||||
dbg("version %s", UDEV_VERSION);
|
||||
|
||||
udev_run_str = getenv("UDEV_RUN");
|
||||
@@ -361,13 +357,15 @@ int main(int argc, char *argv[], char *envp[])
|
||||
/* trigger timeout to prevent hanging processes */
|
||||
alarm(UDEV_ALARM_TIMEOUT);
|
||||
|
||||
sysfs_init();
|
||||
udev_rules_init(&rules, 1);
|
||||
|
||||
udev_scan_class(&device_list);
|
||||
udev_scan_block(&device_list);
|
||||
exec_list(&device_list);
|
||||
|
||||
udev_rules_close(&rules);
|
||||
udev_rules_cleanup(&rules);
|
||||
sysfs_cleanup();
|
||||
logging_close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
.SH "NAME"
|
||||
udevtest \- simulate a udev run and print the action to the console
|
||||
.SH "SYNOPSIS"
|
||||
.HP 31
|
||||
\fBudevtest \fR\fB\fIdevice\-path\fR\fR\fB \fR\fB\fIsubsystem\fR\fR
|
||||
.HP 21
|
||||
\fBudevtest \fR\fB\fIdevice\-path\fR\fR
|
||||
.SH "DESCRIPTION"
|
||||
.PP
|
||||
udevtest simulates a udev run for the given device and prints out the name of the node udev would have created, or the name of the network interface, that would have been renamend.
|
||||
|
||||
+45
-40
@@ -21,17 +21,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "libsysfs/sysfs/libsysfs.h"
|
||||
#include "udev.h"
|
||||
#include "udev_utils.h"
|
||||
#include "udev_version.h"
|
||||
#include "udev_rules.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
#ifdef USE_LOG
|
||||
@@ -53,28 +51,28 @@ void log_message (int priority, const char *format, ...)
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
struct udev_rules rules;
|
||||
struct sysfs_class_device *class_dev;
|
||||
char *devpath;
|
||||
char path[PATH_SIZE];
|
||||
char temp[PATH_SIZE];
|
||||
struct udevice udev;
|
||||
char *subsystem = NULL;
|
||||
struct udevice *udev;
|
||||
struct sysfs_device *dev;
|
||||
int retval;
|
||||
int rc = 0;
|
||||
|
||||
info("version %s", UDEV_VERSION);
|
||||
|
||||
/* initialize our configuration */
|
||||
udev_init_config();
|
||||
udev_config_init();
|
||||
if (udev_log_priority < LOG_INFO)
|
||||
udev_log_priority = LOG_INFO;
|
||||
|
||||
if (argc != 3) {
|
||||
info("Usage: udevtest <devpath> <subsystem>");
|
||||
if (argc != 2) {
|
||||
info("Usage: udevtest <devpath>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* remove sysfs_path if given */
|
||||
if (strncmp(argv[1], sysfs_path, strlen(sysfs_path)) == 0)
|
||||
devpath = &argv[1][strlen(sysfs_path)] ;
|
||||
devpath = &argv[1][strlen(sysfs_path)];
|
||||
else
|
||||
if (argv[1][0] != '/') {
|
||||
/* prepend '/' if missing */
|
||||
@@ -84,39 +82,46 @@ int main(int argc, char *argv[], char *envp[])
|
||||
} else
|
||||
devpath = argv[1];
|
||||
|
||||
subsystem = argv[2];
|
||||
setenv("DEVPATH", devpath, 1);
|
||||
setenv("SUBSYSTEM", subsystem, 1);
|
||||
setenv("ACTION", "add", 1);
|
||||
info("looking at device '%s' from subsystem '%s'", devpath, subsystem);
|
||||
|
||||
/* initialize the naming deamon */
|
||||
sysfs_init();
|
||||
udev_rules_init(&rules, 0);
|
||||
|
||||
/* fill in values and test_run flag*/
|
||||
udev_init_device(&udev, devpath, subsystem, "add");
|
||||
|
||||
/* open the device */
|
||||
snprintf(path, sizeof(path), "%s%s", sysfs_path, udev.devpath);
|
||||
path[sizeof(path)-1] = '\0';
|
||||
class_dev = sysfs_open_class_device_path(path);
|
||||
if (class_dev == NULL) {
|
||||
info("sysfs_open_class_device_path failed");
|
||||
return 1;
|
||||
dev = sysfs_device_get(devpath);
|
||||
if (dev == NULL) {
|
||||
info("unable to open '%s'", devpath);
|
||||
rc = 2;
|
||||
goto exit;
|
||||
}
|
||||
info("opened class_dev->name='%s'", class_dev->name);
|
||||
|
||||
if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS)
|
||||
udev.devt = get_devt(class_dev);
|
||||
udev = udev_device_init();
|
||||
if (udev == NULL) {
|
||||
info("can't open device");
|
||||
rc = 3;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* override built-in sysfs device */
|
||||
udev->dev = dev;
|
||||
strcpy(udev->action, "add");
|
||||
udev->devt = udev_device_get_devt(udev);
|
||||
|
||||
/* simulate node creation with test flag */
|
||||
udev.test_run = 1;
|
||||
if (udev.type == DEV_NET || udev.devt) {
|
||||
udev_rules_get_name(&rules, &udev, class_dev);
|
||||
udev_add_device(&udev, class_dev);
|
||||
} else
|
||||
info("only char and block devices with a dev-file are supported by this test program");
|
||||
sysfs_close_class_device(class_dev);
|
||||
udev->test_run = 1;
|
||||
|
||||
return 0;
|
||||
setenv("DEVPATH", udev->dev->devpath, 1);
|
||||
setenv("SUBSYSTEM", udev->dev->subsystem, 1);
|
||||
setenv("ACTION", "add", 1);
|
||||
|
||||
info("looking at device '%s' from subsystem '%s'", udev->dev->devpath, udev->dev->subsystem);
|
||||
retval = udev_device_event(&rules, udev);
|
||||
if (retval == 0) {
|
||||
struct name_entry *name_loop;
|
||||
|
||||
list_for_each_entry(name_loop, &udev->run_list, node)
|
||||
info("run: '%s'", name_loop->name);
|
||||
}
|
||||
|
||||
exit:
|
||||
udev_rules_cleanup(&rules);
|
||||
sysfs_cleanup();
|
||||
return rc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user