Python Run Code When Thread Finishes - python

I am currently writing a simple file server using Python and the Socket library. I have most of it finished, and now I'm writing a way to push an update to the server. Here's what I expect it to do:
Client Should Send New Python File To Server
Server Deletes File Previous Python File
Server Downloads Python File
Server Runs New Code
Server With Old Code Quits
As of right now, my program can do 1-3, but it still cannot run the new code and quit the program. Since the server program is about 100 lines long, here's a link to the pastebin with the code. If you want to use the uploader too, here it is.
Looking at my code, the problem either lies in my update code:
class updServer(Thread):
def __init__(self,s):
Thread.__init__(self)
self.s = s
def run(self):
os.remove(os.path.realpath(__file__))
fn = self.s.recv(2048)
with open(fn, 'wb') as f:
still_data = True
while still_data:
data = self.s.recv(2048)
if not data:
still_data = False
if data == '':
still_data = False
f.write(data)
f.flush()
os.fsync(f.fileno())
f.close()
global name_of_file
name_of_file = fn
return None
Or in the way that I'm trying to run the new program under it:
elif file_name_sent == b'to update server':
new_t = updServer(conn)
threads.append(new_t)
new_t.start()
global name_of_file
execfile(name_of_file)
os._exit(0)
Note: I have also tried to use os.command and subprocess.call, and I also know that python is on my PATH. Instructions for the uploader are here if you want them.

Related

How to quit script when Telebot sees a new message

I have two python scripts which I run using a batch script. Once one script is executed, the next script starts. I need one of the scripts, the Telebot script, to quit after recording a message in a .json file. Unfortunately, while the message does get recorded, I am having trouble quitting the script, meaning my second python file is not being executed.
import telebot
import json
import os
import sys
import time
with open('JSON_file.json') as f:
data = json.load(f)
group_chat_id = 'CHAT_ID_HERE'
hbot = telebot.TeleBot('BOT_TOKEN_HERE')
hbot.send_photo(chat_id = group_chat_id, caption = data, photo = open('C:/Users/.../image.jpg', 'rb'))
temp = ""
#hbot.message_handler(func=lambda message: True)
def get_input(message):
global temp
message_str = str(message.text)
message_str = message_str.strip()
temp = message_str
print(temp)
with open('JSON_file.json', 'w') as json_file:
json.dump(temp, json_file)
hbot.stop_polling()
if temp != "":
quit()
return
if __name__ == "__main__":
hbot.polling()
I run the code(s) using an executable .bat file, however, the code below does not quit once a message is received. Does anyone know why the quit() command does not stop my code?
Thanks in advance)
I'd recommend using sys.exit instead of quit: https://docs.python.org/3/library/sys.html#sys.exit
In python, we have an in-built quit() function which is used to exit a python program. When it encounters the quit() function in the system, it terminates the execution of the program completely.
It should not be used in production code and this function should only be used in the interpreter.
Source: https://pythonguides.com/python-exit-command/

Is Python (Flask) not closing the file correctly?

I’m new to python. I have a separate flask web application that reads the ‘log.txt’ file in real time (via AJAX)
The expected result is that it shows “message 1” and 5 seconds later shows “message 2”. Then I have the below scripts to write into the files:
When I make a standalone Python script the below works (I see the first message and 5 seconds later the second message):
f = sys.stdout = sys.stderr = open('static/staging/stdout/discovery.log', 'w')
print "Message 1"
f.close()
time.sleep(5)
f = sys.stdout = sys.stderr = open('static/log.txt', 'a')
print "Message 2"
f.close()
time.sleep(5)
When I put it as part of a web app, and the function Is call as below, they both appear together after 10 seconds and not one after the other.
#app.route('/mywriter/')
def myApp():
f = sys.stdout = sys.stderr = open('static/log.txt', 'w')
print "Message 1"
f.close()
time.sleep(5)
f = sys.stdout = sys.stderr = open('static/log.txt', 'a')
print "Message 2”
f.close()
time.sleep(5)
I must point out at this stage that I was using flush() and fsync() but from what I understand doing f.close() will do both of these anyway. Besides it works when I put it as a standalone script, but not when I put it into a method as part of a webapp.
Use the debugger in your IDE. Set a breakpoint at the first time.sleep(), and stepping through line by line you should be able to diagnose the issue much more exactly.

How to exit function with signal on Windows?

