Home | History | Annotate | Line # | Download | only in rpc.statd
statd.c revision 1.9
      1  1.9  christos /*	$NetBSD: statd.c,v 1.9 1997/10/21 20:38:11 christos Exp $	*/
      2  1.1    scottr 
      3  1.1    scottr /*
      4  1.9  christos  * Copyright (c) 1997 Christos Zoulas. All rights reserved.
      5  1.1    scottr  * Copyright (c) 1995
      6  1.1    scottr  *	A.R. Gordon (andrew.gordon (at) net-tel.co.uk).  All rights reserved.
      7  1.1    scottr  *
      8  1.1    scottr  * Redistribution and use in source and binary forms, with or without
      9  1.1    scottr  * modification, are permitted provided that the following conditions
     10  1.1    scottr  * are met:
     11  1.1    scottr  * 1. Redistributions of source code must retain the above copyright
     12  1.1    scottr  *    notice, this list of conditions and the following disclaimer.
     13  1.1    scottr  * 2. Redistributions in binary form must reproduce the above copyright
     14  1.1    scottr  *    notice, this list of conditions and the following disclaimer in the
     15  1.1    scottr  *    documentation and/or other materials provided with the distribution.
     16  1.1    scottr  * 3. All advertising materials mentioning features or use of this software
     17  1.1    scottr  *    must display the following acknowledgement:
     18  1.1    scottr  *	This product includes software developed for the FreeBSD project
     19  1.9  christos  *	This product includes software developed by Christos Zoulas.
     20  1.1    scottr  * 4. Neither the name of the author nor the names of any co-contributors
     21  1.1    scottr  *    may be used to endorse or promote products derived from this software
     22  1.1    scottr  *    without specific prior written permission.
     23  1.1    scottr  *
     24  1.1    scottr  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
     25  1.1    scottr  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  1.1    scottr  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  1.1    scottr  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     28  1.1    scottr  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  1.1    scottr  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  1.1    scottr  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  1.1    scottr  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  1.1    scottr  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  1.1    scottr  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  1.1    scottr  * SUCH DAMAGE.
     35  1.1    scottr  *
     36  1.1    scottr  */
     37  1.1    scottr 
     38  1.5     lukem #include <sys/cdefs.h>
     39  1.5     lukem #ifndef lint
     40  1.9  christos __RCSID("$NetBSD: statd.c,v 1.9 1997/10/21 20:38:11 christos Exp $");
     41  1.5     lukem #endif
     42  1.5     lukem 
     43  1.1    scottr 
     44  1.1    scottr /* main() function for status monitor daemon.  Some of the code in this	*/
     45  1.1    scottr /* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x	*/
     46  1.1    scottr /* The actual program logic is in the file procs.c			*/
     47  1.1    scottr 
     48  1.5     lukem #include <err.h>
     49  1.9  christos #include <ctype.h>
     50  1.1    scottr #include <errno.h>
     51  1.1    scottr #include <fcntl.h>
     52  1.1    scottr #include <signal.h>
     53  1.5     lukem #include <stdio.h>
     54  1.6     lukem #include <stdlib.h>
     55  1.1    scottr #include <string.h>
     56  1.1    scottr #include <syslog.h>
     57  1.1    scottr #include <unistd.h>
     58  1.9  christos #include <db.h>
     59  1.1    scottr 
     60  1.1    scottr #include <rpc/rpc.h>
     61  1.1    scottr 
     62  1.1    scottr #include "statd.h"
     63  1.1    scottr 
     64  1.9  christos struct sigaction sa;
     65  1.1    scottr int     	debug = 0;		/* Controls syslog() for debug msgs */
     66  1.1    scottr int     	_rpcsvcdirty = 0;	/* XXX ??? */
     67  1.9  christos static DB	*db;			/* Database file */
     68  1.9  christos 
     69  1.9  christos Header		 status_info;
     70  1.9  christos 
     71  1.9  christos static char undefdata[] = "\0\1\2\3\4\5\6\7";
     72  1.9  christos static DBT undefkey = {
     73  1.9  christos 	undefdata,
     74  1.9  christos 	sizeof(undefdata)
     75  1.9  christos };
     76  1.9  christos extern char *__progname;
     77  1.9  christos 
     78  1.9  christos 
     79  1.9  christos /* statd.c */
     80  1.9  christos static int walk_one __P((int (*fun )__P ((DBT *, DBT *, void *)), DBT *, DBT *, void *));
     81  1.9  christos static int walk_db __P((int (*fun )__P ((DBT *, DBT *, void *)), void *));
     82  1.9  christos static int reset_host __P((DBT *, DBT *, void *));
     83  1.9  christos static int check_work __P((DBT *, DBT *, void *));
     84  1.9  christos static int unmon_host __P((DBT *, DBT *, void *));
     85  1.9  christos static int notify_one __P((DBT *, DBT *, void *));
     86  1.9  christos static void init_file __P((char *));
     87  1.9  christos static int notify_one_host __P((char *));
     88  1.9  christos static void die __P((int)) __attribute__((__noreturn__));
     89  1.9  christos 
     90  1.9  christos int main __P((int, char **));
     91  1.1    scottr 
     92  1.5     lukem int
     93  1.1    scottr main(argc, argv)
     94  1.1    scottr 	int argc;
     95  1.1    scottr 	char **argv;
     96  1.1    scottr {
     97  1.1    scottr 	SVCXPRT *transp;
     98  1.1    scottr 	int ch;
     99  1.1    scottr 
    100  1.1    scottr 	while ((ch = getopt(argc, argv, "d")) != (-1)) {
    101  1.1    scottr 		switch (ch) {
    102  1.1    scottr 		case 'd':
    103  1.1    scottr 			debug = 1;
    104  1.1    scottr 			break;
    105  1.1    scottr 		default:
    106  1.1    scottr 		case '?':
    107  1.9  christos 			(void) fprintf(stderr, "Usage: %s [-d]", __progname);
    108  1.1    scottr 			/* NOTREACHED */
    109  1.1    scottr 		}
    110  1.1    scottr 	}
    111  1.1    scottr 	(void)pmap_unset(SM_PROG, SM_VERS);
    112  1.1    scottr 
    113  1.1    scottr 	transp = svcudp_create(RPC_ANYSOCK);
    114  1.1    scottr 	if (transp == NULL) {
    115  1.1    scottr 		errx(1, "cannot create udp service.");
    116  1.1    scottr 		/* NOTREACHED */
    117  1.1    scottr 	}
    118  1.1    scottr 	if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP)) {
    119  1.1    scottr 		errx(1, "unable to register (SM_PROG, SM_VERS, udp).");
    120  1.1    scottr 		/* NOTREACHED */
    121  1.1    scottr 	}
    122  1.1    scottr 	transp = svctcp_create(RPC_ANYSOCK, 0, 0);
    123  1.1    scottr 	if (transp == NULL) {
    124  1.1    scottr 		errx(1, "cannot create tcp service.");
    125  1.1    scottr 		/* NOTREACHED */
    126  1.1    scottr 	}
    127  1.1    scottr 	if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP)) {
    128  1.1    scottr 		errx(1, "unable to register (SM_PROG, SM_VERS, tcp).");
    129  1.1    scottr 		/* NOTREACHED */
    130  1.1    scottr 	}
    131  1.1    scottr 	init_file("/var/db/statd.status");
    132  1.1    scottr 
    133  1.1    scottr 	/*
    134  1.1    scottr 	 * Note that it is NOT sensible to run this program from inetd - the
    135  1.1    scottr 	 * protocol assumes that it will run immediately at boot time.
    136  1.1    scottr 	 */
    137  1.9  christos 	if (!debug)
    138  1.9  christos 		daemon(0, 0);
    139  1.1    scottr 	openlog("rpc.statd", 0, LOG_DAEMON);
    140  1.1    scottr 	if (debug)
    141  1.1    scottr 		syslog(LOG_INFO, "Starting - debug enabled");
    142  1.1    scottr 	else
    143  1.1    scottr 		syslog(LOG_INFO, "Starting");
    144  1.1    scottr 
    145  1.9  christos 	sa.sa_handler = die;
    146  1.9  christos 	sa.sa_flags = 0;
    147  1.1    scottr 	sigemptyset(&sa.sa_mask);
    148  1.9  christos 	(void)sigaction(SIGTERM, &sa, NULL);
    149  1.9  christos 	(void)sigaction(SIGQUIT, &sa, NULL);
    150  1.9  christos 	(void)sigaction(SIGHUP, &sa, NULL);
    151  1.9  christos 	(void)sigaction(SIGINT, &sa, NULL);
    152  1.9  christos 
    153  1.9  christos 	sa.sa_handler = SIG_IGN;
    154  1.1    scottr 	sa.sa_flags = SA_RESTART;
    155  1.9  christos 	sigemptyset(&sa.sa_mask);
    156  1.9  christos 	sigaddset(&sa.sa_mask, SIGALRM);
    157  1.1    scottr 
    158  1.1    scottr 	/* Initialisation now complete - start operating */
    159  1.1    scottr 
    160  1.9  christos 	/* Notify hosts that need it */
    161  1.9  christos 	notify_handler(0);
    162  1.1    scottr 
    163  1.9  christos 	while (1)
    164  1.9  christos 		svc_run();		/* Should never return */
    165  1.9  christos 	die(0);
    166  1.1    scottr }
    167  1.1    scottr 
    168  1.9  christos /* notify_handler ---------------------------------------------------------- */
    169  1.1    scottr /*
    170  1.9  christos  * Purpose:	Catch SIGALRM and collect process status
    171  1.1    scottr  * Returns:	Nothing.
    172  1.1    scottr  * Notes:	No special action required, other than to collect the
    173  1.1    scottr  *		process status and hence allow the child to die:
    174  1.1    scottr  *		we only use child processes for asynchronous transmission
    175  1.1    scottr  *		of SM_NOTIFY to other systems, so it is normal for the
    176  1.1    scottr  *		children to exit when they have done their work.
    177  1.1    scottr  */
    178  1.9  christos void
    179  1.9  christos notify_handler(sig)
    180  1.2  christos 	int sig;
    181  1.1    scottr {
    182  1.9  christos 	time_t now;
    183  1.9  christos 
    184  1.9  christos 	NO_ALARM;
    185  1.9  christos 	sa.sa_handler = SIG_IGN;
    186  1.9  christos 	(void)sigaction(SIGALRM, &sa, NULL);
    187  1.9  christos 
    188  1.9  christos 	now = time(NULL);
    189  1.9  christos 
    190  1.9  christos 	(void) walk_db(notify_one, &now);
    191  1.9  christos 
    192  1.9  christos 	if (walk_db(check_work, &now) == 0) {
    193  1.9  christos 		/*
    194  1.9  christos 		 * No more work to be done.
    195  1.9  christos 		 */
    196  1.9  christos 		CLR_ALARM;
    197  1.9  christos 		return;
    198  1.9  christos 	}
    199  1.9  christos 	sync_file();
    200  1.9  christos 	ALARM;
    201  1.9  christos 	alarm(5);
    202  1.1    scottr }
    203  1.1    scottr 
    204  1.1    scottr /* sync_file --------------------------------------------------------------- */
    205  1.1    scottr /*
    206  1.1    scottr  * Purpose:	Packaged call of msync() to flush changes to mmap()ed file
    207  1.1    scottr  * Returns:	Nothing.  Errors to syslog.
    208  1.1    scottr  */
    209  1.1    scottr void
    210  1.1    scottr sync_file()
    211  1.1    scottr {
    212  1.9  christos 	DBT data;
    213  1.9  christos 
    214  1.9  christos 	data.data = &status_info;
    215  1.9  christos 	data.size = sizeof(status_info);
    216  1.9  christos 	switch ((*db->put)(db, &undefkey, &data, 0)) {
    217  1.9  christos 	case 0:
    218  1.9  christos 		return;
    219  1.9  christos 	case -1:
    220  1.9  christos 		goto bad;
    221  1.9  christos 	default:
    222  1.9  christos 		abort();
    223  1.9  christos 	}
    224  1.9  christos 	if ((*db->sync)(db, 0) == -1) {
    225  1.9  christos bad:
    226  1.9  christos 		syslog(LOG_ERR, "database corrupted %m");
    227  1.9  christos 		die(1);
    228  1.9  christos 	}
    229  1.9  christos }
    230  1.9  christos 
    231  1.9  christos /* change_host -------------------------------------------------------------- */
    232  1.9  christos /*
    233  1.9  christos  * Purpose:	Update/Create an entry for host
    234  1.9  christos  * Returns:	Nothing
    235  1.9  christos  * Notes:
    236  1.9  christos  *
    237  1.9  christos  */
    238  1.9  christos void
    239  1.9  christos change_host(hostname, hp)
    240  1.9  christos 	char *hostname;
    241  1.9  christos 	HostInfo *hp;
    242  1.9  christos {
    243  1.9  christos 	DBT key, data;
    244  1.9  christos 	char *ptr;
    245  1.9  christos 
    246  1.9  christos 	for (ptr = hostname; *ptr; ptr++)
    247  1.9  christos 		if (isupper((unsigned char) *ptr))
    248  1.9  christos 			*ptr = tolower((unsigned char) *ptr);
    249  1.9  christos 
    250  1.9  christos 	key.data = hostname;
    251  1.9  christos 	key.size = ptr - hostname + 1;
    252  1.9  christos 	data.data = hp;
    253  1.9  christos 	data.size = sizeof(*hp);
    254  1.9  christos 
    255  1.9  christos 	switch ((*db->put)(db, &key, &data, 0)) {
    256  1.9  christos 	case -1:
    257  1.9  christos 		syslog(LOG_ERR, "database corrupted %m");
    258  1.9  christos 		die(1);
    259  1.9  christos 	case 0:
    260  1.9  christos 		return;
    261  1.9  christos 	default:
    262  1.9  christos 		abort();
    263  1.9  christos 	}
    264  1.1    scottr }
    265  1.1    scottr 
    266  1.9  christos 
    267  1.1    scottr /* find_host -------------------------------------------------------------- */
    268  1.1    scottr /*
    269  1.1    scottr  * Purpose:	Find the entry in the status file for a given host
    270  1.9  christos  * Returns:	Copy of entry in hd, or NULL
    271  1.9  christos  * Notes:
    272  1.9  christos  *
    273  1.1    scottr  */
    274  1.1    scottr HostInfo *
    275  1.9  christos find_host(hostname, hp)
    276  1.1    scottr 	char *hostname;
    277  1.9  christos 	HostInfo *hp;
    278  1.9  christos {
    279  1.9  christos 	DBT key, data;
    280  1.9  christos 	char *ptr;
    281  1.9  christos 
    282  1.9  christos 	for (ptr = hostname; *ptr; ptr++)
    283  1.9  christos 		if (isupper((unsigned char) *ptr))
    284  1.9  christos 			*ptr = tolower((unsigned char) *ptr);
    285  1.9  christos 
    286  1.9  christos 	key.data = hostname;
    287  1.9  christos 	key.size = ptr - hostname + 1;
    288  1.9  christos 	switch ((*db->get)(db, &key, &data, 0)) {
    289  1.9  christos 	case 0:
    290  1.9  christos 		if (data.size != sizeof(*hp))
    291  1.9  christos 			goto bad;
    292  1.9  christos 		return memcpy(hp, data.data, sizeof(*hp));
    293  1.9  christos 	case 1:
    294  1.9  christos 		return NULL;
    295  1.9  christos 	case -1:
    296  1.9  christos 		goto bad;
    297  1.9  christos 	default:
    298  1.9  christos 		abort();
    299  1.9  christos 	}
    300  1.9  christos 
    301  1.9  christos bad:
    302  1.9  christos 	syslog(LOG_ERR, "Database corrupted %m");
    303  1.9  christos 	return NULL;
    304  1.9  christos }
    305  1.9  christos 
    306  1.9  christos /* walk_one ------------------------------------------------------------- */
    307  1.9  christos /*
    308  1.9  christos  * Purpose:	Call the given function if the element is valid
    309  1.9  christos  * Returns:	Nothing - exits on error
    310  1.9  christos  * Notes:
    311  1.9  christos  */
    312  1.9  christos static int
    313  1.9  christos walk_one(fun, key, data, ptr)
    314  1.9  christos 	int (*fun) __P((DBT *, DBT *, void *));
    315  1.9  christos 	DBT *key, *data;
    316  1.9  christos 	void *ptr;
    317  1.9  christos {
    318  1.9  christos 	if (key->size == undefkey.size &&
    319  1.9  christos 	    memcmp(key->data, undefkey.data, key->size) == 0)
    320  1.9  christos 		return 0;
    321  1.9  christos 	if (data->size != sizeof(HostInfo)) {
    322  1.9  christos 		syslog(LOG_ERR, "Bad data in database");
    323  1.9  christos 		die(1);
    324  1.9  christos 	}
    325  1.9  christos 
    326  1.9  christos 	return (*fun)(key, data, ptr);
    327  1.9  christos }
    328  1.9  christos 
    329  1.9  christos /* walk_db -------------------------------------------------------------- */
    330  1.9  christos /*
    331  1.9  christos  * Purpose:	Iterate over all elements calling the given function
    332  1.9  christos  * Returns:	-1 if function failed, 0 on success
    333  1.9  christos  * Notes:
    334  1.9  christos  */
    335  1.9  christos static int
    336  1.9  christos walk_db(fun, ptr)
    337  1.9  christos 	int (*fun) __P((DBT *, DBT *, void *));
    338  1.9  christos 	void *ptr;
    339  1.1    scottr {
    340  1.9  christos 	DBT key, data;
    341  1.9  christos 
    342  1.9  christos 	switch ((*db->seq)(db, &key, &data, R_FIRST)) {
    343  1.9  christos 	case -1:
    344  1.9  christos 		goto bad;
    345  1.9  christos 	case 1:
    346  1.9  christos 		/* We should have at least the magic entry at this point */
    347  1.9  christos 		abort();
    348  1.9  christos 	case 0:
    349  1.9  christos 		if (walk_one(fun, &key, &data, ptr) == -1)
    350  1.9  christos 			return -1;
    351  1.9  christos 		break;
    352  1.9  christos 	default:
    353  1.9  christos 		abort();
    354  1.9  christos 	}
    355  1.9  christos 
    356  1.9  christos 
    357  1.9  christos 	for (;;)
    358  1.9  christos 		switch ((*db->seq)(db, &key, &data, R_NEXT)) {
    359  1.9  christos 		case -1:
    360  1.9  christos 			goto bad;
    361  1.9  christos 		case 1:
    362  1.9  christos 			if (walk_one(fun, &key, &data, ptr) == -1)
    363  1.9  christos 				return -1;
    364  1.1    scottr 			break;
    365  1.9  christos 		case 0:
    366  1.9  christos 			return 0;
    367  1.9  christos 		default:
    368  1.9  christos 			abort();
    369  1.1    scottr 		}
    370  1.9  christos bad:
    371  1.9  christos 	syslog(LOG_ERR, "Corrupted database %m");
    372  1.9  christos 	die(1);
    373  1.9  christos }
    374  1.9  christos 
    375  1.9  christos /* reset_host ------------------------------------------------------------ */
    376  1.9  christos /*
    377  1.9  christos  * Purpose:	Clean up existing hosts in file.
    378  1.9  christos  * Returns:	Always success 0.
    379  1.9  christos  * Notes:	Clean-up of existing file - monitored hosts will have a
    380  1.9  christos  *		pointer to a list of clients, which refers to memory in
    381  1.9  christos  *		the previous incarnation of the program and so are
    382  1.9  christos  *		meaningless now.  These pointers are zeroed and the fact
    383  1.9  christos  *		that the host was previously monitored is recorded by
    384  1.9  christos  *		setting the notifyReqd flag, which will in due course
    385  1.9  christos  *		cause a SM_NOTIFY to be sent.
    386  1.9  christos  *
    387  1.9  christos  *		Note that if we crash twice in quick succession, some hosts
    388  1.9  christos  *		may already have notifyReqd set, where we didn't manage to
    389  1.9  christos  *		notify them before the second crash occurred.
    390  1.9  christos  */
    391  1.9  christos static int
    392  1.9  christos reset_host(key, data, ptr)
    393  1.9  christos 	DBT *key, *data;
    394  1.9  christos 	void *ptr;
    395  1.9  christos {
    396  1.9  christos 	HostInfo *hi = data->data;
    397  1.9  christos 
    398  1.9  christos 	if (hi->monList) {
    399  1.9  christos 		hi->notifyReqd = *(time_t *) data;
    400  1.9  christos 		hi->attempts = 0;
    401  1.9  christos 		hi->monList = NULL;
    402  1.1    scottr 	}
    403  1.9  christos 	return 0;
    404  1.9  christos }
    405  1.9  christos 
    406  1.9  christos /* check_work ------------------------------------------------------------ */
    407  1.9  christos /*
    408  1.9  christos  * Purpose:	Check if there is work to be done.
    409  1.9  christos  * Returns:	0 if there is no work to be done -1 if there is.
    410  1.9  christos  * Notes:
    411  1.9  christos  */
    412  1.9  christos static int
    413  1.9  christos check_work(key, data, ptr)
    414  1.9  christos 	DBT *key, *data;
    415  1.9  christos 	void *ptr;
    416  1.9  christos {
    417  1.9  christos 	HostInfo *hi = data->data;
    418  1.1    scottr 
    419  1.9  christos 	return hi->notifyReqd ? -1 : 0;
    420  1.9  christos }
    421  1.1    scottr 
    422  1.9  christos /* unmon_host ------------------------------------------------------------ */
    423  1.9  christos /*
    424  1.9  christos  * Purpose:	Unmonitor a host
    425  1.9  christos  * Returns:	0
    426  1.9  christos  * Notes:
    427  1.9  christos  */
    428  1.9  christos static int
    429  1.9  christos unmon_host(key, data, ptr)
    430  1.9  christos 	DBT *key, *data;
    431  1.9  christos 	void *ptr;
    432  1.9  christos {
    433  1.9  christos 	char *name = key->data;
    434  1.9  christos 	HostInfo *hi = data->data;
    435  1.9  christos 
    436  1.9  christos 	if (do_unmon(name, hi, ptr))
    437  1.9  christos 		change_host(name, hi);
    438  1.9  christos 	return 0;
    439  1.9  christos }
    440  1.9  christos 
    441  1.9  christos /* notify_one ------------------------------------------------------------ */
    442  1.9  christos /*
    443  1.9  christos  * Purpose:	Notify one host.
    444  1.9  christos  * Returns:	0 if success -1 on failure
    445  1.9  christos  * Notes:
    446  1.9  christos  */
    447  1.9  christos static int
    448  1.9  christos notify_one(key, data, ptr)
    449  1.9  christos 	DBT *key, *data;
    450  1.9  christos 	void *ptr;
    451  1.9  christos {
    452  1.9  christos 	time_t now = *(time_t *) ptr;
    453  1.9  christos 	char *name = key->data;
    454  1.9  christos 	HostInfo *hi = data->data;
    455  1.9  christos 
    456  1.9  christos 	if (hi->notifyReqd == 0 || hi->notifyReqd > now)
    457  1.9  christos 		return 0;
    458  1.9  christos 
    459  1.9  christos 	if (notify_one_host(name)) {
    460  1.9  christos give_up:
    461  1.9  christos 		hi->notifyReqd = 0;
    462  1.9  christos 		hi->attempts = 0;
    463  1.9  christos 		switch ((*db->put)(db, key, data, 0)) {
    464  1.9  christos 		case -1:
    465  1.9  christos 			syslog(LOG_ERR, "Error storing %s (%m)", name);
    466  1.9  christos 		case 0:
    467  1.9  christos 			return 0;
    468  1.9  christos 
    469  1.9  christos 		default:
    470  1.9  christos 			abort();
    471  1.1    scottr 		}
    472  1.1    scottr 	}
    473  1.9  christos 	else {
    474  1.9  christos 		/*
    475  1.9  christos 		 * If one of the initial attempts fails, we wait
    476  1.9  christos 		 * for a while and have another go.  This is necessary
    477  1.9  christos 		 * because when we have crashed, (eg. a power outage)
    478  1.9  christos 		 * it is quite possible that we won't be able to
    479  1.9  christos 		 * contact all monitored hosts immediately on restart,
    480  1.9  christos 		 * either because they crashed too and take longer
    481  1.9  christos 		 * to come up (in which case the notification isn't
    482  1.9  christos 		 * really required), or more importantly if some
    483  1.9  christos 		 * router etc. needed to reach the monitored host
    484  1.9  christos 		 * has not come back up yet.  In this case, we will
    485  1.9  christos 		 * be a bit late in re-establishing locks (after the
    486  1.9  christos 		 * grace period) but that is the best we can do.  We
    487  1.9  christos 		 * try 10 times at 5 sec intervals, 10 more times at
    488  1.9  christos 		 * 1 minute intervals, then 24 more times at hourly
    489  1.9  christos 		 * intervals, finally giving up altogether if the
    490  1.9  christos 		 * host hasn't come back to life after 24 hours.
    491  1.9  christos 		 */
    492  1.9  christos 		if (hi->attempts++ >= 44)
    493  1.9  christos 			goto give_up;
    494  1.9  christos 		else if (hi->attempts < 10)
    495  1.9  christos 			hi->notifyReqd += 5;
    496  1.9  christos 		else if (hi->attempts < 20)
    497  1.9  christos 			hi->notifyReqd += 60;
    498  1.9  christos 		else
    499  1.9  christos 			hi->notifyReqd += 60 * 60;
    500  1.9  christos 		return -1;
    501  1.9  christos 	}
    502  1.1    scottr }
    503  1.1    scottr 
    504  1.1    scottr /* init_file -------------------------------------------------------------- */
    505  1.1    scottr /*
    506  1.1    scottr  * Purpose:	Open file, create if necessary, initialise it.
    507  1.1    scottr  * Returns:	Nothing - exits on error
    508  1.1    scottr  * Notes:	Called before process becomes daemon, hence logs to
    509  1.1    scottr  *		stderr rather than syslog.
    510  1.1    scottr  *		Opens the file, then mmap()s it for ease of access.
    511  1.1    scottr  *		Also performs initial clean-up of the file, zeroing
    512  1.1    scottr  *		monitor list pointers, setting the notifyReqd flag in
    513  1.1    scottr  *		all hosts that had a monitor list, and incrementing
    514  1.1    scottr  *		the state number to the next even value.
    515  1.1    scottr  */
    516  1.9  christos static void
    517  1.1    scottr init_file(filename)
    518  1.1    scottr 	char *filename;
    519  1.1    scottr {
    520  1.9  christos 	DBT data;
    521  1.1    scottr 
    522  1.9  christos 	db = dbopen(filename, O_RDWR|O_CREAT|O_NDELAY|O_EXLOCK, 644, DB_HASH,
    523  1.9  christos 	    NULL);
    524  1.9  christos 	if (db == NULL)
    525  1.9  christos 		err(1, "Cannot open `%s'", filename);
    526  1.9  christos 
    527  1.9  christos 	switch ((*db->get)(db, &undefkey, &data, 0)) {
    528  1.9  christos 	case 1:
    529  1.9  christos 		/* New database */
    530  1.9  christos 		(void)memset(&status_info, 0, sizeof(status_info));
    531  1.9  christos 		sync_file();
    532  1.9  christos 		return;
    533  1.1    scottr 
    534  1.9  christos 	case -1:
    535  1.9  christos 		err(1, "error accessing database (%m)");
    536  1.9  christos 	case 0:
    537  1.9  christos 		/* Existing database */
    538  1.9  christos 		if (data.size != sizeof(status_info))
    539  1.9  christos 			errx(1, "database corrupted %d != %d",
    540  1.9  christos 			    data.size, sizeof(status_info));
    541  1.9  christos 		break;
    542  1.9  christos 	default:
    543  1.9  christos 		abort();
    544  1.1    scottr 	}
    545  1.1    scottr 
    546  1.9  christos 	reset_database();
    547  1.9  christos 	return;
    548  1.9  christos }
    549  1.9  christos 
    550  1.9  christos /* reset_database --------------------------------------------------------- */
    551  1.9  christos /*
    552  1.9  christos  * Purpose:	Clears the statd database
    553  1.9  christos  * Returns:	Nothing
    554  1.9  christos  * Notes:	If this is not called on reset, it will leak memory.
    555  1.9  christos  */
    556  1.9  christos void
    557  1.9  christos reset_database()
    558  1.9  christos {
    559  1.9  christos 	time_t now = time(NULL);
    560  1.9  christos 	walk_db(reset_host, &now);
    561  1.1    scottr 
    562  1.9  christos 	/* Select the next higher even number for the state counter */
    563  1.9  christos 	status_info.ourState =
    564  1.9  christos 	    (status_info.ourState + 2) & 0xfffffffe;
    565  1.9  christos 	status_info.ourState++;	/* XXX - ??? */
    566  1.9  christos 	sync_file();
    567  1.1    scottr }
    568  1.1    scottr 
    569  1.9  christos /* unmon_hosts --------------------------------------------------------- */
    570  1.1    scottr /*
    571  1.9  christos  * Purpose:	Unmonitor all the hosts
    572  1.9  christos  * Returns:	Nothing
    573  1.9  christos  * Notes:
    574  1.1    scottr  */
    575  1.9  christos void
    576  1.9  christos unmon_hosts()
    577  1.9  christos {
    578  1.9  christos 	time_t now = time(NULL);
    579  1.9  christos 	walk_db(unmon_host, &now);
    580  1.9  christos 	sync_file();
    581  1.9  christos }
    582  1.9  christos 
    583  1.1    scottr static int
    584  1.1    scottr notify_one_host(hostname)
    585  1.1    scottr 	char *hostname;
    586  1.1    scottr {
    587  1.1    scottr 	struct timeval timeout = {20, 0};	/* 20 secs timeout */
    588  1.1    scottr 	CLIENT *cli;
    589  1.1    scottr 	char dummy;
    590  1.1    scottr 	stat_chge arg;
    591  1.1    scottr 	char our_hostname[SM_MAXSTRLEN + 1];
    592  1.1    scottr 
    593  1.1    scottr 	gethostname(our_hostname, sizeof(our_hostname));
    594  1.1    scottr 	our_hostname[SM_MAXSTRLEN] = '\0';
    595  1.1    scottr 	arg.mon_name = our_hostname;
    596  1.9  christos 	arg.state = status_info.ourState;
    597  1.1    scottr 
    598  1.1    scottr 	if (debug)
    599  1.1    scottr 		syslog(LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s",
    600  1.1    scottr 		    hostname, our_hostname);
    601  1.1    scottr 
    602  1.1    scottr 	cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
    603  1.1    scottr 	if (!cli) {
    604  1.1    scottr 		syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
    605  1.1    scottr 		    clnt_spcreateerror(""));
    606  1.1    scottr 		return (FALSE);
    607  1.1    scottr 	}
    608  1.1    scottr 	if (clnt_call(cli, SM_NOTIFY, xdr_stat_chge, &arg, xdr_void,
    609  1.1    scottr 	    &dummy, timeout) != RPC_SUCCESS) {
    610  1.1    scottr 		syslog(LOG_ERR, "Failed to contact rpc.statd at host %s",
    611  1.1    scottr 		    hostname);
    612  1.1    scottr 		clnt_destroy(cli);
    613  1.1    scottr 		return (FALSE);
    614  1.1    scottr 	}
    615  1.1    scottr 	clnt_destroy(cli);
    616  1.1    scottr 	return (TRUE);
    617  1.1    scottr }
    618  1.1    scottr 
    619  1.9  christos 
    620  1.9  christos static void
    621  1.9  christos die(n)
    622  1.9  christos 	int n;
    623  1.1    scottr {
    624  1.9  christos 	(*db->close)(db);
    625  1.9  christos 	exit(n);
    626  1.1    scottr }
    627