Jump to content

StdInWrite send an incorrect blank value [Solved]


Go to solution Solved by KaFu,

Recommended Posts

Hello,

I am working on a script to automate the nginx certificate renewal.

I need to work on a kind of command prompt to do this so I work with STDOut and and STDIn.

The two first answers are working just fine. The third seems to send a blank value so the script failed to proceed to the next answer.
The two first need only to send one character but the third need to send a word so it could be the issue.

Do you have any idea what could cause this issue ?
StdInWrite is supposed to be able to send a word (with only standard ASCII character) without any problem so I don't understand.

 

#include <Constants.au3>
#include <AutoItConstants.au3>
#include <MsgBoxConstants.au3>
#include <InetConstants.au3>
#include <WinAPIFiles.au3>
#include <StringConstants.au3>

;Pour renouveler le certificat nginx
Global $intPID = 0, _
$strSTDOutRead = ""

$intPID = Run("C:\nginx\win-acme.v2" & "\wacs.exe", "", @SW_SHOW, $STDIN_CHILD + $STDOUT_CHILD)
If $intPID = 0 Then
    ConsoleWrite("Error while running the application '" & "wacs.exe" & "'. Error: " & @error & @CRLF)
Else
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Please choose from the menu:") Then
            StdinWrite($intPID, "M") ; Full options
            StdinWrite($intPID)
            ExitLoop
        EndIf

    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "How shall we determine the domain(s) to include in the certificate?:") Then
            StdinWrite($intPID, "2") ; Manual input
            StdinWrite($intPID)
            ExitLoop
        EndIf

    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Host:") Then
            StdinWrite($intPID, "*.repl-aced.com") ; Domain name
            StdinWrite($intPID)
            ExitLoop
        EndIf
    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "to accept or type an alternative:") Then
            StdinWrite($intPID, "{ENTER}") ; Accept
            StdinWrite($intPID)
            ConsoleWrite("Test6")
            ExitLoop
        EndIf
            MsgBox($MB_ICONINFORMATION, "", $strSTDOutRead)
            Return
    WEnd
EndIf

 

Edited by ChessMate
Link to post
Share on other sites

you can try using some sleeps in there.  Just bc your code starts reading the stdOut buffer (or tries to ) that doesn't mean that it has been released by the sending process. 

The right way to do it is to use a mutex and lock the std handle object and wait for it to actually locks (it won't if the other process has control of it). There should be a mutexwait function if not I know theres a waitforobject function in the win api udf.  The mutex wont lock until the handle returns the free signal.  That stdoutread just looks at the stdout buffer if its empty it leaves.  You looking may actually be preventing the write on the other end.  It really just depends on what stdout() is doing exactly.  A mutex is pretty much the standard for dealing with shared objects. Well not around here but everywhere else.   

Edited by markyrocks
Link to post
Share on other sites

I have had a hard time understanding what is a muttex and how it works and using Sleep even for 10 seconds doesn't work to fix this.

The StdInWrite($intPID) without paratmeter is supposed to forcefully close the current STDinWrite stream so the sending process is supposed to be stopped.

I have tried this and _WinAPI_WaitForSingleObject but both these WinAPI functions return :

While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Host:") Then
            _WinAPI_CreateMutex($intPID)
            StdinWrite($intPID, "*.repl-aced.com") ; Domain name
            StdinWrite($intPID)
            ExitLoop
        EndIf

    WEnd


image.png.8a0d2549899d3eafa45da4c77bd2bdf8.png

As WinAPIFiles.au3 is included, I don't understand the issue.
  

 

Edited by ChessMate
Link to post
Share on other sites

You were right, the StdOutRead monopolize the connexion.
 

I manage to use _Singleton to check it.

However, how am I supposed to kill the StdOutRead process to let the StdInWrite answer ?
Something must replace this Sleep(1000) but ProcessClose($strSTDOutRead) for example doesn't work.
 

While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Host:") Then
            While(_Singleton ( $intPID , 1 )=0)
              Sleep(1000)
            WEnd
            StdinWrite($intPID)
            StdinWrite($intPID, "*.repl-aced.com") ; Domain name
            StdinWrite($intPID)
            ConsoleWrite("Test3")
            ExitLoop
        EndIf

    WEnd

 

Link to post
Share on other sites
  • Solution

"StdoutRead() does not block, it will return immediately."

Try adding " & @LF" to the end of your StdinWrite() calls, as this does normally trigger execution.

 

StdinWrite(PID) > maybe remove these calls? If the stream is closed, it's gone.

"If the function is called with no second argument, StdinWrite() closes the stream and invalidates it for further writing."

Edited by KaFu
Link to post
Share on other sites

It worked, thank you KaFu:

Just to better understand the issue, why was 

StdinWrite($intPID, "2")

working and

StdinWrite($intPID, "*.repl-aced.com")

not working when they were both after a StdInWrite without arguments ?

Here is the functional code :

 

#include <Constants.au3>
#include <AutoItConstants.au3>
#include <MsgBoxConstants.au3>
#include <InetConstants.au3>
#include <WinAPIFiles.au3>
#include <StringConstants.au3>
#include <Misc.au3>

;Pour renouveler le certificat nginx
Global $intPID = 0, _
$strSTDOutRead = ""

$intPID = Run("C:\nginx\win-acme.v2" & "\wacs.exe", "", @SW_SHOW, $STDIN_CHILD + $STDOUT_CHILD)
If $intPID = 0 Then
    ConsoleWrite("Error while running the application '" & "wacs.exe" & "'. Error: " & @error & @CRLF)
 Else
    $SintPID=_Singleton($intPID ,1)
      If $SintPID = 0 Then
      MsgBox($MB_SYSTEMMODAL, "Warning", "An occurence of test is already running")
      Exit
      EndIf
      MsgBox($MB_SYSTEMMODAL, "OK", "the first occurence of test is running")

    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Please choose from the menu:") Then
            StdinWrite($intPID, "M"& @LF) ; Full options
            ConsoleWrite("Test1 ")
            ExitLoop
        EndIf

    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "How shall we determine the domain(s) to include in the certificate?:") Then
            ;_WinAPI_CreateMutex($intPID)
            StdinWrite($intPID, "2"& @LF) ; Manual input
            ConsoleWrite("Test2 ")
            ExitLoop
        EndIf

    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "Host:") Then
            StdinWrite($intPID, "*.repl-aced.com"& @LF) ; Domain name
            ConsoleWrite("Test3")
            ExitLoop
        EndIf

    WEnd
    While 1
        $strSTDOutRead &= StdoutRead($intPID)
        If @error Then ExitLoop
        If StringInStr($strSTDOutRead, "to accept or type an alternative:") Then
            StdinWrite($intPID, "{ENTER}"& @LF) ; Accept
            ConsoleWrite("Test4")
            ExitLoop
         EndIf
            MsgBox($MB_ICONINFORMATION, "", $strSTDOutRead)
            Return

    WEnd
