cd1e632b4bfa3a748b43dcefebca17f19767eb17
   1#! /usr/bin/python
   2
   3import argparse, logging, os, shutil, re, subprocess, sys, requests, glob, socket, sensors, datetime, time, operator, premailer
   4from sys import stdin
   5from collections import namedtuple, defaultdict
   6
   7diskstat = namedtuple('diskstat', ['cap', 'alloc', 'free', 'ratio'])
   8drivetemp = namedtuple('drivetemp', ['name', 'temp', 'units'])
   9
  10
  11AUTHPATH = "/var/log/auth.log"
  12CRONPATH = "/var/log/cron.log"
  13SYSPATH = "/var/log/syslog"
  14SMBDDIR = "/var/log/samba"
  15ZFSPATH = "/var/log/zpool.log"
  16ALLOCPATH = "/tmp/alloc"
  17POSTFIXPATH = "/var/log/mail.log"
  18HTTPDSTATUS = "http://localhost/server-status"
  19HTTPDDIR = "/var/log/apache2"
  20HOSTNAMEPATH = "/etc/hostname"
  21DUPATHS = ["/home/andrew", "/mnt/andrew"]
  22HDDTEMPS = ["/dev/sda", "/dev/sdc", "/dev/sdd", "/dev/sde"]
  23HDDTEMPPORT = 7634
  24SUMMARYPATH = "/mnt/andrew/temp/logparse-test.html"
  25OUTPUTPATH = "/mnt/andrew/temp/logparse-test2.html"
  26MAILPATH = "/mnt/andrew/temp/log-parse-test-3.html"
  27HEADERPATH = "header.html"
  28STYLEPATH = "main.css"
  29MAILOUT = ""
  30HTMLOUT = ""
  31TXTOUT = ""
  32TITLE = "logparse"
  33MAXLIST = 10
  34CMDNO = 3
  35MAILSUBJECT = "logparse from $hostname$"
  36VERSION = "v0.1"
  37# DEG = u'\N{DEGREE SIGN}'.encode('utf-8')
  38DEG = 'C'
  39
  40# Set up logging
  41logging.basicConfig(level=logging.DEBUG)
  42logger = logging.getLogger('logparse')
  43
  44# Get arguments
  45parser = argparse.ArgumentParser(description='grab logs of some common services and send them by email')
  46parser.add_argument('-t','--to', help='mail recipient (\"to\" address)',required=False)
  47to = parser.parse_args().to
  48
  49def __main__():
  50    logger.info("Beginning log analysis at " + str(timenow))
  51    if (to == None):
  52        logger.info("no recipient address provided, outputting to stdout")
  53    else:
  54        logger.info("email will be sent to " + to)
  55
  56    global tempfile
  57    tempfile = open(SUMMARYPATH, 'w+')
  58    tempfile.write(header(HEADERPATH))
  59    opentag('div', 1, 'main')
  60    sshd()
  61    sudo()
  62    cron()
  63    nameget()
  64    httpd()
  65    smbd()
  66    postfix()
  67    zfs()
  68    temp()
  69    du()
  70    for tag in ['div', 'body', 'html']:
  71        closetag(tag, 1)
  72    tempfile.close()
  73    mailprep(SUMMARYPATH, MAILPATH)
  74    if (to != None):
  75        logger.debug("sending email")
  76        ms = subject(MAILSUBJECT)
  77        cmd = "cat " + MAILPATH + " | mail --debug-level=10 -a 'Content-type: text/html'  -s '" + ms + "' " + to
  78        logger.debug(cmd)
  79        subprocess.call(cmd, shell=True)
  80        logger.info("sent email")
  81
  82
  83def writetitle(title):
  84    if (title == '' or '\n' in title):
  85        logger.error("invalid title")
  86        return
  87        logger.debug("writing title for " + title)
  88    tag('h2', 0, title)
  89
  90def writedata(subtitle, data = None):   # write title and data to tempfile
  91    if (subtitle == ""):
  92        loggger.warning("no subtitle provided.. skipping section")
  93        return
  94
  95    tag('p', 0, subtitle)
  96    if (data == None):
  97        logger.debug("no data provided.. just printing subtitle")
  98    else:
  99        logger.debug("received data " + str(data))
 100        opentag('ul', 1)
 101        for datum in data:
 102            logger.debug("printing datum " + datum)
 103            tag('li', 0, datum)
 104        closetag('ul', 1)
 105
 106def opentag(tag, block = 0, id = None, cl = None):   # write html opening tag
 107    if (block == 1):
 108        tempfile.write('\n')
 109    tempfile.write('<' + tag)
 110    if (id != None):
 111        tempfile.write(" id='" + id + "'")
 112    if (cl != None):
 113        tempfile.write(" class='" + cl + "'")
 114    tempfile.write('>')
 115    if (block == 1):
 116        tempfile.write('\n')
 117
 118def closetag(tag, block = 0):  # write html closing tag
 119    if (block == 0):
 120        tempfile.write("</" + tag + ">")
 121    else:
 122        tempfile.write("\n</" + tag + ">\n")
 123
 124def tag(tag, block = 0, content = ""):  # write html opening tag, content, and html closing tag
 125    opentag(tag, block)
 126    tempfile.write(content)
 127    closetag(tag, block)
 128
 129def header(template):   # return a parsed html header from file
 130    headercontent = open(template, 'r').read()
 131    headercontent = varpattern.sub(lambda m: varfilter[re.escape(m.group(0))], headercontent)
 132    return headercontent
 133
 134def subject(template):
 135    r = varpattern.sub(lambda m: varfilter[re.escape(m.group(0))], template)
 136    logger.debug("returning subject line " + r)
 137    return r
 138
 139def hostname(): # get the hostname
 140    hnfile = open(HOSTNAMEPATH, 'r')
 141    hn = re.search('^(.*)\n*', hnfile.read()).group(1)
 142    return hn
 143
 144def resolve(ip):        # try to resolve an ip to hostname
 145    logger.debug("trying to resolve ip " + ip)
 146    try:
 147        socket.inet_aton(ip)  # succeeds if text contains ip
 148        hn = socket.gethostbyaddr(ip)[0].split(".")[0] # resolve ip to hostname
 149        logger.debug("found hostname " + hn)
 150        return(hn)
 151    except:
 152        logger.debug("failed to resolve hostname for " + ip)
 153        return(ip)  # return ip if no hostname exists
 154
 155def plural(noun, quantity): # return "1 noun" or "n nouns"
 156    if (quantity == 1):
 157        return(str(quantity) + " " + noun)
 158    else:
 159        return(str(quantity) + " " + noun + "s")
 160
 161def parsesize(num, suffix='B'):     # return human-readable size from number of bytes
 162    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
 163        if abs(num) < 1024.0:
 164            return "%3.1f %s%s" % (num, unit, suffix)
 165        num /= 1024.0
 166    return "%.1f%s%s" % (num, 'Yi', suffix)
 167
 168def readlog(path = None, mode = 'r'):   # read file, substituting known paths
 169    if (path == None):
 170        logger.error("no path provided")
 171        return
 172    else:
 173        path = pathpattern.sub(lambda m: pathfilter[re.escape(m.group(0))], path)
 174        return open(path, mode).read()
 175
 176def writelog(path = None, content = "", mode = 'w'):   # read file, substituting known paths
 177    if (path == None or content == None):
 178        logger.error("invalid usage of writelog")
 179        return
 180    else:
 181        path = pathpattern.sub(lambda m: pathfilter[re.escape(m.group(0))], path)
 182        file = open(path, mode)
 183        file.write(content)
 184        file.close()
 185
 186def getusage(path):     # Get disk usage statistics
 187    disk = os.statvfs(path)
 188    cap = float(disk.f_bsize*disk.f_blocks)                     # disk capacity
 189    alloc = float(disk.f_bsize*(disk.f_blocks-disk.f_bfree))    # size of path
 190    free = float(disk.f_bsize*disk.f_bfree)                     # free space on disk (blocks, not usable space)
 191    ratio = alloc / cap * 100                                   # percentage used
 192    return diskstat(cap, alloc, free, ratio)
 193
 194def orderbyfreq(l):     # order a list by the frequency of its elements and remove duplicates
 195    temp_l = l[:]
 196    l = list(set(l))
 197    l.sort(key=lambda x:temp_l.count(x))
 198    return l
 199
 200def addtag(l, tag):  # add prefix and suffix tags to each item in a list
 201    l2 = ['<' + tag + '>' + i + '</' + tag + '>' for i in l]
 202    return l2
 203
 204def truncl(input, limit):      # truncate list
 205    if (len(input) > limit):
 206        more = str(len(input) - limit)
 207        output = input[-limit:]
 208        output.append("+ " + more + " more")
 209        return(output)
 210    else:
 211        return(input)
 212
 213def mailprep(inputpath, outputpath, *stylesheet):
 214    logger.debug("converting stylesheet to inline tags")
 215    old = readlog(inputpath)
 216    pm = premailer.Premailer(old, external_styles=STYLEPATH)
 217    MAILOUT = pm.transform()
 218    logger.info("converted stylesheet to inline tags")
 219    file = open(outputpath, 'w')
 220    file.write(MAILOUT)
 221    file.close()
 222    logger.info("written to temporary mail file")
 223
 224
 225
 226#
 227#
 228#
 229
 230def sshd():
 231    logger.debug("starting sshd section")
 232    opentag('div', 1, 'sshd', 'section')
 233    matches = re.findall('.*sshd.*Accepted publickey for .* from .*', readlog('auth'))    # get all logins
 234    users = []  # list of users with format [username, number of logins] for each item
 235    data = []
 236    num = sum(1 for x in matches)     # total number of logins
 237    for match in matches:
 238        entry = re.search('^.*publickey\sfor\s(\w*)\sfrom\s(\S*)', match)  # [('user', 'ip')]
 239
 240        user = entry.group(1)
 241        ip = entry.group(2)
 242
 243        userhost = user + '@' + resolve(ip)
 244        exists = [i for i, item in enumerate(users) if re.search(userhost, item[0])]
 245        if (exists == []):
 246            users.append([userhost, 1])
 247        else:
 248            users[exists[0]][1] += 1
 249
 250    writetitle('sshd')
 251    subtitle = plural('login', num) + ' from'
 252    if (len(users) == 1):             # if only one user, do not display no of logins for this user
 253        logger.debug("found " + str(len(matches)) + " ssh logins for user " + users[0][0])
 254        subtitle += ' ' + users[0][0]
 255        writedata(subtitle)
 256    else:
 257        subtitle += ':'
 258        for user in users:
 259            data.append(user[0] + ' (' + str(user[1]) + ')')
 260            if len(data) > MAXLIST:     # if there are lots of users, truncate them
 261                data.append('+ ' + str(len(users) - MAXLIST - 1) + " more")
 262                break
 263        logger.debug("found " + str(len(matches)) + " ssh logins for users " + str(data))
 264        writedata(subtitle, data)
 265    closetag('div', 1)
 266    logger.info("finished sshd section")
 267
 268#
 269#
 270#
 271
 272def sudo():
 273    logger.debug("starting sudo section")
 274    opentag('div', 1, 'sudo', 'section')
 275    umatches = re.findall('.*sudo:session\): session opened.*', readlog('auth'))
 276    num = sum(1 for line in umatches)    # total number of sessions
 277    users = []
 278    data = []
 279    for match in umatches:
 280        user = re.search('.*session opened for user root by (\S*)\(uid=.*\)', match).group(1)
 281        exists = [i for i, item in enumerate(users) if re.search(user, item[0])]
 282        if (exists == []):
 283            users.append([user, 1])
 284        else:
 285            users[exists[0]][1] += 1
 286    commands = []
 287    cmatches = re.findall('sudo:.*COMMAND\=(.*)', readlog('auth'))
 288    for cmd in cmatches:
 289        commands.append(cmd)
 290    logger.debug("found the following commands: " + str(commands))
 291    # temp_cmd=commands[:]
 292    # commands = list(set(commands))
 293    # commands.sort(key=lambda x:temp_cmd.count(x))
 294    commands = orderbyfreq(commands)
 295    logger.debug("top 3 sudo commands: " + str(commands[-3:]))
 296
 297    writetitle("sudo")
 298    subtitle = plural("sudo session", num) + " for"
 299    if (len(users) == 1):
 300        logger.debug("found " + str(num) + " sudo session(s) for user " + str(users[0]))
 301        subtitle += ' ' + users[0][0]
 302        writedata(subtitle)
 303    else:
 304        subtitle += ':'
 305        for user in users:
 306            data.append(user[0] + ' (' + str(user[1]) + ')')
 307            if len(data) > 3:
 308                data.append('+ ' + str(len(users) - 2) + " more")
 309                break
 310        logger.debug("found " + str(len(matches)) + " sudo sessions for users " + str(data))
 311        writedata(subtitle, data)
 312    if (len(commands) > 0):
 313        commands = addtag(commands, 'code')
 314        commands = truncl(commands, CMDNO)
 315        writedata("top sudo commands", [c for c in commands])
 316    closetag('div', 1)
 317    logger.info("finished sudo section")
 318
 319#
 320#
 321#
 322
 323def cron():
 324    logger.debug("starting cron section")
 325    opentag('div', 1, 'cron', 'section')
 326    matches = re.findall('.*CMD\s*\(\s*(?!.*cd)(.*)\)', readlog('cron'))
 327    num = sum(1 for line in matches)
 328    commands = []
 329    for match in matches:
 330        commands.append(str(match))
 331    # commands.append([str(match)for match in matches])
 332    logger.debug("found cron command " + str(commands))
 333    logger.info("found " + str(num) + " cron jobs")
 334    subtitle = str(num) + " cron jobs run"
 335    writetitle("cron")
 336    writedata(subtitle)
 337    if (matches > 0):
 338        commands = orderbyfreq(commands)
 339        commands = addtag(commands, 'code')
 340        commands = truncl(commands, CMDNO)
 341        writedata("top cron commands", [c for c in commands])
 342    closetag('div', 1)
 343    logger.info("finished cron section")
 344
 345#
 346#
 347#
 348
 349def nameget():
 350    logger.debug("starting nameget section")
 351    opentag('div', 1, 'nameget', 'section')
 352    syslog = readlog('sys')
 353    failed = re.findall('.*nameget.*downloading of (.*) from .*failed.*', syslog)
 354    n_f = sum(1 for i in failed)
 355    l_f = []
 356    for i in failed:
 357        l_f.append(i)
 358    logger.debug("the following downloads failed: " + str(l_f))
 359    succ = re.findall('.*nameget.*downloaded.*', syslog)
 360    n_s = sum(1 for i in succ)
 361    l_s = []
 362    for i in succ:
 363        l_s.append(i)
 364    logger.debug("the following downloads succeeded: " + str(l_f))
 365    logger.debug("found " + str(n_s) + " successful downloads, and " + str(n_f) + " failed attempts")
 366    writetitle("nameget")
 367    writedata(str(n_s) + " succeeded", truncl(orderbyfreq(l_s), CMDNO))
 368    writedata(str(n_f) + " failed", truncl(orderbyfreq(l_f), CMDNO))
 369    closetag('div', 1)
 370    logger.info("finished nameget section")
 371
 372#
 373#
 374#
 375
 376def httpd():
 377    logger.info("starting httpd section")
 378    opentag('div', 1, 'httpd', 'section')
 379    accesslog = readlog("httpd/access.log")
 380    a = len(accesslog)
 381    errorlog = readlog("httpd/error.log")
 382    e = len(errorlog)
 383    data_b = 0
 384
 385    for line in accesslog.split('\n'):
 386        try:
 387            data_b += int(re.search('.*HTTP/\d\.\d\" 200 (\d*) ', line).group(1))
 388        except Exception as error:
 389            if type(error) is AttributeError:
 390                pass
 391            else:
 392                logger.warning("error processing httpd access log: " + str(error))
 393    data_h = parsesize(data_b)
 394
 395    logger.debug("httpd has transferred " + str(data_b) + " bytes in response to " + str(a) + " requests with " + str(e) + " errors")
 396
 397    writetitle("apache")
 398    writedata(data_h + " transferred")
 399    writedata(str(a) + " requests")
 400    writedata(str(e) + " errors")
 401
 402    closetag('div', 1)
 403    logger.info("finished httpd section")
 404
 405#
 406#
 407#
 408
 409def httpdsession():
 410    # logger.debug("starting httpd section")
 411    opentag('div', 1, 'httpd', 'section')
 412    httpdlog = requests.get(HTTPDSTATUS).content
 413    uptime = re.search('.*uptime: (.*)<', httpdlog).group(1)
 414    uptime = re.sub(' minute[s]', 'm', uptime)
 415    uptime = re.sub(' second[s]', 's', uptime)
 416    uptime = re.sub(' day[s]', 's', uptime)
 417    uptime = re.sub(' month[s]', 'mo', uptime)
 418    accesses = re.search('.*accesses: (.*) - .*', httpdlog).group(1)
 419    traffic = re.search('.*Traffic: (.*)', httpdlog).group(1)
 420    return("<br /><strong>httpd session: </strong> up " + uptime + ", " + accesses + " requests, " + traffic + " transferred")
 421    closetag('div', 1)
 422    # logger.info("finished httpd section")
 423
 424#
 425#
 426#
 427
 428def smbd():
 429    logger.debug("starting smbd section")
 430    opentag('div', 1, 'smbd', 'section')
 431    files = glob.glob(SMBDDIR + "/log.*[!\.gz][!\.old]")    # find list of logfiles
 432    n_auths = 0         # total number of logins from all users
 433    sigma_auths = []    # contains users and their respective no. of logins
 434    output = ""
 435
 436    for file in files:  # one log file for each client
 437
 438        # find the machine (ip or hostname) that this file represents
 439        ip = re.search('log\.(.*)', file).group(1)    # get ip or hostname from file path (/var/log/samba/log.host)
 440        host = resolve(ip)
 441
 442        # count number of logins from each user
 443        matches = re.findall('.*sam authentication for user \[(.*)\] succeeded.*', readlog(file))
 444        for match in matches:
 445            userhost = match + "@" + host
 446            exists = [i for i, item in enumerate(sigma_auths) if re.search(userhost, item[0])]
 447            if (exists == []):
 448                sigma_auths.append([userhost, 1])
 449            else:
 450                sigma_auths[exists[0]][1] += 1
 451            n_auths += 1
 452    writetitle("samba")
 453    subtitle = plural("login", n_auths) + " from"
 454    data = []
 455    if (len(sigma_auths) == 1):             # if only one user, do not display no of logins for this user
 456        subtitle += ' ' + sigma_auths[0][0]
 457        writedata(subtitle)
 458    else:       # multiple users
 459        subtitle += ':'
 460        for x in sigma_auths:
 461            data.append((str(x[0])) + " (" + str(x[1]) + ")")
 462            if len(data) > MAXLIST:      # if many users, truncate them
 463                data.append('+ ' + str(len(sigma_auths) - MAXLIST - 1) + " more")
 464                break
 465        logger.debug("found " + str(n_auths) + " samba logins for users " + str(sigma_auths))
 466        writedata(subtitle, data)
 467    closetag('div', 1)
 468    logger.info("finished smbd section")
 469
 470#
 471#
 472#
 473
 474def postfix():
 475    logger.debug("starting postfix section")
 476    opentag('div', 1, 'postfix', 'section')
 477    messages = re.findall('.*from\=<.*>, size\=(\d*),.*\n.*\n.*\: removed\n.*', readlog('postfix'))
 478    size = sum([int(x) for x in messages])
 479    size = parsesize(size)
 480    n = str(len(messages))
 481    writetitle("postfix")
 482    writedata(n + " messages sent")
 483    writedata("total of " + size)
 484    closetag('div', 1)
 485    logger.info("finished postfix section")
 486
 487#
 488#
 489#
 490
 491def zfs():
 492    logger.debug("starting zfs section")
 493    opentag('div', 1, 'zfs', 'section')
 494    zfslog = readlog('zfs')
 495    logger.debug("got zfs logfile\n" + zfslog + "---end log---")
 496    pool = re.search('.*---\n(\w*)', zfslog).group(1)
 497    scrub = re.search('.*scrub repaired (\d*) in \d*h\d*m with (\d*) errors on (\S*\s)(\S*)\s(\d+\s)', zfslog)
 498    iostat = re.search('.*---\n\w*\s*(\S*)\s*(\S*)\s', zfslog)
 499    scrubrepairs = scrub.group(1)
 500    scruberrors = scrub.group(2)
 501    scrubdate = scrub.group(3) + scrub.group(5) + scrub.group(4)
 502    alloc = iostat.group(1)
 503    free = iostat.group(2)
 504    writetitle("zfs")
 505    subtitle = "Scrub on " + scrubdate + ": "
 506    data = [scrubrepairs + " repaired", scruberrors + " errors", alloc + " used", free + " free"]
 507    writedata(subtitle, data)
 508    closetag('div', 1)
 509    logger.info("finished zfs section")
 510
 511#
 512#
 513#
 514
 515def temp():
 516    logger.debug("starting temp section")
 517    opentag('div', 1, 'temp', 'section')
 518    sensors.init()
 519    coretemps = []
 520    pkgtemp = 0
 521    systemp = 0
 522    try:
 523        print(sensors.iter_detected_chips())
 524        for chip in sensors.iter_detected_chips():
 525            for feature in chip:
 526                if "Core" in feature.label:
 527                    coretemps.append([feature.label, feature.get_value()])
 528                    logger.debug("found core " + feature.label + " at temp " + str(feature.get_value()))
 529                if "CPUTIN" in feature.label:
 530                    pkgtemp = str(feature.get_value())
 531                    logger.debug("found cpu package at temperature " + pkgtemp)
 532                if "SYS" in feature.label:
 533                    systemp = feature.get_value()
 534                    logger.debug("found sys input " + feature.label + " at temp " + str(feature.get_value()))
 535        core_avg = reduce(lambda x, y: x[1] + y[1], coretemps) / len(coretemps)
 536        logger.debug("average cpu temp is " + str(core_avg))
 537        coretemps.append(["avg", str(core_avg)])
 538        coretemps.append(["pkg", pkgtemp])
 539        coretemps = [x[0] + ": " + str(x[1]) + DEG for x in coretemps]
 540    finally:
 541        sensors.cleanup()
 542
 543    # For this to work, `hddtemp` must be running in daemon mode.
 544    # Start it like this (bash):   sudo hddtemp -d /dev/sda /dev/sdX...
 545    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 546    s.connect(('localhost',HDDTEMPPORT))
 547    output = s.recv(4096)
 548    output += s.recv(4096)
 549    s.close()
 550    hddtemps = []
 551    for drive in re.split('\|{2}', output):
 552        try:
 553            fields = re.search('\|*(/dev/sd.)\|.*\|(\d+)\|(.)', drive)
 554            name = fields.group(1)
 555            temp = float(fields.group(2))
 556            units = fields.group(3)
 557            hddtemps.append(drivetemp(name, temp, units))
 558        except:
 559            pass
 560    hddtotal = 0
 561    data = []
 562    for drive in hddtemps:
 563        data.append(drive.name + ': ' + str(drive.temp) + drive.units)
 564        logger.debug("found disk " + drive.name + " at " + str(drive.temp))
 565        hddtotal += drive.temp
 566    logger.debug("found " + str(len(hddtemps)) + " disks")
 567    logger.debug("sum of disk temps is " + str(hddtotal))
 568    hddavg = hddtotal/float(len(hddtemps))
 569    logger.debug("avg disk temp is " + str(hddavg))
 570    data.append("avg: " + str(hddavg))
 571    writetitle("temperatures")
 572    if (systemp != 0):
 573        writedata("sys: " + str(systemp) + DEG)
 574    if (coretemps != ''):
 575        writedata("cores", coretemps)
 576    if (hddtemps != ''):
 577        writedata("disks", data)
 578
 579    closetag('div', 1)
 580    logger.info("finished temp section")
 581
 582#
 583#
 584#
 585
 586def du():
 587    logger.debug("starting du section")
 588    opentag('div', 1, 'du', 'section')
 589    out = []
 590    content = readlog('alloc')
 591    contentnew = ""
 592    for p in DUPATHS:
 593        alloc_f = getusage(p).alloc
 594        delta = None
 595        try:
 596            alloc_i = re.search(p + '\t(.*)\n', content).group(1)
 597            delta = alloc_f - float(alloc_i)
 598        except:
 599            pass
 600        logger.debug("delta is " + str(delta))
 601        if (delta == None):
 602            out.append([p, "used " + parsesize(alloc_f)])
 603        else:
 604            out.append([p, "used " + parsesize(alloc_f), "delta " + parsesize(delta)])
 605        contentnew += (p + '\t' + str(alloc_f) + '\n')
 606    writelog('alloc', contentnew)
 607
 608    writetitle("du")
 609    logger.debug("disk usage data is " + str(out))
 610    for path in out:
 611        writedata(path[0], [p for p in path[1:]])
 612
 613    closetag('div', 1)
 614    logger.info("finished du section")
 615
 616#
 617#
 618#
 619
 620timenow = time.strftime("%H:%M:%S")
 621datenow = time.strftime("%x")
 622
 623pathfilter = {"auth": AUTHPATH, "cron": CRONPATH, "sys": SYSPATH, "postfix": POSTFIXPATH, "smb": SMBDDIR, "zfs": ZFSPATH, "alloc": ALLOCPATH, "httpd": HTTPDDIR, "header": HEADERPATH}
 624pathfilter = dict((re.escape(k), v) for k, v in pathfilter.iteritems())
 625pathpattern = re.compile("|".join(pathfilter.keys()))
 626
 627varfilter = {"$title$": TITLE, "$date$": datenow, "$time$": timenow, "$hostname$": hostname(), "$version$": VERSION}
 628varfilter = dict((re.escape(k), v) for k, v in varfilter.iteritems())
 629varpattern = re.compile("|".join(varfilter.keys()))
 630
 631
 632__main__()