/* * hbeat.c -- * * Simple heartbeating. * * Copyright © 2005-2007 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions of the * GNU General Public License v.2. This program is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY expressed or implied, * including the implied warranties of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat * trademarks that are incorporated in the source code or documentation are not * subject to the GNU General Public License and may only be used or replicated * with the express permission of Red Hat, Inc. * * Red Hat Author(s): Nathan Straz * Dean Jansa */ #include #include #include #include #include #include #include #include "btime.h" #include "hbeat.h" struct hbeat_s { char *host; int max_timeout; hbeat_state_t rhost_state; unsigned int last_rhost_btime; time_t start_quiet_time; }; /* * hbeat_init -- * * Init a heartbeat to host, with max_timeout as supplied. * * Returns: * A hbeat handle, or NULL on error; */ hbeat_t hbeat_init(const char *host, int max_timeout) { struct hbeat_s *hbeatp; struct addrinfo hints, *aip; void *addr; char ipstr[INET6_ADDRSTRLEN]; hbeatp = malloc(sizeof *hbeatp); if (!hbeatp) { return NULL; } memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if (getaddrinfo(host, NULL, &hints, &aip) != 0) { return NULL; } if (aip->ai_family == AF_INET) { addr = &(((struct sockaddr_in *)aip->ai_addr)->sin_addr); } else { addr = &(((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr); } inet_ntop(aip->ai_family, addr, ipstr, sizeof ipstr); freeaddrinfo(aip); hbeatp->host = strdup(ipstr); hbeatp->max_timeout = max_timeout; hbeatp->rhost_state = HOST_ALIVE; hbeatp->last_rhost_btime = 0; hbeatp->start_quiet_time = 0; srandom(time(0)); /* for gen_cookie */ return hbeatp; } /* * hbeat_free -- * * Free a hbeat handle. */ void hbeat_free(hbeat_t hbh) { struct hbeat_s *hbeatp = hbh; free(hbeatp->host); free(hbeatp); } /* * hbeat -- * * Attempt to contact host in the hbeat handle, run the hbeat * "state machine" to decide if we the host is still "alive." * * Returns: * 1 if host has been active or responded withing max_timeout secs. * 0 if host is dead, no response for > max_timeout secs. */ unsigned int hbeat(hbeat_t hbh) { struct hbeat_s *hbeatp = hbh; unsigned int hbeat, hbeat2; time_t current_time; int retval; /* User disabled heart beating */ if (!hbeatp->max_timeout) { hbeatp->rhost_state = HOST_HBEAT_DISABLED; return 1; } hbeat = btime(hbeatp->host); /* quickly sanity check that we're getting the same * hbeat every time we ask for it */ if (hbeat && hbeatp->last_rhost_btime && hbeat != hbeatp->last_rhost_btime) { hbeat2 = btime(hbeatp->host); if (hbeat != hbeat2) { fprintf(stderr, "Got conflicting hbeat times (%d and %d), discarding both\n", hbeat, hbeat2); hbeat = btime(hbeatp->host); } } current_time = time(NULL); if (hbeat) { switch(hbeatp->rhost_state) { /* Remote Host is Responding */ case HOST_REBOOT: case HOST_TIMEOUT: retval = 0; break; case HOST_QUIET: hbeatp->rhost_state = HOST_ALIVE; hbeatp->start_quiet_time = 0; case HOST_ALIVE: if (hbeatp->last_rhost_btime == 0) { hbeatp->last_rhost_btime = hbeat; retval = 1; } else if (abs(hbeat - hbeatp->last_rhost_btime) > 5) { fprintf(stderr, "Host rebooted (%d, %d)\n", hbeat, hbeatp->last_rhost_btime); hbeatp->rhost_state = HOST_REBOOT; retval = 0; } else { hbeatp->last_rhost_btime = hbeat; retval = 1; } break; case HOST_HBEAT_DISABLED: retval = 1; break; }} else { switch (hbeatp->rhost_state) { /* Remote Host is NOT Responding */ case HOST_ALIVE: hbeatp->rhost_state = HOST_QUIET; hbeatp->start_quiet_time = time(NULL); retval = 1; break; case HOST_QUIET: if (current_time - hbeatp->start_quiet_time > hbeatp->max_timeout) { hbeatp->rhost_state = HOST_TIMEOUT; retval = 0; } else { retval = 1; } break; case HOST_TIMEOUT: case HOST_REBOOT: retval = 0; break; case HOST_HBEAT_DISABLED: retval = 1; break; }} return retval; } hbeat_state_t hbeat_getstate(hbeat_t hbh) { return ((struct hbeat_s *)hbh)->rhost_state; } void hbeat_setstate(hbeat_t hbh, hbeat_state_t state) { ((struct hbeat_s *)hbh)->rhost_state = state; return; } int hbeat_getmaxtimeout(hbeat_t hbh) { return ((struct hbeat_s *)hbh)->max_timeout; }