Example #1
0
def make_copies(absreportname):
    '''copy report to weekday folder
    then, copy all files to WIN_ROOT folder, converting them to DOS style
    line ends.'''

    # sanity check
    if not os.path.isfile(absreportname):
        return

    conf = wdlib.check_conf()

    arch_root = wdlib.abspath(conf.get('OUTPUT', 'arch_root'))
    win_root = wdlib.abspath(conf.get('OUTPUT', 'win_root'))
    win_ext = conf.get('OUTPUT', 'win_ext')
    repname = os.path.basename(absreportname)

    # copy report to daily dir
    destname = os.path.join(dailydir(arch_root), repname)
    shutil.copy2(absreportname, destname)
    # copy DOS converted file to daily dir
    wdlib.convEOL(destname, True, dailydir(win_root), win_ext)

    # copy and convert all files from ARCH_ROOT to WIN_ROOT
    root = conf.get('CURRENT RUN', 'OUTPATH')
    dest = root.replace(arch_root, win_root)
    for f in os.listdir(root):
        af = os.path.join(root, f)
        if os.path.isfile(af):
            wdlib.convEOL(af, toDOS=True, destdir=dest, DOSext=win_ext)
Example #2
0
def get_syslog_path():
    '''Get path to syslog file from config file.'''

    conf = wdlib.check_conf()
    if not conf:
        return None
    return conf.get('PATHS', 'syslog')
Example #3
0
def dailydir(root=''):
    ''' Return the name of the weekday folder.'''

    conf = wdlib.check_conf()
    dailyfmt = conf.get('OUTPUT', 'DAILY_DIR_FMT')  # '1-MON'
    s = wdlib.datestr(wdlib._curserial, dailyfmt)
    return os.path.join(root, s)
Example #4
0
def main(filterOnTime=True):
    ''' Check /etc/passwd for anomalies.
    file is parsed, results written to output file, printed.
    '''

    # prepare limit for time filter
    period = wdlib.set_timefilter('last_passwd', filterOnTime)

    # get filename
    pwdpath = ''
    conf = wdlib.check_conf()
    if conf:
        pwdpath = wdlib.abspath(conf.get('PATHS', 'passwd'))

    # error checking:
    if not pwdpath:
        err('check_passwd: Cannot find passwd file "%s". Aborted.\n' % pwdpath)
        return False

    if wdlib.has_wildcards(pwdpath):
        err('check_passwd: No wildcards allowed ("%s")! Aborted.\n' % pwdpath)
        return False

    print wdlib.version_header(__file__, pwdpath, period)

    if parse_passwd(pwdpath, period):
        if write_results(pwdpath, outputname):
            reportOn_passwd(outputname)
        else:
            wdlib.info('write_results fails!')
    else:
        wdlib.info('parse_passwd: nothing found!')

    # log status at end of processing
    wdlib.log_status('last_passwd', is_OK=True)
Example #5
0
def convert_conf():
    '''Convert wdux.conf file once to make changes easier.'''
    conf = wdlib.check_conf()

    # move/remove option <opt> from <oldsect> to <newsect>
    # remove if <newsect> is empty
    moves = [  # oldsect, option, newsect
        ('CURRENT RUN', 'REPPATH', ''),
        ('CURRENT RUN', 'WINPATH', ''),
        ('CHECKVALUES', 'PASSWD_NUMFIELDS', ''),
        ('PATHS', 'PROG_DIR', ''),
        ('PATHS', 'OUT_DIR', ''),
        # next 3: moved to section CURRENT RUN; remove only, are added on init
        ('PLATFORM', 'OS', ''),
        ('PLATFORM', 'HOST', ''),
        ('OUTPUT', 'TODAY', ''),
    ]
    for (oldsect, opt, newsect) in moves:
        if conf.has_section(oldsect):
            if conf.has_option(oldsect, opt):
                if newsect:
                    x = conf.get(oldsect, opt)
                    conf.add(newsect, opt, x)
                conf.remove_option(oldsect, opt)
            # section empty?
            opts = conf.options(oldsect)
            if not len(opts):
                conf.remove_section(oldsect)

    # new options
    if not conf.has_option('CHECKVALUES', 'SYSLOG_SKIP_UNKNOWN'):
        conf.add('CHECKVALUES', 'SYSLOG_SKIP_UNKNOWN', False)

    # 2007-08-11: put daily folders in subfolder DAILY
    dailyfmt = os.path.normpath(conf.get('OUTPUT', 'DAILY_DIR_FMT'))
    # '1-MON' or 'path/1-MON'
    if not dailyfmt.count(os.path.sep):  # contains no path
        dailyfmt = os.path.join('DAILY', dailyfmt)  # prepend folder path
    conf.add('OUTPUT', 'DAILY_DIR_FMT', dailyfmt)
    conf.save()

    # there might exist output dirs, rename to preserve archived files
    # this is a one-time only procedure
    # and depends on a specific format for the daily dirs
    dailyfmt = conf.get('OUTPUT', 'DAILY_DIR_FMT')  # '1-MON' or 'path/1-MON'
    subfolder = os.path.dirname(dailyfmt)
    arch_root = conf.get('OUTPUT', 'arch_root')
    win_root = conf.get('OUTPUT', 'win_root')
    for root in (arch_root, win_root):
        if os.path.isdir(root):
            for d in os.listdir(root):
                ad = os.path.join(root, d)
                if os.path.isdir(ad) and len(d) == 5:
                    if d[0] in '0123456' and d[1] == '-':
                        nd = os.path.join(root, subfolder, d)
                        try:
                            os.rename(ad, nd)
                        except OSError:
                            pass