EndIf

 

 

Link to post
Share on other sites

https://www.autoitscript.com/autoit3/docs/libfunctions/_WinAPI_GetStdHandle.htm

gets the handle to the specific stream.  

https://www.autoitscript.com/autoit3/docs/libfunctions/_WinAPI_WaitForSingleObject.htm

wait for the handle to signal released. 

https://www.autoitscript.com/autoit3/docs/libfunctions/_WinAPI_CreateMutex.htm

you can optionally create a mutex around the attempts to read the stream bc you may be interupted by the sender.

when you'r done call mutex release close the mutex handle. This is pretty basic stuff in the world of multithreaded programming.  I've used it in autoit b4 so i know it works.  They don't exactly make it easy, its more straight forward in other languages.

Link to post
Share on other sites
1 hour ago, ChessMate said:

Just to better understand the issue, why was 



StdinWrite($intPID, "2")

working and



StdinWrite($intPID, "*.repl-aced.com")

not working when they were both after a StdInWrite without arguments ?

 

You're sure it really worked? Normally without a @LF (which is like hitting enter on the console) no command is executed. Maybe the @errors were triggered (no data) and that's why those loops were exited.

You have to give the server process time to execute and answer, so normally I would first loop idly in StdoutRead() to receive an answer at all (maybe with a time-out), and then only exist the loop once data was received and no more is coming. Otherwise you might issue commands too fast for the server to respond properly.

Edited by KaFu
Link to post
Share on other sites
On 1/14/2022 at 3:05 PM, KaFu said:

You're sure it really worked? Normally without a @LF (which is like hitting enter on the console) no command is executed. Maybe the @errors were triggered (no data) and that's why those loops were exited.

You have to give the server process time to execute and answer, so normally I would first loop idly in StdoutRead() to receive an answer at all (maybe with a time-out), and then only exist the loop once data was received and no more is coming. Otherwise you might issue commands too fast for the server to respond properly.

The two first questions were directly following each other in the prompt, not the third so I guess based on your answer that the interval between the two first is short enough to be shorter than the interval between the inputs.

StdinWrite($intPID) probably managed to send a kind of enter signal too, even if it was not the proper way to realize it.
 

Link to post
Share on other sites
  • ChessMate changed the title to StdInWrite send an incorrect blank value [Solved]

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...