# copyright (C) 1997-2006 Jean-Luc Fontaine (mailto:jfontain@free.fr)
# this program is free software: please read the COPYRIGHT file enclosed in this package or use the Help Copyright menu

# $Id: apache.tcl,v 2.22 2006/03/14 22:30:53 jfontain Exp $


package provide apache [lindex {$Revision: 2.22 $} 1]
package require miscellaneous 1
package require apacheutilities 1

namespace eval apache {

    # use an empty hidden column as index since there is only a single row
    array set data {
        updates 0
        0,label {} 0,type ascii 0,message {} 0,0 {}
        1,label accesses 1,type real 1,message {total number of accesses}
        2,label kilobytes 2,type real 2,message {total number of kilobytes served}
        3,label {CPU load} 3,type real 3,message {CPU load in percent}
        4,label {up time} 4,type dictionary 4,message {total running time in d(ays), h(ours), m(inutes) and s(econds)}
        5,label requests 5,type real 5,message {averaged number of requests per second}
        6,label bytes 6,type integer 6,message {averaged number of bytes per second}
        7,label bytes/request 7,type integer 7,message {average number of bytes per requests}
        8,label busy 8,type integer 8,message {number of busy servers}
        9,label idle 9,type integer 9,message {number of idle servers}
        10,label accesses/second 10,type real 10,message {current number of accesses per second}
        11,label kilobytes/second 11,type real 11,message {current kilobytes served per second}
        pollTimes {10 5 20 30 60 120 300}
        views {{visibleColumns {1 2 3 4 5 6 7 8 9 10 11} swap 1}}
        persistent 1
        switches {-r 1 --remote 1 --proxyhost 1 --proxyport 1}
    }
    set file [open apache.htm]
    set data(helpText) [read $file]                                                           ;# initialize HTML help data from file
    close $file
    unset file

    proc initialize {optionsName} {
        upvar 1 $optionsName options
        variable url
        variable data
        variable host

        set url [url options host apache ?auto]
        set data(identifier) apache($host)
        configure options
        ::http::geturl $url -validate 1                                    ;# test server so an error is generated if not responding
    }

    proc update {} {
        variable url
        variable transaction
        variable data

        if {[info exists transaction]} return                                                                                ;# busy
        if {[catch {set transaction [::http::geturl $url -command ::apache::completed]} message]} {
            flashMessage "error: $message"
            resetData
            incr data(updates)                                                                                   ;# show void values
        }
    }

    proc completed {token} {
        variable data
        variable last
        variable transaction
        variable host

        set valid [expr {[::http::ncode $token] == 200}]                                      ;# sole HTTP code for good transaction
        if {$valid} {
            foreach line [split [set ${token}(body)] \n] {
                if {![regexp {^(.+): (.+)$} $line dummy name value]} break                             ;# stop at first invalid line
                set current($name) $value
            }
            set size [array size current]
            if {$size == 3} {                                                ;# only returned: busy and idle servers plus scoreboard
                flashMessage "error: extended status not enabled on $host"
            }
            # check that all statistics are present (per second values are not always returned when server was just (re)started)
            set valid [expr {$size == 10}]
        } else {
            if {[::http::ncode $token] == 404} {
                flashMessage "error: no server status on $host"
            } else {
                flashMessage "error: [::http::code $token]"
            }
        }
        ::http::cleanup $token                                                                              ;# free transaction data
        if {$valid} {
            set data(0,1) $current(Total Accesses)
            set data(0,2) $current(Total kBytes)
            set data(0,3) [format %.2f $current(CPULoad)]
            set data(0,4) [formattedTime $current(Uptime)]
            set data(0,5) [format %.2f $current(ReqPerSec)]
            set data(0,6) [expr {round($current(BytesPerSec))}]
            set data(0,7) [expr {round($current(BytesPerReq))}]
            if {[info exists current(BusyWorkers)]} {                                                   ;# for recent Apache servers
                set data(0,8) $current(BusyWorkers)
                set data(0,9) $current(IdleWorkers)
            } else {                                                                                                 ;# old versions
                set data(0,8) $current(BusyServers)
                set data(0,9) $current(IdleServers)
            }
            if {[info exists last]} {
                set interval [expr {double($current(Uptime) - $last(uptime))}]                  ;# force floating point calculations
                # the following values can wrap around, so force result as 32 bit positive number (on both 32 and 64 bit platforms):
                set data(0,10) [format %.2f [expr {(($current(Total Accesses) - $last(accesses)) & 0xFFFFFFFF) / $interval}]]
                set data(0,11) [format %.2f [expr {(($current(Total kBytes) - $last(kilobytes)) & 0xFFFFFFFF) / $interval}]]
            } else {                                                                                                   ;# first poll
                array set data {0,10 0 0,11 0}
            }
            array set last [list accesses $current(Total Accesses) kilobytes $current(Total kBytes) uptime $current(Uptime)]
        } else {                                                                                             ;# a HTTP error occured
            resetData
        }
        unset transaction                                                                              ;# ready for next transaction
        incr data(updates)
    }

    proc resetData {} {
        variable data
        variable last

        array set data {0,1 ? 0,2 ? 0,3 ? 0,4 ? 0,5 ? 0,6 ? 0,7 ? 0,8 ? 0,9 ? 0,10 ? 0,11 ?}                     ;# show void values
        catch {unset last}
    }

}
