# # Copyright (C) 2010 Alexandros Stergiakis # # 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 . # #//# # 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