Overview | Index by: file name | procedure name | procedure call | annotation
storage.tcl (annotations | original source)

#
#    Copyright (C) 2008  Alexandros Stergiakis <sterg@kth.se>
#
#    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/>.
#

#//#
# Persistent storage across reboots. It relies on distribution-specific event handlers that
# will copy the data to persistent storage, except if the STORAGE_FILE already resides in
# a persistent directory. File is always restored on boot. Data are accessed as an array.
#
# We refer to this persistent storage as Storage. The Storage is appropriate for information
# that are not suitable for the main conf, such as time of last log-in, reason of last reboot,
# log-in history, failed login attempts, and other casual information that are not part of
# the router configuration as such.
#
# Warning:
# Notice that this Storage facility is only for the Master Interpreter/Thread. It is not meant
# to be used for concurrent use by Master and Session threads/interp.
#
# @changelog
#   * 
#
# @copyright 2007-2008 Alexandros Stergiakis <alsterg@gmail.com>
# @author Alexandros Stergiakis <alsterg@gmail.com>
#//#

namespace eval storage {
    namespace import ::helper::writefile ::helper::readfile
    namespace export load unload

# Global:
array set ::Storage {}

variable loaded 0
variable lastjob 0

# Invoked with a trace when-ever Storage is modified.
# @assume storage::load is executed before.
proc update {args} {
    global Storage
    variable lastjob

    if {[catch {after info $lastjob}]} {    ;# No pending job
        Global STORAGE_FILE
    
        # XXX Use mmap (remove trace)
        writefile $STORAGE_FILE [array get Storage]
        
        # Emitted after persistent storage gets modified. This event is rate limitted.
        set lastjob [after 10000 [list event emit PERSISTENT_STORAGE_UPDATE]] ;# (@magic-number)
        # A handler attached to this event can be used to take care of copying the
        # Storage File (STORAGE_FILE) to a safe place after a modification is done.
        # It can also be used to monitor specific variables within Storage. 
    }
}

# Loads stored information from last boot in the global namespace as an array named "Storage".
# Access to the Storage array can then be done as usual with the "global" command.
proc load {} {
    global Storage
    variable loaded
    variable lastjob

    if {$loaded} { return }
    
    Global STORAGE_FILE
    array set Storage [readfile $STORAGE_FILE]
    set loaded 1
    
    # Note: Write variable trace is called after the variable/array is modified
    trace add variable ::Storage write ::storage::update
}

# Unloads Storage from the global namespace and invokes "update" to save it.
# Access with the "global" command will not be possible after this.
proc unload {} {
    global Storage
    variable loaded
    variable lastjob
    
    if {! $loaded} { return }

    if {! [catch {after info $lastjob}]} {    ;# Pending job
        after cancel $lastjob
        event emit PERSISTENT_STORAGE_UPDATE
    }

    set lastjob 0
    trace remove variable ::Storage write ::storage::update
    unset Storage
    set loaded 0
}

namespace ensemble create

} ;# End of Namespace

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