Python: Subprocess communication fails and Terminal crashes - python

I am working on a Python script for automated smoke and unit testing on mobile devices. I use ios-deploy for the iOS solution. Because I try to kill the LLDB session before I terminate the test process, I use a pipe for communication between the processes. Here is a piece of code:
Pipe declaration:
_pipe_cmd_rcv, _pipe_cmd_snd = Pipe()
# Pipe to receive commands
self._pipe_cmd_rcv = _pipe_cmd_rcv
# Pipe to send commands
self._pipe_cmd_snd = _pipe_cmd_snd
The part where I send the exit command to LLDB, followed by a Y to confirm the exit:
self._pipe_cmd_snd.send("exit \n")
self._pipe_cmd_snd.send("Y \n")
And finally the part where I want to receive the input:
pcs = subprocess.Popen(cmd.split(), stdin=self._pipe_cmd_rcv, stdout=subprocess.PIPE, universal_newlines=True)
My intention is to send the exit command to the stdin of the process running LLDB, but unfortunately after the whole test process is finished, I can't use my Terminal anymore. If I type CTRL + C it returns the prompt, and when I hit enter, it pastes the prompt as input. It is like it is stuck in a loop. I have to open a new Terminal window to use it "the normal way". And this is not desired, because the script will be used to run on a CI system. Can anyone figure out what I am doing wrong?

You have spawned the process with input through self._pipe_cmd_rcv but you are trying to send the input to that process via self._pipe_cmd_snd, so it's blocking there for input. Following might help.
self._pipe_cmd_rcv.send("exit \n")
self._pipe_cmd_rcv.send("Y \n")

Related

How to capture stdout of shell after switching users

