source: trunk/tcl/scripts/exec.tcl @ 827

Last change on this file since 827 was 765, checked in by dkearney, 16 years ago

added SIGTERM as the kill signal for rappture exec at steve's request

File size: 4.2 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: exec - simple way to exec with streaming output
3#
4#  This utility makes it easy to exec another tool and get streaming
5#  output from stdout and stderr.  Any stderr messages are specially
6#  encoded with the =RAPPTURE-ERROR=> tag, so they show up in red
7#  in the execution output.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require BLT
16
17namespace eval Rappture { # forward declaration }
18
19# ----------------------------------------------------------------------
20# USAGE: exec <arg> <arg>...
21#
22# This utility acts like the standard Tcl "exec" command, but it also
23# streams stdout and stderr messages while the command is executing,
24# instead of waiting till the end.  Any error messages are prefixed
25# with the special =RAPPTURE-ERROR=> tag, so they are recognized as
26# errors within Rappture.
27# ----------------------------------------------------------------------
28proc Rappture::exec {args} {
29    variable execout
30    variable execctl
31
32    set execout(output) ""
33    set execout(channel) ""
34    set execout(extra) ""
35    set execctl ""
36
37    set status [catch {eval blt::bgexec ::Rappture::execctl \
38        -keepnewline yes \
39        -killsignal SIGTERM \
40        -onoutput {{::Rappture::_exec_out stdout}} \
41        -onerror {{::Rappture::_exec_out stderr}} \
42        $args} result]
43
44    # add any extra stuff pending from the last stdout/stderr change
45    append execout(output) $execout(extra)
46
47    if {$status != 0} {
48        if {[regexp {^KILLED} $execctl] && [llength $execctl] == 4} {
49            set reason [lindex $execctl end]
50            set result "job killed: $reason"
51        }
52        error $result
53    }
54    return $execout(output)
55}
56
57# ----------------------------------------------------------------------
58# USAGE: _exec_out <channel> <message>
59#
60# Called automatically whenever output comes in from the Rappture::exec
61# utility.  Streams the output to stdout and adds it to the "execout"
62# variable, so it can be returned as one large string at the end of
63# the exec.
64# ----------------------------------------------------------------------
65proc Rappture::_exec_out {channel message} {
66    variable execout
67
68    #
69    # If this message is being written to the stderr channel, then
70    # add the =RAPPTURE-ERROR=> prefix to each line.
71    #
72    if {$channel == "stderr"} {
73        set newmesg ""
74        foreach line [split $message \n] {
75            append newmesg "=RAPPTURE-ERROR=>$line\n"
76        }
77        set message $newmesg
78    }
79
80    #
81    # If this message is coming in on the same channel as the
82    # last, then fine, add it on.  But if it's coming in on a
83    # different channel, we must make sure that we're at a good
84    # breakpoint.  If there's not a line break at the end of the
85    # current output, then add the extra stuff onto a buffer
86    # that we will merge in later once we get to a good point.
87    #
88    if {$execout(channel) == ""} {
89        set execout(channel) $channel
90    }
91
92    set ready [expr {[string length $execout(output)] == 0
93        || [string index $execout(output) end] == "\n"}]
94
95    if {$channel != $execout(channel)} {
96        if {$ready} {
97            # changing channels...
98            if {[string length $execout(extra)] > 0} {
99                # any extra stuff on this new channel? put it out now
100                puts -nonewline $execout(extra)
101                append execout(output) $execout(extra)
102                set execout(extra) ""
103            }
104            puts -nonewline $message
105            append execout(output) $message
106            set execout(channel) $channel
107        } else {
108            # not ready to change channels -- keep this for later
109            append execout(extra) $message
110        }
111    } else {
112        # no need to change channels -- keep printing
113        puts -nonewline $message
114        append execout(output) $message
115    }
116}
Note: See TracBrowser for help on using the repository browser.