Example #6
0
def sendmail(filename):
    ''' Send <filename> per eMail.'''
    conf = wdlib.check_conf()

    host = conf.get('CURRENT RUN', 'host')
    subject = conf.get('MAIL', 'subject') + ' on ' + host
    receiver = conf.get('MAIL', 'touser')
    cmd = 'mailx -s "%(subject)s" %(receiver)s < %(filename)s' % vars()
    wdlib.try_system(cmd)
Example #7
0
def get_reportname():
    conf = wdlib.check_conf()

    root = conf.get('CURRENT RUN', 'OUTPATH')
    nametemplate = conf.get('OUTPUT', 'REPORT_NAME')
    # might contain placeholders for date e.g. %Y,%M,%D,%w,%a
    repname = wdlib.datestr(wdlib._curserial, nametemplate)
    p = os.path.join(root, repname)
    return wdlib.abspath(p)
Example #8
0
def create_folder_structure():
    '''Check necessary paths and files, create if missing.
    Delete old detail files to avoid report generation
    from stale data.
    '''
    conf = wdlib.check_conf()
    root = wdlib.joinpath(conf.get('PATHS', 'WDUX_ROOT'),
                          conf.get('PATHS', 'FILES_DIR'))
    pl = conf.get('PATHS', 'tmp_dir')
    tmppath = wdlib.abspath(os.path.join(root, pl))
    if not wdlib.mkpaths(root, pl):
        err('wdux: cannot access temp path "%s"! Aborted.\n' % tmppath)
        return False

    # create the daily archive folder for output files and report
    arch_root = conf.get('OUTPUT', 'arch_root')
    win_root = conf.get('OUTPUT', 'win_root')
    today = wdlib.datestr(wdlib._curserial, conf.get('OUTPUT', 'arch_fmt'))
    conf.add('CURRENT RUN', 'TODAY', today)

    dailyfmt = conf.get('OUTPUT', 'DAILY_DIR_FMT')  # '1-MON'
    # January 1 2001 was a Monday.
    pl = [datetime.date(2001, 1, i + 1).strftime(dailyfmt) for i in range(7)]
    pl.append(today)

    # delete the daily dir only (with detail files and report)
    # if the root dirs do not (yet) exist, do nothing
    wdlib.clearfolder(os.path.join(arch_root, today))
    wdlib.clearfolder(os.path.join(win_root, today))

    if not wdlib.mkpaths(arch_root, pl):
        err('wdux: cannot create path to ARCHIVE "%s"! Aborted.\n' % arch_root)
        return False
    # same folder tree under windows(-format) root
    if not wdlib.mkpaths(win_root, pl):
        err('wdux: cannot create path to WIN files "%s"! Aborted.\n' %
            win_root)
        return False

    # output path is fetched from the config file
    # instead of being constructed every time when needed
    # so that the hierarchy logic remains local
    p = wdlib.abspath(os.path.join(arch_root, today))
    conf.add('CURRENT RUN', 'OUTPATH', p)
    conf.add('CURRENT RUN', 'TMPPATH', tmppath)
    conf.save()
    return True  # create_folder_structure()
