diff options
author | stekloff <stekloff> | 2005-04-06 20:57:01 +0000 |
---|---|---|
committer | stekloff <stekloff> | 2005-04-06 20:57:01 +0000 |
commit | dd8c511d725f816bc64592028ff178616fcde607 (patch) | |
tree | 55250e02ac67ceeb3c2bac3b1e29a78c22be1ac2 /lib | |
parent | 855a337e54321d9aa2b97c38ffa4787d3105257c (diff) | |
download | sysfsutils-dd8c511d725f816bc64592028ff178616fcde607.tar.gz |
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 44 | ||||
-rw-r--r-- | lib/Makefile.am | 6 | ||||
-rw-r--r-- | lib/Makefile.in | 496 | ||||
-rw-r--r-- | lib/dlist.c | 620 | ||||
-rw-r--r-- | lib/sysfs.h | 5 | ||||
-rw-r--r-- | lib/sysfs_bus.c | 467 | ||||
-rw-r--r-- | lib/sysfs_class.c | 686 | ||||
-rw-r--r-- | lib/sysfs_device.c | 674 | ||||
-rw-r--r-- | lib/sysfs_dir.c | 932 | ||||
-rw-r--r-- | lib/sysfs_driver.c | 379 | ||||
-rw-r--r-- | lib/sysfs_utils.c | 393 |
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; } |