summaryrefslogtreecommitdiffstats
path: root/utils/statd
diff options
context:
space:
mode:
Diffstat (limited to 'utils/statd')
-rw-r--r--utils/statd/.gitignore1
-rw-r--r--utils/statd/COPYING340
-rw-r--r--utils/statd/Makefile.am93
-rw-r--r--utils/statd/Makefile.in984
-rw-r--r--utils/statd/TODO13
-rw-r--r--utils/statd/callback.c122
-rw-r--r--utils/statd/hostname.c319
-rw-r--r--utils/statd/misc.c51
-rw-r--r--utils/statd/monitor.c399
-rw-r--r--utils/statd/notlist.c246
-rw-r--r--utils/statd/notlist.h65
-rw-r--r--utils/statd/rmtcall.c285
-rw-r--r--utils/statd/sim_sm_inter.x32
-rw-r--r--utils/statd/simu.c59
-rw-r--r--utils/statd/simulate.c226
-rw-r--r--utils/statd/sm-notify.c928
-rw-r--r--utils/statd/sm-notify.man366
-rwxr-xr-xutils/statd/start-statd33
-rw-r--r--utils/statd/stat.c60
-rw-r--r--utils/statd/statd.c549
-rw-r--r--utils/statd/statd.h68
-rw-r--r--utils/statd/statd.man474
-rw-r--r--utils/statd/svc_run.c138
-rw-r--r--utils/statd/system.h18
24 files changed, 5869 insertions, 0 deletions
diff --git a/utils/statd/.gitignore b/utils/statd/.gitignore
new file mode 100644
index 0000000..99b0cce
--- /dev/null
+++ b/utils/statd/.gitignore
@@ -0,0 +1 @@
+sm-notify
diff --git a/utils/statd/COPYING b/utils/statd/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/utils/statd/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
new file mode 100644
index 0000000..6facc15
--- /dev/null
+++ b/utils/statd/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = statd.man sm-notify.man
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = statd sm-notify
+dist_sbin_SCRIPTS = start-statd
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
+ simu.c stat.c statd.c svc_run.c rmtcall.c \
+ notlist.h statd.h system.h
+sm_notify_SOURCES = sm-notify.c
+
+BUILT_SOURCES = $(GENFILES)
+statd_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+sm_notify_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
+
+if CONFIG_RPCGEN
+RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
+$(RPCGEN):
+ make -C ../../tools/rpcgen all
+else
+RPCGEN = @RPCGEN_PATH@
+endif
+
+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -l -o $@ $<
+
+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -m -o $@ $<
+
+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -c -o $@ $<
+
+$(GENFILES_H): %.h: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -h -o $@ $<
+
+MAINTAINERCLEANFILES = Makefile.in
+
+CLEANFILES = $(GENFILES)
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/statd/Makefile.in b/utils/statd/Makefile.in
new file mode 100644
index 0000000..8754504
--- /dev/null
+++ b/utils/statd/Makefile.in
@@ -0,0 +1,984 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = statd$(EXEEXT) sm-notify$(EXEEXT)
+subdir = utils/statd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/aclocal/bsdsignals.m4 \
+ $(top_srcdir)/aclocal/getrandom.m4 \
+ $(top_srcdir)/aclocal/ipv6.m4 \
+ $(top_srcdir)/aclocal/kerberos5.m4 \
+ $(top_srcdir)/aclocal/keyutils.m4 \
+ $(top_srcdir)/aclocal/libblkid.m4 \
+ $(top_srcdir)/aclocal/libcap.m4 \
+ $(top_srcdir)/aclocal/libevent.m4 \
+ $(top_srcdir)/aclocal/libpthread.m4 \
+ $(top_srcdir)/aclocal/libsqlite3.m4 \
+ $(top_srcdir)/aclocal/libtirpc.m4 \
+ $(top_srcdir)/aclocal/libxml2.m4 \
+ $(top_srcdir)/aclocal/nfs-utils.m4 \
+ $(top_srcdir)/aclocal/rpcsec_vers.m4 \
+ $(top_srcdir)/aclocal/tcp-wrappers.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_sbin_SCRIPTS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)" \
+ "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_sm_notify_OBJECTS = sm-notify.$(OBJEXT)
+sm_notify_OBJECTS = $(am_sm_notify_OBJECTS)
+am__DEPENDENCIES_1 =
+sm_notify_DEPENDENCIES = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_statd_OBJECTS = callback.$(OBJEXT) notlist.$(OBJEXT) misc.$(OBJEXT) \
+ monitor.$(OBJEXT) hostname.$(OBJEXT) simu.$(OBJEXT) \
+ stat.$(OBJEXT) statd.$(OBJEXT) svc_run.$(OBJEXT) \
+ rmtcall.$(OBJEXT)
+statd_OBJECTS = $(am_statd_OBJECTS)
+statd_DEPENDENCIES = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+SCRIPTS = $(dist_sbin_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/callback.Po ./$(DEPDIR)/hostname.Po \
+ ./$(DEPDIR)/misc.Po ./$(DEPDIR)/monitor.Po \
+ ./$(DEPDIR)/notlist.Po ./$(DEPDIR)/rmtcall.Po \
+ ./$(DEPDIR)/simu.Po ./$(DEPDIR)/sm-notify.Po \
+ ./$(DEPDIR)/stat.Po ./$(DEPDIR)/statd.Po \
+ ./$(DEPDIR)/svc_run.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(sm_notify_SOURCES) $(statd_SOURCES)
+DIST_SOURCES = $(sm_notify_SOURCES) $(statd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp COPYING \
+ TODO
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GSSD = @GSSD@
+GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@
+GSSGLUE_LIBS = @GSSGLUE_LIBS@
+GSSKRB_CFLAGS = @GSSKRB_CFLAGS@
+GSSKRB_LIBS = @GSSKRB_LIBS@
+HAVE_GETRANDOM = @HAVE_GETRANDOM@
+HAVE_LIBWRAP = @HAVE_LIBWRAP@
+HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@
+IDMAPD = @IDMAPD@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+K5VERS = @K5VERS@
+KRBCFLAGS = @KRBCFLAGS@
+KRBDIR = @KRBDIR@
+KRBLDFLAGS = @KRBLDFLAGS@
+KRBLIBS = @KRBLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBLKID = @LIBBLKID@
+LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
+LIBCRYPT = @LIBCRYPT@
+LIBEVENT = @LIBEVENT@
+LIBKEYUTILS = @LIBKEYUTILS@
+LIBMOUNT = @LIBMOUNT@
+LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@
+LIBMOUNT_LIBS = @LIBMOUNT_LIBS@
+LIBNSL = @LIBNSL@
+LIBOBJS = @LIBOBJS@
+LIBPTHREAD = @LIBPTHREAD@
+LIBS = @LIBS@
+LIBSOCKET = @LIBSOCKET@
+LIBSQLITE = @LIBSQLITE@
+LIBTIRPC = @LIBTIRPC@
+LIBTOOL = @LIBTOOL@
+LIBWRAP = @LIBWRAP@
+LIBXML2 = @LIBXML2@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PLUGINS = @PATH_PLUGINS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RPCGEN_PATH = @RPCGEN_PATH@
+RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@
+RPCSECGSS_LIBS = @RPCSECGSS_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SVCGSSD = @SVCGSSD@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+VERSION = @VERSION@
+XML2_CFLAGS = @XML2_CFLAGS@
+XML2_LIBS = @XML2_LIBS@
+_rpc_pipefsmount = @_rpc_pipefsmount@
+_statedir = @_statedir@
+_sysconfdir = @_sysconfdir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_gss = @enable_gss@
+enable_ipv6 = @enable_ipv6@
+enable_mountconfig = @enable_mountconfig@
+enable_nfsv4 = @enable_nfsv4@
+enable_nfsv41 = @enable_nfsv41@
+enable_svcgss = @enable_svcgss@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kprefix = @kprefix@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+mountfile = @mountfile@
+nfsconfig = @nfsconfig@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rpc_pipefsmount = @rpc_pipefsmount@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+startstatd = @startstatd@
+statdpath = @statdpath@
+statduser = @statduser@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unitdir = @unitdir@
+man8_MANS = statd.man sm-notify.man
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+dist_sbin_SCRIPTS = start-statd
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
+ simu.c stat.c statd.c svc_run.c rmtcall.c \
+ notlist.h statd.h system.h
+
+sm_notify_SOURCES = sm-notify.c
+BUILT_SOURCES = $(GENFILES)
+statd_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+sm_notify_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
+@CONFIG_RPCGEN_FALSE@RPCGEN = @RPCGEN_PATH@
+@CONFIG_RPCGEN_TRUE@RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
+MAINTAINERCLEANFILES = Makefile.in
+CLEANFILES = $(GENFILES)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu utils/statd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/statd/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+sm-notify$(EXEEXT): $(sm_notify_OBJECTS) $(sm_notify_DEPENDENCIES) $(EXTRA_sm_notify_DEPENDENCIES)
+ @rm -f sm-notify$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sm_notify_OBJECTS) $(sm_notify_LDADD) $(LIBS)
+
+statd$(EXEEXT): $(statd_OBJECTS) $(statd_DEPENDENCIES) $(EXTRA_statd_DEPENDENCIES)
+ @rm -f statd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(statd_OBJECTS) $(statd_LDADD) $(LIBS)
+install-dist_sbinSCRIPTS: $(dist_sbin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dist_sbinSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notlist.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmtcall.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simu.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-notify.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svc_run.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/callback.Po
+ -rm -f ./$(DEPDIR)/hostname.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/monitor.Po
+ -rm -f ./$(DEPDIR)/notlist.Po
+ -rm -f ./$(DEPDIR)/rmtcall.Po
+ -rm -f ./$(DEPDIR)/simu.Po
+ -rm -f ./$(DEPDIR)/sm-notify.Po
+ -rm -f ./$(DEPDIR)/stat.Po
+ -rm -f ./$(DEPDIR)/statd.Po
+ -rm -f ./$(DEPDIR)/svc_run.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-dist_sbinSCRIPTS install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/callback.Po
+ -rm -f ./$(DEPDIR)/hostname.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/monitor.Po
+ -rm -f ./$(DEPDIR)/notlist.Po
+ -rm -f ./$(DEPDIR)/rmtcall.Po
+ -rm -f ./$(DEPDIR)/simu.Po
+ -rm -f ./$(DEPDIR)/sm-notify.Po
+ -rm -f ./$(DEPDIR)/stat.Po
+ -rm -f ./$(DEPDIR)/statd.Po
+ -rm -f ./$(DEPDIR)/svc_run.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_sbinSCRIPTS uninstall-man \
+ uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: all check install install-am install-exec install-exec-am \
+ install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dist_sbinSCRIPTS install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-hook \
+ install-html install-html-am install-info install-info-am \
+ install-man install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-dist_sbinSCRIPTS \
+ uninstall-hook uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+@CONFIG_RPCGEN_TRUE@$(RPCGEN):
+@CONFIG_RPCGEN_TRUE@ make -C ../../tools/rpcgen all
+
+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -l -o $@ $<
+
+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -m -o $@ $<
+
+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -c -o $@ $<
+
+$(GENFILES_H): %.h: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -h -o $@ $<
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/utils/statd/TODO b/utils/statd/TODO
new file mode 100644
index 0000000..0ee050a
--- /dev/null
+++ b/utils/statd/TODO
@@ -0,0 +1,13 @@
+Some things still left to do (not a comprehensive list):
+
+* Go through Olaf's extensive changes (especially the list and callback
+ handling, which is the meat of the server) and understand everything
+ that he's done.
+
+* Continue checking for security holes.
+
+* Handle multiple SM_MON requests that are identical save for the "priv"
+ information. How should I do this? No spec's...(it's not really
+ supposed to happen). [Did Olaf already address this?]
+
+* BETTER CODE COMMENTS!
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
new file mode 100644
index 0000000..bb7c590
--- /dev/null
+++ b/utils/statd/callback.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by Lon Hohberger, Oct. 2000.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <netdb.h>
+
+#include "rpcmisc.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+/* Callback notify list. */
+/* notify_list *cbnl = NULL; ... never used */
+
+
+/*
+ * Services SM_NOTIFY requests.
+ *
+ * When NLM uses an SM_MON request to tell statd to monitor a remote,
+ * the request contains a "mon_name" argument. This is usually the
+ * "caller_name" argument of an NLMPROC_LOCK request. On Linux, the
+ * NLM can send statd the remote's IP address instead of its
+ * caller_name. The NSM protocol does not allow both the remote's
+ * caller_name and it's IP address to be sent in the same SM_MON
+ * request.
+ *
+ * The remote's caller_name is useful because it makes it simple
+ * to identify rebooting remotes by matching the "mon_name" argument
+ * they sent via an SM_NOTIFY request.
+ *
+ * The caller_name string may not be a fully qualified domain name,
+ * or even registered in the DNS database, however. Having the
+ * remote's IP address is useful because then there is no ambiguity
+ * about where to send an SM_NOTIFY after the local system reboots.
+ *
+ * Without the actual caller_name, however, statd must use an
+ * heuristic to match an incoming SM_NOTIFY request to one of the
+ * hosts it is currently monitoring. The incoming mon_name in an
+ * SM_NOTIFY address is converted to a list of IP addresses using
+ * DNS. Each mon_name on statd's monitor list is also converted to
+ * an address list, and the two lists are checked to see if there is
+ * a matching address.
+ *
+ * There are some risks to this strategy:
+ *
+ * 1. The external DNS database is not reliable. It can change
+ * over time, or the forward and reverse mappings could be
+ * inconsistent.
+ *
+ * 2. If statd's monitor list becomes substantial, finding a match
+ * can generate a not inconsequential amount of DNS traffic.
+ *
+ * 3. statd is a single-threaded service. When DNS becomes slow or
+ * unresponsive, statd also becomes slow or unresponsive.
+ *
+ * 4. If the remote does not have a DNS entry at all (or if the
+ * remote can resolve itself, but the local host can't resolve
+ * the remote's hostname), the remote cannot be monitored, and
+ * therefore NLM locking cannot be provided for that host.
+ *
+ * 5. Local DNS resolution can produce different results for the
+ * mon_name than the results the remote might see for the same
+ * query, especially if the remote did not send a caller_name
+ * or mon_name that is a fully qualified domain name.
+ *
+ * Note that a caller_name is passed from NFS client to server,
+ * but the client never knows what mon_name the server might use
+ * to notify it of a reboot. On Linux, the client extracts the
+ * server's name from the devname it was passed by the mount
+ * command. This is often not a fully-qualified domain name.
+ */
+void *
+sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
+{
+ notify_list *lp, *call;
+ static char *result = NULL;
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char ip_addr[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d",
+ argp->mon_name, argp->state);
+
+ if (!statd_present_address(sap, ip_addr, sizeof(ip_addr))) {
+ xlog_warn("Unrecognized sender address");
+ return ((void *) &result);
+ }
+
+ ha_callout("sm-notify", argp->mon_name, ip_addr, argp->state);
+
+ /* quick check - don't bother if we're not monitoring anyone */
+ if (rtnl == NULL) {
+ xlog_warn("SM_NOTIFY from %s while not monitoring any hosts",
+ argp->mon_name);
+ return ((void *) &result);
+ }
+
+ /* okir change: statd doesn't remove the remote host from its
+ * internal monitor list when receiving an SM_NOTIFY call from
+ * it. Lockd will want to continue monitoring the remote host
+ * until it issues an SM_UNMON call.
+ */
+ for (lp = rtnl ; lp ; lp = lp->next)
+ if (NL_STATE(lp) != argp->state &&
+ (statd_matchhostname(argp->mon_name, lp->dns_name) ||
+ statd_matchhostname(ip_addr, lp->dns_name))) {
+ NL_STATE(lp) = argp->state;
+ call = nlist_clone(lp);
+ nlist_insert(&notify, call);
+ }
+
+
+ return ((void *) &result);
+}
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
new file mode 100644
index 0000000..16e21fc
--- /dev/null
+++ b/utils/statd/hostname.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "nfslib.h"
+#include "sockaddr.h"
+#include "statd.h"
+#include "xlog.h"
+
+/**
+ * statd_present_address - convert sockaddr to presentation address
+ * @sap: pointer to socket address to convert
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.
+ *
+ * Returns true if successful; otherwise false.
+ *
+ * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
+ * An alternate version of statd_present_address() is available to
+ * handle older glibcs that do not have getnameinfo(3).
+ */
+#ifdef HAVE_GETNAMEINFO
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ socklen_t salen;
+ int error;
+
+ salen = nfs_sockaddr_length(sap);
+ if (salen == 0) {
+ xlog(D_GENERAL, "%s: unsupported address family",
+ __func__);
+ return false;
+ }
+
+ error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
+ NULL, 0, NI_NUMERICHOST);
+ if (error != 0) {
+ xlog(D_GENERAL, "%s: getnameinfo(3): %s",
+ __func__, gai_strerror(error));
+ return false;
+ }
+ return true;
+}
+#else /* !HAVE_GETNAMEINFO */
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+ if (sin->sin_family != AF_INET) {
+ xlog(D_GENERAL, "%s: unsupported address family", __func__);
+ return false;
+ }
+
+ /* ensure '\0' termination */
+ memset(buf, 0, buflen);
+
+ if (inet_ntop(AF_INET, (char *)&sin->sin_addr,
+ buf, (socklen_t)buflen) == NULL) {
+ xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__);
+ return false;
+ }
+ return true;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/*
+ * Look up the hostname; report exceptional errors. Caller must
+ * call freeaddrinfo(3) if a valid addrinfo is returned.
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+get_addrinfo(const char *hostname, const struct addrinfo *hint)
+{
+ struct addrinfo *ai = NULL;
+ int error;
+
+ error = getaddrinfo(hostname, NULL, hint, &ai);
+ switch (error) {
+ case 0:
+ return ai;
+ case EAI_NONAME:
+ break;
+ default:
+ xlog(D_GENERAL, "%s: failed to resolve host %s: %s",
+ __func__, hostname, gai_strerror(error));
+ }
+
+ return NULL;
+}
+
+#ifdef HAVE_GETNAMEINFO
+static _Bool
+get_nameinfo(const struct sockaddr *sap, const socklen_t salen,
+ /*@out@*/ char *buf, const socklen_t buflen)
+{
+ int error;
+
+ error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD);
+ if (error != 0) {
+ xlog(D_GENERAL, "%s: failed to resolve address: %s",
+ __func__, gai_strerror(error));
+ return false;
+ }
+
+ return true;
+}
+#else /* !HAVE_GETNAMEINFO */
+static _Bool
+get_nameinfo(const struct sockaddr *sap,
+ __attribute__ ((unused)) const socklen_t salen,
+ /*@out@*/ char *buf, socklen_t buflen)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap;
+ struct hostent *hp;
+
+ if (sin->sin_family != AF_INET) {
+ xlog(D_GENERAL, "%s: unknown address family: %d",
+ sin->sin_family);
+ return false;
+ }
+
+ hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr),
+ sizeof(struct in_addr), AF_INET);
+ if (hp == NULL) {
+ xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__);
+ return false;
+ }
+
+ strncpy(buf, hp->h_name, (size_t)buflen);
+ return true;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/**
+ * statd_canonical_name - choose file name for monitor record files
+ * @hostname: C string containing hostname or presentation address
+ *
+ * Returns a '\0'-terminated ASCII string containing a fully qualified
+ * canonical hostname, or NULL if @hostname does not have a reverse
+ * mapping. Caller must free the result with free(3).
+ *
+ * Incoming hostnames are looked up to determine the canonical hostname,
+ * and incoming presentation addresses are converted to canonical
+ * hostnames.
+ */
+__attribute__((__malloc__))
+char *
+statd_canonical_name(const char *hostname)
+{
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ char buf[NI_MAXHOST];
+ struct addrinfo *ai;
+
+ ai = get_addrinfo(hostname, &hint);
+ if (ai != NULL) {
+ /* @hostname was a presentation address */
+ _Bool result;
+ result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
+ buf, (socklen_t)sizeof(buf));
+ nfs_freeaddrinfo(ai);
+ if (!result || buf[0] == '\0')
+ /* OK to use presentation address,
+ * if no reverse map exists */
+ return strdup(hostname);
+ return strdup(buf);
+ }
+
+ /* @hostname was a hostname */
+ hint.ai_flags = AI_CANONNAME;
+ ai = get_addrinfo(hostname, &hint);
+ if (ai == NULL)
+ return NULL;
+ strcpy(buf, ai->ai_canonname);
+ nfs_freeaddrinfo(ai);
+
+ return strdup(buf);
+}
+
+/*
+ * Take care to perform an explicit reverse lookup on presentation
+ * addresses. Otherwise we don't get a real canonical name or a
+ * complete list of addresses.
+ *
+ * Returns an addrinfo list that has ai_canonname filled in, or
+ * NULL if some error occurs. Caller must free the returned
+ * list with freeaddrinfo(3).
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+statd_canonical_list(const char *hostname)
+{
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ char buf[NI_MAXHOST];
+ struct addrinfo *ai;
+
+ ai = get_addrinfo(hostname, &hint);
+ if (ai != NULL) {
+ /* @hostname was a presentation address */
+ _Bool result;
+ result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
+ buf, (socklen_t)sizeof(buf));
+ nfs_freeaddrinfo(ai);
+ if (result)
+ goto out;
+ }
+ /* @hostname was a hostname or had no reverse mapping */
+ strcpy(buf, hostname);
+
+out:
+ hint.ai_flags = AI_CANONNAME;
+ return get_addrinfo(buf, &hint);
+}
+
+/**
+ * statd_matchhostname - check if two hostnames are equivalent
+ * @hostname1: C string containing hostname
+ * @hostname2: C string containing hostname
+ *
+ * Returns true if the hostnames are the same, the hostnames resolve
+ * to the same canonical name, or the hostnames resolve to at least
+ * one address that is the same. False is returned if the hostnames
+ * do not match in any of these ways, if either hostname contains
+ * wildcard characters, if either hostname is a netgroup name, or
+ * if an error occurs.
+ */
+_Bool
+statd_matchhostname(const char *hostname1, const char *hostname2)
+{
+ struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL;
+ _Bool result = false;
+
+ if (strcasecmp(hostname1, hostname2) == 0) {
+ result = true;
+ goto out;
+ }
+
+ results1 = statd_canonical_list(hostname1);
+ if (results1 == NULL)
+ goto out;
+ results2 = statd_canonical_list(hostname2);
+ if (results2 == NULL)
+ goto out;
+
+ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
+ result = true;
+ goto out;
+ }
+
+ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
+ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
+ if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
+ result = true;
+ break;
+ }
+
+out:
+ nfs_freeaddrinfo(results2);
+ nfs_freeaddrinfo(results1);
+
+ xlog(D_CALL, "%s: hostnames %s and %s %s", __func__,
+ hostname1, hostname2,
+ (result ? "matched" : "did not match"));
+ return result;
+}
diff --git a/utils/statd/misc.c b/utils/statd/misc.c
new file mode 100644
index 0000000..f2a086f
--- /dev/null
+++ b/utils/statd/misc.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include "statd.h"
+#include "notlist.h"
+
+/*
+ * Error-checking malloc() wrapper.
+ */
+void *
+xmalloc (size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ return ((void *)NULL);
+
+ if (!(ptr = malloc (size)))
+ xlog_err ("malloc failed");
+
+ return (ptr);
+}
+
+
+/*
+ * Error-checking strdup() wrapper.
+ */
+char *
+xstrdup (const char *string)
+{
+ char *result;
+
+ /* Will only fail if underlying malloc() fails (ENOMEM). */
+ if (!(result = strdup (string)))
+ xlog_err ("strdup failed");
+
+ return (result);
+}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
new file mode 100644
index 0000000..c76589c
--- /dev/null
+++ b/utils/statd/monitor.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ * Modified by H.J. Lu, 1998.
+ * Tighter access control, Olaf Kirch June 1999.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+
+#include "sockaddr.h"
+#include "rpcmisc.h"
+#include "nsm.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+notify_list * rtnl = NULL; /* Run-time notify list. */
+
+/*
+ * Reject requests from non-loopback addresses in order
+ * to prevent attack described in CERT CA-99.05.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
+ */
+static _Bool
+caller_is_localhost(struct svc_req *rqstp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ if (!nfs_is_v4_loopback(sap))
+ goto out_nonlocal;
+ return true;
+
+out_nonlocal:
+ if (!statd_present_address(sap, buf, sizeof(buf)))
+ buf[0] = '\0';
+ xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
+ return false;
+}
+
+/*
+ * Services SM_MON requests.
+ */
+struct sm_stat_res *
+sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
+{
+ static sm_stat_res result;
+ char *mon_name = argp->mon_id.mon_name,
+ *my_name = argp->mon_id.my_id.my_name;
+ struct my_id *id = &argp->mon_id.my_id;
+ char *cp;
+ notify_list *clnt = NULL;
+ struct sockaddr_in my_addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ char *dnsname = NULL;
+ int existing = 0;
+
+ xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
+
+ /* Assume that we'll fail. */
+ result.res_stat = STAT_FAIL;
+ result.state = -1; /* State is undefined for STAT_FAIL. */
+
+ /* 1. Reject any remote callers.
+ * Ignore the my_name specified by the caller, and
+ * use "127.0.0.1" instead.
+ */
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ /* 2. Reject any registrations for non-lockd services.
+ *
+ * This is specific to the linux kernel lockd, which
+ * makes the callback procedure part of the lockd interface.
+ * It is also prone to break when lockd changes its callback
+ * procedure number -- which, in fact, has now happened once.
+ * There must be a better way.... XXX FIXME
+ */
+ if (id->my_prog != 100021 ||
+ (id->my_proc != 16 && id->my_proc != 24))
+ {
+ xlog_warn("Attempt to register callback to %d/%d",
+ id->my_prog, id->my_proc);
+ goto failure;
+ }
+
+ /*
+ * Check hostnames. If I can't look them up, I won't monitor. This
+ * might not be legal, but it adds a little bit of safety and sanity.
+ */
+
+ /* must check for /'s in hostname! See CERT's CA-96.09 for details. */
+ if (strchr(mon_name, '/') || mon_name[0] == '.') {
+ xlog(L_ERROR, "SM_MON request for hostname containing '/' "
+ "or starting '.': %s", mon_name);
+ xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
+ goto failure;
+ }
+
+ /* my_name must not have white space */
+ for (cp=my_name ; *cp ; cp++)
+ if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+ *cp = '_';
+
+ /*
+ * Hostnames checked OK.
+ * Now choose a hostname to use for matching. We cannot
+ * really trust much in the incoming NOTIFY, so to make
+ * sure that multi-homed hosts work nicely, we get an
+ * FQDN now, and use that for matching.
+ */
+ dnsname = statd_canonical_name(mon_name);
+ if (dnsname == NULL) {
+ xlog(L_WARNING, "No canonical hostname found for %s", mon_name);
+ goto failure;
+ }
+
+ /* Now check to see if this is a duplicate, and warn if so.
+ * I will also return STAT_FAIL. (I *think* this is how I should
+ * handle it.)
+ *
+ * Olaf requests that I allow duplicate SM_MON requests for
+ * hosts due to the way he is coding lockd. No problem,
+ * I'll just do a quickie success return and things should
+ * be happy.
+ */
+ clnt = rtnl;
+
+ while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
+ NL_MY_PROC(clnt) == id->my_proc &&
+ NL_MY_PROG(clnt) == id->my_prog &&
+ NL_MY_VERS(clnt) == id->my_vers) {
+ if (memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE)) {
+ xlog(D_GENERAL,
+ "Received SM_MON request with new "
+ "cookie for %s from procedure on %s",
+ mon_name, my_name);
+
+ existing = 1;
+ break;
+ } else {
+ /* Hey! We already know you guys! */
+ xlog(D_GENERAL,
+ "Duplicate SM_MON request for %s "
+ "from procedure on %s",
+ mon_name, my_name);
+
+ /* But we'll let you pass anyway. */
+ free(dnsname);
+ goto success;
+ }
+ }
+ clnt = NL_NEXT(clnt);
+ }
+
+ /*
+ * We're committed...ignoring errors. Let's hope that a malloc()
+ * doesn't fail. (I should probably fix this assumption.)
+ */
+ if (!existing && !(clnt = nlist_new(my_name, mon_name, 0))) {
+ free(dnsname);
+ xlog_warn("out of memory");
+ goto failure;
+ }
+
+ NL_MY_PROG(clnt) = id->my_prog;
+ NL_MY_VERS(clnt) = id->my_vers;
+ NL_MY_PROC(clnt) = id->my_proc;
+ memcpy(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE);
+ clnt->dns_name = dnsname;
+
+ /*
+ * Now, Create file on stable storage for host, first deleting any
+ * existing records on file.
+ */
+ nsm_delete_monitored_host(dnsname, mon_name, my_name, 0);
+
+ if (!nsm_insert_monitored_host(dnsname,
+ (struct sockaddr *)(char *)&my_addr, argp)) {
+ nlist_free(existing ? &rtnl : NULL, clnt);
+ goto failure;
+ }
+
+ /* PRC: do the HA callout: */
+ ha_callout("add-client", mon_name, my_name, -1);
+ if (!existing)
+ nlist_insert(&rtnl, clnt);
+ xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name);
+ success:
+ result.res_stat = STAT_SUCC;
+ /* SUN's sm_inter.x says this should be "state number of local site".
+ * X/Open says '"state" will be contain the state of the remote NSM.'
+ * href=http://www.opengroup.org/onlinepubs/9629799/SM_MON.htm
+ * Linux lockd currently (2.6.21 and prior) ignores whatever is
+ * returned, and given the above contraction, it probably always will..
+ * So we just return what we always returned. If possible, we
+ * have already told lockd about our state number via a sysctl.
+ * If lockd wants the remote state, it will need to
+ * use SM_STAT (and prayer).
+ */
+ result.state = MY_STATE;
+ return (&result);
+
+failure:
+ xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
+ free(clnt);
+ return (&result);
+}
+
+static unsigned int
+load_one_host(const char *hostname,
+ __attribute__ ((unused)) const struct sockaddr *sap,
+ const struct mon *m,
+ __attribute__ ((unused)) const time_t timestamp)
+{
+ notify_list *clnt;
+
+ clnt = nlist_new(m->mon_id.my_id.my_name,
+ m->mon_id.mon_name, 0);
+ if (clnt == NULL)
+ return 0;
+
+ clnt->dns_name = strdup(hostname);
+ if (clnt->dns_name == NULL) {
+ nlist_free(NULL, clnt);
+ free(clnt);
+ return 0;
+ }
+
+ xlog(D_GENERAL, "Adding record for %s to the monitor list...",
+ hostname);
+
+ NL_MY_PROG(clnt) = m->mon_id.my_id.my_prog;
+ NL_MY_VERS(clnt) = m->mon_id.my_id.my_vers;
+ NL_MY_PROC(clnt) = m->mon_id.my_id.my_proc;
+ memcpy(NL_PRIV(clnt), m->priv, SM_PRIV_SIZE);
+
+ nlist_insert(&rtnl, clnt);
+ return 1;
+}
+
+void load_state(void)
+{
+ unsigned int count;
+
+ count = nsm_load_monitor_list(load_one_host);
+ if (count)
+ xlog(D_GENERAL, "Loaded %u previously monitored hosts", count);
+}
+
+/*
+ * Services SM_UNMON requests.
+ *
+ * There is no statement in the X/Open spec's about returning an error
+ * for requests to unmonitor a host that we're *not* monitoring. I just
+ * return the state of the NSM when I get such foolish requests for lack
+ * of any better ideas. (I also log the "offense.")
+ */
+struct sm_stat *
+sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
+{
+ static sm_stat result;
+ notify_list *clnt;
+ char *mon_name = argp->mon_name,
+ *my_name = argp->my_id.my_name;
+ struct my_id *id = &argp->my_id;
+ char *cp;
+
+ xlog(D_CALL, "Received SM_UNMON for %s from %s", mon_name, my_name);
+
+ result.state = MY_STATE;
+
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ /* my_name must not have white space */
+ for (cp=my_name ; *cp ; cp++)
+ if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+ *cp = '_';
+
+
+ /* Check if we're monitoring anyone. */
+ if (rtnl == NULL) {
+ xlog_warn("Received SM_UNMON request from %s for %s while not "
+ "monitoring any hosts", my_name, argp->mon_name);
+ return (&result);
+ }
+ clnt = rtnl;
+
+ /*
+ * OK, we are. Now look for appropriate entry in run-time list.
+ * There should only be *one* match on this, since I block "duplicate"
+ * SM_MON calls. (Actually, duplicate calls are allowed, but only one
+ * entry winds up in the list the way I'm currently handling them.)
+ */
+ while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
+ NL_MY_PROC(clnt) == id->my_proc &&
+ NL_MY_PROG(clnt) == id->my_prog &&
+ NL_MY_VERS(clnt) == id->my_vers) {
+ /* Match! */
+ xlog(D_GENERAL, "UNMONITORING %s for %s",
+ mon_name, my_name);
+
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, my_name, -1);
+
+ nsm_delete_monitored_host(clnt->dns_name,
+ mon_name, my_name, 1);
+ nlist_free(&rtnl, clnt);
+
+ return (&result);
+ } else
+ clnt = NL_NEXT(clnt);
+ }
+
+ failure:
+ xlog_warn("Received erroneous SM_UNMON request from %s for %s",
+ my_name, mon_name);
+ return (&result);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
+{
+ short int count = 0;
+ static sm_stat result;
+ notify_list *clnt;
+ char *my_name = argp->my_name;
+
+ xlog(D_CALL, "Received SM_UNMON_ALL for %s", my_name);
+
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ result.state = MY_STATE;
+
+ if (rtnl == NULL) {
+ xlog_warn("Received SM_UNMON_ALL request from %s "
+ "while not monitoring any hosts", my_name);
+ return (&result);
+ }
+ clnt = rtnl;
+
+ while ((clnt = nlist_gethost(clnt, my_name, 1))) {
+ if (NL_MY_PROC(clnt) == argp->my_proc &&
+ NL_MY_PROG(clnt) == argp->my_prog &&
+ NL_MY_VERS(clnt) == argp->my_vers) {
+ /* Watch stack! */
+ char mon_name[SM_MAXSTRLEN + 1];
+ notify_list *temp;
+
+ xlog(D_GENERAL,
+ "UNMONITORING (SM_UNMON_ALL) %s for %s",
+ NL_MON_NAME(clnt), NL_MY_NAME(clnt));
+ strncpy(mon_name, NL_MON_NAME(clnt),
+ sizeof (mon_name) - 1);
+ mon_name[sizeof (mon_name) - 1] = '\0';
+ temp = NL_NEXT(clnt);
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, my_name, -1);
+ nsm_delete_monitored_host(clnt->dns_name,
+ mon_name, my_name, 1);
+ nlist_free(&rtnl, clnt);
+ ++count;
+ clnt = temp;
+ } else
+ clnt = NL_NEXT(clnt);
+ }
+
+ if (!count) {
+ xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no "
+ "SM_MON requests from it", my_name);
+ }
+
+ failure:
+ return (&result);
+}
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
new file mode 100644
index 0000000..45879a4
--- /dev/null
+++ b/utils/statd/notlist.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ * Modified by Lon Hohberger, Oct. 2000.
+ * - Fixed memory leaks, run-off-end problems, etc.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Simple list management for notify list
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "statd.h"
+#include "notlist.h"
+
+
+#ifdef DEBUG
+/*
+ * LH - The linked list code had some bugs. Used this to help debug
+ * new code.
+ */
+static void
+plist(notify_list *head, int en)
+{
+ /* case where we ran off the end */
+ if (!head) return;
+
+ printf("Entry %d: %s\n",en, NL_MON_NAME(head));
+ plist(head->next, ++en);
+}
+
+static void
+nlist_print(notify_list **head)
+{
+ printf("--- Begin notify list dump ---\n");
+ plist(*head,1);
+ printf("--- End notify list dump ---\n");
+}
+#endif /* DEBUG */
+
+/*
+ * Allocate memory and set up a new notify list entry.
+ */
+notify_list *
+nlist_new(char *my_name, char *mon_name, int state)
+{
+ notify_list *new;
+
+ new = (notify_list *) xmalloc(sizeof(notify_list));
+ memset(new, 0, sizeof(*new));
+
+ NL_TIMES(new) = MAX_TRIES;
+ NL_STATE(new) = state;
+ NL_MY_NAME(new) = xstrdup(my_name);
+ NL_MON_NAME(new) = xstrdup(mon_name);
+
+ return new;
+}
+
+/*
+ * Insert *entry into a notify list at the point specified by
+ * **head. This can be in the middle. However, we do not handle
+ * list _append_ in this function; rather, the only place we should
+ * have to worry about this case is in nlist_insert_timer below.
+ * - entry must not be NULL.
+ */
+void
+nlist_insert(notify_list **head, notify_list *entry)
+{
+ if (*head) {
+ /*
+ * Cases where we're prepending a non-empty list
+ * or inserting possibly in the middle somewhere (eg,
+ * nlist_insert_timer...)
+ */
+ entry->next = (*head); /* Forward pointer */
+ entry->prev = (*head)->prev; /* Back pointer */
+ (*head)->prev = entry; /* head's new back pointer */
+ }
+
+ /* Common to all cases, including new list creation */
+ *head = entry; /* New head */
+
+#ifdef DEBUG
+ nlist_print(head);
+#endif
+}
+
+/*
+ * (re)insert *entry into notify_list **head. This requires that
+ * NL_WHEN(entry) has been set (usually, this is time() + 5 seconds).
+ * - entry must not be NULL
+ *
+ * LH - This used to cause (a) a memory leak and (b) dropped notify-list
+ * entries. The pointer ran off the end of the list, and changed the
+ * head-end to point to the new, one-entry list. All other entries became garbage.
+ *
+ * FIXME: Optimize this function. (I'll work on it - LH)
+ */
+void
+nlist_insert_timer(notify_list **head, notify_list *entry)
+{
+ notify_list *spot = *head, /* Insertion location */
+ /* ...Start at head */
+ *back = NULL; /* Back pointer */
+
+
+ /* Find first entry with higher timeout value or end of list */
+ while (spot && NL_WHEN(spot) <= NL_WHEN(entry)) {
+ /*
+ * Keep the back pointer in case we
+ * run off the end... (see below)
+ */
+ back = spot;
+ spot = spot->next;
+ }
+
+ if (spot == (*head)) {
+ /*
+ * case where we're prepending an empty or non-empty
+ * list or inserting in the middle somewhere. Pass
+ * the real head of the list, since we'll be changing
+ * during the insert...
+ */
+ nlist_insert(head, entry);
+ } else {
+ /* all other cases - don't move the real head pointer */
+ nlist_insert(&spot, entry);
+
+ /*
+ * If spot == entry, then spot was NULL when we called
+ * nlist_insert. This happened because we had run off
+ * the end of the list. Append entry to original list.
+ */
+ if (spot == entry) {
+ back->next = entry;
+ entry->prev = back;
+ }
+ }
+}
+
+/*
+ * Remove *entry from the list pointed to by **head.
+ * Do not destroy *entry. This is normally done before
+ * a re-insertion with a timer, but can be done anywhere.
+ * - entry must not be NULL.
+ */
+void
+nlist_remove(notify_list **head, notify_list *entry)
+{
+ notify_list *prev = entry->prev,
+ *next = entry->next;
+
+ if (next) {
+ next->prev = prev;
+ }
+
+ if (prev) {
+ /* Case(s) where entry isn't at the front */
+ prev->next = next;
+ } else {
+ /* cases where entry is at the front */
+ *head = next;
+ }
+
+ entry->next = entry->prev = NULL;
+#ifdef DEBUG
+ nlist_print(head);
+#endif
+}
+
+/*
+ * Clone an entry in the notify list -
+ * - entry must not be NULL
+ */
+notify_list *
+nlist_clone(notify_list *entry)
+{
+ notify_list *new;
+
+ new = nlist_new(NL_MY_NAME(entry), NL_MON_NAME(entry), NL_STATE(entry));
+ NL_MY_PROG(new) = NL_MY_PROG(entry);
+ NL_MY_VERS(new) = NL_MY_VERS(entry);
+ NL_MY_PROC(new) = NL_MY_PROC(entry);
+ memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE);
+
+ return new;
+}
+
+/*
+ * Destroy an entry in a notify list and free the memory.
+ * If *head is NULL, just free the entry. This would be
+ * done only when we know entry isn't in any list.
+ * - entry must not be NULL.
+ */
+void
+nlist_free(notify_list **head, notify_list *entry)
+{
+ if (head && (*head))
+ nlist_remove(head, entry);
+ if (NL_MY_NAME(entry))
+ free(NL_MY_NAME(entry));
+ if (NL_MON_NAME(entry))
+ free(NL_MON_NAME(entry));
+ free(entry->dns_name);
+}
+
+/*
+ * Destroy an entire notify list
+ */
+void
+nlist_kill(notify_list **head)
+{
+ notify_list *next;
+
+ while (*head) {
+ next = (*head)->next;
+ nlist_free(head, *head);
+ free(*head);
+ *head = next;
+ }
+}
+
+/*
+ * Walk a list looking for a matching name in the NL_MON_NAME field.
+ */
+notify_list *
+nlist_gethost(notify_list *list, char *host, int myname)
+{
+ notify_list *lp;
+
+ for (lp = list; lp; lp = lp->next) {
+ if (statd_matchhostname(host,
+ myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
+ return lp;
+ }
+
+ return (notify_list *) NULL;
+}
diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
new file mode 100644
index 0000000..6ed0da8
--- /dev/null
+++ b/utils/statd/notlist.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 1995, 1997-1999, 2002 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include <netinet/in.h>
+
+/*
+ * Primary information structure.
+ */
+struct notify_list {
+ mon mon; /* Big honkin' NSM structure. */
+ in_port_t port; /* port number for callback */
+ short int times; /* Counter used for various things. */
+ int state; /* For storing notified state for callbacks. */
+ char *dns_name; /* used for matching incoming
+ * NOTIFY requests */
+ struct notify_list *next; /* Linked list forward pointer. */
+ struct notify_list *prev; /* Linked list backward pointer. */
+ uint32_t xid; /* XID of MS_NOTIFY RPC call */
+ time_t when; /* notify: timeout for re-xmit */
+};
+
+typedef struct notify_list notify_list;
+
+/*
+ * Global Variables
+ */
+extern notify_list * rtnl; /* Run-time notify list */
+extern notify_list * notify; /* Pending RPC calls */
+
+/*
+ * List-handling functions
+ */
+extern notify_list * nlist_new(char *, char *, int);
+extern void nlist_insert(notify_list **, notify_list *);
+extern void nlist_remove(notify_list **, notify_list *);
+extern void nlist_insert_timer(notify_list **, notify_list *);
+extern notify_list * nlist_clone(notify_list *);
+extern void nlist_free(notify_list **, notify_list *);
+extern void nlist_kill(notify_list **);
+extern notify_list * nlist_gethost(notify_list *, char *, int);
+
+/*
+ * List-handling macros.
+ * THESE INHERIT INFORMATION FROM PREVIOUSLY-DEFINED MACROS.
+ * (So don't change their order unless you study them first!)
+ */
+#define NL_NEXT(L) ((L)->next)
+#define NL_FIRST NL_NEXT
+#define NL_PREV(L) ((L)->prev)
+#define NL_DATA(L) ((L)->mon)
+#define NL_STATE(L) ((L)->state)
+#define NL_TIMES(L) ((L)->times)
+#define NL_MON_ID(L) (NL_DATA((L)).mon_id)
+#define NL_PRIV(L) (NL_DATA((L)).priv)
+#define NL_MON_NAME(L) (NL_MON_ID((L)).mon_name)
+#define NL_MY_ID(L) (NL_MON_ID((L)).my_id)
+#define NL_MY_NAME(L) (NL_MY_ID((L)).my_name)
+#define NL_MY_PROC(L) (NL_MY_ID((L)).my_proc)
+#define NL_MY_PROG(L) (NL_MY_ID((L)).my_prog)
+#define NL_MY_VERS(L) (NL_MY_ID((L)).my_vers)
+#define NL_WHEN(L) ((L)->when)
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
new file mode 100644
index 0000000..5b26148
--- /dev/null
+++ b/utils/statd/rmtcall.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 1996, 1999 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997-1999.
+ * Modified by H.J. Lu, 1998.
+ * Modified by Lon Hohberger, Oct. 2000
+ * - Bugfix handling client responses.
+ * - Paranoia on NOTIFY_CALLBACK case
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * After reboot, notify all hosts on our notify list. In order not to
+ * hang statd with delivery to dead hosts, we perform all RPC calls in
+ * parallel.
+ *
+ * It would have been nice to use the portmapper's rmtcall feature,
+ * but that's not possible for security reasons (the portmapper would
+ * have to forward the call with root privs for most statd's, which
+ * it won't if it's worth its money).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <time.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sm_inter.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+#include "nsm.h"
+#include "nfsrpc.h"
+
+#if SIZEOF_SOCKLEN_T - 0 == 0
+#define socklen_t int
+#endif
+
+static int sockfd = -1; /* notify socket */
+
+/* How many times to try looking for an unused privileged port */
+#define MAX_BRP_RETRIES 100
+
+/*
+ * Initialize socket used to notify lockd of peer reboots.
+ *
+ * Returns the file descriptor of the new socket if successful;
+ * otherwise returns -1 and logs an error.
+ *
+ * Lockd rejects such requests if the source port is not privileged.
+ * statd_get_socket() must be invoked while statd still holds root
+ * privileges in order for the socket to acquire a privileged source
+ * port.
+ */
+int
+statd_get_socket(void)
+{
+ struct sockaddr_in sin;
+ struct servent *se;
+ static int prevsocks[MAX_BRP_RETRIES];
+ unsigned int retries;
+
+ if (sockfd >= 0)
+ return sockfd;
+
+ retries = 0;
+ do {
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
+ break;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bindresvport(sockfd, &sin) < 0) {
+ xlog(D_GENERAL, "%s: can't bind to reserved port",
+ __func__);
+ break;
+ }
+ se = getservbyport(sin.sin_port, "udp");
+ if (se == NULL)
+ break;
+
+ if (retries == MAX_BRP_RETRIES) {
+ xlog(D_GENERAL, "%s: No unused privileged ports",
+ __func__);
+ break;
+ }
+
+ /* rather not use that port, try again */
+ prevsocks[retries++] = sockfd;
+ } while (1);
+
+ while (retries)
+ close(prevsocks[--retries]);
+
+ if (sockfd < 0)
+ return -1;
+
+ return sockfd;
+}
+
+static notify_list *
+recv_rply(u_long *portp)
+{
+ char msgbuf[NSM_MAXMSGSIZE];
+ ssize_t msglen;
+ notify_list *lp = NULL;
+ XDR xdr;
+ struct sockaddr_in sin;
+ socklen_t alen = (socklen_t)sizeof(sin);
+ uint32_t xid;
+
+ memset(msgbuf, 0, sizeof(msgbuf));
+ msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
+ (struct sockaddr *)(char *)&sin, &alen);
+ if (msglen == (ssize_t)-1) {
+ xlog_warn("%s: recvfrom failed: %m", __func__);
+ return NULL;
+ }
+
+ memset(&xdr, 0, sizeof(xdr));
+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+ xid = nsm_parse_reply(&xdr);
+ if (xid == 0)
+ goto done;
+ if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ struct in_addr addr = sin.sin_addr;
+ char buf[INET_ADDRSTRLEN];
+
+ xlog_warn("%s: Unrecognized reply from %s", __func__,
+ inet_ntop(AF_INET, &addr, buf,
+ (socklen_t)sizeof(buf)));
+ goto done;
+ }
+
+ for (lp = notify; lp != NULL; lp = lp->next) {
+ /* LH - this was a bug... it should have been checking
+ * the xid from the response message from the client,
+ * not the static, internal xid */
+ if (lp->xid != xid)
+ continue;
+ if (lp->port == 0)
+ *portp = nsm_recv_getport(&xdr);
+ break;
+ }
+
+done:
+ xdr_destroy(&xdr);
+ return lp;
+}
+
+/*
+ * Notify operation for a single list entry
+ */
+static int
+process_entry(notify_list *lp)
+{
+ struct sockaddr_in sin;
+
+ if (NL_TIMES(lp) == 0) {
+ xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
+ __func__);
+ return 0;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = lp->port;
+ /* LH - moved address into switch */
+
+ /* __FORCE__ loopback for callbacks to lockd ... */
+ /* Just in case we somehow ignored it thus far */
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (sin.sin_port == 0)
+ lp->xid = nsm_xmit_getport(sockfd, &sin,
+ (rpcprog_t)NL_MY_PROG(lp),
+ (rpcvers_t)NL_MY_VERS(lp));
+ else {
+ struct mon m;
+
+ memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+
+ m.mon_id.mon_name = NL_MON_NAME(lp);
+ m.mon_id.my_id.my_name = NULL;
+ m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
+ m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
+ m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
+
+ lp->xid = nsm_xmit_nlmcall(sockfd,
+ (struct sockaddr *)(char *)&sin,
+ (socklen_t)sizeof(sin), &m, NL_STATE(lp));
+ }
+ if (lp->xid == 0) {
+ xlog_warn("%s: failed to notify port %d",
+ __func__, ntohs(lp->port));
+ }
+ NL_TIMES(lp) -= 1;
+
+ return 1;
+}
+
+/*
+ * Process a datagram received on the notify socket
+ */
+int
+process_reply(FD_SET_TYPE *rfds)
+{
+ notify_list *lp;
+ u_long port;
+
+ if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
+ return 0;
+
+ /* Should not be processed again. */
+ FD_CLR (sockfd, rfds);
+
+ if (!(lp = recv_rply(&port)))
+ return 1;
+
+ if (lp->port == 0) {
+ if (port != 0) {
+ lp->port = htons((unsigned short) port);
+ process_entry(lp);
+ NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
+ nlist_remove(&notify, lp);
+ nlist_insert_timer(&notify, lp);
+ return 1;
+ }
+ xlog_warn("%s: service %d not registered on localhost",
+ __func__, NL_MY_PROG(lp));
+ } else {
+ xlog(D_GENERAL, "%s: Callback to %s (for %s) succeeded",
+ __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
+ }
+ nlist_free(&notify, lp);
+ return 1;
+}
+
+/*
+ * Process a notify list, either for notifying remote hosts after reboot
+ * or for calling back (local) statd clients when the remote has notified
+ * us of a crash.
+ */
+int
+process_notify_list(void)
+{
+ notify_list *entry;
+ time_t now;
+
+ while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
+ if (process_entry(entry)) {
+ NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
+ nlist_remove(&notify, entry);
+ nlist_insert_timer(&notify, entry);
+ } else {
+ xlog(L_ERROR,
+ "%s: Can't callback %s (%d,%d), giving up",
+ __func__,
+ NL_MY_NAME(entry),
+ NL_MY_PROG(entry),
+ NL_MY_VERS(entry));
+ nlist_free(&notify, entry);
+ }
+ }
+
+ return 1;
+}
diff --git a/utils/statd/sim_sm_inter.x b/utils/statd/sim_sm_inter.x
new file mode 100644
index 0000000..4346199
--- /dev/null
+++ b/utils/statd/sim_sm_inter.x
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SIM_SM_PROG {
+ version SIM_SM_VERS {
+ void SIM_SM_MON(struct status) = 1;
+ } = 1;
+} = 200048;
+
+const SM_MAXSTRLEN = 1024;
+const SM_PRIV_SIZE = 16;
+
+/*
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+%#ifndef SM_INTER_X
+struct status {
+ string mon_name<SM_MAXSTRLEN>;
+ int state;
+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+%#endif /* SM_INTER_X */
diff --git a/utils/statd/simu.c b/utils/statd/simu.c
new file mode 100644
index 0000000..f1d0bf8
--- /dev/null
+++ b/utils/statd/simu.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "sockaddr.h"
+#include "rpcmisc.h"
+#include "statd.h"
+#include "notlist.h"
+
+extern void my_svc_exit (void);
+
+
+/*
+ * Services SM_SIMU_CRASH requests.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
+ */
+void *
+sm_simu_crash_1_svc (__attribute__ ((unused)) void *argp, struct svc_req *rqstp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+ static char *result = NULL;
+
+ xlog(D_CALL, "Received SM_SIMU_CRASH");
+
+ if (!nfs_is_v4_loopback(sap))
+ goto out_nonlocal;
+
+ if ((int)nfs_get_port(sap) >= IPPORT_RESERVED) {
+ xlog_warn("SM_SIMU_CRASH call from unprivileged port");
+ goto failure;
+ }
+
+ my_svc_exit ();
+
+ if (rtnl)
+ nlist_kill (&rtnl);
+
+ failure:
+ return ((void *)&result);
+
+ out_nonlocal:
+ if (!statd_present_address(sap, buf, sizeof(buf)))
+ buf[0] = '\0';
+ xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
+ goto failure;
+}
diff --git a/utils/statd/simulate.c b/utils/statd/simulate.c
new file mode 100644
index 0000000..4ed1468
--- /dev/null
+++ b/utils/statd/simulate.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#ifndef SIMULATIONS
+# error How the hell did we get here?
+#endif
+
+/* If we're running the simulator, we're debugging. Pretty simple. */
+#ifndef DEBUG
+# define DEBUG
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcmisc.h>
+#include "statd.h"
+#include "sim_sm_inter.h"
+
+static void daemon_simulator (void);
+static void sim_killer (int sig);
+static void simulate_crash (char *);
+static void simulate_mon (char *, char *, char *, char *, char *);
+static void simulate_stat (char *, char *);
+static void simulate_unmon (char *, char *, char *, char *);
+static void simulate_unmon_all (char *, char *, char *);
+
+static int sim_port = 0;
+
+extern void sim_sm_prog_1 (struct svc_req *, register SVCXPRT);
+extern void svc_exit (void);
+
+void
+simulator (int argc, char **argv)
+{
+ xlog_stderr (1);
+ xlog_syslog (0);
+ xlog_open ("statd simulator");
+
+ if (argc == 2)
+ if (!strcasecmp (*argv, "crash"))
+ simulate_crash (*(&argv[1]));
+
+ if (argc == 3) {
+ if (!strcasecmp (*argv, "stat"))
+ simulate_stat (*(&argv[1]), *(&argv[2]));
+ }
+ if (argc == 4) {
+ if (!strcasecmp (*argv, "unmon_all"))
+ simulate_unmon_all (*(&argv[1]), *(&argv[2]), *(&argv[3]));
+ }
+ if (argc == 5) {
+ if (!strcasecmp (*argv, "unmon"))
+ simulate_unmon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]));
+ }
+ if (argc == 6) {
+ if (!strcasecmp (*argv, "mon"))
+ simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]),
+ *(&argv[5]));
+ }
+ xlog_err ("WTF? Give me something I can use!");
+}
+
+static void
+simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
+ char *fool)
+{
+ CLIENT *client;
+ sm_stat_res *result;
+ mon mon;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as,
+ monitoring);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ memcpy (mon.priv, fool, SM_PRIV_SIZE);
+ mon.mon_id.my_id.my_name = xstrdup (as);
+ sim_port = atoi (proggy) * SIM_SM_PROG;
+ mon.mon_id.my_id.my_prog = sim_port; /* Pseudo-dummy */
+ mon.mon_id.my_id.my_vers = SIM_SM_VERS;
+ mon.mon_id.my_id.my_proc = SIM_SM_MON;
+ mon.mon_id.mon_name = monitoring;
+
+ if (!(result = sm_mon_1 (&mon, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_mon_1"));
+
+ free (mon.mon_id.my_id.my_name);
+
+ if (result->res_stat != STAT_SUCC) {
+ xlog_err ("SM_MON request failed, state: %d", result->state);
+ } else {
+ xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state);
+ xlog (D_GENERAL, "Waiting for callback");
+ daemon_simulator ();
+ exit (0);
+ }
+}
+
+static void
+simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
+{
+ CLIENT *client;
+ sm_stat *result;
+ mon_id mon_id;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as,
+ unmonitoring);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ mon_id.my_id.my_name = xstrdup (as);
+ mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+ mon_id.my_id.my_vers = SIM_SM_VERS;
+ mon_id.my_id.my_proc = SIM_SM_MON;
+ mon_id.mon_name = unmonitoring;
+
+ if (!(result = sm_unmon_1 (&mon_id, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_1"));
+
+ free (mon_id.my_id.my_name);
+ xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state);
+ exit (0);
+}
+
+static void
+simulate_unmon_all (char *calling, char *as, char *proggy)
+{
+ CLIENT *client;
+ sm_stat *result;
+ my_id my_id;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ my_id.my_name = xstrdup (as);
+ my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+ my_id.my_vers = SIM_SM_VERS;
+ my_id.my_proc = SIM_SM_MON;
+
+ if (!(result = sm_unmon_all_1 (&my_id, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1"));
+
+ free (my_id.my_name);
+ xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state);
+ exit (0);
+}
+
+static void
+simulate_crash (char *host)
+{
+ CLIENT *client;
+
+ if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ if (!sm_simu_crash_1 (NULL, client))
+ xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1"));
+
+ exit (0);
+}
+
+static void
+simulate_stat (char *calling, char *monitoring)
+{
+ CLIENT *client;
+ sm_name checking;
+ sm_stat_res *result;
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ checking.mon_name = monitoring;
+
+ if (!(result = sm_stat_1 (&checking, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_stat_1"));
+
+ if (result->res_stat == STAT_SUCC)
+ xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling,
+ monitoring, result->state);
+ else
+ xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling,
+ monitoring, result->state);
+
+ exit (0);
+}
+
+static void
+sim_killer (int sig)
+{
+ pmap_unset (sim_port, SIM_SM_VERS);
+ xlog_err ("Simulator caught signal %d, un-registering and exiting", sig);
+}
+
+static void
+daemon_simulator (void)
+{
+ signal (SIGHUP, sim_killer);
+ signal (SIGINT, sim_killer);
+ signal (SIGTERM, sim_killer);
+ pmap_unset (sim_port, SIM_SM_VERS);
+ /* this registers both UDP and TCP services */
+ rpc_init("statd", sim_port, SIM_SM_VERS, sim_sm_prog_1, 0);
+ svc_run ();
+ pmap_unset (sim_port, SIM_SM_VERS);
+}
+
+void *
+sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp)
+{
+ static char *result;
+
+ xlog (D_GENERAL, "Recieved state %d for mon_name %s (opaque \"%s\")",
+ argp->state, argp->mon_name, argp->priv);
+ svc_exit ();
+ return ((void *)&result);
+}
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
new file mode 100644
index 0000000..ed82b8f
--- /dev/null
+++ b/utils/statd/sm-notify.c
@@ -0,0 +1,928 @@
+/*
+ * Send NSM notify calls to all hosts listed in /var/lib/sm
+ *
+ * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <err.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <time.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <errno.h>
+#include <grp.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "conffile.h"
+#include "sockaddr.h"
+#include "xlog.h"
+#include "nsm.h"
+#include "nfslib.h"
+#include "nfsrpc.h"
+
+/* glibc before 2.3.4 */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
+#define NSM_TIMEOUT 2
+#define NSM_MAX_TIMEOUT 120 /* don't make this too big */
+
+#define NLM_END_GRACE_FILE "/proc/fs/lockd/nlm_end_grace"
+
+int lift_grace = 1;
+int force = 0;
+
+struct nsm_host {
+ struct nsm_host * next;
+ char * name;
+ const char * mon_name;
+ const char * my_name;
+ char * notify_arg;
+ struct addrinfo *ai;
+ time_t last_used;
+ time_t send_next;
+ unsigned int timeout;
+ unsigned int retries;
+ uint32_t xid;
+};
+
+static char nsm_hostname[SM_MAXSTRLEN + 1];
+static int nsm_state;
+static int nsm_family = AF_INET;
+static int opt_debug = 0;
+static _Bool opt_update_state = true;
+static unsigned int opt_max_retry = 15 * 60;
+static char * opt_srcaddr = NULL;
+static char * opt_srcport = NULL;
+
+static void notify(const int sock);
+static int notify_host(int, struct nsm_host *);
+static void recv_reply(int);
+static void insert_host(struct nsm_host *);
+static struct nsm_host *find_host(uint32_t);
+static int record_pid(void);
+
+static struct nsm_host * hosts = NULL;
+
+__attribute__((__malloc__))
+static struct addrinfo *
+smn_lookup(const char *name)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+
+ res_init();
+ error = getaddrinfo(name, NULL, &hint, &ai);
+ if (error != 0) {
+ xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
+#ifdef HAVE_GETNAMEINFO
+static char *
+smn_get_hostname(const struct sockaddr *sap, const socklen_t salen,
+ const char *name)
+{
+ char buf[NI_MAXHOST];
+ int error;
+
+ error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
+ if (error != 0) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+ return strdup(buf);
+}
+#else /* !HAVE_GETNAMEINFO */
+static char *
+smn_get_hostname(const struct sockaddr *sap,
+ __attribute__ ((unused)) const socklen_t salen,
+ const char *name)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
+ const struct in_addr *addr = &sin->sin_addr;
+ struct hostent *hp;
+
+ if (sap->sa_family != AF_INET) {
+ xlog(L_ERROR, "my_name '%s' is unusable: Bad address family",
+ name);
+ return NULL;
+ }
+
+ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
+ if (hp == NULL) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, hstrerror(h_errno));
+ return NULL;
+ }
+ return strdup(hp->h_name);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/*
+ * Presentation addresses are converted to their canonical hostnames.
+ * If the IP address does not map to a hostname, it is an error:
+ * we never send a presentation address as the argument of SM_NOTIFY.
+ *
+ * If "name" is not a presentation address, it is left alone. This
+ * allows the administrator some flexibility if DNS isn't configured
+ * exactly how sm-notify prefers it.
+ *
+ * Returns NUL-terminated C string containing the result, or NULL
+ * if the canonical name doesn't exist or cannot be determined.
+ * The caller must free the result with free(3).
+ */
+__attribute__((__malloc__))
+static char *
+smn_verify_my_name(const char *name)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ };
+ char *retval;
+ int error;
+
+ error = getaddrinfo(name, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ /* @name was a presentation address */
+ retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name);
+ nfs_freeaddrinfo(ai);
+ if (retval == NULL)
+ return NULL;
+ break;
+ case EAI_NONAME:
+ /* @name was not a presentation address */
+ retval = strdup(name);
+ break;
+ default:
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+
+ xlog(D_GENERAL, "Canonical name for my_name '%s': %s",
+ name, retval);
+ return retval;
+}
+
+__attribute__((__malloc__))
+static struct nsm_host *
+smn_alloc_host(const char *hostname, const char *mon_name,
+ const char *my_name, const time_t timestamp)
+{
+ struct nsm_host *host;
+
+ host = calloc(1, sizeof(*host));
+ if (host == NULL)
+ goto out_nomem;
+
+ /*
+ * mon_name and my_name are preserved so sm-notify can
+ * find the right monitor record to remove when it is
+ * done processing this host.
+ */
+ host->name = strdup(hostname);
+ host->mon_name = (const char *)strdup(mon_name);
+ host->my_name = (const char *)strdup(my_name);
+ host->notify_arg = strdup(opt_srcaddr != NULL ?
+ nsm_hostname : my_name);
+ if (host->name == NULL ||
+ host->mon_name == NULL ||
+ host->my_name == NULL ||
+ host->notify_arg == NULL) {
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
+ free(host->name);
+ free(host);
+ goto out_nomem;
+ }
+
+ host->last_used = timestamp;
+ host->timeout = NSM_TIMEOUT;
+ host->retries = 100; /* force address retry */
+
+ return host;
+
+out_nomem:
+ xlog_warn("Unable to allocate memory");
+ return NULL;
+}
+
+static void smn_forget_host(struct nsm_host *host)
+{
+ xlog(D_CALL, "Removing %s (%s, %s) from notify list",
+ host->name, host->mon_name, host->my_name);
+
+ nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
+
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
+ free(host->name);
+ nfs_freeaddrinfo(host->ai);
+
+ free(host);
+}
+
+static unsigned int
+smn_get_host(const char *hostname,
+ __attribute__ ((unused)) const struct sockaddr *sap,
+ const struct mon *m, const time_t timestamp)
+{
+ struct nsm_host *host;
+
+ host = smn_alloc_host(hostname,
+ m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
+ if (host == NULL)
+ return 0;
+
+ insert_host(host);
+ return 1;
+}
+
+#ifdef IPV6_SUPPORTED
+static int smn_socket(void)
+{
+ int sock;
+
+ /*
+ * Use an AF_INET socket if IPv6 is disabled on the
+ * local system.
+ */
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ if (errno != EAFNOSUPPORT) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+ } else
+ nsm_family = AF_INET6;
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+ goto out_close;
+ }
+
+ /*
+ * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However,
+ * since sm-notify open-codes all of its RPC support, it can
+ * use a single socket and let the local network stack provide
+ * the correct mapping between address families automatically.
+ * This is the same thing that is done in the kernel.
+ */
+ if (nsm_family == AF_INET6) {
+ const int zero = 0;
+ socklen_t zerolen = (socklen_t)sizeof(zero);
+
+ if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+ (char *)&zero, zerolen) == -1) {
+ xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
+ goto out_close;
+ }
+ }
+
+ return sock;
+
+out_close:
+ (void)close(sock);
+ return -1;
+}
+#else /* !IPV6_SUPPORTED */
+static int smn_socket(void)
+{
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+ (void)close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+#endif /* !IPV6_SUPPORTED */
+
+/*
+ * If admin specified a source address or srcport, then convert those
+ * to a sockaddr and return it. Otherwise, return an ANYADDR address.
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+smn_bind_address(const char *srcaddr, const char *srcport)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_flags = AI_NUMERICSERV | AI_V4MAPPED,
+ .ai_family = nsm_family,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+
+ if (srcaddr == NULL)
+ hint.ai_flags |= AI_PASSIVE;
+
+ /* Do not allow "node" and "service" parameters both to be NULL */
+ if (srcport == NULL)
+ error = getaddrinfo(srcaddr, "", &hint, &ai);
+ else
+ error = getaddrinfo(srcaddr, srcport, &hint, &ai);
+ if (error != 0) {
+ xlog(L_ERROR,
+ "Invalid bind address or port for RPC socket: %s",
+ gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
+#ifdef HAVE_LIBTIRPC
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+ return bindresvport_sa(sock, sap);
+}
+
+#else /* !HAVE_LIBTIRPC */
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+ if (sap->sa_family != AF_INET) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
+}
+#endif /* !HAVE_LIBTIRPC */
+
+/*
+ * Prepare a socket for sending RPC requests
+ *
+ * Returns a bound datagram socket file descriptor, or -1 if
+ * an error occurs.
+ */
+static int
+smn_create_socket(const char *srcaddr, const char *srcport)
+{
+ int sock, retry_cnt = 0;
+ struct addrinfo *ai;
+
+retry:
+ sock = smn_socket();
+ if (sock == -1)
+ return -1;
+
+ ai = smn_bind_address(srcaddr, srcport);
+ if (ai == NULL) {
+ (void)close(sock);
+ return -1;
+ }
+
+ /* Use source port if provided on the command line,
+ * otherwise use bindresvport */
+ if (srcport) {
+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
+ xlog(L_ERROR, "Failed to bind RPC socket: %m");
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ return -1;
+ }
+ } else {
+ struct servent *se;
+
+ if (smn_bindresvport(sock, ai->ai_addr) == -1) {
+ xlog(L_ERROR,
+ "bindresvport on RPC socket failed: %m");
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ return -1;
+ }
+
+ /* try to avoid known ports */
+ se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
+ if (se != NULL && retry_cnt < 100) {
+ retry_cnt++;
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ goto retry;
+ }
+ }
+
+ nfs_freeaddrinfo(ai);
+ return sock;
+}
+
+/* Inform the kernel that it's OK to lift lockd's grace period */
+static void
+nsm_lift_grace_period(void)
+{
+ int fd;
+
+ fd = open(NLM_END_GRACE_FILE, O_WRONLY);
+ if (fd < 0) {
+ /* Don't warn if file isn't present */
+ if (errno != ENOENT)
+ xlog(L_WARNING, "Unable to open %s: %m",
+ NLM_END_GRACE_FILE);
+ return;
+ }
+
+ if (write(fd, "Y", 1) < 0)
+ xlog(L_WARNING, "Unable to write to %s: %m", NLM_END_GRACE_FILE);
+
+ close(fd);
+ return;
+}
+inline static void
+read_smnotify_conf(char **argv)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("sm-notify");
+ opt_max_retry = conf_get_num("sm-notify", "retry-time", opt_max_retry / 60) * 60;
+ opt_srcport = conf_get_str("sm-notify", "outgoing-port");
+ opt_srcaddr = conf_get_str("sm-notify", "outgoing-addr");
+ lift_grace = conf_get_bool("sm-notify", "lift-grace", lift_grace);
+
+ s = conf_get_str("statd", "state-directory-path");
+ if (s && !nsm_setup_pathnames(argv[0], s))
+ exit(1);
+ opt_update_state = conf_get_bool("sm-notify", "update-state", opt_update_state);
+ force = conf_get_bool("sm-notify", "force", force);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, sock;
+ char * progname;
+
+ progname = strrchr(argv[0], '/');
+ if (progname != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Read in config setting */
+ read_smnotify_conf(argv);
+
+ while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'd':
+ opt_debug++;
+ break;
+ case 'm':
+ opt_max_retry = atoi(optarg) * 60;
+ break;
+ case 'n':
+ opt_update_state = false;
+ break;
+ case 'p':
+ opt_srcport = optarg;
+ break;
+ case 'v':
+ opt_srcaddr = optarg;
+ break;
+ case 'P':
+ if (!nsm_setup_pathnames(argv[0], optarg))
+ exit(1);
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+
+ if (optind < argc) {
+usage: fprintf(stderr,
+ "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
+ " [-P /path/to/state/directory] [-v my_host_name]\n",
+ progname);
+ exit(1);
+ }
+
+ if (opt_debug) {
+ xlog_syslog(0);
+ xlog_stderr(1);
+ xlog_config(D_ALL, 1);
+ } else {
+ xlog_syslog(1);
+ xlog_stderr(0);
+ }
+
+ xlog_open(progname);
+ xlog(L_NOTICE, "Version " VERSION " starting");
+
+ if (nsm_is_default_parentdir()) {
+ if (record_pid() == 0 && force == 0 && opt_update_state) {
+ /* already run, don't try again */
+ xlog(L_NOTICE, "Already notifying clients; Exiting!");
+ exit(0);
+ }
+ }
+
+ if (opt_srcaddr != NULL) {
+ char *name;
+
+ name = smn_verify_my_name(opt_srcaddr);
+ if (name == NULL)
+ exit(1);
+
+ strncpy(nsm_hostname, name, sizeof(nsm_hostname)-1);
+ free(name);
+ }
+
+ (void)nsm_retire_monitored_hosts();
+ if (nsm_load_notify_list(smn_get_host) == 0) {
+ xlog(D_GENERAL, "No hosts to notify; exiting");
+ if (lift_grace)
+ nsm_lift_grace_period();
+ return 0;
+ }
+
+ nsm_state = nsm_get_state(opt_update_state);
+ if (nsm_state == 0)
+ exit(1);
+ nsm_update_kernel_state(nsm_state);
+
+ if (!opt_debug) {
+ xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
+
+ if (daemon(0, 0) < 0) {
+ xlog(L_ERROR, "unable to background: %m");
+ exit(1);
+ }
+
+ close(0);
+ close(1);
+ close(2);
+ }
+
+ sock = smn_create_socket(opt_srcaddr, opt_srcport);
+ if (sock == -1)
+ exit(1);
+
+ if (!nsm_drop_privileges(-1))
+ exit(1);
+
+ notify(sock);
+
+ if (hosts) {
+ struct nsm_host *hp;
+
+ while ((hp = hosts) != 0) {
+ hosts = hp->next;
+ xlog(L_NOTICE, "Unable to notify %s, giving up",
+ hp->name);
+ }
+ exit(1);
+ }
+
+ exit(0);
+}
+
+/*
+ * Notify hosts
+ */
+static void
+notify(const int sock)
+{
+ time_t failtime = 0;
+
+ if (opt_max_retry)
+ failtime = time(NULL) + opt_max_retry;
+
+ while (hosts) {
+ struct pollfd pfd;
+ time_t now = time(NULL);
+ unsigned int sent = 0;
+ struct nsm_host *hp;
+ long wait;
+
+ if (failtime && now >= failtime)
+ break;
+
+ while (hosts && ((wait = hosts->send_next - now) <= 0)) {
+ /* Never send more than 10 packets at once */
+ if (sent++ >= 10)
+ break;
+
+ /* Remove queue head */
+ hp = hosts;
+ hosts = hp->next;
+
+ if (notify_host(sock, hp))
+ continue;
+
+ /* Set the timeout for this call, using an
+ exponential timeout strategy */
+ wait = hp->timeout;
+ if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
+ hp->timeout = NSM_MAX_TIMEOUT;
+ hp->send_next = now + wait;
+ hp->retries++;
+
+ insert_host(hp);
+ }
+ if (hosts == NULL)
+ return;
+
+ xlog(D_GENERAL, "Host %s due in %ld seconds",
+ hosts->name, wait);
+
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+
+ wait *= 1000;
+ if (wait < 100)
+ wait = 100;
+ if (poll(&pfd, 1, wait) != 1)
+ continue;
+
+ recv_reply(sock);
+ }
+}
+
+/*
+ * Send notification to a single host
+ */
+static int
+notify_host(int sock, struct nsm_host *host)
+{
+ struct sockaddr *sap;
+ socklen_t salen;
+
+ if (host->ai == NULL) {
+ host->ai = smn_lookup(host->name);
+ if (host->ai == NULL) {
+ xlog_warn("DNS resolution of %s failed; "
+ "retrying later", host->name);
+ return 0;
+ }
+ }
+
+ /* If we retransmitted 4 times, reset the port to force
+ * a new portmap lookup (in case statd was restarted).
+ * We also rotate through multiple IP addresses at this
+ * point.
+ */
+ if (host->retries >= 4) {
+ /* don't rotate if there is only one addrinfo */
+ if (host->ai->ai_next != NULL) {
+ struct addrinfo *first = host->ai;
+ struct addrinfo **next = &host->ai;
+
+ /* remove the first entry from the list */
+ host->ai = first->ai_next;
+ first->ai_next = NULL;
+ /* find the end of the list */
+ next = &first->ai_next;
+ while ( *next )
+ next = & (*next)->ai_next;
+ /* put first entry at end */
+ *next = first;
+ }
+
+ nfs_set_port(host->ai->ai_addr, 0);
+ host->retries = 0;
+ }
+
+ sap = host->ai->ai_addr;
+ salen = host->ai->ai_addrlen;
+
+ if (nfs_get_port(sap) == 0)
+ host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
+ else
+ host->xid = nsm_xmit_notify(sock, sap, salen,
+ SM_PROG, host->notify_arg, nsm_state);
+
+ return 0;
+}
+
+static void
+smn_defer(struct nsm_host *host)
+{
+ host->xid = 0;
+ host->send_next = time(NULL) + NSM_MAX_TIMEOUT;
+ host->timeout = NSM_MAX_TIMEOUT;
+ insert_host(host);
+}
+
+static void
+smn_schedule(struct nsm_host *host)
+{
+ host->retries = 0;
+ host->xid = 0;
+ host->send_next = time(NULL);
+ host->timeout = NSM_TIMEOUT;
+ insert_host(host);
+}
+
+/*
+ * Extract the returned port number and set up the SM_NOTIFY call.
+ */
+static void
+recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
+{
+ uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
+
+ if (port == 0) {
+ /* No binding for statd... */
+ xlog(D_GENERAL, "No statd on host %s", host->name);
+ smn_defer(host);
+ } else {
+ xlog(D_GENERAL, "Processing rpcbind reply for %s (port %u)",
+ host->name, port);
+ nfs_set_port(sap, port);
+ smn_schedule(host);
+ }
+}
+
+/*
+ * Successful NOTIFY call. Server returns void.
+ *
+ * Try sending another SM_NOTIFY with an unqualified "my_name"
+ * argument. Reuse the port number. If "my_name" is already
+ * unqualified, we're done.
+ */
+static void
+recv_notify_reply(struct nsm_host *host)
+{
+ char *dot = strchr(host->notify_arg, '.');
+
+ if (dot != NULL) {
+ *dot = '\0';
+ smn_schedule(host);
+ } else {
+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
+ smn_forget_host(host);
+ }
+}
+
+/*
+ * Receive reply from remote host
+ */
+static void
+recv_reply(int sock)
+{
+ struct nsm_host *hp;
+ struct sockaddr *sap;
+ char msgbuf[NSM_MAXMSGSIZE];
+ uint32_t xid;
+ ssize_t msglen;
+ XDR xdr;
+
+ memset(msgbuf, 0 , sizeof(msgbuf));
+ msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
+ if (msglen < 0)
+ return;
+
+ xlog(D_GENERAL, "Received packet...");
+
+ memset(&xdr, 0, sizeof(xdr));
+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+ xid = nsm_parse_reply(&xdr);
+ if (xid == 0)
+ goto out;
+
+ /* Before we look at the data, find the host struct for
+ this reply */
+ if ((hp = find_host(xid)) == NULL)
+ goto out;
+
+ sap = hp->ai->ai_addr;
+ if (nfs_get_port(sap) == 0)
+ recv_rpcbind_reply(sap, hp, &xdr);
+ else
+ recv_notify_reply(hp);
+
+out:
+ xdr_destroy(&xdr);
+}
+
+/*
+ * Insert host into notification list, sorted by next send time
+ */
+static void
+insert_host(struct nsm_host *host)
+{
+ struct nsm_host **where, *p;
+
+ where = &hosts;
+ while ((p = *where) != 0) {
+ /* Sort in ascending order of timeout */
+ if (host->send_next < p->send_next)
+ break;
+ /* If we have the same timeout, put the
+ * most recently used host first.
+ * This makes sure that "recent" hosts
+ * get notified first.
+ */
+ if (host->send_next == p->send_next
+ && host->last_used > p->last_used)
+ break;
+ where = &p->next;
+ }
+
+ host->next = *where;
+ *where = host;
+ xlog(D_GENERAL, "Added host %s to notify list", host->name);
+}
+
+/*
+ * Find host given the XID
+ */
+static struct nsm_host *
+find_host(uint32_t xid)
+{
+ struct nsm_host **where, *p;
+
+ where = &hosts;
+ while ((p = *where) != 0) {
+ if (p->xid == xid) {
+ *where = p->next;
+ return p;
+ }
+ where = &p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Record pid in /run/sm-notify.pid
+ * This file should remain until a reboot, even if the
+ * program exits.
+ * If file already exists, fail.
+ */
+static int record_pid(void)
+{
+ char pid[20];
+ ssize_t len;
+ int fd;
+
+ (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
+ fd = open("/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
+ if (fd < 0)
+ return 0;
+
+ len = write(fd, pid, strlen(pid));
+ if ((len < 0) || ((size_t)len != strlen(pid))) {
+ xlog_warn("Writing to pid file failed: errno %d (%m)",
+ errno);
+ }
+
+ (void)close(fd);
+ return 1;
+}
diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
new file mode 100644
index 0000000..addf5d3
--- /dev/null
+++ b/utils/statd/sm-notify.man
@@ -0,0 +1,366 @@
+.\"@(#)sm-notify.8"
+.\"
+.\" Copyright (C) 2004 Olaf Kirch <okir@suse.de>
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH SM-NOTIFY 8 "1 November 2009
+.SH NAME
+sm-notify \- send reboot notifications to NFS peers
+.SH SYNOPSIS
+.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "]
+.SH DESCRIPTION
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 and version 3, the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
+.B sm-notify
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
+.B rpc.statd
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client records the server's hostname used on the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command normally sends
+.I my_name
+string recorded when that remote was monitored.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
+.SH OPTIONS
+.TP
+.B -d
+Keeps
+.B sm-notify
+attached to its controlling terminal and running in the foreground
+so that notification progress may be monitored directly.
+.TP
+.B -f
+Send notifications even if
+.B sm-notify
+has already run since the last system reboot.
+.TP
+.BI -m " retry-time
+Specifies the length of time, in minutes, to continue retrying
+notifications to unresponsive hosts.
+If this option is not specified,
+.B sm-notify
+attempts to send notifications for 15 minutes.
+Specifying a value of 0 causes
+.B sm-notify
+to continue sending notifications to unresponsive peers
+until it is manually killed.
+.IP
+Notifications are retried if sending fails,
+the remote does not respond,
+the remote's NSM service is not registered,
+or if there is a DNS failure
+which prevents the remote's
+.I mon_name
+from being resolved to an address.
+.IP
+Hosts are not removed from the notification list until a valid
+reply has been received.
+However, the SM_NOTIFY procedure has a void result.
+There is no way for
+.B sm-notify
+to tell if the remote recognized the sender and has started
+appropriate lock recovery.
+.TP
+.B -n
+Prevents
+.B sm-notify
+from updating the local system's NSM state number.
+.TP
+.BI -p " port
+Specifies the source port number
+.B sm-notify
+should use when sending reboot notifications.
+If this option is not specified, a randomly chosen ephemeral port is used.
+.IP
+This option can be used to traverse a firewall between client and server.
+.TP
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B sm-notify
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B sm-notify
+attempts to set its effective UID and GID to the owner
+and group of the subdirectory
+.B sm
+of this directory. After changing the effective ids,
+.B sm-notify
+only needs to access files in
+.B sm
+and
+.B sm.bak
+within the state-directory-path.
+.TP
+.BI -v " ipaddr " | " hostname
+Specifies the network address from which to send reboot notifications,
+and the
+.I mon_name
+argument to use when sending SM_NOTIFY requests.
+If this option is not specified,
+.B sm-notify
+uses a wildcard address as the transport bind address,
+and uses the
+.I my_name
+recorded when the remote was monitored as the
+.I mon_name
+argument when sending SM_NOTIFY requests.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If the
+.I ipaddr
+form is used, the
+.B sm-notify
+command converts this address to a hostname for use as the
+.I mon_name
+argument when sending SM_NOTIFY requests.
+.IP
+This option can be useful in multi-homed configurations where
+the remote requires notification from a specific network address.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [sm-notify]
+or, in one case, the
+.B [statd]
+section of the
+.I /etc/nfs.conf
+configuration file.
+
+Values recognized in the
+.B [sm-notify]
+section include:
+.BR retry-time ,
+.BR outgoing-port ", and"
+.BR outgoing-addr .
+These have the same effect as the command line options
+.BR m ,
+.BR p ", and"
+.B v
+respectively.
+
+An additional value recognized in the
+.B [sm-notify]
+section is
+.BR lift-grace .
+By default,
+.B sm-notify
+will lift lockd's grace period early if it has no hosts to notify.
+Some high availability configurations will run one
+.B sm-notify
+per floating IP address. In these configurations, lifting the
+grace period early may prevent clients from reclaiming locks.
+.RB "Setting " lift-grace " to " n
+will prevent
+.B sm-notify
+from ending the grace period early.
+.B lift-grace
+has no corresponding command line option.
+
+The value recognized in the
+.B [statd]
+section is
+.BR state-directory-path .
+
+.SH SECURITY
+The
+.B sm-notify
+command must be started as root to acquire privileges needed
+to access the state information database.
+It drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into the
+.B sm-notify
+command ,it will choose an appropriate IPv4 or IPv6 transport
+based on the network address returned by DNS for each remote peer.
+It should be fully compatible with remote systems
+that do not support TI-RPC or IPv6.
+.PP
+Currently, the
+.B sm-notify
+command supports sending notification only via datagram transport protocols.
+.SH FILES
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /proc/sys/fs/nfs/nsm_local_state
+kernel's copy of the NSM state number
+.SH SEE ALSO
+.BR rpc.statd (8),
+.BR nfs (5),
+.BR uname (2),
+.BR hostname (7)
+.PP
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
+.SH AUTHORS
+Olaf Kirch <okir@suse.de>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/start-statd b/utils/statd/start-statd
new file mode 100755
index 0000000..b11a7d9
--- /dev/null
+++ b/utils/statd/start-statd
@@ -0,0 +1,33 @@
+#!/bin/sh
+# nfsmount calls this script when mounting a filesystem with locking
+# enabled, but when statd does not seem to be running (based on
+# /run/rpc.statd.pid).
+# It should run statd with whatever flags are apropriate for this
+# site.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"
+
+# Use flock to serialize the running of this script
+exec 9> /run/rpc.statd.lock
+flock -e 9
+
+if [ -s /run/rpc.statd.pid ] &&
+ [ "1$(cat /run/rpc.statd.pid)" -gt 1 ] &&
+ kill -0 "$(cat /run/rpc.statd.pid)" > /dev/null 2>&1
+then
+ # statd already running - must have been slow to respond.
+ exit 0
+fi
+# First try systemd if it's installed.
+if [ -d /run/systemd/system ]; then
+ # Quit only if the call worked.
+ if systemctl start rpc-statd.service; then
+ # Ensure systemd knows not to stop rpc.statd or its dependencies
+ # on 'systemctl isolate ..'
+ systemctl add-wants --runtime remote-fs.target rpc-statd.service
+ exit 0
+ fi
+fi
+
+cd /
+# Fall back to launching it ourselves.
+exec rpc.statd --no-notify
diff --git a/utils/statd/stat.c b/utils/statd/stat.c
new file mode 100644
index 0000000..8d8b65e
--- /dev/null
+++ b/utils/statd/stat.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 1995, 1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netdb.h>
+#include "statd.h"
+
+/*
+ * Services SM_STAT requests.
+ *
+ * According the the X/Open spec's on this procedure: "Implementations
+ * should not rely on this procedure being operative. In many current
+ * implementations of the NSM it will always return a 'STAT_FAIL'
+ * status." My implementation is operative; it returns 'STAT_SUCC'
+ * whenever it can resolve the hostname that it's being asked to
+ * monitor, and returns 'STAT_FAIL' otherwise.
+ *
+ * sm_inter.x says the 'state' returned should be
+ * "state number of site sm_name". It is not clear how to get this.
+ * X/Open says:
+ * STAT_SUCC
+ * The NSM will monitor the given host. "sm_stat_res.state" contains
+ * the state of the NSM.
+ * Which implies that 'state' is the state number of the *local* NSM.
+ * href=http://www.opengroup.org/onlinepubs/9629799/SM_STAT.htm
+ *
+ * We return the *local* state as
+ * 1/ We have easy access to it.
+ * 2/ It might be useful to a remote client who needs it and has no
+ * other way to get it.
+ * 3/ That's what we always did in the past.
+ */
+struct sm_stat_res *
+sm_stat_1_svc(struct sm_name *argp,
+ __attribute__ ((unused)) struct svc_req *rqstp)
+{
+ static sm_stat_res result;
+ char *name;
+
+ xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name);
+
+ name = statd_canonical_name(argp->mon_name);
+ if (name == NULL) {
+ result.res_stat = STAT_FAIL;
+ xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name);
+ } else {
+ result.res_stat = STAT_SUCC;
+ xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name);
+ free(name);
+ }
+ result.state = MY_STATE;
+ return(&result);
+}
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
new file mode 100644
index 0000000..a469a67
--- /dev/null
+++ b/utils/statd/statd.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by H.J. Lu, 1998.
+ * Modified by L. Hohberger of Mission Critical Linux, 2000.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcmisc.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <grp.h>
+
+#include "conffile.h"
+#include "statd.h"
+#include "nfslib.h"
+#include "nfsrpc.h"
+#include "nsm.h"
+
+/* Socket operations */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int run_mode = 0; /* foreground logging mode */
+
+/* LH - I had these local to main, but it seemed silly to have
+ * two copies of each - one in main(), one static in log.c...
+ * It also eliminates the 256-char static in log.c */
+static char *name_p = NULL;
+
+/* PRC: a high-availability callout program can be specified with -H
+ * When this is done, the program will receive callouts whenever clients
+ * are added or deleted to the notify list */
+char *ha_callout_prog = NULL;
+
+static struct option longopts[] =
+{
+ { "foreground", 0, 0, 'F' },
+ { "no-syslog", 0, 0, 'd' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "outgoing-port", 1, 0, 'o' },
+ { "port", 1, 0, 'p' },
+ { "name", 1, 0, 'n' },
+ { "state-directory-path", 1, 0, 'P' },
+ { "notify-mode", 0, 0, 'N' },
+ { "ha-callout", 1, 0, 'H' },
+ { "no-notify", 0, 0, 'L' },
+ { "nlm-port", 1, 0, 'T'},
+ { "nlm-udp-port", 1, 0, 'U'},
+ { NULL, 0, 0, 0 }
+};
+
+extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
+stat_chge SM_stat_chge;
+
+#ifdef SIMULATIONS
+extern void simulator (int, char **);
+#endif
+
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+
+static void
+sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ /* remote host authorization check */
+ if (!check_default("statd", nfs_getrpccaller(transp), SM_PROG)) {
+ svcerr_auth (transp, AUTH_FAILED);
+ return;
+ }
+
+ sm_prog_1 (rqstp, transp);
+}
+
+#define sm_prog_1 sm_prog_1_wrapper
+#endif
+
+static void
+statd_unregister(void) {
+ nfs_svc_unregister(SM_PROG, SM_VERS);
+}
+
+/*
+ * Signal handler.
+ */
+static void
+killer (int sig)
+{
+ statd_unregister ();
+ xlog(D_GENERAL, "Caught signal %d, un-registering and exiting", sig);
+ exit(0);
+}
+
+static void
+sigusr (int sig)
+{
+ extern void my_svc_exit (void);
+ xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
+ MY_STATE);
+ my_svc_exit();
+}
+
+/*
+ * Startup information.
+ */
+static void log_modes(void)
+{
+ char buf[128]; /* watch stack size... */
+
+ /* No flags = no message */
+ if (!run_mode) return;
+
+ memset(buf,0,128);
+ sprintf(buf,"Flags: ");
+ if (run_mode & MODE_NODAEMON)
+ strcat(buf,"No-Daemon ");
+ if (run_mode & MODE_LOG_STDERR)
+ strcat(buf,"Log-STDERR ");
+#ifdef HAVE_LIBTIRPC
+ strcat(buf, "TI-RPC ");
+#endif
+
+ xlog_warn("%s", buf);
+}
+
+/*
+ * Since we do more than standard statd stuff, we might need to
+ * help the occasional admin.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,"usage: %s [options]\n", name_p);
+ fprintf(stderr," -h, -?, --help Print this help screen.\n");
+ fprintf(stderr," -F, --foreground Foreground (no-daemon mode)\n");
+ fprintf(stderr," -d, --no-syslog Verbose logging to stderr. Foreground mode only.\n");
+ fprintf(stderr," -p, --port Port to listen on\n");
+ fprintf(stderr," -o, --outgoing-port Port for outgoing connections\n");
+ fprintf(stderr," -V, -v, --version Display version information and exit.\n");
+ fprintf(stderr," -n, --name Specify a local hostname.\n");
+ fprintf(stderr," -P State directory path.\n");
+ fprintf(stderr," -N Run in notify only mode.\n");
+ fprintf(stderr," -L, --no-notify Do not perform any notification.\n");
+ fprintf(stderr," -H Specify a high-availability callout program.\n");
+}
+
+static const char *pidfile = "/run/rpc.statd.pid";
+
+int pidfd = -1;
+static void create_pidfile(void)
+{
+ FILE *fp;
+
+ unlink(pidfile);
+ fp = fopen(pidfile, "w");
+ if (!fp)
+ xlog_err("Opening %s failed: %m\n", pidfile);
+ fprintf(fp, "%d\n", getpid());
+ pidfd = dup(fileno(fp));
+ if (fclose(fp) < 0) {
+ xlog_warn("Flushing pid file failed: errno %d (%m)\n",
+ errno);
+ }
+}
+
+static void truncate_pidfile(void)
+{
+ if (pidfd >= 0) {
+ if (ftruncate(pidfd, 0) < 0) {
+ xlog_warn("truncating pid file failed: errno %d (%m)\n",
+ errno);
+ }
+ }
+}
+
+static void run_sm_notify(int outport)
+{
+ char op[20];
+ char *av[6];
+ int ac = 0;
+
+ av[ac++] = "/usr/sbin/sm-notify";
+ if (run_mode & MODE_NODAEMON)
+ av[ac++] = "-d";
+ if (outport) {
+ sprintf(op, "-p%d", outport);
+ av[ac++] = op;
+ }
+ if (run_mode & STATIC_HOSTNAME) {
+ av[ac++] = "-v";
+ av[ac++] = MY_NAME;
+ }
+ av[ac] = NULL;
+ execv(av[0], av);
+ fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
+ exit(2);
+
+}
+
+static void set_nlm_port(char *type, int port)
+{
+ char nbuf[20];
+ char pathbuf[40];
+ int fd;
+ if (!port)
+ return;
+ snprintf(nbuf, sizeof(nbuf), "%d", port);
+ snprintf(pathbuf, sizeof(pathbuf), "/proc/sys/fs/nfs/nlm_%sport", type);
+ fd = open(pathbuf, O_WRONLY);
+ if (fd < 0 && errno == ENOENT) {
+ /* probably module not loaded */
+ if (system("modprobe lockd"))
+ {/* ignore return value */};
+ fd = open(pathbuf, O_WRONLY);
+ }
+ if (fd >= 0) {
+ if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf))
+ fprintf(stderr, "%s: fail to set NLM %s port: %s\n",
+ name_p, type, strerror(errno));
+ close(fd);
+ } else
+ fprintf(stderr, "%s: failed to open %s: %s\n",
+ name_p, pathbuf, strerror(errno));
+}
+int port = 0, out_port = 0;
+int nlm_udp = 0, nlm_tcp = 0;
+
+inline static void
+read_statd_conf(char **argv)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("statd");
+
+ out_port = conf_get_num("statd", "outgoing-port", out_port);
+ port = conf_get_num("statd", "port", port);
+
+ MY_NAME = conf_get_str("statd", "name");
+ if (MY_NAME)
+ run_mode |= STATIC_HOSTNAME;
+
+ s = conf_get_str("statd", "state-directory-path");
+ if (s && !nsm_setup_pathnames(argv[0], s))
+ exit(1);
+
+ s = conf_get_str("statd", "ha-callout");
+ if (s)
+ ha_callout_prog = s;
+
+ nlm_tcp = conf_get_num("lockd", "port", nlm_tcp);
+ /* udp defaults to the same as tcp ! */
+ nlm_udp = conf_get_num("lockd", "udp-port", nlm_tcp);
+
+ if (conf_get_bool("statd", "no-notify", false))
+ run_mode |= MODE_NO_NOTIFY;
+}
+
+/*
+ * Entry routine/main loop.
+ */
+int main (int argc, char **argv)
+{
+ extern char *optarg;
+ int pid;
+ int arg;
+ struct rlimit rlim;
+ int notify_sockfd;
+ char *env;
+
+ /* Default: daemon mode, no other options */
+ run_mode = 0;
+
+ env = getenv("RPC_STATD_NO_NOTIFY");
+ if (env && atoi(env) > 0)
+ run_mode |= MODE_NO_NOTIFY;
+
+ /* Log to stderr if there's an error during startup */
+ xlog_stderr(1);
+ xlog_syslog(0);
+
+ /* Set the basename */
+ if ((name_p = strrchr(argv[0],'/')) != NULL) {
+ name_p ++;
+ } else {
+ name_p = argv[0];
+ }
+
+ /* Set hostname */
+ MY_NAME = NULL;
+
+ /* Read in config setting */
+ read_statd_conf(argv);
+
+ /* Process command line switches */
+ while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:LT:U:", longopts, NULL)) != EOF) {
+ switch (arg) {
+ case 'V': /* Version */
+ case 'v':
+ printf("%s version " VERSION "\n",name_p);
+ exit(0);
+ case 'F': /* Foreground/nodaemon mode */
+ run_mode |= MODE_NODAEMON;
+ break;
+ case 'N':
+ run_mode |= MODE_NOTIFY_ONLY;
+ break;
+ case 'L': /* Listen only */
+ run_mode |= MODE_NO_NOTIFY;
+ break;
+ case 'd': /* No daemon only - log to stderr */
+ run_mode |= MODE_LOG_STDERR;
+ break;
+ case 'o':
+ out_port = atoi(optarg);
+ if (out_port < 1 || out_port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port < 1 || port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'T': /* NLM TCP and UDP port */
+ nlm_tcp = atoi(optarg);
+ if (nlm_tcp < 1 || nlm_tcp > 65535) {
+ fprintf(stderr, "%s: bad nlm port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ if (nlm_udp == 0)
+ nlm_udp = nlm_tcp;
+ break;
+ case 'U': /* NLM UDP port */
+ nlm_udp = atoi(optarg);
+ if (nlm_udp < 1 || nlm_udp > 65535) {
+ fprintf(stderr, "%s: bad nlm UDP port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'n': /* Specify local hostname */
+ run_mode |= STATIC_HOSTNAME;
+ MY_NAME = xstrdup(optarg);
+ break;
+ case 'P':
+ if (!nsm_setup_pathnames(argv[0], optarg))
+ exit(1);
+ break;
+ case 'H': /* PRC: specify the ha-callout program */
+ if ((ha_callout_prog = xstrdup(optarg)) == NULL)
+ exit(1);
+ break;
+ case '?': /* heeeeeelllllllpppp? heh */
+ case 'h':
+ usage();
+ exit (0);
+ default: /* oh dear ... heh */
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* Refuse to start if another statd is running */
+ if (nfs_probe_statd()) {
+ fprintf(stderr, "Statd service already running!\n");
+ exit(1);
+ }
+
+ if (port == out_port && port != 0) {
+ fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
+ exit(-1);
+ }
+
+ if (run_mode & MODE_NOTIFY_ONLY) {
+ fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
+ name_p);
+ run_sm_notify(out_port);
+ }
+
+ if (!(run_mode & MODE_NODAEMON)) {
+ run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
+ daemon mode. */
+ }
+
+ if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
+ fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ else {
+ /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
+ if (rlim.rlim_cur > FD_SETSIZE) {
+ rlim.rlim_cur = FD_SETSIZE;
+
+ if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
+ fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ }
+ }
+ }
+
+ set_nlm_port("tcp", nlm_tcp);
+ set_nlm_port("udp", nlm_udp);
+
+#ifdef SIMULATIONS
+ if (argc > 1)
+ /* LH - I _really_ need to update simulator... */
+ simulator (--argc, ++argv); /* simulator() does exit() */
+#endif
+
+ daemon_init((run_mode & MODE_NODAEMON));
+
+ if (run_mode & MODE_LOG_STDERR) {
+ xlog_syslog(0);
+ xlog_stderr(1);
+ xlog_config(D_ALL, 1);
+ } else {
+ xlog_syslog(1);
+ xlog_stderr(0);
+ }
+
+ xlog_open(name_p);
+ xlog(L_NOTICE, "Version " VERSION " starting");
+
+ log_modes();
+
+ signal (SIGHUP, killer);
+ signal (SIGINT, killer);
+ signal (SIGTERM, killer);
+ /* PRC: trap SIGUSR1 to re-read notify list from disk */
+ signal(SIGUSR1, sigusr);
+ /* WARNING: the following works on Linux and SysV, but not BSD! */
+ signal(SIGCHLD, SIG_IGN);
+ /*
+ * Ignore SIGPIPE to avoid statd dying when peers close their
+ * TCP connection while we're trying to reply to them.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ create_pidfile();
+ atexit(truncate_pidfile);
+
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
+ }
+
+ /* Make sure we have a privilege port for calling into the kernel */
+ if ((notify_sockfd = statd_get_socket()) < 0)
+ exit(1);
+
+ /* If sm-notify didn't take all the state files, load
+ * state information into our notify-list so we can
+ * pass on any SM_NOTIFY that arrives
+ */
+ load_state();
+
+ MY_STATE = nsm_get_state(0);
+ if (MY_STATE == 0)
+ exit(1);
+ xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
+ nsm_update_kernel_state(MY_STATE);
+
+ /*
+ * ORDER
+ * Clear old listeners while still root, to override any
+ * permission checking done by rpcbind.
+ */
+ statd_unregister();
+
+ /*
+ * ORDER
+ */
+ if (!nsm_drop_privileges(pidfd))
+ exit(1);
+
+ /*
+ * ORDER
+ * Create RPC listeners after dropping privileges. This permits
+ * statd to unregister its own listeners when it exits.
+ */
+ if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
+ xlog(L_ERROR, "failed to create RPC listeners, exiting");
+ exit(1);
+ }
+ atexit(statd_unregister);
+
+ /* If we got this far, we have successfully started */
+ daemon_ready();
+
+ for (;;) {
+ /*
+ * Handle incoming requests: SM_NOTIFY socket requests, as
+ * well as callbacks from lockd.
+ */
+ my_svc_run(notify_sockfd); /* I rolled my own, Olaf made it better... */
+
+ /* Only get here when simulating a crash so we should probably
+ * start sm-notify running again. As we have already dropped
+ * privileges, this might not work, but I don't think
+ * responding to SM_SIMU_CRASH is an important use cases to
+ * get perfect.
+ */
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
+ }
+
+ }
+ return 0;
+}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
new file mode 100644
index 0000000..bb1fecb
--- /dev/null
+++ b/utils/statd/statd.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "sm_inter.h"
+#include "system.h"
+#include "xlog.h"
+
+/*
+ * Status definitions.
+ */
+#define STAT_FAIL stat_fail
+#define STAT_SUCC stat_succ
+
+/*
+ * Function prototypes.
+ */
+extern _Bool statd_matchhostname(const char *hostname1, const char *hostname2);
+extern _Bool statd_present_address(const struct sockaddr *sap, char *buf,
+ const size_t buflen);
+__attribute__((__malloc__))
+extern char * statd_canonical_name(const char *hostname);
+
+extern void my_svc_run(int);
+extern void notify_hosts(void);
+extern void shuffle_dirs(void);
+extern int statd_get_socket(void);
+extern int process_notify_list(void);
+extern int process_reply(FD_SET_TYPE *);
+extern char * xstrdup(const char *);
+extern void * xmalloc(size_t);
+extern void load_state(void);
+
+/*
+ * Host status structure and macros.
+ */
+extern stat_chge SM_stat_chge;
+#define MY_NAME SM_stat_chge.mon_name
+#define MY_STATE SM_stat_chge.state
+
+/*
+ * Some timeout values. (Timeout values are in whole seconds.)
+ */
+#define CALLBACK_TIMEOUT 3 /* For client call-backs. */
+#define NOTIFY_TIMEOUT 5 /* For status-change notifications. */
+#define SELECT_TIMEOUT 10 /* Max select() timeout when work to do. */
+#define MAX_TRIES 5 /* Max number of tries for any host. */
+
+/*
+ * Modes of operation - Lon
+ */
+extern int run_mode;
+#define MODE_NODAEMON 1 /* No-daemon/foreground mode. */
+#define MODE_LOG_STDERR 2 /* in foreground mode, log to stderr */
+#define MODE_NOTIFY_ONLY 4 /* Send SM_NOTIFY to everyone monitored on
+ a single interface/alias */
+/* LH - notify_only mode would be for notifying hosts on an IP alias
+ * that just came back up, for ex, when failing over a HA service to
+ * another host.... */
+#define STATIC_HOSTNAME 8 /* Always use the hostname set by -n */
+#define MODE_NO_NOTIFY 16 /* Don't notify peers of a reboot */
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
new file mode 100644
index 0000000..7441ffd
--- /dev/null
+++ b/utils/statd/statd.man
@@ -0,0 +1,474 @@
+.\"@(#)rpc.statd.8"
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
+.\" Modified by Lon Hohberger, 2000.
+.\" Modified by Paul Clements, 2004.
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH RPC.STATD 8 "1 November 2009"
+.SH NAME
+rpc.statd \- NSM service daemon
+.SH SYNOPSIS
+.BI "rpc.statd [-dh?FLNvV] [-H " prog "] [-n " my-name "] [-o " outgoing-port ]
+.ti +10
+.BI "[-p " listener-port "] [-P " path ]
+.ti +10
+.BI "[--nlm-port " port "] [--nlm-udp-port " port ]
+.SH DESCRIPTION
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 [RFC1094] and NFS version 3 [RFC1813], the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
+.B sm-notify
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
+.B rpc.statd
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client uses the server hostname from the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command sends the
+.I my_name
+string recorded when that remote was monitored.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
+.SH OPTIONS
+.TP
+.BR -d , " --no-syslog
+Causes
+.B rpc.statd
+to write log messages on
+.I stderr
+instead of to the system log,
+if the
+.B -F
+option was also specified.
+.TP
+.BR -F , " --foreground
+Keeps
+.B rpc.statd
+attached to its controlling terminal so that NSM
+operation can be monitored directly or run under a debugger.
+If this option is not specified,
+.B rpc.statd
+backgrounds itself soon after it starts.
+.TP
+.BR -h , " -?" , " --help
+Causes
+.B rpc.statd
+to display usage information on
+.I stderr
+and then exit.
+.TP
+.BI "\-H," "" " \-\-ha-callout " prog
+Specifies a high availability callout program.
+If this option is not specified, no callouts are performed.
+See the
+.B High-availability callouts
+section below for details.
+.TP
+.BR -L , " --no-notify
+Prevents
+.B rpc.statd
+from running the
+.B sm-notify
+command when it starts up,
+preserving the existing NSM state number and monitor list.
+.IP
+Note: the
+.B sm-notify
+command contains a check to ensure it runs only once after each system reboot.
+This prevents spurious reboot notification if
+.B rpc.statd
+restarts without the
+.B -L
+option.
+.TP
+.BI "\-n, " "" "\-\-name " ipaddr " | " hostname
+This string is only used by the
+.B sm-notify
+command as the source address from which to send reboot notification requests.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If this option is not specified,
+.B rpc.statd
+uses a wildcard address as the transport bind address.
+See
+.BR sm-notify (8)
+for details.
+.TP
+.BR -N
+Causes
+.B rpc.statd
+to run the
+.B sm-notify
+command, and then exit.
+Since the
+.B sm-notify
+command can also be run directly, this option is deprecated.
+.TP
+.BI "\-o," "" " \-\-outgoing\-port " port
+Specifies the source port number the
+.B sm-notify
+command should use when sending reboot notifications.
+See
+.BR sm-notify (8)
+for details.
+.TP
+.BI "\-p," "" " \-\-port " port
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
+.B rpc.statd
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of its listeners when
+SM_NOTIFY requests must traverse a firewall between clients and
+servers.
+.TP
+.BI "\-T," "" " \-\-nlm\-port " port
+Specifies the port number that
+.I lockd
+should listen on for
+.B NLM
+requests. This sets both the TCP and UDP ports unless the UDP port is
+set separately.
+.TP
+.BI "\-U," "" " \-\-nlm\-udp\-port " port
+Specifies the UDP port number that
+.I lockd
+should listen on for
+.B NLM
+requests.
+.TP
+.BI "\-P, " "" \-\-state\-directory\-path " pathname"
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B rpc.statd
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B rpc.statd
+attempts to set its effective UID and GID to the owner
+and group of the subdirectory
+.B sm
+of this directory. After changing the effective ids,
+.B rpc.statd
+only needs to access files in
+.B sm
+and
+.B sm.bak
+within the state-directory-path.
+.TP
+.BR -v ", " -V ", " --version
+Causes
+.B rpc.statd
+to display version information on
+.I stderr
+and then exit.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [statd]
+or, in some cases, the
+.B [lockd]
+sections of the
+.I /etc/nfs.conf
+configuration file.
+Values recognized in the
+.B [statd]
+section include
+.BR port ,
+.BR outgoing-port ,
+.BR name ,
+.BR state-directory-path ", and"
+.B ha-callout
+which each have the same effect as the option with the same name.
+
+The values recognized in the
+.B [lockd]
+section include
+.B port
+and
+.B udp-port
+which have the same effect as the
+.B --nlm-port
+and
+.B --nlm-udp-port
+options, respectively.
+
+.SH SECURITY
+The
+.B rpc.statd
+daemon must be started as root to acquire privileges needed
+to create sockets with privileged source ports, and to access the
+state information database.
+Because
+.B rpc.statd
+maintains a long-running network service, however, it drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.PP
+You can also protect your
+.B rpc.statd
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+To use the
+.B tcp_wrapper
+library, add the hostnames of peers that should be allowed access to
+.IR /etc/hosts.allow .
+Use the daemon name
+.B statd
+even if the
+.B rpc.statd
+binary has a different filename.
+.P
+For further information see the
+.BR tcpd (8)
+and
+.BR hosts_access (5)
+man pages.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS High-availability callouts
+.B rpc.statd
+can exec a special callout program during processing of
+successful SM_MON, SM_UNMON, and SM_UNMON_ALL requests,
+or when it receives SM_NOTIFY.
+Such a program may be used in High Availability NFS (HA-NFS)
+environments to track lock state that may need to be migrated after
+a system reboot.
+.PP
+The name of the callout program is specified with the
+.B -H
+option.
+The program is run with 3 arguments:
+The first is either
+.B add-client
+.B del-client
+or
+.B sm-notify
+depending on the reason for the callout.
+The second is the
+.I mon_name
+of the monitored peer.
+The third is the
+.I caller_name
+of the requesting lock manager for
+.B add-client
+or
+.B del-client
+, otherwise it is
+.I IP_address
+of the caller sending SM_NOTIFY.
+The forth is the
+.I state_value
+in the SM_NOTIFY request.
+
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into
+.BR rpc.statd ,
+it attempts to start listeners on network transports marked 'visible' in
+.IR /etc/netconfig .
+As long as at least one network transport listener starts successfully,
+.B rpc.statd
+will operate.
+.SH ENVIRONMENT
+.TP
+.B RPC_STATD_NO_NOTIFY=
+If set to a positive integer, has the same effect as
+.IR \-\-no\-notify .
+.SH FILES
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /run/run.statd.pid
+pid file
+.TP 2.5i
+.I /etc/netconfig
+network transport capability database
+.SH SEE ALSO
+.BR sm-notify (8),
+.BR nfs (5),
+.BR rpc.nfsd (8),
+.BR rpcbind (8),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
+.SH AUTHORS
+Jeff Uphoff <juphoff@users.sourceforge.net>
+.br
+Olaf Kirch <okir@monad.swb.de>
+.br
+H.J. Lu <hjl@gnu.org>
+.br
+Lon Hohberger <hohberger@missioncriticallinux.com>
+.br
+Paul Clements <paul.clements@steeleye.com>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
new file mode 100644
index 0000000..e343c76
--- /dev/null
+++ b/utils/statd/svc_run.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 1984 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This has been modified for my own evil purposes to prevent deadlocks
+ * when two hosts start NSM's simultaneously and try to notify each
+ * other (which mainly occurs during testing), or to stop and smell the
+ * roses when I have callbacks due.
+ * --Jeff Uphoff.
+ */
+
+/*
+ * This is the RPC server side idle loop.
+ * Wait for input, call server program.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <time.h>
+#include <inttypes.h>
+#include "statd.h"
+#include "notlist.h"
+
+void my_svc_exit(void);
+static int svc_stop = 0;
+
+/*
+ * This is the global notify list onto which all SM_NOTIFY and CALLBACK
+ * requests are put.
+ */
+notify_list * notify = NULL;
+
+/*
+ * Jump-off function.
+ */
+void
+my_svc_exit(void)
+{
+ svc_stop = 1;
+}
+
+
+/*
+ * The heart of the server. A crib from libc for the most part...
+ */
+void
+my_svc_run(int sockfd)
+{
+ FD_SET_TYPE readfds;
+ int selret;
+ time_t now;
+
+ svc_stop = 0;
+
+ for (;;) {
+ if (svc_stop)
+ return;
+
+ /* Ah, there are some notifications to be processed */
+ while (notify && NL_WHEN(notify) <= time(&now)) {
+ process_notify_list();
+ }
+
+ readfds = SVC_FDSET;
+ /* Set notify sockfd for waiting for reply */
+ FD_SET(sockfd, &readfds);
+ if (notify) {
+ struct timeval tv;
+
+ tv.tv_sec = NL_WHEN(notify) - now;
+ tv.tv_usec = 0;
+ xlog(D_GENERAL, "Waiting for reply... (timeo %jd)",
+ (intmax_t)tv.tv_sec);
+ selret = select(FD_SETSIZE, &readfds,
+ (void *) 0, (void *) 0, &tv);
+ } else {
+ xlog(D_GENERAL, "Waiting for client connections");
+ selret = select(FD_SETSIZE, &readfds,
+ (void *) 0, (void *) 0, (struct timeval *) 0);
+ }
+
+ switch (selret) {
+ case -1:
+ if (errno == EINTR || errno == ECONNREFUSED
+ || errno == ENETUNREACH || errno == EHOSTUNREACH)
+ continue;
+ xlog(L_ERROR, "my_svc_run() - select: %m");
+ return;
+
+ case 0:
+ /* A notify/callback timed out. */
+ continue;
+
+ default:
+ selret -= process_reply(&readfds);
+ if (selret) {
+ FD_CLR(sockfd, &readfds);
+ svc_getreqset(&readfds);
+ }
+ }
+ }
+}
diff --git a/utils/statd/system.h b/utils/statd/system.h
new file mode 100644
index 0000000..a1739c4
--- /dev/null
+++ b/utils/statd/system.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 1996 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997, 1999.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * System-dependent declarations
+ */
+
+#ifdef FD_SETSIZE
+# define FD_SET_TYPE fd_set
+# define SVC_FDSET svc_fdset
+#else
+# define FD_SET_TYPE int
+# define SVC_FDSET svc_fds
+#endif