Tcl7.6 User Commands Man Page -- namespace (n)
Table of Contents


NAME

namespace - evaluate scripts in a particular namespace context

SYNOPSIS

namespace name ?-local? ?-enforced state? ?--? script?

DESCRIPTION

The namespace command finds or creates a namespace name and executes an optional script code string in that context. This command is usually used to add commands and variables, or to redefine commands and variables, in a namespace. When the script is executed, the namespace command adds a call frame to the stack, so the namespace command counts as another level for the uplevel and upvar commands.

If the -local flag is included, the namespace name will be found or created in the local namespace context. Otherwise, name could reference any namespace found by following the import list (see below for rules regarding name resolution). The -local flag allows a child namespace to be created even if another namespace can be found with the same name.

If the -enforced flag is included, its argument state is a boolean value enabling or disabling special rules for command/variable name resolution. This makes it possible to create "safe" namespaces with restricted access to commands. See the section on safe namespaces below for more details.

The optional "--" marks the end of the option list. The next argument is treated as a command script, even if it starts with a "-" character.

OVERVIEW

A namespace is a collection of commands and global variables that is kept apart from the usual global scope. This allows Tcl code libraries to be packaged in a well-defined manner, and prevents unwanted interactions with other libraries. A namespace can also have child namespaces within it, so one library can contain its own private copy of many other libraries. The global scope (named "::") is the root namespace for an interpreter; all other namespaces are contained within it.

Commands and variables local to a namespace can be referenced using simple names within the namespace. But in another context, they must be qualified by the namespace name. Namespace qualifiers leading up to a command or variable name are separated by the "::" string.

If a command, variable or namespace name is absolutely qualified (i.e., it starts with "::") then it is accessed directly. For example, the name "::foo::bar::x" refers to the variable "x", which is in the namespace "bar", which is in the namespace "foo", which is in the global namespace. If the name does not start with "::", it is treated relative to the current namespace context. Lookup starts in the current namespace, then continues through all other namespaces included on the "import" list. The "info which" command can be used to check the results of name resolution. Whenever a name is resolved, the result is cached to keep namespace performance on par with vanilla Tcl.

By default, the import list for each namespace includes the parent namespace, so commands at the global scope can be accessed transparently. The import list can be modified by using the import command within a namespace.

By default, commands and variables are "public" and can be accessed from any other namespace context. The commands "protected" and "private" can be used when defining commands and variables to restrict access to them. These commands designate which parts of a library are meant to be accessed by users, and which parts are not.

A namespace can be deleted using the "delete namespace" command. This deletes all commands and variables in the namespace, and deletes all child namespaces as well.

EXAMPLE

The following namespace implements a simple counter facility:

namespace counter {
variable x 0

proc restart {{by 1}} {
global x
set x 0
next $by
}

proc next {{by 1}} {
global x
incr x $by
return $x
}
} puts "Count up by 2..."
puts " count: [counter::next 2]"
puts " count: [counter::next 2]"
puts " count: [counter::next 2]"
puts "restart: [counter::restart 2]"
puts " count: [counter::next 2]"

The counter namespace contains two commands (restart and next) and one global variable (x). All of these elements are protected by the namespace, so there is no conflict, for example, between "counter::x" and another variable named "x" in the global namespace.

Procedures execute in the context of the namespace that contains them. Within counter::restart and counter::next, the variable name "x" refers to "::counter::x". Within counter::restart, the command "next" refers to "::counter::next". Outside of the namespace, these names must be explicitly qualified with the "counter::" namespace path.

SAFE NAMESPACES

Namespaces include a special enforcement feature that can be activated using the -enforced flag. When enforcement is turned on, command and variable references can be intercepted, and the usual lookup rules can be modified. This supports the construction of "safe" namespaces, which interpret code from an untrusted source and deny access to commands which could damage the system.

Whenever a command name is encountered, the namespace facility checks to see if the current namespace context is enforced. If it is not, the usual name resolution rules are carried out. If it is, the namespace facility executes the following command in that context:

enforce_cmd name

If this procedure returns an error, access to that command is denied. If it returns a null string, name resolution continues according to the usual rules. Otherwise, it should return the same string name, or the name of another command to be substituted in its place. This procedure is only invoked the first time a command name is encountered. The results are cached in the name resolution tables, so performance is not adversely affected.

Variable references are handled the same way, except that the following command is invoked to resolve the reference:

enforce_var name

Note that enforcement is carried out before any of the usual name resolution rules come into play. Because of this, even absolute references like "::exec" or "::counter::next" can be intercepted and dealt with.

Because the enforcement procedures apply to all of the command/variable references in a namespace, it can be difficult to define procedures in an enforced namespace and have them work correctly. If you deny access to the "proc" command, for example, you will not be able to define any procedures in the namespace. To avoid problems like this, it is usually better to use enforced namespaces as follows. Set up a namespace containing the enforce_cmd and enforce_var procedures, along with any other code needed to enforce the namespace. Within that namespace, include a child namespace that is empty, but has enforcement turned on. Commands can be fed to the child namespace, which will automatically look to its parent for the enforcement procedures and all other commands/variables. Procedures may be referenced from the child, but they will actually execute in the parent namespace, which is not enforced.

In the following example, a "safe" namespace is constructed which will interpret any command string, but will guard access to commands like "exec" and "open" which are considered harmful. Calls to "exec" are intercepted and sent to "safe_exec" for execution. This logs the offending command in a file "security.log" and returns the null string. Calls to "open" are intercepted and sent to "safe_open". This allows read access to ordinary files, but blocks write operations and execution of processes. Note that the interception and redirection of commands happens only when commands are interpreted in the namespace "safe::isolated". In procedures like "safe_exec" and "safe_open", which are interpreted in namespace "safe", access to "exec" and "open" is allowed.

namespace safe {

proc interpret {cmds} {
namespace isolated $cmds
}

proc safe_exec {args} {
set mesg "access denied: $args" puts stderr $mesg
catch {
set fid [open "security.log" a] puts $fid $mesg
close $fid

}
}

proc safe_open {args} {
set file [lindex $args 0]
if {[string match |* $file]} { error "cannot open process: $file" }
set access [lindex $args 1]
if {$access == "r"} {
return [eval open $args]
}
error "cannot open with write access: [lindex $args 0]" }

proc enforce_cmd {name} {
global commands
if {[info exists commands($name)]} { return $commands($name)
}
return $name
}
set commands(exec) safe_exec
set commands(::exec) safe_exec
set commands(open) safe_open
set commands(::open) safe_open

proc enforce_var {name} {
if {[string match *::* $name]} { error "variable access denied: $name" }
return $name
}

namespace isolated -local -enforced yes }

#
# Use this to interpret a nasty script: #
safe::interpret {
#
# Files can be read but not written. #
set cmd {
set fid [open "/etc/passwd" r] set info [read $fid]
close $fid
}
puts "read: [catch $cmd result] => $result"

set cmd {

set fid [open "$env(HOME)/.cshrc" w] puts $fid "# ha! ha!
puts $fid "# make the user think his files have been erased" puts $fid "alias ls `echo "total 0"' close $fid
}
puts "write: [catch $cmd result] => $result"

#
# Kill all of the jobs we can find! #
set processInfo [lrange [split [exec ps -gx] 0 1 end] foreach line $processInfo {
set pid [lindex $line 0]
exec kill -9 $pid
}
}

KEYWORDS

delete, import, private, protected, public, variable


Table of Contents