Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No way to send stdin to running process. #72

Closed
rustyscottweber opened this issue Oct 7, 2015 · 20 comments
Closed

No way to send stdin to running process. #72

rustyscottweber opened this issue Oct 7, 2015 · 20 comments

Comments

@rustyscottweber
Copy link

I love what is here and would like to know more about this project to expand the capability of what pywinrm can do. Most importantly, I want to add the capability to send stdin to the running process. Can this be easily done? or is this impossible?

@pdunnigan
Copy link
Contributor

Can you be more specific?

What is the "running process"? A process triggered by winrm on the
windows server? Or an existing process started by something else?

Maybe a specific use case would help.

Thanks

rustyscottweber wrote:

I love what is here and would like to know more about this project to
expand the capability of what pywinrm can do. Most importantly, I want
to add the capability to send stdin to the running process. Can this
be easily done? or is this impossible?

@rustyscottweber
Copy link
Author

Specifically, I would like to send stdin to a process that was generated by win rm so that I can provide answers to the running process.

@rustyscottweber
Copy link
Author

I've specially been looking at the protocol object's _raw_get_command_output method to see if there was something there that would allow a string be sent to a process given a cmd id and console id. However, my knowledge of what is going on is quite limited.

@diyan
Copy link
Owner

diyan commented Oct 7, 2015

@rustyscottweber Looks like you are talking about expect and pexpect tools.

Agree, it's nice to manage stdin/sdout remotely but this should be implemented in another library on top of pywinrm.

IMO it's not in scope of pywinrm.

...but if someone would take this challenge and will ask for some changes in pywinrm - I would be happy to discuss.

Closing for now. Feel free to re-open.

@diyan diyan closed this as completed Oct 7, 2015
@rustyscottweber
Copy link
Author

I think you misunderstand. Currently, I am not attempting mirror the functionality of expect and pexpect in pywinrm. I am merely attempting to send input, preferably as a string, to a process that I am already managing remotely through pywinrm.


self.win_rm_session = winrm.Session(self.hostname,
                                                    (self.username, self.password))
self.protocol = self.win_rm_session.protocol
self.shell_id = self.protocol.open_shell()
self.command_id = self.protocol.run_command(self.shell_id, cmd)
command_done = False
while not command_done:
    # how do I send stdin?
    stdout, stderr, return_code, command_done = self.protocol._raw_get_command_output(self.shell_id, self.command_id)

@diyan diyan reopened this Oct 9, 2015
@diyan
Copy link
Owner

diyan commented Oct 9, 2015

@rustyscottweber Oh. I see. Basically you just want to send predefined stdin to a process without any conditions and smart interactions, correct? I'm re-opening this issue since functionaliy is not yet present in pywinrm. Need to think about implementation.

@rustyscottweber
Copy link
Author

Yes. That is exactly what I would like to accomplish. However, my knowledge of Windows rpc xml is limited. It would make sense that the _raw_get_command_output's stream variable would also take a stdin parameter to send to the process, but this is Windows and being that easy is against the rules.

@rustyscottweber
Copy link
Author

http://www.rubydoc.info/github/WinRb/WinRM/WinRM/WinRMWebService#write_stdin-instance_method
According to a simple google search, a method already exists in ruby's extensions for pywinrm.


def write_stdin(shell_id, command_id, stdin)
  # Signal the Command references to terminate (close stdout/stderr)
  body = {
    "#{NS_WIN_SHELL}:Send" => {
      "#{NS_WIN_SHELL}:Stream" => {
        "@Name" => 'stdin',
        "@CommandId" => command_id,
        :content! => Base64.encode64(stdin)
      }
    }
  }
  builder = Builder::XmlMarkup.new
  builder.instruct!(:xml, :encoding => 'UTF-8')
  builder.tag! :env, :Envelope, namespaces do |env|
    env.tag!(:env, :Header) { |h| h << Gyoku.xml(merge_headers(header,resource_uri_cmd,action_send,selector_shell_id(shell_id))) }
    env.tag!(:env, :Body) do |env_body|
      env_body << Gyoku.xml(body)
    end
  end
  resp = send_message(builder.target!)
  true
end

How this translates in python is not yet clear to me however.

@rustyscottweber
Copy link
Author

@rustyscottweber
Copy link
Author

What I currently have. However, this gives me


    def _send_to_input(self, shell_id, command_id, input, i_stream='stdin'):
        rq = {'env:Envelope': self._get_soap_header(
                                                    resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
                                                    action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send',
                                                    shell_id=shell_id)}
        stream = rq['env:Envelope'].setdefault('env:Body', {}).setdefault('rsp:Send', {}).setdefault('rsp:Stream', {})
        stream['@CommandId'] = command_id
        stream['@Name'] = i_stream
        stream['#text'] = input
        xml_out = xmltodict.unparse(rq)
        rs = self.send_message(xml_out)

