Page 1 of 1

[Solved] Processes opened for update won't respond to data written to them

Posted: Sat Jan 05, 2019 2:24 am
by speedbump1981
I've been working on a field that behaves like a terminal emulator, but it's only half-working. When you press the Return key, whatever command you've typed is supposed to be written to the user's shell. So far my implementation sort-of works, but any commands I send just seem to disappear into a void. The shell doesn't respond at all, or at least that's how it seems.

I also used SSH and TELNET with what appeared to be a functioning terminal, but the same problem with processes not responding to written commands is still present. I can connect and login to my Raspberry Pi, but I can't execute commands once I'm successfully logged in. I can connect to my POP3 server, but I can't seem to send the commands to let me interact with it.

I'm scratching my head on this one, and I think it has something to do with "open process" being a Psudo-Terminal? IDK, but I would love some perspective on this. Please let me know if you have any questions, tips, criticisms. Thank you :)

Testing:
  1. Create a new scrolling field called "terminal", in a new blank stack.
  2. Set the script of field "terminal" to...

    Code: Select all

    global _Terminal
    
    
    ### Monitor for data to write to the command
    on returnInField
        ### Collect the data to send
        put the lineIndex of selectedChunk into _Terminal["send_line"]
        put line _Terminal["send_line"] of field "terminal" into _Terminal["send_line_data"]
        put _Terminal["send_line_data"] &cr after _Terminal["output"]
        
        ### Internal Commands
        switch _Terminal["send_line_data"]
            case "termDisconnect"
                termDisconnect
                pass returnInField
                break
            case "clear"
                put empty into _Terminal["output"]
                put empty into fld "terminal"
                focus field "terminal"
                pass returnInField
                break
        end switch
        
        
        ### Custom Command
        if ( word 1 of _Terminal["send_line_data"] ) is "termConnect" then
            termOpen ( char 13 to -1 of _Terminal["send_line_data"] )
            pass returnInField
        end if
        
        ### Send the command
        termCommand
        
        
        pass returnInField
    end returnInField
    
    
    
    command termCommand
        ### If disconnected, connect to the Shell
        if _Terminal["command"] is empty then termOpen ( the shellCommand )
        
        ### Do we need to actually send anything?
        if _Terminal["send_line_data"] is not empty then
            
            ### Remove anything prefixed from the Terminal Output
            if _Terminal["prefix"] is not empty then replace _Terminal["prefix"] with "" in _Terminal["send_line_data"]
            
            ### Remember this command so we can arrow through it.
            put _Terminal["send_line_data"] &cr after _Terminal["send_history"]
            
            ### outputLineEndings
            local tOutputEnd
            put outputLineEndings into tOutputEnd
            
            termComment ( "Writing" && quote & _Terminal["send_line_data"] & quote )
            write _Terminal["send_line_data"] to process _Terminal["command"]
            
            ### Update the Output
            ### There has to be a better way to do this
            if it is not empty then
                put it after _Terminal["output"]
                termFocus
            end if
            
            ### Check for errors
            switch the result
                case "eof"
                    ### Command is closed
                    termComment "end of file"
                    termDisconnect
                    exit termCommand
                    break
                    
                case "process is not open for read"
                    ### Command is closed
                    termComment "process is not open for read"
                    termDisconnect
                    exit termCommand
                    break
                    
                case not empty
                    ### Unknown result
                    termComment ( "result:" && the result )
                    break
            end switch
            
        end if
    end termCommand
    
    
    
    ### termOpen "/bin/bash"
    ### termOpen "/bin/sh"
    command termOpen tCommand
        ### Windows only?
        set hideConsoleWindows to false
        
        ### New Command Data
        put tCommand into _Terminal["command"]
        
        ### Open the Command
        termComment "Opening"
        open process _Terminal["command"] for text update
        
        if it is not empty then
            put it after _Terminal["output"]
            termFocus
        end if
        
        ### Check for errors
        switch the result
            case "eof"
                ### Command is closed
                termComment "end of file"
                termDisconnect
                exit termOpen
                break
                
            case "process is not open for read"
                ### Command is closed
                termComment "process is not open for read"
                termDisconnect
                exit termOpen
                break
                
            case not empty
                ### Unknown result
                termComment ( the result )
                break
        end switch
        
        ### Start polling automatically
        termQueue
    end termOpen
    
    
    
    command termQueue
        if _Terminal["command"] is not empty then
            ### Remove Extra messages
            termPrune
            
            ### Send newest message
            send "termPoll" to me in 500 milliseconds
            put the result into _Terminal["poll_id"]
        else
            termComment "termQueue issued but nothing is open"
        end if
    end termQueue
    
    
    command termPrune
        ### Check for extra polling commands to remove
        local tPendingIDs
        put pendingMessages() into tPendingIDs
        filter lines of tPendingIDs with "termPoll"
        if ( number of lines in tPendingIDs ) > 0 then
            
            ### There they are! Get 'em!
            set itemDelimiter to comma
            repeat with x = 1 to the number of lines in tPendingIDs
                cancel ( item 1 of line x of tPendingIDs )
            end repeat
        end if
    end termPrune
    
    
    
    command termPoll
        ### Get any new information from the process
        ### blocking: linefeed, OEF, end
        read from process _Terminal["command"] at 0 until empty
        
        ### Update the Output
        ### There has to be a better way to do this
        if it is not empty then
            put it &cr after _Terminal["output"]
            termFocus
        end if
        
        ### Check for errors
        switch the result
            case "eof"
                ### Command is closed
                termComment "end of file"
                termDisconnect
                exit termPoll
                break
                
            case "process is not open for read"
                ### Command is closed
                termComment "process is not open for read"
                termDisconnect
                exit termPoll
                break
                
            case not empty
                ### Unknown result
                termComment ( the result )
                break
        end switch
        
        ### Start polling
        termQueue
    end termPoll
    
    
    
    ### termComment "Process is no longer open"
    ##! Add a Verbose flag to turn this off
    command termComment tComment
        if _Terminal["command"] is empty then
            put ( "[terminal]" && tComment &cr ) after _Terminal["output"]
        else
            put ( "[terminal]" && word 1 of _Terminal["command"] & ":" && tComment &cr ) after _Terminal["output"]
        end if
        
        put _Terminal["output"] into field "terminal"
        
        termFocus
    end termComment
    
    
    
    command termDisconnect tKill
        termPrune
        
        close process _Terminal["command"]
        put empty into _Terminal["command"]
        
        if tKill is true then delete global _Terminal
        
        termFocus
    end termDisconnect
    
    
    
    command termFocus
        lock screen
        
        put _Terminal["output"] into field "terminal"
        put charIndex of the last char of field "terminal" into _Terminal["prefix_end"]
        put charIndex of the last line of field "terminal" into _Terminal["prefix_start"]
        
        put _Terminal["prefix_end"] - _Terminal["prefix_start"] into _Terminal["prefix_width"]
        put char _Terminal["prefix_start"] to _Terminal["prefix_end"] of field "terminal" into _Terminal["prefix"]
        
        select the last char of field "terminal"
        
        unlock screen
    end termFocus
    
  3. Type any command to send it to your user shell. I usually just use simple stuff like "ls", "cd", or "uname -a".
  4. You can also connect directly to a process with termConnect. For example...

    Code: Select all

    termConnect ssh -tt user@server
    termConnect telnet mail.server 110
    termConnect uname -a
    Direct connections is where I typically see the most activity from processes, but usually in terms of a connection response. For example, SSH to my Raspberry Pi resulted in a Credentials system dialog being presented, and my POP3 client connection was actively kicked by my server for inactivity and I received a full response. So again, it sort-of works.
  5. termDisconnect is the current way to disconnect from the open process, but sometimes it doesn't work. It also doesn't support concurrent connections, so it's totally possible to have several processes open by accident. I usually have the msg box open for performing manual disconnections. :wink:

    Code: Select all

    termDisconnect

[Solved] Re: Processes opened for update won't respond to data written to them

Posted: Mon Jan 07, 2019 9:12 pm
by speedbump1981
It looks like I was missing a Control Operator, in this case a linefeed at the end of the written command. So my write command now looks like this:

Code: Select all

write _Terminal["send_line_data"] & linefeed to process _Terminal["command"]
I went ahead and uploaded my example, Terminal-ish, to the Sample Stacks area if anyone would like to mess around with it.

Edit: Works on Linux Mint 19, but not on OS X Mojave.