#!/usr/bin/env python # $Id: hbc,v 1.9 2012/03/29 02:08:36 andreas Exp $ # requre python-filelock on Linux # require py27-lockfile on *bsd # or run sudo easy_install-2.7 lockfile import sys import time import socket import os import signal import getopt import string import select import errno import traceback from lockfile import FileLock PORT=50003 INTERVAL=10 DBG = True sock=None class NullDevice: def write(self, s): pass def handler(signum, frame): global up if up == 0: return sys.exit(0) def getsock(host): try: r=socket.getaddrinfo(host, 50001, 0, 0, socket.SOL_UDP) except socket.gaierror: return None if r[0][0] == 28: af_type=socket.AF_INET6 elif r[0][0] == 2: af_type=socket.AF_INET else: return None if verbose: print "socktype: %s" % af_type sock=socket.socket(af_type, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, \ sock.getsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR) | 1) if verbose: print "get socket %s" % sock return sock def socksend(msg, tohost): global sock if sock == None: sock=getsock(tohost[0]) sock.sendto(msg, tohost) if verbose: print "msg %s sent" % msg def process(): ackcount=0 lastT=time.time() while up: sleep=(lastT+interval) - time.time() if verbose: print "sleep %s" % sleep if sleep > 0: try: r=select.select([sock.fileno()],[],[],sleep) # time.sleep(interval) except: break if verbose: print r if sock.fileno() in r[0]: data, addr = sock.recvfrom(1024) if data == "ACK": ackcount+=1 else: try: os.system(data) except: pass continue lastT=time.time() for hb_host in hb_hosts: try: msg="interval=%s;name=%s;time=%s;acks=%s" % (interval, iam, time.time(), ackcount) if verbose: print "sock.send('%s', (%s, %s))" % (msg, hb_host, hb_port) socksend(msg, (hb_host, hb_port)) except: pass msgonly=False helpflag=False verbose=False daemon=False optlist=[] args=[] msgboot=[] home=os.environ['HOME'] configfile="%s/.hbrc" % home try: optlist, args = getopt.getopt(sys.argv[1:], 'bc:dhm:v') except: helpflag=True for o,a in optlist: if o == '-b': msgboot.append("boot=1") elif o == '-c': configfile=a elif o == '-d': daemon=True elif o == '-h': helpflag=True elif o == '-m': msgboot.append("service=%s" % "service") a.replace(';',':') msgboot.append("msg=%s" % a) msgonly=True elif o == '-v': verbose=True if helpflag: print "hbc HeartBeatClient" print "usage: hbc [-bdhv] [-c configfile] [-m msg][host1 [..]]" print print " -b indicate machine boot" print " -c configfile" print " -d daemonize" print " -h this help" print " -m send a message" print " -v verbose" print print """ config file can contain hb_hosts=('host1', 'host2', ..._ hb_port=50003 interval=20 logfile=... logfmt={|test|msg} grace=SECONDS reportstrict={True|False} """ sys.exit(1) # # set defaults hb_port=PORT interval=INTERVAL hb_hosts=[] iam=socket.gethostname() try: f=open(configfile,"r") if verbose: print "notice: using config file %s" % configfile except: if verbose: print "warning: running without config file: %s" % configfile f=None if f: while 1: l=f.readline() if len(l) == 0: break r=l[:-1].split('=') if r[0] == 'hb_hosts': hb_hosts=eval(r[1]) if verbose: print "notice: cfg hb_hosts: %s" % hb_hosts elif r[0] == 'interval': interval=eval(r[1]) elif r[0] == 'hb_port': hb_port=eval(r[1]) elif r[0] == 'name': iam=eval(r[1]) if verbose: print "name set to %s" % iam f.close() if len(args) != 0: hb_hosts=args if len(hb_hosts) == 0: print "no hb server specified" sys.exit(1) if verbose: print "notice: hb_hosts: %s" % str(hb_hosts) print "notice: hb_port: %s" % hb_port print "notice: interval: %s" % interval print "notice: iam: %s" % iam if not msgonly: msgboot.append("interval=%s" % interval) if len(msgboot) > 0: msgboot.append("name=%s" % iam) msgboot.append("time=%s" % time.time()) msgboot.append("acks=0") msg=";".join(msgboot) while 1: fail=0 for hb_host in hb_hosts: try: if verbose: print "sock.send('%s', (%s, %s))" % (msg, hb_host, hb_port) socksend(msg, (hb_host, hb_port)) except: fail=1 if fail: time.sleep(10) else: break if verbose: print "msgboot done msgonly=%s" % msgonly if msgonly: sys.exit(0) # # if daemon: pid=os.fork() if pid > 0: if verbose: print "daemoinizing... pid=%d" % pid sys.exit(0) if not DBG: os.close(0) os.close(1) os.close(2) sys.stdin.close() sys.stdout = NullDevice() sys.stderr = NullDevice() os.chdir("/") os.setsid() os.umask(0) lock = FileLock('/tmp/hbc.pid') lock.acquire() up=1 signal.signal(signal.SIGTERM, handler) signal.signal(signal.SIGHUP, handler) try: process() except: data='hbc died:\n'+traceback.format_exc() open("/tmp/hbc.log","a").write(data) sys.exit(1) up=0 msg="shutdown=1;name=%s;acks=%s" % (iam, ackcount) for hb_host in hb_hosts: if verbose: print "hbc: sock.send('%s', (%s, %s))" % (msg, hb_host, hb_port) socksend(msg, (hb_host, hb_port)) time.sleep(1) sock.close() lock.release()