However, I get the following message returned and I'm confused as to it's meaning.

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd" xml:lang="en-US">
<s:Header>
    <a:Action>http://schemas.dmtf.org/wbem/wsman/1/wsman/fault</a:Action>
    <a:MessageID>uuid:A49FC78A-BAFD-46CE-A0FC-706A7223A4E4</a:MessageID>
    <a:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:To>
    <a:RelatesTo>uuid:880fc38b-d48b-4482-a841-6ba8b65b6164</a:RelatesTo>
</s:Header>
<s:Body>
    <s:Fault>
        <s:Code>
            <s:Value>s:Receiver</s:Value>
            <s:Subcode>
                <s:Value>w:InternalError</s:Value>
            </s:Subcode>
        </s:Code>
        <s:Reason>
            <s:Text xml:lang="en-US">The data is invalid. </s:Text>
        </s:Reason>
        <s:Detail>
            <f:WSManFault xmlns:f="http://schemas.microsoft.com/wbem/wsman/1/wsmanfault" Code="13" Machine="windows-host">
            <f:Message>The data is invalid. </f:Message>
        </f:WSManFault>
    </s:Detail>
</s:Fault>
</s:Body>
</s:Envelope>

@rustyscottweber
Copy link
Author

well.. I have been stuck for a while on this and I'm still not sure on how to make stdin work... Any thoughts on implementation?

@megahall
Copy link

megahall commented Feb 8, 2017

@rustyscottweber Any update on this item?

@rustyscottweber
Copy link
Author

I got stuck on how exactly to format the XML.. Since there is no wsdl file to work from. I wasn't able to send a packet to winrm without it returning an XML error.

@megahall
Copy link

megahall commented Feb 9, 2017

Thanks for checking in. I was hoping there might be a way to do it as I needed it too. I'll post back if I can get it to work. This is definitely the most painful protocol I have ever seen.

@alv000h
Copy link

alv000h commented Jun 21, 2018

+1

@mj84
Copy link

mj84 commented Dec 19, 2018

Hey guys,
I just stumbled across this thread while trying to implement the exact same functionality and wanted to share my findings.

This is what I currently have (inside protocol.py):

def _send_to_stdin(self, command_id, input):
    req = {'env:Envelope': self._get_soap_header(
        resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',  # NOQA
        action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send',  # NOQA
        shell_id=self.shell_id)}
    stdin = req['env:Envelope'].setdefault('env:Body', {}).setdefault(
        'rsp:Send', {}).setdefault('rsp:Stream', {})
    stdin['@CommandId'] = command_id
    stdin['@Name'] = 'stdin'
    stdin['@xmlns:rsp'] = 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'
    input = (input + '\r\n').encode('ascii')
    stdin['#text'] = b64encode(input)
    print(xmltodict.unparse(req))
    res = self.send_message(xmltodict.unparse(req))
    print(res)

Most of the code is inspired by pywinrm's open_shell() and run_command() methods and https://msdn.microsoft.com/en-us/library/cc251742.aspx

I have abused _raw_get_command_output() to inspect the stdout and stderr streams of that process which is working fine.
So far, I am not seeing any error, but I can't see the content being passed to my running process which is a powershell.
I also enabled WinRM's activity log which shows that the Send request is being properly processed.

@mj84
Copy link

mj84 commented Dec 19, 2018

While digging around a bit further I found the following in Ansible's
ansible/lib/ansible/plugins/connection/winrm.py:
(see: https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/connection/winrm.py)

def _winrm_send_input(self, protocol, shell_id, command_id, stdin, eof=False):
    rq = {'env:Envelope': protocol._get_soap_header(
        resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
        action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send',
        shell_id=shell_id)}
    stream = rq['env:Envelope'].setdefault('env:Body', {}).setdefault('rsp:Send', {})\
        .setdefault('rsp:Stream', {})
    stream['@Name'] = 'stdin'
    stream['@CommandId'] = command_id
    stream['#text'] = base64.b64encode(to_bytes(stdin))
    if eof:
        stream['@End'] = 'true'
    protocol.send_message(xmltodict.unparse(rq))

@mj84
Copy link

mj84 commented Dec 19, 2018

Sorry for spamming this thread, but I discovered something.
It seems that my problems are due to the fact that I tried to run a powershell command inside the shell.
I tried running the del /p d:\temp\out.txt command from Microsoft's example and then later successfully sent y\r\n to that command and the file vanished.

EDIT
I got it working successfully with powershell now.
When launching powershell, it must be instructed to read Commands from stdin like this:
self.run_command(self.shell_id, 'powershell -Command -')

@rustyscottweber
Copy link
Author

I don't speak for every one, but I for one don't mind spamming a thread so long as it is full of useful information. Let me take a look at what ansible has. Thank you for digging that up.

@jborean93
Copy link
Collaborator

Implemented with #271

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants