Overview | Index by: file name |
procedure name |
procedure call |
annotation
interface-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 "interface" module defines commands for interface configuration.
#
# @todo Better value checking within each handler: Mtu, Mac, ...
#//#
module require base 1.0
module provide interface 1.0
namespace eval ::module::interface {
namespace import ::helper::* ::module::api::* \
::module::base::dlist_procedures \
::module::base::dlist_avail_procs
namespace export dlist_interfaces \
get_interfaces \
get_mac \
get_state \
get_mtu \
iface_print_callback \
iface_rename_callback
proc description {} {
return "Interface configuration"
}
proc version {} {
}
proc check {} {
foreach feature [list \
CONFIG_NAMEIF \
CONFIG_IFCONFIG \
CONFIG_IFPLUGD \
CONFIG_FEATURE_IFCONFIG_STATUS \
CONFIG_FEATURE_IFCONFIG_SLIP \
CONFIG_FEATURE_IFCONFIG_HW \
CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS] {
if {! [::helper::busybox_has $feature]} { ;# Can return error.
error "Busybox doesn't have support for $feature."
}
}
}
proc reset {} {
variable renames
variable interface
# All interfaces present on boot are 'up' by default, shutdown is explicitly listed in running-conf,
# for those interfaces that need to be in 'down' state. Any interface however that appears after boot
# will be on any state the kernel puts it, typically in 'down' state. That's why we use get_state,
# so that we are able to sence the state of newly created interfaces as dictated by the kernel.
foreach iface [get_interfaces] {
exec ifconfig $iface up
}
# Restore original names.
foreach iface [array names renames] {
set mac [get_mac $iface]
exec nameif -s $renames($iface) $mac
unset renames($iface)
unset interface($iface)
}
array set renames {}
array set interface {}
foreach iface [get_interfaces] {
set interface($iface) [dict create]
}
}
proc constructor {} {
Global BASE_DIR
log::Info "Loading \"interface\" module: [description]"
check
# Mapping of <current name> => <original name> of an interface.
# An entry only present if interface has been renamed.
variable renames
array set renames {}
# Mapping of <interface> => <dict> of interface properties.
variable interface
array set interface {}
# List of callback procedures to execute while generating the
# interface running configuration section. This flexibility is
# not currently provided from the XML-based mechanism.
# Order matters. They are executed in order of registration.
# No need to reset this variable.
variable print_callbacks [list]
log::Debug "Interfaces found: [get_interfaces]"
reset
# -f/-F Treat link detection error as link down/link up
# (otherwise exit on error)
# -a Do not up interface automatically
# -M Monitor creation/destruction of interface
# (otherwise it must exist)
# -r PROG Script to run
# -x ARG Extra argument for script
# -I Don't exit on nonzero exit code from script
# -p Don't run script on daemon startup
# -q Don't run script on daemon quit
# -l Run script on startup even if no cable is detected
# -t SECS Poll time in seconds
# -u SECS Delay before running script after link up
# -d SECS Delay after link down
# -m MODE API mode (mii, priv, ethtool, wlan, auto)
killall ifplugd
variable interface_event_token [event bind INTERFACE LINKSTATE [namespace current]::event_INTERFACE_LINKSTATE]
foreach iface [get_interfaces -loopback] {
exec ifplugd -i $iface -f -a -I -p -q -t 1 -u 0 -d 0 -m auto -r [file join $BASE_DIR scripts ifplugd]
}
# Finally load Command Specs
sysconf loadspecs "modules/interface/interface.specs"
}
proc destructor {} {
variable interface_event_token
# First unload Command Specs
sysconf remove "interface"
killall ifplugd
reset
event unbind $interface_event_token
}
################
# Exported
################
# Get the the mac addess of an interface.
#
# @param iface Interface name.
# @return Corresponding mac.
proc get_mac {iface} {
Global SYS_DIR
return [string trim [::fileutil::cat [file join $SYS_DIR class net $iface address]]]
}
# Get interface state.
#
# @param iface Interface name.
# @return "up", "down", or "unknown".
proc get_state {iface} {
Global SYS_DIR
return [string trim [::fileutil::cat [file join $SYS_DIR class net $iface operstate]]]
}
# Get interface MTU.
#
# @param iface Interface name.
# @return Corresponding mtu.
proc get_mtu {iface} {
Global SYS_DIR
return [string trim [::fileutil::cat [file join $SYS_DIR class net $iface mtu]]]
}
# Get the list of intefaces currently on the system
#
# @param flags -loopback : Don't include loopback interfaces in returned list.
# @return A list consisting of all the interface names.
proc get_interfaces {args} {
Global SYS_DIR
set ifs [glob -directory [file join $SYS_DIR class net] -tails -nocomplain -- *]
foreach p $args {
switch -exact -- $p {
"-loopback" { lremove ifs "lo" }
"+null" { lappend ifs "null" }
default { error "unrecognized option $p "}
}
}
return $ifs
}
# Alias of get_interfaces.
proc dlist_interfaces {sid args} {
get_interfaces {*}$args
}
# Register callback procs to be called when generating interface running-conf section.
proc iface_print_callback {name} {
variable print_callbacks
lappend print_callbacks $name
}
# Register callback procs to be called when renaming an interface.
proc iface_rename_callback {name} {
variable rename_callbacks
lappend rename_callbacks $name
}
################
# Handlers
################
command ShInterface {cmdline argstart sid out no arguments args} {
if {[dict exists $arguments INTERFACE]} {
set iface [dict get $arguments INTERFACE]
exec ifconfig $iface
} else {
exec ifconfig -a
}
}
command Interface {cmdline argstart sid out no arguments args} {
set iface [dict get $arguments INTERFACE]
sysconf confmode $sid set "interface"
sysconf confmode $sid setstring "(interface-${iface})"
sysconf confmode $sid store $iface
return
}
proc print_Interface {} {
variable print_callbacks
variable interface
set result "\n"
foreach iface [lsort [array names interface]] {
set properties $interface($iface)
append result "interface $iface\n"
if {[dict exists $properties description]} {
set val [dict get $properties description]
append result " description {$val}\n"
}
foreach c $print_callbacks { append result [$c $iface] }
if {[dict exists $properties mtu]} {
set val [dict get $properties mtu]
append result " mtu $val\n"
}
if {[dict exists $properties mac]} {
set val [dict get $properties mac]
append result " mac $val\n"
}
if {[dict exists $properties onlinkup]} {
set val [dict get $properties onlinkup]
append result " on linkup $val\n"
}
if {[dict exists $properties onlinkdown]} {
set val [dict get $properties onlinkdown]
append result " on linkdown $val\n"
}
if {[get_state $iface] eq "down"} {
append result " shutdown\n"
}
append result " exit\n"
append result "#\n"
}
return $result
}
command RenameInterface {cmdline argstart sid out no arguments args} {
variable renames
variable interface
set iface [dict get $arguments INTERFACE]
set properties $interface($iface)
if {[get_state $iface] eq "up"} {
error "interface must be in shutdown state to rename"
}
if {$no} {
if {! [info exists renames($iface)]} {
error "interface already in its original name"
}
set origname $renames($iface)
set mac [get_mac $iface]
exec nameif -s $origname $mac
unset renames($iface)
set interface($origname) $interface($iface)
unset interface($iface)
# Call rename callbacks, so that other MikroConf modules update their state as well.
foreach cb $rename_callbacks {
$cb $iface $origname ;# from to
}
return
}
set newname [dict get $arguments NAME]
if {$newname in [get_interfaces]} {
error "interface with this name already exists"
}
set mac [get_mac $iface]
exec nameif -s $newname $mac
if {[info exists renames($iface)]} { ;# renamed before
set origname $renames($iface)
unset renames($iface)
} else { ;# first time renamed
set origname $iface
}
set renames($newname) $origname
set interface($newname) $interface($origname)
unset interface($origname)
# Call rename callbacks, so that other MikroConf modules update their state as well.
foreach cb $rename_callbacks {
$cb $iface $newname ;# from to
}
return
}
proc print_RenameInterface {} {
variable renames
set result {}
foreach iface [array names renames] {
append result "rename $renames($iface) $iface\n"
}
if {! [lempty $result]} {
set result "#\n${result}#\n"
}
return $result
}
command Shutdown {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
exec ifconfig $iface up
} else {
exec ifconfig $iface down
}
return
}
command Description {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
dict unset interface($iface) description
} else {
dict set interface($iface) description [dict get $arguments DESCRIPTION]
}
return
}
# We let 'ifconfig' to check validity of provided value.
command Mtu {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
exec ifconfig $iface mtu [dict get interface($iface) mtu_orig]
dict unset interface($iface) mtu mtu_orig
} else {
set old [get_mtu $iface]
set new [dict get $arguments MTU]
if {! [string is integer $new]} {
ferror "illegal MTU" [getpos MTU]
}
exec ifconfig $iface mtu $new
dict set interface($iface) mtu $new
dict set interface($iface) mtu_orig $old
}
return
}
# We let 'ifconfig' to check validity of provided value.
command Mac {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
exec ifconfig $iface hw ether [dict get interface($iface) mac_orig]
dict unset interface($iface) mac mac_orig
} else {
set old [get_mac $iface]
set new [dict get $arguments MAC]
exec ifconfig $iface hw ether $new
dict set interface($iface) mac $new
dict set interface($iface) mac_orig $old
}
return
}
command OnLinkdown {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
dict unset interface($iface) onlinkdown
} else {
dict set interface($iface) onlinkdown [concat [dict get $arguments PROCEDURE] [getall ARGUMENT]]
}
return
}
command OnLinkup {cmdline argstart sid out no arguments args} {
variable interface
set iface [sysconf confmode $sid store]
if {$no} {
dict unset interface($iface) onlinkup
} else {
dict set interface($iface) onlinkup [concat [dict get $arguments PROCEDURE] [getall ARGUMENT]]
}
return
}
proc event_INTERFACE_LINKSTATE {tag event details} {
variable interface
lassign $details iface state
if {! [info exists interface($iface)]} { return }
set properties $interface($iface)
switch -exact -- $state {
"up" {
if {[dict exists $properties onlinkup]} {
set name [dict get $properties onlinkup]
if {[catch {
procedures::invoke {*}$name
} result options]} {
log::Error -stack $options "Event handler error \"$name\": $result"
}
}
}
"down" {
if {[dict exists $properties onlinkdown]} {
set name [dict get $properties onlinkdown]
if {[catch {
procedures::invoke {*}$name
} result options]} {
log::Error -stack $options "Event handler error \"$name\": $result"
}
}
}
default {
log::Notice "unrecognized interface state \"$state\""
}
}
}
} ;# End of Namespace
Overview | Index by: file name |
procedure name |
procedure call |
annotation
File generated 2010-03-13 at 22:28.