#!/usr/bin/env python # $Id: hbc,v 1.9 2012/03/29 02:08:36 andreas Exp $ # require on Linux # python-filelock # python-daemon vs 1.61 or > # on *bsd # py27-lockfile # py-27-daemon # or run sudo easy_install-2.7 lockfile daemon import sys import time import socket import os import signal import getopt import string import select import errno import traceback import lockfile import daemon import daemon.pidfile import syslog PORT = 50003 INTERVAL = 10 PIDFILE = '/tmp/hbc.pid' DBG = True sock = None up = True ackcount = 0 class NullDevice: def write(self, s): pass def syslogtrace(note): logm = '%s hbc died: \n%s' % (note, traceback.format_exc()) for l in logm.split('\n'): syslog.syslog(' tb: %s' % l) 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: syslog.syslog("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: syslog.syslog("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: syslog.syslog("msg %s sent" % msg) def process(): global up, sock, ackcount if sock == None: sock=getsock(tohost[0]) ackcount=0 lastT=time.time() while up: sleep=(lastT+interval) - time.time() if verbose: syslog.syslog("sleep %s" % sleep) if sleep > 0: try: r=select.select([sock.fileno()],[],[],sleep) except: if up: syslogtrace('select') break if verbose: syslog.syslog("r is %s" % str(r)) if sock.fileno() in r[0]: data, addr = sock.recvfrom(1024) if data == "ACK": ackcount+=1 else: try: os.system(data) except: syslogtrace('System') 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: syslog.syslog("sock.send('%s', (%s, %s))" % (msg, hb_host, hb_port)) socksend(msg, (hb_host, hb_port)) except: pass def cleanup(a, b): global up, sock, ackcount up = False syslog.syslog('exit a=%s b=%s' % (str(a), str(b))) msg="shutdown=1;name=%s;acks=%s" % (iam, ackcount) for hb_host in hb_hosts: if verbose: syslog.syslog("hbc: sock.send('%s', (%s, %s))" % (msg, hb_host, hb_port)) socksend(msg, (hb_host, hb_port)) time.sleep(1) sock.close() msgonly=False helpflag=False verbose=False fdaemon=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': fdaemon=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) # # syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON) if fdaemon: pidfile = daemon.pidfile.TimeoutPIDLockFile(PIDFILE, acquire_timeout=-1) try: opid = pidfile.read_pid() except: opid = None if verbose: print "opid %s" % opid if opid: try: os.kill(opid, 0) is_running = True except: is_running = False if verbose: print "is_running %s" % is_running if is_running: print "process still alive %s" % opid sys.exit(1) print "warning: stale pid file removed" os.unlink(PIDFILE) print "daemoinizing... %s" % os.getpid() context = daemon.DaemonContext( working_directory='/tmp', umask=0o002, pidfile=pidfile, ) context.signal_map = { signal.SIGTERM: cleanup, signal.SIGHUP: 'terminate', # signal.SIGUSR1: reload_program_config, } context.files_preserve = [sock, sock.fileno()] with context: syslog.syslog('starting heartbeat to %s' % ','.join(hb_hosts)) up = True try: process() except: syslogtrace('process') cleanup(0, None)