I have the following code written in Python 2.7 on Windows. I want to check for updates for the current python script and update it, if there is an update, with a new version through ftp server preserving the filename and then executing the new python script after terminating the current through the os.kill with SIGNTERM.
I went with the exit function approach but I read that in Windows this only works with the atexit library and default python exit methods. So I used a combination of the atexit.register() and the signal handler.
***necessary libraries***
filematch = 'test.py'
version = '0.0'
checkdir = os.path.abspath(".")
dircontent = os.listdir(checkdir)
r = StringIO()
def exithandler():
try:
try:
if filematch in dircontent:
os.remove(checkdir + '\\' + filematch)
except Exception as e:
print e
ftp = FTP(ip address)
ftp.login(username, password)
ftp.cwd('/Test')
for filename in ftp.nlst(filematch):
fhandle = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, fhandle.write)
fhandle.close()
subprocess.Popen([sys.executable, "test.py"])
print 'Test file successfully updated.'
except Exception as e:
print e
ftp = FTP(ip address)
ftp.login(username, password)
ftp.cwd('/Test')
ftp.retrbinary('RETR version.txt', r.write)
if(r.getvalue() != version):
atexit.register(exithandler)
somepid = os.getpid()
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
os.kill(somepid, signal.SIGTERM)
print 'Successfully replaced and started the file'
Using the:
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
I get:
Traceback (most recent call last):
File "C:\Users\STiX\Desktop\Python Keylogger\test.py", line 50, in <module>
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
NameError: name 'SIGTERM' is not defined
But I get the job done without a problem except if I use the current code in a more complex script where the script give me the same error but terminates right away for some reason.
On the other hand though, if I use it the correct way, signal.SIGTERM, the process goes straight to termination and the exit function never executed. Why is that?
How can I make this work on Windows and get the outcome that I described above successfully?
What you are trying to do seems a bit complicated (and dangerous from an infosec-perspective ;-). I would suggest to handle the reload-file-when-updated part of the functionality be adding a controller class that imports the python script you have now as a module and, starts it and the reloads it when it is updated (based on a function return or other technique) - look this way for inspiration - https://stackoverflow.com/a/1517072/1010991
Edit - what about exe?
Another hacky technique for manipulating the file of the currently running program would be the shell ping trick. It can be used from all programming languages. The trick is to send a shell command that is not executed before after the calling process has terminated. Use ping to cause the delay and chain the other commands with &. For your use case it could be something like this:
import subprocess
subprocess.Popen("ping -n 2 -w 2000 1.1.1.1 > Nul & del hack.py & rename hack_temp.py hack.py & hack.py ", shell=True)
Edit 2 - Alternative solution to original question
Since python does not block write access to the currently running script an alternative concept to solve the original question would be:
import subprocess
print "hello"
a = open(__file__,"r")
running_script_as_string = a.read()
b = open(__file__,"w")
b.write(running_script_as_string)
b.write("\nprint 'updated version of hack'")
b.close()
subprocess.Popen("python hack.py")

Unblock a file in windows from a python script

