This commit was generated by cvs2svn to compensate for changes in r14125,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
commit
42a2c10504
13
sbin/rpc.statd/Makefile
Normal file
13
sbin/rpc.statd/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
# $Id$
|
||||
|
||||
PROG = rpc.statd
|
||||
SRCS = statd.c procs.c file.c
|
||||
MAN8 = rpc.statd.8
|
||||
|
||||
DPADD= ${LIBRPCSVC}
|
||||
LDADD= -lrpcsvc
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
test: test.c
|
||||
cc -o test test.c -lrpcsvc
|
356
sbin/rpc.statd/file.c
Normal file
356
sbin/rpc.statd/file.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h> /* For mmap() */
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "statd.h"
|
||||
|
||||
FileLayout *status_info; /* Pointer to the mmap()ed status file */
|
||||
static int status_fd; /* File descriptor for the open file */
|
||||
static off_t status_file_len; /* Current on-disc length of file */
|
||||
|
||||
/* sync_file --------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Packaged call of msync() to flush changes to mmap()ed file
|
||||
Returns: Nothing. Errors to syslog.
|
||||
*/
|
||||
|
||||
void sync_file(void)
|
||||
{
|
||||
if (msync((void *)status_info, 0, 0) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "msync() failed: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* find_host -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Find the entry in the status file for a given host
|
||||
Returns: Pointer to that entry in the mmap() region, or NULL.
|
||||
Notes: Also creates entries if requested.
|
||||
Failure to create also returns NULL.
|
||||
*/
|
||||
|
||||
HostInfo *find_host(char *hostname, int create)
|
||||
{
|
||||
HostInfo *hp;
|
||||
HostInfo *spare_slot = NULL;
|
||||
HostInfo *result = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
|
||||
{
|
||||
if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
|
||||
{
|
||||
result = hp;
|
||||
break;
|
||||
}
|
||||
if (!spare_slot && !hp->monList && !hp->notifyReqd)
|
||||
spare_slot = hp;
|
||||
}
|
||||
|
||||
/* Return if entry found, or if not asked to create one. */
|
||||
if (result || !create) return (result);
|
||||
|
||||
/* Now create an entry, using the spare slot if one was found or */
|
||||
/* adding to the end of the list otherwise, extending file if reqd */
|
||||
if (!spare_slot)
|
||||
{
|
||||
off_t desired_size;
|
||||
spare_slot = &status_info->hosts[status_info->noOfHosts];
|
||||
desired_size = ((char*)spare_slot - (char*)status_info) + sizeof(HostInfo);
|
||||
if (desired_size > status_file_len)
|
||||
{
|
||||
/* Extend file by writing 1 byte of junk at the desired end pos */
|
||||
lseek(status_fd, desired_size - 1, SEEK_SET);
|
||||
i = write(status_fd, &i, 1);
|
||||
if (i < 1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to extend status file");
|
||||
return (NULL);
|
||||
}
|
||||
status_file_len = desired_size;
|
||||
}
|
||||
status_info->noOfHosts++;
|
||||
}
|
||||
|
||||
/* Initialise the spare slot that has been found/created */
|
||||
/* Note that we do not msync(), since the caller is presumed to be */
|
||||
/* about to modify the entry further */
|
||||
memset(spare_slot, 0, sizeof(HostInfo));
|
||||
strncpy(spare_slot->hostname, hostname, SM_MAXSTRLEN);
|
||||
return (spare_slot);
|
||||
}
|
||||
|
||||
/* init_file -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Open file, create if necessary, initialise it.
|
||||
Returns: Nothing - exits on error
|
||||
Notes: Called before process becomes daemon, hence logs to
|
||||
stderr rather than syslog.
|
||||
Opens the file, then mmap()s it for ease of access.
|
||||
Also performs initial clean-up of the file, zeroing
|
||||
monitor list pointers, setting the notifyReqd flag in
|
||||
all hosts that had a monitor list, and incrementing
|
||||
the state number to the next even value.
|
||||
*/
|
||||
|
||||
void init_file(char *filename)
|
||||
{
|
||||
int new_file = FALSE;
|
||||
char buf[HEADER_LEN];
|
||||
int i;
|
||||
|
||||
/* try to open existing file - if not present, create one */
|
||||
status_fd = open(filename, O_RDWR);
|
||||
if ((status_fd < 0) && (errno == ENOENT))
|
||||
{
|
||||
status_fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
new_file = TRUE;
|
||||
}
|
||||
if (status_fd < 0)
|
||||
{
|
||||
perror("rpc.statd");
|
||||
fprintf(stderr, "Unable to open status file %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* File now open. mmap() it, with a generous size to allow for */
|
||||
/* later growth, where we will extend the file but not re-map it. */
|
||||
status_info = (FileLayout *)
|
||||
mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
|
||||
|
||||
if (status_info == (FileLayout *) -1)
|
||||
{
|
||||
perror("rpc.statd");
|
||||
fprintf(stderr, "Unable to mmap() status file\n");
|
||||
}
|
||||
|
||||
status_file_len = lseek(status_fd, 0L, SEEK_END);
|
||||
|
||||
/* If the file was not newly created, validate the contents, and if */
|
||||
/* defective, re-create from scratch. */
|
||||
if (!new_file)
|
||||
{
|
||||
if ((status_file_len < HEADER_LEN) || (status_file_len
|
||||
< (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
|
||||
{
|
||||
fprintf(stderr, "rpc.statd: status file is corrupt\n");
|
||||
new_file = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialisation of a new, empty file. */
|
||||
if (new_file)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
lseek(status_fd, 0L, SEEK_SET);
|
||||
write(status_fd, buf, HEADER_LEN);
|
||||
status_file_len = HEADER_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Clean-up of existing file - monitored hosts will have a pointer */
|
||||
/* to a list of clients, which refers to memory in the previous */
|
||||
/* incarnation of the program and so are meaningless now. These */
|
||||
/* pointers are zeroed and the fact that the host was previously */
|
||||
/* monitored is recorded by setting the notifyReqd flag, which will */
|
||||
/* in due course cause a SM_NOTIFY to be sent. */
|
||||
/* Note that if we crash twice in quick succession, some hosts may */
|
||||
/* already have notifyReqd set, where we didn't manage to notify */
|
||||
/* them before the second crash occurred. */
|
||||
for (i = 0; i < status_info->noOfHosts; i++)
|
||||
{
|
||||
HostInfo *this_host = &status_info->hosts[i];
|
||||
|
||||
if (this_host->monList)
|
||||
{
|
||||
this_host->notifyReqd = TRUE;
|
||||
this_host->monList = NULL;
|
||||
}
|
||||
}
|
||||
/* Select the next higher even number for the state counter */
|
||||
status_info->ourState = (status_info->ourState + 2) & 0xfffffffe;
|
||||
/*???????******/ status_info->ourState++;
|
||||
}
|
||||
}
|
||||
|
||||
/* xdr_stat_chge ----------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: XDR-encode structure of type stat_chge
|
||||
Returns: TRUE if successful
|
||||
Notes: This function is missing from librpcsvc, because the
|
||||
sm_inter.x distributed by Sun omits the SM_NOTIFY
|
||||
procedure used between co-operating statd's
|
||||
*/
|
||||
|
||||
bool_t xdr_stat_chge(XDR *xdrs, stat_chge *objp)
|
||||
{
|
||||
if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
if (!xdr_int(xdrs, &objp->state))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
|
||||
/* notify_one_host --------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Perform SM_NOTIFY procedure at specified host
|
||||
Returns: TRUE if success, FALSE if failed.
|
||||
*/
|
||||
|
||||
static int notify_one_host(char *hostname)
|
||||
{
|
||||
struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
|
||||
CLIENT *cli;
|
||||
char dummy;
|
||||
stat_chge arg;
|
||||
char our_hostname[SM_MAXSTRLEN+1];
|
||||
|
||||
gethostname(our_hostname, sizeof(our_hostname));
|
||||
our_hostname[SM_MAXSTRLEN] = '\0';
|
||||
arg.mon_name = our_hostname;
|
||||
arg.state = status_info->ourState;
|
||||
|
||||
if (debug) syslog (LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s", hostname, our_hostname);
|
||||
|
||||
cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
|
||||
clnt_spcreateerror(""));
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if (clnt_call(cli, SM_NOTIFY, xdr_stat_chge, &arg, xdr_void, &dummy, timeout)
|
||||
!= RPC_SUCCESS)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact rpc.statd at host %s", hostname);
|
||||
clnt_destroy(cli);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
clnt_destroy(cli);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/* notify_hosts ------------------------------------------------------------ */
|
||||
/*
|
||||
Purpose: Send SM_NOTIFY to all hosts marked as requiring it
|
||||
Returns: Nothing, immediately - forks a process to do the work.
|
||||
Notes: Does nothing if there are no monitored hosts.
|
||||
Called after all the initialisation has been done -
|
||||
logs to syslog.
|
||||
*/
|
||||
|
||||
void notify_hosts(void)
|
||||
{
|
||||
int i;
|
||||
int attempts;
|
||||
int work_to_do = FALSE;
|
||||
HostInfo *hp;
|
||||
pid_t pid;
|
||||
|
||||
/* First check if there is in fact any work to do. */
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->notifyReqd)
|
||||
{
|
||||
work_to_do = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!work_to_do) return; /* No work found */
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (pid) return;
|
||||
|
||||
/* Here in the child process. We continue until all the hosts marked */
|
||||
/* as requiring notification have been duly notified. */
|
||||
/* If one of the initial attempts fails, we sleep for a while and */
|
||||
/* have another go. This is necessary because when we have crashed, */
|
||||
/* (eg. a power outage) it is quite possible that we won't be able to */
|
||||
/* contact all monitored hosts immediately on restart, either because */
|
||||
/* they crashed too and take longer to come up (in which case the */
|
||||
/* notification isn't really required), or more importantly if some */
|
||||
/* router etc. needed to reach the monitored host has not come back */
|
||||
/* up yet. In this case, we will be a bit late in re-establishing */
|
||||
/* locks (after the grace period) but that is the best we can do. */
|
||||
/* We try 10 times at 5 sec intervals, 10 more times at 1 minute */
|
||||
/* intervals, then 24 more times at hourly intervals, finally */
|
||||
/* giving up altogether if the host hasn't come back to life after */
|
||||
/* 24 hours. */
|
||||
|
||||
for (attempts = 0; attempts < 44; attempts++)
|
||||
{
|
||||
work_to_do = FALSE; /* Unless anything fails */
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->notifyReqd)
|
||||
{
|
||||
if (notify_one_host(hp->hostname))
|
||||
{
|
||||
hp->notifyReqd = FALSE;
|
||||
sync_file();
|
||||
}
|
||||
else work_to_do = TRUE;
|
||||
}
|
||||
}
|
||||
if (!work_to_do) break;
|
||||
if (attempts < 10) sleep(5);
|
||||
else if (attempts < 20) sleep(60);
|
||||
else sleep(60*60);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
352
sbin/rpc.statd/procs.c
Normal file
352
sbin/rpc.statd/procs.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
#include <netdb.h> /* for gethostbyname() */
|
||||
|
||||
#include "statd.h"
|
||||
|
||||
/* sm_stat_1 --------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC call to enquire if a host can be monitored
|
||||
Returns: TRUE for any hostname that can be looked up to give
|
||||
an address.
|
||||
*/
|
||||
|
||||
struct sm_stat_res *sm_stat_1(sm_name *arg)
|
||||
{
|
||||
static sm_stat_res res;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
|
||||
|
||||
if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
|
||||
res.res_stat = stat_fail;
|
||||
}
|
||||
|
||||
res.state = status_info->ourState;
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_mon_1 ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to establish a monitor request
|
||||
Returns: Success, unless lack of resources prevents
|
||||
the necessary structures from being set up
|
||||
to record the request, or if the hostname is not
|
||||
valid (as judged by gethostbyname())
|
||||
*/
|
||||
|
||||
struct sm_stat_res *sm_mon_1(mon *arg)
|
||||
{
|
||||
static sm_stat_res res;
|
||||
HostInfo *hp;
|
||||
MonList *lp;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
|
||||
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
|
||||
arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
|
||||
arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
|
||||
arg->mon_id.my_id.my_proc);
|
||||
}
|
||||
|
||||
res.res_stat = stat_fail; /* Assume fail until set otherwise */
|
||||
res.state = status_info->ourState;
|
||||
|
||||
/* Find existing host entry, or create one if not found */
|
||||
/* If find_host() fails, it will have logged the error already. */
|
||||
if (!gethostbyname(arg->mon_id.mon_name))
|
||||
{
|
||||
syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
|
||||
}
|
||||
else if (hp = find_host(arg->mon_id.mon_name, TRUE))
|
||||
{
|
||||
lp = (MonList *)malloc(sizeof(MonList));
|
||||
if (!lp)
|
||||
{
|
||||
syslog(LOG_ERR, "Out of memory");
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
|
||||
lp->notifyProg = arg->mon_id.my_id.my_prog;
|
||||
lp->notifyVers = arg->mon_id.my_id.my_vers;
|
||||
lp->notifyProc = arg->mon_id.my_id.my_proc;
|
||||
memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
|
||||
|
||||
lp->next = hp->monList;
|
||||
hp->monList = lp;
|
||||
sync_file();
|
||||
|
||||
res.res_stat = stat_succ; /* Report success */
|
||||
}
|
||||
}
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* do_unmon ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Remove a monitor request from a host
|
||||
Returns: TRUE if found, FALSE if not found.
|
||||
Notes: Common code from sm_unmon_1 and sm_unmon_all_1
|
||||
In the unlikely event of more than one identical monitor
|
||||
request, all are removed.
|
||||
*/
|
||||
|
||||
static int do_unmon(HostInfo *hp, my_id *idp)
|
||||
{
|
||||
MonList *lp, *next;
|
||||
MonList *last = NULL;
|
||||
int result = FALSE;
|
||||
|
||||
lp = hp->monList;
|
||||
while (lp)
|
||||
{
|
||||
if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
|
||||
&& (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
|
||||
&& (idp->my_vers == lp->notifyVers))
|
||||
{
|
||||
/* found one. Unhook from chain and free. */
|
||||
next = lp->next;
|
||||
if (last) last->next = next;
|
||||
else hp->monList = next;
|
||||
free(lp);
|
||||
lp = next;
|
||||
result = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
last = lp;
|
||||
lp = lp->next;
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* sm_unmon_1 -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to release a monitor request.
|
||||
Returns: Local machine's status number
|
||||
Notes: The supplied mon_id should match the value passed in an
|
||||
earlier call to sm_mon_1
|
||||
*/
|
||||
|
||||
struct sm_stat *sm_unmon_1(mon_id *arg)
|
||||
{
|
||||
static sm_stat res;
|
||||
HostInfo *hp;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
|
||||
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
|
||||
arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
|
||||
arg->my_id.my_vers, arg->my_id.my_proc);
|
||||
}
|
||||
|
||||
if (hp = find_host(arg->mon_name, FALSE))
|
||||
{
|
||||
if (do_unmon(hp, &arg->my_id)) sync_file();
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "unmon request from %s, no matching monitor",
|
||||
arg->my_id.my_name);
|
||||
}
|
||||
}
|
||||
else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
|
||||
arg->my_id.my_name, arg->mon_name);
|
||||
|
||||
res.state = status_info->ourState;
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_unmon_all_1 ---------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to release monitor requests.
|
||||
Returns: Local machine's status number
|
||||
Notes: Releases all monitor requests (if any) from the specified
|
||||
host and program number.
|
||||
*/
|
||||
|
||||
struct sm_stat *sm_unmon_all_1(my_id *arg)
|
||||
{
|
||||
static sm_stat res;
|
||||
HostInfo *hp;
|
||||
MonList *lp;
|
||||
int i;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
|
||||
arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
|
||||
}
|
||||
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
|
||||
{
|
||||
do_unmon(hp, arg);
|
||||
}
|
||||
sync_file();
|
||||
|
||||
res.state = status_info->ourState;
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_simu_crash_1 --------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to simulate a crash
|
||||
Returns: Nothing
|
||||
Notes: Standardised mechanism for debug purposes
|
||||
The specification says that we should drop all of our
|
||||
status information (apart from the list of monitored hosts
|
||||
on disc). However, this would confuse the rpc.lockd
|
||||
which would be unaware that all of its monitor requests
|
||||
had been silently junked. Hence we in fact retain all
|
||||
current requests and simply increment the status counter
|
||||
and inform all hosts on the monitor list.
|
||||
*/
|
||||
|
||||
void *sm_simu_crash_1(void)
|
||||
{
|
||||
static char dummy;
|
||||
int work_to_do;
|
||||
HostInfo *hp;
|
||||
int i;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
|
||||
|
||||
/* Simulate crash by setting notify-required flag on all monitored */
|
||||
/* hosts, and incrementing our status number. notify_hosts() is */
|
||||
/* then called to fork a process to do the notifications. */
|
||||
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->monList)
|
||||
{
|
||||
work_to_do = TRUE;
|
||||
hp->notifyReqd = TRUE;
|
||||
}
|
||||
}
|
||||
status_info->ourState += 2; /* always even numbers if not crashed */
|
||||
|
||||
if (work_to_do) notify_hosts();
|
||||
|
||||
return (&dummy);
|
||||
}
|
||||
|
||||
/* sm_notify_1 ------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure notifying local statd of the crash of another
|
||||
Returns: Nothing
|
||||
Notes: There is danger of deadlock, since it is quite likely that
|
||||
the client procedure that we call will in turn call us
|
||||
to remove or adjust the monitor request.
|
||||
We therefore fork() a process to do the notifications.
|
||||
Note that the main HostInfo structure is in a mmap()
|
||||
region and so will be shared with the child, but the
|
||||
monList pointed to by the HostInfo is in normal memory.
|
||||
Hence if we read the monList before forking, we are
|
||||
protected from the parent servicing other requests
|
||||
that modify the list.
|
||||
*/
|
||||
|
||||
void *sm_notify_1(stat_chge *arg)
|
||||
{
|
||||
struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
|
||||
CLIENT *cli;
|
||||
static char dummy;
|
||||
status tx_arg; /* arg sent to callback procedure */
|
||||
MonList *lp;
|
||||
HostInfo *hp;
|
||||
pid_t pid;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
|
||||
arg->mon_name, arg->state);
|
||||
|
||||
hp = find_host(arg->mon_name, FALSE);
|
||||
if (!hp)
|
||||
{
|
||||
/* Never heard of this host - why is it notifying us? */
|
||||
syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
|
||||
return;
|
||||
}
|
||||
lp = hp->monList;
|
||||
if (!lp) return (FALSE); /* We know this host, but have no */
|
||||
/* outstanding requests. */
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (pid) return (&dummy); /* Parent returns */
|
||||
|
||||
while (lp)
|
||||
{
|
||||
tx_arg.mon_name = arg->mon_name;
|
||||
tx_arg.state = arg->state;
|
||||
memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
|
||||
cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
|
||||
clnt_spcreateerror(""));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg, xdr_void, &dummy,
|
||||
timeout) != RPC_SUCCESS)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
|
||||
lp->notifyHost);
|
||||
}
|
||||
clnt_destroy(cli);
|
||||
}
|
||||
lp = lp->next;
|
||||
}
|
||||
|
||||
exit (0); /* Child quits */
|
||||
}
|
||||
|
107
sbin/rpc.statd/rpc.statd.8
Normal file
107
sbin/rpc.statd/rpc.statd.8
Normal file
@ -0,0 +1,107 @@
|
||||
.\" -*- nroff -*-
|
||||
.\"
|
||||
.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. 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.
|
||||
.\" 3. All advertising materials mentioning features or use of this software
|
||||
.\" must display the following acknowledgement:
|
||||
.\" This product includes software developed by the University of
|
||||
.\" California, Berkeley and its contributors.
|
||||
.\" 4. Neither the name of the University 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 AUTHOR 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 AUTHOR 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.
|
||||
.\"
|
||||
.\"
|
||||
.Dd September 19, 1995
|
||||
.Dt RPC.STATD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rpc.statd
|
||||
.Nd host status monitoring daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/rpc.statd
|
||||
.Op Fl d
|
||||
.Sh DESCRIPTION
|
||||
.Nm rpc.rstatd
|
||||
is a daemon which co-operates with rpc.statd daemons on other hosts to provide
|
||||
a status monitoring service. The daemon accepts requests from
|
||||
programs running on the local host (typically,
|
||||
.Xr rpc.lockd 8 ,
|
||||
the NFS file locking daemon) to monitor the status of specified
|
||||
hosts. If a monitored host crashes and restarts, the remote daemon will
|
||||
notify the local daemon, which in turn will notify the local program(s)
|
||||
which requested the monitoring service. Conversely, if this host crashes
|
||||
and re-starts, when the
|
||||
.Nm rpc.statd
|
||||
re-starts, it will notify all of the hosts which were being monitored
|
||||
at the time of the crash.
|
||||
.Pp
|
||||
Options and operands available for
|
||||
.Nm rpc.statd :
|
||||
.Bl -tag -width Ds
|
||||
.It Fl d
|
||||
The
|
||||
.Fl d
|
||||
option causes debugging information to be written to syslog, recording
|
||||
all RPC transations to the daemon. These messages are logged with level
|
||||
LOG_DEBUG and facility LOG_DAEMON. Error conditions are logged irrespective
|
||||
of this option, using level LOG_ERR.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Nm rpc.rstatd
|
||||
daemon must NOT be invoked by
|
||||
.Xr inetd 8
|
||||
because the protocol assumes that the daemon will run from system start time.
|
||||
Instead, it should be run from
|
||||
.Xr rc 8
|
||||
after the network has been started.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /usr/include/rpcsvc/sm_inter.x -compact
|
||||
.It Pa /var/db/statd.status
|
||||
non-volatile record of currently monitored hosts.
|
||||
.It Pa /usr/include/rpcsvc/sm_inter.x
|
||||
RPC protocol specification used by local applications to register monitoring requests.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr rpc.lockd 8 ,
|
||||
.Xr rc 8 ,
|
||||
.Xr syslog 3
|
||||
.Sh BUGS
|
||||
There is no means for the daemon to tell when a monitored host has
|
||||
disappeared permanently (eg. catastrophic harware failure), as opposed
|
||||
to transient failure of the host or an intermediate router. At present,
|
||||
it will re-try notification attempts at frequent intervals for 10 minutes,
|
||||
then hourly, and finally gives up after 24 hours.
|
||||
|
||||
The protocol requires that symmetric monitor requests are made to both
|
||||
the local and remote daemon in order to establish a monitored relationship.
|
||||
This is convenient for the NFS locking protocol, but probably reduces the
|
||||
usefulness of the monitoring system for other applications.
|
||||
|
||||
The current implementation uses more than 1Kbyte per monitored host in
|
||||
the status file (and also in VM). This may be inefficient for NFS servers
|
||||
with large numbers of clients.
|
||||
.Sh STANDARDS
|
||||
The implementation is based on the specification in X/Open CAE Specification
|
||||
C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
|
232
sbin/rpc.statd/statd.c
Normal file
232
sbin/rpc.statd/statd.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* main() function for status monitor daemon. Some of the code in this */
|
||||
/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */
|
||||
/* The actual program logic is in the file procs.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include "statd.h"
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[] = "$id: $";
|
||||
#endif /* not lint */
|
||||
|
||||
int debug = 0; /* Controls syslog() calls for debug messages */
|
||||
|
||||
static void sm_prog_1();
|
||||
static void handle_sigchld();
|
||||
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
SVCXPRT *transp;
|
||||
struct sigaction sa;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if (strcmp(argv[1], "-d"))
|
||||
{
|
||||
fprintf(stderr, "Usage: rpc.statd [-d]\n");
|
||||
exit(1);
|
||||
}
|
||||
debug = 1;
|
||||
}
|
||||
|
||||
(void)pmap_unset(SM_PROG, SM_VERS);
|
||||
|
||||
transp = svcudp_create(RPC_ANYSOCK);
|
||||
if (transp == NULL)
|
||||
{
|
||||
fprintf(stderr, "cannot create udp service.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP))
|
||||
{
|
||||
fprintf(stderr, "unable to register (SM_PROG, SM_VERS, udp).\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
|
||||
if (transp == NULL)
|
||||
{
|
||||
fprintf(stderr, "cannot create tcp service.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
|
||||
fprintf(stderr, "unable to register (SM_PROG, SM_VERS, tcp).\n");
|
||||
exit(1);
|
||||
}
|
||||
init_file("/var/db/statd.status");
|
||||
|
||||
/* Note that it is NOT sensible to run this program from inetd - the */
|
||||
/* protocol assumes that it will run immediately at boot time. */
|
||||
daemon(0, 0);
|
||||
openlog("rpc.statd", 0, LOG_DAEMON);
|
||||
if (debug) syslog(LOG_INFO, "Starting - debug enabled");
|
||||
else syslog(LOG_INFO, "Starting");
|
||||
|
||||
/* Install signal handler to collect exit status of child processes */
|
||||
sa.sa_handler = handle_sigchld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGCHLD);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
/* Initialisation now complete - start operating */
|
||||
notify_hosts(); /* Forks a process (if necessary) to do the */
|
||||
/* SM_NOTIFY calls, which may be slow. */
|
||||
|
||||
svc_run(); /* Should never return */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* handle_sigchld ---------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Catch SIGCHLD and collect process status
|
||||
Retruns: Nothing.
|
||||
Notes: No special action required, other than to collect the
|
||||
process status and hence allow the child to die:
|
||||
we only use child processes for asynchronous transmission
|
||||
of SM_NOTIFY to other systems, so it is normal for the
|
||||
children to exit when they have done their work.
|
||||
*/
|
||||
|
||||
static void handle_sigchld(int sig, int code, struct sigcontext *scp)
|
||||
{
|
||||
int pid, status;
|
||||
pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
|
||||
if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
|
||||
else if (status == 0)
|
||||
{
|
||||
if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
|
||||
}
|
||||
else syslog(LOG_ERR, "Child %d failed with status %d", pid,
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
|
||||
/* sm_prog1 ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Handle one RPC request
|
||||
Returns: Nothing
|
||||
Notes: Called from RPC libraray on receipt of a request.
|
||||
Code for this function was auto-generated by rpcgen.
|
||||
*/
|
||||
|
||||
static void
|
||||
sm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
|
||||
{
|
||||
union
|
||||
{
|
||||
struct sm_name sm_stat_1_arg;
|
||||
struct mon sm_mon_1_arg;
|
||||
struct mon_id sm_unmon_1_arg;
|
||||
struct my_id sm_unmon_all_1_arg;
|
||||
struct stat_chge sm_notify_1_arg;
|
||||
} argument;
|
||||
char *result;
|
||||
bool_t (*xdr_argument)(), (*xdr_result)();
|
||||
char *(*local)();
|
||||
|
||||
switch (rqstp->rq_proc)
|
||||
{
|
||||
case NULLPROC:
|
||||
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
|
||||
return;
|
||||
|
||||
case SM_STAT:
|
||||
xdr_argument = xdr_sm_name;
|
||||
xdr_result = xdr_sm_stat_res;
|
||||
local = (char *(*)()) sm_stat_1;
|
||||
break;
|
||||
|
||||
case SM_MON:
|
||||
xdr_argument = xdr_mon;
|
||||
xdr_result = xdr_sm_stat_res;
|
||||
local = (char *(*)()) sm_mon_1;
|
||||
break;
|
||||
|
||||
case SM_UNMON:
|
||||
xdr_argument = xdr_mon_id;
|
||||
xdr_result = xdr_sm_stat;
|
||||
local = (char *(*)()) sm_unmon_1;
|
||||
break;
|
||||
|
||||
case SM_UNMON_ALL:
|
||||
xdr_argument = xdr_my_id;
|
||||
xdr_result = xdr_sm_stat;
|
||||
local = (char *(*)()) sm_unmon_all_1;
|
||||
break;
|
||||
|
||||
case SM_SIMU_CRASH:
|
||||
xdr_argument = xdr_void;
|
||||
xdr_result = xdr_void;
|
||||
local = (char *(*)()) sm_simu_crash_1;
|
||||
break;
|
||||
|
||||
case SM_NOTIFY:
|
||||
xdr_argument = xdr_stat_chge;
|
||||
xdr_result = xdr_void;
|
||||
local = (char *(*)()) sm_notify_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
svcerr_noproc(transp);
|
||||
return;
|
||||
}
|
||||
|
||||
bzero((char *)&argument, sizeof(argument));
|
||||
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
|
||||
{
|
||||
svcerr_decode(transp);
|
||||
return;
|
||||
}
|
||||
result = (*local)(&argument, rqstp);
|
||||
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
|
||||
{
|
||||
svcerr_systemerr(transp);
|
||||
}
|
||||
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
|
||||
{
|
||||
syslog(LOG_ERR, "unable to free arguments");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
123
sbin/rpc.statd/statd.h
Normal file
123
sbin/rpc.statd/statd.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <rpcsvc/sm_inter.h>
|
||||
|
||||
/* These pieces are missing from the distributed sm_inter.x, which */
|
||||
/* omits the SM_NOTIFY procedure used between cooperating rpc.statd's */
|
||||
|
||||
#define SM_NOTIFY ((u_long)6)
|
||||
extern void *sm_notify_1();
|
||||
|
||||
struct stat_chge
|
||||
{
|
||||
char *mon_name;
|
||||
int state;
|
||||
};
|
||||
typedef struct stat_chge stat_chge;
|
||||
bool_t xdr_stat_chge();
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/*
|
||||
Data structures for recording monitored hosts
|
||||
|
||||
The information held by the status monitor comprises a list of hosts
|
||||
that we have been asked to monitor, and, associated with each monitored
|
||||
host, one or more clients to be called back if the monitored host crashes.
|
||||
|
||||
The list of monitored hosts must be retained over a crash, so that upon
|
||||
re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
|
||||
cause them to start recovery processing. On the other hand, the client
|
||||
call-backs are not required to be preserved: they are assumed (in the
|
||||
protocol design) to be local processes which will have crashed when
|
||||
we did, and so are discarded on restart.
|
||||
|
||||
We handle this by keeping the list of monitored hosts in a file
|
||||
(/var/statd.state) which is mmap()ed and whose format is described
|
||||
by the typedef FileLayout. The lists of client callbacks are chained
|
||||
off this structure, but are held in normal memory and so will be
|
||||
lost after a re-boot. Hence the actual values of MonList * pointers
|
||||
in the copy on disc have no significance, but their NULL/non-NULL
|
||||
status indicates whether this host is actually being monitored or if it
|
||||
is an empty slot in the file.
|
||||
*/
|
||||
|
||||
typedef struct MonList_s
|
||||
{
|
||||
struct MonList_s *next; /* Next in list or NULL */
|
||||
char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
|
||||
int notifyProg; /* RPC program number to call */
|
||||
int notifyVers; /* version number */
|
||||
int notifyProc; /* procedure number */
|
||||
unsigned char notifyData[16]; /* Opaque data from caller */
|
||||
} MonList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
|
||||
int notifyReqd; /* TRUE if we've crashed and not yet */
|
||||
/* informed the monitored host */
|
||||
MonList *monList; /* List of clients to inform if we */
|
||||
/* hear that the monitored host has */
|
||||
/* crashed, NULL if no longer monitored */
|
||||
} HostInfo;
|
||||
|
||||
|
||||
/* Overall file layout. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ourState; /* State number as defined in statd protocol */
|
||||
int noOfHosts; /* Number of elements in hosts[] */
|
||||
char reserved[248]; /* Reserved for future use */
|
||||
HostInfo hosts[1]; /* vector of monitored hosts */
|
||||
} FileLayout;
|
||||
|
||||
#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Global variables */
|
||||
|
||||
extern FileLayout *status_info; /* The mmap()ed status file */
|
||||
|
||||
extern int debug; /* =1 to enable diagnostics to syslog */
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
|
||||
extern void init_file(char * /*filename*/);
|
||||
extern void notify_hosts(void);
|
||||
extern void sync_file(void);
|
138
sbin/rpc.statd/test.c
Normal file
138
sbin/rpc.statd/test.c
Normal file
@ -0,0 +1,138 @@
|
||||
#include <stdio.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <rpcsvc/sm_inter.h>
|
||||
|
||||
|
||||
/* Default timeout can be changed using clnt_control() */
|
||||
static struct timeval TIMEOUT = { 25, 0 };
|
||||
|
||||
struct sm_stat_res *
|
||||
sm_stat_1(argp, clnt)
|
||||
struct sm_name *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat_res res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat_res *
|
||||
sm_mon_1(argp, clnt)
|
||||
struct mon *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat_res res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat *
|
||||
sm_unmon_1(argp, clnt)
|
||||
struct mon_id *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat *
|
||||
sm_unmon_all_1(argp, clnt)
|
||||
struct my_id *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
sm_simu_crash_1(argp, clnt)
|
||||
void *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static char res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return ((void *)&res);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CLIENT *cli;
|
||||
char dummy;
|
||||
void *out;
|
||||
struct mon mon;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "usage: test <hostname> | crash\n");
|
||||
fprintf(stderr, "Always talks to statd at localhost\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Creating client for localhost\n" );
|
||||
cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
printf("Failed to create client\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mon.mon_id.mon_name = argv[1];
|
||||
mon.mon_id.my_id.my_name = argv[1];
|
||||
mon.mon_id.my_id.my_prog = SM_PROG;
|
||||
mon.mon_id.my_id.my_vers = SM_VERS;
|
||||
mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
|
||||
|
||||
if (strcmp(argv[1], "crash"))
|
||||
{
|
||||
/* Hostname given */
|
||||
struct sm_stat_res *res;
|
||||
if (res = sm_mon_1(&mon, cli))
|
||||
{
|
||||
printf("Success!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fail\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out = sm_simu_crash_1(&dummy, cli))
|
||||
{
|
||||
printf("Success!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
13
usr.sbin/rpc.statd/Makefile
Normal file
13
usr.sbin/rpc.statd/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
# $Id$
|
||||
|
||||
PROG = rpc.statd
|
||||
SRCS = statd.c procs.c file.c
|
||||
MAN8 = rpc.statd.8
|
||||
|
||||
DPADD= ${LIBRPCSVC}
|
||||
LDADD= -lrpcsvc
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
test: test.c
|
||||
cc -o test test.c -lrpcsvc
|
356
usr.sbin/rpc.statd/file.c
Normal file
356
usr.sbin/rpc.statd/file.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h> /* For mmap() */
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "statd.h"
|
||||
|
||||
FileLayout *status_info; /* Pointer to the mmap()ed status file */
|
||||
static int status_fd; /* File descriptor for the open file */
|
||||
static off_t status_file_len; /* Current on-disc length of file */
|
||||
|
||||
/* sync_file --------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Packaged call of msync() to flush changes to mmap()ed file
|
||||
Returns: Nothing. Errors to syslog.
|
||||
*/
|
||||
|
||||
void sync_file(void)
|
||||
{
|
||||
if (msync((void *)status_info, 0, 0) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "msync() failed: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* find_host -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Find the entry in the status file for a given host
|
||||
Returns: Pointer to that entry in the mmap() region, or NULL.
|
||||
Notes: Also creates entries if requested.
|
||||
Failure to create also returns NULL.
|
||||
*/
|
||||
|
||||
HostInfo *find_host(char *hostname, int create)
|
||||
{
|
||||
HostInfo *hp;
|
||||
HostInfo *spare_slot = NULL;
|
||||
HostInfo *result = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
|
||||
{
|
||||
if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
|
||||
{
|
||||
result = hp;
|
||||
break;
|
||||
}
|
||||
if (!spare_slot && !hp->monList && !hp->notifyReqd)
|
||||
spare_slot = hp;
|
||||
}
|
||||
|
||||
/* Return if entry found, or if not asked to create one. */
|
||||
if (result || !create) return (result);
|
||||
|
||||
/* Now create an entry, using the spare slot if one was found or */
|
||||
/* adding to the end of the list otherwise, extending file if reqd */
|
||||
if (!spare_slot)
|
||||
{
|
||||
off_t desired_size;
|
||||
spare_slot = &status_info->hosts[status_info->noOfHosts];
|
||||
desired_size = ((char*)spare_slot - (char*)status_info) + sizeof(HostInfo);
|
||||
if (desired_size > status_file_len)
|
||||
{
|
||||
/* Extend file by writing 1 byte of junk at the desired end pos */
|
||||
lseek(status_fd, desired_size - 1, SEEK_SET);
|
||||
i = write(status_fd, &i, 1);
|
||||
if (i < 1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to extend status file");
|
||||
return (NULL);
|
||||
}
|
||||
status_file_len = desired_size;
|
||||
}
|
||||
status_info->noOfHosts++;
|
||||
}
|
||||
|
||||
/* Initialise the spare slot that has been found/created */
|
||||
/* Note that we do not msync(), since the caller is presumed to be */
|
||||
/* about to modify the entry further */
|
||||
memset(spare_slot, 0, sizeof(HostInfo));
|
||||
strncpy(spare_slot->hostname, hostname, SM_MAXSTRLEN);
|
||||
return (spare_slot);
|
||||
}
|
||||
|
||||
/* init_file -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Open file, create if necessary, initialise it.
|
||||
Returns: Nothing - exits on error
|
||||
Notes: Called before process becomes daemon, hence logs to
|
||||
stderr rather than syslog.
|
||||
Opens the file, then mmap()s it for ease of access.
|
||||
Also performs initial clean-up of the file, zeroing
|
||||
monitor list pointers, setting the notifyReqd flag in
|
||||
all hosts that had a monitor list, and incrementing
|
||||
the state number to the next even value.
|
||||
*/
|
||||
|
||||
void init_file(char *filename)
|
||||
{
|
||||
int new_file = FALSE;
|
||||
char buf[HEADER_LEN];
|
||||
int i;
|
||||
|
||||
/* try to open existing file - if not present, create one */
|
||||
status_fd = open(filename, O_RDWR);
|
||||
if ((status_fd < 0) && (errno == ENOENT))
|
||||
{
|
||||
status_fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
new_file = TRUE;
|
||||
}
|
||||
if (status_fd < 0)
|
||||
{
|
||||
perror("rpc.statd");
|
||||
fprintf(stderr, "Unable to open status file %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* File now open. mmap() it, with a generous size to allow for */
|
||||
/* later growth, where we will extend the file but not re-map it. */
|
||||
status_info = (FileLayout *)
|
||||
mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
|
||||
|
||||
if (status_info == (FileLayout *) -1)
|
||||
{
|
||||
perror("rpc.statd");
|
||||
fprintf(stderr, "Unable to mmap() status file\n");
|
||||
}
|
||||
|
||||
status_file_len = lseek(status_fd, 0L, SEEK_END);
|
||||
|
||||
/* If the file was not newly created, validate the contents, and if */
|
||||
/* defective, re-create from scratch. */
|
||||
if (!new_file)
|
||||
{
|
||||
if ((status_file_len < HEADER_LEN) || (status_file_len
|
||||
< (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
|
||||
{
|
||||
fprintf(stderr, "rpc.statd: status file is corrupt\n");
|
||||
new_file = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialisation of a new, empty file. */
|
||||
if (new_file)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
lseek(status_fd, 0L, SEEK_SET);
|
||||
write(status_fd, buf, HEADER_LEN);
|
||||
status_file_len = HEADER_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Clean-up of existing file - monitored hosts will have a pointer */
|
||||
/* to a list of clients, which refers to memory in the previous */
|
||||
/* incarnation of the program and so are meaningless now. These */
|
||||
/* pointers are zeroed and the fact that the host was previously */
|
||||
/* monitored is recorded by setting the notifyReqd flag, which will */
|
||||
/* in due course cause a SM_NOTIFY to be sent. */
|
||||
/* Note that if we crash twice in quick succession, some hosts may */
|
||||
/* already have notifyReqd set, where we didn't manage to notify */
|
||||
/* them before the second crash occurred. */
|
||||
for (i = 0; i < status_info->noOfHosts; i++)
|
||||
{
|
||||
HostInfo *this_host = &status_info->hosts[i];
|
||||
|
||||
if (this_host->monList)
|
||||
{
|
||||
this_host->notifyReqd = TRUE;
|
||||
this_host->monList = NULL;
|
||||
}
|
||||
}
|
||||
/* Select the next higher even number for the state counter */
|
||||
status_info->ourState = (status_info->ourState + 2) & 0xfffffffe;
|
||||
/*???????******/ status_info->ourState++;
|
||||
}
|
||||
}
|
||||
|
||||
/* xdr_stat_chge ----------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: XDR-encode structure of type stat_chge
|
||||
Returns: TRUE if successful
|
||||
Notes: This function is missing from librpcsvc, because the
|
||||
sm_inter.x distributed by Sun omits the SM_NOTIFY
|
||||
procedure used between co-operating statd's
|
||||
*/
|
||||
|
||||
bool_t xdr_stat_chge(XDR *xdrs, stat_chge *objp)
|
||||
{
|
||||
if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
if (!xdr_int(xdrs, &objp->state))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
|
||||
/* notify_one_host --------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Perform SM_NOTIFY procedure at specified host
|
||||
Returns: TRUE if success, FALSE if failed.
|
||||
*/
|
||||
|
||||
static int notify_one_host(char *hostname)
|
||||
{
|
||||
struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
|
||||
CLIENT *cli;
|
||||
char dummy;
|
||||
stat_chge arg;
|
||||
char our_hostname[SM_MAXSTRLEN+1];
|
||||
|
||||
gethostname(our_hostname, sizeof(our_hostname));
|
||||
our_hostname[SM_MAXSTRLEN] = '\0';
|
||||
arg.mon_name = our_hostname;
|
||||
arg.state = status_info->ourState;
|
||||
|
||||
if (debug) syslog (LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s", hostname, our_hostname);
|
||||
|
||||
cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
|
||||
clnt_spcreateerror(""));
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if (clnt_call(cli, SM_NOTIFY, xdr_stat_chge, &arg, xdr_void, &dummy, timeout)
|
||||
!= RPC_SUCCESS)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact rpc.statd at host %s", hostname);
|
||||
clnt_destroy(cli);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
clnt_destroy(cli);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/* notify_hosts ------------------------------------------------------------ */
|
||||
/*
|
||||
Purpose: Send SM_NOTIFY to all hosts marked as requiring it
|
||||
Returns: Nothing, immediately - forks a process to do the work.
|
||||
Notes: Does nothing if there are no monitored hosts.
|
||||
Called after all the initialisation has been done -
|
||||
logs to syslog.
|
||||
*/
|
||||
|
||||
void notify_hosts(void)
|
||||
{
|
||||
int i;
|
||||
int attempts;
|
||||
int work_to_do = FALSE;
|
||||
HostInfo *hp;
|
||||
pid_t pid;
|
||||
|
||||
/* First check if there is in fact any work to do. */
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->notifyReqd)
|
||||
{
|
||||
work_to_do = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!work_to_do) return; /* No work found */
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (pid) return;
|
||||
|
||||
/* Here in the child process. We continue until all the hosts marked */
|
||||
/* as requiring notification have been duly notified. */
|
||||
/* If one of the initial attempts fails, we sleep for a while and */
|
||||
/* have another go. This is necessary because when we have crashed, */
|
||||
/* (eg. a power outage) it is quite possible that we won't be able to */
|
||||
/* contact all monitored hosts immediately on restart, either because */
|
||||
/* they crashed too and take longer to come up (in which case the */
|
||||
/* notification isn't really required), or more importantly if some */
|
||||
/* router etc. needed to reach the monitored host has not come back */
|
||||
/* up yet. In this case, we will be a bit late in re-establishing */
|
||||
/* locks (after the grace period) but that is the best we can do. */
|
||||
/* We try 10 times at 5 sec intervals, 10 more times at 1 minute */
|
||||
/* intervals, then 24 more times at hourly intervals, finally */
|
||||
/* giving up altogether if the host hasn't come back to life after */
|
||||
/* 24 hours. */
|
||||
|
||||
for (attempts = 0; attempts < 44; attempts++)
|
||||
{
|
||||
work_to_do = FALSE; /* Unless anything fails */
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->notifyReqd)
|
||||
{
|
||||
if (notify_one_host(hp->hostname))
|
||||
{
|
||||
hp->notifyReqd = FALSE;
|
||||
sync_file();
|
||||
}
|
||||
else work_to_do = TRUE;
|
||||
}
|
||||
}
|
||||
if (!work_to_do) break;
|
||||
if (attempts < 10) sleep(5);
|
||||
else if (attempts < 20) sleep(60);
|
||||
else sleep(60*60);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
352
usr.sbin/rpc.statd/procs.c
Normal file
352
usr.sbin/rpc.statd/procs.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
#include <netdb.h> /* for gethostbyname() */
|
||||
|
||||
#include "statd.h"
|
||||
|
||||
/* sm_stat_1 --------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC call to enquire if a host can be monitored
|
||||
Returns: TRUE for any hostname that can be looked up to give
|
||||
an address.
|
||||
*/
|
||||
|
||||
struct sm_stat_res *sm_stat_1(sm_name *arg)
|
||||
{
|
||||
static sm_stat_res res;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
|
||||
|
||||
if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
|
||||
res.res_stat = stat_fail;
|
||||
}
|
||||
|
||||
res.state = status_info->ourState;
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_mon_1 ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to establish a monitor request
|
||||
Returns: Success, unless lack of resources prevents
|
||||
the necessary structures from being set up
|
||||
to record the request, or if the hostname is not
|
||||
valid (as judged by gethostbyname())
|
||||
*/
|
||||
|
||||
struct sm_stat_res *sm_mon_1(mon *arg)
|
||||
{
|
||||
static sm_stat_res res;
|
||||
HostInfo *hp;
|
||||
MonList *lp;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
|
||||
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
|
||||
arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
|
||||
arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
|
||||
arg->mon_id.my_id.my_proc);
|
||||
}
|
||||
|
||||
res.res_stat = stat_fail; /* Assume fail until set otherwise */
|
||||
res.state = status_info->ourState;
|
||||
|
||||
/* Find existing host entry, or create one if not found */
|
||||
/* If find_host() fails, it will have logged the error already. */
|
||||
if (!gethostbyname(arg->mon_id.mon_name))
|
||||
{
|
||||
syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
|
||||
}
|
||||
else if (hp = find_host(arg->mon_id.mon_name, TRUE))
|
||||
{
|
||||
lp = (MonList *)malloc(sizeof(MonList));
|
||||
if (!lp)
|
||||
{
|
||||
syslog(LOG_ERR, "Out of memory");
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
|
||||
lp->notifyProg = arg->mon_id.my_id.my_prog;
|
||||
lp->notifyVers = arg->mon_id.my_id.my_vers;
|
||||
lp->notifyProc = arg->mon_id.my_id.my_proc;
|
||||
memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
|
||||
|
||||
lp->next = hp->monList;
|
||||
hp->monList = lp;
|
||||
sync_file();
|
||||
|
||||
res.res_stat = stat_succ; /* Report success */
|
||||
}
|
||||
}
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* do_unmon ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Remove a monitor request from a host
|
||||
Returns: TRUE if found, FALSE if not found.
|
||||
Notes: Common code from sm_unmon_1 and sm_unmon_all_1
|
||||
In the unlikely event of more than one identical monitor
|
||||
request, all are removed.
|
||||
*/
|
||||
|
||||
static int do_unmon(HostInfo *hp, my_id *idp)
|
||||
{
|
||||
MonList *lp, *next;
|
||||
MonList *last = NULL;
|
||||
int result = FALSE;
|
||||
|
||||
lp = hp->monList;
|
||||
while (lp)
|
||||
{
|
||||
if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
|
||||
&& (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
|
||||
&& (idp->my_vers == lp->notifyVers))
|
||||
{
|
||||
/* found one. Unhook from chain and free. */
|
||||
next = lp->next;
|
||||
if (last) last->next = next;
|
||||
else hp->monList = next;
|
||||
free(lp);
|
||||
lp = next;
|
||||
result = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
last = lp;
|
||||
lp = lp->next;
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* sm_unmon_1 -------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to release a monitor request.
|
||||
Returns: Local machine's status number
|
||||
Notes: The supplied mon_id should match the value passed in an
|
||||
earlier call to sm_mon_1
|
||||
*/
|
||||
|
||||
struct sm_stat *sm_unmon_1(mon_id *arg)
|
||||
{
|
||||
static sm_stat res;
|
||||
HostInfo *hp;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
|
||||
syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
|
||||
arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
|
||||
arg->my_id.my_vers, arg->my_id.my_proc);
|
||||
}
|
||||
|
||||
if (hp = find_host(arg->mon_name, FALSE))
|
||||
{
|
||||
if (do_unmon(hp, &arg->my_id)) sync_file();
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "unmon request from %s, no matching monitor",
|
||||
arg->my_id.my_name);
|
||||
}
|
||||
}
|
||||
else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
|
||||
arg->my_id.my_name, arg->mon_name);
|
||||
|
||||
res.state = status_info->ourState;
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_unmon_all_1 ---------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to release monitor requests.
|
||||
Returns: Local machine's status number
|
||||
Notes: Releases all monitor requests (if any) from the specified
|
||||
host and program number.
|
||||
*/
|
||||
|
||||
struct sm_stat *sm_unmon_all_1(my_id *arg)
|
||||
{
|
||||
static sm_stat res;
|
||||
HostInfo *hp;
|
||||
MonList *lp;
|
||||
int i;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
|
||||
arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
|
||||
}
|
||||
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
|
||||
{
|
||||
do_unmon(hp, arg);
|
||||
}
|
||||
sync_file();
|
||||
|
||||
res.state = status_info->ourState;
|
||||
|
||||
return (&res);
|
||||
}
|
||||
|
||||
/* sm_simu_crash_1 --------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure to simulate a crash
|
||||
Returns: Nothing
|
||||
Notes: Standardised mechanism for debug purposes
|
||||
The specification says that we should drop all of our
|
||||
status information (apart from the list of monitored hosts
|
||||
on disc). However, this would confuse the rpc.lockd
|
||||
which would be unaware that all of its monitor requests
|
||||
had been silently junked. Hence we in fact retain all
|
||||
current requests and simply increment the status counter
|
||||
and inform all hosts on the monitor list.
|
||||
*/
|
||||
|
||||
void *sm_simu_crash_1(void)
|
||||
{
|
||||
static char dummy;
|
||||
int work_to_do;
|
||||
HostInfo *hp;
|
||||
int i;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
|
||||
|
||||
/* Simulate crash by setting notify-required flag on all monitored */
|
||||
/* hosts, and incrementing our status number. notify_hosts() is */
|
||||
/* then called to fork a process to do the notifications. */
|
||||
|
||||
for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
|
||||
{
|
||||
if (hp->monList)
|
||||
{
|
||||
work_to_do = TRUE;
|
||||
hp->notifyReqd = TRUE;
|
||||
}
|
||||
}
|
||||
status_info->ourState += 2; /* always even numbers if not crashed */
|
||||
|
||||
if (work_to_do) notify_hosts();
|
||||
|
||||
return (&dummy);
|
||||
}
|
||||
|
||||
/* sm_notify_1 ------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: RPC procedure notifying local statd of the crash of another
|
||||
Returns: Nothing
|
||||
Notes: There is danger of deadlock, since it is quite likely that
|
||||
the client procedure that we call will in turn call us
|
||||
to remove or adjust the monitor request.
|
||||
We therefore fork() a process to do the notifications.
|
||||
Note that the main HostInfo structure is in a mmap()
|
||||
region and so will be shared with the child, but the
|
||||
monList pointed to by the HostInfo is in normal memory.
|
||||
Hence if we read the monList before forking, we are
|
||||
protected from the parent servicing other requests
|
||||
that modify the list.
|
||||
*/
|
||||
|
||||
void *sm_notify_1(stat_chge *arg)
|
||||
{
|
||||
struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
|
||||
CLIENT *cli;
|
||||
static char dummy;
|
||||
status tx_arg; /* arg sent to callback procedure */
|
||||
MonList *lp;
|
||||
HostInfo *hp;
|
||||
pid_t pid;
|
||||
|
||||
if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
|
||||
arg->mon_name, arg->state);
|
||||
|
||||
hp = find_host(arg->mon_name, FALSE);
|
||||
if (!hp)
|
||||
{
|
||||
/* Never heard of this host - why is it notifying us? */
|
||||
syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
|
||||
return;
|
||||
}
|
||||
lp = hp->monList;
|
||||
if (!lp) return (FALSE); /* We know this host, but have no */
|
||||
/* outstanding requests. */
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (pid) return (&dummy); /* Parent returns */
|
||||
|
||||
while (lp)
|
||||
{
|
||||
tx_arg.mon_name = arg->mon_name;
|
||||
tx_arg.state = arg->state;
|
||||
memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
|
||||
cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
|
||||
clnt_spcreateerror(""));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg, xdr_void, &dummy,
|
||||
timeout) != RPC_SUCCESS)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
|
||||
lp->notifyHost);
|
||||
}
|
||||
clnt_destroy(cli);
|
||||
}
|
||||
lp = lp->next;
|
||||
}
|
||||
|
||||
exit (0); /* Child quits */
|
||||
}
|
||||
|
107
usr.sbin/rpc.statd/rpc.statd.8
Normal file
107
usr.sbin/rpc.statd/rpc.statd.8
Normal file
@ -0,0 +1,107 @@
|
||||
.\" -*- nroff -*-
|
||||
.\"
|
||||
.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. 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.
|
||||
.\" 3. All advertising materials mentioning features or use of this software
|
||||
.\" must display the following acknowledgement:
|
||||
.\" This product includes software developed by the University of
|
||||
.\" California, Berkeley and its contributors.
|
||||
.\" 4. Neither the name of the University 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 AUTHOR 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 AUTHOR 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.
|
||||
.\"
|
||||
.\"
|
||||
.Dd September 19, 1995
|
||||
.Dt RPC.STATD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rpc.statd
|
||||
.Nd host status monitoring daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm /sbin/rpc.statd
|
||||
.Op Fl d
|
||||
.Sh DESCRIPTION
|
||||
.Nm rpc.rstatd
|
||||
is a daemon which co-operates with rpc.statd daemons on other hosts to provide
|
||||
a status monitoring service. The daemon accepts requests from
|
||||
programs running on the local host (typically,
|
||||
.Xr rpc.lockd 8 ,
|
||||
the NFS file locking daemon) to monitor the status of specified
|
||||
hosts. If a monitored host crashes and restarts, the remote daemon will
|
||||
notify the local daemon, which in turn will notify the local program(s)
|
||||
which requested the monitoring service. Conversely, if this host crashes
|
||||
and re-starts, when the
|
||||
.Nm rpc.statd
|
||||
re-starts, it will notify all of the hosts which were being monitored
|
||||
at the time of the crash.
|
||||
.Pp
|
||||
Options and operands available for
|
||||
.Nm rpc.statd :
|
||||
.Bl -tag -width Ds
|
||||
.It Fl d
|
||||
The
|
||||
.Fl d
|
||||
option causes debugging information to be written to syslog, recording
|
||||
all RPC transations to the daemon. These messages are logged with level
|
||||
LOG_DEBUG and facility LOG_DAEMON. Error conditions are logged irrespective
|
||||
of this option, using level LOG_ERR.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Nm rpc.rstatd
|
||||
daemon must NOT be invoked by
|
||||
.Xr inetd 8
|
||||
because the protocol assumes that the daemon will run from system start time.
|
||||
Instead, it should be run from
|
||||
.Xr rc 8
|
||||
after the network has been started.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /usr/include/rpcsvc/sm_inter.x -compact
|
||||
.It Pa /var/db/statd.status
|
||||
non-volatile record of currently monitored hosts.
|
||||
.It Pa /usr/include/rpcsvc/sm_inter.x
|
||||
RPC protocol specification used by local applications to register monitoring requests.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr rpc.lockd 8 ,
|
||||
.Xr rc 8 ,
|
||||
.Xr syslog 3
|
||||
.Sh BUGS
|
||||
There is no means for the daemon to tell when a monitored host has
|
||||
disappeared permanently (eg. catastrophic harware failure), as opposed
|
||||
to transient failure of the host or an intermediate router. At present,
|
||||
it will re-try notification attempts at frequent intervals for 10 minutes,
|
||||
then hourly, and finally gives up after 24 hours.
|
||||
|
||||
The protocol requires that symmetric monitor requests are made to both
|
||||
the local and remote daemon in order to establish a monitored relationship.
|
||||
This is convenient for the NFS locking protocol, but probably reduces the
|
||||
usefulness of the monitoring system for other applications.
|
||||
|
||||
The current implementation uses more than 1Kbyte per monitored host in
|
||||
the status file (and also in VM). This may be inefficient for NFS servers
|
||||
with large numbers of clients.
|
||||
.Sh STANDARDS
|
||||
The implementation is based on the specification in X/Open CAE Specification
|
||||
C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
|
232
usr.sbin/rpc.statd/statd.c
Normal file
232
usr.sbin/rpc.statd/statd.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* main() function for status monitor daemon. Some of the code in this */
|
||||
/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */
|
||||
/* The actual program logic is in the file procs.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include "statd.h"
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[] = "$id: $";
|
||||
#endif /* not lint */
|
||||
|
||||
int debug = 0; /* Controls syslog() calls for debug messages */
|
||||
|
||||
static void sm_prog_1();
|
||||
static void handle_sigchld();
|
||||
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
SVCXPRT *transp;
|
||||
struct sigaction sa;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if (strcmp(argv[1], "-d"))
|
||||
{
|
||||
fprintf(stderr, "Usage: rpc.statd [-d]\n");
|
||||
exit(1);
|
||||
}
|
||||
debug = 1;
|
||||
}
|
||||
|
||||
(void)pmap_unset(SM_PROG, SM_VERS);
|
||||
|
||||
transp = svcudp_create(RPC_ANYSOCK);
|
||||
if (transp == NULL)
|
||||
{
|
||||
fprintf(stderr, "cannot create udp service.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP))
|
||||
{
|
||||
fprintf(stderr, "unable to register (SM_PROG, SM_VERS, udp).\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
|
||||
if (transp == NULL)
|
||||
{
|
||||
fprintf(stderr, "cannot create tcp service.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
|
||||
fprintf(stderr, "unable to register (SM_PROG, SM_VERS, tcp).\n");
|
||||
exit(1);
|
||||
}
|
||||
init_file("/var/db/statd.status");
|
||||
|
||||
/* Note that it is NOT sensible to run this program from inetd - the */
|
||||
/* protocol assumes that it will run immediately at boot time. */
|
||||
daemon(0, 0);
|
||||
openlog("rpc.statd", 0, LOG_DAEMON);
|
||||
if (debug) syslog(LOG_INFO, "Starting - debug enabled");
|
||||
else syslog(LOG_INFO, "Starting");
|
||||
|
||||
/* Install signal handler to collect exit status of child processes */
|
||||
sa.sa_handler = handle_sigchld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGCHLD);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
/* Initialisation now complete - start operating */
|
||||
notify_hosts(); /* Forks a process (if necessary) to do the */
|
||||
/* SM_NOTIFY calls, which may be slow. */
|
||||
|
||||
svc_run(); /* Should never return */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* handle_sigchld ---------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Catch SIGCHLD and collect process status
|
||||
Retruns: Nothing.
|
||||
Notes: No special action required, other than to collect the
|
||||
process status and hence allow the child to die:
|
||||
we only use child processes for asynchronous transmission
|
||||
of SM_NOTIFY to other systems, so it is normal for the
|
||||
children to exit when they have done their work.
|
||||
*/
|
||||
|
||||
static void handle_sigchld(int sig, int code, struct sigcontext *scp)
|
||||
{
|
||||
int pid, status;
|
||||
pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
|
||||
if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
|
||||
else if (status == 0)
|
||||
{
|
||||
if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
|
||||
}
|
||||
else syslog(LOG_ERR, "Child %d failed with status %d", pid,
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
|
||||
/* sm_prog1 ---------------------------------------------------------------- */
|
||||
/*
|
||||
Purpose: Handle one RPC request
|
||||
Returns: Nothing
|
||||
Notes: Called from RPC libraray on receipt of a request.
|
||||
Code for this function was auto-generated by rpcgen.
|
||||
*/
|
||||
|
||||
static void
|
||||
sm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
|
||||
{
|
||||
union
|
||||
{
|
||||
struct sm_name sm_stat_1_arg;
|
||||
struct mon sm_mon_1_arg;
|
||||
struct mon_id sm_unmon_1_arg;
|
||||
struct my_id sm_unmon_all_1_arg;
|
||||
struct stat_chge sm_notify_1_arg;
|
||||
} argument;
|
||||
char *result;
|
||||
bool_t (*xdr_argument)(), (*xdr_result)();
|
||||
char *(*local)();
|
||||
|
||||
switch (rqstp->rq_proc)
|
||||
{
|
||||
case NULLPROC:
|
||||
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
|
||||
return;
|
||||
|
||||
case SM_STAT:
|
||||
xdr_argument = xdr_sm_name;
|
||||
xdr_result = xdr_sm_stat_res;
|
||||
local = (char *(*)()) sm_stat_1;
|
||||
break;
|
||||
|
||||
case SM_MON:
|
||||
xdr_argument = xdr_mon;
|
||||
xdr_result = xdr_sm_stat_res;
|
||||
local = (char *(*)()) sm_mon_1;
|
||||
break;
|
||||
|
||||
case SM_UNMON:
|
||||
xdr_argument = xdr_mon_id;
|
||||
xdr_result = xdr_sm_stat;
|
||||
local = (char *(*)()) sm_unmon_1;
|
||||
break;
|
||||
|
||||
case SM_UNMON_ALL:
|
||||
xdr_argument = xdr_my_id;
|
||||
xdr_result = xdr_sm_stat;
|
||||
local = (char *(*)()) sm_unmon_all_1;
|
||||
break;
|
||||
|
||||
case SM_SIMU_CRASH:
|
||||
xdr_argument = xdr_void;
|
||||
xdr_result = xdr_void;
|
||||
local = (char *(*)()) sm_simu_crash_1;
|
||||
break;
|
||||
|
||||
case SM_NOTIFY:
|
||||
xdr_argument = xdr_stat_chge;
|
||||
xdr_result = xdr_void;
|
||||
local = (char *(*)()) sm_notify_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
svcerr_noproc(transp);
|
||||
return;
|
||||
}
|
||||
|
||||
bzero((char *)&argument, sizeof(argument));
|
||||
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
|
||||
{
|
||||
svcerr_decode(transp);
|
||||
return;
|
||||
}
|
||||
result = (*local)(&argument, rqstp);
|
||||
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
|
||||
{
|
||||
svcerr_systemerr(transp);
|
||||
}
|
||||
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
|
||||
{
|
||||
syslog(LOG_ERR, "unable to free arguments");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
123
usr.sbin/rpc.statd/statd.h
Normal file
123
usr.sbin/rpc.statd/statd.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 1995
|
||||
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed for the FreeBSD project
|
||||
* 4. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ANDREW GORDON 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 AUTHOR 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <rpcsvc/sm_inter.h>
|
||||
|
||||
/* These pieces are missing from the distributed sm_inter.x, which */
|
||||
/* omits the SM_NOTIFY procedure used between cooperating rpc.statd's */
|
||||
|
||||
#define SM_NOTIFY ((u_long)6)
|
||||
extern void *sm_notify_1();
|
||||
|
||||
struct stat_chge
|
||||
{
|
||||
char *mon_name;
|
||||
int state;
|
||||
};
|
||||
typedef struct stat_chge stat_chge;
|
||||
bool_t xdr_stat_chge();
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/*
|
||||
Data structures for recording monitored hosts
|
||||
|
||||
The information held by the status monitor comprises a list of hosts
|
||||
that we have been asked to monitor, and, associated with each monitored
|
||||
host, one or more clients to be called back if the monitored host crashes.
|
||||
|
||||
The list of monitored hosts must be retained over a crash, so that upon
|
||||
re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
|
||||
cause them to start recovery processing. On the other hand, the client
|
||||
call-backs are not required to be preserved: they are assumed (in the
|
||||
protocol design) to be local processes which will have crashed when
|
||||
we did, and so are discarded on restart.
|
||||
|
||||
We handle this by keeping the list of monitored hosts in a file
|
||||
(/var/statd.state) which is mmap()ed and whose format is described
|
||||
by the typedef FileLayout. The lists of client callbacks are chained
|
||||
off this structure, but are held in normal memory and so will be
|
||||
lost after a re-boot. Hence the actual values of MonList * pointers
|
||||
in the copy on disc have no significance, but their NULL/non-NULL
|
||||
status indicates whether this host is actually being monitored or if it
|
||||
is an empty slot in the file.
|
||||
*/
|
||||
|
||||
typedef struct MonList_s
|
||||
{
|
||||
struct MonList_s *next; /* Next in list or NULL */
|
||||
char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
|
||||
int notifyProg; /* RPC program number to call */
|
||||
int notifyVers; /* version number */
|
||||
int notifyProc; /* procedure number */
|
||||
unsigned char notifyData[16]; /* Opaque data from caller */
|
||||
} MonList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
|
||||
int notifyReqd; /* TRUE if we've crashed and not yet */
|
||||
/* informed the monitored host */
|
||||
MonList *monList; /* List of clients to inform if we */
|
||||
/* hear that the monitored host has */
|
||||
/* crashed, NULL if no longer monitored */
|
||||
} HostInfo;
|
||||
|
||||
|
||||
/* Overall file layout. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ourState; /* State number as defined in statd protocol */
|
||||
int noOfHosts; /* Number of elements in hosts[] */
|
||||
char reserved[248]; /* Reserved for future use */
|
||||
HostInfo hosts[1]; /* vector of monitored hosts */
|
||||
} FileLayout;
|
||||
|
||||
#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Global variables */
|
||||
|
||||
extern FileLayout *status_info; /* The mmap()ed status file */
|
||||
|
||||
extern int debug; /* =1 to enable diagnostics to syslog */
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
|
||||
extern void init_file(char * /*filename*/);
|
||||
extern void notify_hosts(void);
|
||||
extern void sync_file(void);
|
138
usr.sbin/rpc.statd/test.c
Normal file
138
usr.sbin/rpc.statd/test.c
Normal file
@ -0,0 +1,138 @@
|
||||
#include <stdio.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <rpcsvc/sm_inter.h>
|
||||
|
||||
|
||||
/* Default timeout can be changed using clnt_control() */
|
||||
static struct timeval TIMEOUT = { 25, 0 };
|
||||
|
||||
struct sm_stat_res *
|
||||
sm_stat_1(argp, clnt)
|
||||
struct sm_name *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat_res res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat_res *
|
||||
sm_mon_1(argp, clnt)
|
||||
struct mon *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat_res res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat *
|
||||
sm_unmon_1(argp, clnt)
|
||||
struct mon_id *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
struct sm_stat *
|
||||
sm_unmon_all_1(argp, clnt)
|
||||
struct my_id *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static struct sm_stat res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&res);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
sm_simu_crash_1(argp, clnt)
|
||||
void *argp;
|
||||
CLIENT *clnt;
|
||||
{
|
||||
static char res;
|
||||
|
||||
bzero((char *)&res, sizeof(res));
|
||||
if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
|
||||
return (NULL);
|
||||
}
|
||||
return ((void *)&res);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CLIENT *cli;
|
||||
char dummy;
|
||||
void *out;
|
||||
struct mon mon;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "usage: test <hostname> | crash\n");
|
||||
fprintf(stderr, "Always talks to statd at localhost\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Creating client for localhost\n" );
|
||||
cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
|
||||
if (!cli)
|
||||
{
|
||||
printf("Failed to create client\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mon.mon_id.mon_name = argv[1];
|
||||
mon.mon_id.my_id.my_name = argv[1];
|
||||
mon.mon_id.my_id.my_prog = SM_PROG;
|
||||
mon.mon_id.my_id.my_vers = SM_VERS;
|
||||
mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
|
||||
|
||||
if (strcmp(argv[1], "crash"))
|
||||
{
|
||||
/* Hostname given */
|
||||
struct sm_stat_res *res;
|
||||
if (res = sm_mon_1(&mon, cli))
|
||||
{
|
||||
printf("Success!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fail\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out = sm_simu_crash_1(&dummy, cli))
|
||||
{
|
||||
printf("Success!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user