diff options
Diffstat (limited to 'utils/statd')
-rw-r--r-- | utils/statd/.gitignore | 1 | ||||
-rw-r--r-- | utils/statd/COPYING | 340 | ||||
-rw-r--r-- | utils/statd/Makefile.am | 93 | ||||
-rw-r--r-- | utils/statd/Makefile.in | 984 | ||||
-rw-r--r-- | utils/statd/TODO | 13 | ||||
-rw-r--r-- | utils/statd/callback.c | 122 | ||||
-rw-r--r-- | utils/statd/hostname.c | 319 | ||||
-rw-r--r-- | utils/statd/misc.c | 51 | ||||
-rw-r--r-- | utils/statd/monitor.c | 399 | ||||
-rw-r--r-- | utils/statd/notlist.c | 246 | ||||
-rw-r--r-- | utils/statd/notlist.h | 65 | ||||
-rw-r--r-- | utils/statd/rmtcall.c | 285 | ||||
-rw-r--r-- | utils/statd/sim_sm_inter.x | 32 | ||||
-rw-r--r-- | utils/statd/simu.c | 59 | ||||
-rw-r--r-- | utils/statd/simulate.c | 226 | ||||
-rw-r--r-- | utils/statd/sm-notify.c | 928 | ||||
-rw-r--r-- | utils/statd/sm-notify.man | 366 | ||||
-rwxr-xr-x | utils/statd/start-statd | 33 | ||||
-rw-r--r-- | utils/statd/stat.c | 60 | ||||
-rw-r--r-- | utils/statd/statd.c | 549 | ||||
-rw-r--r-- | utils/statd/statd.h | 68 | ||||
-rw-r--r-- | utils/statd/statd.man | 474 | ||||
-rw-r--r-- | utils/statd/svc_run.c | 138 | ||||
-rw-r--r-- | utils/statd/system.h | 18 |
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(¬ify, 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(¬ify, lp); + nlist_insert_timer(¬ify, 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(¬ify, 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(¬ify, entry); + nlist_insert_timer(¬ify, 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(¬ify, 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 |