Overview | Index by: file name | procedure name | procedure call | annotation
pty.tcl (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/>.
#

#//#
# Execute an external system command on its own PTY.
#
# This is apropriate for executiong external commands that require a controling
# terminal, such as vi, more, less, and telnet from busybox. 'ptyexec' does not
# return until command has finished, but while it is executing the event loop
# is served. The external application can be forced to terminate prematurely.
#//#

namespace eval pty {
namespace export ptyexec ptykill ptyresize

variable term 0
variable busy 0
    
# Execute an external program like "exec" does, but in its own PTY.
# Some interactive programs like 'vi', 'more', 'less', 'telnet', 'sh' assume that they are
# connected on a pty. If this is not the case (like when using "exec"), they malfunction.
#
# The external command takes control of Standard I/O of the session.
#
# Execution can be interrupted by the user with the break key sequence: Ctrl ^ + x/
# This is enforced in session.tcl by calling ptykill (see below).
#
# It supports the syntax of 'bgexec' and also:
#   -root : Execute without root priviledges.
#
# @param args Optional parameters, the program to execute, and arguments to the executable
# @return Nothing if not error occurs, otherwise the error (and options).
# @error
proc ptyexec {args} {
    variable term 0
    variable busy
    
    # XXX -root & --
    if {[lindex $args 0] eq {-root}} {
        set args [lrange $args 1 end]
    }
    
    if {$busy} {
        error "there is an on-going ptyexec session for this thread; only one is allowed per session at any time"
    }

    set stdin_e [fileevent stdin readable]
    fileevent stdin readable {}
    
    set stdin_s [fconfigure stdin]
    set stdout_s [fconfigure stdout]
    set stderr_s [fconfigure stderr]
    
    fconfigure stdin -buffering none -blocking 1 -translation auto
    fconfigure stdout -buffering line -blocking 1 -translation auto
    fconfigure stderr -buffering line -blocking 1 -translation auto
    
    set busy 1
    catch {
    bgexec ::pty::term pty {*}$args >@ stdout <@ stdin 2>@ stderr
    } result options
    set busy 0

    fconfigure stdin {*}$stdin_s
    fconfigure stdout {*}$stdout_s
    fconfigure stderr {*}$stderr_s
    
    fileevent stdin readable $stdin_e
    
    # Ignore error if we forced termination.
    # The error in this case is always: Child process terminated unexpectedly
    if {[lindex $term 0] eq {KILLED}} return
    
    return -options $options $result
}

# Kill an ongoing ptyexec execution.
proc ptykill {} {
    variable term 1
}

# Resize the window size of the PTY of an ongoing ptyexec session.
proc ptyresize {} {
    # XXX
}

} ;# Namespace ends


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