--- anaconda.spec.in | 1 + scripts/Makefile.am | 3 + scripts/analog | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 0 deletions(-) create mode 100755 scripts/analog diff --git a/anaconda.spec.in b/anaconda.spec.in index c855935..c6dbf6a 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -200,6 +200,7 @@ update-desktop-database &> /dev/null || : /lib/udev/rules.d/70-anaconda.rules %{_bindir}/mini-wm %{_sbindir}/anaconda +%{_bindir}/analog %ifarch i386 i486 i586 i686 x86_64 %{_sbindir}/gptsync %{_sbindir}/showpart diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 1c59338..a2b53e6 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -25,4 +25,7 @@ dist_scripts_DATA = mk-images.* pyrc.py dist_noinst_SCRIPTS = getlangnames.py upd-bootimage upd-initrd upd-kernel \ makeupdates +analogdir = $(bindir)/ +dist_analog_SCRIPTS = analog + MAINTAINERCLEANFILES = Makefile.in diff --git a/scripts/analog b/scripts/analog new file mode 100755 index 0000000..ef41f4e --- /dev/null +++ b/scripts/analog @@ -0,0 +1,244 @@ +#! /usr/bin/python +# +# analog: Remote logging manager for the Red Hat Installer +# +# Copyright (C) 2010 +# Red Hat, Inc. All rights reserved. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# +# Author: Ales Kozumplik <akozumpl@xxxxxxxxxx> +# + +import errno +import optparse +import os +import os.path +import signal +import sys + +DEFAULT_PORT = 6080 +DEFAULT_ANALOG_DIR = '.analog' +COMMANDS = ['start', 'stop'] +USAGE = "%prog cmd [options]" +COMMANDS = """Commands: + start start a new syslog daemon + stop stop a running syslog daemon +""" + +RSYSLOG_COMMAND = "/sbin/rsyslogd -c 4 -f %(cfg_file_name)s -i %(pid_file_name)s" +RSYSLOG_TEMPLATE =""" +#### MODULES #### +# Provides TCP syslog reception +$ModLoad imtcp.so +$InputTCPServerRun %(port)s + +#### GLOBAL DIRECTIVES #### + +# Use default timestamp format +$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +# File syncing capability is disabled by default. This feature is usually not required, +# not useful and an extreme performance hit +#$ActionFileEnableSync on + + +#### RULES #### + +$template anaconda_tty4, "%%timestamp:8:$:date-rfc3164%%,%%timestamp:1:3:date-subseconds%% %%syslogseverity-text:::uppercase%% %%programname%%:%%msg%%\\n" +$template anaconda_debug, "%%syslogfacility-text%%|%%hostname%%|%%syslogseverity-text%%|%%syslogtag%%|%%msg%%\\n" +$template anaconda_syslog, "%%timestamp:8:$:date-rfc3164%%,%%timestamp:1:3:date-subseconds%% %%syslogseverity-text:::uppercase%% %%programname%%:%%msg%%\\n" + +*.* %(directory)s/out_all_debug.log;anaconda_debug + +kern.*;\\ +daemon.* %(directory)s/syslog;anaconda_syslog + +:programname, contains, "loader" %(directory)s/anaconda.log;anaconda_syslog +:programname, contains, "anaconda" %(directory)s/anaconda.log;anaconda_syslog +:programname, contains, "program" %(directory)s/program.log;anaconda_syslog +:programname, contains, "storage" %(directory)s/storage.log;anaconda_syslog +:hostname, contains, "sysimage" %(directory)s/install.log.syslog;anaconda_syslog + +# discard those that we logged +:programname, contains, "rsyslogd" ~ +:programname, contains, "loader" ~ +:programname, contains, "anaconda" ~ +:programname, contains, "program" ~ +:programname, contains, "storage" ~ +:hostname, contains, "sysimage" ~ +kern.* ~ +daemon.* ~ +# dump the rest +*.* %(directory)s/unknown_source.log;anaconda_debug + +""" + +# option parsing +class OptParserError(Exception): + def __init__(self, msg, parser): + Exception.__init__(self, msg, parser) + self.msg = msg + self.parser = parser + +class AnalogError(Exception): + def __init__(self, msg): + Exception.__init__(self, msg) + self.msg = msg + +def get_opts(): + parser = optparse.OptionParser(usage=USAGE, + add_help_option=False) + parser.add_option ('-h', '--help', action="callback", callback=help_and_exit, + help="Display this help") + parser.add_option ('-p', type="int", dest="port", + default=DEFAULT_PORT, + help="TCP port the rsyslog daemon will listen on") + (options, args) = parser.parse_args() + if len(args) < 1: + raise OptParserError("no command given", parser) + cmd = args[0] + args = args[1:] + if cmd not in COMMANDS: + raise OptParserError("unknown command %s" % cmd, parser) + if cmd in ['start', 'stop'] and len(args) != 1: + raise OptParserError("no job name specified for '%s'" % cmd, parser) + return (cmd, options, args) + +def help_and_exit(option, opt, value, parser): + parser.print_help() + print + print COMMANDS + sys.exit(0) + +# helper functions +def get_pid(analog_dir, jobname): + """Returns PID of the corresponding rsyslogd or None if it can't be found.""" + location = get_pid_location(analog_dir, jobname) + try: + with open(location) as pidfile: + pid = int(pidfile.read()) + except IOError: + return None + return pid + +def get_pid_location(analog_dir, jobname): + return os.path.join(analog_dir, jobname + '.pid') + +# the main Analog class +class Analog(object): + def __init__(self): + self.base_dir = os.getcwd() + self.analog_dir = os.path.join(self.base_dir, DEFAULT_ANALOG_DIR) + + def execute(self, cmd, opts, args): + job = None + if cmd == 'start': + job_name = args[0] + log_dir = os.path.join(self.base_dir, job_name) + job = StartJob(self.analog_dir, job_name, log_dir, opts.port) + elif cmd == 'stop': + job_name = args[0] + job = StopJob(self.analog_dir, job_name) + job.run() + + def find_analog_dir(self): + if os.access(self.analog_dir, os.R_OK | os.W_OK | os.X_OK): + return True + return False + + def init_analog_dir(self): + print 'initializing .analog dir' + os.mkdir(self.dir) + +class Job(object): + def run(self): + raise NotImplementedError() + + +class StartJob(Job): + def __init__(self, analog_dir, job_name, log_dir, port): + self.analog_dir = analog_dir + self.config_file_name = None + self.job_name = job_name + self.log_dir = log_dir + self.port = port + + def generate_rsyslog_config(self): + values = { + "directory" : self.log_dir, + "port" : self.port + } + config = RSYSLOG_TEMPLATE % values + self.config_file_name = os.path.join(self.analog_dir, self.job_name + ".conf") + with open(self.config_file_name, 'w+') as config_file: + config_file.write(config) + + def prepare_log_dir(self): + try: + os.mkdir(self.log_dir) + except OSError as e: + if e.errno == errno.EEXIST: + pass + else: + raise e + + def run(self): + print 'starting rsyslog daemon, listening on port %d\nlogging under %s' % ( + self.port, self.log_dir) + self.generate_rsyslog_config() + self.prepare_log_dir() + self.start_daemon() + + def start_daemon(self): + pid_location = get_pid_location(self.analog_dir, self.job_name) + values = { + "cfg_file_name" : self.config_file_name, + "pid_file_name" : pid_location + } + cmd = RSYSLOG_COMMAND % values + rc = os.system(cmd) + return rc + +class StopJob(Job): + def __init__(self, analog_dir, job_name): + self.analog_dir = analog_dir + self.job_name = job_name + + def run(self): + self.pid = get_pid(self.analog_dir, self.job_name) + if self.pid is None: + raise AnalogError("no running job of name '%s' found" % self.job_name) + print 'stopping daemon %d' % self.pid + self.stop_daemon() + + def stop_daemon(self): + os.kill(self.pid, signal.SIGTERM) + +if __name__ == "__main__": + try: + (cmd, options, args) = get_opts() + except OptParserError as exc: + exc.parser.error(exc.msg) + sys.exit(1) + analog = Analog() + if not analog.find_analog_dir(): + analog.init_analog_dir() + try: + analog.execute(cmd, options, args) + except AnalogError as e: + print(e) + sys.exit(1) + + sys.exit(0) -- 1.6.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list