Example #9
0
def get_clusterstatus():
    '''Get cluster status of specified package using pipe.'''

    global cl_pkg, cl_sts

    conf = wdlib.check_conf()

    cl_pkg = conf.get('CLUSTER', 'cluster_package') or 'p1'
    cmd = conf.get('CLUSTER', 'cluster_cmd')
    cmd = cmd + ' ' + cl_pkg
    child_in, child_out, child_err = os.popen3(cmd)
    cl_sts = 'unknown'
    # returns e.g.:
    #    p1             up           running      enabled      hucuit36
    for line in child_out.readlines():
        L = line.strip().lower().split()
        if L:
            if L[0] == cl_pkg.lower():
                cl_sts = L[1]
                break
    child_in.close()
    child_out.close()
    child_err.close()
Example #10
0
def main():
    ''' Prepare the folder structure, call scanning modules
    and create report, make copies, mail report.'''

    # ----------------
    # process command line
    # all options except 'help' handled later
    # 'help' will display usage and exit silently

    # when adding modules, modify list modules[] below!

    usage = __doc__ + '\nusage: %prog [options] file(s), -h for help on options'
    p = optparse.OptionParser(usage=usage)
    add = p.add_option  # =a shortcut
    add('-s',
        '--syslog',
        action='store_true',
        dest='opt_s',
        default=False,
        help='scan syslog.log ONLY')
    add('-c',
        '--consolelog',
        action='store_true',
        dest='opt_c',
        default=False,
        help='scan console.log ONLY')
    add('-l',
        '--last',
        action='store_true',
        dest='opt_l',
        default=False,
        help='scan login records ONLY')
    add('-p',
        '--passwd',
        action='store_true',
        dest='opt_p',
        default=False,
        help='scan /etc/passwd ONLY')
    add('-!',
        '--all',
        action='store_false',
        dest='filterOnTime',
        default=True,
        help='do NOT filter on timestamp')
    add('-v',
        '--verbose',
        action='store_true',
        dest='verbose',
        default=False,
        help='enable some more info on stderr')
    add('-o',
        '--output',
        action='store',
        dest='file',
        default='',
        type='string',
        help='redirect output to FILE')
    add('-f',
        '--conf',
        action='store',
        dest='confpath',
        default='',
        type='string',
        help='use as configuration file')
    add('-D',
        '--DEBUG',
        action='store_true',
        dest='DEBUG',
        default=False,
        help='enable debug msgs')

    # parse command line
    (options, args) = p.parse_args()

    # ----------------
    if options.verbose:
        wdlib.verbose = True
    else:
        # if stderr is redirected, verbose if False
        try:
            wdlib.verbose = sys.stderr.isatty()
        except AttributeError:
            pass  # leave it at False

    debugging = options.DEBUG

    # ----------------
    # process run_module boolean options
    modules = [
        (scan_syslog.main, options.opt_s),
        (scan_consolelog.main, options.opt_c),
        (scan_last.main, options.opt_l),
        (check_passwd.main, options.opt_p),
    ]
    # default action w/o run_options is to run all scans
    mods = [m[0] for m in modules if m[1]]
    # if all switches are False, mods is now empty;
    # that means to run them all:
    if not mods:
        mods = [m[0] for m in modules]

    # ----------------
    # determine the configuration file path (path + filename)
    # MANDATORY! or processing will be aborted soon.
    # if empty, some default will be used, see wdlib.py
    # returns the current valid confpath
    confpath = wdlib.set_confpath(options.confpath)

    # ----------------
    # check config file <confpath> for existence
    if not os.path.isfile(confpath) or not wdlib.check_conf():
        err('\n\n')
        err('%s\n' % sep)
        err('%s\n' % __version__)
        err('    WDUX configuration file is missing!\n')
        err('    specified: %s\n' % (confpath))
        err('    Aborted.\n')
        err('%s\n' % sep)
        err('\n\n')
        sys.exit(1)

    # ----------------
    # convert conf file
    convert_conf()

    # ----------------
    # check all folders and create if missing
    if not create_folder_structure():
        cleanup()

    # check mandatory conf options
    if not has_all_conf_options(confpath):
        sys.exit(1)

    # ----------------
    # get cluster status
    get_clusterstatus()

    # ----------------
    # for the operator
    if wdlib.verbose:
        wdux_header(confpath)
        print_overview(mods, wdlib.verbose)

    # ----------------
    # set or get the reportname
    reportname = options.file or get_reportname()

    # ----------------
    # from here on, all print statements print to file <reportname>
    # sys.stderr still goes to sys.stderr
    if not debugging:
        if not wdlib.redirect_stdout(reportname):
            err('wdux: cannot redirect output to report file "%s"! Aborted.\n'
                % reportname)
            sys.exit(1)

    # ----------------
    # here: for the report
    wdux_header(confpath)
    print_overview(mods, verbose=True)

    # ----------------
    # run the modules
    for module in mods:
        module(options.filterOnTime)

    # ----------------
    wdux_endheader()

    # ----------------
    # undo redirection; otherwise os.system() call fails!
    if not debugging:
        wdlib.redirect_stdout('')

    # ----------------
    # duplicate the files
    # this doesn't make sense if the filename is specified for this run
    if not options.file and not debugging:
        make_copies(reportname)

    # ----------------
    # mail the report
    if wdlib.verbose:
        wdlib.info(
            'hint: mail is not sent if verbose or stderr is not redirected!')
    else:
        sendmail(reportname)

    # ----------------
    # done
    cleanup(failed=False)  # successful exit
Example #11
0
def has_all_conf_options(confpath):
    '''Check all mandatory sections and options in specified conf file.'''

    needed = [  # sect, opt, value example or range, desc
        ('CHECKVALUES', 'SYSLOG_SKIP_UNKNOWN', 'True|False',
         'while scanning syslog.log, whether data from sources for which no parser exists are ignored'
         ),
        ('CLUSTER', 'cluster_package', 'p1', 'name of the cluster package'),
        ('CLUSTER', 'cluster_cmd', '/usr/sbin/cmviewcl -p',
         'command to query the cluster status for package <arg1>'),
        ('MAIL', 'subject', 'WatchDog/UX report',
         'subject line for the mailed report'),
        ('MAIL', 'touser', 'admins', 'recipients of mailed report'),
        ('OUTPUT', 'ARCH_FMT', '%Y-%m-%d',
         'C-style format string for naming daily output folders'),
        ('OUTPUT', 'ARCH_ROOT', 'ARCHIVE',
         'root of UNIX text format output files'),
        ('OUTPUT', 'DAILY_DIR_FMT', '%w-%a',
         'C-style format string for naming weekday output folders'),
        ('OUTPUT', 'REPORT_NAME', 'report', 'name of report file'),
        ('OUTPUT', 'WIN_EXT', 'txt',
         'filename extension for Windows text files, w/o leading dot'),
        ('OUTPUT', 'WIN_ROOT', 'WIN',
         'root of Windows text format output files'),
        ('PATHS', 'CONSOLE_LOG', '<name>', 'full path to console.log file'),
        ('PATHS', 'FILES_DIR', 'files', 'path for root containing TMP_DIR'),
        ('PATHS', 'PASSWD', '/etc/passwd', 'path to password file'),
        ('PATHS', 'SYSLOG', '/var/adm/syslog.log', 'path to syslog file'),
        ('PATHS', 'TMP_DIR', 'TMP',
         'relative path for temp files below FILES_DIR'),
        ('PATHS', 'WDUX_ROOT', '/home/watchdog/wdux',
         'path for root containing FILES_DIR'),
        ('PLATFORM', 'lastb_CMD', '/usr/sbin/lastb -Rx',
         'command for listing failed logins'),
        ('PLATFORM', 'last_CMD', '/usr/sbin/last -Rx',
         'command for listing successful logins'),
    ]

    missing = []
    reason = [
        'option "%(opt)s" in section "[%(sect)s]" missing',
        '"[%(sect)s]/%(opt)s" is empty'
    ]

    # read configuration file
    conf = wdlib.check_conf()
    if not conf:  # critical error
        return False

    for params in needed:
        sect, opt, val, desc = params
        if not conf.has_section(sect):  # section missing
            conf.add_section(sect)
        if conf.has_option(sect, opt):
            v = conf.get(sect, opt)
            if not v:  # value missing
                idx = needed.index(params)
                missing.append((1, idx))
        else:  # option missing
            idx = needed.index(params)
            missing.append((0, idx))

    if missing:
        print 'Mandatory options or values are missing from config file %s!' % confpath
        print 'Empty entries are created now, please fill in the values!'
        print
        for r, idx in missing:
            sect, opt, val, desc = needed[idx]
            print 'error:          ' + (reason[r] % vars())
            print 'description:    %s' % desc
            print 'possible value: e.g. "%s"' % val
            print
            if not opt:
                conf.add_section(sect)
            else:
                conf.add(sect, opt, '')
        conf.save()
        return False
    return True  # has_all_conf_options(confpath)