aboutsummaryrefslogtreecommitdiff
path: root/libsysfs
diff options
context:
space:
mode:
authorAlan Brault <alan.brault@incruentatus.net>2010-12-07 18:46:06 -0500
committerAlan Brault <alan.brault@incruentatus.net>2010-12-07 18:46:06 -0500
commit78d2728b5b5a50cb72db8d14b46cf86284aea03b (patch)
treeb7993b2b5ecc83a7292b46ca8272f69221e4cc77 /libsysfs
parenta70b36f98f42924c36d6b14cd10fb874d6a8c779 (diff)
downloadNetNuke2-78d2728b5b5a50cb72db8d14b46cf86284aea03b.tar.gz
Fully integrate libsysfs into NetNuke (it compiles and links correctly)
Diffstat (limited to 'libsysfs')
-rw-r--r--libsysfs/LGPL441
-rw-r--r--libsysfs/Makefile.am7
-rw-r--r--libsysfs/Makefile.in587
-rw-r--r--libsysfs/dlist.c657
-rw-r--r--libsysfs/sysfs.h64
-rw-r--r--libsysfs/sysfs_attr.c632
-rw-r--r--libsysfs/sysfs_bus.c317
-rw-r--r--libsysfs/sysfs_class.c551
-rw-r--r--libsysfs/sysfs_device.c424
-rw-r--r--libsysfs/sysfs_dir.c1071
-rw-r--r--libsysfs/sysfs_driver.c289
-rw-r--r--libsysfs/sysfs_module.c279
-rw-r--r--libsysfs/sysfs_utils.c307
13 files changed, 5626 insertions, 0 deletions
diff --git a/libsysfs/LGPL b/libsysfs/LGPL
new file mode 100644
index 0000000..e00a829
--- /dev/null
+++ b/libsysfs/LGPL
@@ -0,0 +1,441 @@
+
+ GNU Lesser Public License
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ [This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your freedom to
+ share and change it. By contrast, the GNU General Public Licenses are
+ intended to guarantee your freedom to share and change free software--to
+ make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some specially
+ designated software packages--typically libraries--of the Free Software
+ Foundation and other authors who decide to use it. You can use it too, but
+ we suggest you first think carefully about whether this license or the
+ ordinary General Public License is the better strategy to use in any
+ particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use, not
+ price. Our General Public Licenses are designed to make sure that you have
+ the freedom to distribute copies of free software (and charge for this
+ service if you wish); that you receive source code or can get it if you
+ want it; that you can change the software and use pieces of it in new free
+ programs; and that you are informed that you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+ distributors to deny you these rights or to ask you to surrender these
+ rights. These restrictions translate to certain responsibilities for you
+ if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis or
+ for a fee, you must give the recipients all the rights that we gave you.
+ You must make sure that they, too, receive or can get the source code. If
+ you link other code with the library, you must provide complete object
+ files to the recipients, so that they can relink them with the library
+ after making changes to the library and recompiling it. And you must show
+ them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+ library, and (2) we offer you this license, which gives you legal
+ permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that there is
+ no warranty for the free library. Also, if the library is modified by
+ someone else and passed on, the recipients should know that what they have
+ is not the original version, so that the original author's reputation will
+ not be affected by problems that might be introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of any
+ free program. We wish to make sure that a company cannot effectively
+ restrict the users of a free program by obtaining a restrictive license
+ from a patent holder. Therefore, we insist that any patent license
+ obtained for a version of the library must be consistent with the full
+ freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+ GNU General Public License. This license, the GNU Lesser General Public
+ License, applies to certain designated libraries, and is quite different
+ from the ordinary General Public License. We use this license for certain
+ libraries in order to permit linking those libraries into non-free
+ programs.
+
+ When a program is linked with a library, whether statically or using a
+ shared library, the combination of the two is legally speaking a combined
+ work, a derivative of the original library. The ordinary General Public
+ License therefore permits such linking only if the entire combination fits
+ its criteria of freedom. The Lesser General Public License permits more
+ lax criteria for linking other code with the library.
+
+ We call this license the "Lesser" General Public License because it does
+ Less to protect the user's freedom than the ordinary General Public
+ License. It also provides other free software developers Less of an
+ advantage over competing non-free programs. These disadvantages are the
+ reason we use the ordinary General Public License for many libraries.
+ However, the Lesser license provides advantages in certain special
+ circumstances.
+
+ For example, on rare occasions, there may be a special need to encourage
+ the widest possible use of a certain library, so that it becomes a
+ de-facto standard. To achieve this, non-free programs must be allowed to
+ use the library. A more frequent case is that a free library does the same
+ job as widely used non-free libraries. In this case, there is little to
+ gain by limiting the free library to free software only, so we use the
+ Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+ programs enables a greater number of people to use a large body of free
+ software. For example, permission to use the GNU C Library in non-free
+ programs enables many more people to use the whole GNU operating system,
+ as well as its variant, the GNU/Linux operating system.
+
+ Although the Lesser General Public License is Less protective of the
+ users' freedom, it does ensure that the user of a program that is linked
+ with the Library has the freedom and the wherewithal to run that program
+ using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+ modification follow. Pay close attention to the difference between a "work
+ based on the library" and a "work that uses the library". The former
+ contains code derived from the library, whereas the latter must be
+ combined with the library in order to run.
+
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other program
+ which contains a notice placed by the copyright holder or other authorized
+ party saying it may be distributed under the terms of this Lesser General
+ Public License (also called "this License"). Each licensee is addressed as
+ "you".
+
+ A "library" means a collection of software functions and/or data prepared
+ so as to be conveniently linked with application programs (which use some
+ of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work which
+ has been distributed under these terms. A "work based on the Library"
+ means either the Library or any derivative work under copyright law: that
+ is to say, a work containing the Library or a portion of it, either
+ verbatim or with modifications and/or translated straightforwardly into
+ another language. (Hereinafter, translation is included without limitation
+ in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for making
+ modifications to it. For a library, complete source code means all the
+ source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the library.
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of running a
+ program using the Library is not restricted, and output from such a
+ program is covered only if its contents constitute a work based on the
+ Library (independent of the use of the Library in a tool for writing it).
+ Whether that is true depends on what the Library does and what the program
+ that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's complete
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an appropriate
+ copyright notice and disclaimer of warranty; keep intact all the notices
+ that refer to this License and to the absence of any warranty; and
+ distribute a copy of this License along with the Library.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Library or any portion of it,
+ thus forming a work based on the Library, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that
+ you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no charge to
+ all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a table
+ of data to be supplied by an application program that uses the facility,
+ other than as an argument passed when the facility is invoked, then you
+ must make a good faith effort to ensure that, in the event an
+ application does not supply such function or table, the facility still
+ operates, and performs whatever part of its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has a
+ purpose that is entirely well-defined independent of the application.
+ Therefore, Subsection 2d requires that any application-supplied function
+ or table used by this function must be optional: if the application does
+ not supply it, the square root function must still compute square
+ roots.)
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the Library, and
+ can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works. But when you
+ distribute the same sections as part of a whole which is a work based on
+ the Library, the distribution of the whole must be on the terms of this
+ License, whose permissions for other licensees extend to the entire
+ whole, and thus to each and every part regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Library.
+
+ In addition, mere aggregation of another work not based on the Library
+ with the Library (or with a work based on the Library) on a volume of a
+ storage or distribution medium does not bring the other work under the
+ scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+ License instead of this License to a given copy of the Library. To do
+ this, you must alter all the notices that refer to this License, so that
+ they refer to the ordinary GNU General Public License, version 2, instead
+ of to this License. (If a newer version than version 2 of the ordinary GNU
+ General Public License has appeared, then you can specify that version
+ instead if you wish.) Do not make any other change in these notices.
+
+ Once this change is made in a given copy, it is irreversible for that
+ copy, so the ordinary GNU General Public License applies to all subsequent
+ copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of the
+ Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or derivative of
+ it, under Section 2) in object code or executable form under the terms of
+ Sections 1 and 2 above provided that you accompany it with the complete
+ corresponding machine-readable source code, which must be distributed
+ under the terms of Sections 1 and 2 above on a medium customarily used for
+ software interchange.
+
+ If distribution of object code is made by offering access to copy from a
+ designated place, then offering equivalent access to copy the source code
+ from the same place satisfies the requirement to distribute the source
+ code, even though third parties are not compelled to copy the source along
+ with the object code.
+
+ 5. A program that contains no derivative of any portion of the Library,
+ but is designed to work with the Library by being compiled or linked with
+ it, is called a "work that uses the Library". Such a work, in isolation,
+ is not a derivative work of the Library, and therefore falls outside the
+ scope of this License.
+
+ However, linking a "work that uses the Library" with the Library creates
+ an executable that is a derivative of the Library (because it contains
+ portions of the Library), rather than a "work that uses the library". The
+ executable is therefore covered by this License. Section 6 states terms
+ for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file that
+ is part of the Library, the object code for the work may be a derivative
+ work of the Library even though the source code is not. Whether this is
+ true is especially significant if the work can be linked without the
+ Library, or if the work is itself a library. The threshold for this to be
+ true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data structure
+ layouts and accessors, and small macros and small inline functions (ten
+ lines or less in length), then the use of the object file is unrestricted,
+ regardless of whether it is legally a derivative work. (Executables
+ containing this object code plus portions of the Library will still fall
+ under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may distribute
+ the object code for the work under the terms of Section 6. Any executables
+ containing that work also fall under Section 6, whether or not they are
+ linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or link a
+ "work that uses the Library" with the Library to produce a work containing
+ portions of the Library, and distribute that work under terms of your
+ choice, provided that the terms permit modification of the work for the
+ customer's own use and reverse engineering for debugging such
+ modifications.
+
+ You must give prominent notice with each copy of the work that the Library
+ is used in it and that the Library and its use are covered by this
+ License. You must supply a copy of this License. If the work during
+ execution displays copyright notices, you must include the copyright
+ notice for the Library among them, as well as a reference directing the
+ user to the copy of this License. Also, you must do one of these things:
+
+ a) Accompany the work with the complete corresponding machine-readable
+ source code for the Library including whatever changes were used in the
+ work (which must be distributed under Sections 1 and 2 above); and, if
+ the work is an executable linked with the Library, with the complete
+ machine-readable "work that uses the Library", as object code and/or
+ source code, so that the user can modify the Library and then relink to
+ produce a modified executable containing the modified Library. (It is
+ understood that the user who changes the contents of definitions files
+ in the Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the Library.
+ A suitable mechanism is one that (1) uses at run time a copy of the
+ library already present on the user's computer system, rather than
+ copying library functions into the executable, and (2) will operate
+ properly with a modified version of the library, if the user installs
+ one, as long as the modified version is interface-compatible with the
+ version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least three
+ years, to give the same user the materials specified in Subsection 6a,
+ above, for a charge no more than the cost of performing this
+ distribution.
+
+ d) If distribution of the work is made by offering access to copy from a
+ designated place, offer equivalent access to copy the above specified
+ materials from the same place.
+
+ e) Verify that the user has already received a copy of these materials
+ or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the Library"
+ must include any data and utility programs needed for reproducing the
+ executable from it. However, as a special exception, the materials to be
+ distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler, kernel,
+ and so on) of the operating system on which the executable runs, unless
+ that component itself accompanies the executable.
+
+ It may happen that this requirement contradicts the license restrictions
+ of other proprietary libraries that do not normally accompany the
+ operating system. Such a contradiction means you cannot use both them and
+ the Library together in an executable that you distribute.
+
+ 7. You may place library facilities that are a work based on the Library
+ side-by-side in a single library together with other library facilities
+ not covered by this License, and distribute such a combined library,
+ provided that the separate distribution of the work based on the Library
+ and of the other library facilities is otherwise permitted, and provided
+ that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work based on
+ the Library, uncombined with any other library facilities. This must be
+ distributed under the terms of the Sections above.
+
+ b) Give prominent notice with the combined library of the fact that part
+ of it is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute the
+ Library except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense, link with, or distribute the
+ Library is void, and will automatically terminate your rights under this
+ License. However, parties who have received copies, or rights, from you
+ under this License will not have their licenses terminated so long as such
+ parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute
+ the Library or its derivative works. These actions are prohibited by law
+ if you do not accept this License. Therefore, by modifying or distributing
+ the Library (or any work based on the Library), you indicate your
+ acceptance of this License to do so, and all its terms and conditions for
+ copying, distributing or modifying the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+ Library), the recipient automatically receives a license from the original
+ licensor to copy, distribute, link with or modify the Library subject to
+ these terms and conditions. You may not impose any further restrictions on
+ the recipients' exercise of the rights granted herein. You are not
+ responsible for enforcing compliance by third parties with this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute
+ so as to satisfy simultaneously your obligations under this License and
+ any other pertinent obligations, then as a consequence you may not
+ distribute the Library at all. For example, if a patent license would not
+ permit royalty-free redistribution of the Library by all those who receive
+ copies directly or indirectly through you, then the only way you could
+ satisfy both it and this License would be to refrain entirely from
+ distribution of the Library.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply,
+ and the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any such
+ claims; this section has the sole purpose of protecting the integrity of
+ the free software distribution system which is implemented by public
+ license practices. Many people have made generous contributions to the
+ wide range of software distributed through that system in reliance on
+ consistent application of that system; it is up to the author/donor to
+ decide if he or she is willing to distribute software through any other
+ system and a licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be a
+ consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Library under this License may add an
+ explicit geographical distribution limitation excluding those countries,
+ so that distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the limitation as if
+ written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new versions
+ of the Lesser General Public License from time to time. Such new versions
+ will be similar in spirit to the present version, but may differ in detail
+ to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Library
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Library does not specify a license version
+ number, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+ programs whose distribution conditions are incompatible with these, write
+ to the author to ask for permission. For software which is copyrighted by
+ the Free Software Foundation, write to the Free Software Foundation; we
+ sometimes make exceptions for this. Our decision will be guided by the two
+ goals of preserving the free status of all derivatives of our free
+ software and of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+ TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE
+ LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+ REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT
+ LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES
+ SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE
+ WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
diff --git a/libsysfs/Makefile.am b/libsysfs/Makefile.am
new file mode 100644
index 0000000..8d37bcc
--- /dev/null
+++ b/libsysfs/Makefile.am
@@ -0,0 +1,7 @@
+lib_LTLIBRARIES = libsysfs.la
+libsysfs_la_SOURCES = sysfs_utils.c sysfs_attr.c sysfs_class.c dlist.c \
+ sysfs_device.c sysfs_driver.c sysfs_bus.c sysfs_module.c sysfs.h
+INCLUDES = -I../include
+libsysfs_la_LDFLAGS = -version-info 2:1:0
+EXTRA_CFLAGS = @EXTRA_CLFAGS@
+libsysfs_la_CFLAGS = -Wall -W -Wstrict-prototypes $(EXTRA_CLFAGS)
diff --git a/libsysfs/Makefile.in b/libsysfs/Makefile.in
new file mode 100644
index 0000000..840aeea
--- /dev/null
+++ b/libsysfs/Makefile.in
@@ -0,0 +1,587 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libsysfs
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libsysfs_la_LIBADD =
+am_libsysfs_la_OBJECTS = libsysfs_la-sysfs_utils.lo \
+ libsysfs_la-sysfs_attr.lo libsysfs_la-sysfs_class.lo \
+ libsysfs_la-dlist.lo libsysfs_la-sysfs_device.lo \
+ libsysfs_la-sysfs_driver.lo libsysfs_la-sysfs_bus.lo \
+ libsysfs_la-sysfs_module.lo
+libsysfs_la_OBJECTS = $(am_libsysfs_la_OBJECTS)
+libsysfs_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libsysfs_la_CFLAGS) \
+ $(CFLAGS) $(libsysfs_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libsysfs_la_SOURCES)
+DIST_SOURCES = $(libsysfs_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LTLIBRARIES = libsysfs.la
+libsysfs_la_SOURCES = sysfs_utils.c sysfs_attr.c sysfs_class.c dlist.c \
+ sysfs_device.c sysfs_driver.c sysfs_bus.c sysfs_module.c sysfs.h
+
+INCLUDES = -I../include
+libsysfs_la_LDFLAGS = -version-info 2:1:0
+EXTRA_CFLAGS = @EXTRA_CLFAGS@
+libsysfs_la_CFLAGS = -Wall -W -Wstrict-prototypes $(EXTRA_CLFAGS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libsysfs/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libsysfs/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ 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 "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libsysfs.la: $(libsysfs_la_OBJECTS) $(libsysfs_la_DEPENDENCIES)
+ $(libsysfs_la_LINK) -rpath $(libdir) $(libsysfs_la_OBJECTS) $(libsysfs_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+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_attr.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_driver.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_module.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsysfs_la-sysfs_utils.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libsysfs_la-sysfs_utils.lo: sysfs_utils.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_utils.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_utils.Tpo -c -o libsysfs_la-sysfs_utils.lo `test -f 'sysfs_utils.c' || echo '$(srcdir)/'`sysfs_utils.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_utils.Tpo $(DEPDIR)/libsysfs_la-sysfs_utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_utils.c' object='libsysfs_la-sysfs_utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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_attr.lo: sysfs_attr.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_attr.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_attr.Tpo -c -o libsysfs_la-sysfs_attr.lo `test -f 'sysfs_attr.c' || echo '$(srcdir)/'`sysfs_attr.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_attr.Tpo $(DEPDIR)/libsysfs_la-sysfs_attr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_attr.c' object='libsysfs_la-sysfs_attr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_attr.lo `test -f 'sysfs_attr.c' || echo '$(srcdir)/'`sysfs_attr.c
+
+libsysfs_la-sysfs_class.lo: sysfs_class.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_class.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_class.Tpo -c -o libsysfs_la-sysfs_class.lo `test -f 'sysfs_class.c' || echo '$(srcdir)/'`sysfs_class.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_class.Tpo $(DEPDIR)/libsysfs_la-sysfs_class.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_class.c' object='libsysfs_la-sysfs_class.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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-dlist.lo: dlist.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-dlist.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-dlist.Tpo -c -o libsysfs_la-dlist.lo `test -f 'dlist.c' || echo '$(srcdir)/'`dlist.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-dlist.Tpo $(DEPDIR)/libsysfs_la-dlist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dlist.c' object='libsysfs_la-dlist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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
+
+libsysfs_la-sysfs_device.lo: sysfs_device.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_device.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_device.Tpo -c -o libsysfs_la-sysfs_device.lo `test -f 'sysfs_device.c' || echo '$(srcdir)/'`sysfs_device.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_device.Tpo $(DEPDIR)/libsysfs_la-sysfs_device.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_device.c' object='libsysfs_la-sysfs_device.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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.lo: sysfs_driver.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_driver.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_driver.Tpo -c -o libsysfs_la-sysfs_driver.lo `test -f 'sysfs_driver.c' || echo '$(srcdir)/'`sysfs_driver.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_driver.Tpo $(DEPDIR)/libsysfs_la-sysfs_driver.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_driver.c' object='libsysfs_la-sysfs_driver.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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-sysfs_bus.lo: sysfs_bus.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_bus.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_bus.Tpo -c -o libsysfs_la-sysfs_bus.lo `test -f 'sysfs_bus.c' || echo '$(srcdir)/'`sysfs_bus.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_bus.Tpo $(DEPDIR)/libsysfs_la-sysfs_bus.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_bus.c' object='libsysfs_la-sysfs_bus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --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_module.lo: sysfs_module.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -MT libsysfs_la-sysfs_module.lo -MD -MP -MF $(DEPDIR)/libsysfs_la-sysfs_module.Tpo -c -o libsysfs_la-sysfs_module.lo `test -f 'sysfs_module.c' || echo '$(srcdir)/'`sysfs_module.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libsysfs_la-sysfs_module.Tpo $(DEPDIR)/libsysfs_la-sysfs_module.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysfs_module.c' object='libsysfs_la-sysfs_module.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsysfs_la_CFLAGS) $(CFLAGS) -c -o libsysfs_la-sysfs_module.lo `test -f 'sysfs_module.c' || echo '$(srcdir)/'`sysfs_module.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+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; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ 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; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ 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; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$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:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_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
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-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/libsysfs/dlist.c b/libsysfs/dlist.c
new file mode 100644
index 0000000..a0c771a
--- /dev/null
+++ b/libsysfs/dlist.c
@@ -0,0 +1,657 @@
+/*
+ * dlist.c
+ *
+ * Copyright (C) 2003 Eric J Bohm
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021110307 USA
+ *
+ */
+
+
+/* Double linked list implementation.
+
+ * You allocate the data and give dlist the pointer.
+ * If your data is complex set the dlist->del_func to a an appropriate
+ * delete function. Otherwise dlist will just use free.
+
+*/
+#include <stdlib.h>
+#include "dlist.h"
+
+/*
+ * Return pointer to node at marker.
+ * else null if no nodes.
+ */
+
+inline void *dlist_mark(Dlist *list)
+{
+ if(list->marker!=NULL)
+ return(list->marker->data);
+ else
+ return(NULL);
+}
+
+/*
+ * Set marker to start.
+ */
+
+inline void dlist_start(Dlist *list)
+{
+ list->marker=list->head;
+}
+
+/*
+ * Set marker to end.
+ */
+
+inline void dlist_end(Dlist *list)
+{
+ list->marker=list->head;
+}
+
+/* internal use function
+ * quickie inline to consolidate the marker movement logic
+ * in one place
+ *
+ * when direction true it moves marker after
+ * when direction false it moves marker before.
+ * return pointer to data at new marker
+ * if nowhere to move the marker in desired direction return null
+ */
+inline void *_dlist_mark_move(Dlist *list,int direction)
+{
+ if(direction)
+ {
+ if( list->marker && list->marker->next!=NULL)
+ list->marker=list->marker->next;
+ else
+ return(NULL);
+ }
+ else
+ {
+ if( list->marker && list->marker->prev!=NULL)
+ list->marker=list->marker->prev;
+ else
+ return(NULL);
+ }
+ if(list->marker!=list->head)
+ return(list->marker->data);
+ else
+ return(NULL);
+}
+
+/*
+ * Create new linked list to store nodes of datasize.
+ * return null if list cannot be created.
+ */
+Dlist *dlist_new(size_t datasize)
+{
+ Dlist *list=NULL;
+ if((list=malloc(sizeof(Dlist))))
+ {
+ list->marker=NULL;
+ list->count=0L;
+ list->data_size=datasize;
+ list->del_func=free;
+ list->head=&(list->headnode);
+ list->head->prev=NULL;
+ list->head->next=NULL;
+ list->head->data=NULL;
+ }
+ return(list);
+}
+
+/*
+ * Create new linked list to store nodes of datasize set list
+ * data node delete function to the passed in del_func
+ * return null if list cannot be created.
+ */
+Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*))
+{
+ Dlist *list=NULL;
+ list=dlist_new(datasize);
+ if(list!=NULL)
+ list->del_func=del_func;
+ return(list);
+}
+
+
+/*
+ * remove marker node from list
+ * call data_delete function on data if registered.
+ * otherwise call free.
+ * when direction true it moves marker after
+ * when direction false it moves marker before.
+ * free marker node
+ * return nothing.
+ */
+void dlist_delete(Dlist *list,int direction)
+{
+ if((list->marker != list->head)&&(list->marker!=NULL))
+ {
+ DL_node *corpse;
+ corpse=list->marker;
+ _dlist_mark_move(list,direction);
+ if(list->head->next==corpse)
+ list->head->next=corpse->next;
+ if(list->head->prev==corpse)
+ list->head->prev=corpse->prev;
+ if(corpse->prev!=NULL) //should be impossible
+ corpse->prev->next=corpse->next;
+ if(corpse->next!=NULL) //should be impossible
+ corpse->next->prev=corpse->prev;
+ list->del_func(corpse->data);
+ list->count--;
+ free(corpse);
+ }
+}
+
+/*
+ * Insert node containing data at marker.
+ * If direction true it inserts after.
+ * If direction false it inserts before.
+ * move marker to inserted node
+ * return pointer to inserted node
+ */
+void *dlist_insert(Dlist *list,void *data,int direction)
+{
+ DL_node *new_node=NULL;
+ if(list==NULL || data==NULL)
+ return(NULL);
+ if(list->marker==NULL) //in case the marker ends up unset
+ list->marker=list->head;
+ if((new_node=malloc(sizeof(DL_node))))
+ {
+ new_node->data=data;
+ new_node->prev=NULL;
+ new_node->next=NULL;
+ list->count++;
+ if(list->head->next==NULL) //no l
+ {
+ list->head->next=list->head->prev=new_node;
+ new_node->prev=list->head;
+ new_node->next=list->head;
+ }
+ else if(direction)
+ {
+ new_node->next=list->marker->next;
+ new_node->prev=list->marker;
+ list->marker->next->prev=new_node;
+ list->marker->next=new_node;
+ }
+ else
+ {
+ new_node->prev=list->marker->prev;
+ new_node->next=list->marker;
+ list->marker->prev->next=new_node;
+ list->marker->prev=new_node;
+ }
+ list->marker=new_node;
+ }
+ else
+ {
+ return(NULL);
+ }
+ return(list->marker->data);
+}
+
+/* internal use only
+ * Insert dl_node at marker.
+ * If direction true it inserts after.
+ * If direction false it inserts before.
+ * move marker to inserted node
+ * return pointer to inserted node
+ */
+void *_dlist_insert_dlnode(struct dlist *list,struct dl_node *new_node,int direction)
+{
+ if(list==NULL || new_node==NULL)
+ return(NULL);
+ if(list->marker==NULL) //in case the marker ends up unset
+ list->marker=list->head;
+ list->count++;
+ if(list->head->next==NULL)
+ {
+ list->head->next=list->head->prev=new_node;
+ new_node->prev=list->head;
+ new_node->next=list->head;
+ }
+ else if(direction)
+ {
+ new_node->next=list->marker->next;
+ new_node->prev=list->marker;
+ list->marker->next->prev=new_node;
+ list->marker->next=new_node;
+ }
+ else
+ {
+ new_node->prev=list->marker->prev;
+ new_node->next=list->marker;
+ list->marker->prev->next=new_node;
+ list->marker->prev=new_node;
+ }
+ list->marker=new_node;
+ return(list->marker);
+}
+
+
+
+/*
+ * Remove DL_node from list without deallocating data.
+ * if marker == killme .
+ * when direction true it moves marker after
+ * when direction false it moves marker before.
+ * to previous if there is no next.
+ */
+void *_dlist_remove(Dlist *list,DL_node *killme,int direction)
+{
+ if(killme!=NULL)
+ {
+ void *killer_data=killme->data;
+ // take care of head and marker pointers.
+ if(list->marker==killme)
+ _dlist_mark_move(list,direction);
+ if(killme ==list->head->next)
+ list->head->next=killme->next;
+ if(killme==list->head->prev)
+ list->head->prev=killme->prev;
+ // remove from list
+ if(killme->prev !=NULL)
+ killme->prev->next=killme->next;
+ if(killme->next !=NULL)
+ killme->next->prev=killme->prev;
+ list->count--;
+ free(killme);
+ return(killer_data);
+ }
+ else
+ return (NULL);
+}
+
+/*
+ * move dl_node from source to dest
+ * if marker == target .
+ * when direction true it moves marker after
+ * when direction false it moves marker before.
+ * to previous if there is no next.
+ */
+void dlist_move(struct dlist *source, struct dlist *dest, struct dl_node *target,int direction)
+{
+
+ if(target!=NULL)
+ {
+ if(target==source->head)
+ {
+ //not even going to try
+ }
+ else
+ {
+ // take care of head and marker pointers.
+ if(source->marker==target)
+ _dlist_mark_move(source,direction);
+ if(target ==source->head->next)
+ source->head->next=target->next;
+ if(target==source->head->prev)
+ source->head->prev=target->prev;
+ // remove from list
+ if(source->count==1)
+ {
+ target->prev=NULL;
+ target->next=NULL;
+ source->head->next=NULL;
+ source->head->prev=NULL;
+ }
+ else
+ {
+ if(target->prev !=NULL)
+ target->prev->next=target->next;
+ if(target->next !=NULL)
+ target->next->prev=target->prev;
+ target->prev=NULL;
+ target->next=NULL;
+ }
+ source->count--;
+ _dlist_insert_dlnode(dest,target,direction);
+ }
+ }
+}
+
+
+/*
+ * Insert node containing data after end.
+ */
+void dlist_push(Dlist *list,void *data)
+{
+ list->marker=list->head->prev;
+ dlist_insert(list,data,1);
+}
+
+/*
+ * Insert node containing data at start.
+ */
+
+void dlist_unshift(Dlist *list,void *data)
+
+{
+ list->marker=list->head->next;
+ dlist_insert(list,data,0);
+}
+
+void dlist_unshift_sorted(Dlist *list, void *data,
+ int (*sorter)(void *new_elem, void *old_elem))
+{
+ if (list->count == 0)
+ dlist_unshift(list, data);
+ else {
+ list->marker=list->head->next;
+ dlist_insert_sorted(list, data, sorter);
+ }
+}
+
+/*
+ * Remove end node from list.
+ * Return pointer to data in removed node.
+ * Null if no nodes.
+ */
+
+void *dlist_pop(Dlist *list)
+{
+ return(_dlist_remove(list,list->head->prev,0));
+}
+
+/*
+ * Remove start node from list.
+ * Return pointer to data in removed node.
+ * Null if no nodes.
+ */
+
+void *dlist_shift(Dlist *list)
+{
+ return(_dlist_remove(list,list->head->next,1));
+}
+
+
+/*
+ * destroy the list freeing all memory
+ */
+
+
+void dlist_destroy(Dlist *list)
+{
+ if(list !=NULL)
+ {
+ dlist_start(list);
+ dlist_next(list);
+ while (dlist_mark(list)) {
+ dlist_delete(list,1);
+ }
+ free(list);
+ }
+}
+
+/**
+ * Return void pointer to list_data element matching comp function criteria
+ * else null
+ * Does not move the marker.
+ */
+
+void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *))
+{
+ /* test the comp function on each node */
+ struct dl_node *nodepointer;
+ dlist_for_each_nomark(list,nodepointer)
+ if(comp(target,nodepointer->data))
+ return(nodepointer->data);
+ return(NULL);
+}
+
+/**
+ * Apply the node_operation function to each data node in the list
+ */
+void dlist_transform(struct dlist *list, void (*node_operation)(void *))
+{
+ struct dl_node *nodepointer;
+ dlist_for_each_nomark(list,nodepointer)
+ node_operation(nodepointer->data);
+}
+
+/**
+ * insert new into list in sorted order
+ * sorter function in form int sorter(new,ith)
+ * must return 1 for when new should go before ith
+ * else 0
+ * return pointer to inserted node
+ * NOTE: assumes list is already sorted
+ */
+void *dlist_insert_sorted(struct dlist *list, void *new, int (*sorter)(void *, void *))
+{
+ for(dlist_start(list),dlist_next(list); \
+ list->marker!=list->head && !sorter(new,list->marker->data);dlist_next(list));
+ return(dlist_insert_before(list,new));
+}
+
+/*
+ * NOTE: internal use only
+ */
+int _dlist_merge(struct dlist *listsource, struct dlist *listdest, unsigned int passcount, int (*compare)(void *, void *))
+{
+
+ struct dl_node *l1head;
+ struct dl_node *l2head;
+ struct dl_node *target;
+ unsigned int l1count=0;
+ unsigned int l2count=0;
+ unsigned int mergecount=0;
+ while(listsource->count>0)
+ {
+ l1head=listsource->head->next;
+ l2head=l1head;
+ while((l1count<passcount)&&(l2head!=listsource->head))
+ {
+ l2head=l2head->next;
+ l1count++;
+ }
+ // so now we have two lists to merge
+
+ if(l2head==listsource->head)
+ {// l2count
+ l2count=0;
+ }
+ else
+ {
+ l2count=passcount;
+ }
+ while(l1count>0 || l2count>0)
+ {
+ mergecount++;
+ if((l2count>0)&&(l1count>0))
+ {
+ // we have things to merge
+ int result=compare(l1head->data,l2head->data);
+ if(result>0)
+ {
+ // move from l2
+ target=l2head;
+ l2head=l2head->next;
+ dlist_move(listsource,listdest,target,1);
+ l2count--;
+ if(l2head==listsource->head)
+ l2count=0;
+ }
+ else
+ {
+ // move from l1
+ target=l1head;
+ l1head=l1head->next;
+ dlist_move(listsource,listdest,target,1);
+ l1count--;
+ }
+ }
+ else if(l1count>0)
+ {
+ // only have l1 to work with
+ while(l1count>0)
+ {
+ target=l1head;
+ l1head=l1head->next;
+ dlist_move(listsource,listdest,target,1);
+ l1count--;
+ }
+ }
+ else if(l2count>0)
+ {
+ // only have l2 to work with
+ while(l2count>0)
+ {
+ if(l2head==listsource->head)
+ {
+ l2count=0;
+ }
+ else
+ {
+ target=l2head;
+ l2head=l2head->next;
+ dlist_move(listsource,listdest,target,1);
+ l2count--;
+ }
+ }
+ }
+ else
+ { //nothing left and this should be unreachable
+ }
+ }
+ }
+ return(mergecount);
+}
+
+/**
+ * mergesort the list based on compare
+ * compare function in form int sorter(void * a,void * b)
+ * must return >0 for a after b
+ * must return <0 for a before b
+ * else 0
+
+ * NOTE: mergesort changes the mark pointer
+ */
+void dlist_sort_custom(struct dlist *list, int (*compare)(void *, void *))
+{
+
+ struct dlist *listsource, *listdest, *swap;
+ struct dlist *templist;
+ unsigned int passcount = 1;
+ unsigned int mergecount = 1;
+
+ if(list->count<2)
+ return;
+
+ dlist_start(list);
+ templist = dlist_new(list->data_size);
+
+ templist->del_func = list->del_func;
+
+ // do nothing if there isn't anything to sort
+ listsource = list;
+ listdest = templist;
+ 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);
+}
+
+/*
+ * The dlist_filter_sort() function scans the list, calling
+ * filter() on each list entry. Entries for which filter()
+ * returns zero are discarded from the list. Then the list is
+ * sorted using mergesort() with the comparison function compare()
+ * and returned. If filter is NULL, all entries are selected.
+*/
+void dlist_filter_sort(struct dlist *list, int (*filter) (void *),
+ int (*compare) (void *, void *))
+{
+ struct dl_node *nodepointer,*temp;
+ void *data;
+
+ if(!list->count)
+ return;
+
+ /* if there is no filter function, directly sort all the entries */
+ if (!filter)
+ goto sort;
+
+ /* filter the unwanted entries in the list */
+ nodepointer = list->head->next;
+ while(nodepointer!=list->head) {
+ if (!filter(nodepointer->data)) {
+ temp = nodepointer->next;
+ data = _dlist_remove(list, nodepointer, 0);
+ if(data)
+ list->del_func(data);
+ nodepointer = temp;
+ }
+ else
+ nodepointer=nodepointer->next;
+ }
+
+sort:
+ /* sort out the entries */
+ dlist_sort_custom(list, compare);
+}
+
+
+/* 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/libsysfs/sysfs.h b/libsysfs/sysfs.h
new file mode 100644
index 0000000..76754a4
--- /dev/null
+++ b/libsysfs/sysfs.h
@@ -0,0 +1,64 @@
+/*
+ * sysfs.h
+ *
+ * Internal Header Definitions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _SYSFS_H_
+#define _SYSFS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define safestrcpy(to, from) strncpy(to, from, sizeof(to)-1)
+#define safestrcat(to, from) strncat(to, from, sizeof(to) - strlen(to)-1)
+
+#define safestrcpymax(to, from, max) \
+do { \
+ to[max-1] = '\0'; \
+ strncpy(to, from, max-1); \
+} while (0)
+
+#define safestrcatmax(to, from, max) \
+do { \
+ to[max-1] = '\0'; \
+ strncat(to, from, max - strlen(to)-1); \
+} while (0)
+
+extern struct sysfs_attribute *get_attribute(void *dev, const char *name);
+extern struct dlist *read_dir_subdirs(const char *path);
+extern struct dlist *read_dir_links(const char *path);
+extern struct dlist *get_dev_attributes_list(void *dev);
+extern struct dlist *get_attributes_list(struct dlist *alist, const char *path);
+
+/* Debugging */
+#ifdef DEBUG
+#define dprintf(format, arg...) fprintf(stderr, format, ## arg)
+#else
+#define dprintf(format, arg...) do { } while (0)
+#endif
+
+#endif /* _SYSFS_H_ */
diff --git a/libsysfs/sysfs_attr.c b/libsysfs/sysfs_attr.c
new file mode 100644
index 0000000..8819926
--- /dev/null
+++ b/libsysfs/sysfs_attr.c
@@ -0,0 +1,632 @@
+/*
+ * sysfs_dir.c
+ *
+ * Directory utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+static int sort_char(void *new, void *old)
+{
+ return ((strncmp((char *)new, (char *)old,
+ strlen((char *)new))) < 0 ? 1 : 0);
+}
+
+/**
+ * sysfs_del_name: free function for sysfs_open_subsystem_list
+ * @name: memory area to be freed
+ */
+static void sysfs_del_name(void *name)
+{
+ free(name);
+}
+
+/**
+ * sysfs_del_attribute: routine for dlist integration
+ */
+static void sysfs_del_attribute(void *attr)
+{
+ sysfs_close_attribute((struct sysfs_attribute *)attr);
+}
+
+/**
+ * attr_name_equal: compares attributes by name
+ * @a: attribute name for comparison
+ * @b: sysfs_attribute to be compared.
+ * returns 1 if a==b->name or 0 if not equal
+ */
+static int attr_name_equal(void *a, void *b)
+{
+ if (!a || !b)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_attribute *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * sysfs_close_attribute: closes and cleans up attribute
+ * @sysattr: attribute to close.
+ */
+void sysfs_close_attribute(struct sysfs_attribute *sysattr)
+{
+ if (sysattr) {
+ if (sysattr->value)
+ free(sysattr->value);
+ free(sysattr);
+ }
+}
+
+/**
+ * alloc_attribute: allocates and initializes attribute structure
+ * returns struct sysfs_attribute with success and NULL with error.
+ */
+static struct sysfs_attribute *alloc_attribute(void)
+{
+ return (struct sysfs_attribute *)
+ calloc(1, sizeof(struct sysfs_attribute));
+}
+
+/**
+ * sysfs_open_attribute: creates sysfs_attribute structure
+ * @path: path to attribute.
+ * returns sysfs_attribute struct with success and NULL with error.
+ */
+struct sysfs_attribute *sysfs_open_attribute(const char *path)
+{
+ struct sysfs_attribute *sysattr = NULL;
+ struct stat fileinfo;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+ sysattr = alloc_attribute();
+ if (!sysattr) {
+ dprintf("Error allocating attribute at %s\n", path);
+ return NULL;
+ }
+ if (sysfs_get_name_from_path(path, sysattr->name,
+ SYSFS_NAME_LEN) != 0) {
+ dprintf("Error retrieving attrib name from path: %s\n", path);
+ sysfs_close_attribute(sysattr);
+ return NULL;
+ }
+ safestrcpy(sysattr->path, path);
+ if ((stat(sysattr->path, &fileinfo)) != 0) {
+ dprintf("Stat failed: No such attribute?\n");
+ sysattr->method = 0;
+ free(sysattr);
+ sysattr = NULL;
+ } else {
+ if (fileinfo.st_mode & S_IRUSR)
+ sysattr->method |= SYSFS_METHOD_SHOW;
+ if (fileinfo.st_mode & S_IWUSR)
+ sysattr->method |= SYSFS_METHOD_STORE;
+ }
+
+ return sysattr;
+}
+
+/**
+ * sysfs_read_attribute: reads value from attribute
+ * @sysattr: attribute to read
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_read_attribute(struct sysfs_attribute *sysattr)
+{
+ char *fbuf = NULL;
+ char *vbuf = NULL;
+ ssize_t length = 0;
+ long pgsize = 0;
+ int fd;
+
+ if (!sysattr) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
+ dprintf("Show method not supported for attribute %s\n",
+ sysattr->path);
+ errno = EACCES;
+ return -1;
+ }
+ pgsize = getpagesize();
+ fbuf = (char *)calloc(1, pgsize+1);
+ if (!fbuf) {
+ dprintf("calloc failed\n");
+ return -1;
+ }
+ if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
+ dprintf("Error reading attribute %s\n", sysattr->path);
+ free(fbuf);
+ return -1;
+ }
+ length = read(fd, fbuf, pgsize);
+ if (length < 0) {
+ dprintf("Error reading from attribute %s\n", sysattr->path);
+ close(fd);
+ free(fbuf);
+ return -1;
+ }
+ if (sysattr->len > 0) {
+ if ((sysattr->len == length) &&
+ (!(strncmp(sysattr->value, fbuf, length)))) {
+ close(fd);
+ free(fbuf);
+ return 0;
+ }
+ free(sysattr->value);
+ }
+ sysattr->len = length;
+ close(fd);
+ vbuf = (char *)realloc(fbuf, length+1);
+ if (!vbuf) {
+ dprintf("realloc failed\n");
+ free(fbuf);
+ return -1;
+ }
+ sysattr->value = vbuf;
+
+ return 0;
+}
+
+/**
+ * sysfs_write_attribute: write value to the attribute
+ * @sysattr: attribute to write
+ * @new_value: value to write
+ * @len: length of "new_value"
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_write_attribute(struct sysfs_attribute *sysattr,
+ const char *new_value, size_t len)
+{
+ int fd;
+ int length;
+
+ if (!sysattr || !new_value || len == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(sysattr->method & SYSFS_METHOD_STORE)) {
+ dprintf ("Store method not supported for attribute %s\n",
+ sysattr->path);
+ errno = EACCES;
+ return -1;
+ }
+ if (sysattr->method & SYSFS_METHOD_SHOW) {
+ /*
+ * read attribute again to see if we can get an updated value
+ */
+ if ((sysfs_read_attribute(sysattr))) {
+ dprintf("Error reading attribute\n");
+ return -1;
+ }
+ if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0 &&
+ (len == sysattr->len)) {
+ dprintf("Attr %s already has the requested value %s\n",
+ sysattr->name, new_value);
+ return 0;
+ }
+ }
+ /*
+ * open O_WRONLY since some attributes have no "read" but only
+ * "write" permission
+ */
+ if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
+ dprintf("Error reading attribute %s\n", sysattr->path);
+ return -1;
+ }
+
+ length = write(fd, new_value, len);
+ if (length < 0) {
+ dprintf("Error writing to the attribute %s - invalid value?\n",
+ sysattr->name);
+ close(fd);
+ return -1;
+ } else if ((unsigned int)length != len) {
+ dprintf("Could not write %zd bytes to attribute %s\n",
+ len, sysattr->name);
+ /*
+ * since we could not write user supplied number of bytes,
+ * restore the old value if one available
+ */
+ if (sysattr->method & SYSFS_METHOD_SHOW) {
+ length = write(fd, sysattr->value, sysattr->len);
+ close(fd);
+ return -1;
+ }
+ }
+
+ /*
+ * Validate length that has been copied. Alloc appropriate area
+ * in sysfs_attribute. Verify first if the attribute supports reading
+ * (show method). If it does not, do not bother
+ */
+ if (sysattr->method & SYSFS_METHOD_SHOW) {
+ if (length != sysattr->len) {
+ sysattr->value = (char *)realloc
+ (sysattr->value, length);
+ sysattr->len = length;
+ safestrcpymax(sysattr->value, new_value, length);
+ } else {
+ /*"length" of the new value is same as old one */
+ safestrcpymax(sysattr->value, new_value, length);
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+/**
+ * add_attribute_to_list: open and add attribute at path to given dlist
+ * @list: dlist attribute is to be added
+ * @path: path to attribute
+ * returns pointer to attr added with success and NULL with error.
+ */
+static struct sysfs_attribute *add_attribute_to_list(struct dlist *alist,
+ const char *path)
+{
+ struct sysfs_attribute *attr;
+
+ attr = sysfs_open_attribute(path);
+ if (!attr) {
+ dprintf("Error opening attribute %s\n", path);
+ return NULL;
+ }
+ if (attr->method & SYSFS_METHOD_SHOW) {
+ if (sysfs_read_attribute(attr)) {
+ dprintf("Error reading attribute %s\n", path);
+ sysfs_close_attribute(attr);
+ return NULL;
+ }
+ }
+
+ if (!alist) {
+ alist = dlist_new_with_delete
+ (sizeof(struct sysfs_attribute), sysfs_del_attribute);
+ }
+ dlist_unshift_sorted(alist, attr, sort_list);
+ return attr;
+}
+
+/**
+ * add_attribute: open and add attribute at path to given directory
+ * @dev: device whose attribute is to be added
+ * @path: path to attribute
+ * returns pointer to attr added with success and NULL with error.
+ */
+static struct sysfs_attribute *add_attribute(void *dev, const char *path)
+{
+ struct sysfs_attribute *attr;
+
+ attr = sysfs_open_attribute(path);
+ if (!attr) {
+ dprintf("Error opening attribute %s\n", path);
+ return NULL;
+ }
+ if (attr->method & SYSFS_METHOD_SHOW) {
+ if (sysfs_read_attribute(attr)) {
+ dprintf("Error reading attribute %s\n", path);
+ sysfs_close_attribute(attr);
+ return NULL;
+ }
+ }
+
+ if (!((struct sysfs_device *)dev)->attrlist) {
+ ((struct sysfs_device *)dev)->attrlist = dlist_new_with_delete
+ (sizeof(struct sysfs_attribute), sysfs_del_attribute);
+ }
+ dlist_unshift_sorted(((struct sysfs_device *)dev)->attrlist,
+ attr, sort_list);
+
+ return attr;
+}
+
+/*
+ * get_attribute - given a sysfs_* struct and a name, return the
+ * sysfs_attribute corresponding to "name"
+ * returns sysfs_attribute on success and NULL on error
+ */
+struct sysfs_attribute *get_attribute(void *dev, const char *name)
+{
+ struct sysfs_attribute *cur = NULL;
+ char path[SYSFS_PATH_MAX];
+
+ if (!dev || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (((struct sysfs_device *)dev)->attrlist) {
+ /* check if attr is already in the list */
+ cur = (struct sysfs_attribute *)dlist_find_custom
+ ((((struct sysfs_device *)dev)->attrlist),
+ (void *)name, attr_name_equal);
+ if (cur)
+ return cur;
+ }
+ safestrcpymax(path, ((struct sysfs_device *)dev)->path,
+ SYSFS_PATH_MAX);
+ safestrcatmax(path, "/", SYSFS_PATH_MAX);
+ safestrcatmax(path, name, SYSFS_PATH_MAX);
+ if (!sysfs_path_is_file(path))
+ cur = add_attribute((void *)dev, path);
+ return cur;
+}
+
+/**
+ * read_dir_links: grabs links in a specific directory
+ * @sysdir: sysfs directory to read
+ * returns list of link names with success and NULL with error.
+ */
+struct dlist *read_dir_links(const char *path)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX], *linkname;
+ struct dlist *linklist = NULL;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+ dir = opendir(path);
+ if (!dir) {
+ dprintf("Error opening directory %s\n", path);
+ return NULL;
+ }
+ while ((dirent = readdir(dir)) != NULL) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if (!sysfs_path_is_link(file_path)) {
+ if (!linklist) {
+ linklist = dlist_new_with_delete
+ (SYSFS_NAME_LEN, sysfs_del_name);
+ if (!linklist) {
+ dprintf("Error creating list\n");
+ return NULL;
+ }
+ }
+ linkname = (char *)calloc(1, SYSFS_NAME_LEN);
+ safestrcpymax(linkname, dirent->d_name, SYSFS_NAME_LEN);
+ dlist_unshift_sorted(linklist, linkname, sort_char);
+ }
+ }
+ closedir(dir);
+ return linklist;
+}
+
+void sysfs_close_dev_tree(void *dev);
+
+int add_subdirectory(struct sysfs_device *dev, char *path)
+{
+ struct sysfs_device *newdev;
+
+ if (!path)
+ return -1;
+
+ newdev = sysfs_open_device_path(path);
+ if (newdev == NULL)
+ return -1;
+
+ if (dev->children == NULL)
+ dev->children = dlist_new_with_delete(
+ sizeof(struct sysfs_device), sysfs_close_dev_tree);
+
+ dlist_unshift_sorted(dev->children, newdev, sort_list);
+ return 0;
+}
+
+/**
+ * read_dir_subdirs: grabs subdirs in a specific directory
+ * @sysdir: sysfs directory to read
+ * returns list of directory names with success and NULL with error.
+ */
+struct sysfs_device *sysfs_read_dir_subdirs(const char *path)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX];
+ struct sysfs_device *dev = NULL;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ dev = sysfs_open_device_path(path);
+
+ dir = opendir(path);
+ if (!dir) {
+ dprintf("Error opening directory %s\n", path);
+ return NULL;
+ }
+ while ((dirent = readdir(dir)) != NULL) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if (!sysfs_path_is_dir(file_path))
+ add_subdirectory(dev, file_path);
+ }
+ closedir(dir);
+ return dev;
+}
+
+/**
+ * read_dir_subdirs: grabs subdirs in a specific directory
+ * @sysdir: sysfs directory to read
+ * returns list of directory names with success and NULL with error.
+ */
+struct dlist *read_dir_subdirs(const char *path)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX], *dir_name;
+ struct dlist *dirlist = NULL;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+ dir = opendir(path);
+ if (!dir) {
+ dprintf("Error opening directory %s\n", path);
+ return NULL;
+ }
+ while ((dirent = readdir(dir)) != NULL) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if (!sysfs_path_is_dir(file_path)) {
+ if (!dirlist) {
+ dirlist = dlist_new_with_delete
+ (SYSFS_NAME_LEN, sysfs_del_name);
+ if (!dirlist) {
+ dprintf("Error creating list\n");
+ return NULL;
+ }
+ }
+ dir_name = (char *)calloc(1, SYSFS_NAME_LEN);
+ safestrcpymax(dir_name, dirent->d_name, SYSFS_NAME_LEN);
+ dlist_unshift_sorted(dirlist, dir_name, sort_char);
+ }
+ }
+ closedir(dir);
+ return dirlist;
+}
+
+/**
+ * get_attributes_list: build a list of attributes for the given path
+ * @path: grab attributes at the given path
+ * returns dlist of attributes on success and NULL on failure
+ */
+struct dlist *get_attributes_list(struct dlist *alist, const char *path)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ char file_path[SYSFS_PATH_MAX];
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ dir = opendir(path);
+ if (!dir) {
+ dprintf("Error opening directory %s\n", path);
+ return NULL;
+ }
+ while ((dirent = readdir(dir)) != NULL) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if (!sysfs_path_is_file(file_path)) {
+ if (!alist) {
+ alist = dlist_new_with_delete
+ (sizeof(struct sysfs_attribute),
+ sysfs_del_attribute);
+ if (!alist) {
+ dprintf("Error creating list\n");
+ return NULL;
+ }
+ }
+ add_attribute_to_list(alist, file_path);
+ }
+ }
+ closedir(dir);
+ return alist;
+}
+
+/**
+ * get_dev_attributes_list: build a list of attributes for the given device
+ * @dev: devices whose attributes list is required
+ * returns dlist of attributes on success and NULL on failure
+ */
+struct dlist *get_dev_attributes_list(void *dev)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ struct sysfs_attribute *attr = NULL;
+ char file_path[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX];
+
+ if (!dev) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, ((struct sysfs_device *)dev)->path);
+ dir = opendir(path);
+ if (!dir) {
+ dprintf("Error opening directory %s\n", path);
+ return NULL;
+ }
+ while ((dirent = readdir(dir)) != NULL) {
+ if (0 == strcmp(dirent->d_name, "."))
+ continue;
+ if (0 == strcmp(dirent->d_name, ".."))
+ continue;
+ memset(file_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(file_path, path);
+ safestrcat(file_path, "/");
+ safestrcat(file_path, dirent->d_name);
+ if (!sysfs_path_is_file(file_path)) {
+ if (((struct sysfs_device *)dev)->attrlist) {
+ /* check if attr is already in the list */
+ attr = (struct sysfs_attribute *)
+ dlist_find_custom
+ ((((struct sysfs_device *)dev)->attrlist),
+ (void *)dirent->d_name, attr_name_equal);
+ if (attr)
+ continue;
+ else
+ add_attribute(dev, file_path);
+ } else
+ attr = add_attribute(dev, file_path);
+ }
+ }
+ closedir(dir);
+ return ((struct sysfs_device *)dev)->attrlist;
+}
diff --git a/libsysfs/sysfs_bus.c b/libsysfs/sysfs_bus.c
new file mode 100644
index 0000000..8d236a7
--- /dev/null
+++ b/libsysfs/sysfs_bus.c
@@ -0,0 +1,317 @@
+/*
+ * sysfs_bus.c
+ *
+ * Generic bus utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+static void sysfs_close_dev(void *dev)
+{
+ sysfs_close_device((struct sysfs_device *)dev);
+}
+
+static void sysfs_close_drv(void *drv)
+{
+ sysfs_close_driver((struct sysfs_driver *)drv);
+}
+
+/*
+ * compares names.
+ * @a: name looked for
+ * @b: sysfs_device comparing being compared
+ * returns 1 if a==b->name or 0 not equal
+ */
+static int name_equal(void *a, void *b)
+{
+ if (!a || !b)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_device *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * sysfs_close_bus: close single bus
+ * @bus: bus structure
+ */
+void sysfs_close_bus(struct sysfs_bus *bus)
+{
+ if (bus) {
+ if (bus->attrlist)
+ dlist_destroy(bus->attrlist);
+ if (bus->devices)
+ dlist_destroy(bus->devices);
+ if (bus->drivers)
+ dlist_destroy(bus->drivers);
+ free(bus);
+ }
+}
+
+/**
+ * alloc_bus: mallocs new bus structure
+ * returns sysfs_bus_bus struct or NULL
+ */
+static struct sysfs_bus *alloc_bus(void)
+{
+ return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus));
+}
+
+/**
+ * sysfs_get_bus_devices: gets all devices for bus
+ * @bus: bus to get devices for
+ * returns dlist of devices with success and NULL with failure
+ */
+struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus)
+{
+ struct sysfs_device *dev;
+ struct dlist *linklist;
+ char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
+ char target[SYSFS_PATH_MAX];
+ char *curlink;
+
+ if (!bus) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, bus->path);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DEVICES_NAME);
+
+ linklist = read_dir_links(path);
+ if (linklist) {
+ dlist_for_each_data(linklist, curlink, char) {
+ if (bus->devices) {
+ dev = (struct sysfs_device *)
+ dlist_find_custom(bus->devices,
+ (void *)curlink, name_equal);
+ if (dev)
+ continue;
+ }
+ safestrcpy(devpath, path);
+ safestrcat(devpath, "/");
+ safestrcat(devpath, curlink);
+ if (sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
+ dprintf("Error getting link - %s\n", devpath);
+ continue;
+ }
+ dev = sysfs_open_device_path(target);
+ if (!dev) {
+ dprintf("Error opening device at %s\n",
+ target);
+ continue;
+ }
+ if (!bus->devices)
+ bus->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_dev);
+ dlist_unshift_sorted(bus->devices, dev, sort_list);
+ }
+ sysfs_close_list(linklist);
+ }
+ return (bus->devices);
+}
+
+/**
+ * sysfs_get_bus_drivers: gets all drivers for bus
+ * @bus: bus to get devices for
+ * returns dlist of devices with success and NULL with failure
+ */
+struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus)
+{
+ struct sysfs_driver *drv;
+ struct dlist *dirlist;
+ char path[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX];
+ char *curdir;
+
+ if (!bus) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, bus->path);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_DRIVERS_NAME);
+
+ dirlist = read_dir_subdirs(path);
+ if (dirlist) {
+ dlist_for_each_data(dirlist, curdir, char) {
+ if (bus->drivers) {
+ drv = (struct sysfs_driver *)
+ dlist_find_custom(bus->drivers,
+ (void *)curdir, name_equal);
+ if (drv)
+ continue;
+ }
+ safestrcpy(drvpath, path);
+ safestrcat(drvpath, "/");
+ safestrcat(drvpath, curdir);
+ drv = sysfs_open_driver_path(drvpath);
+ if (!drv) {
+ dprintf("Error opening driver at %s\n",
+ drvpath);
+ continue;
+ }
+ if (!bus->drivers)
+ bus->drivers = dlist_new_with_delete
+ (sizeof(struct sysfs_driver),
+ sysfs_close_drv);
+ dlist_unshift_sorted(bus->drivers, drv, sort_list);
+ }
+ sysfs_close_list(dirlist);
+ }
+ return (bus->drivers);
+}
+
+/**
+ * sysfs_open_bus: opens specific bus and all its devices on system
+ * returns sysfs_bus structure with success or NULL with error.
+ */
+struct sysfs_bus *sysfs_open_bus(const char *name)
+{
+ struct sysfs_bus *bus;
+ char buspath[SYSFS_PATH_MAX];
+
+ if (!name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(buspath, 0, SYSFS_PATH_MAX);
+ if (sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) {
+ dprintf("Sysfs not supported on this system\n");
+ return NULL;
+ }
+
+ safestrcat(buspath, "/");
+ safestrcat(buspath, SYSFS_BUS_NAME);
+ safestrcat(buspath, "/");
+ safestrcat(buspath, name);
+ if (sysfs_path_is_dir(buspath)) {
+ dprintf("Invalid path to bus: %s\n", buspath);
+ return NULL;
+ }
+ bus = alloc_bus();
+ if (!bus) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ safestrcpy(bus->name, name);
+ safestrcpy(bus->path, buspath);
+ if (sysfs_remove_trailing_slash(bus->path)) {
+ dprintf("Incorrect path to bus %s\n", bus->path);
+ sysfs_close_bus(bus);
+ return NULL;
+ }
+
+ return bus;
+}
+
+/**
+ * sysfs_get_bus_device: Get specific device on bus using device's id
+ * @bus: bus to find device on
+ * @id: bus_id for device
+ * returns struct sysfs_device reference or NULL if not found.
+ */
+struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus,
+ const char *id)
+{
+ struct sysfs_device *dev = NULL;
+ char devpath[SYSFS_PATH_MAX], target[SYSFS_PATH_MAX];
+
+ if (!bus || !id) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (bus->devices) {
+ dev = (struct sysfs_device *)dlist_find_custom
+ (bus->devices, (void *)id, name_equal);
+ if (dev)
+ return dev;
+ }
+ safestrcpy(devpath, bus->path);
+ safestrcat(devpath, "/");
+ safestrcat(devpath, SYSFS_DEVICES_NAME);
+ safestrcat(devpath, "/");
+ safestrcat(devpath, id);
+ if (sysfs_path_is_link(devpath)) {
+ dprintf("No such device %s on bus %s?\n", id, bus->name);
+ return NULL;
+ }
+ if (!sysfs_get_link(devpath, target, SYSFS_PATH_MAX)) {
+ dev = sysfs_open_device_path(target);
+ if (!dev) {
+ dprintf("Error opening device at %s\n", target);
+ return NULL;
+ }
+ if (!bus->devices)
+ bus->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_dev);
+ dlist_unshift_sorted(bus->devices, dev, sort_list);
+ }
+ return dev;
+}
+
+/**
+ * sysfs_get_bus_driver: Get specific driver on bus using driver name
+ * @bus: bus to find driver on
+ * @drvname: name of driver
+ * returns struct sysfs_driver reference or NULL if not found.
+ */
+struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus,
+ const char *drvname)
+{
+ struct sysfs_driver *drv;
+ char drvpath[SYSFS_PATH_MAX];
+
+ if (!bus || !drvname) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (bus->drivers) {
+ drv = (struct sysfs_driver *)dlist_find_custom
+ (bus->drivers, (void *)drvname, name_equal);
+ if (drv)
+ return drv;
+ }
+ safestrcpy(drvpath, bus->path);
+ safestrcat(drvpath, "/");
+ safestrcat(drvpath, SYSFS_DRIVERS_NAME);
+ safestrcat(drvpath, "/");
+ safestrcat(drvpath, drvname);
+ drv = sysfs_open_driver_path(drvpath);
+ if (!drv) {
+ dprintf("Error opening driver at %s\n", drvpath);
+ return NULL;
+ }
+ if (!bus->drivers)
+ bus->drivers = dlist_new_with_delete
+ (sizeof(struct sysfs_driver),
+ sysfs_close_drv);
+ dlist_unshift_sorted(bus->drivers, drv, sort_list);
+ return drv;
+}
+
diff --git a/libsysfs/sysfs_class.c b/libsysfs/sysfs_class.c
new file mode 100644
index 0000000..4fe0b82
--- /dev/null
+++ b/libsysfs/sysfs_class.c
@@ -0,0 +1,551 @@
+/*
+ * sysfs_class.c
+ *
+ * Generic class utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+/**
+ * sysfs_close_class_device: closes a single class device.
+ * @dev: class device to close.
+ */
+void sysfs_close_class_device(struct sysfs_class_device *dev)
+{
+ if (dev) {
+ if (dev->parent)
+ sysfs_close_class_device(dev->parent);
+ if (dev->sysdevice)
+ sysfs_close_device(dev->sysdevice);
+ if (dev->attrlist)
+ dlist_destroy(dev->attrlist);
+ free(dev);
+ }
+}
+
+static void sysfs_close_cls_dev(void *dev)
+{
+ sysfs_close_class_device((struct sysfs_class_device *)dev);
+}
+
+/**
+ * sysfs_close_class: close the given class
+ * @cls: sysfs_class to close
+ */
+void sysfs_close_class(struct sysfs_class *cls)
+{
+ if (cls) {
+ if (cls->devices)
+ dlist_destroy(cls->devices);
+ if (cls->attrlist)
+ dlist_destroy(cls->attrlist);
+ free(cls);
+ }
+}
+
+static int cdev_name_equal(void *a, void *b)
+{
+ if (!a || !b)
+ return 0;
+
+ if (strncmp((char *)a, ((struct sysfs_class_device *)b)->name,
+ strlen((char *)a)) == 0)
+ return 1;
+
+ return 0;
+}
+
+static struct sysfs_class *alloc_class(void)
+{
+ return (struct sysfs_class *) calloc(1, sizeof(struct sysfs_class));
+}
+
+/**
+ * alloc_class_device: mallocs and initializes new class device struct.
+ * returns sysfs_class_device or NULL.
+ */
+static struct sysfs_class_device *alloc_class_device(void)
+{
+ struct sysfs_class_device *dev;
+
+ dev = calloc(1, sizeof(struct sysfs_class_device));
+ return dev;
+}
+
+/**
+ * set_classdev_classname: Grabs classname from path
+ * @cdev: class device to set
+ * Returns nothing
+ */
+static void set_classdev_classname(struct sysfs_class_device *cdev)
+{
+ char *c, *e, name[SYSFS_PATH_MAX], link[SYSFS_PATH_MAX];
+ struct stat stats;
+ int count = 0;
+
+ /*
+ * Newer driver core changes have a class:class_device representation.
+ * Check if this cdev belongs to the newer style subsystem and
+ * set the classname appropriately.
+ */
+ memset(name, 0, SYSFS_PATH_MAX);
+ safestrcpy(name, cdev->name);
+ c = strchr(name, ':');
+ if (c) {
+ safestrcpy(cdev->name, c+1);
+ *c = '\0';
+ safestrcpy(cdev->classname, name);
+ return;
+ }
+
+ c = strstr(cdev->path, SYSFS_CLASS_NAME);
+ if (c == NULL)
+ c = strstr(cdev->path, SYSFS_BLOCK_NAME);
+ else
+ c = strstr(c, "/");
+
+ if (c) {
+ if (*c == '/')
+ c++;
+ e = c;
+ while (e != NULL && *e != '/' && *e != '\0') {
+ e++;
+ count++;
+ }
+ strncpy(cdev->classname, c, count);
+ } else {
+ strcpy(link, cdev->path);
+ strcat(link, "/subsystem");
+ sysfs_get_link(link, name, SYSFS_PATH_MAX);
+ if (lstat(name, &stats))
+ safestrcpy(cdev->classname, SYSFS_UNKNOWN);
+ else {
+ c = strrchr(name, '/');
+ if (c)
+ safestrcpy(cdev->classname, c+1);
+ else
+ safestrcpy(cdev->classname, SYSFS_UNKNOWN);
+ }
+ }
+}
+
+/**
+ * sysfs_open_class_device_path: Opens and populates class device
+ * @path: path to class device.
+ * returns struct sysfs_class_device with success and NULL with error.
+ */
+struct sysfs_class_device *sysfs_open_class_device_path(const char *path)
+{
+ struct sysfs_class_device *cdev;
+ char temp_path[SYSFS_PATH_MAX];
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Post linux-2.6.14 driver model supports nested classes with
+ * links to the nested hierarchy at /sys/class/xxx/. Check for
+ * a link to the actual class device if a directory isn't found
+ */
+ if (sysfs_path_is_dir(path)) {
+ dprintf("%s: Directory not found, checking for a link\n", path);
+ if (!sysfs_path_is_link(path)) {
+ if (sysfs_get_link(path, temp_path, SYSFS_PATH_MAX)) {
+ dprintf("Error retrieving link at %s\n", path);
+ return NULL;
+ }
+ } else {
+ dprintf("%s is not a valid class device path\n", path);
+ return NULL;
+ }
+ } else
+ safestrcpy(temp_path, path);
+
+ cdev = alloc_class_device();
+ if (!cdev) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ if (sysfs_get_name_from_path(temp_path, cdev->name, SYSFS_NAME_LEN)) {
+ errno = EINVAL;
+ dprintf("Error getting class device name\n");
+ sysfs_close_class_device(cdev);
+ return NULL;
+ }
+
+ safestrcpy(cdev->path, temp_path);
+ if (sysfs_remove_trailing_slash(cdev->path)) {
+ dprintf("Invalid path to class device %s\n", cdev->path);
+ sysfs_close_class_device(cdev);
+ return NULL;
+ }
+ set_classdev_classname(cdev);
+
+ return cdev;
+}
+
+/**
+ * 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)
+{
+ char abs_path[SYSFS_PATH_MAX], tmp_path[SYSFS_PATH_MAX];
+ char *c;
+
+ if (!clsdev) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (clsdev->parent)
+ return (clsdev->parent);
+
+ memset(abs_path, 0, SYSFS_PATH_MAX);
+ memset(tmp_path, 0, SYSFS_PATH_MAX);
+
+ safestrcpy(tmp_path, clsdev->path);
+ c = strstr(tmp_path, clsdev->classname);
+ c = strchr(c, '/');
+ *c = '\0';
+
+ safestrcpy(abs_path, clsdev->path);
+ c = strrchr(abs_path, '/');
+ *c = '\0';
+
+ if ((strncmp(tmp_path, abs_path, strlen(abs_path))) == 0) {
+ dprintf("Class device %s doesn't have a parent\n",
+ clsdev->name);
+ return NULL;
+ }
+
+ clsdev->parent = sysfs_open_class_device_path(abs_path);
+
+ return clsdev->parent;
+}
+
+/**
+ * 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)
+{
+ char *c;
+
+ if (!classname || !clsdev || !path) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sysfs_get_mnt_path(path, len) != 0) {
+ dprintf("Error getting sysfs mount path\n");
+ return -1;
+ }
+ safestrcatmax(path, "/", len);
+ if (strncmp(classname, SYSFS_BLOCK_NAME,
+ sizeof(SYSFS_BLOCK_NAME)) == 0) {
+ safestrcatmax(path, SYSFS_BLOCK_NAME, len);
+ if (!sysfs_path_is_dir(path))
+ goto done;
+ c = strrchr(path, '/');
+ *(c+1) = '\0';
+ }
+ safestrcatmax(path, SYSFS_CLASS_NAME, len);
+ safestrcatmax(path, "/", len);
+ safestrcatmax(path, classname, len);
+done:
+ safestrcatmax(path, "/", len);
+ safestrcatmax(path, clsdev, len);
+ return 0;
+}
+
+/**
+ * sysfs_open_class_device: Locates a specific class_device and returns it.
+ * Class_device must be closed using sysfs_close_class_device
+ * @classname: Class to search
+ * @name: name of the class_device
+ *
+ * NOTE:
+ * Call sysfs_close_class_device() to close the class device
+ */
+struct sysfs_class_device *sysfs_open_class_device
+ (const char *classname, const char *name)
+{
+ char devpath[SYSFS_PATH_MAX];
+ struct sysfs_class_device *cdev;
+
+ if (!classname || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ if ((get_classdev_path(classname, name, devpath,
+ SYSFS_PATH_MAX)) != 0) {
+ dprintf("Error getting to device %s on class %s\n",
+ name, classname);
+ return NULL;
+ }
+
+ cdev = sysfs_open_class_device_path(devpath);
+ if (!cdev) {
+ dprintf("Error getting class device %s from class %s\n",
+ name, classname);
+ return NULL;
+ }
+ return cdev;
+}
+
+/**
+ * sysfs_get_classdev_attr: searches class device's attributes by name
+ * @clsdev: class device to look through
+ * @name: attribute name to get
+ * returns sysfs_attribute reference with success or NULL with error
+ */
+struct sysfs_attribute *sysfs_get_classdev_attr
+ (struct sysfs_class_device *clsdev, const char *name)
+{
+ if (!clsdev || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_attribute(clsdev, (char *)name);
+}
+
+/**
+ * sysfs_get_classdev_attributes: gets list of classdev attributes
+ * @clsdev: class device whose attributes list is needed
+ * returns dlist of attributes on success or NULL on error
+ */
+struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *clsdev)
+{
+ if (!clsdev) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_dev_attributes_list(clsdev);
+}
+
+/**
+ * sysfs_get_classdev_device: gets the sysfs_device associated with the
+ * given sysfs_class_device
+ * @clsdev: class device whose associated sysfs_device is needed
+ * returns struct sysfs_device * on success or NULL on error
+ */
+struct sysfs_device *sysfs_get_classdev_device
+ (struct sysfs_class_device *clsdev)
+{
+ char linkpath[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
+
+ if (!clsdev) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (clsdev->sysdevice)
+ return clsdev->sysdevice;
+
+ memset(linkpath, 0, SYSFS_PATH_MAX);
+ safestrcpy(linkpath, clsdev->path);
+ safestrcat(linkpath, "/device");
+ if (!sysfs_path_is_link(linkpath)) {
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ if (!sysfs_get_link(linkpath, devpath, SYSFS_PATH_MAX))
+ clsdev->sysdevice = sysfs_open_device_path(devpath);
+ }
+ return clsdev->sysdevice;
+}
+
+/**
+ * sysfs_open_class: opens specific class and all its devices on system
+ * returns sysfs_class structure with success or NULL with error.
+ */
+struct sysfs_class *sysfs_open_class(const char *name)
+{
+ struct sysfs_class *cls = NULL;
+ char *c, classpath[SYSFS_PATH_MAX];
+
+ if (!name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(classpath, 0, SYSFS_PATH_MAX);
+ if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) {
+ dprintf("Sysfs not supported on this system\n");
+ return NULL;
+ }
+
+ safestrcat(classpath, "/");
+ if (strcmp(name, SYSFS_BLOCK_NAME) == 0) {
+ safestrcat(classpath, SYSFS_BLOCK_NAME);
+ if (!sysfs_path_is_dir(classpath))
+ goto done;
+ c = strrchr(classpath, '/');
+ *(c+1) = '\0';
+ }
+ safestrcat(classpath, SYSFS_CLASS_NAME);
+ safestrcat(classpath, "/");
+ safestrcat(classpath, name);
+done:
+ if (sysfs_path_is_dir(classpath)) {
+ dprintf("Class %s not found on the system\n", name);
+ return NULL;
+ }
+
+ cls = alloc_class();
+ if (cls == NULL) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ safestrcpy(cls->name, name);
+ safestrcpy(cls->path, classpath);
+ if ((sysfs_remove_trailing_slash(cls->path)) != 0) {
+ dprintf("Invalid path to class device %s\n", cls->path);
+ sysfs_close_class(cls);
+ return NULL;
+ }
+
+ return cls;
+}
+
+/**
+ * sysfs_get_class_device: get specific class device using the device's id
+ * @cls: sysfs_class to find the device on
+ * @name: name of the class device to look for
+ *
+ * Returns sysfs_class_device * on success and NULL on failure
+ */
+struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *cls,
+ const char *name)
+{
+ char path[SYSFS_PATH_MAX];
+ struct sysfs_class_device *cdev = NULL;
+
+ if (!cls || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (cls->devices) {
+ cdev = (struct sysfs_class_device *)dlist_find_custom
+ (cls->devices, (void *)name, cdev_name_equal);
+ if (cdev)
+ return cdev;
+ }
+
+ safestrcpy(path, cls->path);
+ safestrcat(path, "/");
+ safestrcat(path, name);
+ cdev = sysfs_open_class_device_path(path);
+ if (!cdev) {
+ dprintf("Error opening class device at %s\n", path);
+ return NULL;
+ }
+ if (!cls->devices)
+ cls->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_class_device),
+ sysfs_close_cls_dev);
+
+ dlist_unshift_sorted(cls->devices, cdev, sort_list);
+ return cdev;
+}
+
+/**
+ * Add class devices to list
+ */
+static void add_cdevs_to_classlist(struct sysfs_class *cls, struct dlist *list)
+{
+ char path[SYSFS_PATH_MAX], *cdev_name;
+ struct sysfs_class_device *cdev = NULL;
+
+ if (cls == NULL || list == NULL)
+ return;
+
+ dlist_for_each_data(list, cdev_name, char) {
+ if (cls->devices) {
+ cdev = (struct sysfs_class_device *)
+ dlist_find_custom(cls->devices,
+ (void *)cdev_name, cdev_name_equal);
+ if (cdev)
+ continue;
+ }
+ safestrcpy(path, cls->path);
+ safestrcat(path, "/");
+ safestrcat(path, cdev_name);
+ cdev = sysfs_open_class_device_path(path);
+ if (cdev) {
+ if (!cls->devices)
+ cls->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_class_device),
+ sysfs_close_cls_dev);
+ dlist_unshift_sorted(cls->devices, cdev,
+ sort_list);
+ }
+ }
+}
+
+/**
+ * sysfs_get_class_devices: get all class devices in the given class
+ * @cls: sysfs_class whose devices list is needed
+ *
+ * Returns a dlist of sysfs_class_device * on success and NULL on failure
+ */
+struct dlist *sysfs_get_class_devices(struct sysfs_class *cls)
+{
+ char path[SYSFS_PATH_MAX];
+ struct dlist *dirlist, *linklist;
+
+ if (!cls) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Post linux-2.6.14, we have nested classes and links under
+ * /sys/class/xxx/. are also valid class devices
+ */
+ safestrcpy(path, cls->path);
+ dirlist = read_dir_subdirs(path);
+ if (dirlist) {
+ add_cdevs_to_classlist(cls, dirlist);
+ sysfs_close_list(dirlist);
+ }
+
+ linklist = read_dir_links(path);
+ if (linklist) {
+ add_cdevs_to_classlist(cls, linklist);
+ sysfs_close_list(linklist);
+ }
+
+ return cls->devices;
+}
diff --git a/libsysfs/sysfs_device.c b/libsysfs/sysfs_device.c
new file mode 100644
index 0000000..b783ec5
--- /dev/null
+++ b/libsysfs/sysfs_device.c
@@ -0,0 +1,424 @@
+/*
+ * sysfs_device.c
+ *
+ * Generic device utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+/**
+ * get_dev_driver: fills in the dev->driver_name field
+ * Returns 0 on SUCCESS and -1 on error
+ */
+static int get_dev_driver(struct sysfs_device *dev)
+{
+ char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
+
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
+ safestrcatmax(path, "/driver", SYSFS_PATH_MAX);
+ if (!sysfs_path_is_link(path)) {
+ if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
+ if (!sysfs_get_name_from_path(devpath,
+ dev->driver_name, SYSFS_NAME_LEN))
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/**
+ * sysfs_get_device_bus: retrieves the bus name the device is on, checks path
+ * to bus' link to make sure it has correct device.
+ * @dev: device to get busname.
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_get_device_bus(struct sysfs_device *dev)
+{
+ char devpath[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX];
+
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(path, 0, SYSFS_PATH_MAX);
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
+ safestrcatmax(path, "/bus", SYSFS_PATH_MAX);
+ if (!sysfs_path_is_link(path)) {
+ if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
+ if (!sysfs_get_name_from_path(devpath,
+ dev->bus, SYSFS_NAME_LEN))
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/**
+ * get_dev_subsystem: fills in the dev->subsystem field
+ * Returns 0 on SUCCESS and -1 on error
+ */
+static int get_dev_subsystem(struct sysfs_device *dev)
+{
+ char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
+
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+ memset(path, 0, SYSFS_PATH_MAX);
+ memset(devpath, 0, SYSFS_PATH_MAX);
+ safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
+ safestrcatmax(path, "/subsystem", SYSFS_PATH_MAX);
+ if (!sysfs_path_is_link(path)) {
+ if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
+ if (!sysfs_get_name_from_path(devpath,
+ dev->subsystem, SYSFS_NAME_LEN))
+ return 0;
+ }
+ }
+ return -1;
+}
+/**
+ * sysfs_close_dev_tree: routine for dlist integration
+ */
+void sysfs_close_dev_tree(void *dev)
+{
+ sysfs_close_device_tree((struct sysfs_device *)dev);
+}
+
+/**
+ * sysfs_close_device_tree: closes every device in the supplied tree,
+ * closing children only.
+ * @devroot: device root of tree.
+ */
+void sysfs_close_device_tree(struct sysfs_device *devroot)
+{
+ if (devroot) {
+ if (devroot->children) {
+ struct sysfs_device *child = NULL;
+
+ dlist_for_each_data(devroot->children, child,
+ struct sysfs_device)
+ sysfs_close_device_tree(child);
+ }
+ devroot->children = NULL;
+ sysfs_close_device(devroot);
+ }
+}
+
+/**
+ * sysfs_close_device: closes and cleans up a device
+ * @dev = device to clean up
+ */
+void sysfs_close_device(struct sysfs_device *dev)
+{
+ if (dev) {
+ if (dev->parent)
+ sysfs_close_device(dev->parent);
+ if (dev->children && dev->children->count)
+ dlist_destroy(dev->children);
+ if (dev->attrlist)
+ dlist_destroy(dev->attrlist);
+ free(dev);
+ }
+}
+
+/**
+ * alloc_device: allocates and initializes device structure
+ * returns struct sysfs_device
+ */
+static struct sysfs_device *alloc_device(void)
+{
+ return (struct sysfs_device *) calloc(1, sizeof(struct sysfs_device));
+}
+
+/**
+ * sysfs_open_device_path: opens and populates device structure
+ * @path: path to device, this is the /sys/devices/ path
+ * returns sysfs_device structure with success or NULL with error
+ */
+struct sysfs_device *sysfs_open_device_path(const char *path)
+{
+ struct sysfs_device *dev;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (sysfs_path_is_dir(path)) {
+ dprintf("Incorrect path to device: %s\n", path);
+ return NULL;
+ }
+ dev = alloc_device();
+ if (!dev) {
+ dprintf("Error allocating device at %s\n", path);
+ return NULL;
+ }
+ if (sysfs_get_name_from_path(path, dev->bus_id, SYSFS_NAME_LEN)) {
+ errno = EINVAL;
+ dprintf("Error getting device bus_id\n");
+ sysfs_close_device(dev);
+ return NULL;
+ }
+ safestrcpy(dev->path, path);
+ if (sysfs_remove_trailing_slash(dev->path)) {
+ dprintf("Invalid path to device %s\n", dev->path);
+ sysfs_close_device(dev);
+ return NULL;
+ }
+ /*
+ * The "name" attribute no longer exists... return the device's
+ * sysfs representation instead, in the "dev->name" field, which
+ * implies that the dev->name and dev->bus_id contain same data.
+ */
+ safestrcpy(dev->name, dev->bus_id);
+
+ if (sysfs_get_device_bus(dev))
+ dprintf("Could not get device bus\n");
+
+ if (get_dev_driver(dev)) {
+ dprintf("Could not get device %s's driver\n", dev->bus_id);
+ safestrcpy(dev->driver_name, SYSFS_UNKNOWN);
+ }
+
+ if (get_dev_subsystem(dev)) {
+ dprintf("Could not get device %s's subsystem\n", dev->bus_id);
+ safestrcpy(dev->subsystem, SYSFS_UNKNOWN);
+ }
+ return dev;
+}
+
+/**
+ * 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.
+ */
+struct sysfs_device *sysfs_open_device_tree(const char *path)
+{
+ struct sysfs_device *rootdev = NULL, *new = NULL;
+ struct sysfs_device *cur = NULL;
+ struct sysfs_device *devlist;
+
+ 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;
+ }
+
+ devlist = sysfs_read_dir_subdirs(path);
+ if (devlist->children) {
+ dlist_for_each_data(devlist->children, cur,
+ struct sysfs_device) {
+ 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);
+ }
+ }
+
+ return rootdev;
+}
+
+/**
+ * sysfs_get_device_attr: searches dev's attributes by name
+ * @dev: device to look through
+ * @name: attribute name to get
+ * returns sysfs_attribute reference with success or NULL with error.
+ */
+struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
+ const char *name)
+{
+ if (!dev || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_attribute(dev, (char *)name);
+}
+
+/**
+ * sysfs_get_device_attributes: gets list of device attributes
+ * @dev: device whose attributes list is needed
+ * returns dlist of attributes on success or NULL on error
+ */
+struct dlist *sysfs_get_device_attributes(struct sysfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_dev_attributes_list(dev);
+}
+
+/**
+ * get_device_absolute_path: looks up the bus the device is on, gets
+ * absolute path to the device
+ * @device: device for which path is needed
+ * @path: buffer to store absolute path
+ * @psize: size of "path"
+ * Returns 0 on success -1 on failure
+ */
+static int get_device_absolute_path(const char *device, const char *bus,
+ char *path, size_t psize)
+{
+ char bus_path[SYSFS_PATH_MAX];
+
+ if (!device || !path) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(bus_path, 0, SYSFS_PATH_MAX);
+ if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX)) {
+ dprintf ("Sysfs not supported on this system\n");
+ return -1;
+ }
+ safestrcat(bus_path, "/");
+ safestrcat(bus_path, SYSFS_BUS_NAME);
+ safestrcat(bus_path, "/");
+ safestrcat(bus_path, bus);
+ safestrcat(bus_path, "/");
+ safestrcat(bus_path, SYSFS_DEVICES_NAME);
+ safestrcat(bus_path, "/");
+ safestrcat(bus_path, device);
+ /*
+ * We now are at /sys/bus/"bus_name"/devices/"device" which is a link.
+ * Now read this link to reach to the device.
+ */
+ if (sysfs_get_link(bus_path, path, psize)) {
+ dprintf("Error getting to device %s\n", device);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * sysfs_open_device: open a device by id (use the "bus" subsystem)
+ * @bus: bus the device belongs to
+ * @bus_id: bus_id of the device to open - has to be the "bus_id" in
+ * /sys/bus/xxx/devices
+ * returns struct sysfs_device if found, NULL otherwise
+ * NOTE:
+ * 1. Use sysfs_close_device to close the device
+ * 2. Bus the device is on must be supplied
+ * Use sysfs_find_device_bus to get the bus name
+ */
+struct sysfs_device *sysfs_open_device(const char *bus, const char *bus_id)
+{
+ char sysfs_path[SYSFS_PATH_MAX];
+ struct sysfs_device *device;
+
+ if (!bus_id || !bus) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(sysfs_path, 0, SYSFS_PATH_MAX);
+ if (get_device_absolute_path(bus_id, bus, sysfs_path,
+ SYSFS_PATH_MAX)) {
+ dprintf("Error getting to device %s\n", bus_id);
+ return NULL;
+ }
+
+ device = sysfs_open_device_path(sysfs_path);
+ if (!device) {
+ dprintf("Error opening device %s\n", bus_id);
+ return NULL;
+ }
+ return device;
+}
+
+/**
+ * sysfs_get_device_parent: opens up given device's parent and returns a
+ * reference to its sysfs_device
+ * @dev: sysfs_device whose parent is requested
+ * Returns sysfs_device of the parent on success and NULL on failure
+ */
+struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev)
+{
+ char ppath[SYSFS_PATH_MAX], dpath[SYSFS_PATH_MAX], *tmp;
+
+ if (!dev) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dev->parent)
+ return (dev->parent);
+
+ memset(ppath, 0, SYSFS_PATH_MAX);
+ memset(dpath, 0, SYSFS_PATH_MAX);
+ safestrcpy(ppath, dev->path);
+ tmp = strrchr(ppath, '/');
+ if (!tmp) {
+ dprintf("Invalid path to device %s\n", ppath);
+ return NULL;
+ }
+ if (*(tmp + 1) == '\0') {
+ *tmp = '\0';
+ tmp = strrchr(tmp, '/');
+ if (tmp == NULL) {
+ dprintf("Invalid path to device %s\n", ppath);
+ return NULL;
+ }
+ }
+ *tmp = '\0';
+
+ /* Make sure we're at the top of the device tree */
+ if (sysfs_get_mnt_path(dpath, SYSFS_PATH_MAX) != 0) {
+ dprintf("Sysfs not supported on this system\n");
+ return NULL;
+ }
+ safestrcat(dpath, "/");
+ safestrcat(dpath, SYSFS_DEVICES_NAME);
+
+ if (strcmp(dpath, ppath) == 0) {
+ dprintf("Device at %s does not have a parent\n", dev->path);
+ return NULL;
+ }
+
+ dev->parent = sysfs_open_device_path(ppath);
+ if (!dev->parent) {
+ dprintf("Error opening device %s's parent at %s\n",
+ dev->bus_id, ppath);
+ return NULL;
+ }
+ return (dev->parent);
+}
diff --git a/libsysfs/sysfs_dir.c b/libsysfs/sysfs_dir.c
new file mode 100644
index 0000000..5276991
--- /dev/null
+++ b/libsysfs/sysfs_dir.c
@@ -0,0 +1,1071 @@
+/*
+ * sysfs_dir.c
+ *
+ * Directory utility functions for libsysfs
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+/**
+ * sysfs_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.
+ */
+void sysfs_close_attribute(struct sysfs_attribute *sysattr)
+{
+ if (sysattr != NULL) {
+ if (sysattr->value != NULL)
+ free(sysattr->value);
+ free(sysattr);
+ }
+}
+
+/**
+ * alloc_attribute: allocates and initializes attribute structure
+ * returns struct sysfs_attribute with success and NULL with error.
+ */
+static struct sysfs_attribute *alloc_attribute(void)
+{
+ return (struct sysfs_attribute *)
+ calloc(1, sizeof(struct sysfs_attribute));
+}
+
+/**
+ * sysfs_open_attribute: creates sysfs_attribute structure
+ * @path: path to attribute.
+ * returns sysfs_attribute struct with success and NULL with error.
+ */
+struct sysfs_attribute *sysfs_open_attribute(const char *path)
+{
+ struct sysfs_attribute *sysattr = NULL;
+ struct stat fileinfo;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ sysattr = alloc_attribute();
+ if (sysattr == NULL) {
+ dprintf("Error allocating attribute at %s\n", path);
+ return NULL;
+ }
+ if (sysfs_get_name_from_path(path, sysattr->name,
+ SYSFS_NAME_LEN) != 0) {
+ dprintf("Error retrieving attrib name from path: %s\n", path);
+ sysfs_close_attribute(sysattr);
+ return NULL;
+ }
+ safestrcpy(sysattr->path, path);
+ if ((stat(sysattr->path, &fileinfo)) != 0) {
+ dprintf("Stat failed: No such attribute?\n");
+ sysattr->method = 0;
+ free(sysattr);
+ sysattr = NULL;
+ } else {
+ if (fileinfo.st_mode & S_IRUSR)
+ sysattr->method |= SYSFS_METHOD_SHOW;
+ if (fileinfo.st_mode & S_IWUSR)
+ sysattr->method |= SYSFS_METHOD_STORE;
+ }
+
+ return sysattr;
+}
+
+/**
+ * sysfs_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.
+ */
+int sysfs_read_attribute(struct sysfs_attribute *sysattr)
+{
+ char *fbuf = NULL;
+ char *vbuf = NULL;
+ ssize_t length = 0;
+ long pgsize = 0;
+ int fd;
+
+ if (sysattr == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
+ dprintf("Show method not supported for attribute %s\n",
+ sysattr->path);
+ errno = EACCES;
+ return -1;
+ }
+ pgsize = sysconf(_SC_PAGESIZE);
+ fbuf = (char *)calloc(1, pgsize+1);
+ if (fbuf == NULL) {
+ dprintf("calloc failed\n");
+ return -1;
+ }
+ if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
+ dprintf("Error reading attribute %s\n", sysattr->path);
+ free(fbuf);
+ return -1;
+ }
+ length = read(fd, fbuf, pgsize);
+ if (length < 0) {
+ dprintf("Error reading from attribute %s\n", sysattr->path);
+ close(fd);
+ free(fbuf);
+ return -1;
+ }
+ if (sysattr->len > 0) {
+ if ((sysattr->len == length) &&
+ (!(strncmp(sysattr->value, fbuf, length)))) {
+ close(fd);
+ free(fbuf);
+ return 0;
+ }
+ free(sysattr->value);
+ }
+ sysattr->len = length;
+ close(fd);
+ vbuf = (char *)realloc(fbuf, length+1);
+ if (vbuf == NULL) {
+ dprintf("realloc failed\n");
+ free(fbuf);
+ return -1;
+ }
+ sysattr->value = vbuf;
+
+ return 0;
+}
+
+/**
+ * sysfs_read_attribute_value: given path to attribute, return its value.
+ * values can be up to a pagesize, if buffer is smaller the value will
+ * be truncated.
+ * @attrpath: sysfs path to attribute
+ * @value: buffer to put value
+ * @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)
+{
+ struct sysfs_attribute *attr = NULL;
+ size_t length = 0;
+
+ if (attrpath == NULL || value == NULL || vsize == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ attr = sysfs_open_attribute(attrpath);
+ if (attr == NULL) {
+ dprintf("Invalid attribute path %s\n", attrpath);
+ errno = EINVAL;
+ return -1;
+ }
+ if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) {
+ dprintf("Error reading from attribute %s\n", attrpath);
+ sysfs_close_attribute(attr);
+ return -1;
+ }
+ length = strlen(attr->value);
+ if (length > vsize)
+ dprintf("Value length %d is larger than supplied buffer %d\n",
+ length, vsize);
+ safestrcpymax(value, attr->value, vsize);
+ sysfs_close_attribute(attr);
+
+ return 0;
+}
+
+/**
+ * sysfs_get_value_from_attrbutes: given a linked list of attributes and an
+ * attribute name, return its value
+ * @attr: attribute to search
+ * @name: name to look for
+ * returns char * value - could be NULL
+ */
+char *sysfs_get_value_from_attributes(struct dlist *attr, const char *name)
+{
+ struct sysfs_attribute *cur = NULL;
+
+ if (attr == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ dlist_for_each_data(attr, cur, struct sysfs_attribute) {
+ if (strcmp(cur->name, name) == 0)
+ return cur->value;
+ }
+ return NULL;
+}
+
+/**
+ * sysfs_close_link: closes and cleans up link.
+ * @ln: link to close.
+ */
+void sysfs_close_link(struct sysfs_link *ln)
+{
+ if (ln != NULL)
+ free(ln);
+}
+
+/**
+ * sysfs_close_directory: closes directory, cleans up attributes and links
+ * @sysdir: sysfs_directory to close
+ */
+void sysfs_close_directory(struct sysfs_directory *sysdir)
+{
+ if (sysdir != NULL) {
+ 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;
+ }
+}
+
+/**
+ * alloc_directory: allocates and initializes directory structure
+ * returns struct sysfs_directory with success or NULL with error.
+ */
+static struct sysfs_directory *alloc_directory(void)
+{
+ return (struct sysfs_directory *)
+ calloc(1, sizeof(struct sysfs_directory));
+}
+
+/**
+ * alloc_link: allocates and initializes link structure
+ * returns struct sysfs_link with success or NULL with error.
+ */
+static struct sysfs_link *alloc_link(void)
+{
+ return (struct sysfs_link *)calloc(1, sizeof(struct sysfs_link));
+}
+
+/**
+ * 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.
+ * returns: struct sysfs_directory * with success and NULL on error.
+ */
+struct sysfs_directory *sysfs_open_directory(const char *path)
+{
+ struct sysfs_directory *sdir = NULL;
+
+ if (path == NULL) {
+ 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("Error allocating directory %s\n", path);
+ return NULL;
+ }
+ 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;
+}
+
+/**
+ * sysfs_open_link: opens a sysfs link, creates struct, and returns
+ * @path: path of link to open.
+ * returns: struct sysfs_link * with success and NULL on error.
+ */
+struct sysfs_link *sysfs_open_link(const char *linkpath)
+{
+ struct sysfs_link *ln = NULL;
+
+ if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ln = alloc_link();
+ if (ln == NULL) {
+ 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("Invalid link path %s\n", linkpath);
+ return NULL;
+ }
+
+ return ln;
+}
+
+/**
+ * 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.
+ */
+int sysfs_read_directory(struct sysfs_directory *sysdir)
+{
+ DIR *dir = NULL;
+ struct dirent *dirent = NULL;
+ struct stat astats;
+ 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 ((lstat(file_path, &astats)) != 0) {
+ dprintf("stat failed\n");
+ continue;
+ }
+ 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);
+ }
+ }
+ }
+
+ 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;
+ }
+ sub = sysfs_get_subdirectory(cursub, subname);
+ if (sub != NULL)
+ return sub;
+ }
+ }
+ 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/libsysfs/sysfs_driver.c b/libsysfs/sysfs_driver.c
new file mode 100644
index 0000000..5458129
--- /dev/null
+++ b/libsysfs/sysfs_driver.c
@@ -0,0 +1,289 @@
+/*
+ * sysfs_driver.c
+ *
+ * Driver utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+static void sysfs_close_driver_device(void *device)
+{
+ sysfs_close_device((struct sysfs_device *)device);
+}
+
+/**
+ * sysfs_close_driver: closes driver and deletes device lists too
+ * @driver: driver to close
+ */
+void sysfs_close_driver(struct sysfs_driver *driver)
+{
+ if (driver) {
+ if (driver->devices)
+ dlist_destroy(driver->devices);
+ if (driver->attrlist)
+ dlist_destroy(driver->attrlist);
+ if (driver->module)
+ sysfs_close_module(driver->module);
+ free(driver);
+ }
+}
+
+/**
+ * alloc_driver: allocates and initializes driver
+ * returns struct sysfs_driver with success and NULL with error.
+ */
+static struct sysfs_driver *alloc_driver(void)
+{
+ return (struct sysfs_driver *)calloc(1, sizeof(struct sysfs_driver));
+}
+
+/**
+ * get_driver_bus: gets bus the driver is on
+ * Returns 0 on success and 1 on error
+ */
+static int get_driver_bus(struct sysfs_driver *drv)
+{
+ char drvpath[SYSFS_PATH_MAX], *c = NULL;
+
+ if (!drv) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ safestrcpy(drvpath, drv->path);
+ c = strstr(drvpath, SYSFS_DRIVERS_NAME);
+ if (c == NULL)
+ return 1;
+ *--c = '\0';
+ c = strstr(drvpath, SYSFS_BUS_NAME);
+ if (c == NULL)
+ return 1;
+ c = strstr(c, "/");
+ if (c == NULL)
+ return 1;
+ c++;
+ safestrcpy(drv->bus, c);
+ return 0;
+}
+
+/**
+ * sysfs_get_driver_attr: searches drv's attributes by name
+ * @drv: driver to look through
+ * @name: attribute name to get
+ * returns sysfs_attribute reference with success or NULL with error.
+ */
+struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv,
+ const char *name)
+{
+ if (!drv || !name) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_attribute(drv, (char *)name);
+}
+
+/**
+ * sysfs_get_driver_attributes: gets list of driver attributes
+ * @dev: driver whose attributes list is needed
+ * returns dlist of attributes on success or NULL on error
+ */
+struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *drv)
+{
+ if (!drv) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_dev_attributes_list(drv);
+}
+
+/**
+ * sysfs_open_driver_path: opens and initializes driver structure
+ * @path: path to driver directory
+ * returns struct sysfs_driver with success and NULL with error
+ */
+struct sysfs_driver *sysfs_open_driver_path(const char *path)
+{
+ struct sysfs_driver *driver = NULL;
+
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (sysfs_path_is_dir(path)) {
+ dprintf("Invalid path to driver: %s\n", path);
+ return NULL;
+ }
+ driver = alloc_driver();
+ if (!driver) {
+ dprintf("Error allocating driver at %s\n", path);
+ return NULL;
+ }
+ if (sysfs_get_name_from_path(path, driver->name, SYSFS_NAME_LEN)) {
+ dprintf("Error getting driver name from path\n");
+ free(driver);
+ return NULL;
+ }
+ safestrcpy(driver->path, path);
+ if (sysfs_remove_trailing_slash(driver->path)) {
+ dprintf("Invalid path to driver %s\n", driver->path);
+ sysfs_close_driver(driver);
+ return NULL;
+ }
+ if (get_driver_bus(driver)) {
+ dprintf("Could not get the bus driver is on\n");
+ sysfs_close_driver(driver);
+ return NULL;
+ }
+
+ return driver;
+}
+
+/**
+ * get_driver_path: looks up the bus the driver is on and builds path to
+ * the driver.
+ * @bus: bus on which to search
+ * @drv: driver to look for
+ * @path: buffer to return path to driver
+ * @psize: size of "path"
+ * Returns 0 on success and -1 on error
+ */
+static int get_driver_path(const char *bus, const char *drv,
+ char *path, size_t psize)
+{
+ if (!bus || !drv || !path || psize == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sysfs_get_mnt_path(path, psize)) {
+ dprintf("Error getting sysfs mount path\n");
+ return -1;
+ }
+ safestrcatmax(path, "/", psize);
+ safestrcatmax(path, SYSFS_BUS_NAME, psize);
+ safestrcatmax(path, "/", psize);
+ safestrcatmax(path, bus, psize);
+ safestrcatmax(path, "/", psize);
+ safestrcatmax(path, SYSFS_DRIVERS_NAME, psize);
+ safestrcatmax(path, "/", psize);
+ safestrcatmax(path, drv, psize);
+ return 0;
+}
+
+/**
+ * sysfs_open_driver: open driver by name, given its bus
+ * @bus_name: Name of the bus
+ * @drv_name: Name of the driver
+ * Returns the sysfs_driver reference on success and NULL on failure
+ */
+struct sysfs_driver *sysfs_open_driver(const char *bus_name,
+ const char *drv_name)
+{
+ char path[SYSFS_PATH_MAX];
+ struct sysfs_driver *driver = NULL;
+
+ if (!drv_name || !bus_name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(path, 0, SYSFS_PATH_MAX);
+ if (get_driver_path(bus_name, drv_name, path, SYSFS_PATH_MAX)) {
+ dprintf("Error getting to driver %s\n", drv_name);
+ return NULL;
+ }
+ driver = sysfs_open_driver_path(path);
+ if (!driver) {
+ dprintf("Error opening driver at %s\n", path);
+ return NULL;
+ }
+ return driver;
+}
+
+/**
+ * sysfs_get_driver_devices: gets list of devices that use the driver
+ * @drv: sysfs_driver whose device list is needed
+ * Returns dlist of struct sysfs_device on success and NULL on failure
+ */
+struct dlist *sysfs_get_driver_devices(struct sysfs_driver *drv)
+{
+ char *ln = NULL;
+ struct dlist *linklist = NULL;
+ struct sysfs_device *dev = NULL;
+
+ if (!drv) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ linklist = read_dir_links(drv->path);
+ if (linklist) {
+ dlist_for_each_data(linklist, ln, char) {
+
+ if (!strncmp(ln, SYSFS_MODULE_NAME, strlen(ln)))
+ continue;
+
+ dev = sysfs_open_device(drv->bus, ln);
+ if (!dev) {
+ dprintf("Error opening driver's device\n");
+ sysfs_close_list(linklist);
+ return NULL;
+ }
+ if (!drv->devices) {
+ drv->devices = dlist_new_with_delete
+ (sizeof(struct sysfs_device),
+ sysfs_close_driver_device);
+ if (!drv->devices) {
+ dprintf("Error creating device list\n");
+ sysfs_close_list(linklist);
+ return NULL;
+ }
+ }
+ dlist_unshift_sorted(drv->devices, dev, sort_list);
+ }
+ sysfs_close_list(linklist);
+ }
+ return drv->devices;
+}
+
+/**
+ * sysfs_get_driver_module: gets the module being used by this driver
+ * @drv: sysfs_driver whose "module" is needed
+ * Returns sysfs_module on success and NULL on failure
+ */
+struct sysfs_module *sysfs_get_driver_module(struct sysfs_driver *drv)
+{
+ char path[SYSFS_PATH_MAX], mod_path[SYSFS_PATH_MAX];
+
+ if (!drv) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(path, 0, SYSFS_PATH_MAX);
+ safestrcpy(path, drv->path);
+ safestrcat(path, "/");
+ safestrcat(path, SYSFS_MODULE_NAME);
+ if (!sysfs_path_is_link(path)) {
+ memset(mod_path, 0, SYSFS_PATH_MAX);
+ if (!sysfs_get_link(path, mod_path, SYSFS_PATH_MAX))
+ drv->module = sysfs_open_module_path(mod_path);
+ }
+ return drv->module;
+}
diff --git a/libsysfs/sysfs_module.c b/libsysfs/sysfs_module.c
new file mode 100644
index 0000000..375de1a
--- /dev/null
+++ b/libsysfs/sysfs_module.c
@@ -0,0 +1,279 @@
+/*
+ * sysfs_module.c
+ *
+ * Generic module utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+/**
+ * mod_name_equal: compares modules' name
+ * @a: module_name looking for
+ * @b: sysfs_module being compared
+ */
+static int mod_name_equal(void *a, void *b)
+{
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (strcmp(((char *)a), ((struct sysfs_module *)b)->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * sysfs_close_module: closes a module.
+ * @module: sysfs_module device to close.
+ */
+void sysfs_close_module(struct sysfs_module *module)
+{
+ /*
+ * since both parms and sections are attribs _under_ the
+ * subdir of module->directory, they will get closed by
+ * this single call
+ */
+ if (module != NULL) {
+ if (module->attrlist != NULL)
+ dlist_destroy(module->attrlist);
+ if (module->parmlist != NULL)
+ dlist_destroy(module->parmlist);
+ if (module->sections != NULL)
+ dlist_destroy(module->sections);
+ free(module);
+ }
+}
+
+/**
+ * alloc_module: callocs and initializes new module struct.
+ * returns sysfs_module or NULL.
+ */
+static struct sysfs_module *alloc_module(void)
+{
+ return (struct sysfs_module *)calloc(1, sizeof(struct sysfs_module));
+}
+
+/**
+ * sysfs_open_module_path: Opens and populates the module struct
+ * @path: path to module.
+ * returns struct sysfs_module with success and NULL with error.
+ */
+struct sysfs_module *sysfs_open_module_path(const char *path)
+{
+ struct sysfs_module *mod = NULL;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((sysfs_path_is_dir(path)) != 0) {
+ dprintf("%s is not a valid path to a module\n", path);
+ return NULL;
+ }
+ mod = alloc_module();
+ if (mod == NULL) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ if ((sysfs_get_name_from_path(path, mod->name, SYSFS_NAME_LEN)) != 0) {
+ errno = EINVAL;
+ dprintf("Error getting module name\n");
+ sysfs_close_module(mod);
+ return NULL;
+ }
+
+ safestrcpy(mod->path, path);
+ if ((sysfs_remove_trailing_slash(mod->path)) != 0) {
+ dprintf("Invalid path to module %s\n", mod->path);
+ sysfs_close_module(mod);
+ return NULL;
+ }
+
+ return mod;
+}
+
+/**
+ * sysfs_open_module: opens specific module on a system
+ * returns sysfs_module structure with success or NULL with error.
+ */
+struct sysfs_module *sysfs_open_module(const char *name)
+{
+ struct sysfs_module *mod = NULL;
+ char modpath[SYSFS_PATH_MAX];
+
+ if (name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(modpath, 0, SYSFS_PATH_MAX);
+ if ((sysfs_get_mnt_path(modpath, SYSFS_PATH_MAX)) != 0) {
+ dprintf("Sysfs not supported on this system\n");
+ return NULL;
+ }
+
+ safestrcat(modpath, "/");
+ safestrcat(modpath, SYSFS_MODULE_NAME);
+ safestrcat(modpath, "/");
+ safestrcat(modpath, name);
+
+ if ((sysfs_path_is_dir(modpath)) != 0) {
+ dprintf("Module %s not found on the system\n", name);
+ return NULL;
+ }
+
+ mod = alloc_module();
+ if (mod == NULL) {
+ dprintf("calloc failed\n");
+ return NULL;
+ }
+ safestrcpy(mod->name, name);
+ safestrcpy(mod->path, modpath);
+ if ((sysfs_remove_trailing_slash(mod->path)) != 0) {
+ dprintf("Invalid path to module %s\n", mod->path);
+ sysfs_close_module(mod);
+ return NULL;
+ }
+
+ return mod;
+}
+
+/**
+ * sysfs_get_module_attributes: returns a dlist of attributes for
+ * the requested sysfs_module
+ * @cdev: sysfs_module for which attributes are needed
+ * returns a dlist of attributes if exists, NULL otherwise
+ */
+struct dlist *sysfs_get_module_attributes(struct sysfs_module *module)
+{
+ if (module == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return get_dev_attributes_list(module);
+}
+
+/**
+ * sysfs_get_module_attr: searches module's attributes by name
+ * @module: module to look through
+ * @name: attribute name to get
+ * returns sysfs_attribute reference with success or NULL with error
+ */
+struct sysfs_attribute *sysfs_get_module_attr
+ (struct sysfs_module *module, const char *name)
+{
+ if (module == NULL || name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return get_attribute(module, (char *)name);
+}
+
+/**
+ * sysfs_get_module_parms: Get modules list of parameters
+ * @module: sysfs_module whose parmameter list is required
+ * Returns dlist of parameters on SUCCESS and NULL on error
+ */
+struct dlist *sysfs_get_module_parms(struct sysfs_module *module)
+{
+ char ppath[SYSFS_PATH_MAX];
+
+ if (module == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memset(ppath, 0, SYSFS_PATH_MAX);
+ safestrcpy(ppath, module->path);
+ safestrcat(ppath,"/");
+ safestrcat(ppath, SYSFS_MOD_PARM_NAME);
+
+ return (get_attributes_list(module->parmlist, ppath));
+}
+
+/**
+ * sysfs_get_module_sections: Get the set of sections for this module
+ * @module: sysfs_module whose list of sections is required
+ * Returns dlist of sections on SUCCESS and NULL on error
+ */
+struct dlist *sysfs_get_module_sections(struct sysfs_module *module)
+{
+ char ppath[SYSFS_PATH_MAX];
+
+ if (module == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(ppath, 0, SYSFS_PATH_MAX);
+ safestrcpy(ppath, module->path);
+ safestrcat(ppath,"/");
+ safestrcat(ppath, SYSFS_MOD_SECT_NAME);
+
+ return (get_attributes_list(module->sections, ppath));
+}
+
+/**
+ * sysfs_get_module_parm:
+ * @module: sysfs_module to look through
+ * @parm: name of the parameter to look for
+ * Returns sysfs_attribute * on SUCCESS and NULL on error
+ */
+struct sysfs_attribute *sysfs_get_module_parm
+ (struct sysfs_module *module, const char *parm)
+{
+ struct dlist *parm_list = NULL;
+
+ if (module == NULL || parm == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ parm_list = sysfs_get_module_parms(module);
+ if (parm_list == NULL)
+ return NULL;
+
+ return (struct sysfs_attribute *)dlist_find_custom(parm_list,
+ (void *)parm, mod_name_equal);
+}
+
+/**
+ * sysfs_get_module_section
+ * @module: sysfs_module to look through
+ * @section: name of the section to look for
+ * Returns sysfs_attribute * on SUCCESS and NULL on error
+ */
+struct sysfs_attribute *sysfs_get_module_section
+ (struct sysfs_module *module, const char *section)
+{
+ struct dlist *sect_list = NULL;
+
+ if (module == NULL || section == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ sect_list = sysfs_get_module_sections(module);
+ if (sect_list == NULL)
+ return NULL;
+
+ return (struct sysfs_attribute *)dlist_find_custom(sect_list,
+ (void *)section, mod_name_equal);
+}
diff --git a/libsysfs/sysfs_utils.c b/libsysfs/sysfs_utils.c
new file mode 100644
index 0000000..9ca207c
--- /dev/null
+++ b/libsysfs/sysfs_utils.c
@@ -0,0 +1,307 @@
+/*
+ * sysfs_utils.c
+ *
+ * System utility functions for libsysfs
+ *
+ * Copyright (C) IBM Corp. 2003-2005
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "libsysfs.h"
+#include "sysfs.h"
+
+/**
+ * sysfs_remove_trailing_slash: Removes any trailing '/' in the given path
+ * @path: Path to look for the trailing '/'
+ * Returns 0 on success 1 on error
+ */
+int sysfs_remove_trailing_slash(char *path)
+{
+ size_t len;
+
+ if (!path) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ len = strlen(path);
+ while (len > 0 && path[len-1] == '/')
+ path[--len] = '\0';
+ return 0;
+}
+
+/*
+ * sysfs_get_mnt_path: Gets the sysfs mount point.
+ * @mnt_path: place to put "sysfs" mount point
+ * @len: size of mnt_path
+ * returns 0 with success and -1 with error.
+ */
+int sysfs_get_mnt_path(char *mnt_path, size_t len)
+{
+ static char sysfs_path[SYSFS_PATH_MAX] = "";
+ const char *sysfs_path_env;
+
+ if (len == 0 || mnt_path == NULL)
+ return -1;
+
+ /* evaluate only at the first call */
+ if (sysfs_path[0] == '\0') {
+ /* possible overrride of real mount path */
+ sysfs_path_env = getenv(SYSFS_PATH_ENV);
+ if (sysfs_path_env != NULL) {
+ safestrcpymax(mnt_path, sysfs_path_env, len);
+ sysfs_remove_trailing_slash(mnt_path);
+ return 0;
+ }
+ safestrcpymax(mnt_path, SYSFS_MNT_PATH, len);
+ }
+
+ return 0;
+}
+
+/**
+ * sysfs_get_name_from_path: returns last name from a "/" delimited path
+ * @path: path to get name from
+ * @name: where to put name
+ * @len: size of name
+ */
+int sysfs_get_name_from_path(const char *path, char *name, size_t len)
+{
+ char tmp[SYSFS_PATH_MAX];
+ char *n = NULL;
+
+ if (!path || !name || len == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ memset(tmp, 0, SYSFS_PATH_MAX);
+ safestrcpy(tmp, path);
+ n = strrchr(tmp, '/');
+ if (n == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (*(n+1) == '\0') {
+ *n = '\0';
+ n = strrchr(tmp, '/');
+ if (n == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ n++;
+ safestrcpymax(name, n, len);
+ return 0;
+}
+
+/**
+ * sysfs_get_link: returns link source
+ * @path: symbolic link's path
+ * @target: where to put name
+ * @len: size of name
+ */
+int sysfs_get_link(const char *path, char *target, size_t len)
+{
+ char devdir[SYSFS_PATH_MAX];
+ char linkpath[SYSFS_PATH_MAX];
+ char temp_path[SYSFS_PATH_MAX];
+ char *d = NULL, *s = NULL;
+ int slashes = 0, count = 0;
+
+ if (!path || !target || len == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(devdir, 0, SYSFS_PATH_MAX);
+ memset(linkpath, 0, SYSFS_PATH_MAX);
+ memset(temp_path, 0, SYSFS_PATH_MAX);
+ safestrcpy(devdir, path);
+
+ if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) {
+ return -1;
+ }
+ d = linkpath;
+ /*
+ * Three cases here:
+ * 1. relative path => format ../..
+ * 2. absolute path => format /abcd/efgh
+ * 3. relative path _from_ this dir => format abcd/efgh
+ */
+ switch (*d) {
+ case '.':
+ /*
+ * handle the case where link is of type ./abcd/xxx
+ */
+ safestrcpy(temp_path, devdir);
+ if (*(d+1) == '/')
+ d += 2;
+ else if (*(d+1) == '.')
+ goto parse_path;
+ s = strrchr(temp_path, '/');
+ if (s != NULL) {
+ *(s+1) = '\0';
+ safestrcat(temp_path, d);
+ } else {
+ safestrcpy(temp_path, d);
+ }
+ safestrcpymax(target, temp_path, len);
+ break;
+ /*
+ * relative path, getting rid of leading "../.."
+ */
+parse_path:
+ while (*d == '/' || *d == '.') {
+ if (*d == '/')
+ slashes++;
+ d++;
+ }
+ d--;
+ s = &devdir[strlen(devdir)-1];
+ while (s != NULL && count != (slashes+1)) {
+ s--;
+ if (*s == '/')
+ count++;
+ }
+ safestrcpymax(s, d, (SYSFS_PATH_MAX-strlen(devdir)));
+ safestrcpymax(target, devdir, len);
+ break;
+ case '/':
+ /* absolute path - copy as is */
+ safestrcpymax(target, linkpath, len);
+ break;
+ default:
+ /* relative path from this directory */
+ safestrcpy(temp_path, devdir);
+ s = strrchr(temp_path, '/');
+ if (s != NULL) {
+ *(s+1) = '\0';
+ safestrcat(temp_path, linkpath);
+ } else {
+ safestrcpy(temp_path, linkpath);
+ }
+ safestrcpymax(target, temp_path, len);
+ }
+ return 0;
+}
+
+/**
+ * sysfs_close_list: generic list free routine
+ * @list: dlist to free
+ * Returns nothing
+ */
+void sysfs_close_list(struct dlist *list)
+{
+ if (list)
+ dlist_destroy(list);
+}
+
+/**
+ * sysfs_open_directory_list: gets a list of all directories under "path"
+ * @path: path to read
+ * Returns a dlist of supported names or NULL no directories (errno is set
+ * in case of error
+ */
+struct dlist *sysfs_open_directory_list(const char *path)
+{
+ if (!path)
+ return NULL;
+
+ return (read_dir_subdirs(path));
+}
+
+/**
+ * sysfs_open_link_list: gets a list of all links under "path"
+ * @path: path to read
+ * Returns a dlist of supported links or NULL no directories (errno is set
+ * in case of error
+ */
+struct dlist *sysfs_open_link_list(const char *path)
+{
+ if (!path)
+ return NULL;
+
+ return (read_dir_links(path));
+}
+
+/**
+ * sysfs_path_is_dir: Check if the path supplied points to a directory
+ * @path: path to validate
+ * Returns 0 if path points to dir, 1 otherwise
+ */
+int sysfs_path_is_dir(const char *path)
+{
+ struct stat astats;
+
+ if (!path) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((lstat(path, &astats)) != 0) {
+ dprintf("stat() failed\n");
+ return 1;
+ }
+ if (S_ISDIR(astats.st_mode))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * sysfs_path_is_link: Check if the path supplied points to a link
+ * @path: path to validate
+ * Returns 0 if path points to link, 1 otherwise
+ */
+int sysfs_path_is_link(const char *path)
+{
+ struct stat astats;
+
+ if (!path) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((lstat(path, &astats)) != 0) {
+ dprintf("stat() failed\n");
+ return 1;
+ }
+ if (S_ISLNK(astats.st_mode))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * sysfs_path_is_file: Check if the path supplied points to a file
+ * @path: path to validate
+ * Returns 0 if path points to file, 1 otherwise
+ */
+int sysfs_path_is_file(const char *path)
+{
+ struct stat astats;
+
+ if (!path) {
+ errno = EINVAL;
+ return 1;
+ }
+ if ((lstat(path, &astats)) != 0) {
+ dprintf("stat() failed\n");
+ return 1;
+ }
+ if (S_ISREG(astats.st_mode))
+ return 0;
+
+ return 1;
+}