📄 smsd.py
字号:
#! /usr/bin/env python# -*- coding: utf-8 -*-## Copyright 2006 UNINETT AS## This file is part of Network Administration Visualized (NAV)## NAV is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## NAV is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty 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 NAV; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA## Authors: Stein Magnus Jodal <stein.magnus.jodal@uninett.no>#"""The NAV SMS daemon (smsd)smsd dispatches SMS messages from the database to users' phones with the helpof plugins using Gammu and a cell on the COM port, or free SMS services on theweb.Usage: smsd [-h] [-c] [-d sec] [-t phone no.] -h, --help Show this help text -c, --cancel Cancel (mark as ignored) all unsent messages -d, --delay Set delay (in seconds) between queue checks -t, --test Send a test message to <phone no.>"""__copyright__ = "Copyright 2006 UNINETT AS"__license__ = "GPL"__author__ = "Stein Magnus Jodal (stein.magnus.jodal@uninett.no)"__id__ = "$Id: smsd.py 3963 2007-04-12 08:58:37Z mortenv $"import atexitimport ConfigParser # parts require Python >= 2.3import emailimport getoptimport logging # require Python >= 2.3import logging.handlers # require Python >= 2.3import osimport os.pathimport pwdimport smtplibimport socketimport sysimport timeimport signalimport nav.configimport nav.daemonimport nav.logsimport nav.pathimport nav.smsd.navdbqueuefrom nav.smsd.dispatcher import DispatcherError, PermanentDispatcherError# Dispatchers are imported later according to config### PATHSconfigfile = os.path.join(nav.path.sysconfdir, 'smsd.conf')logfile = os.path.join(nav.path.localstatedir, 'log', 'smsd.log')pidfile = os.path.join(nav.path.localstatedir, 'run', 'smsd.pid')### MAIN FUNCTIONdef main(args): # Get command line arguments optcancel = False optdelay = False opttest = False try: opts, args = getopt.getopt(args, 'hcd:t:', ['help', 'cancel', 'delay=', 'test=']) except getopt.GetoptError, error: print >> sys.stderr, "%s\nTry `%s --help' for more information." % \ (error, sys.argv[0]) sys.exit(1) for opt, val in opts: if opt in ('-h', '--help'): usage() sys.exit(0) if opt in ('-c', '--cancel'): optcancel = True if opt in ('-d', '--delay'): optdelay = val if opt in ('-t', '--test'): opttest = val # Set config defaults defaults = { 'username': 'navcron', 'delay': '30', 'autocancel': '0', 'loglevel': 'INFO', 'mailwarnlevel': 'ERROR', 'mailserver': 'localhost', 'mailaddr': nav.config.readConfig('nav.conf')['ADMIN_MAIL'] } # Read config file config = getconfig(defaults) # Set variables username = config['main']['username'] delay = int(config['main']['delay']) autocancel = config['main']['autocancel'] loglevel = eval('logging.' + config['main']['loglevel']) mailwarnlevel = eval('logging.' + config['main']['mailwarnlevel']) mailserver = config['main']['mailserver'] mailaddr = config['main']['mailaddr'] # Initialize logger global logger logger = logging.getLogger('nav.smsd') logger.setLevel(1) # Let all info through to the root node loginitstderr(loglevel) # Set custom loop delay if optdelay: setdelay(optdelay) # Ignore unsent messages if optcancel: queue = nav.smsd.navdbqueue.NAVDBQueue() ignCount = queue.cancel() logger.info("All %d unsent messages ignored.", ignCount) sys.exit(0) # Let the dispatcherhandler take care of our dispatchers dh = nav.smsd.dispatcher.DispatcherHandler(config) # Send test message (in other words: test the dispatcher) if opttest: msg = [(0, "This is a test message from NAV smsd.", 0)] try: (sms, sent, ignored, smsid) = dh.sendsms(opttest, msg) except DispatcherError, error: logger.critical("Sending failed. Exiting. (%s)", error) sys.exit(1) logger.info("SMS sent. Dispatcher returned reference %d.", smsid) sys.exit(0) # Switch user to navcron (only works if we're root) try: nav.daemon.switchuser(username) except nav.daemon.DaemonError, error: logger.error("%s Run as root or %s to enter daemon mode. " \ + "Try `%s --help' for more information.", error, username, sys.argv[0]) sys.exit(1) # Init daemon loggers if not loginitfile(loglevel, logfile): sys.exit(1) if not loginitsmtp(mailwarnlevel, mailaddr, mailserver): sys.exit(1) # Check if already running try: nav.daemon.justme(pidfile) except nav.daemon.DaemonError, error: logger.error(error) sys.exit(1) # Daemonize try: nav.daemon.daemonize(pidfile) except nav.daemon.DaemonError, error: logger.error(error) sys.exit(1) # Reopen log files on SIGHUP signal.signal(signal.SIGHUP, signalhandler) # Initialize queue # NOTE: If we're initalizing a queue with a DB connection before # daemonizing we've experienced that the daemon dies silently upon trying # to use the DB connection after becoming a daemon queue = nav.smsd.navdbqueue.NAVDBQueue() # Automatically cancel unsent messages older than a given interval if autocancel != '0': ignCount = queue.cancel(autocancel) logger.info("%d unsent messages older than '%s' autocanceled.", ignCount, autocancel) # Loop forever while True: logger.debug("Starting loop.") # Queue: Get users with unsent messages users = queue.getusers('N') logger.info("Found %d user(s) with unsent messages.", len(users)) # Loop over cell numbers for user in users: # Queue: Get unsent messages for a user ordered by severity desc msgs = queue.getusermsgs(user, 'N') logger.info("Found %d unsent message(s) for %s.", len(msgs), user) # Dispatcher: Format and send SMS try: (sms, sent, ignored, smsid) = dh.sendsms(user, msgs) except PermanentDispatcherError, error: logger.critical("Sending failed permanently. Exiting. (%s)", error) sys.exit(1) except DispatcherError, error: logger.critical("Sending failed. (%s)", error) break # End this run except Exception, error: logger.exception("Unknown exception: %s", error) logger.info("SMS sent to %s.", user) for msgid in sent: queue.setsentstatus(msgid, 'Y', smsid) for msgid in ignored: queue.setsentstatus(msgid, 'I', smsid) logger.info("%d messages was sent and %d ignored.", len(sent), len(ignored)) # Sleep a bit before the next run logger.debug("Sleeping for %d seconds.", delay) time.sleep(delay) # Devel only #break # Exit nicely sys.exit(0)def signalhandler(signum, frame): """ Signal handler to close and reopen log file(s) on HUP. """ if signum == signal.SIGHUP: logger.info("SIGHUP received; reopening log files") nav.logs.reopen_log_files() logger.info("Log files reopened")### INIT FUNCTIONSdef getconfig(defaults = None): """ Read whole config from file. Arguments: ``defaults'' are passed on to configparser before reading config. Returns: Returns a dict, with sections names as keys and a dict for each section as values. """ config = ConfigParser.SafeConfigParser(defaults) config.read(configfile) sections = config.sections() configdict = {} for section in sections: configsection = config.items(section) sectiondict = {} for opt, val in configsection: sectiondict[opt] = val configdict[section] = sectiondict return configdictdef loginitfile(loglevel, logfile): """Initalize the logging handler for logfile.""" try: filehandler = logging.FileHandler(logfile, 'a') fileformat = '[%(asctime)s] [%(levelname)s] [pid=%(process)d %(name)s] %(message)s' fileformatter = logging.Formatter(fileformat) filehandler.setFormatter(fileformatter) filehandler.setLevel(loglevel) logger = logging.getLogger() logger.addHandler(filehandler) return True except IOError, error: print >> sys.stderr, \ "Failed creating file loghandler. Daemon mode disabled. (%s)" \ % error return Falsedef loginitstderr(loglevel): """Initalize the logging handler for stderr.""" try: stderrhandler = logging.StreamHandler(sys.stderr) stderrformat = '%(levelname)s %(message)s' stderrformatter = logging.Formatter(stderrformat) stderrhandler.setFormatter(stderrformatter) stderrhandler.setLevel(loglevel) logger = logging.getLogger() logger.addHandler(stderrhandler) return True except IOError, error: print >> sys.stderr, \ "Failed creating stderr loghandler. Daemon mode disabled. (%s)" \ % error return Falsedef loginitsmtp(loglevel, mailaddr, mailserver): """Initalize the logging handler for SMTP.""" try: # localuser will be root if smsd was started as root, since # switchuser() is first called at a later time localuser = pwd.getpwuid(os.getuid())[0] hostname = socket.gethostname() fromaddr = localuser + '@' + hostname mailhandler = logging.handlers.SMTPHandler(mailserver, fromaddr, mailaddr, 'NAV smsd warning from ' + hostname) mailformat = '[%(asctime)s] [%(levelname)s] [pid=%(process)d %(name)s] %(message)s' mailformatter = logging.Formatter(mailformat) mailhandler.setFormatter(mailformatter) mailhandler.setLevel(loglevel) logger = logging.getLogger() logger.addHandler(mailhandler) return True except Exception, error: print >> sys.stderr, \ "Failed creating SMTP loghandler. Daemon mode disabled. (%s)" \ % error return Falsedef usage(): """Print a usage screen to stderr.""" print >> sys.stderr, __doc__def setdelay(sec): """Set delay (in seconds) between queue checks.""" global delay if sec.isdigit(): sec = int(sec) delay = sec logger.info("Setting delay to %d seconds.", sec) return True else: logger.warning("Given delay not a digit. Using default.") return False### BEGINif __name__ == '__main__': main(sys.argv[1:])
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -