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