2

Got a problem thats being doing my head in for the last 2 days.

Im running a tcl script (for an eggdrop) which when triggered, executes a local shell command (child process) and if the command is succesfull it spits out the results. However, if the command is NOT succesful, i get an error "Tcl error [proc_ports]: child process exited abnormally:.

What i would like is to create a custom response, if the child process did not find any results.

The script is:

set chan "#help"
bind pub -|- .port proc_ports

proc proc_ports {nick host handle channel testes} {
    global chan
    if {"$chan" == "$channel"} {
        return 0
    }

    if [matchattr $nick |fmn $channel] {
        set ports [lindex $testes 0]
        set fp [ exec grep -w "$ports" scripts/ports | awk {{$1=""; print $0}} ]

        putserv "PRIVMSG $channel :Port \002$ports\002 is normally used for: \002$fp\002"

        return 1
    } else {
        putserv "PRIVMSG $channel :$nick, you do \002NOT\002 have access to this command!"
        return 1
    }
}

I would love to solve this using TCL, to help me learn more, rather than changing the exec into a shell script that would return any errors.

I have read up on the CATCH command in TCL, and have tried many different approaches to the script, however all have failed me :(

Any help would be appreciated.

Cheers.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Instronics
  • 79
  • 3
  • 6

2 Answers2

4
  1. You have HUGE security problem. 1a) Variable "testes" contains users TEXT. You considers that "testes" contains valid TCL list and use "lindex" on it. You should use at least command set ports [lindex [split $testes] 0] 1b) Before you send custom text to run in shell you should check to see if it contains illegal characters. Use string is, regexp, regsub.

  2. To check for an error in the execution of commands you can use the following code:

    set ports [lindex $testes 0]
    if { [catch {exec grep -w "$ports" scripts/ports | awk {{$1=""; print $0}}} fp] } {   
      putserv "PRIVMSG $channel :Something wrong while executing command."
    } {
      putserv "PRIVMSG $channel :Port \002$ports\002 is normally used for: \002$fp\002"
    }
    
Chpock
  • 682
  • 3
  • 9
  • I'd use `regexp {\w+} $testes ports` to grab the first word (testing its result to see if there was no match at all). It'll do the right thing in all cases that you really want to support. – Donal Fellows Feb 10 '13 at 13:08
  • While both solutions worked like a charm for me (thank you everyone), i would like to thank Chpock for pointing out the security issue involved. At the moment i am not TOO worried about it, since i am very careful in who has access to the eggdrop (flags fmn). But in general yes, I must implement what you have mentioned for security reasons, and will do so. Cheers. – Instronics Feb 10 '13 at 13:49
3

There's a few issues here. Firstly, exec produces that sort of error message when the pipeline it runs exits with a non-zero exit-code without writing to stderr. Secondly, grep has an exit-code of 1 when it doesn't find anything. These two features don't sit very well together!

The simplest fix would be to do this:

if {[catch {
    set fp [ exec grep -w "$ports" scripts/ports | awk {{$1=""; print $0}} ]
    putserv "PRIVMSG $channel :Port \002$ports\002 is normally used for: \002$fp\002"
}]} {
    putserv "PRIVMSG $channel :Port \002$ports\002 not in port database"
}

This works because catch yields a 1 as its result if an error occurred, and 0 if there was no error. We'll assume that all errors are a result of not finding anything (not a great idea, but convenient!) but if that bothers you, Tcl 8.6's try command is more discriminating.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • Generally speaking, I'd try to avoid doing the grep/awk each time round. Holding that mapping inside the running Tcl program (e.g., in an associative array) would be quicker and safer. But the code to do it is a more radical change, so I'll go with the minimal hack. – Donal Fellows Feb 10 '13 at 13:10