tcl7.6 User Commands - namespace






NAME

     namespace - evaluate scripts in a particular namespace  con-
     text


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 con-
     tained within it.

     Commands and variables local to a namespace  can  be  refer-
     enced  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 vari-
     able name are separated by the "::" string.

     If a command, variable or namespace name is absolutely qual-
     ified  (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" com-
     mand.   This  deletes  all  commands  and  variables  in the
     namespace, and deletes all child namespaces as well.



EXAMPLE

     The following namespace implements a simple  counter  facil-
     ity:

         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 con-
     tains  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  inter-
     cepted,  and  the  usual lookup rules can be modified.  This
     supports the construction of "safe" namespaces, which inter-
     pret  code  from an untrusted source and deny access to com-
     mands which could damage the system.

     Whenever a command name is encountered, the namespace facil-
     ity  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 diffi-
     cult to define procedures in an enforced namespace and  have
     them  work correctly.  If you deny access to the "proc" com-
     mand, for example, you will not be able to define  any  pro-
     cedures  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  pro-
     cedures 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  con-
     sidered  harmful.   Calls to "exec" are intercepted and sent
     to "safe_exec" for execution.  This logs the offending  com-
     mand  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 inter-
     ception  and  redirection of commands happens only when com-
     mands 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