I'm making a shell with python. So far I have gotten cd to work (not pretty I know, but it's all I need for now). When I su root (for example) I get a root shell, but I can't capture the output I receive after running a command. However the shell does accept my commands, as when I type exit it exits. Is there a way to capture the output of a 'new' shell?
import os, subprocess
while True:
command = input("$ ")
if len(command.split(" ")) >= 2:
print(command.split(" ")[0]) #This line is for debugging
if command.split(" ")[0] == "cd" or command.split(" ")[1] == "cd":
os.chdir(command.split(" ")[command.split(" ").index("cd") + 1])
continue
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, universal_newlines=True)
output, error = process.communicate()
print(output.strip("\n"))
EDIT: To make my request a bit more precise, I'd like a way to authenticate as another user from a python script, basically catching the authentication, doing it in the background and then starting a new subprocess.
You really need to understand how subprocess.Popen works. This command executes a new sub-process (on a Unix machine, calls fork and then exec). The new sub-process is a separate process. Your code just calls communicate once and then discards of it.
If you just create a new shell by calling subprocess.Popen and then running su <user> inside of it, the shell will be closed right after that and the next time, you'll be running the command using the same (original) user again.
What you want is probably to create a single subprocess at the beginning of your application and then be a sort of a proxy between the user and the underlying process, and then just keep writing to its stdin and reading from stdout.
Here's an example:
import os, subprocess
process = subprocess.Popen(["bash"], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, universal_newlines=True)
while True:
command = input("$ ")
process.stdin.write(command + "\n")
process.stdin.flush()
output = process.stdout.readline()
print(output.strip("\n"))
(I removed the cd command parsing bit because it wasn't constructive to understanding the solution here, but you can definitely add specific handlers for specific inputs that wrap the underlying shell)

Wrting to and reading from a running subprocess

I would like to control adb (Android Debug Bridge) from a python script.
In order to do this I want to use the shell command from adb:
import subprocess as sp
adb = 'path-to-adb.exe'
print("Running shell command!")
p = sp.Popen([adb, 'shell'], stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
p.stdin.write('ls\r\n'.encode('utf-8'))
print(p.stdout.readlines().decode('utf-8'))
print('Do more stuff and eventually shut down.')
The idea is that I would write a command to the android shell, wait for the response then write another and so on... But whenever I call read() or readlines() on the running process it just does not return.
If however I call communicate() it works fine and returns the expected result. The problem with communicate() is that it ends the process.
I have looked at several questions here on Stackoverflow but here the answere always seems to be to wait the process to terminate (by either using communicate() or subprocess.run()). Am I missing something here? Am I just not supposed to interact with a running process?

Closing Plink window with Python

I want to communicate with a data-logger via Telnet. Therefore, I wrote the following python-script:
import subprocess
command ='plink.exe -telnet -P 23 12.17.46.06'
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1, shell=False)
answer = p.communicate('command')[0]
print answer
By running the script, a plink-windows pops up. The python script seems to wait for some action to be done inside the plink command window. By closing the window manually, the desired "answer" shows up inside python.
I am looking for a command / procedure to close plink directly out of python. It seems not to be sufficient to just close the subprocess, as in this case only the communication between python and plink gets closed and not the program plink.exe itself.
Any help is appreciated!
Regards, Phil
The documentation for the communicate() function says: Wait for process to terminate. Thus the function does not return until plink.exe exits and thus your program doesn't get the output until then.
You should add to your 'command' something that will close the telnet connection. When the far end closes the telnet connection plink.exe will exit and its window will close. If your telnet session runs a unix shell you could add '; exit' to your command.
You can check if your task within plink tunnel is complete and then execute
taskkill within your script
something like,
killProg=taskkill /f /fi "imagename eq plink.exe"
p.communicate('killProg')[0]
That will kill plink while keeping the tunnel open to execute other commands.

Python and subprocess input piping

I have a small script that launches and, every half hour, feeds a command to a java program (game server manager) as if the user was typing it. However, after reading documentation and experimenting, I can't figure out how I can get two things:
1) A version which allows the user to type commands into the terminal windoe and they will be sent to the server manager input just as the "save-all" command is.
2) A version which remains running, but sends any new input to the system itself, removing the need for a second terminal window. This one is actually half-happening right now as when something is typed, there is no visual feedback, but once the program is ended, it's clear the terminal has received the input. For example, a list of directory contents will be there if "dir" was typed while the program was running. This one is more for understanding than practicality.
Thanks for the help. Here's the script:
from time import sleep
import sys,os
import subprocess
# Launches the server with specified parameters, waits however
# long is specified in saveInterval, then saves the map.
# Edit the value after "saveInterval =" to desired number of minutes.
# Default is 30
saveInterval = 30
# Start the server. Substitute the launch command with whatever you please.
p = subprocess.Popen('java -Xmx1024M -Xms1024M -jar minecraft_server.jar',
shell=False,
stdin=subprocess.PIPE);
while(True):
sleep(saveInterval*60)
# Comment out these two lines if you want the save to happen silently.
p.stdin.write("say Backing up map...\n")
p.stdin.flush()
# Stop all other saves to prevent corruption.
p.stdin.write("save-off\n")
p.stdin.flush()
sleep(1)
# Perform save
p.stdin.write("save-all\n")
p.stdin.flush()
sleep(10)
# Allow other saves again.
p.stdin.write("save-on\n")
p.stdin.flush()
Replace your sleep() with a call to select((sys.stdin, ), (), (), saveInterval*60) -- that will have the same timeout but listens on stdin for user commands. When select says you have input, read a line from sys.stdin and feed it to your process. When select indicates a timeout, perform the "save" command that you're doing now.
It won't completely solve your problem, but you might find python's cmd module useful. It's a way of easily implementing an extensible command line loop (often called a REPL).
You can run the program using screen, then you can send the input to the specific screen session instead of to the program directly (if you are in Windows just install cygwin).

Retrieving Raw_Input from a system ran script

I'm using the OS.System command to call a python script.
example:
OS.System("call jython script.py")
In the script I'm calling, the following command is present:
x = raw_input("Waiting for input")
If I run script.py from the command line I can input data no problem, if I run it via the automated approach I get an EOFError. I've read in the past that this happens because the system expects a computer to be running it and therefore could never receive input data in this way.
So the question is how can I get python to wait for user input while being run in an automated way?
The problem is the way you run your child script. Since you use os.system() the script's input channel is closed immediately and the raw_input() prompt hits an EOF (end of file). And even if that didn't happen, you wouldn't have a way to actually send some input text to the child as I assume you'd want given that you are using raw_input().
You should use the subprocess module instead.
import subprocess
from subprocess import PIPE
p = subprocess.Popen(["jython", "script.py"], stdin=PIPE, stdout=PIPE)
print p.communicate("My input")
Your question is a bit unclear. What is the process calling your Python script and how is it being run? If the parent process has no standard input, the child won't have it either.

Categories