Overview | Index by: file name | procedure name | procedure call | annotation
syslog-1.0.tm (annotations | original source)

#
#    Copyright (C) 2010 Alexandros Stergiakis <alsterg@gmail.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#//#
# The "syslog" module defines commands for system logging (syslog) and terminal/console logging.
#
# @assume We assume that if syslogd is already running, it is running with proper arguments.
#//#

module require base 1.0
module provide syslog 1.0

namespace eval ::module::syslog {
namespace import ::helper::* ::module::api::*

proc description {} {
    return "System logging configuration and introspection"
}

proc version {} {

}

proc check {} {
    foreach feature [list \
                     CONFIG_FEATURE_SYSLOG \
                     CONFIG_SYSLOGD \
                     CONFIG_KLOGD] {
        if {! [::helper::busybox_has $feature]} {
            error "Busybox doesn't have support for $feature."
        }
    }
}

proc update_syslogd {} {
    variable syslog_server
    variable syslog_trap
    
    killall syslogd
    
    if {[lempty $syslog_server]} {
        exec syslogd -l${syslog_trap} -C4 ;# (@magic-number)
    } else {
        lassign $syslog_server host port
        exec syslogd -L -R${host}:${port} -l${syslog_trap} -C4 ;# (@magic-number)
    }
}

variable first_time 1

proc reset {} {
    variable first_time
    variable buffersize_def
    variable monitor_def
    variable logthreas_def
    
    # We modify shared variables so that new sessions will see the new value.
    Global -rw MONITOR_DEF LOGTHREAS_DEF BUFFERSIZE_DEF
    set BUFFERSIZE_DEF $buffersize_def
    set MONITOR_DEF $monitor_def
    set LOGTHREAS_DEF $logthreas_def
    variable buffersize $BUFFERSIZE_DEF
    variable buffer ""
    
    # Unfortunately there is an incompatibility the way busybox and mikroconf number severities:
    # @todo Fix this.
    # syslogd & klogd expect a threashold number 1-8 where 1:emergency and 8:debug
    # mikroconf (BUFFERSIZE_DEF & LOGTHREAS_DEF) expects 0-7 where 0:debug and 7:emergency
    
    variable syslog_server {}
    variable syslog_trap 8 ;# by default send all messages to syslog server (@magic-number)

    # Soft reset the first time.
    # We assume that if syslogd is running, it is running with proper arguments.
    # No need to restart syslogd.
    if {$first_time && [isrunning syslogd]} {
        set first_time 0
        return
    }
    
    update_syslogd
}

proc constructor {} {
    log::Info "Loading \"syslog\" module: [description]"
    
    check
    
    if {! [isrunning klogd]} {
        exec klogd -c 1 ;# do not print any messages to the console
    }
    
    Global MONITOR_DEF LOGTHREAS_DEF BUFFERSIZE_DEF
    variable monitor_def $MONITOR_DEF
    variable logthreas_def $LOGTHREAS_DEF
    variable buffersize_def $BUFFERSIZE_DEF
    
    reset

    # Finally load Command Specs
    sysconf loadspecs "modules/syslog/syslog.specs"
}

proc destructor {} {    
    # First unload Command Specs
    sysconf remove "syslog"
    
    reset
    
    # We don't terminate syslogd/klogd.
}

################
# Handlers
################

command Log {cmdline argstart sid out no arguments args} {
    set severity [::textutil::string::cap [dict get $arguments SEVERITY]]
    log::$severity -session $sid -category User -- [dict get $arguments MESSAGE]
    return
}

command ClearLogs {cmdline argstart sid out no arguments args} {
    log::clearLogs
    return
}

command ShLogs {cmdline argstart sid out no arguments args} {
    Session $sid COLUMNS
    
    set params {}
    if {[dict exists $arguments NUMBER]} {
        set number [dict get $arguments NUMBER]
        if {! [string is integer $number] || ! (1 <= $number <= 10000)} { ;# ( @magic-number )
            ferror "must be an integer in the range 1..10000" [getpos NUMBER]
        }
        append params " -number $number"
    }
    
    if {[dict exists $arguments SEVERITY]} {
        set severity [dict get $arguments SEVERITY]
        append params " -severity $severity"
    }
    
    if {[dict exists $arguments FACILITY]} {
        set facility [dict get $arguments FACILITY]
        append params " -facility $facility"
    }

    if {[dict exists $arguments IDENT]} {
        set ident [dict get $arguments IDENT]
        append params " -ident $ident"
    }
    
    if {[dict exists $arguments REGEXP]} {
        set regexp [dict get $arguments REGEXP]
        append params " -regexp $regexp"
    }
    
    if {[dict exists $arguments MATCH]} {
        set match [dict get $arguments MATCH]
        append params " -match $match"
    }
    
    set logs [::log::getLogs {*}$params]
    
    for {set i 0} {$i < [llength $logs]} {incr i} {
        array set val [lindex $logs $i]
        
        set age [expr {[clock seconds] - $val(Time)}]
        if {[dict exists $arguments detailed]} {
            puts "Severity: $val(Severity)"
            puts "Time: [clock format $val(Time)]"
            puts "Age: [secs2age $age]"
            puts "Facility: $val(Facility)"
            puts "Message: $val(Message)\n"
        } else {
            puts "[secs2age $age -simple] : $val(Severity) : $val(Message)"
        }
    }
    return
}

command TermMonitor {cmdline argstart sid out no arguments args} {
    Session -rw $sid MONITOR LOGTHREAS
    if {$no} {
        Global LOGTHREAS_DEF
        set MONITOR 0
        set LOGTHREAS $LOGTHREAS_DEF
    } else {
        set MONITOR 1
        set LOGTHREAS [::log::severity2num [dict get $arguments SEVERITY]]
    }
    return
}

command TermMonitor_config {cmdline argstart sid out no arguments args} {
    Global -rw MONITOR_DEF LOGTHREAS_DEF
    variable logthreas_def
    if {$no} {
        set MONITOR_DEF 0
        set LOGTHREAS_DEF $logthreas_def
    } else {
        set MONITOR_DEF 1
        set LOGTHREAS_DEF [::log::severity2num [dict get $arguments SEVERITY]]
    }
    return
}

proc print_TermMonitor_config {} {
    Global MONITOR_DEF LOGTHREAS_DEF
    variable monitor_def
    variable logthreas_def
    if {$monitor_def ne $MONITOR_DEF || $logthreas_def ne $LOGTHREAS_DEF} {
        if {$MONITOR_DEF} {
            return "terminal monitor [::log::severity2str $LOGTHREAS_DEF]\n"
        } else {
            return "no terminal monitor\n"
        }
    }
    return
}

command LoggingHost {cmdline argstart sid out no arguments args} {
    variable syslog_server
    
    set host [dict get $arguments HOST]
    if {! [ishostname $host] && ! [isip $host]} {
        ferror "illegal IP address or hostname" [getpos HOST]
    }
    
    if {$no} {
        set syslog_server {}
        return [update_syslogd]
    }
    
    if {[dict exists $arguments PORT]} {
        set port [dict get $arguments PORT]
        if {! [isipport $port]} {
            ferror "illegal port number" [getpos PORT]
        }
    } else {
        set port 514 ;# @(magic-number)
    }
    
    set syslog_server [list $host $port]
    update_syslogd
}

proc print_LoggingHost {} {
    variable syslog_server
    if {! [lempty $syslog_server]} {
        lassign $syslog_server host port
        if {$port != 514} { ;# @(magic-number)
            return "logging host $host $port\n"
        } else {
            return "logging host $host\n"
        }
    }
    return
}

command LoggingTrap {cmdline argstart sid out no arguments args} {
    variable syslog_trap
    variable syslog_server
    
    if {$no} {
        set syslog_trap 8 ;# by default send all messages to syslog server (@magic-number)
    } else {
        set num [::log::severity2num [dict get $arguments SEVERITY]]
        set syslog_trap [expr {8 - $num}]
    }
    
    update_syslogd
}

proc print_LoggingTrap {} {
    variable syslog_trap
    
    if {$syslog_trap != 8} { ;# by default send all messages to syslog server (@magic-number)
        set num [expr {8 - $syslog_trap}]
        return "logging trap [::log::severity2str $num]\n"
    }
}

command LoggingConsole {cmdline argstart sid out no arguments args} {
    #XXX
}

proc print_LoggingConsole {} {
    #XXX
}

command LoggingBuffer {cmdline argstart sid out no arguments args} {
    Global BUFFERSIZE_DEF
    variable buffer
    variable buffersize
    
    if {$no} {
        set buffersize $BUFFERSIZE_DEF
    } else {
        set number [dict get $arguments NUMBER]
        if {! [string is integer $number] || ! (1 <= $number <= 100000)} { ;# ( @magic-number )
            ferror "must be an integer in the range 1..100000" [getpos NUMBER]
        }
        set buffersize $number
    }
    
    set buffer [lrange $buffer end+[expr {-1*$buffersize+1}] end]
    return
}

proc print_LoggingBuffer {} {
    Global BUFFERSIZE_DEF
    variable buffersize
    
    if {$buffersize != $BUFFERSIZE_DEF} {
        return "logging buffer $buffersize\n"
    }
    return
}

} ;# End of Namespace

Overview | Index by: file name | procedure name | procedure call | annotation
File generated 2010-03-13 at 22:28.