Overview | Index by: file name |
procedure name |
procedure call |
annotation
fs-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 "fs" module provide commands that operate on local and remote file systems.
#
# Supported local filesystems:
# * Those supported by busybox's applets.
# Supported remote filesystems:
# * NFS
#
#//#
module require base 1.0
module provide fs 1.0
namespace eval ::module::fs {
namespace import ::helper::* ::module::api::*
proc description {} {
return "Local/Remote filesystem access support"
}
proc version {} {
}
proc check_nfs {} {
foreach feature [list \
CONFIG_NFS_FS \
CONFIG_LOCKD \
CONFIG_SUNRPC \
CONFIG_NFS_COMMON] {
if {! [::helper::kernel_has $feature]} { ;# Can return error.
error "Kernel doesn't have support for $feature."
}
}
foreach feature [list \
CONFIG_FEATURE_MOUNT_NFS] {
if {! [::helper::busybox_has $feature]} { ;# Can return error.
error "Busybox doesn't have support for $feature."
}
}
exec modprobe sunrpc
exec modprobe lockd
exec modprobe nfs
}
proc check {} {
variable features
foreach feature [list \
CONFIG_MD5SUM \
CONFIG_SHA1SUM \
CONFIG_FSCK \
CONFIG_DD \
CONFIG_DF \
CONFIG_STAT \
CONFIG_MOUNT \
CONFIG_UMOUNT \
CONFIG_SWAPONOFF \
CONFIG_MKSWAP \
CONFIG_MODPROBE \
CONFIG_FDISK] {
if {! [::helper::busybox_has $feature]} { ;# Can return error.
error "Busybox doesn't have support for $feature."
}
}
if {[catch {
check_nfs
} reason]} {
log::Notice "Deactivating NFS. Reason: $reason"
} else {
lappend features "nfs"
}
}
proc reset {} {
variable mounted_swap
variable mounted_device
variable mounted_device_map
variable mounted_nfs
foreach m $mounted_swap {
exec swapoff $m
}
set mounted_swap {}
foreach m $mounted_device {
exec umount $m
}
set mounted_device {}
array set mounted_device_map {}
foreach m $mounted_nfs {
lassign $m server remote local
exec umount -f $local
}
set mounted_nfs {}
}
proc constructor {} {
log::Info "Loading \"fs\" module: [description]"
variable features {}
variable mounted_swap {}
variable mounted_device {}
variable mounted_device_map
array set mounted_device_map {}
variable mounted_nfs {}
check
reset
# Finally load Command Specs
sysconf loadspecs "modules/fs/fs.specs"
if {"nfs" in $features} {
sysconf loadspecs "modules/fs/nfs.specs"
}
}
proc destructor {} {
# First unload Command Specs
sysconf remove "fs"
reset
}
################
# Handlers
################
# Warning: It modifies the working directory for the process.
# This proc is only executed when the "pwd" Tcl command is not accessible (for example in a safe interp),
# otherwise the Tcl command is executed, even if the CLI convenience facilities indicate a MikroConf command.
command Cd {cmdline argstart sid out no arguments args} {
cd [dict get $arguments DIRECTORY]
return
}
command More {cmdline argstart sid out no arguments args} {
foreach f [glob -nocomplain -types {f l} -- [dict get $arguments URL]] {
sputs [::fileutil::cat $f]
}
return
}
command Delete {cmdline argstart sid out no arguments args} {
file delete -force -- {*}[glob -nocomplain -- [dict get $arguments URL]]
return
}
command Dir {cmdline argstart sid out no arguments args} {
if {[dict exists $arguments URL]} {
set dir [dict get $arguments URL]
if {[string match {*://} $dir]} {
append dir *
}
} else {
set dir [file join [pwd] *]
}
puts "Directory listing for $dir"
puts [format {%-5s %-11s %-11s %-11s %-s} "Perm" "Bytes" "Access" "Modified" "Name"]
foreach f [lsort [glob -nocomplain -- $dir]] {
if {! [file exists $f]} { continue }
set name [file tail $f]
if {$name eq "." || $name eq ".."} { continue }
set perm {}
if {[file isfile $f]} {append perm "-"}
if {[file isdirectory $f]} {append perm "d"; append name "/"}
if {[file readable $f]} {append perm "r"}
if {[file writable $f]} {append perm "w"}
if {[file executable $f]} {append perm "e"}
if {[catch { file size $f } size]} { set size "Unavailable" }
if {[catch { clock format [file atime $f] -format {%Y-%m-%d} } atime]} { set atime "Unavailable" }
if {[catch { clock format [file mtime $f] -format {%Y-%m-%d} } mtime]} { set mtime "Unavailable" }
puts [format {%-5s %-11s %-11s %-11s %-s} \
$perm \
$size \
$atime \
$mtime \
$name]
}
return
}
command Mkdir {cmdline argstart sid out no arguments args} {
file mkdir [dict get $arguments URL]
return
}
# This proc is only executed when the "pwd" Tcl command is not accessible (for example in a safe interp),
# otherwise the Tcl command is executed, even if the CLI convenience facilities indicate a MikroConf command.
command Pwd {cmdline argstart sid out no arguments args} {
return [pwd]
}
command Rmdir {cmdline argstart sid out no arguments args} {
set dirs [glob -nocomplain -type d -- [dict get $arguments URL]]
foreach d $dirs {
if {[lempty [glob -nocomplain -- $d]]} {
file delete -force -- $d
}
}
return
}
# @diff "move" instead of "rename".
command Move {cmdline argstart sid out no arguments args} {
set sources [getall SOURCE]
if {[llength $sources] > 1} { ;# Move
file rename -force -- source {*}$sources [dict get $arguments TARGET]
} else { ;# Rename
file rename -force -- $sources [dict get $arguments TARGET]
}
return
}
command EraseVolume {cmdline argstart sid out no arguments args} {
Global DEV_DIR DEVICE_MAPS
set dev [string map $DEVICE_MAPS [dict get $arguments VOLUME]]
set dev [file join $DEV_DIR $dev]
if {! [file exists $dev]} {
ferror "cannot find device \"$dev\"" [getpos VOLUME]
}
exec umount $dev
# Run the following on Slave, for MikroConf to be responsive.
bgexec dd if=[file join $DEV_DIR zero] of=$dev
return
}
command Format {cmdline argstart sid out no arguments args} {
Global DEV_DIR DEVICE_MAPS
set dev [string map $DEVICE_MAPS [dict get $arguments DEVICE]]
set dev [file join $DEV_DIR $dev]
if {! [file exists $dev]} {
ferror "cannot find device" [getpos DEVICE]
}
set fstype [dict get $arguments FILESYSTEM]
if {$fstype eq "swap"} {
bgexec mkswap $dev
return
}
# Check for availability of the executable.
if {[lempty [auto_execok mkfs.$fstype]]} {
ferror "formatting \"$fstype\" filesystem is not supported" [getpos FILESYSTEM]
}
switch -exact -- $fstype {
"minix" { set param {-c -n 30 -v} }
"vfat" {}
default {
# No param
}
}
# Execute in Slave, for MikroConf to be responsive.
bgexec mkfs.$fstype {*}$param $dev
return
}
proc dlist_filesystems {sid args} {
set fs [list swap]
if {[::helper::busybox_has CONFIG_MKFS_MINIX]} {
lappend fs "minix"
}
if {[::helper::busybox_has CONFIG_MKFS_VFAT]} {
lappend fs "vfat"
}
return $fs
}
# @interactive
command Fsck {cmdline argstart sid out no arguments args} {
Global DEV_DIR DEVICE_MAPS
set dev [string map $DEVICE_MAPS [dict get $arguments DEVICE]]
set dev [file join $DEV_DIR $dev]
if {! [file exists $dev]} {
ferror "cannot find device \"$dev\"" [getpos DEVICE]
}
# Execute in Slave, for MikroConf to be responsive.
bgexec fsck $dev
return
}
command ShFileDescriptors {cmdline argstart sid out no arguments args} {
Global PROC_DIR
if {[dict exists $arguments PID]} {
set dir [file join $PROC_DIR [dict get $arguments PID] fd]
if {! [file isdirectory $dir]} {
ferror "process not found" [getpos PID]
}
} else {
set dir [file join $PROC_DIR [pid] fd]
}
puts "Open File Descriptors\n"
puts [format { %-3s %-s} "FD" "Opened object"]
set fds [lsort -integer [glob -nocomplain -tails -directory $dir -- *]]
foreach fd $fds {
if {! [file exists [file join $dir $fd]]} { continue } ;# We need this because a file descriptor dissapears in the mean time.
set target [file link [file join $dir $fd]]
puts [format { %-3s %-s} $fd $target]
}
return
}
command ShFileInformation {cmdline argstart sid out no arguments args} {
exec stat [dict get $arguments FILEPATH]
}
command ShFileSystem {cmdline argstart sid out no arguments args} {
if {[dict exists $arguments FILEPATH]} {
exec stat -f [dict get $arguments FILEPATH]
} else {
exec df -P -h
}
}
command ShDisks {cmdline argstart sid out no arguments args} {
exec fdisk -l ;# better than /proc/partitions
}
command ShStat {cmdline argstart sid out no arguments args} {
exec fdisk -l ;# better than /proc/partitions
}
command ShFlash {cmdline argstart sid out no arguments args} {
# Invoke distribution-specific flash verification code.
event generate FLASH SHOW $out
return
}
command VerifyFlash {cmdline argstart sid out no arguments args} {
# Invoke distribution-specific flash verification code.
event generate FLASH VERIFY $out
}
proc dlist_devices {sid args} {
Global DEV_DIR
glob -nocomplain -directory $DEV_DIR -tail -- sd*
}
command MountSwap {cmdline argstart sid out no arguments args} {
Global DEV_DIR
variable mounted_swap
set dev [file join $DEV_DIR [dict get $arguments DEVICE]]
if {$no} {
if {$dev ni $mounted_swap} {
ferror "not mounted" [getpos DEVICE]
}
exec swapoff $dev
lremove mounted_swap $dev
} else {
if {$dev in $mounted_swap} {
ferror "already mounted" [getpos DEVICE]
}
exec swapon $dev
lappend mounted_swap $dev
}
return
}
proc print_MountSwap {} {
variable mounted_swap
set result {}
foreach dev $mounted_swap {
append result "mount swap $dev\n"
}
return $result
}
command MountDevice {cmdline argstart sid out no arguments args} {
Global DEV_DIR
variable mounted_device
variable mounted_device_map
set dev [file join $DEV_DIR [dict get $arguments DEVICE]]
if {$no} {
if {$dev ni $mounted_device} {
ferror "not mounted" [getpos DEVICE]
}
exec umount $dev
lremove mounted_device $dev
unset mounted_device_map($dev)
return
}
if {$dev in $mounted_device} {
ferror "already mounted" [getpos DEVICE]
}
set dir [dict get $arguments DIRECTORY]
if {! [file isdirectory $dir]} {
ferror "not a directory or non-existant directory" [getpos DIRECTORY]
}
set params {}
if {[dict exists $arguments FILESYSTEM]} {
set fs [dict get $arguments FILESYSTEM]
append params "-t $fs "
} else {
set fs {}
}
exec mount {*}$params $dev $dir
lappend mounted_device $dev
set mounted_device_map($dev) [dict create directory $dir filesystem $fs]
return
}
proc print_MountDevice {} {
variable mounted_device
variable mounted_device_map
set result {}
foreach dev $mounted_device {
set dir [dict get $mounted_device_map($dev) directory]
set fs [dict get $mounted_device_map($dev) filesystem]
append result "mount device $dev $dir $fs\n"
}
return $result
}
command MountNfs {cmdline argstart sid out no arguments args} {
variable mounted_nfs
set server [dict get $arguments SERVER]
if {! [isip $server] && ! [ishostname $hostname]} {
ferror "invalid hostname or address" [getpos SERVER]
}
set remote [dict get $arguments REMOTE_PATH]
if {[file pathtype $remote] ne "absolute"} {
ferror "invalid path; requires an absolute directory path" [getpos REMOTE_PATH]
}
set local [dict get $arguments LOCAL_PATH]
if {! [file isdirectory $local]} {
ferror "not a directory or non-existant directory" [getpos LOCAL_PATH]
}
set key [list $server $remote $local]
if {$no} {
if {$key ni $mounted_nfs} {
error "not mounted"
}
exec umount -f $local
lremove mounted_nfs $key
return
}
if {$key in $mounted_nfs} {
error "already mounted"
}
# bgexec due to DNS resolution and network protocol delays.
bgexec mount -t nfs ${server}:${remote} $local -o nolock
lappend mounted_nfs $key
return
}
proc print_MountNfs {} {
variable mounted_nfs
set result {}
foreach m $mounted_nfs {
lassign $m server remote local
append result "mount nfs $server $remote $local\n"
}
return $result
}
} ;# End of Namespace
Overview | Index by: file name |
procedure name |
procedure call |
annotation
File generated 2010-03-13 at 22:28.