<div dir="ltr">Rebased (.gitignore) and applied.</div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Feb 24, 2017 at 1:50 PM, Gris Ge <span dir="ltr"><<a href="mailto:fge@redhat.com" target="_blank">fge@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Features:<br>
<br>
* Use mpath_cmd.h for IPC connection and use output of 'show maps json'.<br>
* Library user guide will be 'man 3 libdmmp.h'.<br>
* Every public function has its own manpage in section 3 which is<br>
generated by linux 'kernel-doc' tool.<br>
<br>
Usage:<br>
<br>
make -j5<br>
sudo make install \<br>
bindir=/usr/sbin/ \<br>
syslibdir=/usr/lib64/ \<br>
libdir=/usr/lib64/multipath \<br>
rcdir=/etc/rc.d/init.d \<br>
unitdir=/usr/lib/systemd/<wbr>system \<br>
includedir=/usr/include<br>
make -C libdmmp check<br>
make -C libdmmp speed_test<br>
<br>
man libdmmp.h<br>
man dmmp_mpath_array_get<br>
man <dmmp function name><br>
<br>
Performance:<br>
<br>
* 10k scsi_debug sdX with 2 disks per mpath (i7-6820HQ 16GiB RAM):<br>
$ make -C libdmmp speed_test<br>
Got 5000 mpath<br>
real 3.22<br>
user 0.15<br>
sys 0.01<br>
<br>
Misc:<br>
* Developer note is libdmmp/DEV_NOTES.<br>
<br>
Changes since V4:<br>
<br>
* Add new function dmmp_mpath_kdev_name_get() to query the '/dev/dm-01' for<br>
mpath.<br>
* Updated manpages.<br>
* Rebased to current master ea4367159d32444e48a409a4f1c4f1<wbr>8324b737a9.<br>
<br>
Signed-off-by: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
---<br>
.gitignore | 4 +<br>
Makefile | 1 +<br>
Makefile.inc | 3 +<br>
libdmmp/DEV_NOTES | 41 +<br>
libdmmp/Makefile | 84 +<br>
libdmmp/docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a> | 28 +<br>
libdmmp/docs/kernel-doc | 3156 ++++++++++++++++++++++++++++++<wbr>+++++++<br>
libdmmp/docs/libdmmp.h.3 | 113 ++<br>
libdmmp/docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a> | 40 +<br>
libdmmp/libdmmp.c | 286 ++++<br>
libdmmp/<a href="http://libdmmp.pc.in" rel="noreferrer" target="_blank">libdmmp.pc.in</a> | 9 +<br>
libdmmp/libdmmp/libdmmp.h | 653 ++++++++<br>
libdmmp/libdmmp_misc.c | 87 +<br>
libdmmp/libdmmp_mp.c | 159 ++<br>
libdmmp/libdmmp_path.c | 115 ++<br>
libdmmp/libdmmp_pg.c | 208 +++<br>
libdmmp/libdmmp_private.h | 208 +++<br>
libdmmp/test/Makefile | 30 +<br>
libdmmp/test/libdmmp_speed_<wbr>test.c | 49 +<br>
libdmmp/test/libdmmp_test.c | 147 ++<br>
20 files changed, 5421 insertions(+)<br>
create mode 100644 libdmmp/DEV_NOTES<br>
create mode 100644 libdmmp/Makefile<br>
create mode 100644 libdmmp/docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a><br>
create mode 100644 libdmmp/docs/kernel-doc<br>
create mode 100644 libdmmp/docs/libdmmp.h.3<br>
create mode 100644 libdmmp/docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a><br>
create mode 100644 libdmmp/libdmmp.c<br>
create mode 100644 libdmmp/<a href="http://libdmmp.pc.in" rel="noreferrer" target="_blank">libdmmp.pc.in</a><br>
create mode 100644 libdmmp/libdmmp/libdmmp.h<br>
create mode 100644 libdmmp/libdmmp_misc.c<br>
create mode 100644 libdmmp/libdmmp_mp.c<br>
create mode 100644 libdmmp/libdmmp_path.c<br>
create mode 100644 libdmmp/libdmmp_pg.c<br>
create mode 100644 libdmmp/libdmmp_private.h<br>
create mode 100644 libdmmp/test/Makefile<br>
create mode 100644 libdmmp/test/libdmmp_speed_<wbr>test.c<br>
create mode 100644 libdmmp/test/libdmmp_test.c<br>
<br>
diff --git a/.gitignore b/.gitignore<br>
index aee4ece..f0fbd7e 100644<br>
--- a/.gitignore<br>
+++ b/.gitignore<br>
@@ -12,3 +12,7 @@ mpathpersist/mpathpersist<br>
.nfs*<br>
*.swp<br>
*.patch<br>
+libdmmp/docs/man/*.3.gz<br>
+libdmmp/*.so.*<br>
+libdmmp/test/libdmmp_test<br>
+libdmmp/test/libdmmp_speed_<wbr>test<br>
diff --git a/Makefile b/Makefile<br>
index 228d9ac..9f8bf77 100644<br>
--- a/Makefile<br>
+++ b/Makefile<br>
@@ -30,6 +30,7 @@ BUILDDIRS = \<br>
libmultipath/prioritizers \<br>
libmultipath/checkers \<br>
libmpathpersist \<br>
+ libdmmp \<br>
multipath \<br>
multipathd \<br>
mpathpersist \<br>
diff --git a/Makefile.inc b/Makefile.inc<br>
index 8f8e53e..93d8e34 100644<br>
--- a/Makefile.inc<br>
+++ b/Makefile.inc<br>
@@ -55,6 +55,9 @@ unitdir = $(prefix)/$(SYSTEMDPATH)/<wbr>systemd/system<br>
mpathpersistdir = $(TOPDIR)/libmpathpersist<br>
mpathcmddir = $(TOPDIR)/libmpathcmd<br>
thirdpartydir = $(TOPDIR)/third-party<br>
+libdmmpdir = $(TOPDIR)/libdmmp<br>
+includedir = $(prefix)/usr/include<br>
+pkgconfdir = $(prefix)/usr/share/pkgconfig<br>
<br>
GZIP = gzip -9 -c<br>
RM = rm -f<br>
diff --git a/libdmmp/DEV_NOTES b/libdmmp/DEV_NOTES<br>
new file mode 100644<br>
index 0000000..220a9f4<br>
--- /dev/null<br>
+++ b/libdmmp/DEV_NOTES<br>
@@ -0,0 +1,41 @@<br>
+== Planed features ==<br>
+ * Expose all properties used by /usr/bin/multipath<br>
+<br>
+== Code style ==<br>
+ * Keep things as simple as possible.<br>
+ * Linux Kernel code style.<br>
+ * Don't use typedef.<br>
+ * Don't use enum.<br>
+ * We are not smarter than API user, so don't create wrapping function like:<br>
+<br>
+ ```<br>
+ dmmp_mpath_search_by_id(struct dmmp_context *ctx,<br>
+ struct dmmp_mpath **dmmp_mp,<br>
+ uint32_t dmmp_mp_count, const char *id)<br>
+<br>
+ dmmp_path_group_id_search(<wbr>struct dmmp_mpath *dmmp_mp,<br>
+ const char *blk_name)<br>
+ ```<br>
+ * The performance is the same for query single mpath and query all mpaths,<br>
+ so no `dmmp_mpath_of_wwid(struct dmmp_context *ctx, const char *wwid)` yet.<br>
+<br>
+== Naming scheme ==<br>
+ * Public constants should be named as `DMMP_XXX_YYY`.<br>
+ * Public functions should be named as `dmmp_<noun>_<verb>`.<br>
+ * Private constants should be named as `_DMMP_XXX_YYY`.<br>
+ * Private functions should be named as `_dmmp_<noun>_<verb>`.<br>
+<br>
+== Code Layout ==<br>
+ * libdmmp_private.h<br>
+ Internal functions or macros.<br>
+ * libdmmp.c<br>
+ Handling multipathd IPC and generate dmmp_context and<br>
+ dmmp_mpath_array_get().<br>
+ * libdmmp_mp.c<br>
+ For `struct dmmp_mpath`<br>
+ * libdmmp_pg.c<br>
+ For `struct dmmp_path_group`<br>
+ * libdmmp_path.c<br>
+ For `struct dmmp_path`<br>
+ * libdmmp_misc.c<br>
+ Misc functions.<br>
diff --git a/libdmmp/Makefile b/libdmmp/Makefile<br>
new file mode 100644<br>
index 0000000..c98ae67<br>
--- /dev/null<br>
+++ b/libdmmp/Makefile<br>
@@ -0,0 +1,84 @@<br>
+# Makefile<br>
+#<br>
+# Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+# Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+#<br>
+include ../Makefile.inc<br>
+<br>
+LIBDMMP_VERSION=0.1.0<br>
+SONAME=$(LIBDMMP_VERSION)<br>
+DEVLIB = libdmmp.so<br>
+LIBS = $(DEVLIB).$(SONAME)<br>
+LIBDEPS = -pthread<br>
+PKGFILE = libdmmp.pc<br>
+EXTRA_MAN_FILES = libdmmp.h.3<br>
+HEADERS = libdmmp/libdmmp.h<br>
+OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o<br>
+<br>
+CFLAGS += -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \<br>
+ $(shell pkg-config --cflags json-c)<br>
+LDFLAGS += $(shell pkg-config --libs json-c) -L$(mpathcmddir) -lmpathcmd<br>
+<br>
+all: $(LIBS) doc<br>
+<br>
+$(LIBS): $(OBJS)<br>
+ $(CC) $(LDFLAGS) $(SHARED_FLAGS) \<br>
+ -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS)<br>
+ $(LN) $@ $(DEVLIB)<br>
+<br>
+install:<br>
+ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)<br>
+ $(INSTALL_PROGRAM) -m 644 -D \<br>
+ $(HEADERS) $(DESTDIR)/$(includedir)/$(<wbr>HEADERS)<br>
+ $(LN) $(LIBS) $(DESTDIR)/$(syslibdir)/$(<wbr>DEVLIB)<br>
+ $(INSTALL_PROGRAM) -m 644 -D \<br>
+ $(PKGFILE).in $(DESTDIR)/$(pkgconfdir)/$(<wbr>PKGFILE)<br>
+ perl -i -pe 's|__VERSION__|$(LIBDMMP_<wbr>VERSION)|g' \<br>
+ $(DESTDIR)/$(pkgconfdir)/$(<wbr>PKGFILE)<br>
+ perl -i -pe 's|__LIBDIR__|$(syslibdir)|g' \<br>
+ $(DESTDIR)/$(pkgconfdir)/$(<wbr>PKGFILE)<br>
+ perl -i -pe 's|__INCLUDEDIR__|$(<wbr>includedir)|g' \<br>
+ $(DESTDIR)/$(pkgconfdir)/$(<wbr>PKGFILE)<br>
+ @for file in docs/man/*.3.gz; do \<br>
+ $(INSTALL_PROGRAM) -m 644 -D \<br>
+ $$file \<br>
+ $(DESTDIR)/$(man3dir)/ || exit $?; \<br>
+ done<br>
+<br>
+uninstall:<br>
+ $(RM) $(DESTDIR)$(syslibdir)/$(LIBS)<br>
+ $(RM) $(DESTDIR)$(includedir)/$(<wbr>HEADERS)<br>
+ $(RM) $(DESTDIR)/$(syslibdir)/$(<wbr>DEVLIB)<br>
+ @for file in $(DESTDIR)/$(man3dir)/dmmp_*; do \<br>
+ $(RM) $$file; \<br>
+ done<br>
+ $(RM) $(DESTDIR)$(man3dir)/libdmmp.<wbr>h*<br>
+<br>
+clean:<br>
+ $(RM) core *.a *.o *.gz *.so *.so.*<br>
+ $(RM) docs/man/*.3.gz<br>
+ $(MAKE) -C test clean<br>
+<br>
+check: all<br>
+ $(MAKE) -C test check<br>
+<br>
+speed_test: all<br>
+ $(MAKE) -C test speed_test<br>
+<br>
+doc: docs/man/$(EXTRA_MAN_FILES).gz<br>
+<br>
+TEMPFILE := $(shell mktemp)<br>
+<br>
+docs/man/$(EXTRA_MAN_FILES).<wbr>gz: $(HEADERS)<br>
+ @for file in $(EXTRA_MAN_FILES); do \<br>
+ $(INSTALL_PROGRAM) -v -m 644 -D docs/$$file docs/man/$$file; \<br>
+ done<br>
+ cat $(HEADERS) | \<br>
+ perl docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a> > $(TEMPFILE)<br>
+ perl docs/kernel-doc -man $(TEMPFILE) | \<br>
+ perl docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a> docs/man<br>
+ -rm -f $(TEMPFILE)<br>
+ @for file in docs/man/*.3; do \<br>
+ gzip -f $$file; \<br>
+ done<br>
+ find docs/man -type f -name \*[0-9].gz<br>
diff --git a/libdmmp/docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a> b/libdmmp/docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a><br>
new file mode 100644<br>
index 0000000..9a9a4ce<br>
--- /dev/null<br>
+++ b/libdmmp/docs/<a href="http://doc-preclean.pl" rel="noreferrer" target="_blank">doc-preclean.pl</a><br>
@@ -0,0 +1,28 @@<br>
+#!/usr/bin/perl<br>
+# Copyright (C) 2016 Red Hat, Inc.<br>
+#<br>
+# This program is free software: you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>
+# the Free Software Foundation, either version 3 of the License, or<br>
+# (at your option) any later version.<br>
+#<br>
+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+# GNU General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU General Public License<br>
+# along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+#<br>
+# Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+<br>
+use strict;<br>
+<br>
+my @REMOVE_KEY_LIST=("DMMP_DLL_<wbr>EXPORT");<br>
+<br>
+while (<>) {<br>
+ for my $key (@REMOVE_KEY_LIST) {<br>
+ (s/$key//g);<br>
+ }<br>
+ print;<br>
+}<br>
diff --git a/libdmmp/docs/kernel-doc b/libdmmp/docs/kernel-doc<br>
new file mode 100644<br>
index 0000000..030fc63<br>
--- /dev/null<br>
+++ b/libdmmp/docs/kernel-doc<br>
@@ -0,0 +1,3156 @@<br>
+#!/usr/bin/perl -w<br>
+<br>
+use strict;<br>
+<br>
+## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##<br>
+## Copyright (C) 2000, 1 Tim Waugh <<a href="mailto:twaugh@redhat.com">twaugh@redhat.com</a>> ##<br>
+## Copyright (C) 2001 Simon Huggins ##<br>
+## Copyright (C) 2005-2012 Randy Dunlap ##<br>
+## Copyright (C) 2012 Dan Luedtke ##<br>
+## ##<br>
+## #define enhancements by Armin Kuster <<a href="mailto:akuster@mvista.com">akuster@mvista.com</a>> ##<br>
+## Copyright (c) 2000 MontaVista Software, Inc. ##<br>
+## ##<br>
+## This software falls under the GNU General Public License. ##<br>
+## Please read the COPYING file for more information ##<br>
+<br>
+# 18/01/2001 - Cleanups<br>
+# Functions prototyped as foo(void) same as foo()<br>
+# Stop eval'ing where we don't need to.<br>
+# -- <a href="mailto:huggie@earth.li">huggie@earth.li</a><br>
+<br>
+# 27/06/2001 - Allowed whitespace after initial "/**" and<br>
+# allowed comments before function declarations.<br>
+# -- Christian Kreibich <<a href="mailto:ck@whoop.org">ck@whoop.org</a>><br>
+<br>
+# Still to do:<br>
+# - add perldoc documentation<br>
+# - Look more closely at some of the scarier bits :)<br>
+<br>
+# 26/05/2001 - Support for separate source and object trees.<br>
+# Return error code.<br>
+# Keith Owens <<a href="mailto:kaos@ocs.com.au">kaos@ocs.com.au</a>><br>
+<br>
+# 23/09/2001 - Added support for typedefs, structs, enums and unions<br>
+# Support for Context section; can be terminated using empty line<br>
+# Small fixes (like spaces vs. \s in regex)<br>
+# -- Tim Jansen <<a href="mailto:tim@tjansen.de">tim@tjansen.de</a>><br>
+<br>
+# 25/07/2012 - Added support for HTML5<br>
+# -- Dan Luedtke <<a href="mailto:mail@danrl.de">mail@danrl.de</a>><br>
+<br>
+sub usage {<br>
+ my $message = <<"EOF";<br>
+Usage: $0 [OPTION ...] FILE ...<br>
+<br>
+Read C language source or header FILEs, extract embedded documentation comments,<br>
+and print formatted documentation to standard output.<br>
+<br>
+The documentation comments are identified by "/**" opening comment mark. See<br>
+Documentation/kernel-doc-<wbr>nano-HOWTO.txt for the documentation comment syntax.<br>
+<br>
+Output format selection (mutually exclusive):<br>
+ -docbook Output DocBook format.<br>
+ -html Output HTML format.<br>
+ -html5 Output HTML5 format.<br>
+ -list Output symbol list format. This is for use by docproc.<br>
+ -man Output troff manual page format. This is the default.<br>
+ -rst Output reStructuredText format.<br>
+ -text Output plain text format.<br>
+<br>
+Output selection (mutually exclusive):<br>
+ -export Only output documentation for symbols that have been<br>
+ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()<br>
+ in any input FILE or -export-file FILE.<br>
+ -internal Only output documentation for symbols that have NOT been<br>
+ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()<br>
+ in any input FILE or -export-file FILE.<br>
+ -function NAME Only output documentation for the given function(s)<br>
+ or DOC: section title(s). All other functions and DOC:<br>
+ sections are ignored. May be specified multiple times.<br>
+ -nofunction NAME Do NOT output documentation for the given function(s);<br>
+ only output documentation for the other functions and<br>
+ DOC: sections. May be specified multiple times.<br>
+<br>
+Output selection modifiers:<br>
+ -no-doc-sections Do not output DOC: sections.<br>
+ -enable-lineno Enable output of #define LINENO lines. Only works with<br>
+ reStructuredText format.<br>
+ -export-file FILE Specify an additional FILE in which to look for<br>
+ EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with<br>
+ -export or -internal. May be specified multiple times.<br>
+<br>
+Other parameters:<br>
+ -v Verbose output, more warnings and other information.<br>
+ -h Print this help.<br>
+<br>
+EOF<br>
+ print $message;<br>
+ exit 1;<br>
+}<br>
+<br>
+#<br>
+# format of comments.<br>
+# In the following table, (...)? signifies optional structure.<br>
+# (...)* signifies 0 or more structure elements<br>
+# /**<br>
+# * function_name(:)? (- short description)?<br>
+# (* @parameterx: (description of parameter x)?)*<br>
+# (* a blank line)?<br>
+# * (Description:)? (Description of function)?<br>
+# * (section header: (section description)? )*<br>
+# (*)?*/<br>
+#<br>
+# So .. the trivial example would be:<br>
+#<br>
+# /**<br>
+# * my_function<br>
+# */<br>
+#<br>
+# If the Description: header tag is omitted, then there must be a blank line<br>
+# after the last parameter specification.<br>
+# e.g.<br>
+# /**<br>
+# * my_function - does my stuff<br>
+# * @my_arg: its mine damnit<br>
+# *<br>
+# * Does my stuff explained.<br>
+# */<br>
+#<br>
+# or, could also use:<br>
+# /**<br>
+# * my_function - does my stuff<br>
+# * @my_arg: its mine damnit<br>
+# * Description: Does my stuff explained.<br>
+# */<br>
+# etc.<br>
+#<br>
+# Besides functions you can also write documentation for structs, unions,<br>
+# enums and typedefs. Instead of the function name you must write the name<br>
+# of the declaration; the struct/union/enum/typedef must always precede<br>
+# the name. Nesting of declarations is not supported.<br>
+# Use the argument mechanism to document members or constants.<br>
+# e.g.<br>
+# /**<br>
+# * struct my_struct - short description<br>
+# * @a: first member<br>
+# * @b: second member<br>
+# *<br>
+# * Longer description<br>
+# */<br>
+# struct my_struct {<br>
+# int a;<br>
+# int b;<br>
+# /* private: */<br>
+# int c;<br>
+# };<br>
+#<br>
+# All descriptions can be multiline, except the short function description.<br>
+#<br>
+# For really longs structs, you can also describe arguments inside the<br>
+# body of the struct.<br>
+# eg.<br>
+# /**<br>
+# * struct my_struct - short description<br>
+# * @a: first member<br>
+# * @b: second member<br>
+# *<br>
+# * Longer description<br>
+# */<br>
+# struct my_struct {<br>
+# int a;<br>
+# int b;<br>
+# /**<br>
+# * @c: This is longer description of C<br>
+# *<br>
+# * You can use paragraphs to describe arguments<br>
+# * using this method.<br>
+# */<br>
+# int c;<br>
+# };<br>
+#<br>
+# This should be use only for struct/enum members.<br>
+#<br>
+# You can also add additional sections. When documenting kernel functions you<br>
+# should document the "Context:" of the function, e.g. whether the functions<br>
+# can be called form interrupts. Unlike other sections you can end it with an<br>
+# empty line.<br>
+# A non-void function should have a "Return:" section describing the return<br>
+# value(s).<br>
+# Example-sections should contain the string EXAMPLE so that they are marked<br>
+# appropriately in DocBook.<br>
+#<br>
+# Example:<br>
+# /**<br>
+# * user_function - function that can only be called in user context<br>
+# * @a: some argument<br>
+# * Context: !in_interrupt()<br>
+# *<br>
+# * Some description<br>
+# * Example:<br>
+# * user_function(22);<br>
+# */<br>
+# ...<br>
+#<br>
+#<br>
+# All descriptive text is further processed, scanning for the following special<br>
+# patterns, which are highlighted appropriately.<br>
+#<br>
+# 'funcname()' - function<br>
+# '$ENVVAR' - environmental variable<br>
+# '&struct_name' - name of a structure (up to two words including 'struct')<br>
+# '@parameter' - name of a parameter<br>
+# '%CONST' - name of a constant.<br>
+<br>
+## init lots of data<br>
+<br>
+<br>
+my $errors = 0;<br>
+my $warnings = 0;<br>
+my $anon_struct_union = 0;<br>
+<br>
+# match expressions used to find embedded type information<br>
+my $type_constant = '\%([-_\w]+)';<br>
+my $type_func = '(\w+)\(\)';<br>
+my $type_param = '\@(\w+(\.\.\.)?)';<br>
+my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params<br>
+my $type_struct = '\&((struct\s*)*[_\w]+)';<br>
+my $type_struct_xml = '\\&((struct\s*)*[_\w]+)';<br>
+my $type_env = '(\$\w+)';<br>
+my $type_enum_full = '\&(enum)\s*([_\w]+)';<br>
+my $type_struct_full = '\&(struct)\s*([_\w]+)';<br>
+my $type_typedef_full = '\&(typedef)\s*([_\w]+)';<br>
+my $type_union_full = '\&(union)\s*([_\w]+)';<br>
+my $type_member = '\&([_\w]+)((\.|->)[_\w]+)';<br>
+my $type_member_func = $type_member . '\(\)';<br>
+<br>
+# Output conversion substitutions.<br>
+# One for each output format<br>
+<br>
+# these work fairly well<br>
+my @highlights_html = (<br>
+ [$type_constant, "<i>\$1</i>"],<br>
+ [$type_func, "<b>\$1</b>"],<br>
+ [$type_struct_xml, "<i>\$1</i>"],<br>
+ [$type_env, "<b><i>\$1</i></b>"],<br>
+ [$type_param, "<tt><b>\$1</b></tt>"]<br>
+ );<br>
+my $local_lt = "\\\\\\\\lt:";<br>
+my $local_gt = "\\\\\\\\gt:";<br>
+my $blankline_html = $local_lt . "p" . $local_gt; # was "<p>"<br>
+<br>
+# html version 5<br>
+my @highlights_html5 = (<br>
+ [$type_constant, "<span class=\"const\">\$1</span>"],<br>
+ [$type_func, "<span class=\"func\">\$1</span>"],<br>
+ [$type_struct_xml, "<span class=\"struct\">\$1</span>"],<br>
+ [$type_env, "<span class=\"env\">\$1</span>"],<br>
+ [$type_param, "<span class=\"param\">\$1</span>]"]<br>
+ );<br>
+my $blankline_html5 = $local_lt . "br /" . $local_gt;<br>
+<br>
+# XML, docbook format<br>
+my @highlights_xml = (<br>
+ ["([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>"],<br>
+ [$type_constant, "<constant>\$1</constant>"],<br>
+ [$type_struct_xml, "<structname>\$1</structname>"<wbr>],<br>
+ [$type_param, "<parameter>\$1</parameter>"],<br>
+ [$type_func, "<function>\$1</function>"],<br>
+ [$type_env, "<envar>\$1</envar>"]<br>
+ );<br>
+my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n";<br>
+<br>
+# gnome, docbook format<br>
+my @highlights_gnome = (<br>
+ [$type_constant, "<replaceable class=\"option\">\$1</<wbr>replaceable>"],<br>
+ [$type_func, "<function>\$1</function>"],<br>
+ [$type_struct, "<structname>\$1</structname>"<wbr>],<br>
+ [$type_env, "<envar>\$1</envar>"],<br>
+ [$type_param, "<parameter>\$1</parameter>" ]<br>
+ );<br>
+my $blankline_gnome = "</para><para>\n";<br>
+<br>
+# these are pretty rough<br>
+my @highlights_man = (<br>
+ [$type_constant, "\$1"],<br>
+ [$type_func, "\\\\fB\$1\\\\fP"],<br>
+ [$type_struct, "\\\\fI\$1\\\\fP"],<br>
+ [$type_param, "\\\\fI\$1\\\\fP"]<br>
+ );<br>
+my $blankline_man = "";<br>
+<br>
+# text-mode<br>
+my @highlights_text = (<br>
+ [$type_constant, "\$1"],<br>
+ [$type_func, "\$1"],<br>
+ [$type_struct, "\$1"],<br>
+ [$type_param, "\$1"]<br>
+ );<br>
+my $blankline_text = "";<br>
+<br>
+# rst-mode<br>
+my @highlights_rst = (<br>
+ [$type_constant, "``\$1``"],<br>
+ # Note: need to escape () to avoid func matching later<br>
+ [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\<wbr>\) <\$1>`"],<br>
+ [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"],<br>
+ [$type_fp_param, "**\$1\\\\(\\\\)**"],<br>
+ [$type_func, "\\:c\\:func\\:`\$1()`"],<br>
+ [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],<br>
+ [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],<br>
+ [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],<br>
+ [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"],<br>
+ # in rst this can refer to any type<br>
+ [$type_struct, "\\:c\\:type\\:`\$1`"],<br>
+ [$type_param, "**\$1**"]<br>
+ );<br>
+my $blankline_rst = "\n";<br>
+<br>
+# list mode<br>
+my @highlights_list = (<br>
+ [$type_constant, "\$1"],<br>
+ [$type_func, "\$1"],<br>
+ [$type_struct, "\$1"],<br>
+ [$type_param, "\$1"]<br>
+ );<br>
+my $blankline_list = "";<br>
+<br>
+# read arguments<br>
+if ($#ARGV == -1) {<br>
+ usage();<br>
+}<br>
+<br>
+my $kernelversion;<br>
+my $dohighlight = "";<br>
+<br>
+my $verbose = 0;<br>
+my $output_mode = "man";<br>
+my $output_preformatted = 0;<br>
+my $no_doc_sections = 0;<br>
+my $enable_lineno = 0;<br>
+my @highlights = @highlights_man;<br>
+my $blankline = $blankline_man;<br>
+my $modulename = "Kernel API";<br>
+<br>
+use constant {<br>
+ OUTPUT_ALL => 0, # output all symbols and doc sections<br>
+ OUTPUT_INCLUDE => 1, # output only specified symbols<br>
+ OUTPUT_EXCLUDE => 2, # output everything except specified symbols<br>
+ OUTPUT_EXPORTED => 3, # output exported symbols<br>
+ OUTPUT_INTERNAL => 4, # output non-exported symbols<br>
+};<br>
+my $output_selection = OUTPUT_ALL;<br>
+my $show_not_found = 0;<br>
+<br>
+my @export_file_list;<br>
+<br>
+my @build_time;<br>
+if (defined($ENV{'KBUILD_BUILD_<wbr>TIMESTAMP'}) &&<br>
+ (my $seconds = `date -d"${ENV{'KBUILD_BUILD_<wbr>TIMESTAMP'}}" +%s`) ne '') {<br>
+ @build_time = gmtime($seconds);<br>
+} else {<br>
+ @build_time = localtime;<br>
+}<br>
+<br>
+my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',<br>
+ 'July', 'August', 'September', 'October',<br>
+ 'November', 'December')[$build_time[4]] .<br>
+ " " . ($build_time[5]+1900);<br>
+<br>
+# Essentially these are globals.<br>
+# They probably want to be tidied up, made more localised or something.<br>
+# CAVEAT EMPTOR! Some of the others I localised may not want to be, which<br>
+# could cause "use of undefined value" or other bugs.<br>
+my ($function, %function_table, %parametertypes, $declaration_purpose);<br>
+my $declaration_start_line;<br>
+my ($type, $declaration_name, $return_type);<br>
+my ($newsection, $newcontents, $prototype, $brcount, %source_map);<br>
+<br>
+if (defined($ENV{'KBUILD_VERBOSE'<wbr>})) {<br>
+ $verbose = "$ENV{'KBUILD_VERBOSE'}";<br>
+}<br>
+<br>
+# Generated docbook code is inserted in a template at a point where<br>
+# docbook v3.1 requires a non-zero sequence of RefEntry's; see:<br>
+# <a href="http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html" rel="noreferrer" target="_blank">http://www.oasis-open.org/<wbr>docbook/documentation/<wbr>reference/html/refentry.html</a><br>
+# We keep track of number of generated entries and generate a dummy<br>
+# if needs be to ensure the expanded template can be postprocessed<br>
+# into html.<br>
+my $section_counter = 0;<br>
+<br>
+my $lineprefix="";<br>
+<br>
+# Parser states<br>
+use constant {<br>
+ STATE_NORMAL => 0, # normal code<br>
+ STATE_NAME => 1, # looking for function name<br>
+ STATE_FIELD => 2, # scanning field start<br>
+ STATE_PROTO => 3, # scanning prototype<br>
+ STATE_DOCBLOCK => 4, # documentation block<br>
+ STATE_INLINE => 5, # gathering documentation outside main block<br>
+};<br>
+my $state;<br>
+my $in_doc_sect;<br>
+<br>
+# Inline documentation state<br>
+use constant {<br>
+ STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)<br>
+ STATE_INLINE_NAME => 1, # looking for member name (@foo:)<br>
+ STATE_INLINE_TEXT => 2, # looking for member documentation<br>
+ STATE_INLINE_END => 3, # done<br>
+ STATE_INLINE_ERROR => 4, # error - Comment without header was found.<br>
+ # Spit a warning as it's not<br>
+ # proper kernel-doc and ignore the rest.<br>
+};<br>
+my $inline_doc_state;<br>
+<br>
+#declaration types: can be<br>
+# 'function', 'struct', 'union', 'enum', 'typedef'<br>
+my $decl_type;<br>
+<br>
+my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.<br>
+my $doc_end = '\*/';<br>
+my $doc_com = '\s*\*\s*';<br>
+my $doc_com_body = '\s*\* ?';<br>
+my $doc_decl = $doc_com . '(\w+)';<br>
+# @params and a strictly limited set of supported section names<br>
+my $doc_sect = $doc_com .<br>
+ '\s*(\@[.\w]+|\@\.\.\.|<wbr>description|context|returns?|<wbr>notes?|examples?)\s*:(.*)';<br>
+my $doc_content = $doc_com_body . '(.*)';<br>
+my $doc_block = $doc_com . 'DOC:\s*(.*)?';<br>
+my $doc_inline_start = '^\s*/\*\*\s*$';<br>
+my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)';<br>
+my $doc_inline_end = '^\s*\*/\s*$';<br>
+my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*<wbr>)\s*\*/\s*$';<br>
+my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(<wbr>\s*(\w+)\s*\)\s*;';<br>
+<br>
+my %parameterdescs;<br>
+my %parameterdesc_start_lines;<br>
+my @parameterlist;<br>
+my %sections;<br>
+my @sectionlist;<br>
+my %section_start_lines;<br>
+my $sectcheck;<br>
+my $struct_actual;<br>
+<br>
+my $contents = "";<br>
+my $new_start_line = 0;<br>
+<br>
+# the canonical section names. see also $doc_sect above.<br>
+my $section_default = "Description"; # default section<br>
+my $section_intro = "Introduction";<br>
+my $section = $section_default;<br>
+my $section_context = "Context";<br>
+my $section_return = "Return";<br>
+<br>
+my $undescribed = "-- undescribed --";<br>
+<br>
+reset_state();<br>
+<br>
+while ($ARGV[0] =~ m/^-(.*)/) {<br>
+ my $cmd = shift @ARGV;<br>
+ if ($cmd eq "-html") {<br>
+ $output_mode = "html";<br>
+ @highlights = @highlights_html;<br>
+ $blankline = $blankline_html;<br>
+ } elsif ($cmd eq "-html5") {<br>
+ $output_mode = "html5";<br>
+ @highlights = @highlights_html5;<br>
+ $blankline = $blankline_html5;<br>
+ } elsif ($cmd eq "-man") {<br>
+ $output_mode = "man";<br>
+ @highlights = @highlights_man;<br>
+ $blankline = $blankline_man;<br>
+ } elsif ($cmd eq "-text") {<br>
+ $output_mode = "text";<br>
+ @highlights = @highlights_text;<br>
+ $blankline = $blankline_text;<br>
+ } elsif ($cmd eq "-rst") {<br>
+ $output_mode = "rst";<br>
+ @highlights = @highlights_rst;<br>
+ $blankline = $blankline_rst;<br>
+ } elsif ($cmd eq "-docbook") {<br>
+ $output_mode = "xml";<br>
+ @highlights = @highlights_xml;<br>
+ $blankline = $blankline_xml;<br>
+ } elsif ($cmd eq "-list") {<br>
+ $output_mode = "list";<br>
+ @highlights = @highlights_list;<br>
+ $blankline = $blankline_list;<br>
+ } elsif ($cmd eq "-gnome") {<br>
+ $output_mode = "gnome";<br>
+ @highlights = @highlights_gnome;<br>
+ $blankline = $blankline_gnome;<br>
+ } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document<br>
+ $modulename = shift @ARGV;<br>
+ } elsif ($cmd eq "-function") { # to only output specific functions<br>
+ $output_selection = OUTPUT_INCLUDE;<br>
+ $function = shift @ARGV;<br>
+ $function_table{$function} = 1;<br>
+ } elsif ($cmd eq "-nofunction") { # output all except specific functions<br>
+ $output_selection = OUTPUT_EXCLUDE;<br>
+ $function = shift @ARGV;<br>
+ $function_table{$function} = 1;<br>
+ } elsif ($cmd eq "-export") { # only exported symbols<br>
+ $output_selection = OUTPUT_EXPORTED;<br>
+ %function_table = ();<br>
+ } elsif ($cmd eq "-internal") { # only non-exported symbols<br>
+ $output_selection = OUTPUT_INTERNAL;<br>
+ %function_table = ();<br>
+ } elsif ($cmd eq "-export-file") {<br>
+ my $file = shift @ARGV;<br>
+ push(@export_file_list, $file);<br>
+ } elsif ($cmd eq "-v") {<br>
+ $verbose = 1;<br>
+ } elsif (($cmd eq "-h") || ($cmd eq "--help")) {<br>
+ usage();<br>
+ } elsif ($cmd eq '-no-doc-sections') {<br>
+ $no_doc_sections = 1;<br>
+ } elsif ($cmd eq '-enable-lineno') {<br>
+ $enable_lineno = 1;<br>
+ } elsif ($cmd eq '-show-not-found') {<br>
+ $show_not_found = 1;<br>
+ }<br>
+}<br>
+<br>
+# continue execution near EOF;<br>
+<br>
+# get kernel version from env<br>
+sub get_kernel_version() {<br>
+ my $version = 'unknown kernel version';<br>
+<br>
+ if (defined($ENV{'KERNELVERSION'}<wbr>)) {<br>
+ $version = $ENV{'KERNELVERSION'};<br>
+ }<br>
+ return $version;<br>
+}<br>
+<br>
+#<br>
+sub print_lineno {<br>
+ my $lineno = shift;<br>
+ if ($enable_lineno && defined($lineno)) {<br>
+ print "#define LINENO " . $lineno . "\n";<br>
+ }<br>
+}<br>
+##<br>
+# dumps section contents to arrays/hashes intended for that purpose.<br>
+#<br>
+sub dump_section {<br>
+ my $file = shift;<br>
+ my $name = shift;<br>
+ my $contents = join "\n", @_;<br>
+<br>
+ if ($name =~ m/$type_param/) {<br>
+ $name = $1;<br>
+ $parameterdescs{$name} = $contents;<br>
+ $sectcheck = $sectcheck . $name . " ";<br>
+ $parameterdesc_start_lines{$<wbr>name} = $new_start_line;<br>
+ $new_start_line = 0;<br>
+ } elsif ($name eq "@\.\.\.") {<br>
+ $name = "...";<br>
+ $parameterdescs{$name} = $contents;<br>
+ $sectcheck = $sectcheck . $name . " ";<br>
+ $parameterdesc_start_lines{$<wbr>name} = $new_start_line;<br>
+ $new_start_line = 0;<br>
+ } else {<br>
+ if (defined($sections{$name}) && ($sections{$name} ne "")) {<br>
+ # Only warn on user specified duplicate section names.<br>
+ if ($name ne $section_default) {<br>
+ print STDERR "${file}:$.: warning: duplicate section name '$name'\n";<br>
+ ++$warnings;<br>
+ }<br>
+ $sections{$name} .= $contents;<br>
+ } else {<br>
+ $sections{$name} = $contents;<br>
+ push @sectionlist, $name;<br>
+ $section_start_lines{$name} = $new_start_line;<br>
+ $new_start_line = 0;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# dump DOC: section after checking that it should go out<br>
+#<br>
+sub dump_doc_section {<br>
+ my $file = shift;<br>
+ my $name = shift;<br>
+ my $contents = join "\n", @_;<br>
+<br>
+ if ($no_doc_sections) {<br>
+ return;<br>
+ }<br>
+<br>
+ if (($output_selection == OUTPUT_ALL) ||<br>
+ ($output_selection == OUTPUT_INCLUDE &&<br>
+ defined($function_table{$name}<wbr>)) ||<br>
+ ($output_selection == OUTPUT_EXCLUDE &&<br>
+ !defined($function_table{$<wbr>name})))<br>
+ {<br>
+ dump_section($file, $name, $contents);<br>
+ output_blockhead({'<wbr>sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'module' => $modulename,<br>
+ 'content-only' => ($output_selection != OUTPUT_ALL), });<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output function<br>
+#<br>
+# parameterdescs, a hash.<br>
+# function => "function name"<br>
+# parameterlist => @list of parameters<br>
+# parameterdescs => %parameter descriptions<br>
+# sectionlist => @list of sections<br>
+# sections => %section descriptions<br>
+#<br>
+<br>
+sub output_highlight {<br>
+ my $contents = join "\n",@_;<br>
+ my $line;<br>
+<br>
+# DEBUG<br>
+# if (!defined $contents) {<br>
+# use Carp;<br>
+# confess "output_highlight got called with no args?\n";<br>
+# }<br>
+<br>
+ if ($output_mode eq "html" || $output_mode eq "html5" ||<br>
+ $output_mode eq "xml") {<br>
+ $contents = local_unescape($contents);<br>
+ # convert data read & converted thru xml_escape() into &xyz; format:<br>
+ $contents =~ s/\\\\\\/\&/g;<br>
+ }<br>
+# print STDERR "contents b4:$contents\n";<br>
+ eval $dohighlight;<br>
+ die $@ if $@;<br>
+# print STDERR "contents af:$contents\n";<br>
+<br>
+# strip whitespaces when generating html5<br>
+ if ($output_mode eq "html5") {<br>
+ $contents =~ s/^\s+//;<br>
+ $contents =~ s/\s+$//;<br>
+ }<br>
+ foreach $line (split "\n", $contents) {<br>
+ if (! $output_preformatted) {<br>
+ $line =~ s/^\s*//;<br>
+ }<br>
+ if ($line eq ""){<br>
+ if (! $output_preformatted) {<br>
+ print $lineprefix, local_unescape($blankline);<br>
+ }<br>
+ } else {<br>
+ $line =~ s/\\\\\\/\&/g;<br>
+ if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {<br>
+ print "\\&$line";<br>
+ } else {<br>
+ print $lineprefix, $line;<br>
+ }<br>
+ }<br>
+ print "\n";<br>
+ }<br>
+}<br>
+<br>
+# output sections in html<br>
+sub output_section_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my $section;<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "<h3>$section</h3>\n";<br>
+ print "<blockquote>\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ print "</blockquote>\n";<br>
+ }<br>
+}<br>
+<br>
+# output enum in html<br>
+sub output_enum_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ print "<h2>enum " . $args{'enum'} . "</h2>\n";<br>
+<br>
+ print "<b>enum " . $args{'enum'} . "</b> {<br>\n";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print " <b>" . $parameter . "</b>";<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",\n";<br>
+ }<br>
+ print "<br>";<br>
+ }<br>
+ print "};<br>\n";<br>
+<br>
+ print "<h3>Constants</h3>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "<dt><b>" . $parameter . "</b>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter});<br>
+ }<br>
+ print "</dl>\n";<br>
+ output_section_html(@_);<br>
+ print "<hr>\n";<br>
+}<br>
+<br>
+# output typedef in html<br>
+sub output_typedef_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ print "<h2>typedef " . $args{'typedef'} . "</h2>\n";<br>
+<br>
+ print "<b>typedef " . $args{'typedef'} . "</b>\n";<br>
+ output_section_html(@_);<br>
+ print "<hr>\n";<br>
+}<br>
+<br>
+# output struct in html<br>
+sub output_struct_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+<br>
+ print "<h2>" . $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "</h2>\n";<br>
+ print "<b>" . $args{'type'} . " " . $args{'struct'} . "</b> {<br>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($parameter =~ /^#/) {<br>
+ print "$parameter<br>\n";<br>
+ next;<br>
+ }<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print " <i>$1</i><b>$parameter</b>) <i>($2)</i>;<br>\n";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print " <i>$1</i> <b>$parameter</b>$2;<br>\n";<br>
+ } else {<br>
+ print " <i>$type</i> <b>$parameter</b>;<br>\n";<br>
+ }<br>
+ }<br>
+ print "};<br>\n";<br>
+<br>
+ print "<h3>Members</h3>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print "<dt><b>" . $parameter . "</b>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ }<br>
+ print "</dl>\n";<br>
+ output_section_html(@_);<br>
+ print "<hr>\n";<br>
+}<br>
+<br>
+# output function in html<br>
+sub output_function_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ print "<h2>" . $args{'function'} . " - " . $args{'purpose'} . "</h2>\n";<br>
+ print "<i>" . $args{'functiontype'} . "</i>\n";<br>
+ print "<b>" . $args{'function'} . "</b>\n";<br>
+ print "(";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print "<i>$1</i><b>$parameter</b>) <i>($2)</i>";<br>
+ } else {<br>
+ print "<i>" . $type . "</i> <b>" . $parameter . "</b>";<br>
+ }<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",\n";<br>
+ }<br>
+ }<br>
+ print ")\n";<br>
+<br>
+ print "<h3>Arguments</h3>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print "<dt><b>" . $parameter . "</b>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ }<br>
+ print "</dl>\n";<br>
+ output_section_html(@_);<br>
+ print "<hr>\n";<br>
+}<br>
+<br>
+# output DOC: block header in html<br>
+sub output_blockhead_html(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "<h3>$section</h3>\n";<br>
+ print "<ul>\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ print "</ul>\n";<br>
+ }<br>
+ print "<hr>\n";<br>
+}<br>
+<br>
+# output sections in html5<br>
+sub output_section_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my $section;<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "<section>\n";<br>
+ print "<h1>$section</h1>\n";<br>
+ print "<p>\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ print "</p>\n";<br>
+ print "</section>\n";<br>
+ }<br>
+}<br>
+<br>
+# output enum in html5<br>
+sub output_enum_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ my $html5id;<br>
+<br>
+ $html5id = $args{'enum'};<br>
+ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;<br>
+ print "<article class=\"enum\" id=\"enum:". $html5id . "\">";<br>
+ print "<h1>enum " . $args{'enum'} . "</h1>\n";<br>
+ print "<ol class=\"code\">\n";<br>
+ print "<li>";<br>
+ print "<span class=\"keyword\">enum</span> ";<br>
+ print "<span class=\"identifier\">" . $args{'enum'} . "</span> {";<br>
+ print "</li>\n";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "<li class=\"indent\">";<br>
+ print "<span class=\"param\">" . $parameter . "</span>";<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",";<br>
+ }<br>
+ print "</li>\n";<br>
+ }<br>
+ print "<li>};</li>\n";<br>
+ print "</ol>\n";<br>
+<br>
+ print "<section>\n";<br>
+ print "<h1>Constants</h1>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "<dt>" . $parameter . "</dt>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter});<br>
+ print "</dd>\n";<br>
+ }<br>
+ print "</dl>\n";<br>
+ print "</section>\n";<br>
+ output_section_html5(@_);<br>
+ print "</article>\n";<br>
+}<br>
+<br>
+# output typedef in html5<br>
+sub output_typedef_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ my $html5id;<br>
+<br>
+ $html5id = $args{'typedef'};<br>
+ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;<br>
+ print "<article class=\"typedef\" id=\"typedef:" . $html5id . "\">\n";<br>
+ print "<h1>typedef " . $args{'typedef'} . "</h1>\n";<br>
+<br>
+ print "<ol class=\"code\">\n";<br>
+ print "<li>";<br>
+ print "<span class=\"keyword\">typedef</<wbr>span> ";<br>
+ print "<span class=\"identifier\">" . $args{'typedef'} . "</span>";<br>
+ print "</li>\n";<br>
+ print "</ol>\n";<br>
+ output_section_html5(@_);<br>
+ print "</article>\n";<br>
+}<br>
+<br>
+# output struct in html5<br>
+sub output_struct_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $html5id;<br>
+<br>
+ $html5id = $args{'struct'};<br>
+ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;<br>
+ print "<article class=\"struct\" id=\"struct:" . $html5id . "\">\n";<br>
+ print "<hgroup>\n";<br>
+ print "<h1>" . $args{'type'} . " " . $args{'struct'} . "</h1>";<br>
+ print "<h2>". $args{'purpose'} . "</h2>\n";<br>
+ print "</hgroup>\n";<br>
+ print "<ol class=\"code\">\n";<br>
+ print "<li>";<br>
+ print "<span class=\"type\">" . $args{'type'} . "</span> ";<br>
+ print "<span class=\"identifier\">" . $args{'struct'} . "</span> {";<br>
+ print "</li>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "<li class=\"indent\">";<br>
+ if ($parameter =~ /^#/) {<br>
+ print "<span class=\"param\">" . $parameter ."</span>\n";<br>
+ print "</li>\n";<br>
+ next;<br>
+ }<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print "<span class=\"type\">$1</span> ";<br>
+ print "<span class=\"param\">$parameter</<wbr>span>";<br>
+ print "<span class=\"type\">)</span> ";<br>
+ print "(<span class=\"args\">$2</span>);";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print "<span class=\"type\">$1</span> ";<br>
+ print "<span class=\"param\">$parameter</<wbr>span>";<br>
+ print "<span class=\"bits\">$2</span>;";<br>
+ } else {<br>
+ print "<span class=\"type\">$type</span> ";<br>
+ print "<span class=\"param\">$parameter</<wbr>span>;";<br>
+ }<br>
+ print "</li>\n";<br>
+ }<br>
+ print "<li>};</li>\n";<br>
+ print "</ol>\n";<br>
+<br>
+ print "<section>\n";<br>
+ print "<h1>Members</h1>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print "<dt>" . $parameter . "</dt>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print "</dd>\n";<br>
+ }<br>
+ print "</dl>\n";<br>
+ print "</section>\n";<br>
+ output_section_html5(@_);<br>
+ print "</article>\n";<br>
+}<br>
+<br>
+# output function in html5<br>
+sub output_function_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+ my $html5id;<br>
+<br>
+ $html5id = $args{'function'};<br>
+ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;<br>
+ print "<article class=\"function\" id=\"func:". $html5id . "\">\n";<br>
+ print "<hgroup>\n";<br>
+ print "<h1>" . $args{'function'} . "</h1>";<br>
+ print "<h2>" . $args{'purpose'} . "</h2>\n";<br>
+ print "</hgroup>\n";<br>
+ print "<ol class=\"code\">\n";<br>
+ print "<li>";<br>
+ print "<span class=\"type\">" . $args{'functiontype'} . "</span> ";<br>
+ print "<span class=\"identifier\">" . $args{'function'} . "</span> (";<br>
+ print "</li>";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "<li class=\"indent\">";<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print "<span class=\"type\">$1</span> ";<br>
+ print "<span class=\"param\">$parameter</<wbr>span>";<br>
+ print "<span class=\"type\">)</span> ";<br>
+ print "(<span class=\"args\">$2</span>)";<br>
+ } else {<br>
+ print "<span class=\"type\">$type</span> ";<br>
+ print "<span class=\"param\">$parameter</<wbr>span>";<br>
+ }<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",";<br>
+ }<br>
+ print "</li>\n";<br>
+ }<br>
+ print "<li>)</li>\n";<br>
+ print "</ol>\n";<br>
+<br>
+ print "<section>\n";<br>
+ print "<h1>Arguments</h1>\n";<br>
+ print "<p>\n";<br>
+ print "<dl>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print "<dt>" . $parameter . "</dt>\n";<br>
+ print "<dd>";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print "</dd>\n";<br>
+ }<br>
+ print "</dl>\n";<br>
+ print "</section>\n";<br>
+ output_section_html5(@_);<br>
+ print "</article>\n";<br>
+}<br>
+<br>
+# output DOC: block header in html5<br>
+sub output_blockhead_html5(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+ my $html5id;<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ $html5id = $section;<br>
+ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g;<br>
+ print "<article class=\"doc\" id=\"doc:". $html5id . "\">\n";<br>
+ print "<h1>$section</h1>\n";<br>
+ print "<p>\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ print "</p>\n";<br>
+ }<br>
+ print "</article>\n";<br>
+}<br>
+<br>
+sub output_section_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my $section;<br>
+ # print out each section<br>
+ $lineprefix=" ";<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "<refsect1>\n";<br>
+ print "<title>$section</title>\n";<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "<informalexample><<wbr>programlisting>\n";<br>
+ $output_preformatted = 1;<br>
+ } else {<br>
+ print "<para>\n";<br>
+ }<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ $output_preformatted = 0;<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "</programlisting></<wbr>informalexample>\n";<br>
+ } else {<br>
+ print "</para>\n";<br>
+ }<br>
+ print "</refsect1>\n";<br>
+ }<br>
+}<br>
+<br>
+# output function in XML DocBook<br>
+sub output_function_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+ my $id;<br>
+<br>
+ $id = "API-" . $args{'function'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ print "<refentry id=\"$id\">\n";<br>
+ print "<refentryinfo>\n";<br>
+ print " <title>LINUX</title>\n";<br>
+ print " <productname>Kernel Hackers Manual</productname>\n";<br>
+ print " <date>$man_date</date>\n";<br>
+ print "</refentryinfo>\n";<br>
+ print "<refmeta>\n";<br>
+ print " <refentrytitle><phrase>" . $args{'function'} . "</phrase></refentrytitle>\n";<br>
+ print " <manvolnum>9</manvolnum>\n";<br>
+ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";<br>
+ print "</refmeta>\n";<br>
+ print "<refnamediv>\n";<br>
+ print " <refname>" . $args{'function'} . "</refname>\n";<br>
+ print " <refpurpose>\n";<br>
+ print " ";<br>
+ output_highlight ($args{'purpose'});<br>
+ print " </refpurpose>\n";<br>
+ print "</refnamediv>\n";<br>
+<br>
+ print "<refsynopsisdiv>\n";<br>
+ print " <title>Synopsis</title>\n";<br>
+ print " <funcsynopsis><funcprototype>\<wbr>n";<br>
+ print " <funcdef>" . $args{'functiontype'} . " ";<br>
+ print "<function>" . $args{'function'} . " </function></funcdef>\n";<br>
+<br>
+ $count = 0;<br>
+ if ($#{$args{'parameterlist'}} >= 0) {<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print " <paramdef>$1<parameter>$<wbr>parameter</parameter>)\n";<br>
+ print " <funcparams>$2</funcparams></<wbr>paramdef>\n";<br>
+ } else {<br>
+ print " <paramdef>" . $type;<br>
+ print " <parameter>$parameter</<wbr>parameter></paramdef>\n";<br>
+ }<br>
+ }<br>
+ } else {<br>
+ print " <void/>\n";<br>
+ }<br>
+ print " </funcprototype></<wbr>funcsynopsis>\n";<br>
+ print "</refsynopsisdiv>\n";<br>
+<br>
+ # print parameters<br>
+ print "<refsect1>\n <title>Arguments</title>\n";<br>
+ if ($#{$args{'parameterlist'}} >= 0) {<br>
+ print " <variablelist>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print " <varlistentry>\n <term><parameter>$parameter</<wbr>parameter></term>\n";<br>
+ print " <listitem>\n <para>\n";<br>
+ $lineprefix=" ";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print " </para>\n </listitem>\n </varlistentry>\n";<br>
+ }<br>
+ print " </variablelist>\n";<br>
+ } else {<br>
+ print " <para>\n None\n </para>\n";<br>
+ }<br>
+ print "</refsect1>\n";<br>
+<br>
+ output_section_xml(@_);<br>
+ print "</refentry>\n\n";<br>
+}<br>
+<br>
+# output struct in XML DocBook<br>
+sub output_struct_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $id;<br>
+<br>
+ $id = "API-struct-" . $args{'struct'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ print "<refentry id=\"$id\">\n";<br>
+ print "<refentryinfo>\n";<br>
+ print " <title>LINUX</title>\n";<br>
+ print " <productname>Kernel Hackers Manual</productname>\n";<br>
+ print " <date>$man_date</date>\n";<br>
+ print "</refentryinfo>\n";<br>
+ print "<refmeta>\n";<br>
+ print " <refentrytitle><phrase>" . $args{'type'} . " " . $args{'struct'} . "</phrase></refentrytitle>\n";<br>
+ print " <manvolnum>9</manvolnum>\n";<br>
+ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";<br>
+ print "</refmeta>\n";<br>
+ print "<refnamediv>\n";<br>
+ print " <refname>" . $args{'type'} . " " . $args{'struct'} . "</refname>\n";<br>
+ print " <refpurpose>\n";<br>
+ print " ";<br>
+ output_highlight ($args{'purpose'});<br>
+ print " </refpurpose>\n";<br>
+ print "</refnamediv>\n";<br>
+<br>
+ print "<refsynopsisdiv>\n";<br>
+ print " <title>Synopsis</title>\n";<br>
+ print " <programlisting>\n";<br>
+ print $args{'type'} . " " . $args{'struct'} . " {\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($parameter =~ /^#/) {<br>
+ my $prm = $parameter;<br>
+ # convert data read & converted thru xml_escape() into &xyz; format:<br>
+ # This allows us to have #define macros interspersed in a struct.<br>
+ $prm =~ s/\\\\\\/\&/g;<br>
+ print "$prm\n";<br>
+ next;<br>
+ }<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ defined($args{'parameterdescs'<wbr>}{$parameter_name}) || next;<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print " $1 $parameter) ($2);\n";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print " $1 $parameter$2;\n";<br>
+ } else {<br>
+ print " " . $type . " " . $parameter . ";\n";<br>
+ }<br>
+ }<br>
+ print "};";<br>
+ print " </programlisting>\n";<br>
+ print "</refsynopsisdiv>\n";<br>
+<br>
+ print " <refsect1>\n";<br>
+ print " <title>Members</title>\n";<br>
+<br>
+ if ($#{$args{'parameterlist'}} >= 0) {<br>
+ print " <variablelist>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ defined($args{'parameterdescs'<wbr>}{$parameter_name}) || next;<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print " <varlistentry>";<br>
+ print " <term>$parameter</term>\n";<br>
+ print " <listitem><para>\n";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print " </para></listitem>\n";<br>
+ print " </varlistentry>\n";<br>
+ }<br>
+ print " </variablelist>\n";<br>
+ } else {<br>
+ print " <para>\n None\n </para>\n";<br>
+ }<br>
+ print " </refsect1>\n";<br>
+<br>
+ output_section_xml(@_);<br>
+<br>
+ print "</refentry>\n\n";<br>
+}<br>
+<br>
+# output enum in XML DocBook<br>
+sub output_enum_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+ my $id;<br>
+<br>
+ $id = "API-enum-" . $args{'enum'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ print "<refentry id=\"$id\">\n";<br>
+ print "<refentryinfo>\n";<br>
+ print " <title>LINUX</title>\n";<br>
+ print " <productname>Kernel Hackers Manual</productname>\n";<br>
+ print " <date>$man_date</date>\n";<br>
+ print "</refentryinfo>\n";<br>
+ print "<refmeta>\n";<br>
+ print " <refentrytitle><phrase>enum " . $args{'enum'} . "</phrase></refentrytitle>\n";<br>
+ print " <manvolnum>9</manvolnum>\n";<br>
+ print " <refmiscinfo class=\"version\">" . $kernelversion . "</refmiscinfo>\n";<br>
+ print "</refmeta>\n";<br>
+ print "<refnamediv>\n";<br>
+ print " <refname>enum " . $args{'enum'} . "</refname>\n";<br>
+ print " <refpurpose>\n";<br>
+ print " ";<br>
+ output_highlight ($args{'purpose'});<br>
+ print " </refpurpose>\n";<br>
+ print "</refnamediv>\n";<br>
+<br>
+ print "<refsynopsisdiv>\n";<br>
+ print " <title>Synopsis</title>\n";<br>
+ print " <programlisting>\n";<br>
+ print "enum " . $args{'enum'} . " {\n";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print " $parameter";<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",";<br>
+ }<br>
+ print "\n";<br>
+ }<br>
+ print "};";<br>
+ print " </programlisting>\n";<br>
+ print "</refsynopsisdiv>\n";<br>
+<br>
+ print "<refsect1>\n";<br>
+ print " <title>Constants</title>\n";<br>
+ print " <variablelist>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print " <varlistentry>";<br>
+ print " <term>$parameter</term>\n";<br>
+ print " <listitem><para>\n";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print " </para></listitem>\n";<br>
+ print " </varlistentry>\n";<br>
+ }<br>
+ print " </variablelist>\n";<br>
+ print "</refsect1>\n";<br>
+<br>
+ output_section_xml(@_);<br>
+<br>
+ print "</refentry>\n\n";<br>
+}<br>
+<br>
+# output typedef in XML DocBook<br>
+sub output_typedef_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $id;<br>
+<br>
+ $id = "API-typedef-" . $args{'typedef'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ print "<refentry id=\"$id\">\n";<br>
+ print "<refentryinfo>\n";<br>
+ print " <title>LINUX</title>\n";<br>
+ print " <productname>Kernel Hackers Manual</productname>\n";<br>
+ print " <date>$man_date</date>\n";<br>
+ print "</refentryinfo>\n";<br>
+ print "<refmeta>\n";<br>
+ print " <refentrytitle><phrase>typedef " . $args{'typedef'} . "</phrase></refentrytitle>\n";<br>
+ print " <manvolnum>9</manvolnum>\n";<br>
+ print "</refmeta>\n";<br>
+ print "<refnamediv>\n";<br>
+ print " <refname>typedef " . $args{'typedef'} . "</refname>\n";<br>
+ print " <refpurpose>\n";<br>
+ print " ";<br>
+ output_highlight ($args{'purpose'});<br>
+ print " </refpurpose>\n";<br>
+ print "</refnamediv>\n";<br>
+<br>
+ print "<refsynopsisdiv>\n";<br>
+ print " <title>Synopsis</title>\n";<br>
+ print " <synopsis>typedef " . $args{'typedef'} . ";</synopsis>\n";<br>
+ print "</refsynopsisdiv>\n";<br>
+<br>
+ output_section_xml(@_);<br>
+<br>
+ print "</refentry>\n\n";<br>
+}<br>
+<br>
+# output in XML DocBook<br>
+sub output_blockhead_xml(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ my $id = $args{'module'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ # print out each section<br>
+ $lineprefix=" ";<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ if (!$args{'content-only'}) {<br>
+ print "<refsect1>\n <title>$section</title>\n";<br>
+ }<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "<example><para>\n";<br>
+ $output_preformatted = 1;<br>
+ } else {<br>
+ print "<para>\n";<br>
+ }<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ $output_preformatted = 0;<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "</para></example>\n";<br>
+ } else {<br>
+ print "</para>";<br>
+ }<br>
+ if (!$args{'content-only'}) {<br>
+ print "\n</refsect1>\n";<br>
+ }<br>
+ }<br>
+<br>
+ print "\n\n";<br>
+}<br>
+<br>
+# output in XML DocBook<br>
+sub output_function_gnome {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+ my $id;<br>
+<br>
+ $id = $args{'module'} . "-" . $args{'function'};<br>
+ $id =~ s/[^A-Za-z0-9]/-/g;<br>
+<br>
+ print "<sect2>\n";<br>
+ print " <title id=\"$id\">" . $args{'function'} . "</title>\n";<br>
+<br>
+ print " <funcsynopsis>\n";<br>
+ print " <funcdef>" . $args{'functiontype'} . " ";<br>
+ print "<function>" . $args{'function'} . " ";<br>
+ print "</function></funcdef>\n";<br>
+<br>
+ $count = 0;<br>
+ if ($#{$args{'parameterlist'}} >= 0) {<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print " <paramdef>$1 <parameter>$parameter</<wbr>parameter>)\n";<br>
+ print " <funcparams>$2</funcparams></<wbr>paramdef>\n";<br>
+ } else {<br>
+ print " <paramdef>" . $type;<br>
+ print " <parameter>$parameter</<wbr>parameter></paramdef>\n";<br>
+ }<br>
+ }<br>
+ } else {<br>
+ print " <void>\n";<br>
+ }<br>
+ print " </funcsynopsis>\n";<br>
+ if ($#{$args{'parameterlist'}} >= 0) {<br>
+ print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";<br>
+ print "<tgroup cols=\"2\">\n";<br>
+ print "<colspec colwidth=\"2*\">\n";<br>
+ print "<colspec colwidth=\"8*\">\n";<br>
+ print "<tbody>\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print " <row><entry align=\"right\"><parameter>$<wbr>parameter</parameter></entry>\<wbr>n";<br>
+ print " <entry>\n";<br>
+ $lineprefix=" ";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print " </entry></row>\n";<br>
+ }<br>
+ print " </tbody></tgroup></<wbr>informaltable>\n";<br>
+ } else {<br>
+ print " <para>\n None\n </para>\n";<br>
+ }<br>
+<br>
+ # print out each section<br>
+ $lineprefix=" ";<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "<simplesect>\n <title>$section</title>\n";<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "<example><programlisting>\n";<br>
+ $output_preformatted = 1;<br>
+ } else {<br>
+ }<br>
+ print "<para>\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ $output_preformatted = 0;<br>
+ print "</para>\n";<br>
+ if ($section =~ m/EXAMPLE/i) {<br>
+ print "</programlisting></example>\<wbr>n";<br>
+ } else {<br>
+ }<br>
+ print " </simplesect>\n";<br>
+ }<br>
+<br>
+ print "</sect2>\n\n";<br>
+}<br>
+<br>
+##<br>
+# output function in man<br>
+sub output_function_man(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n";<br>
+<br>
+ print ".SH NAME\n";<br>
+ print $args{'function'} . " \\- " . $args{'purpose'} . "\n";<br>
+<br>
+ print ".SH SYNOPSIS\n";<br>
+ if ($args{'functiontype'} ne "") {<br>
+ print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n";<br>
+ } else {<br>
+ print ".B \"" . $args{'function'} . "\n";<br>
+ }<br>
+ $count = 0;<br>
+ my $parenth = "(";<br>
+ my $post = ",";<br>
+ foreach my $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($count == $#{$args{'parameterlist'}}) {<br>
+ $post = ");";<br>
+ }<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n";<br>
+ } else {<br>
+ $type =~ s/([^\*])$/$1 /;<br>
+ print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n";<br>
+ }<br>
+ $count++;<br>
+ $parenth = "";<br>
+ }<br>
+<br>
+ print ".SH ARGUMENTS\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print ".IP \"" . $parameter . "\" 12\n";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ }<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print ".SH \"", uc $section, "\"\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output enum in man<br>
+sub output_enum_man(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n";<br>
+<br>
+ print ".SH NAME\n";<br>
+ print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n";<br>
+<br>
+ print ".SH SYNOPSIS\n";<br>
+ print "enum " . $args{'enum'} . " {\n";<br>
+ $count = 0;<br>
+ foreach my $parameter (@{$args{'parameterlist'}}) {<br>
+ print ".br\n.BI \" $parameter\"\n";<br>
+ if ($count == $#{$args{'parameterlist'}}) {<br>
+ print "\n};\n";<br>
+ last;<br>
+ }<br>
+ else {<br>
+ print ", \<a href="http://n.br" rel="noreferrer" target="_blank">n.br</a>\n";<br>
+ }<br>
+ $count++;<br>
+ }<br>
+<br>
+ print ".SH Constants\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print ".IP \"" . $parameter . "\" 12\n";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ }<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print ".SH \"$section\"\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output struct in man<br>
+sub output_struct_man(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+<br>
+ print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n";<br>
+<br>
+ print ".SH NAME\n";<br>
+ print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n";<br>
+<br>
+ print ".SH SYNOPSIS\n";<br>
+ print $args{'type'} . " " . $args{'struct'} . " {\<a href="http://n.br" rel="noreferrer" target="_blank">n.br</a>\n";<br>
+<br>
+ foreach my $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($parameter =~ /^#/) {<br>
+ print ".BI \"$parameter\"\<a href="http://n.br" rel="noreferrer" target="_blank">n.br</a>\n";<br>
+ next;<br>
+ }<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print ".BI \" " . $1 . "\" " . $parameter . " \") (" . $2 . ")" . "\"\n;\n";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print ".BI \" " . $1 . "\ \" " . $parameter . $2 . " \"" . "\"\n;\n";<br>
+ } else {<br>
+ $type =~ s/([^\*])$/$1 /;<br>
+ print ".BI \" " . $type . "\" " . $parameter . " \"" . "\"\n;\n";<br>
+ }<br>
+ print "\<a href="http://n.br" rel="noreferrer" target="_blank">n.br</a>\n";<br>
+ }<br>
+ print "};\<a href="http://n.br" rel="noreferrer" target="_blank">n.br</a>\n";<br>
+<br>
+ print ".SH Members\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print ".IP \"" . $parameter . "\" 12\n";<br>
+ output_highlight($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ }<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print ".SH \"$section\"\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output typedef in man<br>
+sub output_typedef_man(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+<br>
+ print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n";<br>
+<br>
+ print ".SH NAME\n";<br>
+ print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n";<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print ".SH \"$section\"\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+sub output_blockhead_man(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $count;<br>
+<br>
+ print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n";<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print ".SH \"$section\"\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output in text<br>
+sub output_function_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $start;<br>
+<br>
+ print "Name:\n\n";<br>
+ print $args{'function'} . " - " . $args{'purpose'} . "\n";<br>
+<br>
+ print "\nSynopsis:\n\n";<br>
+ if ($args{'functiontype'} ne "") {<br>
+ $start = $args{'functiontype'} . " " . $args{'function'} . " (";<br>
+ } else {<br>
+ $start = $args{'function'} . " (";<br>
+ }<br>
+ print $start;<br>
+<br>
+ my $count = 0;<br>
+ foreach my $parameter (@{$args{'parameterlist'}}) {<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print $1 . $parameter . ") (" . $2;<br>
+ } else {<br>
+ print $type . " " . $parameter;<br>
+ }<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",\n";<br>
+ print " " x length($start);<br>
+ } else {<br>
+ print ");\n\n";<br>
+ }<br>
+ }<br>
+<br>
+ print "Arguments:\n\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ print $parameter . "\n\t" . $args{'parameterdescs'}{$<wbr>parameter_name} . "\n";<br>
+ }<br>
+ output_section_text(@_);<br>
+}<br>
+<br>
+#output sections in text<br>
+sub output_section_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my $section;<br>
+<br>
+ print "\n";<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "$section:\n\n";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+ print "\n\n";<br>
+}<br>
+<br>
+# output enum in text<br>
+sub output_enum_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ print "Enum:\n\n";<br>
+<br>
+ print "enum " . $args{'enum'} . " - " . $args{'purpose'} . "\n\n";<br>
+ print "enum " . $args{'enum'} . " {\n";<br>
+ $count = 0;<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "\t$parameter";<br>
+ if ($count != $#{$args{'parameterlist'}}) {<br>
+ $count++;<br>
+ print ",";<br>
+ }<br>
+ print "\n";<br>
+ }<br>
+ print "};\n\n";<br>
+<br>
+ print "Constants:\n\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "$parameter\n\t";<br>
+ print $args{'parameterdescs'}{$<wbr>parameter} . "\n";<br>
+ }<br>
+<br>
+ output_section_text(@_);<br>
+}<br>
+<br>
+# output typedef in text<br>
+sub output_typedef_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $count;<br>
+ print "Typedef:\n\n";<br>
+<br>
+ print "typedef " . $args{'typedef'} . " - " . $args{'purpose'} . "\n";<br>
+ output_section_text(@_);<br>
+}<br>
+<br>
+# output struct as text<br>
+sub output_struct_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+<br>
+ print $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "\n\n";<br>
+ print $args{'type'} . " " . $args{'struct'} . " {\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($parameter =~ /^#/) {<br>
+ print "$parameter\n";<br>
+ next;<br>
+ }<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print "\t$1 $parameter) ($2);\n";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print "\t$1 $parameter$2;\n";<br>
+ } else {<br>
+ print "\t" . $type . " " . $parameter . ";\n";<br>
+ }<br>
+ }<br>
+ print "};\n\n";<br>
+<br>
+ print "Members:\n\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ print "$parameter\n\t";<br>
+ print $args{'parameterdescs'}{$<wbr>parameter_name} . "\n";<br>
+ }<br>
+ print "\n";<br>
+ output_section_text(@_);<br>
+}<br>
+<br>
+sub output_blockhead_text(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print " $section:\n";<br>
+ print " -> ";<br>
+ output_highlight($args{'<wbr>sections'}{$section});<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# output in restructured text<br>
+#<br>
+<br>
+#<br>
+# This could use some work; it's used to output the DOC: sections, and<br>
+# starts by putting out the name of the doc section itself, but that tends<br>
+# to duplicate a header already in the template file.<br>
+#<br>
+sub output_blockhead_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ if ($output_selection != OUTPUT_INCLUDE) {<br>
+ print "**$section**\n\n";<br>
+ }<br>
+ print_lineno($section_start_<wbr>lines{$section});<br>
+ output_highlight_rst($args{'<wbr>sections'}{$section});<br>
+ print "\n";<br>
+ }<br>
+}<br>
+<br>
+sub output_highlight_rst {<br>
+ my $contents = join "\n",@_;<br>
+ my $line;<br>
+<br>
+ # undo the evil effects of xml_escape() earlier<br>
+ $contents = xml_unescape($contents);<br>
+<br>
+ eval $dohighlight;<br>
+ die $@ if $@;<br>
+<br>
+ foreach $line (split "\n", $contents) {<br>
+ print $lineprefix . $line . "\n";<br>
+ }<br>
+}<br>
+<br>
+sub output_function_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+ my $oldprefix = $lineprefix;<br>
+ my $start = "";<br>
+<br>
+ if ($args{'typedef'}) {<br>
+ print ".. c:type:: ". $args{'function'} . "\n\n";<br>
+ print_lineno($declaration_<wbr>start_line);<br>
+ print " **Typedef**: ";<br>
+ $lineprefix = "";<br>
+ output_highlight_rst($args{'<wbr>purpose'});<br>
+ $start = "\n\n**Syntax**\n\n ``";<br>
+ } else {<br>
+ print ".. c:function:: ";<br>
+ }<br>
+ if ($args{'functiontype'} ne "") {<br>
+ $start .= $args{'functiontype'} . " " . $args{'function'} . " (";<br>
+ } else {<br>
+ $start .= $args{'function'} . " (";<br>
+ }<br>
+ print $start;<br>
+<br>
+ my $count = 0;<br>
+ foreach my $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($count ne 0) {<br>
+ print ", ";<br>
+ }<br>
+ $count++;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print $1 . $parameter . ") (" . $2;<br>
+ } else {<br>
+ print $type . " " . $parameter;<br>
+ }<br>
+ }<br>
+ if ($args{'typedef'}) {<br>
+ print ");``\n\n";<br>
+ } else {<br>
+ print ")\n\n";<br>
+ print_lineno($declaration_<wbr>start_line);<br>
+ $lineprefix = " ";<br>
+ output_highlight_rst($args{'<wbr>purpose'});<br>
+ print "\n";<br>
+ }<br>
+<br>
+ print "**Parameters**\n\n";<br>
+ $lineprefix = " ";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ my $parameter_name = $parameter;<br>
+ #$parameter_name =~ s/\[.*//;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+<br>
+ if ($type ne "") {<br>
+ print "``$type $parameter``\n";<br>
+ } else {<br>
+ print "``$parameter``\n";<br>
+ }<br>
+<br>
+ print_lineno($parameterdesc_<wbr>start_lines{$parameter_name});<br>
+<br>
+ if (defined($args{'<wbr>parameterdescs'}{$parameter_<wbr>name}) &&<br>
+ $args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) {<br>
+ output_highlight_rst($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ } else {<br>
+ print " *undescribed*\n";<br>
+ }<br>
+ print "\n";<br>
+ }<br>
+<br>
+ $lineprefix = $oldprefix;<br>
+ output_section_rst(@_);<br>
+}<br>
+<br>
+sub output_section_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my $section;<br>
+ my $oldprefix = $lineprefix;<br>
+ $lineprefix = "";<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "**$section**\n\n";<br>
+ print_lineno($section_start_<wbr>lines{$section});<br>
+ output_highlight_rst($args{'<wbr>sections'}{$section});<br>
+ print "\n";<br>
+ }<br>
+ print "\n";<br>
+ $lineprefix = $oldprefix;<br>
+}<br>
+<br>
+sub output_enum_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $oldprefix = $lineprefix;<br>
+ my $count;<br>
+ my $name = "enum " . $args{'enum'};<br>
+<br>
+ print "\n\n.. c:type:: " . $name . "\n\n";<br>
+ print_lineno($declaration_<wbr>start_line);<br>
+ $lineprefix = " ";<br>
+ output_highlight_rst($args{'<wbr>purpose'});<br>
+ print "\n";<br>
+<br>
+ print "**Constants**\n\n";<br>
+ $lineprefix = " ";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ print "``$parameter``\n";<br>
+ if ($args{'parameterdescs'}{$<wbr>parameter} ne $undescribed) {<br>
+ output_highlight_rst($args{'<wbr>parameterdescs'}{$parameter});<br>
+ } else {<br>
+ print " *undescribed*\n";<br>
+ }<br>
+ print "\n";<br>
+ }<br>
+<br>
+ $lineprefix = $oldprefix;<br>
+ output_section_rst(@_);<br>
+}<br>
+<br>
+sub output_typedef_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $oldprefix = $lineprefix;<br>
+ my $name = "typedef " . $args{'typedef'};<br>
+<br>
+ print "\n\n.. c:type:: " . $name . "\n\n";<br>
+ print_lineno($declaration_<wbr>start_line);<br>
+ $lineprefix = " ";<br>
+ output_highlight_rst($args{'<wbr>purpose'});<br>
+ print "\n";<br>
+<br>
+ $lineprefix = $oldprefix;<br>
+ output_section_rst(@_);<br>
+}<br>
+<br>
+sub output_struct_rst(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter);<br>
+ my $oldprefix = $lineprefix;<br>
+ my $name = $args{'type'} . " " . $args{'struct'};<br>
+<br>
+ print "\n\n.. c:type:: " . $name . "\n\n";<br>
+ print_lineno($declaration_<wbr>start_line);<br>
+ $lineprefix = " ";<br>
+ output_highlight_rst($args{'<wbr>purpose'});<br>
+ print "\n";<br>
+<br>
+ print "**Definition**\n\n";<br>
+ print "::\n\n";<br>
+ print " " . $args{'type'} . " " . $args{'struct'} . " {\n";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ if ($parameter =~ /^#/) {<br>
+ print " " . "$parameter\n";<br>
+ next;<br>
+ }<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]<wbr>*)\)/) {<br>
+ # pointer-to-function<br>
+ print " $1 $parameter) ($2);\n";<br>
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {<br>
+ # bitfield<br>
+ print " $1 $parameter$2;\n";<br>
+ } else {<br>
+ print " " . $type . " " . $parameter . ";\n";<br>
+ }<br>
+ }<br>
+ print " };\n\n";<br>
+<br>
+ print "**Members**\n\n";<br>
+ $lineprefix = " ";<br>
+ foreach $parameter (@{$args{'parameterlist'}}) {<br>
+ ($parameter =~ /^#/) && next;<br>
+<br>
+ my $parameter_name = $parameter;<br>
+ $parameter_name =~ s/\[.*//;<br>
+<br>
+ ($args{'parameterdescs'}{$<wbr>parameter_name} ne $undescribed) || next;<br>
+ $type = $args{'parametertypes'}{$<wbr>parameter};<br>
+ print_lineno($parameterdesc_<wbr>start_lines{$parameter_name});<br>
+ print "``" . $parameter . "``\n";<br>
+ output_highlight_rst($args{'<wbr>parameterdescs'}{$parameter_<wbr>name});<br>
+ print "\n";<br>
+ }<br>
+ print "\n";<br>
+<br>
+ $lineprefix = $oldprefix;<br>
+ output_section_rst(@_);<br>
+}<br>
+<br>
+<br>
+## list mode output functions<br>
+<br>
+sub output_function_list(%) {<br>
+ my %args = %{$_[0]};<br>
+<br>
+ print $args{'function'} . "\n";<br>
+}<br>
+<br>
+# output enum in list<br>
+sub output_enum_list(%) {<br>
+ my %args = %{$_[0]};<br>
+ print $args{'enum'} . "\n";<br>
+}<br>
+<br>
+# output typedef in list<br>
+sub output_typedef_list(%) {<br>
+ my %args = %{$_[0]};<br>
+ print $args{'typedef'} . "\n";<br>
+}<br>
+<br>
+# output struct as list<br>
+sub output_struct_list(%) {<br>
+ my %args = %{$_[0]};<br>
+<br>
+ print $args{'struct'} . "\n";<br>
+}<br>
+<br>
+sub output_blockhead_list(%) {<br>
+ my %args = %{$_[0]};<br>
+ my ($parameter, $section);<br>
+<br>
+ foreach $section (@{$args{'sectionlist'}}) {<br>
+ print "DOC: $section\n";<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# generic output function for all types (function, struct/union, typedef, enum);<br>
+# calls the generated, variable output_ function name based on<br>
+# functype and output_mode<br>
+sub output_declaration {<br>
+ no strict 'refs';<br>
+ my $name = shift;<br>
+ my $functype = shift;<br>
+ my $func = "output_${functype}_$output_<wbr>mode";<br>
+ if (($output_selection == OUTPUT_ALL) ||<br>
+ (($output_selection == OUTPUT_INCLUDE ||<br>
+ $output_selection == OUTPUT_EXPORTED) &&<br>
+ defined($function_table{$name}<wbr>)) ||<br>
+ (($output_selection == OUTPUT_EXCLUDE ||<br>
+ $output_selection == OUTPUT_INTERNAL) &&<br>
+ !($functype eq "function" && defined($function_table{$name}<wbr>))))<br>
+ {<br>
+ &$func(@_);<br>
+ $section_counter++;<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# generic output function - calls the right one based on current output mode.<br>
+sub output_blockhead {<br>
+ no strict 'refs';<br>
+ my $func = "output_blockhead_" . $output_mode;<br>
+ &$func(@_);<br>
+ $section_counter++;<br>
+}<br>
+<br>
+##<br>
+# takes a declaration (struct, union, enum, typedef) and<br>
+# invokes the right handler. NOT called for functions.<br>
+sub dump_declaration($$) {<br>
+ no strict 'refs';<br>
+ my ($prototype, $file) = @_;<br>
+ my $func = "dump_" . $decl_type;<br>
+ &$func(@_);<br>
+}<br>
+<br>
+sub dump_union($$) {<br>
+ dump_struct(@_);<br>
+}<br>
+<br>
+sub dump_struct($$) {<br>
+ my $x = shift;<br>
+ my $file = shift;<br>
+ my $nested;<br>
+<br>
+ if ($x =~ /(struct|union)\s+(\w+)\s*{(.*<wbr>)}/) {<br>
+ #my $decl_type = $1;<br>
+ $declaration_name = $2;<br>
+ my $members = $3;<br>
+<br>
+ # ignore embedded structs or unions<br>
+ $members =~ s/({.*})//g;<br>
+ $nested = $1;<br>
+<br>
+ # ignore members marked private:<br>
+ $members =~ s/\/\*\s*private:.*?\/\*\s*<wbr>public:.*?\*\///gosi;<br>
+ $members =~ s/\/\*\s*private:.*//gosi;<br>
+ # strip comments:<br>
+ $members =~ s/\/\*.*?\*\///gos;<br>
+ $nested =~ s/\/\*.*?\*\///gos;<br>
+ # strip kmemcheck_bitfield_{begin,end}<wbr>.*;<br>
+ $members =~ s/kmemcheck_bitfield_.*?;//<wbr>gos;<br>
+ # strip attributes<br>
+ $members =~ s/__attribute__\s*\(\([a-z,_\*<wbr>\s\(\)]*\)\)//i;<br>
+ $members =~ s/__aligned\s*\([^;]*\)//gos;<br>
+ $members =~ s/\s*CRYPTO_MINALIGN_ATTR//<wbr>gos;<br>
+ # replace DECLARE_BITMAP<br>
+ $members =~ s/DECLARE_BITMAP\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;<br>
+<br>
+ create_parameterlist($members, ';', $file);<br>
+ check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested);<br>
+<br>
+ output_declaration($<wbr>declaration_name,<br>
+ 'struct',<br>
+ {'struct' => $declaration_name,<br>
+ 'module' => $modulename,<br>
+ 'parameterlist' => \@parameterlist,<br>
+ 'parameterdescs' => \%parameterdescs,<br>
+ 'parametertypes' => \%parametertypes,<br>
+ 'sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'purpose' => $declaration_purpose,<br>
+ 'type' => $decl_type<br>
+ });<br>
+ }<br>
+ else {<br>
+ print STDERR "${file}:$.: error: Cannot parse struct or union!\n";<br>
+ ++$errors;<br>
+ }<br>
+}<br>
+<br>
+sub dump_enum($$) {<br>
+ my $x = shift;<br>
+ my $file = shift;<br>
+<br>
+ $x =~ s@/\*.*?\*/@@gos; # strip comments.<br>
+ # strip #define macros inside enums<br>
+ $x =~ s@#\s*((define|ifdef)\s+|<wbr>endif)[^;]*;@@gos;<br>
+<br>
+ if ($x =~ /enum\s+(\w+)\s*{(.*)}/) {<br>
+ $declaration_name = $1;<br>
+ my $members = $2;<br>
+<br>
+ foreach my $arg (split ',', $members) {<br>
+ $arg =~ s/^\s*(\w+).*/$1/;<br>
+ push @parameterlist, $arg;<br>
+ if (!$parameterdescs{$arg}) {<br>
+ $parameterdescs{$arg} = $undescribed;<br>
+ print STDERR "${file}:$.: warning: Enum value '$arg' ".<br>
+ "not described in enum '$declaration_name'\n";<br>
+ }<br>
+<br>
+ }<br>
+<br>
+ output_declaration($<wbr>declaration_name,<br>
+ 'enum',<br>
+ {'enum' => $declaration_name,<br>
+ 'module' => $modulename,<br>
+ 'parameterlist' => \@parameterlist,<br>
+ 'parameterdescs' => \%parameterdescs,<br>
+ 'sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'purpose' => $declaration_purpose<br>
+ });<br>
+ }<br>
+ else {<br>
+ print STDERR "${file}:$.: error: Cannot parse enum!\n";<br>
+ ++$errors;<br>
+ }<br>
+}<br>
+<br>
+sub dump_typedef($$) {<br>
+ my $x = shift;<br>
+ my $file = shift;<br>
+<br>
+ $x =~ s@/\*.*?\*/@@gos; # strip comments.<br>
+<br>
+ # Parse function prototypes<br>
+ if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\<wbr>S+)\s*\)\s*\((.*)\);/ ||<br>
+ $x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\<wbr>s*\((.*)\);/) {<br>
+<br>
+ # Function typedefs<br>
+ $return_type = $1;<br>
+ $declaration_name = $2;<br>
+ my $args = $3;<br>
+<br>
+ create_parameterlist($args, ',', $file);<br>
+<br>
+ output_declaration($<wbr>declaration_name,<br>
+ 'function',<br>
+ {'function' => $declaration_name,<br>
+ 'typedef' => 1,<br>
+ 'module' => $modulename,<br>
+ 'functiontype' => $return_type,<br>
+ 'parameterlist' => \@parameterlist,<br>
+ 'parameterdescs' => \%parameterdescs,<br>
+ 'parametertypes' => \%parametertypes,<br>
+ 'sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'purpose' => $declaration_purpose<br>
+ });<br>
+ return;<br>
+ }<br>
+<br>
+ while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) {<br>
+ $x =~ s/\(*.\)\s*;$/;/;<br>
+ $x =~ s/\[*.\]\s*;$/;/;<br>
+ }<br>
+<br>
+ if ($x =~ /typedef.*\s+(\w+)\s*;/) {<br>
+ $declaration_name = $1;<br>
+<br>
+ output_declaration($<wbr>declaration_name,<br>
+ 'typedef',<br>
+ {'typedef' => $declaration_name,<br>
+ 'module' => $modulename,<br>
+ 'sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'purpose' => $declaration_purpose<br>
+ });<br>
+ }<br>
+ else {<br>
+ print STDERR "${file}:$.: error: Cannot parse typedef!\n";<br>
+ ++$errors;<br>
+ }<br>
+}<br>
+<br>
+sub save_struct_actual($) {<br>
+ my $actual = shift;<br>
+<br>
+ # strip all spaces from the actual param so that it looks like one string item<br>
+ $actual =~ s/\s*//g;<br>
+ $struct_actual = $struct_actual . $actual . " ";<br>
+}<br>
+<br>
+sub create_parameterlist($$$) {<br>
+ my $args = shift;<br>
+ my $splitter = shift;<br>
+ my $file = shift;<br>
+ my $type;<br>
+ my $param;<br>
+<br>
+ # temporarily replace commas inside function pointer definition<br>
+ while ($args =~ /(\([^\),]+),/) {<br>
+ $args =~ s/(\([^\),]+),/$1#/g;<br>
+ }<br>
+<br>
+ foreach my $arg (split($splitter, $args)) {<br>
+ # strip comments<br>
+ $arg =~ s/\/\*.*\*\///;<br>
+ # strip leading/trailing spaces<br>
+ $arg =~ s/^\s*//;<br>
+ $arg =~ s/\s*$//;<br>
+ $arg =~ s/\s+/ /;<br>
+<br>
+ if ($arg =~ /^#/) {<br>
+ # Treat preprocessor directive as a typeless variable just to fill<br>
+ # corresponding data structures "correctly". Catch it later in<br>
+ # output_* subs.<br>
+ push_parameter($arg, "", $file);<br>
+ } elsif ($arg =~ m/\(.+\)\s*\(/) {<br>
+ # pointer-to-function<br>
+ $arg =~ tr/#/,/;<br>
+ $arg =~ m/[^\(]+\(\*?\s*(\w*)\s*\)/;<br>
+ $param = $1;<br>
+ $type = $arg;<br>
+ $type =~ s/([^\(]+\(\*?)\s*$param/$1/;<br>
+ save_struct_actual($param);<br>
+ push_parameter($param, $type, $file);<br>
+ } elsif ($arg) {<br>
+ $arg =~ s/\s*:\s*/:/g;<br>
+ $arg =~ s/\s*\[/\[/g;<br>
+<br>
+ my @args = split('\s*,\s*', $arg);<br>
+ if ($args[0] =~ m/\*/) {<br>
+ $args[0] =~ s/(\*+)\s*/ $1/;<br>
+ }<br>
+<br>
+ my @first_arg;<br>
+ if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) {<br>
+ shift @args;<br>
+ push(@first_arg, split('\s+', $1));<br>
+ push(@first_arg, $2);<br>
+ } else {<br>
+ @first_arg = split('\s+', shift @args);<br>
+ }<br>
+<br>
+ unshift(@args, pop @first_arg);<br>
+ $type = join " ", @first_arg;<br>
+<br>
+ foreach $param (@args) {<br>
+ if ($param =~ m/^(\*+)\s*(.*)/) {<br>
+ save_struct_actual($2);<br>
+ push_parameter($2, "$type $1", $file);<br>
+ }<br>
+ elsif ($param =~ m/(.*?):(\d+)/) {<br>
+ if ($type ne "") { # skip unnamed bit-fields<br>
+ save_struct_actual($1);<br>
+ push_parameter($1, "$type:$2", $file)<br>
+ }<br>
+ }<br>
+ else {<br>
+ save_struct_actual($param);<br>
+ push_parameter($param, $type, $file);<br>
+ }<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+sub push_parameter($$$) {<br>
+ my $param = shift;<br>
+ my $type = shift;<br>
+ my $file = shift;<br>
+<br>
+ if (($anon_struct_union == 1) && ($type eq "") &&<br>
+ ($param eq "}")) {<br>
+ return; # ignore the ending }; from anon. struct/union<br>
+ }<br>
+<br>
+ $anon_struct_union = 0;<br>
+ my $param_name = $param;<br>
+ $param_name =~ s/\[.*//;<br>
+<br>
+ if ($type eq "" && $param =~ /\.\.\.$/)<br>
+ {<br>
+ if (!$param =~ /\w\.\.\.$/) {<br>
+ # handles unnamed variable parameters<br>
+ $param = "...";<br>
+ }<br>
+ if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {<br>
+ $parameterdescs{$param} = "variable arguments";<br>
+ }<br>
+ }<br>
+ elsif ($type eq "" && ($param eq "" or $param eq "void"))<br>
+ {<br>
+ $param="void";<br>
+ $parameterdescs{void} = "no arguments";<br>
+ }<br>
+ elsif ($type eq "" && ($param eq "struct" or $param eq "union"))<br>
+ # handle unnamed (anonymous) union or struct:<br>
+ {<br>
+ $type = $param;<br>
+ $param = "{unnamed_" . $param . "}";<br>
+ $parameterdescs{$param} = "anonymous\n";<br>
+ $anon_struct_union = 1;<br>
+ }<br>
+<br>
+ # warn if parameter has no description<br>
+ # (but ignore ones starting with # as these are not parameters<br>
+ # but inline preprocessor statements);<br>
+ # also ignore unnamed structs/unions;<br>
+ if (!$anon_struct_union) {<br>
+ if (!defined $parameterdescs{$param_name} && $param_name !~ /^#/) {<br>
+<br>
+ $parameterdescs{$param_name} = $undescribed;<br>
+<br>
+ if (($type eq 'function') || ($type eq 'enum')) {<br>
+ print STDERR "${file}:$.: warning: Function parameter ".<br>
+ "or member '$param' not " .<br>
+ "described in '$declaration_name'\n";<br>
+ }<br>
+ print STDERR "${file}:$.: warning:" .<br>
+ " No description found for parameter '$param'\n";<br>
+ ++$warnings;<br>
+ }<br>
+ }<br>
+<br>
+ $param = xml_escape($param);<br>
+<br>
+ # strip spaces from $param so that it is one continuous string<br>
+ # on @parameterlist;<br>
+ # this fixes a problem where check_sections() cannot find<br>
+ # a parameter like "addr[6 + 2]" because it actually appears<br>
+ # as "addr[6", "+", "2]" on the parameter list;<br>
+ # but it's better to maintain the param string unchanged for output,<br>
+ # so just weaken the string compare in check_sections() to ignore<br>
+ # "[blah" in a parameter string;<br>
+ ###$param =~ s/\s*//g;<br>
+ push @parameterlist, $param;<br>
+ $parametertypes{$param} = $type;<br>
+}<br>
+<br>
+sub check_sections($$$$$$) {<br>
+ my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck, $nested) = @_;<br>
+ my @sects = split ' ', $sectcheck;<br>
+ my @prms = split ' ', $prmscheck;<br>
+ my $err;<br>
+ my ($px, $sx);<br>
+ my $prm_clean; # strip trailing "[array size]" and/or beginning "*"<br>
+<br>
+ foreach $sx (0 .. $#sects) {<br>
+ $err = 1;<br>
+ foreach $px (0 .. $#prms) {<br>
+ $prm_clean = $prms[$px];<br>
+ $prm_clean =~ s/\[.*\]//;<br>
+ $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*<wbr>\s\(\)]*\)\)//i;<br>
+ # ignore array size in a parameter string;<br>
+ # however, the original param string may contain<br>
+ # spaces, e.g.: addr[6 + 2]<br>
+ # and this appears in @prms as "addr[6" since the<br>
+ # parameter list is split at spaces;<br>
+ # hence just ignore "[..." for the sections check;<br>
+ $prm_clean =~ s/\[.*//;<br>
+<br>
+ ##$prm_clean =~ s/^\**//;<br>
+ if ($prm_clean eq $sects[$sx]) {<br>
+ $err = 0;<br>
+ last;<br>
+ }<br>
+ }<br>
+ if ($err) {<br>
+ if ($decl_type eq "function") {<br>
+ print STDERR "${file}:$.: warning: " .<br>
+ "Excess function parameter " .<br>
+ "'$sects[$sx]' " .<br>
+ "description in '$decl_name'\n";<br>
+ ++$warnings;<br>
+ } else {<br>
+ if ($nested !~ m/\Q$sects[$sx]\E/) {<br>
+ print STDERR "${file}:$.: warning: " .<br>
+ "Excess struct/union/enum/typedef member " .<br>
+ "'$sects[$sx]' " .<br>
+ "description in '$decl_name'\n";<br>
+ ++$warnings;<br>
+ }<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# Checks the section describing the return value of a function.<br>
+sub check_return_section {<br>
+ my $file = shift;<br>
+ my $declaration_name = shift;<br>
+ my $return_type = shift;<br>
+<br>
+ # Ignore an empty return type (It's a macro)<br>
+ # Ignore functions with a "void" return type. (But don't ignore "void *")<br>
+ if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {<br>
+ return;<br>
+ }<br>
+<br>
+ if (!defined($sections{$section_<wbr>return}) ||<br>
+ $sections{$section_return} eq "") {<br>
+ print STDERR "${file}:$.: warning: " .<br>
+ "No description found for return value of " .<br>
+ "'$declaration_name'\n";<br>
+ ++$warnings;<br>
+ }<br>
+}<br>
+<br>
+##<br>
+# takes a function prototype and the name of the current file being<br>
+# processed and spits out all the details stored in the global<br>
+# arrays/hashes.<br>
+sub dump_function($$) {<br>
+ my $prototype = shift;<br>
+ my $file = shift;<br>
+ my $noret = 0;<br>
+<br>
+ $prototype =~ s/^static +//;<br>
+ $prototype =~ s/^extern +//;<br>
+ $prototype =~ s/^asmlinkage +//;<br>
+ $prototype =~ s/^inline +//;<br>
+ $prototype =~ s/^__inline__ +//;<br>
+ $prototype =~ s/^__inline +//;<br>
+ $prototype =~ s/^__always_inline +//;<br>
+ $prototype =~ s/^noinline +//;<br>
+ $prototype =~ s/__init +//;<br>
+ $prototype =~ s/__init_or_module +//;<br>
+ $prototype =~ s/__meminit +//;<br>
+ $prototype =~ s/__must_check +//;<br>
+ $prototype =~ s/__weak +//;<br>
+ my $define = $prototype =~ s/^#\s*define\s+//; #ak added<br>
+ $prototype =~ s/__attribute__\s*\(\([a-z,]*\<wbr>)\)//;<br>
+<br>
+ # Yes, this truly is vile. We are looking for:<br>
+ # 1. Return type (may be nothing if we're looking at a macro)<br>
+ # 2. Function name<br>
+ # 3. Function parameters.<br>
+ #<br>
+ # All the while we have to watch out for function pointer parameters<br>
+ # (which IIRC is what the two sections are for), C types (these<br>
+ # regexps don't even start to express all the possibilities), and<br>
+ # so on.<br>
+ #<br>
+ # If you mess with these regexps, it's a good idea to check that<br>
+ # the following functions' documentation still comes out right:<br>
+ # - parport_register_device (function pointer parameters)<br>
+ # - atomic_set (macro)<br>
+ # - pci_match_device, __copy_to_user (long return type)<br>
+<br>
+ if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) {<br>
+ # This is an object-like macro, it has no return type and no parameter<br>
+ # list.<br>
+ # Function-like macros are not allowed to have spaces between<br>
+ # declaration_name and opening parenthesis (notice the \s+).<br>
+ $return_type = $1;<br>
+ $declaration_name = $2;<br>
+ $noret = 1;<br>
+ } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^<wbr>\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\<wbr>s*\(([^\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:<wbr>]+)\s*\(([^\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~<wbr>:]+)\s*\(([^\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-<wbr>Z0-9_~:]+)\s*\(([^\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-<wbr>Z0-9_~:]+)\s*\(([^\(]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([<wbr>a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)<wbr>/ ||<br>
+ $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^<wbr>\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\<wbr>s*\(([^\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:<wbr>]+)\s*\(([^\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~<wbr>:]+)\s*\(([^\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-<wbr>Z0-9_~:]+)\s*\(([^\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-<wbr>Z0-9_~:]+)\s*\(([^\{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([<wbr>a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)<wbr>/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+(<wbr>[a-zA-Z0-9_~:]+)\s*\(([^\{]*)\<wbr>)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*<wbr>)\s*([a-zA-Z0-9_~:]+)\s*\(([^\<wbr>{]*)\)/ ||<br>
+ $prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\<wbr>s*)\s*([a-zA-Z0-9_~:]+)\s*\(([<wbr>^\{]*)\)/) {<br>
+ $return_type = $1;<br>
+ $declaration_name = $2;<br>
+ my $args = $3;<br>
+<br>
+ create_parameterlist($args, ',', $file);<br>
+ } else {<br>
+ print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n";<br>
+ return;<br>
+ }<br>
+<br>
+ my $prms = join " ", @parameterlist;<br>
+ check_sections($file, $declaration_name, "function", $sectcheck, $prms, "");<br>
+<br>
+ # This check emits a lot of warnings at the moment, because many<br>
+ # functions don't have a 'Return' doc section. So until the number<br>
+ # of warnings goes sufficiently down, the check is only performed in<br>
+ # verbose mode.<br>
+ # TODO: always perform the check.<br>
+ if ($verbose && !$noret) {<br>
+ check_return_section($file, $declaration_name, $return_type);<br>
+ }<br>
+<br>
+ output_declaration($<wbr>declaration_name,<br>
+ 'function',<br>
+ {'function' => $declaration_name,<br>
+ 'module' => $modulename,<br>
+ 'functiontype' => $return_type,<br>
+ 'parameterlist' => \@parameterlist,<br>
+ 'parameterdescs' => \%parameterdescs,<br>
+ 'parametertypes' => \%parametertypes,<br>
+ 'sectionlist' => \@sectionlist,<br>
+ 'sections' => \%sections,<br>
+ 'purpose' => $declaration_purpose<br>
+ });<br>
+}<br>
+<br>
+sub reset_state {<br>
+ $function = "";<br>
+ %parameterdescs = ();<br>
+ %parametertypes = ();<br>
+ @parameterlist = ();<br>
+ %sections = ();<br>
+ @sectionlist = ();<br>
+ $sectcheck = "";<br>
+ $struct_actual = "";<br>
+ $prototype = "";<br>
+<br>
+ $state = STATE_NORMAL;<br>
+ $inline_doc_state = STATE_INLINE_NA;<br>
+}<br>
+<br>
+sub tracepoint_munge($) {<br>
+ my $file = shift;<br>
+ my $tracepointname = 0;<br>
+ my $tracepointargs = 0;<br>
+<br>
+ if ($prototype =~ m/TRACE_EVENT\((.*?),/) {<br>
+ $tracepointname = $1;<br>
+ }<br>
+ if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/<wbr>) {<br>
+ $tracepointname = $1;<br>
+ }<br>
+ if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {<br>
+ $tracepointname = $2;<br>
+ }<br>
+ $tracepointname =~ s/^\s+//; #strip leading whitespace<br>
+ if ($prototype =~ m/TP_PROTO\((.*?)\)/) {<br>
+ $tracepointargs = $1;<br>
+ }<br>
+ if (($tracepointname eq 0) || ($tracepointargs eq 0)) {<br>
+ print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n".<br>
+ "$prototype\n";<br>
+ } else {<br>
+ $prototype = "static inline void trace_$tracepointname($<wbr>tracepointargs)";<br>
+ }<br>
+}<br>
+<br>
+sub syscall_munge() {<br>
+ my $void = 0;<br>
+<br>
+ $prototype =~ s@[\r\n\t]+@ @gos; # strip newlines/CR's/tabs<br>
+## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-<wbr>zA-Z0-9_)*\s*\)/) {<br>
+ if ($prototype =~ m/SYSCALL_DEFINE0/) {<br>
+ $void = 1;<br>
+## $prototype = "long sys_$1(void)";<br>
+ }<br>
+<br>
+ $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name<br>
+ if ($prototype =~ m/long (sys_.*?),/) {<br>
+ $prototype =~ s/,/\(/;<br>
+ } elsif ($void) {<br>
+ $prototype =~ s/\)/\(void\)/;<br>
+ }<br>
+<br>
+ # now delete all of the odd-number commas in $prototype<br>
+ # so that arg types & arg names don't have a comma between them<br>
+ my $count = 0;<br>
+ my $len = length($prototype);<br>
+ if ($void) {<br>
+ $len = 0; # skip the for-loop<br>
+ }<br>
+ for (my $ix = 0; $ix < $len; $ix++) {<br>
+ if (substr($prototype, $ix, 1) eq ',') {<br>
+ $count++;<br>
+ if ($count % 2 == 1) {<br>
+ substr($prototype, $ix, 1) = ' ';<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+sub process_proto_function($$) {<br>
+ my $x = shift;<br>
+ my $file = shift;<br>
+<br>
+ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line<br>
+<br>
+ if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) {<br>
+ # do nothing<br>
+ }<br>
+ elsif ($x =~ /([^\{]*)/) {<br>
+ $prototype .= $1;<br>
+ }<br>
+<br>
+ if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {<br>
+ $prototype =~ s@/\*.*?\*/@@gos; # strip comments.<br>
+ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.<br>
+ $prototype =~ s@^\s+@@gos; # strip leading spaces<br>
+ if ($prototype =~ /SYSCALL_DEFINE/) {<br>
+ syscall_munge();<br>
+ }<br>
+ if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||<br>
+ $prototype =~ /DEFINE_SINGLE_EVENT/)<br>
+ {<br>
+ tracepoint_munge($file);<br>
+ }<br>
+ dump_function($prototype, $file);<br>
+ reset_state();<br>
+ }<br>
+}<br>
+<br>
+sub process_proto_type($$) {<br>
+ my $x = shift;<br>
+ my $file = shift;<br>
+<br>
+ $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.<br>
+ $x =~ s@^\s+@@gos; # strip leading spaces<br>
+ $x =~ s@\s+$@@gos; # strip trailing spaces<br>
+ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line<br>
+<br>
+ if ($x =~ /^#/) {<br>
+ # To distinguish preprocessor directive from regular declaration later.<br>
+ $x .= ";";<br>
+ }<br>
+<br>
+ while (1) {<br>
+ if ( $x =~ /([^{};]*)([{};])(.*)/ ) {<br>
+ $prototype .= $1 . $2;<br>
+ ($2 eq '{') && $brcount++;<br>
+ ($2 eq '}') && $brcount--;<br>
+ if (($2 eq ';') && ($brcount == 0)) {<br>
+ dump_declaration($prototype, $file);<br>
+ reset_state();<br>
+ last;<br>
+ }<br>
+ $x = $3;<br>
+ } else {<br>
+ $prototype .= $x;<br>
+ last;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+# xml_escape: replace <, >, and & in the text stream;<br>
+#<br>
+# however, formatting controls that are generated internally/locally in the<br>
+# kernel-doc script are not escaped here; instead, they begin life like<br>
+# $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings<br>
+# are converted to their mnemonic-expected output, without the 4 * '\' & ':',<br>
+# just before actual output; (this is done by local_unescape())<br>
+sub xml_escape($) {<br>
+ my $text = shift;<br>
+ if (($output_mode eq "text") || ($output_mode eq "man")) {<br>
+ return $text;<br>
+ }<br>
+ $text =~ s/\&/\\\\\\amp;/g;<br>
+ $text =~ s/\</\\\\\\lt;/g;<br>
+ $text =~ s/\>/\\\\\\gt;/g;<br>
+ return $text;<br>
+}<br>
+<br>
+# xml_unescape: reverse the effects of xml_escape<br>
+sub xml_unescape($) {<br>
+ my $text = shift;<br>
+ if (($output_mode eq "text") || ($output_mode eq "man")) {<br>
+ return $text;<br>
+ }<br>
+ $text =~ s/\\\\\\amp;/\&/g;<br>
+ $text =~ s/\\\\\\lt;/</g;<br>
+ $text =~ s/\\\\\\gt;/>/g;<br>
+ return $text;<br>
+}<br>
+<br>
+# convert local escape strings to html<br>
+# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes)<br>
+sub local_unescape($) {<br>
+ my $text = shift;<br>
+ if (($output_mode eq "text") || ($output_mode eq "man")) {<br>
+ return $text;<br>
+ }<br>
+ $text =~ s/\\\\\\\\lt:/</g;<br>
+ $text =~ s/\\\\\\\\gt:/>/g;<br>
+ return $text;<br>
+}<br>
+<br>
+sub map_filename($) {<br>
+ my $file;<br>
+ my ($orig_file) = @_;<br>
+<br>
+ if (defined($ENV{'SRCTREE'})) {<br>
+ $file = "$ENV{'SRCTREE'}" . "/" . $orig_file;<br>
+ } else {<br>
+ $file = $orig_file;<br>
+ }<br>
+<br>
+ if (defined($source_map{$file})) {<br>
+ $file = $source_map{$file};<br>
+ }<br>
+<br>
+ return $file;<br>
+}<br>
+<br>
+sub process_export_file($) {<br>
+ my ($orig_file) = @_;<br>
+ my $file = map_filename($orig_file);<br>
+<br>
+ if (!open(IN,"<$file")) {<br>
+ print STDERR "Error: Cannot open file $file\n";<br>
+ ++$errors;<br>
+ return;<br>
+ }<br>
+<br>
+ while (<IN>) {<br>
+ if (/$export_symbol/) {<br>
+ $function_table{$2} = 1;<br>
+ }<br>
+ }<br>
+<br>
+ close(IN);<br>
+}<br>
+<br>
+sub process_file($) {<br>
+ my $file;<br>
+ my $identifier;<br>
+ my $func;<br>
+ my $descr;<br>
+ my $in_purpose = 0;<br>
+ my $initial_section_counter = $section_counter;<br>
+ my ($orig_file) = @_;<br>
+ my $leading_space;<br>
+<br>
+ $file = map_filename($orig_file);<br>
+<br>
+ if (!open(IN,"<$file")) {<br>
+ print STDERR "Error: Cannot open file $file\n";<br>
+ ++$errors;<br>
+ return;<br>
+ }<br>
+<br>
+ $. = 1;<br>
+<br>
+ $section_counter = 0;<br>
+ while (<IN>) {<br>
+ while (s/\\\s*$//) {<br>
+ $_ .= <IN>;<br>
+ }<br>
+ if ($state == STATE_NORMAL) {<br>
+ if (/$doc_start/o) {<br>
+ $state = STATE_NAME; # next line is always the function name<br>
+ $in_doc_sect = 0;<br>
+ $declaration_start_line = $. + 1;<br>
+ }<br>
+ } elsif ($state == STATE_NAME) {# this line is the function name (always)<br>
+ if (/$doc_block/o) {<br>
+ $state = STATE_DOCBLOCK;<br>
+ $contents = "";<br>
+ $new_start_line = $. + 1;<br>
+<br>
+ if ( $1 eq "" ) {<br>
+ $section = $section_intro;<br>
+ } else {<br>
+ $section = $1;<br>
+ }<br>
+ }<br>
+ elsif (/$doc_decl/o) {<br>
+ $identifier = $1;<br>
+ if (/\s*([\w\s]+?)\s*-/) {<br>
+ $identifier = $1;<br>
+ }<br>
+<br>
+ $state = STATE_FIELD;<br>
+ # if there's no @param blocks need to set up default section<br>
+ # here<br>
+ $contents = "";<br>
+ $section = $section_default;<br>
+ $new_start_line = $. + 1;<br>
+ if (/-(.*)/) {<br>
+ # strip leading/trailing/multiple spaces<br>
+ $descr= $1;<br>
+ $descr =~ s/^\s*//;<br>
+ $descr =~ s/\s*$//;<br>
+ $descr =~ s/\s+/ /g;<br>
+ $declaration_purpose = xml_escape($descr);<br>
+ $in_purpose = 1;<br>
+ } else {<br>
+ $declaration_purpose = "";<br>
+ }<br>
+<br>
+ if (($declaration_purpose eq "") && $verbose) {<br>
+ print STDERR "${file}:$.: warning: missing initial short description on line:\n";<br>
+ print STDERR $_;<br>
+ ++$warnings;<br>
+ }<br>
+<br>
+ if ($identifier =~ m/^struct/) {<br>
+ $decl_type = 'struct';<br>
+ } elsif ($identifier =~ m/^union/) {<br>
+ $decl_type = 'union';<br>
+ } elsif ($identifier =~ m/^enum/) {<br>
+ $decl_type = 'enum';<br>
+ } elsif ($identifier =~ m/^typedef/) {<br>
+ $decl_type = 'typedef';<br>
+ } else {<br>
+ $decl_type = 'function';<br>
+ }<br>
+<br>
+ if ($verbose) {<br>
+ print STDERR "${file}:$.: info: Scanning doc for $identifier\n";<br>
+ }<br>
+ } else {<br>
+ print STDERR "${file}:$.: warning: Cannot understand $_ on line $.",<br>
+ " - I thought it was a doc line\n";<br>
+ ++$warnings;<br>
+ $state = STATE_NORMAL;<br>
+ }<br>
+ } elsif ($state == STATE_FIELD) { # look for head: lines, and include content<br>
+ if (/$doc_sect/i) { # case insensitive for supported section names<br>
+ $newsection = $1;<br>
+ $newcontents = $2;<br>
+<br>
+ # map the supported section names to the canonical names<br>
+ if ($newsection =~ m/^description$/i) {<br>
+ $newsection = $section_default;<br>
+ } elsif ($newsection =~ m/^context$/i) {<br>
+ $newsection = $section_context;<br>
+ } elsif ($newsection =~ m/^returns?$/i) {<br>
+ $newsection = $section_return;<br>
+ } elsif ($newsection =~ m/^\@return$/) {<br>
+ # special: @return is a section, not a param description<br>
+ $newsection = $section_return;<br>
+ }<br>
+<br>
+ if (($contents ne "") && ($contents ne "\n")) {<br>
+ if (!$in_doc_sect && $verbose) {<br>
+ print STDERR "${file}:$.: warning: contents before sections\n";<br>
+ ++$warnings;<br>
+ }<br>
+ dump_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ }<br>
+<br>
+ $in_doc_sect = 1;<br>
+ $in_purpose = 0;<br>
+ $contents = $newcontents;<br>
+ $new_start_line = $.;<br>
+ while ((substr($contents, 0, 1) eq " ") ||<br>
+ substr($contents, 0, 1) eq "\t") {<br>
+ $contents = substr($contents, 1);<br>
+ }<br>
+ if ($contents ne "") {<br>
+ $contents .= "\n";<br>
+ }<br>
+ $section = $newsection;<br>
+ $leading_space = undef;<br>
+ } elsif (/$doc_end/) {<br>
+ if (($contents ne "") && ($contents ne "\n")) {<br>
+ dump_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ $contents = "";<br>
+ }<br>
+ # look for doc_com + <text> + doc_end:<br>
+ if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/'<wbr>) {<br>
+ print STDERR "${file}:$.: warning: suspicious ending line: $_";<br>
+ ++$warnings;<br>
+ }<br>
+<br>
+ $prototype = "";<br>
+ $state = STATE_PROTO;<br>
+ $brcount = 0;<br>
+# print STDERR "end of doc comment, looking for prototype\n";<br>
+ } elsif (/$doc_content/) {<br>
+ # miguel-style comment kludge, look for blank lines after<br>
+ # @parameter line to signify start of description<br>
+ if ($1 eq "") {<br>
+ if ($section =~ m/^@/ || $section eq $section_context) {<br>
+ dump_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ $contents = "";<br>
+ $new_start_line = $.;<br>
+ } else {<br>
+ $contents .= "\n";<br>
+ }<br>
+ $in_purpose = 0;<br>
+ } elsif ($in_purpose == 1) {<br>
+ # Continued declaration purpose<br>
+ chomp($declaration_purpose);<br>
+ $declaration_purpose .= " " . xml_escape($1);<br>
+ $declaration_purpose =~ s/\s+/ /g;<br>
+ } else {<br>
+ my $cont = $1;<br>
+ if ($section =~ m/^@/ || $section eq $section_context) {<br>
+ if (!defined $leading_space) {<br>
+ if ($cont =~ m/^(\s+)/) {<br>
+ $leading_space = $1;<br>
+ } else {<br>
+ $leading_space = "";<br>
+ }<br>
+ }<br>
+<br>
+ $cont =~ s/^$leading_space//;<br>
+ }<br>
+ $contents .= $cont . "\n";<br>
+ }<br>
+ } else {<br>
+ # i dont know - bad line? ignore.<br>
+ print STDERR "${file}:$.: warning: bad line: $_";<br>
+ ++$warnings;<br>
+ }<br>
+ } elsif ($state == STATE_INLINE) { # scanning for inline parameters<br>
+ # First line (state 1) needs to be a @parameter<br>
+ if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {<br>
+ $section = $1;<br>
+ $contents = $2;<br>
+ $new_start_line = $.;<br>
+ if ($contents ne "") {<br>
+ while ((substr($contents, 0, 1) eq " ") ||<br>
+ substr($contents, 0, 1) eq "\t") {<br>
+ $contents = substr($contents, 1);<br>
+ }<br>
+ $contents .= "\n";<br>
+ }<br>
+ $inline_doc_state = STATE_INLINE_TEXT;<br>
+ # Documentation block end */<br>
+ } elsif (/$doc_inline_end/) {<br>
+ if (($contents ne "") && ($contents ne "\n")) {<br>
+ dump_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ $contents = "";<br>
+ }<br>
+ $state = STATE_PROTO;<br>
+ $inline_doc_state = STATE_INLINE_NA;<br>
+ # Regular text<br>
+ } elsif (/$doc_content/) {<br>
+ if ($inline_doc_state == STATE_INLINE_TEXT) {<br>
+ $contents .= $1 . "\n";<br>
+ # nuke leading blank lines<br>
+ if ($contents =~ /^\s*$/) {<br>
+ $contents = "";<br>
+ }<br>
+ } elsif ($inline_doc_state == STATE_INLINE_NAME) {<br>
+ $inline_doc_state = STATE_INLINE_ERROR;<br>
+ print STDERR "${file}:$.: warning: ";<br>
+ print STDERR "Incorrect use of kernel-doc format: $_";<br>
+ ++$warnings;<br>
+ }<br>
+ }<br>
+ } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype)<br>
+ if (/$doc_inline_oneline/) {<br>
+ $section = $1;<br>
+ $contents = $2;<br>
+ if ($contents ne "") {<br>
+ $contents .= "\n";<br>
+ dump_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ $contents = "";<br>
+ }<br>
+ } elsif (/$doc_inline_start/) {<br>
+ $state = STATE_INLINE;<br>
+ $inline_doc_state = STATE_INLINE_NAME;<br>
+ } elsif ($decl_type eq 'function') {<br>
+ process_proto_function($_, $file);<br>
+ } else {<br>
+ process_proto_type($_, $file);<br>
+ }<br>
+ } elsif ($state == STATE_DOCBLOCK) {<br>
+ if (/$doc_end/)<br>
+ {<br>
+ dump_doc_section($file, $section, xml_escape($contents));<br>
+ $section = $section_default;<br>
+ $contents = "";<br>
+ $function = "";<br>
+ %parameterdescs = ();<br>
+ %parametertypes = ();<br>
+ @parameterlist = ();<br>
+ %sections = ();<br>
+ @sectionlist = ();<br>
+ $prototype = "";<br>
+ $state = STATE_NORMAL;<br>
+ }<br>
+ elsif (/$doc_content/)<br>
+ {<br>
+ if ( $1 eq "" )<br>
+ {<br>
+ $contents .= $blankline;<br>
+ }<br>
+ else<br>
+ {<br>
+ $contents .= $1 . "\n";<br>
+ }<br>
+ }<br>
+ }<br>
+ }<br>
+ if ($initial_section_counter == $section_counter) {<br>
+ print STDERR "${file}:1: warning: no structured comments found\n";<br>
+ if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) {<br>
+ print STDERR " Was looking for '$_'.\n" for keys %function_table;<br>
+ }<br>
+ if ($output_mode eq "xml") {<br>
+ # The template wants at least one RefEntry here; make one.<br>
+ print "<refentry>\n";<br>
+ print " <refnamediv>\n";<br>
+ print " <refname>\n";<br>
+ print " ${orig_file}\n";<br>
+ print " </refname>\n";<br>
+ print " <refpurpose>\n";<br>
+ print " Document generation inconsistency\n";<br>
+ print " </refpurpose>\n";<br>
+ print " </refnamediv>\n";<br>
+ print " <refsect1>\n";<br>
+ print " <title>\n";<br>
+ print " Oops\n";<br>
+ print " </title>\n";<br>
+ print " <warning>\n";<br>
+ print " <para>\n";<br>
+ print " The template for this document tried to insert\n";<br>
+ print " the structured comment from the file\n";<br>
+ print " <filename>${orig_file}</<wbr>filename> at this point,\n";<br>
+ print " but none was found.\n";<br>
+ print " This dummy section is inserted to allow\n";<br>
+ print " generation to continue.\n";<br>
+ print " </para>\n";<br>
+ print " </warning>\n";<br>
+ print " </refsect1>\n";<br>
+ print "</refentry>\n";<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+<br>
+$kernelversion = get_kernel_version();<br>
+<br>
+# generate a sequence of code that will splice in highlighting information<br>
+# using the s// operator.<br>
+for (my $k = 0; $k < @highlights; $k++) {<br>
+ my $pattern = $highlights[$k][0];<br>
+ my $result = $highlights[$k][1];<br>
+# print STDERR "scanning pattern:$pattern, highlight:($result)\n";<br>
+ $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";<br>
+}<br>
+<br>
+# Read the file that maps relative names to absolute names for<br>
+# separate source and object directories and for shadow trees.<br>
+if (open(SOURCE_MAP, "<.tmp_filelist.txt")) {<br>
+ my ($relname, $absname);<br>
+ while(<SOURCE_MAP>) {<br>
+ chop();<br>
+ ($relname, $absname) = (split())[0..1];<br>
+ $relname =~ s:^/+::;<br>
+ $source_map{$relname} = $absname;<br>
+ }<br>
+ close(SOURCE_MAP);<br>
+}<br>
+<br>
+if ($output_selection == OUTPUT_EXPORTED ||<br>
+ $output_selection == OUTPUT_INTERNAL) {<br>
+<br>
+ push(@export_file_list, @ARGV);<br>
+<br>
+ foreach (@export_file_list) {<br>
+ chomp;<br>
+ process_export_file($_);<br>
+ }<br>
+}<br>
+<br>
+foreach (@ARGV) {<br>
+ chomp;<br>
+ process_file($_);<br>
+}<br>
+if ($verbose && $errors) {<br>
+ print STDERR "$errors errors\n";<br>
+}<br>
+if ($verbose && $warnings) {<br>
+ print STDERR "$warnings warnings\n";<br>
+}<br>
+<br>
+exit($errors);<br>
diff --git a/libdmmp/docs/libdmmp.h.3 b/libdmmp/docs/libdmmp.h.3<br>
new file mode 100644<br>
index 0000000..45d5be3<br>
--- /dev/null<br>
+++ b/libdmmp/docs/libdmmp.h.3<br>
@@ -0,0 +1,113 @@<br>
+.TH "libdmmp.h" 3 "January 2016" "Device Mapper Multipath API - libdmmp Manual"<br>
+<br>
+.SH NAME<br>
+libdmmp.h \- Device Mapper Multipath API.<br>
+<br>
+.SH SYNOPSIS<br>
+#include <libdmmp/libdmmp.h><br>
+<br>
+.SH "DESCRIPTION"<br>
+<br>
+All the libdmmp public functions ships its own man pages.<br>
+Use 'man 3 <function_name>' to check the detail usage.<br>
+<br>
+.SH "USAGE"<br>
+<br>
+To use libdmmp in your project, we suggest to use the 'pkg-config' way:<br>
+<br>
+ * Add this line into your <a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a>:<br>
+<br>
+ PKG_CHECK_MODULES([LIBDMMP], [libdmmp])<br>
+<br>
+ * Add these lines into your Makefile.am:<br>
+<br>
+ foo_LDFLAGS += $(LIBDMMP_LIBS)<br>
+ foo_CFLAGS += $(LIBDMMP_CFLAGS)<br>
+<br>
+.SH LOG HANDLING<br>
+<br>
+The log handler function could be set via 'dmmp_context_log_func_set()'.<br>
+The log priority could be set via 'dmmp_context_log_priority_<wbr>set()'.<br>
+<br>
+By default, the log priorities is 'DMMP_LOG_PRIORITY_WARNING'.<br>
+By default, the log handler is print log to STDERR, and its code is listed<br>
+below in case you want to create your own log handler.<br>
+<br>
+ static int _DMMP_LOG_STRERR_ALIGN_WIDTH = 80;<br>
+<br>
+ static void _log_stderr(struct dmmp_context *ctx,<br>
+ enum dmmp_log_priority priority,<br>
+ const char *file, int line,<br>
+ const char *func_name,<br>
+ const char *format, va_list args)<br>
+ {<br>
+ int printed_bytes = 0;<br>
+<br>
+ printed_bytes += fprintf(stderr, "libdmmp %s: ",<br>
+ dmmp_log_priority_str(<wbr>priority));<br>
+ printed_bytes += vfprintf(stderr, format, args);<br>
+ userdata = dmmp_context_userdata_get(ctx)<wbr>;<br>
+ if (userdata != NULL)<br>
+ fprintf(stderr, "(with user data at memory address %p)",<br>
+ userdata);<br>
+<br>
+ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) {<br>
+ fprintf(stderr, "%*s # %s:%s():%d\n",<br>
+ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file,<br>
+ func_name, line);<br>
+ } else {<br>
+ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line);<br>
+ }<br>
+ }<br>
+<br>
+<br>
+.SH "SAMPLE CODE"<br>
+<br>
+ #include <libdmmp/libdmmp.h><br>
+<br>
+ int main(int argc, char *argv[]) {<br>
+ struct dmmp_context *ctx = NULL;<br>
+ struct dmmp_mpath **dmmp_mps = NULL;<br>
+ struct dmmp_path_group **dmmp_pgs = NULL;<br>
+ struct dmmp_path **dmmp_ps = NULL;<br>
+ uint32_t dmmp_mp_count = 0;<br>
+ uint32_t dmmp_pg_count = 0;<br>
+ uint32_t dmmp_p_count = 0;<br>
+ const char *name = NULL;<br>
+ const char *wwid = NULL;<br>
+ uint32_t i = 0;<br>
+ int rc = DMMP_OK;<br>
+<br>
+ ctx = dmmp_context_new();<br>
+ dmmp_context_log_priority_set(<wbr>ctx, DMMP_LOG_PRIORITY_DEBUG);<br>
+ // By default, log will be printed to STDERR, you could<br>
+ // change that via dmmp_context_log_func_set()<br>
+ rc = dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count);<br>
+ if (rc != DMMP_OK) {<br>
+ printf("dmmp_mpath_array_get() failed with %d: %s", rc,<br>
+ dmmp_strerror(rc));<br>
+ goto out;<br>
+ }<br>
+ for (i = 0; i < dmmp_mp_count; ++i) {<br>
+ name = dmmp_mpath_name_get(dmmp_mps[<wbr>i]);<br>
+ wwid = dmmp_mpath_wwid_get(dmmp_mps[<wbr>i]);<br>
+ printf("dmmp_mpath_array_get()<wbr>: Got mpath: %s %s\n", name,<br>
+ wwid);<br>
+ // You could use dmmp_path_group_array_get() to retrieve<br>
+ // path group information and then invoke dmmp_path_array_get()<br>
+ // for path information.<br>
+ }<br>
+<br>
+ out:<br>
+ dmmp_context_free(ctx);<br>
+ dmmp_mpath_array_free(dmmp_<wbr>mps, dmmp_mp_count);<br>
+ if (rc != DMMP_OK)<br>
+ exit(1);<br>
+ exit(0);<br>
+ }<br>
+<br>
+.SH "LICENSE"<br>
+GPLv2+<br>
+<br>
+.SH "BUG"<br>
+Please report bug to <<a href="mailto:dm-devel@redhat.com">dm-devel@redhat.com</a>><br>
diff --git a/libdmmp/docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a> b/libdmmp/docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a><br>
new file mode 100644<br>
index 0000000..a97acc1<br>
--- /dev/null<br>
+++ b/libdmmp/docs/<a href="http://split-man.pl" rel="noreferrer" target="_blank">split-man.pl</a><br>
@@ -0,0 +1,40 @@<br>
+#!/usr/bin/perl<br>
+# Originally From:<br>
+# <a href="https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt" rel="noreferrer" target="_blank">https://www.kernel.org/doc/<wbr>Documentation/kernel-doc-nano-<wbr>HOWTO.txt</a><br>
+#<br>
+# Changes:<br>
+# * Create manpage section 3 instead of 9.<br>
+# * Replace 'Kernel Hackers Manual' to<br>
+# 'Device Mapper Multipath API - libdmmp Manual'<br>
+# * Remove LINUX from header.<br>
+# * Remove DMMP_DLL_EXPORT.<br>
+$man_sec_num = 3;<br>
+$title = 'Device Mapper Multipath API - libdmmp Manual';<br>
+<br>
+if ( $#ARGV < 0 ) {<br>
+ die "where do I put the results?\n";<br>
+}<br>
+<br>
+mkdir $ARGV[0], 0777;<br>
+$state = 0;<br>
+while (<STDIN>) {<br>
+ if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) {<br>
+ if ( $state == 1 ) { close OUT }<br>
+ $state = 1;<br>
+ $fn = "$ARGV[0]/$1.$man_sec_num";<br>
+ print STDERR "Creating $fn\n";<br>
+ open OUT, ">$fn" or die "can't open $fn: $!\n";<br>
+<br>
+ # Change man page code from 9 to $man_sec_num;<br>
+ s/^\.TH (\"[^\"]*\") 9 \"([^\"]*)\"/\.TH $1 $man_sec_num \"$2\"/;<br>
+ s/Kernel Hacker's Manual/$title/g;<br>
+ s/LINUX//g;<br>
+<br>
+ print OUT $_;<br>
+ }<br>
+ elsif ( $state != 0 ) {<br>
+ print OUT $_;<br>
+ }<br>
+}<br>
+<br>
+close OUT;<br>
diff --git a/libdmmp/libdmmp.c b/libdmmp/libdmmp.c<br>
new file mode 100644<br>
index 0000000..e29a639<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp.c<br>
@@ -0,0 +1,286 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdint.h><br>
+#include <string.h><br>
+#include <sys/time.h><br>
+#include <sys/resource.h><br>
+#include <libudev.h><br>
+#include <errno.h><br>
+#include <libdevmapper.h><br>
+#include <stdbool.h><br>
+#include <unistd.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+#include <mpath_cmd.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+#include "libdmmp_private.h"<br>
+<br>
+#define _DEFAULT_UXSOCK_TIMEOUT 60000<br>
+/* ^ 60 seconds. On system with 10k sdX, dmmp_mpath_array_get()<br>
+ * only take 3.5 seconds, so this default value should be OK for most users.<br>
+ */<br>
+<br>
+#define _DMMP_IPC_SHOW_JSON_CMD "show maps json"<br>
+#define _DMMP_JSON_MAJOR_KEY "major_version"<br>
+#define _DMMP_JSON_MAJOR_VERSION 0<br>
+#define _DMMP_JSON_MAPS_KEY "maps"<br>
+#define _ERRNO_STR_BUFF_SIZE 256<br>
+<br>
+struct dmmp_context {<br>
+ void (*log_func)(struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line, const char *func_name,<br>
+ const char *format, va_list args);<br>
+ int log_priority;<br>
+ void *userdata;<br>
+ unsigned int tmo;<br>
+};<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>context_log_priority_get,<br>
+ struct dmmp_context, ctx, log_priority,<br>
+ int);<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>context_userdata_get, struct dmmp_context, ctx,<br>
+ userdata, void *);<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>context_timeout_get, struct dmmp_context, ctx, tmo,<br>
+ unsigned int);<br>
+<br>
+_dmmp_array_free_func_gen(<wbr>dmmp_mpath_array_free, struct dmmp_mpath,<br>
+ _dmmp_mpath_free);<br>
+<br>
+void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file,<br>
+ int line, const char *func_name, const char *format, ...)<br>
+{<br>
+ va_list args;<br>
+<br>
+ va_start(args, format);<br>
+ ctx->log_func(ctx, priority, file, line, func_name, format, args);<br>
+ va_end(args);<br>
+}<br>
+<br>
+struct dmmp_context *dmmp_context_new(void)<br>
+{<br>
+ struct dmmp_context *ctx = NULL;<br>
+<br>
+ ctx = (struct dmmp_context *) malloc(sizeof(struct dmmp_context));<br>
+<br>
+ if (ctx == NULL)<br>
+ return NULL;<br>
+<br>
+ ctx->log_func = _dmmp_log_stderr;<br>
+ ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT;<br>
+ ctx->userdata = NULL;<br>
+ ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT;<br>
+<br>
+ return ctx;<br>
+}<br>
+<br>
+void dmmp_context_free(struct dmmp_context *ctx)<br>
+{<br>
+ free(ctx);<br>
+}<br>
+<br>
+void dmmp_context_log_priority_set(<wbr>struct dmmp_context *ctx, int priority)<br>
+{<br>
+ assert(ctx != NULL);<br>
+ ctx->log_priority = priority;<br>
+}<br>
+<br>
+void dmmp_context_timeout_set(<wbr>struct dmmp_context *ctx, unsigned int tmo)<br>
+{<br>
+ assert(ctx != NULL);<br>
+ ctx->tmo = tmo;<br>
+}<br>
+<br>
+void dmmp_context_log_func_set<br>
+ (struct dmmp_context *ctx,<br>
+ void (*log_func)(struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line, const char *func_name,<br>
+ const char *format, va_list args))<br>
+{<br>
+ assert(ctx != NULL);<br>
+ ctx->log_func = log_func;<br>
+}<br>
+<br>
+void dmmp_context_userdata_set(<wbr>struct dmmp_context *ctx, void *userdata)<br>
+{<br>
+ assert(ctx != NULL);<br>
+ ctx->userdata = userdata;<br>
+}<br>
+<br>
+int dmmp_mpath_array_get(struct dmmp_context *ctx,<br>
+ struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count)<br>
+{<br>
+ struct dmmp_mpath *dmmp_mp = NULL;<br>
+ int rc = DMMP_OK;<br>
+ char *j_str = NULL;<br>
+ json_object *j_obj = NULL;<br>
+ json_object *j_obj_map = NULL;<br>
+ enum json_tokener_error j_err = json_tokener_success;<br>
+ json_tokener *j_token = NULL;<br>
+ struct array_list *ar_maps = NULL;<br>
+ uint32_t i = 0;<br>
+ int cur_json_major_version = -1;<br>
+ int ar_maps_len = -1;<br>
+ int socket_fd = -1;<br>
+ int errno_save = 0;<br>
+ char errno_str_buff[_ERRNO_STR_<wbr>BUFF_SIZE];<br>
+<br>
+ assert(ctx != NULL);<br>
+ assert(dmmp_mps != NULL);<br>
+ assert(dmmp_mp_count != NULL);<br>
+<br>
+ *dmmp_mps = NULL;<br>
+ *dmmp_mp_count = 0;<br>
+<br>
+ socket_fd = mpath_connect();<br>
+ if (socket_fd == -1) {<br>
+ errno_save = errno;<br>
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);<br>
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);<br>
+ if (errno_save == ECONNREFUSED) {<br>
+ rc = DMMP_ERR_NO_DAEMON;<br>
+ _error(ctx, "Socket connection refuse. "<br>
+ "Maybe multipathd daemon is not running");<br>
+ } else {<br>
+ _error(ctx, "IPC failed with error %d(%s)", errno_save,<br>
+ errno_str_buff);<br>
+ rc = DMMP_ERR_IPC_ERROR;<br>
+ }<br>
+ goto out;<br>
+ }<br>
+<br>
+ if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD,<br>
+ &j_str, ctx->tmo) != 0) {<br>
+ errno_save = errno;<br>
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);<br>
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);<br>
+ mpath_disconnect(socket_fd);<br>
+ if (errno_save == ETIMEDOUT) {<br>
+ rc = DMMP_ERR_IPC_TIMEOUT;<br>
+ _error(ctx, "IPC communication timeout, try to "<br>
+ "increase it via dmmp_context_timeout_set()");<br>
+ goto out;<br>
+ }<br>
+ _error(ctx, "IPC failed when process command '%s' with "<br>
+ "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save,<br>
+ errno_str_buff);<br>
+ rc = DMMP_ERR_IPC_ERROR;<br>
+ goto out;<br>
+ }<br>
+<br>
+ if ((j_str == NULL) || (strlen(j_str) == 0)) {<br>
+ _error(ctx, "IPC return empty reply for command %s",<br>
+ _DMMP_IPC_SHOW_JSON_CMD);<br>
+ rc = DMMP_ERR_IPC_ERROR;<br>
+ goto out;<br>
+ }<br>
+<br>
+ _debug(ctx, "Got json output from multipathd: '%s'", j_str);<br>
+ j_token = json_tokener_new();<br>
+ if (j_token == NULL) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: json_tokener_new() retuned NULL");<br>
+ goto out;<br>
+ }<br>
+ j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1);<br>
+<br>
+ if (j_obj == NULL) {<br>
+ rc = DMMP_ERR_IPC_ERROR;<br>
+ j_err = json_tokener_get_error(j_<wbr>token);<br>
+ _error(ctx, "Failed to parse JSON output from multipathd IPC: "<br>
+ "%s", json_tokener_error_desc(j_err)<wbr>);<br>
+ goto out;<br>
+ }<br>
+<br>
+ _json_obj_get_value(ctx, j_obj, cur_json_major_version,<br>
+ _DMMP_JSON_MAJOR_KEY, json_type_int,<br>
+ json_object_get_int, rc, out);<br>
+<br>
+ if (cur_json_major_version != _DMMP_JSON_MAJOR_VERSION) {<br>
+ rc = DMMP_ERR_INCOMPATIBLE;<br>
+ _error(ctx, "Incompatible multipathd JSON major version %d, "<br>
+ "should be %d", cur_json_major_version,<br>
+ _DMMP_JSON_MAJOR_VERSION);<br>
+ goto out;<br>
+ }<br>
+ _debug(ctx, "multipathd JSON major version(%d) check pass",<br>
+ _DMMP_JSON_MAJOR_VERSION);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj, ar_maps, _DMMP_JSON_MAPS_KEY,<br>
+ json_type_array, json_object_get_array, rc, out);<br>
+<br>
+ if (ar_maps == NULL) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: Got NULL map array from "<br>
+ "_json_obj_get_value()");<br>
+ goto out;<br>
+ }<br>
+<br>
+ ar_maps_len = array_list_length(ar_maps);<br>
+ if (ar_maps_len < 0) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: Got negative length for ar_maps");<br>
+ goto out;<br>
+ }<br>
+ else if (ar_maps_len == 0)<br>
+ goto out;<br>
+ else<br>
+ *dmmp_mp_count = ar_maps_len & UINT32_MAX;<br>
+<br>
+ *dmmp_mps = (struct dmmp_mpath **)<br>
+ malloc(sizeof(struct dmmp_mpath *) * (*dmmp_mp_count));<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mps, rc, out);<br>
+ for (; i < *dmmp_mp_count; ++i)<br>
+ (*dmmp_mps)[i] = NULL;<br>
+<br>
+ for (i = 0; i < *dmmp_mp_count; ++i) {<br>
+ j_obj_map = array_list_get_idx(ar_maps, i);<br>
+ if (j_obj_map == NULL) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: array_list_get_idx() return NULL");<br>
+ goto out;<br>
+ }<br>
+<br>
+ dmmp_mp = _dmmp_mpath_new();<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mp, rc, out);<br>
+ (*dmmp_mps)[i] = dmmp_mp;<br>
+ _good(_dmmp_mpath_update(ctx, dmmp_mp, j_obj_map), rc, out);<br>
+ }<br>
+<br>
+out:<br>
+ if (socket_fd >= 0)<br>
+ mpath_disconnect(socket_fd);<br>
+ free(j_str);<br>
+ if (j_token != NULL)<br>
+ json_tokener_free(j_token);<br>
+ if (j_obj != NULL)<br>
+ json_object_put(j_obj);<br>
+<br>
+ if (rc != DMMP_OK) {<br>
+ dmmp_mpath_array_free(*dmmp_<wbr>mps, *dmmp_mp_count);<br>
+ *dmmp_mps = NULL;<br>
+ *dmmp_mp_count = 0;<br>
+ }<br>
+<br>
+ return rc;<br>
+}<br>
diff --git a/libdmmp/<a href="http://libdmmp.pc.in" rel="noreferrer" target="_blank">libdmmp.pc.in</a> b/libdmmp/<a href="http://libdmmp.pc.in" rel="noreferrer" target="_blank">libdmmp.pc.in</a><br>
new file mode 100644<br>
index 0000000..ebb8cad<br>
--- /dev/null<br>
+++ b/libdmmp/<a href="http://libdmmp.pc.in" rel="noreferrer" target="_blank">libdmmp.pc.in</a><br>
@@ -0,0 +1,9 @@<br>
+includedir=__INCLUDEDIR__<br>
+libdir=__LIBDIR__<br>
+<br>
+Name: libdmmp<br>
+Version: __VERSION__<br>
+Description: Device mapper multipath management library<br>
+Requires:<br>
+Libs: -L${libdir} -ldmmp<br>
+Cflags: -I${includedir}<br>
diff --git a/libdmmp/libdmmp/libdmmp.h b/libdmmp/libdmmp/libdmmp.h<br>
new file mode 100644<br>
index 0000000..3fc8e6f<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp/libdmmp.h<br>
@@ -0,0 +1,653 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+<br>
+#ifndef _LIB_DMMP_H_<br>
+#define _LIB_DMMP_H_<br>
+<br>
+#include <stdint.h><br>
+#include <stdarg.h><br>
+<br>
+#ifdef __cplusplus<br>
+extern "C" {<br>
+#endif<br>
+<br>
+#define DMMP_DLL_EXPORT __attribute__ ((visibility ("default")))<br>
+#define DMMP_DLL_LOCAL __attribute__ ((visibility ("hidden")))<br>
+<br>
+#define DMMP_OK 0<br>
+#define DMMP_ERR_BUG 1<br>
+#define DMMP_ERR_NO_MEMORY 2<br>
+#define DMMP_ERR_IPC_TIMEOUT 3<br>
+#define DMMP_ERR_IPC_ERROR 4<br>
+#define DMMP_ERR_NO_DAEMON 5<br>
+#define DMMP_ERR_INCOMPATIBLE 6<br>
+<br>
+/*<br>
+ * Use the syslog severity level as log priority<br>
+ */<br>
+#define DMMP_LOG_PRIORITY_ERROR 3<br>
+#define DMMP_LOG_PRIORITY_WARNING 4<br>
+#define DMMP_LOG_PRIORITY_INFO 6<br>
+#define DMMP_LOG_PRIORITY_DEBUG 7<br>
+<br>
+#define DMMP_LOG_PRIORITY_DEFAULT DMMP_LOG_PRIORITY_WARNING<br>
+<br>
+/**<br>
+ * dmmp_log_priority_str() - Convert log priority to string.<br>
+ *<br>
+ * Convert log priority to string (const char *).<br>
+ *<br>
+ * @priority:<br>
+ * int. Log priority.<br>
+ *<br>
+ * Return:<br>
+ * const char *. Valid string are:<br>
+ *<br>
+ * * "ERROR" for DMMP_LOG_PRIORITY_ERROR<br>
+ *<br>
+ * * "WARN " for DMMP_LOG_PRIORITY_WARNING<br>
+ *<br>
+ * * "INFO " for DMMP_LOG_PRIORITY_INFO<br>
+ *<br>
+ * * "DEBUG" for DMMP_LOG_PRIORITY_DEBUG<br>
+ *<br>
+ * * "Invalid argument" for invalid log priority.<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_log_priority_str(int priority);<br>
+<br>
+DMMP_DLL_EXPORT struct dmmp_context;<br>
+<br>
+DMMP_DLL_EXPORT struct dmmp_mpath;<br>
+<br>
+DMMP_DLL_EXPORT struct dmmp_path_group;<br>
+<br>
+#define DMMP_PATH_GROUP_STATUS_UNKNOWN 0<br>
+#define DMMP_PATH_GROUP_STATUS_ENABLED 1<br>
+#define DMMP_PATH_GROUP_STATUS_<wbr>DISABLED 2<br>
+#define DMMP_PATH_GROUP_STATUS_ACTIVE 3<br>
+<br>
+DMMP_DLL_EXPORT struct dmmp_path;<br>
+<br>
+#define DMMP_PATH_STATUS_UNKNOWN 0<br>
+//#define DMMP_PATH_STATUS_UNCHECKED 1<br>
+// ^ print.h does not expose this.<br>
+#define DMMP_PATH_STATUS_DOWN 2<br>
+#define DMMP_PATH_STATUS_UP 3<br>
+#define DMMP_PATH_STATUS_SHAKY 4<br>
+#define DMMP_PATH_STATUS_GHOST 5<br>
+#define DMMP_PATH_STATUS_PENDING 6<br>
+#define DMMP_PATH_STATUS_TIMEOUT 7<br>
+//#define DMMP_PATH_STATUS_REMOVED 8<br>
+// ^ print.h does not expose this.<br>
+#define DMMP_PATH_STATUS_DELAYED 9<br>
+<br>
+/**<br>
+ * dmmp_strerror() - Convert error code to string.<br>
+ *<br>
+ * Convert error code (int) to string (const char *):<br>
+ *<br>
+ * * DMMP_OK -- "OK"<br>
+ *<br>
+ * * DMMP_ERR_BUG -- "BUG of libdmmp library"<br>
+ *<br>
+ * * DMMP_ERR_NO_MEMORY -- "Out of memory"<br>
+ *<br>
+ * * DMMP_ERR_IPC_TIMEOUT -- "Timeout when communicate with multipathd,<br>
+ * try to set bigger timeout value via dmmp_context_timeout_set ()"<br>
+ *<br>
+ * * DMMP_ERR_IPC_ERROR -- "Error when communicate with multipathd daemon"<br>
+ *<br>
+ * * DMMP_ERR_NO_DAEMON -- "The multipathd daemon not started"<br>
+ *<br>
+ * * DMMP_ERR_INCOMPATIBLE -- "The multipathd daemon version is not<br>
+ * compatible with current library"<br>
+ *<br>
+ * * Other invalid error number -- "Invalid argument"<br>
+ *<br>
+ * @rc:<br>
+ * int. Return code by libdmmp functions. When provided error code is not a<br>
+ * valid error code, return "Invalid argument".<br>
+ *<br>
+ * Return:<br>
+ * const char *. The meaning of provided error code.<br>
+ *<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_strerror(int rc);<br>
+<br>
+/**<br>
+ * dmmp_context_new() - Create struct dmmp_context.<br>
+ *<br>
+ * The default logging level (DMMP_LOG_PRIORITY_DEFAULT) is<br>
+ * DMMP_LOG_PRIORITY_WARNING which means only warning and error message will be<br>
+ * forward to log handler function. The default log handler function will print<br>
+ * log message to STDERR, to change so, please use dmmp_context_log_func_set()<br>
+ * to set your own log handler, check manpage libdmmp.h(3) for detail.<br>
+ *<br>
+ * Return:<br>
+ * Pointer of 'struct dmmp_context'. Should be freed by<br>
+ * dmmp_context_free().<br>
+ */<br>
+DMMP_DLL_EXPORT struct dmmp_context *dmmp_context_new(void);<br>
+<br>
+/**<br>
+ * dmmp_context_free() - Release the memory of struct dmmp_context.<br>
+ *<br>
+ * Release the memory of struct dmmp_context, but the userdata memory defined<br>
+ * via dmmp_context_userdata_set() will not be touched.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_context_free(struct dmmp_context *ctx);<br>
+<br>
+/**<br>
+ * dmmp_context_timeout_set() - Set IPC timeout.<br>
+ *<br>
+ * By default, the IPC to multipathd daemon will timeout after 60 seconds.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * @tmo:<br>
+ * Timeout in milliseconds(1 seconds equal 1000 milliseconds).<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_context_timeout_set(<wbr>struct dmmp_context *ctx,<br>
+ unsigned int tmo);<br>
+<br>
+/**<br>
+ * dmmp_context_timeout_get() - Get IPC timeout.<br>
+ *<br>
+ * Retrieve timeout value of IPC connection to multipathd daemon.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * unsigned int. Timeout in milliseconds.<br>
+ */<br>
+DMMP_DLL_EXPORT unsigned int dmmp_context_timeout_get(<wbr>struct dmmp_context *ctx);<br>
+<br>
+/**<br>
+ * dmmp_context_log_priority_set(<wbr>) - Set log priority.<br>
+ *<br>
+ *<br>
+ * When library generates log message, only equal or more important(less value)<br>
+ * message will be forwarded to log handler function. Valid log priority values<br>
+ * are:<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_ERROR -- 3<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_WARNING -- 4<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_INFO -- 5<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_DEBUG -- 7<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * @priority:<br>
+ * int, log priority.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_context_log_priority_set(<wbr>struct dmmp_context *ctx,<br>
+ int priority);<br>
+<br>
+/**<br>
+ * dmmp_context_log_priority_get(<wbr>) - Get log priority.<br>
+ *<br>
+ * Retrieve current log priority. Valid log priority values are:<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_ERROR -- 3<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_WARNING -- 4<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_INFO -- 5<br>
+ *<br>
+ * * DMMP_LOG_PRIORITY_DEBUG -- 7<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * int, log priority.<br>
+ */<br>
+DMMP_DLL_EXPORT int dmmp_context_log_priority_get(<wbr>struct dmmp_context *ctx);<br>
+<br>
+/**<br>
+ * dmmp_context_log_func_set() - Set log handler function.<br>
+ *<br>
+ * Set custom log handler. The log handler will be invoked when log message<br>
+ * is equal or more important(less value) than log priority setting.<br>
+ * Please check manpage libdmmp.h(3) for detail usage.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @log_func:<br>
+ * Pointer of log handler function.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_context_log_func_set<br>
+ (struct dmmp_context *ctx,<br>
+ void (*log_func)<br>
+ (struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line, const char *func_name,<br>
+ const char *format, va_list args));<br>
+<br>
+/**<br>
+ * dmmp_context_userdata_set() - Set user data pointer.<br>
+ *<br>
+ * Store user data pointer into 'struct dmmp_context'.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @userdata:<br>
+ * Pointer of user defined data.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_context_userdata_set(<wbr>struct dmmp_context *ctx,<br>
+ void *userdata);<br>
+<br>
+/**<br>
+ * dmmp_context_userdata_get() - Get user data pointer.<br>
+ *<br>
+ * Retrieve user data pointer from 'struct dmmp_context'.<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * void *. Pointer of user defined data.<br>
+ */<br>
+DMMP_DLL_EXPORT void *dmmp_context_userdata_get(<wbr>struct dmmp_context *ctx);<br>
+<br>
+/**<br>
+ * dmmp_mpath_array_get() - Query all existing multipath devices.<br>
+ *<br>
+ * Query all existing multipath devices and store them into a pointer array.<br>
+ * The memory of 'dmmp_mps' should be freed via dmmp_mpath_array_free().<br>
+ *<br>
+ * @ctx:<br>
+ * Pointer of 'struct dmmp_context'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_mps:<br>
+ * Output pointer array of 'struct dmmp_mpath'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_mp_count:<br>
+ * Output pointer of uint32_t. Hold the size of 'dmmp_mps' pointer array.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * int. Valid error codes are:<br>
+ *<br>
+ * * DMMP_OK<br>
+ *<br>
+ * * DMMP_ERR_BUG<br>
+ *<br>
+ * * DMMP_ERR_NO_MEMORY<br>
+ *<br>
+ * * DMMP_ERR_NO_DAEMON<br>
+ *<br>
+ * * DMMP_ERR_INCONSISTENT_DATA<br>
+ *<br>
+ * Error number could be converted to string by dmmp_strerror().<br>
+ */<br>
+DMMP_DLL_EXPORT int dmmp_mpath_array_get(struct dmmp_context *ctx,<br>
+ struct dmmp_mpath ***dmmp_mps,<br>
+ uint32_t *dmmp_mp_count);<br>
+<br>
+/**<br>
+ * dmmp_mpath_array_free() - Free 'struct dmmp_mpath' pointer array.<br>
+ *<br>
+ * Free the 'dmmp_mps' pointer array generated by dmmp_mpath_array_get().<br>
+ * If provided 'dmmp_mps' pointer is NULL or dmmp_mp_count == 0, do nothing.<br>
+ *<br>
+ * @dmmp_mps:<br>
+ * Pointer of 'struct dmmp_mpath' array.<br>
+ * @dmmp_mp_count:<br>
+ * uint32_t, the size of 'dmmp_mps' pointer array.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_mpath_array_free(struct dmmp_mpath **dmmp_mps,<br>
+ uint32_t dmmp_mp_count);<br>
+<br>
+/**<br>
+ * dmmp_mpath_wwid_get() - Retrieve WWID of certain mpath.<br>
+ *<br>
+ * @dmmp_mp:<br>
+ * Pointer of 'struct dmmp_mpath'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * const char *. No need to free this memory, the resources will get<br>
+ * freed when dmmp_mpath_array_free().<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_mpath_wwid_get(struct dmmp_mpath *dmmp_mp);<br>
+<br>
+/**<br>
+ * dmmp_mpath_name_get() - Retrieve name(alias) of certain mpath.<br>
+ *<br>
+ * Retrieve the name (also known as alias) of certain mpath.<br>
+ * When the config 'user_friendly_names' been set 'no', the name will be<br>
+ * identical to WWID retrieved by dmmp_mpath_wwid_get().<br>
+ *<br>
+ * @dmmp_mp:<br>
+ * Pointer of 'struct dmmp_mpath'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * const char *. No need to free this memory, the resources will get<br>
+ * freed when dmmp_mpath_array_free().<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_mpath_name_get(struct dmmp_mpath *dmmp_mp);<br>
+<br>
+/**<br>
+ * dmmp_mpath_kdev_name_get() - Retrieve kernel DEVNAME of certain mpath.<br>
+ *<br>
+ * Retrieve DEVNAME name used by kernel uevent of specified mpath.<br>
+ * Example: 'dm-1'.<br>
+ *<br>
+ * @dmmp_mp:<br>
+ * Pointer of 'struct dmmp_mpath'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * const char *. No need to free this memory, the resources will get<br>
+ * freed when dmmp_mpath_array_free().<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_mpath_kdev_name_get<br>
+ (struct dmmp_mpath *dmmp_mp);<br>
+<br>
+/**<br>
+ * dmmp_path_group_array_get() - Retrieve path groups pointer array.<br>
+ *<br>
+ * Retrieve the path groups of certain mpath.<br>
+ *<br>
+ * The memory of output pointer array is hold by 'struct dmmp_mpath', no<br>
+ * need to free this memory, the resources will got freed when<br>
+ * dmmp_mpath_array_free().<br>
+ *<br>
+ * @dmmp_mp:<br>
+ * Pointer of 'struct dmmp_mpath'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_pgs:<br>
+ * Output pointer of 'struct dmmp_path_group' pointer array.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_pg_count:<br>
+ * Output pointer of uint32_t. Hold the size of 'dmmp_pgs' pointer array.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_path_group_array_get<br>
+ (struct dmmp_mpath *dmmp_mp, struct dmmp_path_group ***dmmp_pgs,<br>
+ uint32_t *dmmp_pg_count);<br>
+<br>
+/**<br>
+ * dmmp_path_group_id_get() - Retrieve path group ID.<br>
+ *<br>
+ * Retrieve the path group ID which could be used to switch active path group<br>
+ * via command:<br>
+ *<br>
+ * multipathd -k'switch multipath mpathb group $id'<br>
+ *<br>
+ * @dmmp_pg:<br>
+ * Pointer of 'struct dmmp_path_group'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * uint32_t.<br>
+ */<br>
+DMMP_DLL_EXPORT uint32_t dmmp_path_group_id_get<br>
+ (struct dmmp_path_group *dmmp_pg);<br>
+<br>
+/**<br>
+ * dmmp_path_group_priority_get() - Retrieve path group priority.<br>
+ *<br>
+ * The enabled path group with highest priority will be next active path group<br>
+ * if active path group down.<br>
+ *<br>
+ * @dmmp_pg:<br>
+ * Pointer of 'struct dmmp_path_group'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * uint32_t.<br>
+ */<br>
+DMMP_DLL_EXPORT uint32_t dmmp_path_group_priority_get<br>
+ (struct dmmp_path_group *dmmp_pg);<br>
+<br>
+/**<br>
+ * dmmp_path_group_status_get() - Retrieve path group status.<br>
+ *<br>
+ * The valid path group statuses are:<br>
+ *<br>
+ * * DMMP_PATH_GROUP_STATUS_UNKNOWN<br>
+ *<br>
+ * * DMMP_PATH_GROUP_STATUS_ENABLED -- standby to be active<br>
+ *<br>
+ * * DMMP_PATH_GROUP_STATUS_<wbr>DISABLED -- disabled due to all path down<br>
+ *<br>
+ * * DMMP_PATH_GROUP_STATUS_ACTIVE -- selected to handle I/O<br>
+ *<br>
+ * @dmmp_pg:<br>
+ * Pointer of 'struct dmmp_path_group'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * uint32_t.<br>
+ */<br>
+DMMP_DLL_EXPORT uint32_t dmmp_path_group_status_get<br>
+ (struct dmmp_path_group *dmmp_pg);<br>
+<br>
+/**<br>
+ * dmmp_path_group_status_str() - Convert path group status to string.<br>
+ *<br>
+ * Convert path group status uint32_t to string (const char *).<br>
+ *<br>
+ * @pg_status:<br>
+ * uint32_t. Path group status.<br>
+ * When provided value is not a valid path group status, return "Invalid<br>
+ * argument".<br>
+ *<br>
+ * Return:<br>
+ * const char *. Valid string are:<br>
+ *<br>
+ * * "Invalid argument"<br>
+ *<br>
+ * * "undef"<br>
+ *<br>
+ * * "enabled"<br>
+ *<br>
+ * * "disabled"<br>
+ *<br>
+ * * "active"<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_path_group_status_str(<wbr>uint32_t pg_status);<br>
+<br>
+/**<br>
+ * dmmp_path_group_selector_get() - Retrieve path group selector.<br>
+ *<br>
+ * Path group selector determine which path in active path group will be<br>
+ * use to next I/O.<br>
+ *<br>
+ * @dmmp_pg:<br>
+ * Pointer of 'struct dmmp_path_group'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * const char *.<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_path_group_selector_get<br>
+ (struct dmmp_path_group *dmmp_pg);<br>
+<br>
+/**<br>
+ * dmmp_path_array_get() - Retrieve path pointer array.<br>
+ *<br>
+ * The memory of output pointer array is hold by 'struct dmmp_mpath', no<br>
+ * need to free this memory, the resources will got freed when<br>
+ * dmmp_mpath_array_free().<br>
+ *<br>
+ * @dmmp_pg:<br>
+ * Pointer of 'struct dmmp_path_group'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_ps:<br>
+ * Output pointer of 'struct dmmp_path' pointer array.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ * @dmmp_p_count:<br>
+ * Output pointer of uint32_t. Hold the size of 'dmmp_ps' pointer array.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * void<br>
+ */<br>
+DMMP_DLL_EXPORT void dmmp_path_array_get(struct dmmp_path_group *dmmp_pg,<br>
+ struct dmmp_path ***dmmp_ps,<br>
+ uint32_t *dmmp_p_count);<br>
+<br>
+/**<br>
+ * dmmp_path_blk_name_get() - Retrieve block name.<br>
+ *<br>
+ * Retrieve block name of certain path. The example of block names are 'sda',<br>
+ * 'nvme0n1'.<br>
+ *<br>
+ * @dmmp_p:<br>
+ * Pointer of 'struct dmmp_path'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * const char *. No need to free this memory, the resources will get<br>
+ * freed when dmmp_mpath_array_free().<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_path_blk_name_get(struct dmmp_path *dmmp_p);<br>
+<br>
+/**<br>
+ * dmmp_path_status_get() - Retrieve the path status.<br>
+ *<br>
+ * The valid path statuses are:<br>
+ *<br>
+ * * DMMP_PATH_STATUS_UNKNOWN<br>
+ *<br>
+ * * DMMP_PATH_STATUS_DOWN<br>
+ *<br>
+ * Path is down and you shouldn't try to send commands to it.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_UP<br>
+ *<br>
+ * Path is up and I/O can be sent to it.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_SHAKY<br>
+ *<br>
+ * Only emc_clariion checker when path not available for "normal"<br>
+ * operations.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_GHOST<br>
+ *<br>
+ * Only hp_sw and rdac checkers. Indicates a "passive/standby"<br>
+ * path on active/passive HP arrays. These paths will return valid<br>
+ * answers to certain SCSI commands (tur, read_capacity, inquiry,<br>
+ * start_stop), but will fail I/O commands. The path needs an<br>
+ * initialization command to be sent to it in order for I/Os to<br>
+ * succeed.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_PENDING<br>
+ *<br>
+ * Available for all async checkers when a check IO is in flight.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_TIMEOUT<br>
+ *<br>
+ * Only tur checker when command timed out.<br>
+ *<br>
+ * * DMMP_PATH_STATUS_DELAYED<br>
+ *<br>
+ * If a path fails after being up for less than delay_watch_checks checks,<br>
+ * when it comes back up again, it will not be marked as up until it has<br>
+ * been up for delay_wait_checks checks. During this time, it is marked as<br>
+ * "delayed".<br>
+ *<br>
+ * @dmmp_p:<br>
+ * Pointer of 'struct dmmp_path'.<br>
+ * If this pointer is NULL, your program will be terminated by assert.<br>
+ *<br>
+ * Return:<br>
+ * uint32_t.<br>
+ */<br>
+DMMP_DLL_EXPORT uint32_t dmmp_path_status_get(struct dmmp_path *dmmp_p);<br>
+<br>
+/**<br>
+ * dmmp_path_status_str() - Convert path status to string.<br>
+ *<br>
+ * Convert path status uint32_t to string (const char *):<br>
+ *<br>
+ * * DMMP_PATH_STATUS_UNKNOWN -- "undef"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_DOWN -- "faulty"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_UP -- "ready"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_SHAKY -- "shaky"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_GHOST -- "ghost"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_PENDING -- "pending"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_TIMEOUT -- "timeout"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_REMOVED -- "removed"<br>
+ *<br>
+ * * DMMP_PATH_STATUS_DELAYED -- "delayed"<br>
+ *<br>
+ * @path_status:<br>
+ * uint32_t. Path status.<br>
+ * When provided value is not a valid path status, return<br>
+ * "Invalid argument".<br>
+ *<br>
+ * Return:<br>
+ * const char *. The meaning of status value.<br>
+ */<br>
+DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status);<br>
+<br>
+#ifdef __cplusplus<br>
+} /* End of extern "C" */<br>
+#endif<br>
+<br>
+#endif /* End of _LIB_DMMP_H_ */<br>
diff --git a/libdmmp/libdmmp_misc.c b/libdmmp/libdmmp_misc.c<br>
new file mode 100644<br>
index 0000000..27f1161<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp_misc.c<br>
@@ -0,0 +1,87 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <string.h><br>
+#include <stdarg.h><br>
+#include <errno.h><br>
+#include <limits.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+#include "libdmmp_private.h"<br>
+<br>
+#define _DMMP_LOG_STRERR_ALIGN_WIDTH 80<br>
+/* ^ Only used in _dmmp_log_stderr() for pretty log output.<br>
+ * When provided log message is less than 80 bytes, fill it with space, then<br>
+ * print code file name, function name, line after the 80th bytes.<br>
+ */<br>
+<br>
+static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = {<br>
+ {DMMP_OK, "OK"},<br>
+ {DMMP_ERR_NO_MEMORY, "Out of memory"},<br>
+ {DMMP_ERR_BUG, "BUG of libdmmp library"},<br>
+ {DMMP_ERR_IPC_TIMEOUT, "Timeout when communicate with multipathd, "<br>
+ "try to increase it via "<br>
+ "dmmp_context_timeout_set()"},<br>
+ {DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"},<br>
+ {DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"},<br>
+ {DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"},<br>
+};<br>
+<br>
+_dmmp_str_func_gen(dmmp_<wbr>strerror, int, rc, _DMMP_RC_MSG_CONV);<br>
+<br>
+static const struct _num_str_conv _DMMP_PRI_CONV[] = {<br>
+ {DMMP_LOG_PRIORITY_DEBUG, "DEBUG"},<br>
+ {DMMP_LOG_PRIORITY_INFO, "INFO"},<br>
+ {DMMP_LOG_PRIORITY_WARNING, "WARNING"},<br>
+ {DMMP_LOG_PRIORITY_ERROR, "ERROR"},<br>
+};<br>
+_dmmp_str_func_gen(dmmp_log_<wbr>priority_str, int, priority, _DMMP_PRI_CONV);<br>
+<br>
+void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line, const char *func_name,<br>
+ const char *format, va_list args)<br>
+{<br>
+ int printed_bytes = 0;<br>
+ void *userdata = NULL;<br>
+<br>
+ printed_bytes += fprintf(stderr, "libdmmp %s: ",<br>
+ dmmp_log_priority_str(<wbr>priority));<br>
+ printed_bytes += vfprintf(stderr, format, args);<br>
+<br>
+ userdata = dmmp_context_userdata_get(ctx)<wbr>;<br>
+ if (userdata != NULL)<br>
+ fprintf(stderr, "(userdata address: %p)",<br>
+ userdata);<br>
+ /* ^ Just demonstrate how userdata could be used and<br>
+ * bypass clang static analyzer about unused ctx argument warning<br>
+ */<br>
+<br>
+ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) {<br>
+ fprintf(stderr, "%*s # %s:%s():%d\n",<br>
+ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file,<br>
+ func_name, line);<br>
+ } else {<br>
+ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line);<br>
+ }<br>
+}<br>
diff --git a/libdmmp/libdmmp_mp.c b/libdmmp/libdmmp_mp.c<br>
new file mode 100644<br>
index 0000000..bc48d0e<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp_mp.c<br>
@@ -0,0 +1,159 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdlib.h><br>
+#include <stdio.h><br>
+#include <inttypes.h><br>
+#include <string.h><br>
+#include <errno.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+#include "libdmmp_private.h"<br>
+<br>
+struct dmmp_mpath {<br>
+ char *wwid;<br>
+ char *alias;<br>
+ uint32_t dmmp_pg_count;<br>
+ struct dmmp_path_group **dmmp_pgs;<br>
+ char *kdev_name;<br>
+};<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>mpath_name_get, struct dmmp_mpath, dmmp_mp,<br>
+ alias, const char *);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>mpath_wwid_get, struct dmmp_mpath, dmmp_mp,<br>
+ wwid, const char *);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>mpath_kdev_name_get, struct dmmp_mpath, dmmp_mp,<br>
+ kdev_name, const char *);<br>
+<br>
+struct dmmp_mpath *_dmmp_mpath_new(void)<br>
+{<br>
+ struct dmmp_mpath *dmmp_mp = NULL;<br>
+<br>
+ dmmp_mp = (struct dmmp_mpath *) malloc(sizeof(struct dmmp_mpath));<br>
+<br>
+ if (dmmp_mp != NULL) {<br>
+ dmmp_mp->wwid = NULL;<br>
+ dmmp_mp->alias = NULL;<br>
+ dmmp_mp->dmmp_pg_count = 0;<br>
+ dmmp_mp->dmmp_pgs = NULL;<br>
+ }<br>
+ return dmmp_mp;<br>
+}<br>
+<br>
+int _dmmp_mpath_update(struct dmmp_context *ctx, struct dmmp_mpath *dmmp_mp,<br>
+ json_object *j_obj_map)<br>
+{<br>
+ int rc = DMMP_OK;<br>
+ const char *wwid = NULL;<br>
+ const char *alias = NULL;<br>
+ struct array_list *ar_pgs = NULL;<br>
+ int ar_pgs_len = -1;<br>
+ uint32_t i = 0;<br>
+ struct dmmp_path_group *dmmp_pg = NULL;<br>
+ const char *kdev_name = NULL;<br>
+<br>
+ assert(ctx != NULL);<br>
+ assert(dmmp_mp != NULL);<br>
+ assert(j_obj_map != NULL);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_map, wwid, "uuid", json_type_string,<br>
+ json_object_get_string, rc, out);<br>
+ _json_obj_get_value(ctx, j_obj_map, alias, "name", json_type_string,<br>
+ json_object_get_string, rc, out);<br>
+ _json_obj_get_value(ctx, j_obj_map, kdev_name, "sysfs",<br>
+ json_type_string, json_object_get_string, rc, out);<br>
+<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, wwid, rc, out);<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, alias, rc, out);<br>
+<br>
+ dmmp_mp->wwid = strdup(wwid);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mp->wwid, rc, out);<br>
+ dmmp_mp->alias = strdup(alias);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mp->alias, rc, out);<br>
+ dmmp_mp->kdev_name = strdup(kdev_name);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mp->kdev_name, rc, out);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_map, ar_pgs, "path_groups",<br>
+ json_type_array, json_object_get_array, rc, out);<br>
+ ar_pgs_len = array_list_length(ar_pgs);<br>
+ if (ar_pgs_len < 0) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: Got negative length for ar_pgs");<br>
+ goto out;<br>
+ }<br>
+ else if (ar_pgs_len == 0)<br>
+ goto out;<br>
+ else<br>
+ dmmp_mp->dmmp_pg_count = ar_pgs_len & UINT32_MAX;<br>
+<br>
+ dmmp_mp->dmmp_pgs = (struct dmmp_path_group **)<br>
+ malloc(sizeof(struct dmmp_path_group *) *<br>
+ dmmp_mp->dmmp_pg_count);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_mp->dmmp_pgs, rc, out);<br>
+ for (; i < dmmp_mp->dmmp_pg_count; ++i)<br>
+ dmmp_mp->dmmp_pgs[i] = NULL;<br>
+<br>
+ for (i = 0; i < dmmp_mp->dmmp_pg_count; ++i) {<br>
+ dmmp_pg = _dmmp_path_group_new();<br>
+ _dmmp_alloc_null_check(ctx, dmmp_pg, rc, out);<br>
+ dmmp_mp->dmmp_pgs[i] = dmmp_pg;<br>
+ _good(_dmmp_path_group_update(<wbr>ctx, dmmp_pg,<br>
+ array_list_get_idx(ar_pgs, i)),<br>
+ rc, out);<br>
+ }<br>
+<br>
+ _debug(ctx, "Got mpath wwid: '%s', alias: '%s'", dmmp_mp->wwid,<br>
+ dmmp_mp->alias);<br>
+<br>
+out:<br>
+ if (rc != DMMP_OK)<br>
+ _dmmp_mpath_free(dmmp_mp);<br>
+ return rc;<br>
+}<br>
+<br>
+void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp)<br>
+{<br>
+ if (dmmp_mp == NULL)<br>
+ return ;<br>
+<br>
+ free((char *) dmmp_mp->alias);<br>
+ free((char *) dmmp_mp->wwid);<br>
+ free((char *) dmmp_mp->kdev_name);<br>
+<br>
+ if (dmmp_mp->dmmp_pgs != NULL)<br>
+ _dmmp_path_group_array_free(<wbr>dmmp_mp->dmmp_pgs,<br>
+ dmmp_mp->dmmp_pg_count);<br>
+<br>
+ free(dmmp_mp);<br>
+}<br>
+<br>
+void dmmp_path_group_array_get(<wbr>struct dmmp_mpath *dmmp_mp,<br>
+ struct dmmp_path_group ***dmmp_pgs,<br>
+ uint32_t *dmmp_pg_count)<br>
+{<br>
+ assert(dmmp_mp != NULL);<br>
+ assert(dmmp_pgs != NULL);<br>
+ assert(dmmp_pg_count != NULL);<br>
+<br>
+ *dmmp_pgs = dmmp_mp->dmmp_pgs;<br>
+ *dmmp_pg_count = dmmp_mp->dmmp_pg_count;<br>
+}<br>
diff --git a/libdmmp/libdmmp_path.c b/libdmmp/libdmmp_path.c<br>
new file mode 100644<br>
index 0000000..47a2162<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp_path.c<br>
@@ -0,0 +1,115 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdlib.h><br>
+#include <inttypes.h><br>
+#include <string.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+#include "libdmmp_private.h"<br>
+<br>
+#define _DMMP_SHOW_PS_INDEX_BLK_NAME 0<br>
+#define _DMMP_SHOW_PS_INDEX_SATAUS 1<br>
+#define _DMMP_SHOW_PS_INDEX_WWID 2<br>
+#define _DMMP_SHOW_PS_INDEX_PGID 3<br>
+<br>
+struct dmmp_path {<br>
+ char *blk_name;<br>
+ uint32_t status;<br>
+};<br>
+<br>
+static const struct _num_str_conv _DMMP_PATH_STATUS_CONV[] = {<br>
+ {DMMP_PATH_STATUS_UNKNOWN, "undef"},<br>
+ {DMMP_PATH_STATUS_UP, "ready"},<br>
+ {DMMP_PATH_STATUS_DOWN, "faulty"},<br>
+ {DMMP_PATH_STATUS_SHAKY, "shaky"},<br>
+ {DMMP_PATH_STATUS_GHOST, "ghost"},<br>
+ {DMMP_PATH_STATUS_PENDING, "i/o pending"},<br>
+ {DMMP_PATH_STATUS_TIMEOUT, "i/o timeout"},<br>
+ {DMMP_PATH_STATUS_DELAYED, "delayed"},<br>
+};<br>
+<br>
+_dmmp_str_func_gen(dmmp_path_<wbr>status_str, uint32_t, path_status,<br>
+ _DMMP_PATH_STATUS_CONV);<br>
+_dmmp_str_conv_func_gen(_<wbr>dmmp_path_status_str_conv, ctx, path_status_str,<br>
+ uint32_t, DMMP_PATH_STATUS_UNKNOWN,<br>
+ _DMMP_PATH_STATUS_CONV);<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_blk_name_get, struct dmmp_path, dmmp_p,<br>
+ blk_name, const char *);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_status_get, struct dmmp_path, dmmp_p,<br>
+ status, uint32_t);<br>
+<br>
+struct dmmp_path *_dmmp_path_new(void)<br>
+{<br>
+ struct dmmp_path *dmmp_p = NULL;<br>
+<br>
+ dmmp_p = (struct dmmp_path *) malloc(sizeof(struct dmmp_path));<br>
+<br>
+ if (dmmp_p != NULL) {<br>
+ dmmp_p->blk_name = NULL;<br>
+ dmmp_p->status = DMMP_PATH_STATUS_UNKNOWN;<br>
+ }<br>
+ return dmmp_p;<br>
+}<br>
+<br>
+int _dmmp_path_update(struct dmmp_context *ctx, struct dmmp_path *dmmp_p,<br>
+ json_object *j_obj_p)<br>
+{<br>
+ int rc = DMMP_OK;<br>
+ const char *blk_name = NULL;<br>
+ const char *status_str = NULL;<br>
+<br>
+ assert(ctx != NULL);<br>
+ assert(dmmp_p != NULL);<br>
+ assert(j_obj_p != NULL);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_p, blk_name, "dev",<br>
+ json_type_string, json_object_get_string, rc, out);<br>
+ _json_obj_get_value(ctx, j_obj_p, status_str, "chk_st",<br>
+ json_type_string, json_object_get_string, rc, out);<br>
+<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, blk_name, rc, out);<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, status_str, rc, out);<br>
+<br>
+ dmmp_p->blk_name = strdup(blk_name);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_p->blk_name, rc, out);<br>
+<br>
+ dmmp_p->status = _dmmp_path_status_str_conv(<wbr>ctx, status_str);<br>
+<br>
+ _debug(ctx, "Got path blk_name: '%s'", dmmp_p->blk_name);<br>
+ _debug(ctx, "Got path status: %s(%" PRIu32 ")",<br>
+ dmmp_path_status_str(dmmp_p-><wbr>status), dmmp_p->status);<br>
+<br>
+out:<br>
+ if (rc != DMMP_OK)<br>
+ _dmmp_path_free(dmmp_p);<br>
+ return rc;<br>
+}<br>
+<br>
+void _dmmp_path_free(struct dmmp_path *dmmp_p)<br>
+{<br>
+ if (dmmp_p == NULL)<br>
+ return;<br>
+ free(dmmp_p->blk_name);<br>
+ free(dmmp_p);<br>
+}<br>
diff --git a/libdmmp/libdmmp_pg.c b/libdmmp/libdmmp_pg.c<br>
new file mode 100644<br>
index 0000000..5149161<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp_pg.c<br>
@@ -0,0 +1,208 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdlib.h><br>
+#include <stdint.h><br>
+#include <inttypes.h><br>
+#include <string.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+#include "libdmmp_private.h"<br>
+<br>
+#define _DMMP_SHOW_PGS_CMD "show groups raw format %w|%g|%p|%t|%s"<br>
+#define _DMMP_SHOW_PG_INDEX_WWID 0<br>
+#define _DMMP_SHOW_PG_INDEX_PG_ID 1<br>
+#define _DMMP_SHOW_PG_INDEX_PRI 2<br>
+#define _DMMP_SHOW_PG_INDEX_STATUS 3<br>
+#define _DMMP_SHOW_PG_INDEX_SELECTOR 4<br>
+<br>
+struct dmmp_path_group {<br>
+ uint32_t id;<br>
+ /* ^ pgindex of struct path, will be used for path group switch */<br>
+ uint32_t status;<br>
+ uint32_t priority;<br>
+ char *selector;<br>
+ uint32_t dmmp_p_count;<br>
+ struct dmmp_path **dmmp_ps;<br>
+};<br>
+<br>
+static const struct _num_str_conv _DMMP_PATH_GROUP_STATUS_CONV[] = {<br>
+ {DMMP_PATH_GROUP_STATUS_<wbr>UNKNOWN, "undef"},<br>
+ {DMMP_PATH_GROUP_STATUS_<wbr>ACTIVE, "active"},<br>
+ {DMMP_PATH_GROUP_STATUS_<wbr>DISABLED, "disabled"},<br>
+ {DMMP_PATH_GROUP_STATUS_<wbr>ENABLED, "enabled"},<br>
+};<br>
+<br>
+_dmmp_str_func_gen(dmmp_path_<wbr>group_status_str, uint32_t, pg_status,<br>
+ _DMMP_PATH_GROUP_STATUS_CONV);<br>
+_dmmp_str_conv_func_gen(_<wbr>dmmp_path_group_status_str_<wbr>conv, ctx, pg_status_str,<br>
+ uint32_t, DMMP_PATH_GROUP_STATUS_<wbr>UNKNOWN,<br>
+ _DMMP_PATH_GROUP_STATUS_CONV);<br>
+<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_group_id_get, struct dmmp_path_group, dmmp_pg,<br>
+ id, uint32_t);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_group_status_get, struct dmmp_path_group,<br>
+ dmmp_pg, status, uint32_t);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_group_priority_get, struct dmmp_path_group,<br>
+ dmmp_pg, priority, uint32_t);<br>
+_dmmp_getter_func_gen(dmmp_<wbr>path_group_selector_get, struct dmmp_path_group,<br>
+ dmmp_pg, selector, const char *);<br>
+_dmmp_array_free_func_gen(_<wbr>dmmp_path_group_array_free, struct dmmp_path_group,<br>
+ _dmmp_path_group_free);<br>
+<br>
+<br>
+struct dmmp_path_group *_dmmp_path_group_new(void)<br>
+{<br>
+ struct dmmp_path_group *dmmp_pg = NULL;<br>
+<br>
+ dmmp_pg = (struct dmmp_path_group *)<br>
+ malloc(sizeof(struct dmmp_path_group));<br>
+<br>
+ if (dmmp_pg != NULL) {<br>
+ dmmp_pg->id = _DMMP_PATH_GROUP_ID_UNKNOWN;<br>
+ dmmp_pg->status = DMMP_PATH_GROUP_STATUS_<wbr>UNKNOWN;<br>
+ dmmp_pg->priority = 0;<br>
+ dmmp_pg->selector = NULL;<br>
+ dmmp_pg->dmmp_p_count = 0;<br>
+ dmmp_pg->dmmp_ps = NULL;<br>
+ }<br>
+ return dmmp_pg;<br>
+}<br>
+int _dmmp_path_group_update(struct dmmp_context *ctx,<br>
+ struct dmmp_path_group *dmmp_pg,<br>
+ json_object *j_obj_pg)<br>
+{<br>
+ int rc = DMMP_OK;<br>
+ uint32_t id = 0;<br>
+ int priority_int = -1 ;<br>
+ const char *status_str = NULL;<br>
+ const char *selector = NULL;<br>
+ struct array_list *ar_ps = NULL;<br>
+ int ar_ps_len = -1;<br>
+ uint32_t i = 0;<br>
+ struct dmmp_path *dmmp_p = NULL;<br>
+<br>
+ assert(ctx != NULL);<br>
+ assert(dmmp_pg != NULL);<br>
+ assert(j_obj_pg != NULL);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_pg, status_str, "dm_st",<br>
+ json_type_string, json_object_get_string, rc, out);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_pg, selector, "selector",<br>
+ json_type_string, json_object_get_string, rc, out);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_pg, priority_int, "pri",<br>
+ json_type_int, json_object_get_int, rc, out);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_pg, id, "group",<br>
+ json_type_int, json_object_get_int, rc, out);<br>
+<br>
+ dmmp_pg->priority = (priority_int <= 0) ? 0 : priority_int & UINT32_MAX;<br>
+<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, status_str, rc, out);<br>
+ _dmmp_null_or_empty_str_check(<wbr>ctx, selector, rc, out);<br>
+<br>
+ dmmp_pg->selector = strdup(selector);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_pg->selector, rc, out);<br>
+<br>
+ dmmp_pg->id = id;<br>
+<br>
+ if (dmmp_pg->id == _DMMP_PATH_GROUP_ID_UNKNOWN) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: Got unknown(%d) path group ID",<br>
+ _DMMP_PATH_GROUP_ID_UNKNOWN);<br>
+ goto out;<br>
+ }<br>
+<br>
+ dmmp_pg->status = _dmmp_path_group_status_str_<wbr>conv(ctx, status_str);<br>
+<br>
+ _json_obj_get_value(ctx, j_obj_pg, ar_ps, "paths",<br>
+ json_type_array, json_object_get_array, rc, out);<br>
+<br>
+ ar_ps_len = array_list_length(ar_ps);<br>
+ if (ar_ps_len < 0) {<br>
+ rc = DMMP_ERR_BUG;<br>
+ _error(ctx, "BUG: Got negative length for ar_ps");<br>
+ goto out;<br>
+ }<br>
+ else if (ar_ps_len == 0)<br>
+ goto out;<br>
+ else<br>
+ dmmp_pg->dmmp_p_count = ar_ps_len & UINT32_MAX;<br>
+<br>
+ dmmp_pg->dmmp_ps = (struct dmmp_path **)<br>
+ malloc(sizeof(struct dmmp_path *) * dmmp_pg->dmmp_p_count);<br>
+ _dmmp_alloc_null_check(ctx, dmmp_pg->dmmp_ps, rc, out);<br>
+ for (; i < dmmp_pg->dmmp_p_count; ++i)<br>
+ dmmp_pg->dmmp_ps[i] = NULL;<br>
+<br>
+ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) {<br>
+ dmmp_p = _dmmp_path_new();<br>
+ _dmmp_alloc_null_check(ctx, dmmp_p, rc, out);<br>
+ dmmp_pg->dmmp_ps[i] = dmmp_p;<br>
+ _good(_dmmp_path_update(ctx, dmmp_p,<br>
+ array_list_get_idx(ar_ps, i)),<br>
+ rc, out);<br>
+ }<br>
+<br>
+ _debug(ctx, "Got path group id: %" PRIu32 "", dmmp_pg->id);<br>
+ _debug(ctx, "Got path group priority: %" PRIu32 "", dmmp_pg->priority);<br>
+ _debug(ctx, "Got path group status: %s(%" PRIu32 ")",<br>
+ dmmp_path_group_status_str(<wbr>dmmp_pg->status), dmmp_pg->status);<br>
+ _debug(ctx, "Got path group selector: '%s'", dmmp_pg->selector);<br>
+<br>
+out:<br>
+ if (rc != DMMP_OK)<br>
+ _dmmp_path_group_free(dmmp_pg)<wbr>;<br>
+ return rc;<br>
+}<br>
+<br>
+void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg)<br>
+{<br>
+ uint32_t i = 0;<br>
+<br>
+ if (dmmp_pg == NULL)<br>
+ return;<br>
+<br>
+ free((char *) dmmp_pg->selector);<br>
+<br>
+ if (dmmp_pg->dmmp_ps != NULL) {<br>
+ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) {<br>
+ _dmmp_path_free(dmmp_pg->dmmp_<wbr>ps[i]);<br>
+ }<br>
+ free(dmmp_pg->dmmp_ps);<br>
+ }<br>
+ free(dmmp_pg);<br>
+}<br>
+<br>
+void dmmp_path_array_get(struct dmmp_path_group *mp_pg,<br>
+ struct dmmp_path ***mp_paths,<br>
+ uint32_t *dmmp_p_count)<br>
+{<br>
+ assert(mp_pg != NULL);<br>
+ assert(mp_paths != NULL);<br>
+ assert(dmmp_p_count != NULL);<br>
+<br>
+ *mp_paths = mp_pg->dmmp_ps;<br>
+ *dmmp_p_count = mp_pg->dmmp_p_count;<br>
+}<br>
diff --git a/libdmmp/libdmmp_private.h b/libdmmp/libdmmp_private.h<br>
new file mode 100644<br>
index 0000000..e23c995<br>
--- /dev/null<br>
+++ b/libdmmp/libdmmp_private.h<br>
@@ -0,0 +1,208 @@<br>
+/*<br>
+ * Copyright (C) 2015 - 2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ * Todd Gill <<a href="mailto:tgill@redhat.com">tgill@redhat.com</a>><br>
+ */<br>
+<br>
+#ifndef _LIB_DMMP_PRIVATE_H_<br>
+#define _LIB_DMMP_PRIVATE_H_<br>
+<br>
+/*<br>
+ * Notes:<br>
+ * Internal/Private functions does not check input argument but using<br>
+ * assert() to abort if NULL pointer found in argument.<br>
+ */<br>
+<br>
+#include <stdint.h><br>
+#include <string.h><br>
+#include <assert.h><br>
+#include <json.h><br>
+<br>
+#include "libdmmp/libdmmp.h"<br>
+<br>
+#ifdef __cplusplus<br>
+extern "C" {<br>
+#endif<br>
+<br>
+#define _good(rc, rc_val, out) \<br>
+ do { \<br>
+ rc_val = rc; \<br>
+ if (rc_val != DMMP_OK) \<br>
+ goto out; \<br>
+ } while(0)<br>
+<br>
+#define _DMMP_PATH_GROUP_ID_UNKNOWN 0<br>
+<br>
+DMMP_DLL_LOCAL struct _num_str_conv {<br>
+ const uint32_t value;<br>
+ const char *str;<br>
+};<br>
+<br>
+#define _dmmp_str_func_gen(func_name, var_type, var, conv_array) \<br>
+const char *func_name(var_type var) { \<br>
+ size_t i = 0; \<br>
+ uint32_t tmp_var = var & UINT32_MAX; \<br>
+ /* In the whole libdmmp, we don't have negative value */ \<br>
+ for (; i < sizeof(conv_array)/sizeof(<wbr>conv_array[0]); ++i) { \<br>
+ if ((conv_array[i].value) == tmp_var) \<br>
+ return conv_array[i].str; \<br>
+ } \<br>
+ return "Invalid argument"; \<br>
+}<br>
+<br>
+#define _dmmp_str_conv_func_gen(func_<wbr>name, ctx, var_name, out_type, \<br>
+ unknown_value, conv_array) \<br>
+static out_type func_name(struct dmmp_context *ctx, const char *var_name) { \<br>
+ size_t i = 0; \<br>
+ for (; i < sizeof(conv_array)/sizeof(<wbr>conv_array[0]); ++i) { \<br>
+ if (strcmp(conv_array[i].str, var_name) == 0) \<br>
+ return conv_array[i].value; \<br>
+ } \<br>
+ _warn(ctx, "Got unknown " #var_name ": '%s'", var_name); \<br>
+ return unknown_value; \<br>
+}<br>
+<br>
+#define _json_obj_get_value(ctx, j_obj, out_value, key, value_type, \<br>
+ value_func, rc, out) \<br>
+do { \<br>
+ json_type j_type = json_type_null; \<br>
+ json_object *j_obj_tmp = NULL; \<br>
+ if (json_object_object_get_ex(j_<wbr>obj, key, &j_obj_tmp) != TRUE) { \<br>
+ _error(ctx, "Invalid JSON output from multipathd IPC: " \<br>
+ "key '%s' not found", key); \<br>
+ rc = DMMP_ERR_IPC_ERROR; \<br>
+ goto out; \<br>
+ } \<br>
+ if (j_obj_tmp == NULL) { \<br>
+ _error(ctx, "BUG: Got NULL j_obj_tmp from " \<br>
+ "json_object_object_get_ex() while it return TRUE"); \<br>
+ rc = DMMP_ERR_BUG; \<br>
+ goto out; \<br>
+ } \<br>
+ j_type = json_object_get_type(j_obj_<wbr>tmp); \<br>
+ if (j_type != value_type) { \<br>
+ _error(ctx, "Invalid value type for key'%s' of JSON output " \<br>
+ "from multipathd IPC. Should be %s(%d), " \<br>
+ "but got %s(%d)", key, json_type_to_name(value_type), \<br>
+ value_type, json_type_to_name(j_type), j_type); \<br>
+ rc = DMMP_ERR_IPC_ERROR; \<br>
+ goto out; \<br>
+ } \<br>
+ out_value = value_func(j_obj_tmp); \<br>
+} while(0);<br>
+<br>
+DMMP_DLL_LOCAL int _dmmp_ipc_exec(struct dmmp_context *ctx, const char *cmd,<br>
+ char **output);<br>
+<br>
+DMMP_DLL_LOCAL struct dmmp_mpath *_dmmp_mpath_new(void);<br>
+DMMP_DLL_LOCAL struct dmmp_path_group *_dmmp_path_group_new(void);<br>
+DMMP_DLL_LOCAL struct dmmp_path *_dmmp_path_new(void);<br>
+<br>
+DMMP_DLL_LOCAL int _dmmp_mpath_update(struct dmmp_context *ctx,<br>
+ struct dmmp_mpath *dmmp_mp,<br>
+ json_object *j_obj_map);<br>
+DMMP_DLL_LOCAL int _dmmp_path_group_update(struct dmmp_context *ctx,<br>
+ struct dmmp_path_group *dmmp_pg,<br>
+ json_object *j_obj_pg);<br>
+DMMP_DLL_LOCAL int _dmmp_path_update(struct dmmp_context *ctx,<br>
+ struct dmmp_path *dmmp_p,<br>
+ json_object *j_obj_p);<br>
+<br>
+DMMP_DLL_LOCAL void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp);<br>
+DMMP_DLL_LOCAL void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg);<br>
+DMMP_DLL_LOCAL void _dmmp_path_group_array_free<br>
+ (struct dmmp_path_group **dmmp_pgs, uint32_t dmmp_pg_count);<br>
+DMMP_DLL_LOCAL void _dmmp_path_free(struct dmmp_path *dmmp_p);<br>
+DMMP_DLL_LOCAL void _dmmp_log(struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line,<br>
+ const char *func_name,<br>
+ const char *format, ...);<br>
+DMMP_DLL_LOCAL void _dmmp_log_err_str(struct dmmp_context *ctx, int rc);<br>
+<br>
+DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority,<br>
+ const char *file, int line,<br>
+ const char *func_name, const char *format,<br>
+ va_list args);<br>
+<br>
+<br>
+#define _dmmp_log_cond(ctx, prio, arg...) \<br>
+ do { \<br>
+ if (dmmp_context_log_priority_<wbr>get(ctx) >= prio) \<br>
+ _dmmp_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, \<br>
+ ## arg); \<br>
+ } while (0)<br>
+<br>
+#define _debug(ctx, arg...) \<br>
+ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_DEBUG, ## arg)<br>
+#define _info(ctx, arg...) \<br>
+ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_INFO, ## arg)<br>
+#define _warn(ctx, arg...) \<br>
+ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_WARNING, ## arg)<br>
+#define _error(ctx, arg...) \<br>
+ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_ERROR, ## arg)<br>
+<br>
+/*<br>
+ * Check pointer returned by malloc() or strdup(), if NULL, set<br>
+ * rc as DMMP_ERR_NO_MEMORY, report error and goto goto_out.<br>
+ */<br>
+#define _dmmp_alloc_null_check(ctx, ptr, rc, goto_out) \<br>
+ do { \<br>
+ if (ptr == NULL) { \<br>
+ rc = DMMP_ERR_NO_MEMORY; \<br>
+ _error(ctx, dmmp_strerror(rc)); \<br>
+ goto goto_out; \<br>
+ } \<br>
+ } while(0)<br>
+<br>
+#define _dmmp_null_or_empty_str_check(<wbr>ctx, var, rc, goto_out) \<br>
+ do { \<br>
+ if (var == NULL) { \<br>
+ rc = DMMP_ERR_BUG; \<br>
+ _error(ctx, "BUG: Got NULL " #var); \<br>
+ goto goto_out; \<br>
+ } \<br>
+ if (strlen(var) == 0) { \<br>
+ rc = DMMP_ERR_BUG; \<br>
+ _error(ctx, "BUG: Got empty " #var); \<br>
+ goto goto_out; \<br>
+ } \<br>
+ } while(0)<br>
+<br>
+#define _dmmp_getter_func_gen(func_<wbr>name, struct_name, struct_data, \<br>
+ prop_name, prop_type) \<br>
+ prop_type func_name(struct_name *struct_data) \<br>
+ { \<br>
+ assert(struct_data != NULL); \<br>
+ return struct_data->prop_name; \<br>
+ }<br>
+<br>
+#define _dmmp_array_free_func_gen(<wbr>func_name, struct_name, struct_free_func) \<br>
+ void func_name(struct_name **ptr_array, uint32_t ptr_count) \<br>
+ { \<br>
+ uint32_t i = 0; \<br>
+ if (ptr_array == NULL) \<br>
+ return; \<br>
+ for (; i < ptr_count; ++i) \<br>
+ struct_free_func(ptr_array[i])<wbr>; \<br>
+ free(ptr_array); \<br>
+ }<br>
+<br>
+#ifdef __cplusplus<br>
+} /* End of extern "C" */<br>
+#endif<br>
+<br>
+#endif /* End of _LIB_DMMP_PRIVATE_H_ */<br>
diff --git a/libdmmp/test/Makefile b/libdmmp/test/Makefile<br>
new file mode 100644<br>
index 0000000..68f1af3<br>
--- /dev/null<br>
+++ b/libdmmp/test/Makefile<br>
@@ -0,0 +1,30 @@<br>
+# Makefile<br>
+#<br>
+# Copyright (C) 2015-2016 Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+#<br>
+include ../../Makefile.inc<br>
+<br>
+_libdmmpdir=../$(libdmmpdir)<br>
+_mpathcmddir=../$(<wbr>mpathcmddir)<br>
+<br>
+TEST_EXEC = libdmmp_test<br>
+SPD_TEST_EXEC = libdmmp_speed_test<br>
+CFLAGS += -I$(_libdmmpdir)<br>
+LDFLAGS += -L$(_libdmmpdir) -ldmmp<br>
+<br>
+all: $(TEST_EXEC) $(SPD_TEST_EXEC)<br>
+<br>
+check: $(TEST_EXEC) $(SPD_TEST_EXEC)<br>
+ sudo env LD_LIBRARY_PATH=$(_libdmmpdir)<wbr>:$(_mpathcmddir) \<br>
+ valgrind --quiet --leak-check=full \<br>
+ --show-reachable=no --show-possibly-lost=no \<br>
+ --trace-children=yes --error-exitcode=1 \<br>
+ ./$(TEST_EXEC)<br>
+ $(MAKE) speed_test<br>
+<br>
+speed_test: $(SPD_TEST_EXEC)<br>
+ sudo env LD_LIBRARY_PATH=$(_libdmmpdir)<wbr>:$(_mpathcmddir) \<br>
+ time -p ./$(SPD_TEST_EXEC)<br>
+<br>
+clean:<br>
+ rm -f $(TEST_EXEC)<br>
diff --git a/libdmmp/test/libdmmp_speed_<wbr>test.c b/libdmmp/test/libdmmp_speed_<wbr>test.c<br>
new file mode 100644<br>
index 0000000..372cd39<br>
--- /dev/null<br>
+++ b/libdmmp/test/libdmmp_speed_<wbr>test.c<br>
@@ -0,0 +1,49 @@<br>
+/*<br>
+ * Copyright (C) 2015-2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdint.h><br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <inttypes.h><br>
+#include <string.h><br>
+#include <pthread.h><br>
+#include <unistd.h><br>
+<br>
+#include <libdmmp/libdmmp.h><br>
+<br>
+int main(int argc, char *argv[])<br>
+{<br>
+ struct dmmp_context *ctx = NULL;<br>
+ struct dmmp_mpath **dmmp_mps = NULL;<br>
+ uint32_t dmmp_mp_count = 0;<br>
+ int rc = EXIT_SUCCESS;<br>
+<br>
+ ctx = dmmp_context_new();<br>
+ dmmp_context_log_priority_set(<wbr>ctx, DMMP_LOG_PRIORITY_WARNING);<br>
+<br>
+ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) {<br>
+ printf("FAILED\n");<br>
+ rc = EXIT_FAILURE;<br>
+ } else {<br>
+ printf("Got %" PRIu32 " mpath\n", dmmp_mp_count);<br>
+ dmmp_mpath_array_free(dmmp_<wbr>mps, dmmp_mp_count);<br>
+ }<br>
+ dmmp_context_free(ctx);<br>
+ exit(rc);<br>
+}<br>
diff --git a/libdmmp/test/libdmmp_test.c b/libdmmp/test/libdmmp_test.c<br>
new file mode 100644<br>
index 0000000..00b40e9<br>
--- /dev/null<br>
+++ b/libdmmp/test/libdmmp_test.c<br>
@@ -0,0 +1,147 @@<br>
+/*<br>
+ * Copyright (C) 2015-2016 Red Hat, Inc.<br>
+ *<br>
+ * This program is free software: you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License as published by<br>
+ * the Free Software Foundation, either version 3 of the License, or<br>
+ * (at your option) any later version.<br>
+ *<br>
+ * This program is distributed in the hope that it will be useful,<br>
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+ * GNU General Public License for more details.<br>
+ *<br>
+ * You should have received a copy of the GNU General Public License<br>
+ * along with this program. If not, see <<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>><wbr>.<br>
+ *<br>
+ * Author: Gris Ge <<a href="mailto:fge@redhat.com">fge@redhat.com</a>><br>
+ */<br>
+<br>
+#include <stdint.h><br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <inttypes.h><br>
+#include <string.h><br>
+#include <pthread.h><br>
+#include <unistd.h><br>
+<br>
+#include <libdmmp/libdmmp.h><br>
+<br>
+#define FAIL(rc, out, ...) \<br>
+ do { \<br>
+ rc = EXIT_FAILURE; \<br>
+ fprintf(stderr, "FAIL: "__VA_ARGS__ ); \<br>
+ goto out; \<br>
+ } while(0)<br>
+#define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ );<br>
+#define FILE_NAME_SIZE 256<br>
+#define TMO 10000 /* Forcing timeout to 10 seconds */<br>
+<br>
+int test_paths(struct dmmp_path_group *mp_pg)<br>
+{<br>
+ struct dmmp_path **mp_ps = NULL;<br>
+ uint32_t mp_p_count = 0;<br>
+ uint32_t i = 0;<br>
+ const char *blk_name = NULL;<br>
+ int rc = EXIT_SUCCESS;<br>
+<br>
+ dmmp_path_array_get(mp_pg, &mp_ps, &mp_p_count);<br>
+ if (mp_p_count == 0)<br>
+ FAIL(rc, out, "dmmp_path_array_get(): Got no path\n");<br>
+ for (i = 0; i < mp_p_count; ++i) {<br>
+ blk_name = dmmp_path_blk_name_get(mp_ps[<wbr>i]);<br>
+ if (blk_name == NULL)<br>
+ FAIL(rc, out, "dmmp_path_blk_name_get(): Got NULL\n");<br>
+ PASS("dmmp_path_blk_name_get()<wbr>: %s\n", blk_name);<br>
+ PASS("dmmp_path_status_get(): %" PRIu32 " -- %s\n",<br>
+ dmmp_path_status_get(mp_ps[i])<wbr>,<br>
+ dmmp_path_status_str(dmmp_<wbr>path_status_get(mp_ps[i])));<br>
+ }<br>
+out:<br>
+ return rc;<br>
+}<br>
+<br>
+int test_path_groups(struct dmmp_mpath *dmmp_mp)<br>
+{<br>
+ struct dmmp_path_group **dmmp_pgs = NULL;<br>
+ uint32_t dmmp_pg_count = 0;<br>
+ uint32_t i = 0;<br>
+ int rc = EXIT_SUCCESS;<br>
+<br>
+ dmmp_path_group_array_get(<wbr>dmmp_mp, &dmmp_pgs, &dmmp_pg_count);<br>
+ if ((dmmp_pg_count == 0) && (dmmp_pgs != NULL))<br>
+ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is not NULL "<br>
+ "but mp_pg_count is 0\n");<br>
+ if ((dmmp_pg_count != 0) && (dmmp_pgs == NULL))<br>
+ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is NULL "<br>
+ "but mp_pg_count is not 0\n");<br>
+ if (dmmp_pg_count == 0)<br>
+ FAIL(rc, out, "dmmp_path_group_array_get(): "<br>
+ "Got 0 path group\n");<br>
+<br>
+ PASS("dmmp_path_group_array_<wbr>get(): Got %" PRIu32 " path groups\n",<br>
+ dmmp_pg_count);<br>
+<br>
+ for (i = 0; i < dmmp_pg_count; ++i) {<br>
+ PASS("dmmp_path_group_id_get()<wbr>: %" PRIu32 "\n",<br>
+ dmmp_path_group_id_get(dmmp_<wbr>pgs[i]));<br>
+ PASS("dmmp_path_group_<wbr>priority_get(): %" PRIu32 "\n",<br>
+ dmmp_path_group_priority_get(<wbr>dmmp_pgs[i]));<br>
+ PASS("dmmp_path_group_status_<wbr>get(): %" PRIu32 " -- %s\n",<br>
+ dmmp_path_group_status_get(<wbr>dmmp_pgs[i]),<br>
+ dmmp_path_group_status_str<br>
+ (dmmp_path_group_status_get(<wbr>dmmp_pgs[i])));<br>
+ PASS("dmmp_path_group_<wbr>selector_get(): %s\n",<br>
+ dmmp_path_group_selector_get(<wbr>dmmp_pgs[i]));<br>
+ rc = test_paths(dmmp_pgs[i]);<br>
+ if (rc != 0)<br>
+ goto out;<br>
+ }<br>
+out:<br>
+ return rc;<br>
+}<br>
+<br>
+int main(int argc, char *argv[])<br>
+{<br>
+ struct dmmp_context *ctx = NULL;<br>
+ struct dmmp_mpath **dmmp_mps = NULL;<br>
+ uint32_t dmmp_mp_count = 0;<br>
+ const char *name = NULL;<br>
+ const char *wwid = NULL;<br>
+ const char *kdev = NULL;<br>
+ uint32_t i = 0;<br>
+ int rc = EXIT_SUCCESS;<br>
+<br>
+ ctx = dmmp_context_new();<br>
+ dmmp_context_log_priority_set(<wbr>ctx, DMMP_LOG_PRIORITY_DEBUG);<br>
+ dmmp_context_userdata_set(ctx, ctx);<br>
+ dmmp_context_userdata_set(ctx, NULL);<br>
+ dmmp_context_timeout_set(ctx, TMO);<br>
+ if (dmmp_context_timeout_get(ctx) != TMO)<br>
+ FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set "<br>
+ "timeout to %u", TMO);<br>
+<br>
+ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)<br>
+ FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");<br>
+ if (dmmp_mp_count == 0)<br>
+ FAIL(rc, out, "dmmp_mpath_array_get(): "<br>
+ "Got no multipath devices\n");<br>
+ PASS("dmmp_mpath_array_get(): Got %" PRIu32 " mpath\n", dmmp_mp_count);<br>
+ for (i = 0; i < dmmp_mp_count; ++i) {<br>
+ name = dmmp_mpath_name_get(dmmp_mps[<wbr>i]);<br>
+ wwid = dmmp_mpath_wwid_get(dmmp_mps[<wbr>i]);<br>
+ kdev = dmmp_mpath_kdev_name_get(dmmp_<wbr>mps[i]);<br>
+ if ((name == NULL) ||(wwid == NULL) || (kdev == NULL))<br>
+ FAIL(rc, out,<br>
+ "dmmp_mpath_array_get(): Got NULL name or wwid");<br>
+ PASS("dmmp_mpath_array_get(): Got mpath(%s): %s %s\n",<br>
+ kdev, name, wwid);<br>
+ rc = test_path_groups(dmmp_mps[i]);<br>
+ if (rc != 0)<br>
+ goto out;<br>
+ }<br>
+ dmmp_mpath_array_free(dmmp_<wbr>mps, dmmp_mp_count);<br>
+out:<br>
+ dmmp_context_free(ctx);<br>
+ exit(rc);<br>
+}<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.8.3.1<br>
<br>
--<br>
dm-devel mailing list<br>
<a href="mailto:dm-devel@redhat.com">dm-devel@redhat.com</a><br>
<a href="https://www.redhat.com/mailman/listinfo/dm-devel" rel="noreferrer" target="_blank">https://www.redhat.com/<wbr>mailman/listinfo/dm-devel</a><br>
</font></span></blockquote></div><br></div>