Could I unblock a file in windows(7), which is automatically blocked by windows (downloaded from Internet) from a python script? A WindowsError is raised when such a file is encountered. I thought of catching this exception, and running a powershell script that goes something like:
Parameter Set: ByPath
Unblock-File [-Path] <String[]> [-Confirm] [-WhatIf] [ <CommonParameters>]
Parameter Set: ByLiteralPath
Unblock-File -LiteralPath <String[]> [-Confirm] [-WhatIf] [ <CommonParameters>]
I don't know powershell scripting. But if I had one I could call it from python. Could you folks help?
Yes, all you have to do is call the following command line from Python:
powershell.exe -Command Unblock-File -Path "c:\path\to\blocked file.ps1"
From this page about the Unblock-File command: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/unblock-file?view=powershell-7.2
Internally, the Unblock-File cmdlet removes the Zone.Identifier alternate data stream, which has a value of 3 to indicate that it was downloaded from the internet.
To remove an alternate data stream ads_name from a file path\to\file.ext, simply delete path\to\file.ext:ads_name:
try:
os.remove(your_file_path + ':Zone.Identifier')
except FileNotFoundError:
# The ADS did not exist, it was already unblocked or
# was never blocked in the first place
pass
# No need to open up a PowerShell subprocess!
(And similarly, to check if a file is blocked you can use os.path.isfile(your_file_path + ':Zone.Identifier'))
In a PowerShell script, you can use Unblock-File for this, or simply Remove-Item -Path $your_file_path':Zone.Identifier'.
Remove-Item also has a specific flag for alternate data streams: Remove-Item -Stream Zone.Identifier (which you can pipe in multiple files to, or a single -Path)
Late to the party . . . .
I have found that the Block status is simply an extra 'file' (stream) attached in NTFS and it can actually be accessed and somewhat manipulated by ordinary means. These are called Alternative Data Streams.
The ADS for file blocking (internet zone designation) is called ':Zone.Identifier' and contains, I think, some useful information:
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://www.google.com/
HostUrl=https://imgs.somewhere.com/product/1969297/some-pic.jpg
All the other info I have found says to just delete this extra stream.... But, personally, I want to keep this info.... So I tried changing the ZoneId to 0, but it still shows as Blocked in Windows File Properties.
I settled on moving it to another stream name so I can still find it later.
The below script originated from a more generic script called pyADS. I only care about deleting / changing the Zone.Identifier attached stream -- which can all be done with simple Python commands. So this is a stripped-down version. It has several really nice background references listed. I am currently running the latest Windows 10 and Python 3.8+; I make no guarantees this works on older versions.
import os
'''
References:
Accessing alternative data-streams of files on an NTFS volume https://www.codeproject.com/Articles/2670/Accessing-alternative-data-streams-of-files-on-an
Original ADS class (pyADS) https://github.com/RobinDavid/pyADS
SysInternal streams applet https://learn.microsoft.com/en-us/sysinternals/downloads/streams
Windows: killing the Zone.Identifier NTFS alternate data stream https://wiert.me/2011/11/25/windows-killing-the-zone-identifier-ntfs-alternate-data-stream-from-a-file-to-prevent-security-warning-popup/
Zone.Information https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
About URL Security Zones https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms537183(v=vs.85)?redirectedfrom=MSDN
GREAT info: How Windows Determines That the File.... http://woshub.com/how-windows-determines-that-the-file-has-been-downloaded-from-the-internet/
Dixin's Blog: Understanding File Blocking and Unblocking https://weblogs.asp.net/dixin/understanding-the-internet-file-blocking-and-unblocking
'''
class ADS2():
def __init__(self, filename):
self.filename = filename
def full_filename(self, stream):
return "%s:%s" % (self.filename, stream)
def add_stream_from_file(self, filename):
if os.path.exists(filename):
with open(filename, "rb") as f: content = f.read()
return self.add_stream_from_string(filename, content)
else:
print("Could not find file: {0}".format(filename))
return False
def add_stream_from_string(self, stream_name, bytes):
fullname = self.full_filename(os.path.basename(stream_name))
if os.path.exists(fullname):
print("Stream name already exists")
return False
else:
fd = open(fullname, "wb")
fd.write(bytes)
fd.close()
return True
def delete_stream(self, stream):
try:
os.remove(self.full_filename(stream))
return True
except:
return False
def get_stream_content(self, stream):
fd = open(self.full_filename(stream), "rb")
content = fd.read()
fd.close()
return content
def UnBlockFile(file, retainInfo=True):
ads = ADS2(file)
if zi := ads.get_stream_content("Zone.Identifier"):
ads.delete_stream("Zone.Identifier")
if retainInfo: ads.add_stream_from_string("Download.Info", zi)
### Usage:
from unblock_files import UnBlockFile
UnBlockFile(r"D:\Downloads\some-pic.jpg")
Before:
D:\downloads>dir /r
Volume in drive D is foo
Directory of D:\downloads
11/09/2021 10:05 AM 8 some-pic.jpg
126 some-pic.jpg:Zone.Identifier:$DATA
1 File(s) 8 bytes
D:\downloads>more <some-pic.jpg:Zone.Identifier:$DATA
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://www.google.com/
HostUrl=https://imgs.somewhere.com/product/1969297/some-pic.jpg
After:
D:\downloads>dir /r
Volume in drive D is foo
Directory of D:\downloads
11/09/2021 10:08 AM 8 some-pic.jpg
126 some-pic.jpg:Download.Info:$DATA
1 File(s) 8 bytes

Serial port does not work in rewritten Python code

I have a Python program that reads some parameters from an Arduino and stores it in a database. The serial port is set up and used like this:
ser = serial.Serial(port=port, baudrate=9600)
ser.write('*')
while 1 :
ser.write('*')
out = ''
# Let's wait one second before reading output (let's give device time to answer).
time.sleep(1)
while ser.inWaiting() > 0:
out += ser.read(1)
if out != '':
etc ... handling data
(The Arduino is set up so when it receives a star, it sends back a data string.) I would like to rewrite this as a daemon, so I am using the python-daemon library. In the init-part, I just define the port name, and then:
def run(self):
self.ser = serial.Serial(port=self.port,baudrate=9600)
while True:
self.ser.write('*')
out = ''
# Let's wait one second before reading output (give device time to answer).
time.sleep(1)
while self.ser.inWaiting() > 0:
out += self.ser.read(1)
if out != '':
etc ...
Everything is equal, except that I am now doing the serial handling within an App-object. The first version runs fine, when I try to run the latter, I get
File "storedaemon.py", line 89, in run
while self.ser.inWaiting() > 0:
File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 435, in inWaiting
s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
IOError: [Errno 9] Bad file descriptor
I am not able to see what has changed - except that I have tossed the code inside a new object. I have tried both to do the initialisation in init and in run, but I end up with the same result.
(The complete scripts are available at hhv3.sickel.net/b/storedata.py and hhv3.sickel.net/b/storedaemon.py.)
During the daemonization of your app, all file handlers are closed except stdin, stderr and stdout. This includes the connection to /dev/log, which then fails with the fd error (so it looks like this has nothing to do with the serial fd, but instead with the handler's socket).
You need either to add this FD to the exclusion list:
class App():
def __init__(self):
...
self.files_preserve = [handler.socket]
...
Or alternatively, set up the handler after the daemon process forked:
class App():
def run(self):
handler = logging.handlers.SysLogHandler(address = '/dev/log')
my_logger.addHandler(handler)
my_logger.debug(appname+': Starting up storedata')
...
Both version ran fine during my tests.

Categories

Resources