summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile44
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/Makefile.in496
-rw-r--r--lib/dlist.c620
-rw-r--r--lib/sysfs.h5
-rw-r--r--lib/sysfs_bus.c467
-rw-r--r--lib/sysfs_class.c686
-rw-r--r--lib/sysfs_device.c674
-rw-r--r--lib/sysfs_dir.c932
-rw-r--r--lib/sysfs_driver.c379
-rw-r--r--lib/sysfs_utils.c393
11 files changed, 4045 insertions, 657 deletions
diff --git a/lib/Makefile b/lib/Makefile
deleted file mode 100644
index cf07a84..0000000
--- a/lib/Makefile
+++ /dev/null
@@ -1,44 +0,0 @@
-# Makefile for libsysfs.a
-# Copyright (c) International Business Machines Corp., 2003
-
-CC=gcc
-
-H_INCLUDE=../include
-LIB_INCLUDE=.
-OBJS=sysfs_bus.o sysfs_class.o sysfs_device.o sysfs_dir.o sysfs_driver.o \
- sysfs_utils.o
-
-# Install directory
-
-# Options
-CFLAGS=-O2 -Wall -ansi -g
-
-# sysfs library
-LIBSYSFS=libsysfs.a
-
-RM=rm -f
-
-libsysfs.a: $(OBJS)
- ar cru $(LIBSYSFS) $(OBJS)
- ranlib $(LIBSYSFS)
-
-sysfs_bus.o: sysfs_bus.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_bus.c
-
-sysfs_class.o: sysfs_class.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_class.c
-
-sysfs_device.o: sysfs_device.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_device.c
-
-sysfs_dir.o: sysfs_dir.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_dir.c
-
-sysfs_driver.o: sysfs_driver.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_driver.c
-
-sysfs_utils.o: sysfs_utils.c
- $(CC) -I$(H_INCLUDE) -I$(LIB_INCLUDE) $(CFLAGS) -c sysfs_utils.c
-
-clean:
- $(RM) *.o *~ core $(LIBSYSFS)
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..2a11ba9
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,6 @@
+lib_LTLIBRARIES = libsysfs.la
+libsysfs_la_SOURCES = sysfs_utils.c sysfs_dir.c sysfs_bus.c sysfs_class.c \
+ sysfs_device.c sysfs_driver.c sysfs.h dlist.c
+INCLUDES = -I../include
+libsysfs_la_LDFLAGS = -version-info 1:2:0
+libsysfs_la_CFLAGS = -Wall -W -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls
diff --git a/lib/Makefile.in b/lib/Makefile.in
new file mode 100644
index 0000000..72a6e49
--- /dev/null
+++ b/lib/Makefile.in
@@ -0,0 +1,496 @@
+# Makefile.in generated by automake 1.6.3 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+AMTAR = @AMTAR@
+AS = @AS@
+AWK = @AWK@
+CC = @CC@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+OBJDUMP = @OBJDUMP@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+STRIP = @STRIP@
+VERSION = @VERSION@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+lib_LTLIBRARIES = libsysfs.la
+libsysfs_la_SOURCES = sysfs_utils.c sysfs_dir.c sysfs_bus.c sysfs_class.c \
+ sysfs_device.c sysfs_driver.c sysfs.h dlist.c
+
+INCLUDES = -I../include
+libsysfs_la_LDFLAGS = -version-info 1:2:0
+libsysfs_la_CFLAGS = -Wall -W -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls
+subdir = lib
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(lib_LTLIBRARIES)
+
+libsysfs_la_LIBADD =
+am_libsysfs_la_OBJECTS = libsysfs_la-sysfs_utils.lo \
+ libsysfs_la-sysfs_dir.lo libsysfs_la-sysfs_bus.lo \
+ libsysfs_la-sysfs_class.lo libsysfs_la-sysfs_device.lo \
+ libsysfs_la-sysfs_driver.lo libsysfs_la-dlist.lo
+libsysfs_la_OBJECTS = $(am_libsysfs_la_OBJECTS)
+
+DEFS = @DEFS@
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/libsysfs_la-dlist.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_bus.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_class.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_device.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_dir.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_driver.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/libsysfs_la-sysfs_utils.Plo
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
+ $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+CFLAGS = @CFLAGS@
+DIST_SOURCES = $(libsysfs_la_SOURCES)
+DIST_COMMON = Makefile.am Makefile.in
+SOURCES = $(libsysfs_la_SOURCES)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lib/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+libLTLIBRARIES_INSTALL = $(INSTALL)
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \
+ $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test -z "$dir" && dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libsysfs_la-sysfs_utils.lo: sysfs_utils.c
+libsysfs_la-sysfs_dir.lo: sysfs_dir.c
+libsysfs_la-sysfs_bus.lo: sysfs_bus.c
+libsysfs_la-sysfs_class.lo: sysfs_class.c
+libsysfs_la-sysfs_device.lo: sysfs_device.c
+libsysfs_la-sysfs_driver.lo: sysfs_driver.c
+libsysfs_la-dlist.lo: dlist.c
+libsysfs.la: $(libsysfs_la_OBJECTS) $(libsysfs_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(libsysfs_la_LDFLAGS) $(libsysfs_la_OBJECTS) $(libsysfs_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-dlist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_bus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_class.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_device.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_dir.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_driver.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_utils.Plo@am__quote@
+
+distclean-depend:
+ -rm -rf ./$(DEPDIR)
+
+.c.o:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$<
+
+.c.obj:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `cygpath -w $<`
+
+.c.lo:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
+libsysfs_la-sysfs_utils.o: sysfs_utils.c
+@AMDEP_TRUE@ source='sysfs_utils.c' object='libsysfs_la-sysfs_utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_utils.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_utils.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_utils.o `test -f 'sysfs_utils.c' || echo '$(srcdir)/'`sysfs_utils.c
+
+libsysfs_la-sysfs_utils.obj: sysfs_utils.c
+@AMDEP_TRUE@ source='sysfs_utils.c' object='libsysfs_la-sysfs_utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_utils.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_utils.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_utils.obj `cygpath -w sysfs_utils.c`
+
+libsysfs_la-sysfs_utils.lo: sysfs_utils.c
+@AMDEP_TRUE@ source='sysfs_utils.c' object='libsysfs_la-sysfs_utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_utils.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_utils.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_utils.lo `test -f 'sysfs_utils.c' || echo '$(srcdir)/'`sysfs_utils.c
+
+libsysfs_la-sysfs_dir.o: sysfs_dir.c
+@AMDEP_TRUE@ source='sysfs_dir.c' object='libsysfs_la-sysfs_dir.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_dir.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_dir.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_dir.o `test -f 'sysfs_dir.c' || echo '$(srcdir)/'`sysfs_dir.c
+
+libsysfs_la-sysfs_dir.obj: sysfs_dir.c
+@AMDEP_TRUE@ source='sysfs_dir.c' object='libsysfs_la-sysfs_dir.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_dir.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_dir.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_dir.obj `cygpath -w sysfs_dir.c`
+
+libsysfs_la-sysfs_dir.lo: sysfs_dir.c
+@AMDEP_TRUE@ source='sysfs_dir.c' object='libsysfs_la-sysfs_dir.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_dir.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_dir.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_dir.lo `test -f 'sysfs_dir.c' || echo '$(srcdir)/'`sysfs_dir.c
+
+libsysfs_la-sysfs_bus.o: sysfs_bus.c
+@AMDEP_TRUE@ source='sysfs_bus.c' object='libsysfs_la-sysfs_bus.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_bus.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_bus.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_bus.o `test -f 'sysfs_bus.c' || echo '$(srcdir)/'`sysfs_bus.c
+
+libsysfs_la-sysfs_bus.obj: sysfs_bus.c
+@AMDEP_TRUE@ source='sysfs_bus.c' object='libsysfs_la-sysfs_bus.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_bus.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_bus.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_bus.obj `cygpath -w sysfs_bus.c`
+
+libsysfs_la-sysfs_bus.lo: sysfs_bus.c
+@AMDEP_TRUE@ source='sysfs_bus.c' object='libsysfs_la-sysfs_bus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_bus.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_bus.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_bus.lo `test -f 'sysfs_bus.c' || echo '$(srcdir)/'`sysfs_bus.c
+
+libsysfs_la-sysfs_class.o: sysfs_class.c
+@AMDEP_TRUE@ source='sysfs_class.c' object='libsysfs_la-sysfs_class.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_class.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_class.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_class.o `test -f 'sysfs_class.c' || echo '$(srcdir)/'`sysfs_class.c
+
+libsysfs_la-sysfs_class.obj: sysfs_class.c
+@AMDEP_TRUE@ source='sysfs_class.c' object='libsysfs_la-sysfs_class.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_class.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_class.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_class.obj `cygpath -w sysfs_class.c`
+
+libsysfs_la-sysfs_class.lo: sysfs_class.c
+@AMDEP_TRUE@ source='sysfs_class.c' object='libsysfs_la-sysfs_class.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_class.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_class.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_class.lo `test -f 'sysfs_class.c' || echo '$(srcdir)/'`sysfs_class.c
+
+libsysfs_la-sysfs_device.o: sysfs_device.c
+@AMDEP_TRUE@ source='sysfs_device.c' object='libsysfs_la-sysfs_device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_device.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_device.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_device.o `test -f 'sysfs_device.c' || echo '$(srcdir)/'`sysfs_device.c
+
+libsysfs_la-sysfs_device.obj: sysfs_device.c
+@AMDEP_TRUE@ source='sysfs_device.c' object='libsysfs_la-sysfs_device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_device.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_device.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_device.obj `cygpath -w sysfs_device.c`
+
+libsysfs_la-sysfs_device.lo: sysfs_device.c
+@AMDEP_TRUE@ source='sysfs_device.c' object='libsysfs_la-sysfs_device.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_device.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_device.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_device.lo `test -f 'sysfs_device.c' || echo '$(srcdir)/'`sysfs_device.c
+
+libsysfs_la-sysfs_driver.o: sysfs_driver.c
+@AMDEP_TRUE@ source='sysfs_driver.c' object='libsysfs_la-sysfs_driver.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_driver.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_driver.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_driver.o `test -f 'sysfs_driver.c' || echo '$(srcdir)/'`sysfs_driver.c
+
+libsysfs_la-sysfs_driver.obj: sysfs_driver.c
+@AMDEP_TRUE@ source='sysfs_driver.c' object='libsysfs_la-sysfs_driver.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_driver.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_driver.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_driver.obj `cygpath -w sysfs_driver.c`
+
+libsysfs_la-sysfs_driver.lo: sysfs_driver.c
+@AMDEP_TRUE@ source='sysfs_driver.c' object='libsysfs_la-sysfs_driver.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-sysfs_driver.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-sysfs_driver.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_driver.lo `test -f 'sysfs_driver.c' || echo '$(srcdir)/'`sysfs_driver.c
+
+libsysfs_la-dlist.o: dlist.c
+@AMDEP_TRUE@ source='dlist.c' object='libsysfs_la-dlist.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-dlist.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-dlist.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-dlist.o `test -f 'dlist.c' || echo '$(srcdir)/'`dlist.c
+
+libsysfs_la-dlist.obj: dlist.c
+@AMDEP_TRUE@ source='dlist.c' object='libsysfs_la-dlist.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-dlist.Po' tmpdepfile='$(DEPDIR)/libsysfs_la-dlist.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-dlist.obj `cygpath -w dlist.c`
+
+libsysfs_la-dlist.lo: dlist.c
+@AMDEP_TRUE@ source='dlist.c' object='libsysfs_la-dlist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/libsysfs_la-dlist.Plo' tmpdepfile='$(DEPDIR)/libsysfs_la-dlist.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-dlist.lo `test -f 'dlist.c' || echo '$(srcdir)/'`dlist.c
+CCDEPMODE = @CCDEPMODE@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ETAGS = etags
+ETAGSFLAGS =
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$tags$$unique" \
+ || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ @list='$(DISTFILES)'; for file in $$list; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-compile distclean-depend \
+ distclean-generic distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool distclean distclean-compile \
+ distclean-depend distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am info info-am install \
+ install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool tags uninstall \
+ uninstall-am uninstall-info-am uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/dlist.c b/lib/dlist.c
new file mode 100644
index 0000000..c4ee324
--- /dev/null
+++ b/lib/dlist.c
@@ -0,0 +1,620 @@
+/*
+ * 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 "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;
+
+}
+
diff --git a/lib/sysfs.h b/lib/sysfs.h
index eb2a002..4ef8c28 100644
--- a/lib/sysfs.h
+++ b/lib/sysfs.h
@@ -3,7 +3,7 @@
*
* Internal Header Definitions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -34,9 +34,6 @@
#include <errno.h>
/* external library functions */
-extern int lstat(const char *file_name, struct stat *buf);
-extern int readlink(const char *path, char *buf, size_t bufsize);
-extern int getpagesize(void);
extern int isascii(int c);
/* Debugging */
diff --git a/lib/sysfs_bus.c b/lib/sysfs_bus.c
index c7b6036..d47b94c 100644
--- a/lib/sysfs_bus.c
+++ b/lib/sysfs_bus.c
@@ -3,7 +3,7 @@
*
* Generic bus utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,28 +23,62 @@
#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 devices' bus ids.
+ * @a: device id looking for
+ * @b: sysfs_device comparing being compared
+ * returns 1 if a==b->bus_id or 0 not equal
+ */
+static int bus_device_id_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_device *)b)->bus_id)
+ == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * compares drivers' names.
+ * @a: driver name looking for
+ * @b: sysfs_driver comparing being compared
+ * returns 1 if a==b->name or 0 not equal
+ */
+static int bus_driver_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_driver *)b)->name) == 0)
+ return 1;
+ return 0;
+}
+
/**
* sysfs_close_bus: close single bus
* @bus: bus structure
*/
void sysfs_close_bus(struct sysfs_bus *bus)
{
- struct sysfs_device *curdev = NULL, *nextdev = NULL;
- struct sysfs_driver *curdrv = NULL, *nextdrv = NULL;
-
if (bus != NULL) {
if (bus->directory != NULL)
sysfs_close_directory(bus->directory);
- for (curdev = bus->devices; curdev != NULL;
- curdev = nextdev) {
- nextdev = curdev->next;
- sysfs_close_device(curdev);
- }
- for (curdrv = bus->drivers; curdrv != NULL;
- curdrv = nextdrv) {
- nextdrv = curdrv->next;
- sysfs_close_driver(curdrv);
- }
+ if (bus->devices)
+ dlist_destroy(bus->devices);
+ if (bus->drivers)
+ dlist_destroy(bus->drivers);
free(bus);
}
}
@@ -59,243 +93,308 @@ static struct sysfs_bus *alloc_bus(void)
}
/**
- * open_bus_dir: opens up sysfs bus directory
- * returns sysfs_directory struct with success and NULL with error
+ * 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
*/
-static struct sysfs_directory *open_bus_dir(const char *name)
+struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus)
{
- struct sysfs_directory *busdir = NULL, *cur = NULL, *next = NULL;
- char buspath[SYSFS_PATH_MAX];
+ struct sysfs_device *bdev = NULL;
+ struct sysfs_directory *devdir = NULL;
+ struct sysfs_link *curl = NULL;
+ char path[SYSFS_PATH_MAX];
- if (name == NULL) {
+ if (bus == NULL) {
errno = EINVAL;
return NULL;
}
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, bus->path);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DEVICES_NAME);
+ devdir = sysfs_open_directory(path);
+ if (devdir == NULL)
+ return NULL;
- memset(buspath, 0, SYSFS_PATH_MAX);
- if ((sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) != 0) {
- dprintf(stderr, "Sysfs not supported on this system\n");
+ if (sysfs_read_dir_links(devdir) != 0) {
+ sysfs_close_directory(devdir);
return NULL;
}
- strcat(buspath, SYSFS_BUS_DIR);
- strcat(buspath, "/");
- strcat(buspath, name);
- busdir = sysfs_open_directory(buspath);
- if (busdir == NULL) {
+ if (devdir->links != NULL) {
+ dlist_for_each_data(devdir->links, curl, struct sysfs_link) {
+ bdev = sysfs_open_device_path(curl->target);
+ if (bdev == NULL) {
+ dprintf("Error opening device at %s\n",
+ curl->target);
+ continue;
+ }
+ if (bus->devices == NULL)
+ bus->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_dev);
+ dlist_unshift_sorted(bus->devices, bdev, sort_list);
+ }
+ }
+ sysfs_close_directory(devdir);
+
+ return (bus->devices);
+}
+
+/**
+ * sysfs_get_bus_drivers: get all pci drivers
+ * @bus: pci bus to add drivers to
+ * returns dlist of drivers with success and NULL with error
+ */
+struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus)
+{
+ struct sysfs_driver *driver = NULL;
+ struct sysfs_directory *drvdir = NULL;
+ struct sysfs_directory *cursub = NULL;
+ char path[SYSFS_PATH_MAX];
+
+ if (bus == NULL) {
errno = EINVAL;
- dprintf(stderr,"Bus %s not supported on this system\n",
- name);
return NULL;
}
- if ((sysfs_read_directory(busdir)) != 0) {
- dprintf(stderr, "Error reading %s bus dir %s\n", name,
- buspath);
- sysfs_close_directory(busdir);
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, bus->path);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DRIVERS_NAME);
+ drvdir = sysfs_open_directory(path);
+ if (drvdir == NULL)
+ return NULL;
+
+ if (sysfs_read_dir_subdirs(drvdir) != 0) {
+ sysfs_close_directory(drvdir);
return NULL;
}
- /* read in devices and drivers subdirs */
- for (cur = busdir->subdirs; cur != NULL; cur = next) {
- next = cur->next;
- if ((sysfs_read_directory(cur)) != 0)
- continue;
+ if (drvdir->subdirs != NULL) {
+ dlist_for_each_data(drvdir->subdirs, cursub,
+ struct sysfs_directory) {
+ driver = sysfs_open_driver_path(cursub->path);
+ if (driver == NULL) {
+ dprintf("Error opening driver at %s\n",
+ cursub->path);
+ continue;
+ }
+ if (bus->drivers == NULL)
+ bus->drivers = dlist_new_with_delete
+ (sizeof(struct sysfs_driver),
+ sysfs_close_drv);
+ dlist_unshift_sorted(bus->drivers, driver, sort_list);
+ }
}
-
- return busdir;
+ sysfs_close_directory(drvdir);
+ return (bus->drivers);
}
/**
- * add_dev_to_bus: adds a bus device to bus device list
- * @bus: bus to add the device
- * @dev: device to add
+ * sysfs_open_bus: opens specific bus and all its devices on system
+ * returns sysfs_bus structure with success or NULL with error.
*/
-static void add_dev_to_bus(struct sysfs_bus *bus, struct sysfs_device *dev)
+struct sysfs_bus *sysfs_open_bus(const char *name)
{
- if (bus != NULL && dev != NULL) {
- dev->next = bus->devices;
- bus->devices = dev;
+ struct sysfs_bus *bus = NULL;
+ char buspath[SYSFS_PATH_MAX];
+
+ if (name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(buspath, 0, SYSFS_PATH_MAX);
+ if ((sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) != 0) {
+ 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)) != 0) {
+ dprintf("Invalid path to bus: %s\n", buspath);
+ return NULL;
+ }
+ bus = alloc_bus();
+ if (bus == NULL) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ safestrcpy(bus->name, name);
+ safestrcpy(bus->path, buspath);
+ if ((sysfs_remove_trailing_slash(bus->path)) != 0) {
+ dprintf("Incorrect path to bus %s\n", bus->path);
+ sysfs_close_bus(bus);
+ return NULL;
+ }
+
+ return bus;
}
/**
- * add_driver_to_bus: adds a bus driver to bus driver list
- * @bus: bus to add driver to
- * @driver: driver to add
+ * 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.
*/
-static void add_driver_to_bus(struct sysfs_bus *bus,
- struct sysfs_driver *driver)
+struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, char *id)
{
- if (bus != NULL && driver != NULL) {
- driver->next = bus->drivers;
- bus->drivers = driver;
+ if (bus == NULL || id == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (bus->devices == NULL) {
+ bus->devices = sysfs_get_bus_devices(bus);
+ if (bus->devices == NULL)
+ return NULL;
}
+
+ return (struct sysfs_device *)dlist_find_custom(bus->devices, id,
+ bus_device_id_equal);
}
/**
- * get_all_bus_devices: gets all devices for bus
- * @bus: bus to get devices for
- * returns 0 with success and -1 with failure
+ * 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.
*/
-static int get_all_bus_devices(struct sysfs_bus *bus)
+struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus,
+ char *drvname)
{
- struct sysfs_device *bdev = NULL;
- struct sysfs_directory *cur = NULL;
- struct sysfs_link *curl = NULL, *nextl = NULL;
- char dirname[SYSFS_NAME_LEN];
-
- if (bus == NULL || bus->directory == NULL) {
+ if (bus == NULL || drvname == NULL) {
errno = EINVAL;
- return -1;
+ return NULL;
}
- for (cur = bus->directory->subdirs; cur != NULL; cur = cur->next) {
- memset(dirname, 0, SYSFS_NAME_LEN);
- if ((sysfs_get_name_from_path(cur->path, dirname,
- SYSFS_NAME_LEN)) != 0)
- continue;
- if (strcmp(dirname, SYSFS_DEVICES_NAME) != 0)
- continue;
- for (curl = cur->links; curl != NULL; curl = nextl) {
- nextl = curl->next;
- bdev = sysfs_open_device(curl->target);
- if (bdev == NULL) {
- dprintf(stderr, "Error opening device at %s\n",
- curl->target);
- continue;
- }
- add_dev_to_bus(bus, bdev);
- }
+
+ if (bus->drivers == NULL) {
+ bus->drivers = sysfs_get_bus_drivers(bus);
+ if (bus->drivers == NULL)
+ return NULL;
}
-
- return 0;
+
+ return (struct sysfs_driver *)dlist_find_custom(bus->drivers, drvname,
+ bus_driver_name_equal);
}
/**
- * get_all_bus_drivers: get all pci drivers
- * @bus: pci bus to add drivers to
- * returns 0 with success and -1 with error
+ * sysfs_get_bus_attributes: returns bus' dlist of attributes
+ * @bus: bus to get attributes for.
+ * returns dlist of attributes or NULL if there aren't any.
*/
-static int get_all_bus_drivers(struct sysfs_bus *bus)
+struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus)
{
- struct sysfs_driver *driver = NULL;
- struct sysfs_directory *cur = NULL, *next = NULL;
- struct sysfs_directory *cursub = NULL, *nextsub = NULL;
- char dirname[SYSFS_NAME_LEN];
+ if (bus == NULL)
+ return NULL;
- if (bus == NULL || bus->directory == NULL) {
- errno = EINVAL;
- return -1;
+ if (bus->directory == NULL) {
+ bus->directory = sysfs_open_directory(bus->path);
+ if (bus->directory == NULL)
+ return NULL;
}
- for (cur = bus->directory->subdirs; cur != NULL; cur = next) {
- next = cur->next;
- memset(dirname, 0, SYSFS_NAME_LEN);
- if ((sysfs_get_name_from_path(cur->path, dirname,
- SYSFS_NAME_LEN)) != 0)
- continue;
- if (strcmp(dirname, SYSFS_DRIVERS_NAME) != 0)
- continue;
- for (cursub = cur->subdirs; cursub != NULL; cursub = nextsub) {
- nextsub = cursub->next;
- driver = sysfs_open_driver(cursub->path);
- if (driver == NULL) {
- dprintf(stderr, "Error opening driver at %s\n",
- cursub->path);
- continue;
- }
- add_driver_to_bus(bus, driver);
- }
+ if (bus->directory->attributes == NULL) {
+ if ((sysfs_read_dir_attributes(bus->directory)) != 0)
+ return NULL;
}
-
- return 0;
+ return bus->directory->attributes;
}
/**
- * match_bus_device_to_driver: returns 1 if device is bound to driver
- * @driver: driver to match
- * @busid: busid of device to match
- * returns 1 if found and 0 if not found
+ * sysfs_refresh_bus_attributes: refreshes the bus's list of attributes
+ * @bus: sysfs_bus whose attributes to refresh
+ *
+ * NOTE: Upon return, prior references to sysfs_attributes for this bus
+ * _may_ not be valid
+ *
+ * Returns list of attributes on success and NULL on failure
*/
-static int match_bus_device_to_driver(struct sysfs_driver *driver, char *busid)
+struct dlist *sysfs_refresh_bus_attributes(struct sysfs_bus *bus)
{
- struct sysfs_link *cur = NULL, *next = NULL;
- int found = 0;
-
- if (driver == NULL || driver->directory == NULL || busid == NULL) {
+ if (bus == NULL) {
errno = EINVAL;
- return found;
+ return NULL;
}
- for (cur = driver->directory->links; cur != NULL && found == 0;
- cur = next) {
- next = cur->next;
- if ((strcmp(cur->name, busid)) == 0)
- found++;
+
+ if (bus->directory == NULL)
+ return (sysfs_get_bus_attributes(bus));
+
+ if ((sysfs_refresh_dir_attributes(bus->directory)) != 0) {
+ dprintf("Error refreshing bus attributes\n");
+ return NULL;
}
- return found;
+
+ return (bus->directory->attributes);
}
/**
- * link_bus_devices_to_drivers: goes through and links devices to drivers
- * @bus: bus to link
+ * sysfs_get_bus_attribute: gets a specific bus attribute, if buses had
+ * attributes.
+ * @bus: bus to retrieve attribute from
+ * @attrname: attribute name to retrieve
+ * returns reference to sysfs_attribute if found or NULL if not found
*/
-static void link_bus_devices_to_drivers(struct sysfs_bus *bus)
+struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus,
+ char *attrname)
{
- struct sysfs_device *dev = NULL, *nextdev = NULL;
- struct sysfs_driver *drv = NULL, *nextdrv = NULL;
+ struct dlist *attrlist = NULL;
- if (bus != NULL && bus->devices != NULL && bus->drivers != NULL) {
- for (dev = bus->devices; dev != NULL; dev = nextdev) {
- nextdev = dev->next;
-
- for (drv = bus->drivers; drv != NULL; drv = nextdrv) {
- nextdrv = drv->next;
- if ((match_bus_device_to_driver(drv,
- dev->bus_id)) != 0) {
- dev->driver = drv;
- drv->device = dev;
- }
- }
- }
+ if (bus == NULL) {
+ errno = EINVAL;
+ return NULL;
}
+ attrlist = sysfs_get_bus_attributes(bus);
+ if (attrlist == NULL)
+ return NULL;
+
+ return sysfs_get_directory_attribute(bus->directory, attrname);
}
/**
- * sysfs_open_bus: opens specific bus and all its devices on system
- * returns sysfs_bus structure with success or NULL with error.
+ * sysfs_find_driver_bus: locates the bus the driver is on.
+ * @driver: name of the driver to locate
+ * @busname: buffer to copy name to
+ * @bsize: buffer size
+ * returns 0 with success, -1 with error
*/
-struct sysfs_bus *sysfs_open_bus(const char *name)
+int sysfs_find_driver_bus(const char *driver, char *busname, size_t bsize)
{
- struct sysfs_bus *bus = NULL;
- struct sysfs_directory *busdir = NULL;
+ char subsys[SYSFS_PATH_MAX], *bus = NULL, *curdrv = NULL;
+ struct dlist *buslist = NULL, *drivers = NULL;
- if (name == NULL) {
+ if (driver == NULL || busname == NULL) {
errno = EINVAL;
- return NULL;
+ return -1;
}
- bus = alloc_bus();
- if (bus == NULL) {
- perror("malloc");
- return NULL;
- }
- strcpy(bus->name, name);
- busdir = open_bus_dir(name);
- if (busdir == NULL) {
- dprintf(stderr,"Invalid bus, %s not supported on this system\n",
- name);
- sysfs_close_bus(bus);
- return NULL;
- }
- bus->directory = busdir;
- if ((get_all_bus_devices(bus)) != 0) {
- dprintf(stderr, "Error reading %s bus devices\n", name);
- sysfs_close_bus(bus);
- return NULL;
- }
- if ((get_all_bus_drivers(bus)) != 0) {
- dprintf(stderr, "Error reading %s bus drivers\n", name);
- sysfs_close_bus(bus);
- return NULL;
+ memset(subsys, 0, SYSFS_PATH_MAX);
+ safestrcpy(subsys, SYSFS_BUS_NAME);
+ buslist = sysfs_open_subsystem_list(subsys);
+ if (buslist != NULL) {
+ dlist_for_each_data(buslist, bus, char) {
+ memset(subsys, 0, SYSFS_PATH_MAX);
+ safestrcpy(subsys, SYSFS_BUS_NAME);
+ safestrcat(subsys, "/");
+ safestrcat(subsys, bus);
+ safestrcat(subsys, "/");
+ safestrcat(subsys, SYSFS_DRIVERS_NAME);
+ drivers = sysfs_open_subsystem_list(subsys);
+ if (drivers != NULL) {
+ dlist_for_each_data(drivers, curdrv, char) {
+ if (strcmp(driver, curdrv) == 0) {
+ safestrcpymax(busname,
+ bus, bsize);
+ sysfs_close_list(drivers);
+ sysfs_close_list(buslist);
+ return 0;
+ }
+ }
+ sysfs_close_list(drivers);
+ }
+ }
+ sysfs_close_list(buslist);
}
- link_bus_devices_to_drivers(bus);
-
- return bus;
+ return -1;
}
diff --git a/lib/sysfs_class.c b/lib/sysfs_class.c
index dee8514..a132bb6 100644
--- a/lib/sysfs_class.c
+++ b/lib/sysfs_class.c
@@ -3,7 +3,7 @@
*
* Generic class utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +23,27 @@
#include "libsysfs.h"
#include "sysfs.h"
+static void sysfs_close_cls_dev(void *dev)
+{
+ sysfs_close_class_device((struct sysfs_class_device *)dev);
+}
+
+/**
+ * class_name_equal: compares class_devices' name
+ * @a: class_name looking for
+ * @b: sysfs_class_device being compared
+ */
+static int class_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_class_device *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
/**
* sysfs_close_class_device: closes a single class device.
* @dev: class device to close.
@@ -36,25 +57,23 @@ void sysfs_close_class_device(struct sysfs_class_device *dev)
sysfs_close_device(dev->sysdevice);
if (dev->driver != NULL)
sysfs_close_driver(dev->driver);
+ if (dev->parent != NULL)
+ sysfs_close_class_device(dev->parent);
free(dev);
}
}
/**
* sysfs_close_class: close single class
- * @class: class structure
+ * @cls: class structure
*/
void sysfs_close_class(struct sysfs_class *cls)
{
- struct sysfs_class_device *cur = NULL, *next = NULL;
-
if (cls != NULL) {
if (cls->directory != NULL)
sysfs_close_directory(cls->directory);
- for (cur = cls->devices; cur != NULL; cur = next) {
- next = cur->next;
- sysfs_close_class_device(cur);
- }
+ if (cls->devices != NULL)
+ dlist_destroy(cls->devices);
free(cls);
}
}
@@ -78,193 +97,612 @@ static struct sysfs_class *alloc_class(void)
return (struct sysfs_class *)calloc(1, sizeof(struct sysfs_class));
}
-/**
- * open_class_dir: opens up sysfs class directory
- * returns sysfs_directory struct with success and NULL with error
+/**
+ * set_classdev_classname: Grabs classname from path
+ * @cdev: class device to set
+ * Returns nothing
*/
-static struct sysfs_directory *open_class_dir(const char *name)
+static void set_classdev_classname(struct sysfs_class_device *cdev)
{
- struct sysfs_directory *classdir = NULL;
- char classpath[SYSFS_PATH_MAX];
+ char *c = NULL, *e = NULL;
+ int count = 0;
- if (name == NULL) {
- errno = EINVAL;
- return NULL;
+ c = strstr(cdev->path, SYSFS_CLASS_NAME);
+ if (c == NULL) {
+ c = strstr(cdev->path, SYSFS_BLOCK_NAME);
+ } else {
+ c = strstr(c, "/");
}
- memset(classpath, 0, SYSFS_PATH_MAX);
- if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) {
- dprintf(stderr, "Sysfs not supported on this system\n");
- return NULL;
- }
-
- strcat(classpath, SYSFS_CLASS_DIR);
- strcat(classpath, "/");
- strcat(classpath, name);
- classdir = sysfs_open_directory(classpath);
- if (classdir == NULL) {
- errno = EINVAL;
- dprintf(stderr,"Class %s not supported on this system\n",
- name);
- return NULL;
- }
- if ((sysfs_read_directory(classdir)) != 0) {
- dprintf(stderr, "Error reading %s class dir %s\n", name,
- classpath);
- sysfs_close_directory(classdir);
- return NULL;
+ 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);
}
-
- return classdir;
}
/**
- * sysfs_open_class_device: Opens and populates class device
+ * 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(const char *path)
+struct sysfs_class_device *sysfs_open_class_device_path(const char *path)
{
struct sysfs_class_device *cdev = NULL;
- struct sysfs_directory *dir = NULL, *cur = NULL;
- struct sysfs_link *curl = NULL;
- struct sysfs_device *sdev = NULL;
- struct sysfs_driver *drv = NULL;
if (path == NULL) {
errno = EINVAL;
return NULL;
}
+ if ((sysfs_path_is_dir(path)) != 0) {
+ dprintf("%s is not a valid path to a class device\n", path);
+ return NULL;
+ }
cdev = alloc_class_device();
if (cdev == NULL) {
- perror("malloc");
+ dprintf("calloc failed\n");
return NULL;
}
if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) {
errno = EINVAL;
- dprintf(stderr, "Invalid class device path %s\n", path);
+ dprintf("Error getting class device name\n");
sysfs_close_class_device(cdev);
return NULL;
}
- dir = sysfs_open_directory(path);
- if (dir == NULL) {
- dprintf(stderr, "Error opening class device at %s\n", path);
+ safestrcpy(cdev->path, path);
+ if ((sysfs_remove_trailing_slash(cdev->path)) != 0) {
+ dprintf("Invalid path to class device %s\n", cdev->path);
sysfs_close_class_device(cdev);
return NULL;
}
- if ((sysfs_read_directory(dir)) != 0) {
- dprintf(stderr, "Error reading class device at %s\n", path);
- sysfs_close_directory(dir);
- sysfs_close_class_device(cdev);
+ set_classdev_classname(cdev);
+
+ return cdev;
+}
+
+/**
+ * sysfs_get_class_devices: gets all devices for class
+ * @cls: class to get devices for
+ * returns dlist of class_devices with success and NULL with error
+ */
+struct dlist *sysfs_get_class_devices(struct sysfs_class *cls)
+{
+ struct sysfs_class_device *dev = NULL;
+ struct sysfs_directory *cur = NULL;
+
+ if (cls == NULL) {
+ errno = EINVAL;
return NULL;
}
- cdev->directory = dir;
- cur = cdev->directory->subdirs;
- while(cur != NULL) {
- sysfs_read_directory(cur);
- cur = cur->next;
+ if (cls->devices != NULL)
+ return cls->devices;
+
+ if (cls->directory == NULL) {
+ cls->directory = sysfs_open_directory(cls->path);
+ if (cls->directory == NULL)
+ return NULL;
}
- /* get driver and device, if implemented */
- curl = cdev->directory->links;
- while (curl != NULL) {
- if (strncmp(curl->name, SYSFS_DEVICES_NAME, 6) == 0) {
- sdev = sysfs_open_device(curl->target);
- if (sdev != NULL) {
- cdev->sysdevice = sdev;
- if (cdev->driver != NULL)
- sdev->driver = cdev->driver;
- }
- } else if (strncmp(curl->name, SYSFS_DRIVERS_NAME, 6) == 0) {
- drv = sysfs_open_driver(curl->target);
- if (drv != NULL) {
- cdev->driver = drv;
- if (cdev->sysdevice != NULL)
- drv->device = cdev->sysdevice;
+
+ if ((sysfs_read_dir_subdirs(cls->directory)) != 0)
+ return NULL;
+
+ if (cls->directory->subdirs != NULL) {
+ dlist_for_each_data(cls->directory->subdirs, cur,
+ struct sysfs_directory) {
+ dev = sysfs_open_class_device_path(cur->path);
+ if (dev == NULL) {
+ dprintf("Error opening device at %s\n",
+ cur->path);
+ continue;
}
+ if (cls->devices == NULL)
+ cls->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_class_device),
+ sysfs_close_cls_dev);
+ dlist_unshift_sorted(cls->devices, dev, sort_list);
}
- curl = curl->next;
}
- return cdev;
+ return cls->devices;
}
/**
- * add_dev_to_class: adds a class device to class list
- * @class: class to add the device
- * @dev: device to add
+ * sysfs_open_class: opens specific class and all its devices on system
+ * returns sysfs_class structure with success or NULL with error.
*/
-static void add_dev_to_class(struct sysfs_class *cls,
- struct sysfs_class_device *dev)
+struct sysfs_class *sysfs_open_class(const char *name)
{
- if (cls != NULL && dev != NULL) {
- dev->next = cls->devices;
- cls->devices = dev;
+ struct sysfs_class *cls = NULL;
+ char classpath[SYSFS_PATH_MAX];
+
+ if (name == NULL) {
+ 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)) != 0) {
+ 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
+ * @class: class to find device on
+ * @name: class name of the device
+ */
+struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *cls,
+ char *name)
+{
+ if (cls == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
}
+
+ if (cls->devices == NULL) {
+ cls->devices = sysfs_get_class_devices(cls);
+ if (cls->devices == NULL)
+ return NULL;
+ }
+ return (struct sysfs_class_device *)dlist_find_custom(cls->devices,
+ name, class_name_equal);
}
/**
- * get_all_class_devices: gets all devices for class
- * @class: class to get devices for
- * returns 0 with success and -1 with failure
+ * sysfs_get_classdev_device: returns the sysfs_device corresponding to
+ * sysfs_class_device, if present
+ * @clsdev: class device whose sysfs_device is required
+ * Returns sysfs_device on success, NULL on error or if device is not
+ * implemented
+ */
+struct sysfs_device *sysfs_get_classdev_device
+ (struct sysfs_class_device *clsdev)
+{
+ struct sysfs_link *devlink = NULL;
+ char devpath[SYSFS_PATH_MAX];
+
+ if (clsdev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ safestrcpy(devpath, clsdev->path);
+ safestrcat(devpath, "/device");
+ if ((sysfs_path_is_link(devpath)) != 0) {
+ if (clsdev->sysdevice != NULL) {
+ sysfs_close_device(clsdev->sysdevice);
+ clsdev->sysdevice = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->directory == NULL) {
+ clsdev->directory = sysfs_open_directory(clsdev->path);
+ if (clsdev->directory == NULL)
+ return NULL;
+ }
+ devlink = sysfs_get_directory_link(clsdev->directory, "device");
+ if (devlink == NULL) {
+ if (clsdev->sysdevice != NULL) {
+ dprintf("Device link no longer exists\n");
+ sysfs_close_device(clsdev->sysdevice);
+ clsdev->sysdevice = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->sysdevice != NULL) {
+ if (!strncmp(devlink->target, clsdev->sysdevice->path,
+ SYSFS_PATH_MAX))
+ /* sysdevice hasn't changed */
+ return (clsdev->sysdevice);
+ else
+ /* come here only if the device link for has changed */
+ sysfs_close_device(clsdev->sysdevice);
+ }
+
+ clsdev->sysdevice = sysfs_open_device_path(devlink->target);
+ if (clsdev->sysdevice == NULL)
+ return NULL;
+
+ return (clsdev->sysdevice);
+}
+
+/**
+ * sysfs_get_classdev_driver: returns the sysfs_driver corresponding to
+ * sysfs_class_device, if present
+ * @clsdev: class device whose sysfs_device is required
+ * Returns sysfs_driver on success, NULL on error or if driver is not
+ * implemented
+ */
+struct sysfs_driver *sysfs_get_classdev_driver
+ (struct sysfs_class_device *clsdev)
+{
+ struct sysfs_link *drvlink = NULL;
+ char drvpath[SYSFS_PATH_MAX];
+
+ if (clsdev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ safestrcpy(drvpath, clsdev->path);
+ safestrcat(drvpath, "/driver");
+ if ((sysfs_path_is_link(drvpath)) != 0) {
+ if (clsdev->driver != NULL) {
+ sysfs_close_driver(clsdev->driver);
+ clsdev->driver = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->directory == NULL) {
+ clsdev->directory = sysfs_open_directory(clsdev->path);
+ if (clsdev->directory == NULL)
+ return NULL;
+ }
+ drvlink = sysfs_get_directory_link(clsdev->directory, "driver");
+ if (drvlink == NULL) {
+ if (clsdev->driver != NULL) {
+ dprintf("Driver link no longer exists\n");
+ sysfs_close_driver(clsdev->driver);
+ clsdev->driver = NULL;
+ }
+ return NULL;
+ }
+ if (clsdev->driver != NULL) {
+ if (!strncmp(drvlink->target, clsdev->driver->path,
+ SYSFS_PATH_MAX))
+ /* driver hasn't changed */
+ return (clsdev->driver);
+ else
+ /* come here only if the device link for has changed */
+ sysfs_close_driver(clsdev->driver);
+ }
+
+ clsdev->driver = sysfs_open_driver_path(drvlink->target);
+ if (clsdev->driver == NULL)
+ return NULL;
+
+ return (clsdev->driver);
+}
+
+/**
+ * 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_all_class_devices(struct sysfs_class *cls)
+static int get_blockdev_parent(struct sysfs_class_device *clsdev)
{
- struct sysfs_class_device *dev = NULL;
- struct sysfs_directory *cur = NULL, *next = NULL;
+ char parent_path[SYSFS_PATH_MAX], *c = NULL;
+
+ 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 == NULL) {
+ 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 == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (clsdev->parent != NULL)
+ 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;
+}
- if (cls == NULL || cls->directory == 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 == NULL || clsdev == NULL || path == NULL) {
errno = EINVAL;
return -1;
}
- for (cur = cls->directory->subdirs; cur != NULL; cur = next) {
- next = cur->next;
- dev = sysfs_open_class_device(cur->path);
- if (dev == NULL) {
- dprintf(stderr, "Error opening device at %s\n",
- cur->path);
- continue;
- }
- add_dev_to_class(cls, dev);
+ 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: opens specific class and all its devices on system
- * returns sysfs_class structure with success or NULL with error.
+ * 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 *sysfs_open_class(const char *name)
+struct sysfs_class_device *sysfs_open_class_device
+ (const char *classname, const char *name)
{
- struct sysfs_class *cls = NULL;
- struct sysfs_directory *classdir = NULL;
+ char devpath[SYSFS_PATH_MAX];
+ struct sysfs_class_device *cdev = NULL;
- if (name == NULL) {
+ if (classname == NULL || name == NULL) {
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 == NULL) {
+ dprintf("Error getting class device %s from class %s\n",
+ name, classname);
+ return NULL;
+ }
+ return cdev;
+}
- cls = alloc_class();
- if (cls == NULL) {
- perror("malloc");
+/**
+ * sysfs_get_classdev_attributes: returns a dlist of attributes for
+ * the requested class_device
+ * @cdev: sysfs_class_dev for which attributes are needed
+ * returns a dlist of attributes if exists, NULL otherwise
+ */
+struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev)
+{
+ if (cdev == NULL)
return NULL;
+
+ if (cdev->directory == NULL) {
+ cdev->directory = sysfs_open_directory(cdev->path);
+ if (cdev->directory == NULL)
+ return NULL;
}
- strcpy(cls->name, name);
- classdir = open_class_dir(name);
- if (classdir == NULL) {
- dprintf(stderr,
- "Invalid class, %s not supported on this system\n",
- name);
- sysfs_close_class(cls);
+ if (cdev->directory->attributes == NULL) {
+ if ((sysfs_read_dir_attributes(cdev->directory)) != 0)
+ return NULL;
+ }
+ return (cdev->directory->attributes);
+}
+
+/**
+ * sysfs_refresh_clsassdev_attributes: refreshes the driver's list of attributes
+ * @clsdev: sysfs_class_device whose attributes to refresh
+ *
+ * NOTE: Upon return, prior references to sysfs_attributes for this classdev
+ * _may_ not be valid
+ *
+ * Returns list of attributes on success and NULL on failure
+ */
+struct dlist *sysfs_refresh_classdev_attributes
+ (struct sysfs_class_device *clsdev)
+{
+ if (clsdev == NULL) {
+ errno = EINVAL;
return NULL;
}
- cls->directory = classdir;
- if ((get_all_class_devices(cls)) != 0) {
- dprintf(stderr, "Error reading %s class devices\n", name);
- sysfs_close_class(cls);
+
+ if (clsdev->directory == NULL)
+ return (sysfs_get_classdev_attributes(clsdev));
+
+ if ((sysfs_refresh_dir_attributes(clsdev->directory)) != 0) {
+ dprintf("Error refreshing class_device attributes\n");
return NULL;
}
- return cls;
+ return (clsdev->directory->attributes);
+}
+
+/**
+ * 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)
+{
+ struct sysfs_attribute *cur = NULL;
+ struct sysfs_directory *sdir = NULL;
+ struct dlist *attrlist = NULL;
+
+ if (clsdev == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * First, see if it's in the current directory. Then look at
+ * subdirs since class devices can have subdirs of attributes.
+ */
+ attrlist = sysfs_get_classdev_attributes(clsdev);
+ if (attrlist != NULL) {
+ cur = sysfs_get_directory_attribute(clsdev->directory,
+ (char *)name);
+ if (cur != NULL)
+ return cur;
+ }
+
+ if (clsdev->directory == NULL)
+ return NULL;
+
+ if (clsdev->directory->subdirs == NULL)
+ if ((sysfs_read_dir_subdirs(clsdev->directory)) != 0 ||
+ clsdev->directory->subdirs == NULL)
+ return NULL;
+
+ if (clsdev->directory->subdirs != NULL) {
+ dlist_for_each_data(clsdev->directory->subdirs, sdir,
+ struct sysfs_directory) {
+ if ((sysfs_path_is_dir(sdir->path)) != 0)
+ continue;
+ cur = sysfs_get_directory_attribute(sdir,
+ (char *)name);
+ if (cur == NULL)
+ continue;
+ }
+ }
+ return cur;
}
+
+/**
+ * sysfs_open_classdev_attr: read an attribute for a given class device
+ * @classname: name of the class on which to look
+ * @dev: class device name for which the attribute has to be read
+ * @attrib: attribute to read
+ * Returns sysfs_attribute * on SUCCESS and NULL on error
+ *
+ * NOTE:
+ * A call to sysfs_close_attribute() is required to close the
+ * attribute returned and to free memory
+ */
+struct sysfs_attribute *sysfs_open_classdev_attr(const char *classname,
+ const char *dev, const char *attrib)
+{
+ struct sysfs_attribute *attribute = NULL;
+ char path[SYSFS_PATH_MAX];
+
+ if (classname == NULL || dev == NULL || attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ if ((get_classdev_path(classname, dev, path, SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to device %s on class %s\n",
+ dev, classname);
+ return NULL;
+ }
+ safestrcat(path, "/");
+ safestrcat(path, attrib);
+ attribute = sysfs_open_attribute(path);
+ if (attribute == NULL) {
+ dprintf("Error opening attribute %s on class device %s\n",
+ attrib, dev);
+ return NULL;
+ }
+ if ((sysfs_read_attribute(attribute)) != 0) {
+ dprintf("Error reading attribute %s for class device %s\n",
+ attrib, dev);
+ sysfs_close_attribute(attribute);
+ return NULL;
+ }
+ return attribute;
+}
+
diff --git a/lib/sysfs_device.c b/lib/sysfs_device.c
index 185b5cf..83c3adc 100644
--- a/lib/sysfs_device.c
+++ b/lib/sysfs_device.c
@@ -3,7 +3,7 @@
*
* Generic device utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -24,17 +24,185 @@
#include "sysfs.h"
/**
+ * get_dev_driver: fills in the dev->driver_name field, but searches by
+ * opening subsystem. Only to be used if no driver link exists in
+ * device directory.
+ *
+ * Returns 0 on SUCCESS and 1 on error
+ */
+static int get_dev_driver(struct sysfs_device *dev)
+{
+ struct dlist *drvlist = NULL;
+ char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
+ char *drv = NULL, *c = NULL;
+
+ if (dev == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ if (dev->bus[0] == '\0')
+ return 1;
+ memset(path, 0, SYSFS_PATH_MAX);
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, SYSFS_BUS_NAME);
+ safestrcat(path, "/");
+ safestrcat(path, dev->bus);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DRIVERS_NAME);
+
+ safestrcpy(devpath, dev->path);
+ c = strstr(devpath, SYSFS_DEVICES_NAME);
+ if (c == NULL)
+ return 1;
+ *c = '\0';
+ safestrcatmax(c, path, (sizeof(devpath) - strlen(devpath)));
+
+ drvlist = sysfs_open_subsystem_list(path);
+ if (drvlist != NULL) {
+ dlist_for_each_data(drvlist, drv, char) {
+ safestrcpy(path, devpath);
+ safestrcat(path, "/");
+ safestrcat(path, drv);
+ safestrcat(path, "/");
+ safestrcat(path, dev->bus_id);
+ if (sysfs_path_is_link(path) == 0) {
+ safestrcpy(dev->driver_name, drv);
+ sysfs_close_list(drvlist);
+ return 0;
+ }
+ }
+ sysfs_close_list(drvlist);
+ }
+ return 1;
+}
+
+/*
+ * get_device_driver_name: gets device's driver name, searches for driver
+ * link first before going the brute force route.
+ * @dev: device to retrieve driver
+ * returns 0 with success and 1 with error
+ */
+static int get_device_driver_name(struct sysfs_device *dev)
+{
+ char devpath[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX];
+
+ if (dev == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ memset(drvpath, 0, SYSFS_PATH_MAX);
+ safestrcpy(devpath, dev->path);
+ safestrcat(devpath, "/driver");
+
+ if ((sysfs_get_link(devpath, drvpath, SYSFS_PATH_MAX)) != 0)
+ return(get_dev_driver(dev));
+
+ return (sysfs_get_name_from_path(drvpath, dev->driver_name,
+ SYSFS_NAME_LEN));
+}
+
+/**
+ * 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 subsys[SYSFS_NAME_LEN], path[SYSFS_PATH_MAX];
+ char target[SYSFS_PATH_MAX], *bus = NULL, *c = NULL;
+ struct dlist *buslist = NULL;
+
+ if (dev == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(subsys, 0, SYSFS_NAME_LEN);
+ safestrcpy(subsys, SYSFS_BUS_NAME); /* subsys = bus */
+ buslist = sysfs_open_subsystem_list(subsys);
+ if (buslist != NULL) {
+ dlist_for_each_data(buslist, bus, char) {
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, dev->path);
+ c = strstr(path, "/devices");
+ if (c == NULL) {
+ dprintf("Invalid path to device %s\n", path);
+ sysfs_close_list(buslist);
+ return -1;
+ }
+ *c = '\0';
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_BUS_NAME);
+ safestrcat(path, "/");
+ safestrcat(path, bus);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DEVICES_NAME);
+ safestrcat(path, "/");
+ safestrcat(path, dev->bus_id);
+ if ((sysfs_path_is_link(path)) == 0) {
+ memset(target, 0, SYSFS_PATH_MAX);
+ if ((sysfs_get_link(path, target,
+ SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting link target\n");
+ sysfs_close_list(buslist);
+ return -1;
+ }
+ if (!(strncmp(target, dev->path,
+ SYSFS_PATH_MAX))) {
+ safestrcpy(dev->bus, bus);
+ sysfs_close_list(buslist);
+ return 0;
+ }
+ }
+ }
+ sysfs_close_list(buslist);
+ }
+ 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 != NULL) {
+ if (devroot->children != NULL) {
+ 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_dev_tree: routine for dlist integration
+ */
+static void sysfs_close_dev_tree(void *dev)
+{
+ sysfs_close_device_tree((struct sysfs_device *)dev);
+}
+
+/**
* sysfs_close_device: closes and cleans up a device
* @dev = device to clean up
*/
void sysfs_close_device(struct sysfs_device *dev)
{
if (dev != NULL) {
- dev->next = NULL;
- dev->driver = NULL;
+ if (dev->parent != NULL)
+ sysfs_close_device(dev->parent);
if (dev->directory != NULL)
sysfs_close_directory(dev->directory);
- dev->children = NULL;
+ if (dev->children != NULL && dev->children->count == 0)
+ dlist_destroy(dev->children);
free(dev);
}
}
@@ -49,151 +217,485 @@ static struct sysfs_device *alloc_device(void)
}
/**
- * 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.
+ * open_device_dir: opens up sysfs_directory for specific root dev
+ * @name: name of root
+ * returns struct sysfs_directory with success and NULL with error
*/
-struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
- const char *name)
+static struct sysfs_directory *open_device_dir(const char *path)
{
- struct sysfs_attribute *cur = NULL;
- char attrname[SYSFS_NAME_LEN];
+ struct sysfs_directory *rdir = NULL;
- if (dev == NULL || dev->directory == NULL || name == NULL) {
+ if (path == NULL) {
errno = EINVAL;
return NULL;
}
- for (cur = dev->directory->attributes; cur != NULL; cur = cur->next) {
- if ((sysfs_get_name_from_path(cur->path, attrname,
- SYSFS_NAME_LEN)) != 0)
- continue;
- if (strcmp(name, attrname) != 0)
- continue;
- return cur;
+ rdir = sysfs_open_directory(path);
+ if (rdir == NULL) {
+ errno = EINVAL;
+ dprintf ("Device %s not supported on this system\n", path);
+ return NULL;
}
-
- return NULL;
+ if ((sysfs_read_dir_subdirs(rdir)) != 0) {
+ dprintf ("Error reading device at dir %s\n", path);
+ sysfs_close_directory(rdir);
+ return NULL;
+ }
+
+ return rdir;
}
/**
- * sysfs_open_device: opens and populates device structure
+ * 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(const char *path)
+struct sysfs_device *sysfs_open_device_path(const char *path)
{
struct sysfs_device *dev = NULL;
- struct sysfs_directory *sdir = NULL;
- char *p = NULL;
if (path == NULL) {
errno = EINVAL;
return NULL;
}
+ if ((sysfs_path_is_dir(path)) != 0) {
+ dprintf("Incorrect path to device: %s\n", path);
+ return NULL;
+ }
dev = alloc_device();
if (dev == NULL) {
- dprintf(stderr, "Error allocating device at %s\n", path);
+ dprintf("Error allocating device at %s\n", path);
return NULL;
}
- sdir = sysfs_open_directory(path);
- if (sdir == NULL) {
- dprintf(stderr, "Invalid device at %s\n", path);
+ if ((sysfs_get_name_from_path(path, dev->bus_id,
+ SYSFS_NAME_LEN)) != 0) {
errno = EINVAL;
+ dprintf("Error getting device bus_id\n");
sysfs_close_device(dev);
return NULL;
}
- if ((sysfs_read_directory(sdir)) != 0) {
- dprintf(stderr, "Error reading device directory at %s\n", path);
- sysfs_close_directory(sdir);
+ safestrcpy(dev->path, path);
+ if ((sysfs_remove_trailing_slash(dev->path)) != 0) {
+ dprintf("Invalid path to device %s\n", dev->path);
sysfs_close_device(dev);
return NULL;
}
- dev->directory = sdir;
- sysfs_get_name_from_path(sdir->path, dev->bus_id, SYSFS_NAME_LEN);
- /* get device name */
- p = sysfs_get_value_from_attributes(sdir->attributes,
- SYSFS_NAME_ATTRIBUTE);
- if (p != NULL) {
- strncpy(dev->name, p, SYSFS_NAME_LEN);
- p = dev->name + strlen(dev->name) - 1;
- if ((strlen(dev->name) > 0) && *p == '\n')
- *p = '\0';
+ /*
+ * 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) != 0)
+ dprintf("Could not get device bus\n");
+
+ if (get_device_driver_name(dev) != 0) {
+ dprintf("Could not get device %s's driver\n", dev->bus_id);
+ safestrcpy(dev->driver_name, SYSFS_UNKNOWN);
}
return dev;
}
/**
- * sysfs_close_device_tree: closes every device in the supplied tree,
- * closing children only.
- * @devroot: device root of tree.
+ * sysfs_open_device_tree: opens root device and all of its children,
+ * creating a tree of devices. Only opens children.
+ * @path: sysfs path to devices
+ * returns struct sysfs_device and its children with success or NULL with
+ * error.
*/
-void sysfs_close_device_tree(struct sysfs_device *devroot)
+struct sysfs_device *sysfs_open_device_tree(const char *path)
{
- if (devroot != NULL) {
- if (devroot->children != NULL) {
- struct sysfs_device *child = NULL, *next = NULL;
-
- for (child = devroot->children; child != NULL;
- child = next) {
- next = child->next;
- sysfs_close_device_tree(child);
+ struct sysfs_device *rootdev = NULL, *new = NULL;
+ struct sysfs_directory *cur = NULL;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ rootdev = sysfs_open_device_path(path);
+ if (rootdev == NULL) {
+ dprintf("Error opening root device at %s\n", path);
+ return NULL;
+ }
+ if (rootdev->directory == NULL) {
+ rootdev->directory = open_device_dir(rootdev->path);
+ if (rootdev->directory == NULL)
+ return NULL;
+ }
+ if (rootdev->directory->subdirs != NULL) {
+ dlist_for_each_data(rootdev->directory->subdirs, cur,
+ struct sysfs_directory) {
+ new = sysfs_open_device_tree(cur->path);
+ if (new == NULL) {
+ dprintf("Error opening device tree at %s\n",
+ cur->path);
+ sysfs_close_device_tree(rootdev);
+ return NULL;
}
+ if (rootdev->children == NULL)
+ rootdev->children = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_dev_tree);
+ dlist_unshift_sorted(rootdev->children,
+ new, sort_list);
}
- sysfs_close_device(devroot);
}
+
+ return rootdev;
}
/**
- * add_device_child_to_parent: adds child device to parent
- * @parent: parent device.
- * @child: child device to add.
+ * sysfs_close_root_device: closes root and all devices
+ * @root: root device to close
*/
-static void add_device_child_to_parent(struct sysfs_device *parent,
- struct sysfs_device *child)
+void sysfs_close_root_device(struct sysfs_root_device *root)
{
- if (parent != NULL && child != NULL) {
- child->next = parent->children;
- parent->children = child;
- child->parent = parent;
+ if (root != NULL) {
+ if (root->devices != NULL)
+ dlist_destroy(root->devices);
+ if (root->directory != NULL)
+ sysfs_close_directory(root->directory);
+ free(root);
}
}
/**
- * sysfs_open_device_tree: opens root device and all of its children,
- * creating a tree of devices. Only opens children.
- * @path: sysfs path to devices
- * returns struct sysfs_device and its children with success or NULL with
- * error.
+ * sysfs_get_root_devices: opens up all the devices under this root device
+ * @root: root device to open devices for
+ * returns dlist of devices with success and NULL with error
*/
-struct sysfs_device *sysfs_open_device_tree(const char *path)
+struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root)
{
- struct sysfs_device *rootdev = NULL, *new = NULL;
+ struct sysfs_device *dev = NULL;
struct sysfs_directory *cur = NULL;
- if (path == NULL) {
+ if (root == NULL) {
errno = EINVAL;
return NULL;
}
- rootdev = sysfs_open_device(path);
- if (rootdev == NULL) {
- dprintf(stderr, "Error opening root device at %s\n", path);
+ if (root->directory == NULL) {
+ root->directory = open_device_dir(root->path);
+ if (root->directory == NULL)
+ return NULL;
+ }
+
+ if (root->directory->subdirs == NULL)
+ return 0;
+
+ dlist_for_each_data(root->directory->subdirs, cur,
+ struct sysfs_directory) {
+ dev = sysfs_open_device_tree(cur->path);
+ if (dev == NULL) {
+ dprintf ("Error opening device at %s\n", cur->path);
+ continue;
+ }
+ if (root->devices == NULL)
+ root->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_dev_tree);
+ dlist_unshift_sorted(root->devices, dev, sort_list);
+ }
+
+ return root->devices;
+}
+
+/**
+ * sysfs_open_root_device: opens sysfs devices root and all of its
+ * devices.
+ * @name: name of /sys/devices/root to open
+ * returns struct sysfs_root_device if success and NULL with error
+ */
+struct sysfs_root_device *sysfs_open_root_device(const char *name)
+{
+ struct sysfs_root_device *root = NULL;
+ char rootpath[SYSFS_PATH_MAX];
+
+ if (name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(rootpath, 0, SYSFS_PATH_MAX);
+ if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) {
+ dprintf ("Sysfs not supported on this system\n");
+ return NULL;
+ }
+
+ safestrcat(rootpath, "/");
+ safestrcat(rootpath, SYSFS_DEVICES_NAME);
+ safestrcat(rootpath, "/");
+ safestrcat(rootpath, name);
+ if ((sysfs_path_is_dir(rootpath)) != 0) {
+ errno = EINVAL;
+ dprintf("Invalid root device: %s\n", name);
+ return NULL;
+ }
+ root = (struct sysfs_root_device *)calloc
+ (1, sizeof(struct sysfs_root_device));
+ if (root == NULL) {
+ dprintf("calloc failure\n");
return NULL;
}
- cur = rootdev->directory->subdirs;
- while (cur != NULL) {
- new = sysfs_open_device_tree(cur->path);
- if (new == NULL) {
- dprintf(stderr, "Error opening device tree at %s\n",
- cur->path);
- sysfs_close_device_tree(rootdev);
+ safestrcpy(root->name, name);
+ safestrcpy(root->path, rootpath);
+ if ((sysfs_remove_trailing_slash(root->path)) != 0) {
+ dprintf("Invalid path to root device %s\n", root->path);
+ sysfs_close_root_device(root);
+ return NULL;
+ }
+ return root;
+}
+
+/**
+ * sysfs_get_device_attributes: returns a dlist of attributes corresponding to
+ * the specific device
+ * @device: struct sysfs_device * for which attributes are to be returned
+ */
+struct dlist *sysfs_get_device_attributes(struct sysfs_device *device)
+{
+ if (device == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (device->directory == NULL) {
+ device->directory = sysfs_open_directory(device->path);
+ if (device->directory == NULL)
+ return NULL;
+ }
+ if (device->directory->attributes == NULL) {
+ if ((sysfs_read_dir_attributes(device->directory)) != 0)
+ return NULL;
+ }
+ return (device->directory->attributes);
+}
+
+/**
+ * sysfs_refresh_device_attributes: refreshes the device's list of attributes
+ * @device: sysfs_device whose attributes to refresh
+ *
+ * NOTE: Upon return, prior references to sysfs_attributes for this device
+ * _may_ not be valid
+ *
+ * Returns list of attributes on success and NULL on failure
+ */
+struct dlist *sysfs_refresh_device_attributes(struct sysfs_device *device)
+{
+ if (device == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (device->directory == NULL)
+ return (sysfs_get_device_attributes(device));
+
+ if ((sysfs_refresh_dir_attributes(device->directory)) != 0) {
+ dprintf("Error refreshing device attributes\n");
+ return NULL;
+ }
+
+ return (device->directory->attributes);
+}
+
+/**
+ * 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)
+{
+ struct dlist *attrlist = NULL;
+
+ if (dev == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ attrlist = sysfs_get_device_attributes(dev);
+ if (attrlist == NULL)
+ return NULL;
+
+ return sysfs_get_directory_attribute(dev->directory, (char *)name);
+}
+
+/**
+ * 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 == NULL || path == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(bus_path, 0, SYSFS_PATH_MAX);
+ if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) {
+ 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)) != 0) {
+ 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 = NULL;
+
+ if (bus_id == NULL || bus == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(sysfs_path, 0, SYSFS_PATH_MAX);
+ if ((get_device_absolute_path(bus_id, bus, sysfs_path,
+ SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to device %s\n", bus_id);
+ return NULL;
+ }
+
+ device = sysfs_open_device_path(sysfs_path);
+ if (device == NULL) {
+ 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], *tmp = NULL;
+
+ if (dev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dev->parent != NULL)
+ return (dev->parent);
+
+ memset(ppath, 0, SYSFS_PATH_MAX);
+ safestrcpy(ppath, dev->path);
+ tmp = strrchr(ppath, '/');
+ if (tmp == NULL) {
+ 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;
}
- add_device_child_to_parent(rootdev, new);
- cur = cur->next;
}
+ *tmp = '\0';
+
+ /*
+ * All "devices" have the "detach_state" attribute - validate here
+ */
+ safestrcat(ppath, "/detach_state");
+ if ((sysfs_path_is_file(ppath)) != 0) {
+ dprintf("Device at %s does not have a parent\n", dev->path);
+ return NULL;
+ }
+ tmp = strrchr(ppath, '/');
+ *tmp = '\0';
+ dev->parent = sysfs_open_device_path(ppath);
+ if (dev->parent == NULL) {
+ dprintf("Error opening device %s's parent at %s\n",
+ dev->bus_id, ppath);
+ return NULL;
+ }
+ return (dev->parent);
+}
- return rootdev;
+/*
+ * sysfs_open_device_attr: open the given device's attribute
+ * @bus: Bus on which to look
+ * @dev_id: device for which attribute is required
+ * @attrname: name of the attribute to look for
+ * Returns struct sysfs_attribute on success and NULL on failure
+ *
+ * NOTE:
+ * A call to sysfs_close_attribute() is required to close
+ * the attribute returned and free memory.
+ */
+struct sysfs_attribute *sysfs_open_device_attr(const char *bus,
+ const char *bus_id, const char *attrib)
+{
+ struct sysfs_attribute *attribute = NULL;
+ char devpath[SYSFS_PATH_MAX];
+
+ if (bus == NULL || bus_id == NULL || attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ if ((get_device_absolute_path(bus_id, bus, devpath,
+ SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to device %s\n", bus_id);
+ return NULL;
+ }
+ safestrcat(devpath, "/");
+ safestrcat(devpath, attrib);
+ attribute = sysfs_open_attribute(devpath);
+ if (attribute == NULL) {
+ dprintf("Error opening attribute %s for device %s\n",
+ attrib, bus_id);
+ return NULL;
+ }
+ if ((sysfs_read_attribute(attribute)) != 0) {
+ dprintf("Error reading attribute %s for device %s\n",
+ attrib, bus_id);
+ sysfs_close_attribute(attribute);
+ return NULL;
+ }
+ return attribute;
}
+
diff --git a/lib/sysfs_dir.c b/lib/sysfs_dir.c
index e6ad906..5276991 100644
--- a/lib/sysfs_dir.c
+++ b/lib/sysfs_dir.c
@@ -1,9 +1,9 @@
/*
- * syfs_dir.c
+ * sysfs_dir.c
*
* Directory utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -24,6 +24,81 @@
#include "sysfs.h"
/**
+ * sysfs_del_attribute: routine for dlist integration
+ */
+static void sysfs_del_attribute(void *attr)
+{
+ sysfs_close_attribute((struct sysfs_attribute *)attr);
+}
+
+/**
+ * sysfs_del_link: routine for dlist integration
+ */
+static void sysfs_del_link(void *ln)
+{
+ sysfs_close_link((struct sysfs_link *)ln);
+}
+
+/**
+ * sysfs_del_dir: routine for dlist integration
+ */
+static void sysfs_del_directory(void *dir)
+{
+ sysfs_close_directory((struct sysfs_directory *)dir);
+}
+
+/**
+ * dir_attribute_name_equal: compares dir 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 dir_attribute_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_attribute *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dir_link_name_equal: compares dir links by name
+ * @a: link name for comparison
+ * @b: sysfs_link to be compared.
+ * returns 1 if a==b->name or 0 if not equal
+ */
+static int dir_link_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_link *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dir_subdir_name_equal: compares subdirs by name
+ * @a: name of subdirectory to compare
+ * @b: sysfs_directory subdirectory to be compared
+ * returns 1 if a==b->name or 0 if not equal
+ */
+static int dir_subdir_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_directory *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
* sysfs_close_attribute: closes and cleans up attribute
* @sysattr: attribute to close.
*/
@@ -62,13 +137,21 @@ struct sysfs_attribute *sysfs_open_attribute(const char *path)
}
sysattr = alloc_attribute();
if (sysattr == NULL) {
- dprintf(stderr, "Error allocating attribute at %s\n", path);
+ dprintf("Error allocating attribute at %s\n", path);
return NULL;
}
- strncpy(sysattr->path, path, sizeof(sysattr->path));
+ 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) {
- perror("stat");
+ 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;
@@ -80,6 +163,94 @@ struct sysfs_attribute *sysfs_open_attribute(const char *path)
}
/**
+ * 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 == NULL || new_value == NULL || 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)) != 0) {
+ 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 %d 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;
+}
+
+/**
* sysfs_read_attribute: reads value from attribute
* @sysattr: attribute to read
* returns 0 with success and -1 with error.
@@ -88,8 +259,8 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr)
{
char *fbuf = NULL;
char *vbuf = NULL;
- size_t length = 0;
- int pgsize = 0;
+ ssize_t length = 0;
+ long pgsize = 0;
int fd;
if (sysattr == NULL) {
@@ -97,34 +268,43 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr)
return -1;
}
if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
- dprintf (stderr, "Show method not supported for attribute %s\n",
+ dprintf("Show method not supported for attribute %s\n",
sysattr->path);
+ errno = EACCES;
return -1;
}
- pgsize = getpagesize();
+ pgsize = sysconf(_SC_PAGESIZE);
fbuf = (char *)calloc(1, pgsize+1);
if (fbuf == NULL) {
- perror("calloc");
+ dprintf("calloc failed\n");
return -1;
}
if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
- dprintf (stderr, "Error reading attribute %s\n", sysattr->path);
+ dprintf("Error reading attribute %s\n", sysattr->path);
free(fbuf);
return -1;
}
length = read(fd, fbuf, pgsize);
if (length < 0) {
- dprintf (stderr, "Error reading from attribute %s\n",
- sysattr->path);
+ 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 == NULL) {
- perror("realloc");
+ dprintf("realloc failed\n");
free(fbuf);
return -1;
}
@@ -142,33 +322,33 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr)
* @vsize: size of value buffer
* returns 0 with success and -1 with error.
*/
-int sysfs_read_attribute_value(const char *attrpath, char *value, size_t vsize)
+int sysfs_read_attribute_value(const char *attrpath,
+ char *value, size_t vsize)
{
struct sysfs_attribute *attr = NULL;
size_t length = 0;
- if (attrpath == NULL || value == NULL) {
+ if (attrpath == NULL || value == NULL || vsize == 0) {
errno = EINVAL;
return -1;
}
attr = sysfs_open_attribute(attrpath);
if (attr == NULL) {
- dprintf(stderr, "Invalid attribute path %s\n", attrpath);
+ dprintf("Invalid attribute path %s\n", attrpath);
errno = EINVAL;
return -1;
}
if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) {
- dprintf(stderr, "Error reading from attribute %s\n", attrpath);
+ dprintf("Error reading from attribute %s\n", attrpath);
sysfs_close_attribute(attr);
return -1;
}
length = strlen(attr->value);
if (length > vsize)
- dprintf(stderr,
- "Value length %d is larger than supplied buffer %d\n",
+ dprintf("Value length %d is larger than supplied buffer %d\n",
length, vsize);
- strncpy(value, attr->value, vsize);
+ safestrcpymax(value, attr->value, vsize);
sysfs_close_attribute(attr);
return 0;
@@ -181,83 +361,29 @@ int sysfs_read_attribute_value(const char *attrpath, char *value, size_t vsize)
* @name: name to look for
* returns char * value - could be NULL
*/
-char *sysfs_get_value_from_attributes(struct sysfs_attribute *attr,
- const char *name)
+char *sysfs_get_value_from_attributes(struct dlist *attr, const char *name)
{
struct sysfs_attribute *cur = NULL;
- char tmpname[SYSFS_NAME_LEN];
if (attr == NULL || name == NULL) {
errno = EINVAL;
return NULL;
- }
- cur = attr;
- while (cur != NULL) {
- memset(tmpname, 0, SYSFS_NAME_LEN);
- if ((sysfs_get_name_from_path(cur->path, tmpname,
- SYSFS_NAME_LEN)) != 0) {
- cur = cur->next;
- continue;
- }
- if (strcmp(tmpname, name) == 0)
+ }
+ dlist_for_each_data(attr, cur, struct sysfs_attribute) {
+ if (strcmp(cur->name, name) == 0)
return cur->value;
- cur = cur->next;
}
return NULL;
}
/**
- * add_subdir_to_dir: adds subdirectory to directory's subdirs
- * @sysdir: directory to add subdir to
- * @subdir: subdirectory to add.
- */
-static void add_subdir_to_dir(struct sysfs_directory *sysdir,
- struct sysfs_directory *subdir)
-{
- if (sysdir != NULL && subdir != NULL) {
- subdir->next = sysdir->subdirs;
- sysdir->subdirs = subdir;
- }
-}
-
-/**
- * add_attr_to_dir: adds attribute to directory's attributes
- * @sysdir: directory to add attribute to
- * @sysattr: attribute to add.
- */
-static void add_attr_to_dir(struct sysfs_directory *sysdir,
- struct sysfs_attribute *sysattr)
-{
- if (sysdir != NULL && sysattr != NULL) {
- sysattr->next = sysdir->attributes;
- sysdir->attributes = sysattr;
- }
-}
-
-/**
* sysfs_close_link: closes and cleans up link.
* @ln: link to close.
*/
void sysfs_close_link(struct sysfs_link *ln)
{
- if (ln != NULL) {
- ln->next = NULL;
+ if (ln != NULL)
free(ln);
- }
-}
-
-/**
- * add_link_to_dir: adds link to directory's links list.
- * @sysdir: directory to add it to.
- * @ln: link to add.
- */
-static void add_link_to_dir(struct sysfs_directory *sysdir,
- struct sysfs_link *ln)
-{
- if (sysdir != NULL && ln != NULL) {
- ln->next = sysdir->links;
- sysdir->links = ln;
- }
}
/**
@@ -266,36 +392,15 @@ static void add_link_to_dir(struct sysfs_directory *sysdir,
*/
void sysfs_close_directory(struct sysfs_directory *sysdir)
{
- struct sysfs_directory *sdir = NULL, *dnext = NULL;
- struct sysfs_link *dlink = NULL, *nextl = NULL;
- struct sysfs_attribute *attr = NULL, *anext = NULL;
-
if (sysdir != NULL) {
- if (sysdir->subdirs != NULL) {
- for (sdir = sysdir->subdirs; sdir != NULL;
- sdir = dnext) {
- dnext = sdir->next;
- sysfs_close_directory(sdir);
- }
- }
- if (sysdir->links != NULL) {
- for (dlink = sysdir->links; dlink != NULL;
- dlink = nextl) {
- nextl = dlink->next;
- sysfs_close_link(dlink);
- }
- }
- if (sysdir->attributes != NULL) {
- for (attr = sysdir->attributes; attr != NULL;
- attr = anext) {
- anext = attr->next;
- /* sysfs_close_attribute(attr); */
- if (attr->value != NULL)
- free(attr->value);
- free(attr);
- }
- }
+ if (sysdir->subdirs != NULL)
+ dlist_destroy(sysdir->subdirs);
+ if (sysdir->links != NULL)
+ dlist_destroy(sysdir->links);
+ if (sysdir->attributes != NULL)
+ dlist_destroy(sysdir->attributes);
free(sysdir);
+ sysdir = NULL;
}
}
@@ -319,6 +424,38 @@ static struct sysfs_link *alloc_link(void)
}
/**
+ * sysfs_read_all_subdirs: calls sysfs_read_directory for all subdirs
+ * @sysdir: directory whose subdirs need reading.
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_read_all_subdirs(struct sysfs_directory *sysdir)
+{
+ struct sysfs_directory *cursub = NULL;
+ int retval = 0;
+
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sysdir->subdirs == NULL)
+ if ((sysfs_read_dir_subdirs(sysdir)) != 0)
+ return 0;
+ if (sysdir->subdirs != NULL) {
+ dlist_for_each_data(sysdir->subdirs, cursub,
+ struct sysfs_directory) {
+ if ((sysfs_read_dir_subdirs(cursub)) != 0) {
+ dprintf ("Error reading subdirectory %s\n",
+ cursub->name);
+ retval = -1;
+ }
+ }
+ }
+ if (!retval)
+ errno = 0;
+ return retval;
+}
+
+/**
* sysfs_open_directory: opens a sysfs directory, creates dir struct, and
* returns.
* @path: path of directory to open.
@@ -332,12 +469,24 @@ struct sysfs_directory *sysfs_open_directory(const char *path)
errno = EINVAL;
return NULL;
}
+
+ if (sysfs_path_is_dir(path) != 0) {
+ dprintf("Invalid path to directory %s\n", path);
+ errno = EINVAL;
+ return NULL;
+ }
+
sdir = alloc_directory();
if (sdir == NULL) {
- dprintf(stderr, "Error allocating directory %s\n", path);
+ dprintf("Error allocating directory %s\n", path);
return NULL;
}
- strncpy(sdir->path, path, sizeof(sdir->path));
+ if (sysfs_get_name_from_path(path, sdir->name, SYSFS_NAME_LEN) != 0) {
+ dprintf("Error getting directory name from path: %s\n", path);
+ sysfs_close_directory(sdir);
+ return NULL;
+ }
+ safestrcpy(sdir->path, path);
return sdir;
}
@@ -351,21 +500,22 @@ struct sysfs_link *sysfs_open_link(const char *linkpath)
{
struct sysfs_link *ln = NULL;
- if (linkpath == NULL) {
+ if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) {
errno = EINVAL;
return NULL;
}
ln = alloc_link();
if (ln == NULL) {
- dprintf(stderr,
- "Error allocating link %s\n", linkpath);
+ dprintf("Error allocating link %s\n", linkpath);
return NULL;
}
+ safestrcpy(ln->path, linkpath);
if ((sysfs_get_name_from_path(linkpath, ln->name, SYSFS_NAME_LEN)) != 0
|| (sysfs_get_link(linkpath, ln->target, SYSFS_PATH_MAX)) != 0) {
+ sysfs_close_link(ln);
errno = EINVAL;
- dprintf(stderr, "Invalid link path %s\n", linkpath);
+ dprintf("Invalid link path %s\n", linkpath);
return NULL;
}
@@ -373,6 +523,202 @@ struct sysfs_link *sysfs_open_link(const char *linkpath)
}
/**
+ * add_attribute: open and add attribute at path to given directory
+ * @sysdir: directory to add attribute to
+ * @path: path to attribute
+ * returns 0 with success and -1 with error.
+ */
+static int add_attribute(struct sysfs_directory *sysdir, const char *path)
+{
+ struct sysfs_attribute *attr = NULL;
+
+ attr = sysfs_open_attribute(path);
+ if (attr == NULL) {
+ dprintf("Error opening attribute %s\n", path);
+ return -1;
+ }
+ if (attr->method & SYSFS_METHOD_SHOW) {
+ if ((sysfs_read_attribute(attr)) != 0) {
+ dprintf("Error reading attribute %s\n", path);
+ sysfs_close_attribute(attr);
+ return 0;
+ }
+ }
+
+ if (sysdir->attributes == NULL) {
+ sysdir->attributes = dlist_new_with_delete
+ (sizeof(struct sysfs_attribute), sysfs_del_attribute);
+ }
+ dlist_unshift_sorted(sysdir->attributes, attr, sort_list);
+
+ return 0;
+}
+
+/**
+ * add_subdirectory: open and add subdirectory at path to given directory
+ * @sysdir: directory to add subdir to
+ * @path: path to subdirectory
+ * returns 0 with success and -1 with error.
+ */
+static int add_subdirectory(struct sysfs_directory *sysdir, const char *path)
+{
+ struct sysfs_directory *subdir = NULL;
+
+ subdir = sysfs_open_directory(path);
+ if (subdir == NULL) {
+ dprintf("Error opening directory %s\n", path);
+ return -1;
+ }
+ if (sysdir->subdirs == NULL)
+ sysdir->subdirs = dlist_new_with_delete
+ (sizeof(struct sysfs_directory), sysfs_del_directory);
+ dlist_unshift_sorted(sysdir->subdirs, subdir, sort_list);
+ return 0;
+}
+
+/**
+ * add_link: open and add link at path to given directory
+ * @sysdir: directory to add link to
+ * @path: path to link
+ * returns 0 with success and -1 with error.
+ */
+static int add_link(struct sysfs_directory *sysdir, const char *path)
+{
+ struct sysfs_link *ln = NULL;
+
+ ln = sysfs_open_link(path);
+ if (ln == NULL) {
+ dprintf("Error opening link %s\n", path);
+ return -1;
+ }
+ if (sysdir->links == NULL)
+ sysdir->links = dlist_new_with_delete
+ (sizeof(struct sysfs_link), sysfs_del_link);
+ dlist_unshift_sorted(sysdir->links, ln, sort_list);
+ return 0;
+}
+
+/**
+ * sysfs_read_dir_attributes: grabs attributes for the given directory
+ * @sysdir: sysfs directory to open
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_read_dir_attributes(struct sysfs_directory *sysdir)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX];
+ int retval = 0;
+
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ dir = opendir(sysdir->path);
+ if (dir == NULL) {
+ dprintf("Error opening directory %s\n", sysdir->path);
+ return -1;
+ }
+ while(((dirent = readdir(dir)) != NULL) && retval == 0) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, sysdir->path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if ((sysfs_path_is_file(file_path)) == 0)
+ retval = add_attribute(sysdir, file_path);
+ }
+ closedir(dir);
+ if (!retval)
+ errno = 0;
+ return(retval);
+}
+
+/**
+ * sysfs_read_dir_links: grabs links in a specific directory
+ * @sysdir: sysfs directory to read links
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_read_dir_links(struct sysfs_directory *sysdir)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX];
+ int retval = 0;
+
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ dir = opendir(sysdir->path);
+ if (dir == NULL) {
+ dprintf("Error opening directory %s\n", sysdir->path);
+ return -1;
+ }
+ while(((dirent = readdir(dir)) != NULL) && retval == 0) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, sysdir->path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if ((sysfs_path_is_link(file_path)) == 0) {
+ retval = add_link(sysdir, file_path);
+ if (retval != 0)
+ break;
+ }
+ }
+ closedir(dir);
+ if (!retval)
+ errno = 0;
+ return(retval);
+}
+
+/**
+ * sysfs_read_dir_subdirs: grabs subdirs in a specific directory
+ * @sysdir: sysfs directory to read links
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_read_dir_subdirs(struct sysfs_directory *sysdir)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX];
+ int retval = 0;
+
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ dir = opendir(sysdir->path);
+ if (dir == NULL) {
+ dprintf("Error opening directory %s\n", sysdir->path);
+ return -1;
+ }
+ while(((dirent = readdir(dir)) != NULL) && retval == 0) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, sysdir->path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if ((sysfs_path_is_dir(file_path)) == 0)
+ retval = add_subdirectory(sysdir, file_path);
+ }
+ closedir(dir);
+ if (!retval)
+ errno = 0;
+ return(retval);
+}
+
+/**
* sysfs_read_directory: grabs attributes, links, and subdirectories
* @sysdir: sysfs directory to open
* returns 0 with success and -1 with error.
@@ -382,9 +728,6 @@ int sysfs_read_directory(struct sysfs_directory *sysdir)
DIR *dir = NULL;
struct dirent *dirent = NULL;
struct stat astats;
- struct sysfs_attribute *attr = NULL;
- struct sysfs_directory *subdir = NULL;
- struct sysfs_link *ln = NULL;
char file_path[SYSFS_PATH_MAX];
int retval = 0;
@@ -394,7 +737,7 @@ int sysfs_read_directory(struct sysfs_directory *sysdir)
}
dir = opendir(sysdir->path);
if (dir == NULL) {
- perror("opendir");
+ dprintf("Error opening directory %s\n", sysdir->path);
return -1;
}
while(((dirent = readdir(dir)) != NULL) && retval == 0) {
@@ -403,51 +746,326 @@ int sysfs_read_directory(struct sysfs_directory *sysdir)
if (0 == strcmp(dirent->d_name, ".."))
continue;
memset(file_path, 0, SYSFS_PATH_MAX);
- strncpy(file_path, sysdir->path, sizeof(file_path));
- strncat(file_path, "/", sizeof(file_path));
- strncat(file_path, dirent->d_name, sizeof(file_path));
+ safestrcpy(file_path, sysdir->path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
if ((lstat(file_path, &astats)) != 0) {
- perror("stat");
+ dprintf("stat failed\n");
continue;
}
- if (S_ISREG(astats.st_mode)) {
- attr = sysfs_open_attribute(file_path);
- if (attr == NULL) {
- dprintf (stderr, "Error opening attribute %s\n",
- file_path);
- retval = -1;
- break;
+ if (S_ISDIR(astats.st_mode))
+ retval = add_subdirectory(sysdir, file_path);
+
+ else if (S_ISLNK(astats.st_mode))
+ retval = add_link(sysdir, file_path);
+
+ else if (S_ISREG(astats.st_mode))
+ retval = add_attribute(sysdir, file_path);
+ }
+ closedir(dir);
+ if (!retval)
+ errno = 0;
+ return(retval);
+}
+
+/**
+ * sysfs_refresh_dir_attributes: Refresh attributes list
+ * @sysdir: directory whose list of attributes to refresh
+ * Returns 0 on success, 1 on failure
+ */
+int sysfs_refresh_dir_attributes(struct sysfs_directory *sysdir)
+{
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((sysfs_path_is_dir(sysdir->path)) != 0) {
+ dprintf("Invalid path to directory %s\n", sysdir->path);
+ errno = EINVAL;
+ return 1;
+ }
+ if (sysdir->attributes != NULL) {
+ dlist_destroy(sysdir->attributes);
+ sysdir->attributes = NULL;
+ }
+ if ((sysfs_read_dir_attributes(sysdir)) != 0) {
+ dprintf("Error refreshing attributes for directory %s\n",
+ sysdir->path);
+ return 1;
+ }
+ errno = 0;
+ return 0;
+}
+
+/**
+ * sysfs_refresh_dir_links: Refresh links list
+ * @sysdir: directory whose list of links to refresh
+ * Returns 0 on success, 1 on failure
+ */
+int sysfs_refresh_dir_links(struct sysfs_directory *sysdir)
+{
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((sysfs_path_is_dir(sysdir->path)) != 0) {
+ dprintf("Invalid path to directory %s\n", sysdir->path);
+ errno = EINVAL;
+ return 1;
+ }
+ if (sysdir->links != NULL) {
+ dlist_destroy(sysdir->links);
+ sysdir->links = NULL;
+ }
+ if ((sysfs_read_dir_links(sysdir)) != 0) {
+ dprintf("Error refreshing links for directory %s\n",
+ sysdir->path);
+ return 1;
+ }
+ errno = 0;
+ return 0;
+}
+
+/**
+ * sysfs_refresh_dir_subdirs: Refresh subdirs list
+ * @sysdir: directory whose list of subdirs to refresh
+ * Returns 0 on success, 1 on failure
+ */
+int sysfs_refresh_dir_subdirs(struct sysfs_directory *sysdir)
+{
+ if (sysdir == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((sysfs_path_is_dir(sysdir->path)) != 0) {
+ dprintf("Invalid path to directory %s\n", sysdir->path);
+ errno = EINVAL;
+ return 1;
+ }
+ if (sysdir->subdirs != NULL) {
+ dlist_destroy(sysdir->subdirs);
+ sysdir->subdirs = NULL;
+ }
+ if ((sysfs_read_dir_subdirs(sysdir)) != 0) {
+ dprintf("Error refreshing subdirs for directory %s\n",
+ sysdir->path);
+ return 1;
+ }
+ errno = 0;
+ return 0;
+}
+
+/**
+ * sysfs_get_directory_attribute: retrieves attribute attrname from current
+ * directory only
+ * @dir: directory to retrieve attribute from
+ * @attrname: name of attribute to look for
+ *
+ * NOTE: Since we know the attribute to look for, this routine looks for the
+ * attribute if it was created _after_ the attrlist was read initially.
+ *
+ * returns sysfs_attribute if found and NULL if not found
+ */
+struct sysfs_attribute *sysfs_get_directory_attribute
+ (struct sysfs_directory *dir, char *attrname)
+{
+ struct sysfs_attribute *attr = NULL;
+ char new_path[SYSFS_PATH_MAX];
+
+ if (dir == NULL || attrname == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir->attributes == NULL)
+ if ((sysfs_read_dir_attributes(dir) != 0)
+ || (dir->attributes == NULL))
+ return NULL;
+
+ attr = (struct sysfs_attribute *)dlist_find_custom
+ (dir->attributes, attrname, dir_attribute_name_equal);
+ if (attr != NULL) {
+ if ((attr->method & SYSFS_METHOD_SHOW) &&
+ (sysfs_read_attribute(attr)) != 0) {
+ dprintf("Error reading attribute %s\n", attr->name);
+ return NULL;
+ }
+ } else {
+ memset(new_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(new_path, dir->path);
+ safestrcat(new_path, "/");
+ safestrcat(new_path, attrname);
+ if ((sysfs_path_is_file(new_path)) == 0) {
+ if ((add_attribute(dir, new_path)) == 0) {
+ attr = (struct sysfs_attribute *)
+ dlist_find_custom(dir->attributes,
+ attrname, dir_attribute_name_equal);
}
- if (attr->method & SYSFS_METHOD_SHOW) {
- if ((sysfs_read_attribute(attr)) != 0) {
- dprintf (stderr,
- "Error reading attribute %s\n",
- file_path);
- sysfs_close_attribute(attr);
+ }
+ }
+
+ return attr;
+}
+
+/**
+ * sysfs_get_directory_link: retrieves link from one directory list
+ * @dir: directory to retrieve link from
+ * @linkname: name of link to look for
+ * returns reference to sysfs_link if found and NULL if not found
+ */
+struct sysfs_link *sysfs_get_directory_link
+ (struct sysfs_directory *dir, char *linkname)
+{
+ if (dir == NULL || linkname == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (dir->links == NULL) {
+ if ((sysfs_read_dir_links(dir) != 0) || (dir->links == NULL))
+ return NULL;
+ } else {
+ if ((sysfs_refresh_dir_links(dir)) != 0)
+ return NULL;
+ }
+
+ return (struct sysfs_link *)dlist_find_custom(dir->links,
+ linkname, dir_link_name_equal);
+}
+
+/**
+ * sysfs_get_subdirectory: retrieves subdirectory by name.
+ * @dir: directory to search for subdirectory.
+ * @subname: subdirectory name to get.
+ * returns reference to subdirectory or NULL if not found
+ */
+struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir,
+ char *subname)
+{
+ struct sysfs_directory *sub = NULL, *cursub = NULL;
+
+ if (dir == NULL || subname == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir->subdirs == NULL)
+ if (sysfs_read_dir_subdirs(dir) != 0)
+ return NULL;
+
+ sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs,
+ subname, dir_subdir_name_equal);
+ if (sub != NULL)
+ return sub;
+
+ if (dir->subdirs != NULL) {
+ dlist_for_each_data(dir->subdirs, cursub,
+ struct sysfs_directory) {
+ if (cursub->subdirs == NULL) {
+ if (sysfs_read_dir_subdirs(cursub) != 0)
+ continue;
+ if (cursub->subdirs == NULL)
continue;
- }
- }
- add_attr_to_dir(sysdir, attr);
- } else if (S_ISDIR(astats.st_mode)) {
- subdir = sysfs_open_directory(file_path);
- if (subdir == NULL) {
- dprintf (stderr, "Error opening directory %s\n",
- file_path);
- retval = -1;
- break;
- }
- add_subdir_to_dir(sysdir, subdir);
- } else if (S_ISLNK(astats.st_mode)) {
- ln = sysfs_open_link(file_path);
- if (ln == NULL) {
- dprintf(stderr, "Error opening link %s\n",
- file_path);
- retval = -1;
- break;
}
- add_link_to_dir(sysdir, ln);
+ sub = sysfs_get_subdirectory(cursub, subname);
+ if (sub != NULL)
+ return sub;
}
}
- closedir(dir);
- return(retval);
+ return NULL;
+}
+
+/**
+ * sysfs_get_subdirectory_link: looks through all subdirs for specific link.
+ * @dir: directory and subdirectories to search for link.
+ * @linkname: link name to get.
+ * returns reference to link or NULL if not found
+ */
+struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir,
+ char *linkname)
+{
+ struct sysfs_directory *cursub = NULL;
+ struct sysfs_link *ln = NULL;
+
+ if (dir == NULL || linkname == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ln = sysfs_get_directory_link(dir, linkname);
+ if (ln != NULL)
+ return ln;
+
+ if (dir->subdirs == NULL)
+ if (sysfs_read_dir_subdirs(dir) != 0)
+ return NULL;
+
+ if (dir->subdirs != NULL) {
+ dlist_for_each_data(dir->subdirs, cursub,
+ struct sysfs_directory) {
+ ln = sysfs_get_subdirectory_link(cursub, linkname);
+ if (ln != NULL)
+ return ln;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * sysfs_get_dir_attributes: returns dlist of directory attributes
+ * @dir: directory to retrieve attributes from
+ * returns dlist of attributes or NULL
+ */
+struct dlist *sysfs_get_dir_attributes(struct sysfs_directory *dir)
+{
+ if (dir == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir->attributes == NULL) {
+ if (sysfs_read_dir_attributes(dir) != 0)
+ return NULL;
+ }
+
+ return (dir->attributes);
+}
+
+/**
+ * sysfs_get_dir_links: returns dlist of directory links
+ * @dir: directory to return links for
+ * returns dlist of links or NULL
+ */
+struct dlist *sysfs_get_dir_links(struct sysfs_directory *dir)
+{
+ if (dir == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir->links == NULL) {
+ if (sysfs_read_dir_links(dir) != 0)
+ return NULL;
+ }
+
+ return (dir->links);
+}
+
+/**
+ * sysfs_get_dir_subdirs: returns dlist of directory subdirectories
+ * @dir: directory to return subdirs for
+ * returns dlist of subdirs or NULL
+ */
+struct dlist *sysfs_get_dir_subdirs(struct sysfs_directory *dir)
+{
+ if (dir == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir->subdirs == NULL) {
+ if (sysfs_read_dir_subdirs(dir) != 0)
+ return NULL;
+ }
+
+ return (dir->subdirs);
}
diff --git a/lib/sysfs_driver.c b/lib/sysfs_driver.c
index 6813c85..88d26b5 100644
--- a/lib/sysfs_driver.c
+++ b/lib/sysfs_driver.c
@@ -3,7 +3,7 @@
*
* Driver utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,18 +23,47 @@
#include "libsysfs.h"
#include "sysfs.h"
-/**
- * sysfs_close_driver: closes and cleans up driver structure
+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 != NULL) {
+ if (driver->devices != NULL)
+ dlist_destroy(driver->devices);
if (driver->directory != NULL)
sysfs_close_directory(driver->directory);
free(driver);
}
}
+
+/**
+ * open_driver_dir: Open the sysfs_directory for this driver
+ * @driver: Driver whose directory to be opened
+ * Returns 0 on success and 1 on failure
+ */
+static int open_driver_dir(struct sysfs_driver *driver)
+{
+ if (driver == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ if (driver->directory == NULL) {
+ driver->directory = sysfs_open_directory(driver->path);
+ if (driver->directory == NULL) {
+ dprintf("Error opening driver directory at %s\n",
+ driver->path);
+ return 1;
+ }
+ }
+ return 0;
+}
/**
* alloc_driver: allocates and initializes driver
@@ -46,44 +75,348 @@ static struct sysfs_driver *alloc_driver(void)
}
/**
- * sysfs_open_driver: opens and initializes driver structure
+ * 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(const char *path)
+struct sysfs_driver *sysfs_open_driver_path(const char *path)
{
struct sysfs_driver *driver = NULL;
- struct sysfs_directory *sdir = NULL;
- char devname[SYSFS_NAME_LEN];
if (path == NULL) {
errno = EINVAL;
return NULL;
}
- sdir = sysfs_open_directory(path);
- if (sdir == NULL) {
- dprintf (stderr, "Error opening directory %s\n", path);
- return NULL;
- }
- if ((sysfs_read_directory(sdir)) != 0) {
- dprintf (stderr, "Error reading directory %s\n", path);
- sysfs_close_directory(sdir);
+ if ((sysfs_path_is_dir(path)) != 0) {
+ dprintf("Invalid path to driver: %s\n", path);
return NULL;
}
driver = alloc_driver();
if (driver == NULL) {
- dprintf(stderr, "Error allocating driver at %s\n", path);
- sysfs_close_directory(sdir);
+ dprintf("Error allocating driver at %s\n", path);
return NULL;
}
- if ((sysfs_get_name_from_path(path, devname, SYSFS_NAME_LEN)) != 0) {
- dprintf (stderr, "Error reading directory %s\n", path);
- sysfs_close_directory(sdir);
+ if ((sysfs_get_name_from_path(path, driver->name,
+ SYSFS_NAME_LEN)) != 0) {
+ dprintf("Error getting driver name from path\n");
free(driver);
return NULL;
}
- strncpy(driver->name, devname, sizeof(driver->name));
- driver->directory = sdir;
+ safestrcpy(driver->path, path);
+ if ((sysfs_remove_trailing_slash(driver->path)) != 0) {
+ dprintf("Invalid path to driver %s\n", driver->path);
+ sysfs_close_driver(driver);
+ return NULL;
+ }
return driver;
}
+
+/**
+ * sysfs_get_driver_attributes: gets list of attributes for the given driver
+ * @driver: sysfs_driver for which attributes are required
+ * returns a dlist of attributes corresponding to the driver if present
+ * NULL otherwise
+ */
+struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *driver)
+{
+ if (driver == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (driver->directory == NULL) {
+ if ((open_driver_dir(driver)) == 1)
+ return NULL;
+ }
+ if (driver->directory->attributes == NULL) {
+ if ((sysfs_read_dir_attributes(driver->directory)) != 0)
+ return NULL;
+ }
+ return(driver->directory->attributes);
+}
+
+/**
+ * sysfs_refresh_driver_attributes: refreshes the driver's list of attributes
+ * @driver: sysfs_driver whose attributes to refresh
+ *
+ * NOTE: Upon return, prior references to sysfs_attributes for this driver
+ * _may_ not be valid
+ *
+ * Returns list of attributes on success and NULL on failure
+ */
+struct dlist *sysfs_refresh_driver_attributes(struct sysfs_driver *driver)
+{
+ if (driver == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (driver->directory == NULL)
+ return (sysfs_get_driver_attributes(driver));
+
+ if ((sysfs_refresh_dir_attributes(driver->directory)) != 0) {
+ dprintf("Error refreshing driver attributes\n");
+ return NULL;
+ }
+ return (driver->directory->attributes);
+}
+
+/**
+ * sysfs_get_driver_attr: searches driver's attributes by name
+ * @drv: driver to look through
+ * @name: attribute name to get
+ * returns sysfs_attribute reference on success or NULL with error
+ */
+struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv,
+ const char *name)
+{
+ struct dlist *attrlist = NULL;
+
+ if (drv == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ attrlist = sysfs_get_driver_attributes(drv);
+ if (attrlist == NULL)
+ return NULL;
+
+ return sysfs_get_directory_attribute(drv->directory, (char *)name);
+}
+
+/**
+ * sysfs_get_driver_links: gets list of links from the given driver
+ * @driver: sysfs_driver for which links list is required
+ * returns a dlist of links corresponding to the driver if present
+ * NULL otherwise
+ */
+struct dlist *sysfs_get_driver_links(struct sysfs_driver *driver)
+{
+ if (driver == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (driver->directory == NULL)
+ if ((open_driver_dir(driver)) == 1)
+ return NULL;
+
+ if (driver->directory->links == NULL)
+ if ((sysfs_read_dir_links(driver->directory)) != 0)
+ return NULL;
+
+ return(driver->directory->links);
+}
+
+/**
+ * sysfs_get_driver_devices: open up the list of devices this driver supports
+ * @driver: sysfs_driver for which devices are needed
+ * Returns dlist of devices on SUCCESS or NULL with ERROR
+ */
+struct dlist *sysfs_get_driver_devices(struct sysfs_driver *driver)
+{
+ struct sysfs_link *curlink = NULL;
+ struct sysfs_device *device = NULL;
+
+ if (driver == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (driver->devices != NULL)
+ return (driver->devices);
+
+ if (driver->directory == NULL || driver->directory->links == NULL) {
+ struct dlist *list = NULL;
+ list = sysfs_get_driver_links(driver);
+ }
+
+ if (driver->directory->links != NULL) {
+ dlist_for_each_data(driver->directory->links, curlink,
+ struct sysfs_link) {
+ device = sysfs_open_device_path(curlink->target);
+ if (device == NULL) {
+ dprintf("Error opening device at %s\n",
+ curlink->target);
+ return NULL;
+ }
+ if (driver->devices == NULL)
+ driver->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_driver_device);
+ dlist_unshift_sorted(driver->devices, device,
+ sort_list);
+ }
+ }
+ return (driver->devices);
+}
+
+/**
+ * sysfs_refresh_driver_devices: Refreshes drivers list of devices
+ * @driver: sysfs_driver whose devices list needs to be refreshed
+ *
+ * NOTE: Upon return from this function, prior sysfs_device references from
+ * this driver's list of devices _may_ not be valid
+ *
+ * Returns dlist of devices on success and NULL on failure
+ */
+struct dlist *sysfs_refresh_driver_devices(struct sysfs_driver *driver)
+{
+ if (driver == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (driver->devices != NULL) {
+ dlist_destroy(driver->devices);
+ driver->devices = NULL;
+ }
+
+ if (driver->directory == NULL)
+ return (sysfs_get_driver_devices(driver));
+
+ if ((sysfs_refresh_dir_links(driver->directory)) != 0) {
+ dprintf("Error refreshing driver links\n");
+ return NULL;
+ }
+
+ return (sysfs_get_driver_devices(driver));
+}
+
+/**
+ * sysfs_get_driver_device: looks up a device from a list of driver's devices
+ * and returns its sysfs_device corresponding to it
+ * @driver: sysfs_driver on which to search
+ * @name: name of the device to search
+ * Returns a sysfs_device if found, NULL otherwise
+ */
+struct sysfs_device *sysfs_get_driver_device(struct sysfs_driver *driver,
+ const char *name)
+{
+ struct sysfs_device *device = NULL;
+ struct dlist *devlist = NULL;
+
+ if (driver == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (driver->devices == NULL) {
+ devlist = sysfs_get_driver_devices(driver);
+ if (devlist == NULL) {
+ dprintf("Error getting driver devices\n");
+ return NULL;
+ }
+ }
+ dlist_for_each_data(driver->devices, device, struct sysfs_device) {
+ if (!(strncmp(device->name, name, SYSFS_NAME_LEN)))
+ return device;
+ }
+ return NULL;
+}
+
+/**
+ * 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 == NULL || drv == NULL || path == NULL || psize == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sysfs_get_mnt_path(path, psize) != 0) {
+ 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_attr: read the user supplied driver attribute
+ * @bus: bus on which to look
+ * @drv: driver whose attribute has to be read
+ * @attrib: Attribute to be read
+ * Returns struct sysfs_attribute on success and NULL on failure
+ *
+ * NOTE:
+ * A call to sysfs_close_attribute() is required to close the
+ * attribute returned and to free memory
+ */
+struct sysfs_attribute *sysfs_open_driver_attr(const char *bus,
+ const char *drv, const char *attrib)
+{
+ struct sysfs_attribute *attribute = NULL;
+ char path[SYSFS_PATH_MAX];
+
+ if (bus == NULL || drv == NULL || attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(path, 0, SYSFS_PATH_MAX);
+ if ((get_driver_path(bus, drv, path, SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to driver %s\n", drv);
+ return NULL;
+ }
+ safestrcat(path, "/");
+ safestrcat(path, attrib);
+ attribute = sysfs_open_attribute(path);
+ if (attribute == NULL) {
+ dprintf("Error opening attribute %s for driver %s\n",
+ attrib, drv);
+ return NULL;
+ }
+ if ((sysfs_read_attribute(attribute)) != 0) {
+ dprintf("Error reading attribute %s for driver %s\n",
+ attrib, drv);
+ sysfs_close_attribute(attribute);
+ return NULL;
+ }
+ return attribute;
+}
+
+/**
+ * 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 == NULL || bus_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(path, 0, SYSFS_PATH_MAX);
+ if ((get_driver_path(bus_name, drv_name, path, SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to driver %s\n", drv_name);
+ return NULL;
+ }
+ driver = sysfs_open_driver_path(path);
+ if (driver == NULL) {
+ dprintf("Error opening driver at %s\n", path);
+ return NULL;
+ }
+ return driver;
+}
+
diff --git a/lib/sysfs_utils.c b/lib/sysfs_utils.c
index a2410ab..8b1f56e 100644
--- a/lib/sysfs_utils.c
+++ b/lib/sysfs_utils.c
@@ -1,9 +1,9 @@
/*
- * syfs_utils.c
+ * sysfs_utils.c
*
* System utility functions for libsysfs
*
- * Copyright (C) 2003 International Business Machines, Inc.
+ * Copyright (C) IBM Corp. 2003
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,15 +23,45 @@
#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_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)
+{
+ char *c = NULL;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return 1;
+ }
+ c = strrchr(path, '/');
+ if (c == NULL) {
+ dprintf("Invalid path %s\n", path);
+ errno = EINVAL;
+ return 1;
+ }
+ if (*(c+1) == '\0')
+ *c = '\0';
+ return 0;
+}
+
/**
- * sysfs_get_mnt_path: Gets the mount point for specified filesystem.
+ * sysfs_get_fs_mnt_path: Gets the mount point for specified filesystem.
* @fs_type: filesystem type to retrieve mount point
* @mnt_path: place to put the retrieved mount path
* @len: size of mnt_path
* returns 0 with success and -1 with error.
*/
-static int sysfs_get_fs_mnt_path(const char *fs_type, char *mnt_path,
- size_t len)
+static int sysfs_get_fs_mnt_path(const char *fs_type,
+ char *mnt_path, size_t len)
{
FILE *mnt;
struct mntent *mntent;
@@ -39,33 +69,35 @@ static int sysfs_get_fs_mnt_path(const char *fs_type, char *mnt_path,
size_t dirlen = 0;
/* check arg */
- if (fs_type == NULL || mnt_path == NULL) {
+ if (fs_type == NULL || mnt_path == NULL || len == 0) {
errno = EINVAL;
return -1;
}
if ((mnt = setmntent(SYSFS_PROC_MNTS, "r")) == NULL) {
- dprintf(stderr, "Error getting mount information\n");
+ dprintf("Error getting mount information\n");
return -1;
}
while (ret == 0 && dirlen == 0 && (mntent = getmntent(mnt)) != NULL) {
if (strcmp(mntent->mnt_type, fs_type) == 0) {
dirlen = strlen(mntent->mnt_dir);
if (dirlen <= (len - 1)) {
- strcpy(mnt_path, mntent->mnt_dir);
+ safestrcpymax(mnt_path, mntent->mnt_dir, len);
} else {
- dprintf(stderr,
- "Error - mount path too long\n");
+ dprintf("Error - mount path too long\n");
ret = -1;
}
}
}
endmntent(mnt);
if (dirlen == 0 && ret == 0) {
- dprintf(stderr, "Filesystem %s not found!\n", fs_type);
+ dprintf("Filesystem %s not found!\n", fs_type);
errno = EINVAL;
ret = -1;
}
+ if ((sysfs_remove_trailing_slash(mnt_path)) != 0)
+ ret = -1;
+
return ret;
}
@@ -77,12 +109,20 @@ static int sysfs_get_fs_mnt_path(const char *fs_type, char *mnt_path,
*/
int sysfs_get_mnt_path(char *mnt_path, size_t len)
{
- int ret = -1;
+ char *sysfs_path = NULL;
+ int ret = 0;
- if (mnt_path != NULL)
- ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len);
- else
+ if (mnt_path == NULL || len == 0) {
errno = EINVAL;
+ return -1;
+ }
+ sysfs_path = getenv(SYSFS_PATH_ENV);
+ if (sysfs_path != NULL) {
+ safestrcpymax(mnt_path, sysfs_path, len);
+ if ((sysfs_remove_trailing_slash(mnt_path)) != 0)
+ return 1;
+ } else
+ ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len);
return ret;
}
@@ -95,23 +135,33 @@ int sysfs_get_mnt_path(char *mnt_path, size_t len)
*/
int sysfs_get_name_from_path(const char *path, char *name, size_t len)
{
+ char tmp[SYSFS_PATH_MAX];
char *n = NULL;
- if (path == NULL || name == NULL) {
+ if (path == NULL || name == NULL || len == 0) {
errno = EINVAL;
return -1;
}
- n = strrchr(path, '/');
+ 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++;
- strncpy(name, n, len);
-
+ safestrcpymax(name, n, len);
return 0;
}
-
+
/**
* sysfs_get_link: returns link source
* @path: symbolic link's path
@@ -122,35 +172,308 @@ int sysfs_get_link(const char *path, char *target, size_t len)
{
char devdir[SYSFS_PATH_MAX];
char linkpath[SYSFS_PATH_MAX];
- char *d = NULL;
+ char temp_path[SYSFS_PATH_MAX];
+ char *d = NULL, *s = NULL;
+ int slashes = 0, count = 0;
- if (path == NULL || target == NULL) {
+ if (path == NULL || target == NULL || 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 ((sysfs_get_mnt_path(devdir, SYSFS_PATH_MAX)) != 0) {
- dprintf(stderr, "Sysfs not supported on this system\n");
- return -1;
- }
-
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_del_name: free function for sysfs_open_subsystem_list
+ * @name: memory area to be freed
+ */
+static void sysfs_del_name(void *name)
+{
+ free(name);
+}
- /* getting rid of leading "../.." */
- while (*d == '/' || *d == '.')
- d++;
- d--;
+/**
+ * sysfs_close_list: generic list free routine
+ * @list: dlist to free
+ * Returns nothing
+ */
+void sysfs_close_list(struct dlist *list)
+{
+ if (list != NULL)
+ dlist_destroy(list);
+}
+
+/**
+ * sysfs_open_subsystem_list: gets a list of all supported "name" subsystem
+ * details from the system
+ * @name: name of the subsystem, eg., "bus", "class", "devices"
+ * Returns a dlist of supported names or NULL if subsystem not supported
+ */
+struct dlist *sysfs_open_subsystem_list(char *name)
+{
+ char sysfs_path[SYSFS_PATH_MAX], *subsys_name = NULL;
+ char *c = NULL;
+ struct sysfs_directory *dir = NULL, *cur = NULL;
+ struct dlist *list = NULL;
- strcat(devdir, d);
- strncpy(target, devdir, len);
+ if (name == NULL)
+ return NULL;
- return 0;
+ if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
+ dprintf("Error getting sysfs mount point\n");
+ return NULL;
+ }
+
+ safestrcat(sysfs_path, "/");
+ safestrcat(sysfs_path, name);
+ dir = sysfs_open_directory(sysfs_path);
+ if (dir == NULL) {
+ dprintf("Error opening sysfs_directory at %s\n", sysfs_path);
+ return NULL;
+ }
+
+ if ((sysfs_read_dir_subdirs(dir)) != 0) {
+ dprintf("Error reading sysfs_directory at %s\n", sysfs_path);
+ sysfs_close_directory(dir);
+ return NULL;
+ }
+
+ if (dir->subdirs != NULL) {
+ list = dlist_new_with_delete(SYSFS_NAME_LEN,
+ sysfs_del_name);
+ if (list == NULL) {
+ dprintf("Error creating list\n");
+ sysfs_close_directory(dir);
+ return NULL;
+ }
+
+ dlist_for_each_data(dir->subdirs, cur,
+ struct sysfs_directory) {
+ subsys_name = (char *)calloc(1, SYSFS_NAME_LEN);
+ safestrcpymax(subsys_name, cur->name, SYSFS_NAME_LEN);
+ dlist_unshift_sorted(list, subsys_name, sort_char);
+ }
+ }
+ sysfs_close_directory(dir);
+ /*
+ * We are now considering "block" as a "class". Hence, if the subsys
+ * name requested here is "class", verify if "block" is supported on
+ * this system and return the same.
+ */
+ if (strcmp(name, SYSFS_CLASS_NAME) == 0) {
+ c = strstr(sysfs_path, SYSFS_CLASS_NAME);
+ if (c == NULL)
+ goto out;
+ *c = '\0';
+ safestrcpymax(c, SYSFS_BLOCK_NAME,
+ sizeof(sysfs_path) - strlen(sysfs_path));
+ if ((sysfs_path_is_dir(sysfs_path)) == 0) {
+ subsys_name = (char *)calloc(1, SYSFS_NAME_LEN);
+ safestrcpymax(subsys_name, SYSFS_BLOCK_NAME,
+ SYSFS_NAME_LEN);
+ dlist_unshift_sorted(list, subsys_name, sort_char);
+ }
+ }
+out:
+ return list;
+}
+
+
+/**
+ * sysfs_open_bus_devices_list: gets a list of all devices on "name" bus
+ * @name: name of the subsystem, eg., "pci", "scsi", "usb"
+ * Returns a dlist of supported names or NULL if subsystem not supported
+ */
+struct dlist *sysfs_open_bus_devices_list(char *name)
+{
+ char sysfs_path[SYSFS_PATH_MAX], *device_name = NULL;
+ struct sysfs_directory *dir = NULL;
+ struct sysfs_link *cur = NULL;
+ struct dlist *list = NULL;
+
+ if (name == NULL)
+ return NULL;
+
+ if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
+ dprintf("Error getting sysfs mount point\n");
+ return NULL;
+ }
+
+ safestrcat(sysfs_path, "/");
+ safestrcat(sysfs_path, SYSFS_BUS_NAME);
+ safestrcat(sysfs_path, "/");
+ safestrcat(sysfs_path, name);
+ safestrcat(sysfs_path, "/");
+ safestrcat(sysfs_path, SYSFS_DEVICES_NAME);
+ dir = sysfs_open_directory(sysfs_path);
+ if (dir == NULL) {
+ dprintf("Error opening sysfs_directory at %s\n", sysfs_path);
+ return NULL;
+ }
+
+ if ((sysfs_read_dir_links(dir)) != 0) {
+ dprintf("Error reading sysfs_directory at %s\n", sysfs_path);
+ sysfs_close_directory(dir);
+ return NULL;
+ }
+
+ if (dir->links != NULL) {
+ list = dlist_new_with_delete(SYSFS_NAME_LEN,
+ sysfs_del_name);
+ if (list == NULL) {
+ dprintf("Error creating list\n");
+ sysfs_close_directory(dir);
+ return NULL;
+ }
+
+ dlist_for_each_data(dir->links, cur,
+ struct sysfs_link) {
+ device_name = (char *)calloc(1, SYSFS_NAME_LEN);
+ safestrcpymax(device_name, cur->name, SYSFS_NAME_LEN);
+ dlist_unshift_sorted(list, device_name, sort_char);
+ }
+ }
+ sysfs_close_directory(dir);
+ return list;
+}
+
+/**
+ * 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 == NULL) {
+ 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 == NULL) {
+ 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 == NULL) {
+ 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;
}