2

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?

martineau
  • 119,623
  • 25
  • 170
  • 301
  • `signal.SIGTERM` or just 15 should work – Artyer Jun 08 '17 at 15:47
  • @Artyer i used that as i mentioned above. The exit function never get executed using signal.SIGTERM for some reason – Neoptolemos Kyriakou Jun 08 '17 at 15:51
  • Windows doesn't have POSIX signals. The C runtime implements the 6 required for standard C within the current process, plus nonstandard `SIGBREAK`. Python's implementation of `os.kill` on Windows mashes together two completely unrelated functions that don't event target the same thing: `GenerateConsoleCtrlEvent` (to send a Ctrl+C or Ctrl+Break event to a process *group* that's attached to the current console) and `TerminateProcess` to immediately terminate an individual process (like `SIGKILL` on Unix). – Eryk Sun Jun 08 '17 at 20:31

1 Answers1

0

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")
Arne S
  • 1,004
  • 14
  • 41
  • hmm interesting but what if i compile the script into an exe? how am i going to reload the exe after fetching the new one from the ftp server? – Neoptolemos Kyriakou Jun 09 '17 at 11:24
  • That is another question entirely. See updated answer – Arne S Jun 09 '17 at 13:58
  • currently i'm between your suggestions and the `os.execl()`, since it replaces the current process with the new one. What is your thought? Which one will get the job done (especially for the exe file)? – Neoptolemos Kyriakou Jun 09 '17 at 15:29
  • os.execl seems promising as long as it wold be acceptable to have a new name for the updated .exe file. If you want to keep the same name you would probably have to use the ping trick or similar. – Arne S Jun 09 '17 at 15:41
  • @NeoptolemosKyriakou, a frozen executable extracts to a temp directory. Are you looking to replace the extracted script or the executable itself? The executable will still be running, so you have the same problem, for example, as trying to use pip.exe to upgrade itself. Windows doesn't allow unlinking a memory-mapped file (EXE, DLL, mapped data file). You can only rename it to another directory on the volume, and schedule some task to delete it after the program exits. – Eryk Sun Jun 09 '17 at 20:35
  • 1
    `os.execl` is almost never something you want to call on Windows. There is nothing like the Unix exec family of system calls in the Windows API, i.e. nothing to replace the contents of a process in place. This call is faked by the C runtime by spawning a new process (with a new PID) and exiting the current process. This signals every handle for the original process. For example, if cmd.exe was waiting for your program to exit, it will assume it's now the foreground process in the console. If your program also uses the console, this will be a mess. – Eryk Sun Jun 09 '17 at 20:57
  • @eryksun oh boy so i can't really replace a running frozen exe file with an updated one without killing it first right? What about the ping method that `Arne S` suggested, will it work? – Neoptolemos Kyriakou Jun 10 '17 at 09:51