Skip to content

Instantly share code, notes, and snippets.

@madsc13ntist
Created May 3, 2018 19:09
Show Gist options
  • Select an option

  • Save madsc13ntist/d1594fccf681f103b0b5577e37aafb62 to your computer and use it in GitHub Desktop.

Select an option

Save madsc13ntist/d1594fccf681f103b0b5577e37aafb62 to your computer and use it in GitHub Desktop.
A generic script for sending (arbitrary) Syslog messages with Python.
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
### Import Modules
import os
import socket
import hashlib
import logging
import argparse
from argparse import RawTextHelpFormatter
import ConfigParser
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
__version__ = "0.0.1"
__date__ = "05.03.2018"
__author__ = "Joseph Zeranski"
__maintainer__ = "Joseph Zeranski"
__email__ = "madsc13ntist@gmail.com"
__copyright__ = "Copyright (c) 2018 " + __author__
__credits__ = [""]
__description__= "Send a Syslog message to listening syslog servers/connectors."
__build__ = ""
'''''''''' Setup additional formatted usage info '''''''''''
if not __build__:
__build__ = hashlib.md5(open(__file__, 'rb').read()).hexdigest()
EPILOG = ""
if __description__ not in ["", [""], None, False]:
EPILOG += __description__ + "\n"
EPILOG += "version {0!s}, build {1!s}\n".format(__version__, __build__)
EPILOG += "{0!s} <{1!s}>".format(__copyright__, __email__)
if __credits__ not in ["", [""], None, False]:
EPILOG += "\nThanks go out to "
if isinstance(__credits__, str):
EPILOG += __credits__ + "."
elif isinstance(__credits__, list):
if len(__credits__) == 1:
EPILOG += __credits__[0] + "."
else:
EPILOG += "{0!s} and {1!s}.".format(', '.join(__credits__[:-1]), __credits__[-1])
'''''''''' setup usage/command line options '''''''''''
parser = argparse.ArgumentParser(description=EPILOG, formatter_class=RawTextHelpFormatter)
parser.add_argument('-s', '--server',
dest = "syslog_server",
action = "append",
default=[],
required = True,
help='The IP Address of the Syslog Server/Connector.')
parser.add_argument('-p', '--port',
dest = "syslog_port",
action = "store",
default="514",
help='The port number that the Syslog Server/Connector is listening on. [default: 514]')
parser.add_argument('-P', '--protocol',
dest = "syslog_protocol",
action = "store",
default="udp",
help='The protocol to send Syslog messages with. (either tcp or udp). [default: udp]')
parser.add_argument("-c", "--config",
dest="config",
action = "store",
default = os.path.realpath(__file__).replace('.py', '.conf'),
help = "The configuration file to use. [default: {0!s}]".format(os.path.realpath(__file__).replace('.py', '.conf')))
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-m', '--message',
dest = "message",
action = "append",
default=[],
help='The message/text to send to the listening Syslog Server(s)/Connector(s).')
group.add_argument('-f', '--file',
dest = "text_file",
action = "append",
default=[],
help='Use the lines of a text file for the Syslog Message. (one message per line)')
parser.add_argument("-v", "--verbose",
dest = "verbose",
action = "store_true",
default = False,
help = "Print additional information.")
parser.add_argument("-vv", "--debug",
dest="debug",
action = "store_true",
default = False,
help = "Print debug information to the screen.")
parser.add_argument("-l", "--log",
dest="log",
action = "store",
default = False,
help = "The path to a log file of results.")
'''''''''' Parse arguments from user '''''''''''
args, unknown = parser.parse_known_args()
'''''''''' Define Functions '''''''''''
def syslog(data, host, port=514, proto='udp'):
"""
Send data over a TCP connection.
:param data: the syslog message to send.
:param host: The Syslog server's ip address.
:param port: the port number to send the syslog to.
:return: returns True on success and error message on failure.
"""
if isinstance(port, str):
port = int(port)
try:
s = False
if proto.lower() == 'tcp':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP Connection
elif proto.lower() == 'udp':
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP Connection
s.connect((host,port))
s.send("{0!s}".format(data))
s.close()
logging.info("Syslog sent to {0!s} ({1!s}/{2!s}): {3!s}".format(host, port, proto, data))
return True
except Exception as e:
logging.exception("Syslog failed to send to {0!s} ({1!s}/{2!s}): {3!s}".format(host, port, proto, data))
logging.error(str(e))
return str(e)
def lines(filename):
"""
Read in a file and return a generator object. slices are each newline. (without reading the whole file into memory).
:param filename: The text file to read through line by line.
:return: lines of text. (generator object)
"""
line_count = 0
with open(filename) as fp:
logging.debug("opened '{0!s}' for reading.".format(filename))
stream = fp.read().replace('\r\n','\n').replace('\r','\n') #Handling for files with various newlines.
for line in stream.split('\n'):
line_count += 1
yield line
logging.debug("closing '{0!s}' for reading. (read {1:,} lines)".format(filename, line_count))
def main():
"""
MAIN FUNCTION IS MAIN
:return:
"""
'''''''''' Log arguments and parameters passed to script (for Debug) '''''''''''
logging.debug("Running {0!s} version {1!s} (build {2!s})".format(os.path.basename(__file__), __version__, __build__))
for arg in [x for x in dir(args) if not x.startswith('_')]:
logging.debug("args.{0!s} = {1!s}".format(arg, getattr(args, arg)))
'''''''''' Syslog message(s) off to listening syslog Server(s)/Connector(s) '''''''''''
for syslog_server in sorted(args.syslog_server):
if args.text_file not in [[], False, ""]:
for text_file in sorted(args.text_file):
if not os.path.isfile(text_file):
logging.error("File not found: {0!s}".format(os.path.realpath(text_file)))
else:
logging.info("Reading '{0!s}'".format(os.path.realpath(text_file)))
for line in lines(text_file):
syslog(str(line), str(syslog_server), int(args.syslog_port), str(args.syslog_protocol))
else:
for message in sorted(args.message):
syslog(str(message), str(syslog_server), int(args.syslog_port), str(args.syslog_protocol))
# If the script is being executed (not imported).
if __name__ == "__main__":
try:
# '''''''''' check for config file with static parameters ''''''''''''''''''''
if os.path.isfile(args.config):
args.config = os.path.abspath(args.config)
config = ConfigParser.ConfigParser()
config.read(args.config)
required_args = [] # <------- add required args here!
for arg in [x for x in dir(args) if not x.startswith('_')]:
for section in config.sections():
if section in ["settings"]:
if config.has_option(section, arg):
setattr(args, arg, eval(config.get(section, arg)))
if arg in required_args:
if getattr(args, arg) in ['', None]:
logging.critical("Missing required option: {0!s}".format(arg))
exit(1)
# '''''''''' Setup More Robust Logging ''''''''''''''''''''
# Set log level
LOG_LEVEL = logging.WARN
if args.verbose:
LOG_LEVEL = logging.INFO
if args.debug:
LOG_LEVEL = logging.DEBUG
if not args.log:
logging.basicConfig(level=LOG_LEVEL, format='[%(levelname)s] %(funcName)s: %(message)s')
else:
log_file_level = logging.INFO
if LOG_LEVEL == logging.DEBUG:
log_file_level = logging.DEBUG
logging.basicConfig(level=log_file_level,
format='%(asctime)s:%(levelname)s:%(filename)s:%(funcName)s:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
filename=args.log,
filemode='a') # This can be set to "w" to over write everytime the script runs. saves space.
# create logging handler taylored for the user.
console = logging.StreamHandler()
console.setLevel(LOG_LEVEL)
formatter = logging.Formatter('[%(levelname)s] %(funcName)s: %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''' Execute Main '''''''''''''''
main() # Execute the main section
except Exception as e:
logging.exception("Exception: {0!s}".format(e))
exit(1)
@madsc13ntist
Copy link
Author

it prints usage info if you use "-h".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment