diff options
author | hjl <hjl> | 1999-10-18 23:21:12 +0000 |
---|---|---|
committer | hjl <hjl> | 1999-10-18 23:21:12 +0000 |
commit | 8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9 (patch) | |
tree | 0904ef8554ed680fe3244fa618685e1fb7ea148b /utils/statd | |
download | nfs-utils-8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9.tar.gz nfs-utils-8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9.tar.xz nfs-utils-8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9.zip |
Initial revision
Diffstat (limited to 'utils/statd')
-rw-r--r-- | utils/statd/COPYING | 340 | ||||
-rw-r--r-- | utils/statd/COPYRIGHT | 25 | ||||
-rw-r--r-- | utils/statd/Makefile | 58 | ||||
-rw-r--r-- | utils/statd/TODO | 13 | ||||
-rw-r--r-- | utils/statd/callback.c | 53 | ||||
-rw-r--r-- | utils/statd/log.c | 108 | ||||
-rw-r--r-- | utils/statd/log.h | 41 | ||||
-rw-r--r-- | utils/statd/misc.c | 72 | ||||
-rw-r--r-- | utils/statd/monitor.c | 287 | ||||
-rw-r--r-- | utils/statd/notify.c | 75 | ||||
-rw-r--r-- | utils/statd/notlist.c | 125 | ||||
-rw-r--r-- | utils/statd/notlist.h | 111 | ||||
-rw-r--r-- | utils/statd/rmtcall.c | 406 | ||||
-rw-r--r-- | utils/statd/sim_sm_inter.x | 32 | ||||
-rw-r--r-- | utils/statd/simu.c | 29 | ||||
-rw-r--r-- | utils/statd/simulate.c | 225 | ||||
-rw-r--r-- | utils/statd/sm_inter.x | 132 | ||||
-rw-r--r-- | utils/statd/stat.c | 37 | ||||
-rw-r--r-- | utils/statd/statd.c | 122 | ||||
-rw-r--r-- | utils/statd/statd.h | 58 | ||||
-rw-r--r-- | utils/statd/statd.man | 53 | ||||
-rw-r--r-- | utils/statd/state.c | 126 | ||||
-rw-r--r-- | utils/statd/svc_run.c | 128 | ||||
-rw-r--r-- | utils/statd/system.h | 18 | ||||
-rw-r--r-- | utils/statd/version.h | 7 |
25 files changed, 2681 insertions, 0 deletions
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/COPYRIGHT b/utils/statd/COPYRIGHT new file mode 100644 index 0000000..7b91031 --- /dev/null +++ b/utils/statd/COPYRIGHT @@ -0,0 +1,25 @@ +rpc.statd -- Network Status Monitor (NSM) protocol daemon for Linux. +Copyright (C) 1995-1999 Jeffrey A. Uphoff + +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., 675 Massachusetts Ave, Cambridge, MA 02139, USA. + +Jeffrey A. Uphoff +Transmeta Corporation +2540 Mission College Blvd. +Santa Clara, CA, 95054 +USA + +Phone: +1-408-919-6458 +Internet: juphoff@transmeta.com diff --git a/utils/statd/Makefile b/utils/statd/Makefile new file mode 100644 index 0000000..3a3a794 --- /dev/null +++ b/utils/statd/Makefile @@ -0,0 +1,58 @@ +# Copyright (C) 1995-1999 Jeffrey A. Uphoff +# Adapted for linux-nfs build tree by Olaf Kirch, 1996. +# +# NSM for Linux. + +# Uncomment for embedded client-side simulation functions. +#SIMUL = -DSIMULATIONS + +# Undefined is normal, defined provides debug logging. +#DEBUG = -DDEBUG + +################################################################## +# no user-serviceable parts below this line +################################################################## +PROGRAM = statd +PREFIX = rpc. +OBJS = $(SRCS:.c=.o) +CCOPTS = $(DEBUG) $(SIMUL) +LIBS = -lexport + +SRCS = $(RPCSRCS) $(SIMSRCS) \ + callback.c notlist.c log.c misc.c monitor.c notify.c simu.c \ + stat.c statd.c state.c svc_run.c rmtcall.c +HDRS = $(RPCHDRS) $(SIMHDRS) + +RPCSRCS = sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c +RPCHDRS = sm_inter.h + +ifdef SIMUL +SIMSRCS = sim_sm_inter_clnt.c sim_sm_inter_svc.c +SIMHDRS = sim_sm_inter.h +SRCS += simulate.c +endif + +MAN8 = statd + +include $(TOP)rules.mk + +AFLAGS += -Wno-unused + +$(RPCHDRS) $(RPCSRCS): sm_inter.x + $(RM) $(RPCHDRS) $(RPCSRCS) + $(RPCGEN) -h -o sm_inter.h $< + $(RPCGEN) -l -o sm_inter_clnt.c $< + $(RPCGEN) -m -o sm_inter_svc.c $< + $(RPCGEN) -c -o sm_inter_xdr.c $< + +$(SIMHDRS) $(SIMSRCS): sim_sm_inter.x + $(RM) $(SIMHDRS) $(SIMSRCS) + $(RPCGEN) -h -o sim_sm_inter.h $< + $(RPCGEN) -l -o sim_sm_inter_clnt.c $< + $(RPCGEN) -m -o sim_sm_inter_svc.c $< + +clean:: + $(RM) $(PROGRAM) + +distclean:: + $(RM) $(RPCHDRS) $(RPCSRCS) $(SIMHDRS) $(SIMSRCS) 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..e3fad6a --- /dev/null +++ b/utils/statd/callback.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, Oct. 1996. + * + * NSM for Linux. + */ + +#include "config.h" +#include "statd.h" +#include "notlist.h" + +/* Callback notify list. */ +notify_list *cbnl = NULL; + + +/* + * Services SM_NOTIFY requests. + * Any clients that have asked us to monitor that host are put on + * the global callback list, which is processed as soon as statd + * returns to svc_run. + */ +void * +sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp) +{ + notify_list *lp, *call; + static char *result = NULL; + + dprintf(L_DEBUG, "Received SM_NOTIFY from %s, state: %d", + argp->mon_name, argp->state); + + if ((lp = rtnl) != NULL) { + log(L_WARNING, "SM_NOTIFY from %s--nobody looking!", + argp->mon_name, argp->state); + 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. + */ + while ((lp = nlist_gethost(lp, argp->mon_name, 0)) != NULL) { + if (NL_STATE(lp) != argp->state) { + NL_STATE(lp) = argp->state; + call = nlist_clone(lp); + NL_TYPE(call) = NOTIFY_CALLBACK; + nlist_insert(¬ify, call); + } + lp = NL_NEXT(lp); + } + + return ((void *) &result); +} diff --git a/utils/statd/log.c b/utils/statd/log.c new file mode 100644 index 0000000..38f7d3a --- /dev/null +++ b/utils/statd/log.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 1995 Olaf Kirch + * Modified by Jeffrey A. Uphoff, 1995, 1997, 1999. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +/* + * log.c - logging functions for lockd/statd + * 260295 okir started with simply syslog logging. + */ + +#include "config.h" + +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include <sys/types.h> +#include "log.h" + +static char progname[256]; +static pid_t mypid; + /* Turns on logging to console/stderr. */ +static int opt_debug = 0; /* Will be command-line option, eventually */ + +void +log_init(char *name) +{ + char *sp; + + openlog(name, LOG_PID, LOG_LOCAL5); + if ((sp = strrchr(name, '/')) != NULL) + name = ++sp; + strncpy(progname, name, sizeof (progname) - 1); + progname[sizeof (progname) - 1] = '\0'; + mypid = getpid(); +} + +void +log_background(void) +{ + /* NOP */ +} + +void +log_enable(int level) +{ + opt_debug = 1; +} + +int +log_enabled(int level) +{ + return opt_debug; +} + +void +die(char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf (buffer, 1024, fmt, ap); + va_end(ap); + buffer[1023]=0; + + log(L_FATAL, "%s", buffer); + +#ifndef DEBUG + exit (2); +#else + abort(); /* make a core */ +#endif +} + +void +log(int level, char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf (buffer, 1024, fmt, ap); + va_end(ap); + buffer[1023]=0; + + if (level < L_DEBUG) { + syslog(level, buffer); + } + + if (opt_debug) { + time_t now; + struct tm * tm; + + time(&now); + tm = localtime(&now); + fprintf (stderr, "%02d.%02d.%02d %02d:%02d:%02d %s[%d]: %s\n", + tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec, + progname, mypid, + buffer); + } +} diff --git a/utils/statd/log.h b/utils/statd/log.h new file mode 100644 index 0000000..f00bb63 --- /dev/null +++ b/utils/statd/log.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 1995 Olaf Kirch + * Modified by Jeffrey A. Uphoff, 1996, 1997, 1999. + * + * NSM for Linux. + */ + +/* + * logging functionality + * 260295 okir + */ + +#ifndef _LOCKD_LOG_H_ +#define _LOCKD_LOG_H_ + +#include <syslog.h> + +void log_init(char *name); +void log_background(void); +void log_enable(int facility); +int log_enabled(int facility); +void log(int level, char *fmt, ...); +void die(char *fmt, ...); + +/* + * Map per-application severity to system severity. What's fatal for + * lockd is merely an itching spot from the universe's point of view. + */ +#define L_CRIT LOG_CRIT +#define L_FATAL LOG_ERR +#define L_ERROR LOG_WARNING +#define L_WARNING LOG_NOTICE +#define L_DEBUG LOG_DEBUG + +#ifdef DEBUG +#define dprintf log +#else +#define dprintf if (0) log +#endif + +#endif /* _LOCKD_LOG_H_ */ diff --git a/utils/statd/misc.c b/utils/statd/misc.c new file mode 100644 index 0000000..42f6e57 --- /dev/null +++ b/utils/statd/misc.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1995-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +#include "config.h" + +#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))) + /* SHIT! SHIT! SHIT! */ + die ("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))) + die ("strdup failed"); + + return (result); +} + + +/* + * Call with check=1 to verify that this host is not still on the rtnl + * before unlinking file. + */ +void +xunlink (char *path, char *host, short int check) +{ + char *tozap; + + tozap=alloca (strlen(path)+strlen(host)+2); + sprintf (tozap, "%s/%s", path, host); + + if (!check || !nlist_gethost(rtnl, host, 0)) + if (unlink (tozap) == -1) + log (L_ERROR, "unlink (%s): %s", tozap, strerror (errno)); + else + dprintf (L_DEBUG, "Unlinked %s", tozap); + else + dprintf (L_DEBUG, "Not unlinking %s--host still monitored.", tozap); +} diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c new file mode 100644 index 0000000..5a782dc --- /dev/null +++ b/utils/statd/monitor.c @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#include "config.h" + +#include <fcntl.h> +#include <limits.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include "misc.h" +#include "statd.h" +#include "notlist.h" + +notify_list * rtnl = NULL; /* Run-time notify list. */ + + +/* + * 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 *path; + int fd; + notify_list *clnt; + struct in_addr my_addr; +#ifdef RESTRICTED_STATD + struct in_addr mon_addr, caller; +#else + struct hostent *hostinfo = NULL; +#endif + + /* Assume that we'll fail. */ + result.res_stat = STAT_FAIL; + result.state = -1; /* State is undefined for STAT_FAIL. */ + + /* Restrict access to statd. + * In the light of CERT CA-99.05, we tighten access to + * statd. --okir + */ +#ifdef RESTRICTED_STATD + /* 1. Reject anyone not calling from 127.0.0.1. + * Ignore the my_name specified by the caller, and + * use "127.0.0.1" instead. + */ + caller = svc_getcaller(rqstp->rq_xprt)->sin_addr; + if (caller.s_addr != htonl(INADDR_LOOPBACK)) { + log(L_WARNING, + "Call to statd from non-local host %s", + inet_ntoa(caller)); + goto failure; + } + my_addr.s_addr = htonl(INADDR_LOOPBACK); + my_name = "127.0.0.1"; + + /* 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. + */ + if (id->my_proc != 100021) { + log(L_WARNING, + "Attempt to register callback to service %d", + id->my_proc); + goto failure; + } + + /* 3. mon_name must be an address in dotted quad. + * Again, specific to the linux kernel lockd. + */ + if (!inet_aton(mon_name, &mon_addr)) { + log(L_WARNING, + "Attempt to register host %s (not a dotted quad)", + mon_name); + goto failure; + } +#else + /* + * 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, '/')) { + log(L_CRIT, "SM_MON request for hostname containing '/': %s", + mon_name); + log(L_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!"); + goto failure; + } else if (gethostbyname(mon_name) == NULL) { + log(L_WARNING, "gethostbyname error for %s", mon_name); + goto failure; + } else if (!(hostinfo = gethostbyname(my_name))) { + log(L_WARNING, "gethostbyname error for %s", my_name); + goto failure; + } else + my_addr = *(struct in_addr *) hostinfo->h_addr; +#endif + + /* + * Hostnames checked OK. + * 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. + */ + if (rtnl) { + notify_list *temp = rtnl; + + while ((temp = nlist_gethost(temp, mon_name, 0))) { + if (matchhostname(NL_MY_NAME(temp), my_name) && + NL_MY_PROC(temp) == id->my_proc && + NL_MY_PROG(temp) == id->my_prog && + NL_MY_VERS(temp) == id->my_vers) { + /* Hey! We already know you guys! */ + dprintf(L_DEBUG, + "Duplicate SM_MON request for %s " + "from procedure on %s", + mon_name, my_name); + + /* But we'll let you pass anyway. */ + result.res_stat = STAT_SUCC; + result.state = MY_STATE; + return (&result); + } + temp = NL_NEXT(temp); + } + } + + /* + * We're committed...ignoring errors. Let's hope that a malloc() + * doesn't fail. (I should probably fix this assumption.) + */ + if (!(clnt = nlist_new(my_name, mon_name, 0))) { + log(L_WARNING, "out of memory"); + goto failure; + } + + NL_ADDR(clnt) = my_addr; + 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); + + /* + * Now, Create file on stable storage for host. + */ + + path=xmalloc(strlen(SM_DIR)+strlen(mon_name)+2); + sprintf(path, SM_DIR "/%s", mon_name); + if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { + /* Didn't fly. We won't monitor. */ + log(L_ERROR, "creat(%s) failed: %m", path); + nlist_free(NULL, clnt); + free(path); + goto failure; + } + free(path); + nlist_insert(&rtnl, clnt); + close(fd); + + result.res_stat = STAT_SUCC; + result.state = MY_STATE; + dprintf(L_DEBUG, "MONITORING %s for %s", mon_name, my_name); + return (&result); + +failure: + log(L_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); + return (&result); +} + + +/* + * 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; + + result.state = MY_STATE; + + /* Check if we're monitoring anyone. */ + if (!(clnt = rtnl)) { + log(L_WARNING, + "Received SM_UNMON request from %s for %s while not " + "monitoring any hosts.", my_name, argp->mon_name); + return (&result); + } + + /* + * 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 (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! */ + dprintf(L_DEBUG, "UNMONITORING %s for %s", + mon_name, my_name); + nlist_free(&rtnl, clnt); + xunlink(SM_DIR, mon_name, 1); + + return (&result); + } else + clnt = NL_NEXT(clnt); + } + + log(L_WARNING, "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; + + result.state = MY_STATE; + + if (!(clnt = rtnl)) { + log(L_WARNING, "Received SM_UNMON_ALL request from %s " + "while not monitoring any hosts", argp->my_name); + return (&result); + } + + while ((clnt = nlist_gethost(clnt, argp->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; + + dprintf(L_DEBUG, + "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); + nlist_free(&rtnl, clnt); + xunlink(SM_DIR, mon_name, 1); + ++count; + clnt = temp; + } else + clnt = NL_NEXT(clnt); + } + + if (!count) { + dprintf(L_DEBUG, "SM_UNMON_ALL request from %s with no " + "SM_MON requests from it.", argp->my_name); + } + + return (&result); +} diff --git a/utils/statd/notify.c b/utils/statd/notify.c new file mode 100644 index 0000000..89d2946 --- /dev/null +++ b/utils/statd/notify.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, Oct. 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +/* + * NSM notify list handling. + */ + +#include "config.h" + +#include <dirent.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include "misc.h" +#include "statd.h" +#include "notlist.h" + +/* + * Initial (startup) notify list. + */ +notify_list *inl = NULL; + + +/* + * Get list of hosts from stable storage, build list of hosts to + * contact. These hosts are added to the global RPC notify list + * which is processed as soon as statd enters svc_run. + */ +void +notify_hosts(void) +{ + DIR *nld; + struct dirent *de; + notify_list *call; + + if (!(nld = opendir(SM_BAK_DIR))) { + perror("opendir"); + exit(errno); + } + + while ((de = readdir(nld))) { + if (de->d_name[0] == '.') + continue; + + /* The following can happen for loopback NFS mounts + * (e.g. with cfsd) */ + if (matchhostname(de->d_name, MY_NAME) + || matchhostname(de->d_name, "localhost")) { + char *fname; + fname=xmalloc(strlen(SM_BAK_DIR)+sizeof(de->d_name)+2); + dprintf(L_DEBUG, "We're on our own notify list?!?"); + sprintf(fname, SM_BAK_DIR "/%s", de->d_name); + if (unlink(fname)) + log(L_ERROR, "unlink(%s): %s", + fname, strerror(errno)); + free(fname); + continue; + } + + call = nlist_new(MY_NAME, de->d_name, -1); + NL_TYPE(call) = NOTIFY_REBOOT; + nlist_insert(¬ify, call); + } + + if (closedir(nld) == -1) { + perror("closedir"); + exit(1); + } +} diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c new file mode 100644 index 0000000..bc0c294 --- /dev/null +++ b/utils/statd/notlist.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +/* + * Simple list management for notify list + */ + +#include "config.h" + +#include <string.h> +#include "misc.h" +#include "statd.h" +#include "notlist.h" + +notify_list * +nlist_new(char *my_name, char *mon_name, int state) +{ + notify_list *new; + + if (!(new = (notify_list *) xmalloc(sizeof(notify_list)))) + return NULL; + memset(new, 0, sizeof(*new)); + + NL_TIMES(new) = MAX_TRIES; + NL_STATE(new) = state; + if (!(NL_MY_NAME(new) = xstrdup(my_name)) + || !(NL_MON_NAME(new) = xstrdup(mon_name))) + return NULL; + + return new; +} + +void +nlist_insert(notify_list **head, notify_list *entry) +{ + notify_list *next = *head, *tail = entry; + + /* Find end of list to be inserted */ + while (tail->next) + tail = tail->next; + + if (next) + next->prev = tail; + tail->next = next; + *head = entry; +} + +void +nlist_insert_timer(notify_list **head, notify_list *entry) +{ + /* Find first entry with higher timeout value */ + while (*head && NL_WHEN(*head) <= NL_WHEN(entry)) + head = &(*head)->next; + nlist_insert(head, entry); +} + +void +nlist_remove(notify_list **head, notify_list *entry) +{ + notify_list *prev = entry->prev, + *next = entry->next; + + if (next) + next->prev = prev; + if (prev) + prev->next = next; + else + *head = next; + entry->next = entry->prev = 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); + NL_ADDR(new) = NL_ADDR(entry); + memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE); + + return new; +} + +void +nlist_free(notify_list **head, notify_list *entry) +{ + if (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); +} + +void +nlist_kill(notify_list **head) +{ + while (*head) + nlist_free(head, *head); +} + +/* + * 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 (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..0c6709c --- /dev/null +++ b/utils/statd/notlist.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 1995, 1997-1999 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. */ + struct in_addr addr; /* IP address for callback. */ + unsigned short port; /* port number for callback */ + short int times; /* Counter used for various things. */ + int state; /* For storing notified state for callbacks. */ + struct notify_list *next; /* Linked list forward pointer. */ + struct notify_list *prev; /* Linked list backward pointer. */ + u_int32_t xid; /* XID of MS_NOTIFY RPC call */ + time_t when; /* notify: timeout for re-xmit */ + int type; /* type of notify (REBOOT/CALLBACK) */ +}; + +typedef struct notify_list notify_list; + +#define NOTIFY_REBOOT 0 /* notify remote of our reboot */ +#define NOTIFY_CALLBACK 1 /* notify client of remote reboot */ + +/* + * 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_ADDR(L) ((L)->addr) +#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) +#define NL_TYPE(L) ((L)->type) + +#if 0 +#define NL_ADD_NO_ZERO(LIST, ITEM)\ + NL_PREV(NL_FIRST((LIST))) = (ITEM);\ + NL_NEXT((ITEM)) = NL_FIRST((LIST));\ + NL_FIRST((LIST)) = (ITEM);\ + NL_PREV((ITEM)) = (LIST);\ + NL_TIMES((ITEM)) = 0; + +#define NL_ADD(LIST, ITEM)\ + NL_ADD_NO_ZERO((LIST), (ITEM));\ + NL_ADDR((ITEM)) = 0;\ + NL_STATE((ITEM)) = 0; + +#define NL_DEL(ITEM)\ + NL_NEXT(NL_PREV((ITEM))) = NL_NEXT((ITEM));\ + NL_PREV(NL_NEXT((ITEM))) = NL_PREV((ITEM)); + +#define NL_FREE(ITEM)\ + if (NL_MY_NAME ((ITEM)))\ + free (NL_MY_NAME ((ITEM)));\ + if (NL_MON_NAME ((ITEM)))\ + free (NL_MON_NAME((ITEM)));\ + free ((ITEM)); + +#define NL_DEL_FREE(ITEM)\ + NL_DEL((ITEM))\ + NL_FREE((ITEM)) + +/* Yuck. Kludge. */ +#define NL_COPY(SRC, DEST)\ + NL_TIMES((DEST)) = NL_TIMES((SRC));\ + NL_STATE((DEST)) = NL_TIMES((SRC));\ + NL_MY_PROC((DEST)) = NL_MY_PROC((SRC));\ + NL_MY_PROG((DEST)) = NL_MY_PROG((SRC));\ + NL_MY_VERS((DEST)) = NL_MY_VERS((SRC));\ + NL_MON_NAME((DEST)) = xstrdup (NL_MON_NAME((SRC)));\ + NL_MY_NAME((DEST)) = xstrdup (NL_MY_NAME((SRC)));\ + memcpy (&NL_ADDR((DEST)), &NL_ADDR((SRC)), sizeof (u_long));\ + memcpy (NL_PRIV((DEST)), NL_PRIV((SRC)), SM_PRIV_SIZE); +#endif diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c new file mode 100644 index 0000000..a08c4b1 --- /dev/null +++ b/utils/statd/rmtcall.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 1996, 1999 Olaf Kirch + * Modified by Jeffrey A. Uphoff, 1997-1999. + * Modified by H.J. Lu, 1998. + * + * 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). + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/pmap_rmt.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include "sm_inter.h" +#include "statd.h" +#include "notlist.h" +#include "log.h" + +#define MAXMSGSIZE (2048 / sizeof(unsigned int)) + +static unsigned long xid = 0; /* RPC XID counter */ +static int sockfd = -1; /* notify socket */ + +/* + * Initialize callback socket + */ +static int +get_socket(void) +{ + struct sockaddr_in sin; + + if (sockfd >= 0) + return sockfd; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + log(L_CRIT, "Can't create socket: %m"); + return -1; + } + + FD_SET(sockfd, &SVC_FDSET); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if (bindresvport(sockfd, &sin) < 0) { + dprintf(L_WARNING, + "process_hosts: can't bind to reserved port\n"); + } + + return sockfd; +} + +/* + * Try to resolve host name for notify/callback request + * + * When compiled with RESTRICTED_STATD defined, we expect all + * host names to be dotted quads. See monitor.c for details. --okir + */ +#ifdef RESTRICTED_STATD +static int +try_to_resolve(notify_list *lp) +{ + char *hname; + + if (NL_TYPE(lp) == NOTIFY_REBOOT) + hname = NL_MON_NAME(lp); + else + hname = NL_MY_NAME(lp); + if (!inet_aton(hname, &(NL_ADDR(lp)))) { + log(L_ERROR, "%s is not an dotted-quad address", hname); + NL_TIMES(lp) = 0; + return 0; + } + + /* XXX: In order to handle multi-homed hosts, we could do + * a reverse lookup, a forward lookup, and cycle through + * all the addresses. + */ + return 1; +} +#else +static int +try_to_resolve(notify_list *lp) +{ + struct hostent *hp; + char *hname; + + if (NL_TYPE(lp) == NOTIFY_REBOOT) + hname = NL_MON_NAME(lp); + else + hname = NL_MY_NAME(lp); + + dprintf(L_DEBUG, "Trying to resolve %s.", hname); + if (!(hp = gethostbyname(hname))) { + herror("gethostbyname"); + NL_TIMES(lp) -= 1; + return 0; + } + + if (hp->h_addrtype != AF_INET) { + log(L_ERROR, "%s is not an AF_INET address", hname); + NL_TIMES(lp) = 0; + return 0; + } + + /* FIXME: should try all addresses for multi-homed hosts in + * alternation because one interface might be down/unreachable. */ + NL_ADDR(lp) = *(struct in_addr *) hp->h_addr; + + dprintf(L_DEBUG, "address of %s is %s", hname, inet_ntoa(NL_ADDR(lp))); + return 1; +} +#endif + +static unsigned long +xmit_call(int sockfd, struct sockaddr_in *sin, + u_int32_t prog, u_int32_t vers, u_int32_t proc, + xdrproc_t func, void *obj) +/* __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */ +{ + unsigned int msgbuf[MAXMSGSIZE], msglen; + struct rpc_msg mesg; + struct pmap pmap; + XDR xdr, *xdrs = &xdr; + int err; + + if (!xid) + xid = getpid() + time(NULL); + + mesg.rm_xid = ++xid; + mesg.rm_direction = CALL; + mesg.rm_call.cb_rpcvers = 2; + if (sin->sin_port == 0) { + sin->sin_port = htons(PMAPPORT); + mesg.rm_call.cb_prog = PMAPPROG; + mesg.rm_call.cb_vers = PMAPVERS; + mesg.rm_call.cb_proc = PMAPPROC_GETPORT; + pmap.pm_prog = prog; + pmap.pm_vers = vers; + pmap.pm_prot = IPPROTO_UDP; + pmap.pm_port = 0; + func = (xdrproc_t) xdr_pmap; + obj = &pmap; + } else { + mesg.rm_call.cb_prog = prog; + mesg.rm_call.cb_vers = vers; + mesg.rm_call.cb_proc = proc; + } + mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL; + mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL; + mesg.rm_call.cb_cred.oa_length = 0; + mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL; + mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL; + mesg.rm_call.cb_verf.oa_length = 0; + + /* Create XDR memory object for encoding */ + xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE); + + /* Encode the RPC header part and payload */ + if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) { + dprintf(L_WARNING, "xmit_mesg: can't encode RPC message!\n"); + xdr_destroy(xdrs); + return 0; + } + + /* Get overall length of datagram */ + msglen = xdr_getpos(xdrs); + + if ((err = sendto(sockfd, msgbuf, msglen, 0, + (struct sockaddr *) sin, sizeof(*sin))) < 0) { + dprintf(L_WARNING, "xmit_mesg: sendto failed: %m"); + } else if (err != msglen) { + dprintf(L_WARNING, "xmit_mesg: short write: %m\n"); + } + + xdr_destroy(xdrs); + + return err == msglen? xid : 0; +} + +static notify_list * +recv_rply(int sockfd, struct sockaddr_in *sin, u_long *portp) +{ + unsigned int msgbuf[MAXMSGSIZE], msglen; + struct rpc_msg mesg; + notify_list *lp = NULL; + XDR xdr, *xdrs = &xdr; + int alen = sizeof(*sin); + + /* Receive message */ + if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0, + (struct sockaddr *) sin, &alen)) < 0) { + dprintf(L_WARNING, "recv_rply: recvfrom failed: %m"); + return NULL; + } + + /* Create XDR object for decoding buffer */ + xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE); + + memset(&mesg, 0, sizeof(mesg)); + mesg.rm_reply.rp_acpt.ar_results.where = NULL; + mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void; + + if (!xdr_replymsg(xdrs, &mesg)) { + log(L_WARNING, "recv_rply: can't decode RPC message!\n"); + goto done; + } + + if (mesg.rm_reply.rp_stat != 0) { + log(L_WARNING, "recv_rply: [%s] RPC status %d\n", + inet_ntoa(sin->sin_addr), + mesg.rm_reply.rp_stat); + goto done; + } + if (mesg.rm_reply.rp_acpt.ar_stat != 0) { + log(L_WARNING, "recv_rply: [%s] RPC status %d\n", + inet_ntoa(sin->sin_addr), + mesg.rm_reply.rp_acpt.ar_stat); + goto done; + } + + for (lp = notify; lp != NULL; lp = lp->next) { + if (lp->xid != xid) + continue; + if (lp->addr.s_addr != sin->sin_addr.s_addr) { + char addr [18]; + strncpy (addr, inet_ntoa(lp->addr), + sizeof (addr) - 1); + addr [sizeof (addr) - 1] = '\0'; + dprintf(L_WARNING, "address mismatch: " + "expected %s, got %s\n", + addr, inet_ntoa(sin->sin_addr)); + } + if (lp->port == 0) { + if (!xdr_u_long(xdrs, portp)) { + log(L_WARNING, "recv_rply: [%s] " + "can't decode reply body!\n", + inet_ntoa(sin->sin_addr)); + lp = NULL; + goto done; + } + } + break; + } + +done: + xdr_destroy(xdrs); + return lp; +} + +/* + * Notify operation for a single list entry + */ +static int +process_entry(int sockfd, notify_list *lp) +{ + struct sockaddr_in sin; + struct status new_status; + xdrproc_t func; + void *objp; + u_int32_t proc, vers, prog; +/* __u32 proc, vers, prog; */ + + if (lp->addr.s_addr == INADDR_ANY && !try_to_resolve(lp)) + return NL_TIMES(lp); + if (NL_TIMES(lp) == 0) { + log(L_DEBUG, "Cannot notify %s, giving up.\n", + inet_ntoa(NL_ADDR(lp))); + return 0; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = lp->port; + sin.sin_addr = lp->addr; + + switch (NL_TYPE(lp)) { + case NOTIFY_REBOOT: + prog = SM_PROG; + vers = SM_VERS; + proc = SM_NOTIFY; + func = (xdrproc_t) xdr_stat_chge; + objp = &SM_stat_chge; + break; + case NOTIFY_CALLBACK: + prog = NL_MY_PROG(lp); + vers = NL_MY_VERS(lp); + proc = NL_MY_PROC(lp); + func = (xdrproc_t) xdr_status; + objp = &new_status; + new_status.mon_name = NL_MON_NAME(lp); + new_status.state = NL_STATE(lp); + memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE); + break; + default: + log(L_ERROR, "notify_host: unknown notify type %d", + NL_TYPE(lp)); + return 0; + } + + lp->xid = xmit_call(sockfd, &sin, prog, vers, proc, func, objp); + if (!lp->xid) { + log(L_WARNING, "notify_host: failed to notify %s\n", + inet_ntoa(lp->addr)); + } + NL_TIMES(lp) -= 1; + + return 1; +} + +/* + * Process a datagram received on the notify socket + */ +int +process_reply(FD_SET_TYPE *rfds) +{ + struct sockaddr_in sin; + notify_list *lp; + u_long port; + + if (sockfd == -1 || !FD_ISSET(sockfd, rfds)) + return 0; + + if (!(lp = recv_rply(sockfd, &sin, &port))) + return 1; + + if (lp->port == 0) { + if (port != 0) { + lp->port = htons((unsigned short) port); + process_entry(sockfd, lp); + NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT; + nlist_remove(¬ify, lp); + nlist_insert_timer(¬ify, lp); + return 1; + } + log(L_WARNING, "recv_rply: [%s] service %d not registered", + inet_ntoa(lp->addr), + NL_TYPE(lp) == NOTIFY_REBOOT? SM_PROG : NL_MY_PROG(lp)); + } else if (NL_TYPE(lp) == NOTIFY_REBOOT) { + dprintf(L_DEBUG, "Notification of %s succeeded.", + NL_MON_NAME(lp)); + xunlink(SM_BAK_DIR, NL_MON_NAME(lp), 0); + } else { + dprintf(L_DEBUG, "Callback to %s (for %d) succeeded.", + 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; + int fd; + + if ((fd = get_socket()) < 0) + return 0; + + while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) { + if (process_entry(fd, entry)) { + NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT; + nlist_remove(¬ify, entry); + nlist_insert_timer(¬ify, entry); + } else if (NL_TYPE(entry) == NOTIFY_CALLBACK) { + log(L_ERROR, + "Can't callback %s (%d,%d), giving up.", + NL_MY_NAME(entry), + NL_MY_PROG(entry), + NL_MY_VERS(entry)); + nlist_free(¬ify, entry); + } else { + log(L_ERROR, + "Can't notify %s, giving up.", + NL_MON_NAME(entry)); + xunlink(SM_BAK_DIR, NL_MON_NAME(entry), 0); + 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..fa4e3a6 --- /dev/null +++ b/utils/statd/simu.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * + * NSM for Linux. + */ + +#include "config.h" +#include "statd.h" +#include "notlist.h" + +extern void my_svc_exit (void); + + +/* + * Services SM_SIMU_CRASH requests. + */ +void * +sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp) +{ + static char *result = NULL; + + log (L_WARNING, "*** SIMULATING CRASH! ***"); + my_svc_exit (); + + if (rtnl) + nlist_kill (&rtnl); + + return ((void *)&result); +} diff --git a/utils/statd/simulate.c b/utils/statd/simulate.c new file mode 100644 index 0000000..4b8d59c --- /dev/null +++ b/utils/statd/simulate.c @@ -0,0 +1,225 @@ +/* + * 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 "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) +{ + log_enable (1); + + 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])); + } + die ("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; + + dprintf (L_DEBUG, "Calling %s (as %s) to monitor %s", calling, as, + monitoring); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) + die ("%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))) + die ("%s", clnt_sperror (client, "sm_mon_1")); + + free (mon.mon_id.my_id.my_name); + + if (result->res_stat != STAT_SUCC) { + log (L_FATAL, "SM_MON request failed, state: %d", result->state); + exit (0); + } else { + dprintf (L_DEBUG, "SM_MON result successful, state: %d\n", result->state); + dprintf (L_DEBUG, "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; + + dprintf (L_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as, + unmonitoring); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) + die ("%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))) + die ("%s", clnt_sperror (client, "sm_unmon_1")); + + free (mon_id.my_id.my_name); + dprintf (L_DEBUG, "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; + + dprintf (L_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as); + + if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) + die ("%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))) + die ("%s", clnt_sperror (client, "sm_unmon_all_1")); + + free (my_id.my_name); + dprintf (L_DEBUG, "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) + die ("%s", clnt_spcreateerror ("clnt_create")); + + if (!sm_simu_crash_1 (NULL, client)) + die ("%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) + die ("%s", clnt_spcreateerror ("clnt_create")); + + checking.mon_name = monitoring; + + if (!(result = sm_stat_1 (&checking, client))) + die ("%s", clnt_sperror (client, "sm_stat_1")); + + if (result->res_stat == STAT_SUCC) + dprintf (L_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling, + monitoring, result->state); + else + dprintf (L_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling, + monitoring, result->state); + + exit (0); +} + +static void +sim_killer (int sig) +{ + log (L_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig); + pmap_unset (sim_port, SIM_SM_VERS); + exit (0); +} + +static void +daemon_simulator (void) +{ + signal (SIGHUP, sim_killer); + signal (SIGINT, sim_killer); + signal (SIGTERM, sim_killer); + pmap_unset (sim_port, SIM_SM_VERS); + do_regist (sim_port, sim_sm_prog_1); +/* do_regist (sim_port, (__dispatch_fn_t)sim_sm_prog_1); */ + 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; + + dprintf (L_DEBUG, "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_inter.x b/utils/statd/sm_inter.x new file mode 100644 index 0000000..5232a28 --- /dev/null +++ b/utils/statd/sm_inter.x @@ -0,0 +1,132 @@ +/* + * Copyright (C) 1986 Sun Microsystems, Inc. + * Modified by Jeffrey A. Uphoff, 1995, 1997-1999. + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Status monitor protocol specification + */ + +#ifdef RPC_CLNT +%#include <string.h> +#endif + +program SM_PROG { + version SM_VERS { + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* if res_stat == stat_succ, state = state number of site sm_name */ + struct sm_stat_res SM_STAT(struct sm_name) = 1; + + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* stat consists of state number of local site */ + struct sm_stat_res SM_MON(struct mon) = 2; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON(struct mon_id) = 3; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON_ALL(struct my_id) = 4; + + void SM_SIMU_CRASH(void) = 5; + + void SM_NOTIFY(struct stat_chge) = 6; + + } = 1; +} = 100024; + +const SM_MAXSTRLEN = 1024; +const SM_PRIV_SIZE = 16; + +struct sm_name { + string mon_name<SM_MAXSTRLEN>; +}; + +struct my_id { + string my_name<SM_MAXSTRLEN>; /* name of the site iniates the monitoring request*/ + int my_prog; /* rpc program # of the requesting process */ + int my_vers; /* rpc version # of the requesting process */ + int my_proc; /* rpc procedure # of the requesting process */ +}; + +struct mon_id { + string mon_name<SM_MAXSTRLEN>; /* name of the site to be monitored */ + struct my_id my_id; +}; + + +struct mon { + struct mon_id mon_id; + opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */ +}; + +struct stat_chge { + string mon_name<SM_MAXSTRLEN>; /* name of the site that had the state change */ + int state; +}; + +/* + * state # of status monitor monitonically increases each time + * status of the site changes: + * an even number (>= 0) indicates the site is down and + * an odd number (> 0) indicates the site is up; + */ +struct sm_stat { + int state; /* state # of status monitor */ +}; + +enum res { + stat_succ = 0, /* status monitor agrees to monitor */ + stat_fail = 1 /* status monitor cannot monitor */ +}; + +struct sm_stat_res { + res res_stat; + int state; +}; + +/* + * structure of the status message sent back by the status monitor + * when monitor site status changes + */ +struct status { + string mon_name<SM_MAXSTRLEN>; + int state; + opaque priv[SM_PRIV_SIZE]; /* stored private information */ +}; + +%#define SM_INTER_X diff --git a/utils/statd/stat.c b/utils/statd/stat.c new file mode 100644 index 0000000..021e786 --- /dev/null +++ b/utils/statd/stat.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 1995, 1997, 1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * + * NSM for Linux. + */ + +#include "config.h" +#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. + */ +struct sm_stat_res * +sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp) +{ + static sm_stat_res result; + + if (gethostbyname (argp->mon_name) == NULL) { + log (L_WARNING, "gethostbyname error for %s", argp->mon_name); + result.res_stat = STAT_FAIL; + dprintf (L_DEBUG, "STAT_FAIL for %s", argp->mon_name); + } else { + result.res_stat = STAT_SUCC; + dprintf (L_DEBUG, "STAT_SUCC for %s", argp->mon_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..3b76e30 --- /dev/null +++ b/utils/statd/statd.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, Oct. 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +#include "config.h" +#include <limits.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include "statd.h" +#include "version.h" + +short int restart = 0; +int _rpcpmstart = 0; /* flags for tirpc rpcgen */ +int _rpcfdtype = 0; +int _rpcsvcdirty = 0; + +extern void sm_prog_1 (struct svc_req *, register SVCXPRT); + +#ifdef SIMULATIONS +extern void simulator (int, char **); +#endif + + +/* + * Signal handler. + */ +static void +killer (int sig) +{ + log (L_FATAL, "Caught signal %d, un-registering and exiting.", sig); + pmap_unset (SM_PROG, SM_VERS); + exit (0); +} + + +/* + * Entry routine/main loop. + */ +int +main (int argc, char **argv) +{ + int pid; + int foreground = 0; + + log_init (argv[0]); + + if (argc == 2 && strcmp (argv [1], "-F") == 0) { + foreground = 1; + argc--; + argv++; + } + +#ifdef SIMULATIONS + if (argc > 1) + simulator (--argc, ++argv); /* simulator() does exit() */ +#endif + + if (!foreground) { + int filedes; + + if ((pid = fork ()) < 0) { + perror ("Could not fork"); + exit (1); + } else if (pid != 0) { + /* Parent. */ + exit (0); + } + /* Child. */ + setsid (); + chdir (DIR_BASE); + + for (filedes = 0; filedes < OPEN_MAX; filedes++) { + close (filedes); + } + } + + /* Child. */ + signal (SIGHUP, killer); + signal (SIGINT, killer); + signal (SIGTERM, killer); + + for (;;) { + pmap_unset (SM_PROG, SM_VERS); + change_state (); + shuffle_dirs (); + notify_hosts (); + ++restart; + do_regist (SM_PROG, sm_prog_1); + my_svc_run (); /* I rolled my own, Olaf made it better... */ + } + return 0; +} + + +/* + * Register services. + */ +void +do_regist(u_long prog, void (*sm_prog_1)()) +/* do_regist(u_long prog, __dispatch_fn_t sm_prog_1) */ +{ + SVCXPRT *transp; + + if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) + die("cannot create udp service."); + + if (!svc_register(transp, prog, SM_VERS, sm_prog_1, IPPROTO_UDP)) + die("unable to register (SM_PROG, SM_VERS, udp)."); + + if ((transp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) + die("cannot create tcp service."); + + if (!svc_register(transp, prog, SM_VERS, sm_prog_1, IPPROTO_TCP)) + die("unable to register (SM_PROG, SM_VERS, tcp)."); +} diff --git a/utils/statd/statd.h b/utils/statd/statd.h new file mode 100644 index 0000000..77a179a --- /dev/null +++ b/utils/statd/statd.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, Dec. 1996. + * + * NSM for Linux. + */ + +#include "config.h" +#include "sm_inter.h" +#include "system.h" +#include "log.h" + +/* + * Paths and filenames. + */ +#if defined(NFS_STATEDIR) +# define DIR_BASE NFS_STATEDIR "/" +#else +# define DIR_BASE "/var/lib/nfs/" +#endif +#define SM_DIR DIR_BASE "sm" +#define SM_BAK_DIR DIR_BASE "sm.bak" +#define SM_STAT_PATH DIR_BASE "state" + +/* + * Status definitions. + */ +#define STAT_FAIL stat_fail +#define STAT_SUCC stat_succ + +/* + * Function prototypes. + */ +extern void change_state(void); +extern void do_regist(u_long, void (*)()); +extern void my_svc_run(void); +extern void notify_hosts(void); +extern void shuffle_dirs(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 xunlink (char *, char *, short int); + +/* + * Host status structure and macros. + */ +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. */ diff --git a/utils/statd/statd.man b/utils/statd/statd.man new file mode 100644 index 0000000..373cf77 --- /dev/null +++ b/utils/statd/statd.man @@ -0,0 +1,53 @@ +.\" +.\" statd(8) +.\" +.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de> +.\" Modified by Jeffrey A. Uphoff, 1999. +.TH rpc.statd 8 "11 June 1999" +.SH NAME +rpc.statd \- NSM status monitor +.SH SYNOPSIS +.B "/usr/sbin/rpc.statd [-F] +.SH DESCRIPTION +The +.B rpc.statd +server implements the NSM (Network Status Monitor) RPC protocol. +This service is somewhat misnomed, since it doesn't actually provide +active monitoring as one might suspect; instead, NSM implements a +reboot notification service. It is used by the NFS file locking service, +.BR rpc.lockd , +to implement lock recovery when the NFS server machine crashes and +reboots. +.SS Operation +For each NFS client or server machine to be monitored, +.B rpc.statd +creates a file in +.BR /var/lib/nfs/sm . +When starting, it iterates through these files and notifies the +peer +.B rpc.statd +on those machines. +.SH OPTIONS +.TP +.B -F +By default, +.B rpc.statd +forks and puts itself in the background when started. The +.B -F +argument tells it to remain in the foreground. This option is +mainly for debugging purposes. +.SH FILES +.BR /var/lib/nfs/sm/state +.br +.BR /var/lib/nfs/sm/* +.br +.BR /var/lib/nfs/sm.bak/* +.SH SEE ALSO +.BR rpc.nfsd(8) +.SH AUTHORS +.br +Jeff Uphoff <juphoff@transmeta.com> +.br +Olaf Kirch <okir@monad.swb.de> +.br +H.J. Lu <hjl@gnu.org> diff --git a/utils/statd/state.c b/utils/statd/state.c new file mode 100644 index 0000000..101c00b --- /dev/null +++ b/utils/statd/state.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +#include "config.h" +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include "statd.h" + + +/* + * Most NSM's keep the status number in an ASCII file. I'm keeping it + * as an int (4-byte binary) for now... + */ +void +change_state (void) +{ + int fd, size; + extern short int restart; + + if ((fd = open (SM_STAT_PATH, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) == -1) + die ("open (%s): %s", SM_STAT_PATH, strerror (errno)); + + if ((size = read (fd, &MY_STATE, sizeof MY_STATE)) == -1) + die ("read (%s): %s", SM_STAT_PATH, strerror (errno)); + + if (size != 0 && size != sizeof MY_STATE) { + log (L_ERROR, "Error in status file format...correcting."); + + if (close (fd) == -1) + die ("close (%s): %s", SM_STAT_PATH, strerror (errno)); + + if ((fd = creat (SM_STAT_PATH, S_IRUSR | S_IWUSR)) == -1) + die ("creat (%s): %s", SM_STAT_PATH, strerror (errno)); + } + log (L_DEBUG, "New state: %u", (++MY_STATE % 2) ? MY_STATE : ++MY_STATE); + + if (lseek (fd, 0, SEEK_SET) == -1) + die ("lseek (%s): %s", SM_STAT_PATH, strerror (errno)); + + if (write (fd, &MY_STATE, sizeof MY_STATE) != sizeof MY_STATE) + die ("write (%s): %s", SM_STAT_PATH, strerror (errno)); + + if (fsync (fd) == -1) + log (L_ERROR, "fsync (%s): %s", SM_STAT_PATH, strerror (errno)); + + if (close (fd) == -1) + log (L_ERROR, "close (%s): %s", SM_STAT_PATH, strerror (errno)); + + if (!restart) { + char fullhost[SM_MAXSTRLEN + 1]; + struct hostent *hostinfo; + + if (gethostname (fullhost, SM_MAXSTRLEN) == -1) + die ("gethostname: %s", strerror (errno)); + + if ((hostinfo = gethostbyname (fullhost)) == NULL) + log (L_ERROR, "gethostbyname error for %s", fullhost); + else { + strncpy (fullhost, hostinfo->h_name, sizeof (fullhost) - 1); + fullhost[sizeof (fullhost) - 1] = '\0'; + } + + MY_NAME = xstrdup (fullhost); + } +} + + +/* + * Fairly traditional use of two directories for this. + */ +void +shuffle_dirs (void) +{ + DIR *nld; + struct dirent *de; + struct stat st; + char *src, *dst; + int len1, len2, len; + + if (stat (SM_DIR, &st) == -1 && errno != ENOENT) + die ("stat (%s): %s", SM_DIR, strerror (errno)); + + if (!S_ISDIR (st.st_mode)) + if (mkdir (SM_DIR, S_IRWXU) == -1) + die ("mkdir (%s): %s", SM_DIR, strerror (errno)); + + memset (&st, 0, sizeof st); + + if (stat (SM_BAK_DIR, &st) == -1 && errno != ENOENT) + die ("stat (%s): %s", SM_BAK_DIR, strerror (errno)); + + if (!S_ISDIR (st.st_mode)) + if (mkdir (SM_BAK_DIR, S_IRWXU) == -1) + die ("mkdir (%s): %s", SM_BAK_DIR, strerror (errno)); + + if (!(nld = opendir (SM_DIR))) + die ("opendir (%s): %s", SM_DIR, strerror (errno)); + + len1=strlen(SM_DIR); + len2=strlen(SM_BAK_DIR); + while ((de = readdir (nld))) { + if (de->d_name[0] == '.') + continue; + len=strlen(de->d_name); + src=xmalloc(len1+len+2); + dst=xmalloc(len2+len+2); + sprintf (src, "%s/%s", SM_DIR, de->d_name); + sprintf (dst, "%s/%s", SM_BAK_DIR, de->d_name); + if (rename (src, dst) == -1) + die ("rename (%s to %s): %s", SM_DIR, SM_BAK_DIR, strerror (errno)); + free(src); + free(dst); + } + if (closedir (nld) == -1) + log (L_ERROR, "closedir (%s): %s", SM_DIR, strerror (errno)); +} diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c new file mode 100644 index 0000000..8f6d9fe --- /dev/null +++ b/utils/statd/svc_run.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1984 Sun Microsystems, Inc. + * Modified by Jeffrey A. Uphoff, 1995, 1997-1999. + * Modified by Olaf Kirch, 1996. + * + * NSM for Linux. + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * 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. + */ +#include "config.h" +#include <errno.h> +#include "statd.h" +#include "notlist.h" + +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(void) +{ + 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; + if (notify) { + struct timeval tv; + + tv.tv_sec = NL_WHEN(notify) - now; + tv.tv_usec = 0; + dprintf(L_DEBUG, "Waiting for reply... (timeo %d)", + tv.tv_sec); + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, &tv); + } else { + dprintf(L_DEBUG, "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; + log(L_ERROR, "my_svc_run() - select: %m"); + return; + + case 0: + /* A notify/callback timed out. */ + continue; + + default: + selret -= process_reply(&readfds); + if (selret) + 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 diff --git a/utils/statd/version.h b/utils/statd/version.h new file mode 100644 index 0000000..12f16bd --- /dev/null +++ b/utils/statd/version.h @@ -0,0 +1,7 @@ +/* + * Copyright (C) 1997-1999 Jeffrey A. Uphoff + * + * NSM for Linux. + */ + +#define STATD_RELEASE "1.1.1" |