Popen.subprocess in threads

197 views Asked by At

I have a client server code in python, wherein the client queries the server about a process running on the server and provides the memory threshold. If the process consumes more memory than the threshold,the server kills the process and restarts it. The problem is since I am creating a new thread for each client, the thread after servicing the client should terminate itself, releasing the connections and restart the killed process. When I am running only one thread(i.e. ONLY ONE CLIENT),everything works fine. But when I am running multiple clients,only one of them releases the connection and restarts the process while the other threads only restart the process but do not connections. I am calling the Popen.subprocess() module inside the threads. Is that a reason or there is some other reason for that?

Here is my code:

from thread import *
import threading
import time
import psutil
import itertools
import ctypes
import string
import os
import sys
import socket
import subprocess
from datetime import datetime

def drives():
    drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
    return list(itertools.compress(string.ascii_uppercase,map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))

t=0
c=drives()
o=[]
while(t<len(c)):
    o.append(str(c[t]+':\\'))
    t=t+1


class procThread(threading.Thread):
    def __init__(self,conn,addr):
        threading.Thread.__init__(self)
        self.conn=conn
        self.addr=addr


    def run(self): # prints the process's info which match the keyword....... SERVER SIDE
    self.conn.send('Welcome to the server\n')
    name=[]
    global o
    m=0
    k=0
    self.conn.send("Enter the key...\n") # for authentication purposes...e.g.here the key is 1
    t=self.conn.recv(8)
    if(t!='1'):  #WRONG KEY....INVALID USER
        self.conn.send("\nInvalid key..teminating the connection.....ABORT\n")
        self.conn.close()
        else:
                fp=open("processlogs.txt","a")
        r=""
        self.conn.send("\nEnter the process keyword ..Press # @ the end ") # e.g. 'Sk' for Skype
        d=self.conn.recv(65536)
        while(d!='#'):
                    r=r+str(d)
            d=self.conn.recv(65536)
        for p in psutil.pids(): # iterates through all the pids of the processes obtained
                    try:
                        p1=psutil.Process(p)
            if(r in p1.name()):
                            p2=p1.get_memory_info()
                            t=p1.name()+' '
                            d=str(p)+' '
                            self.conn.send(d)# prints the pid of the process
                            self.conn.send(t),# prints the name of the process
                            d=str(p2[0]/(1024*1024))+' '
                            self.conn.send(d) # print memory in MB
                            self.conn.send('MB\t')
                            for connect in p1.connections(kind='tcp'):
                                d=str(connect.laddr[0])+' '
                self.conn.send(d) # prints ip
                                d=str(connect.laddr[1])+' '
                self.conn.send(d) # prints tcp ports                        
                    except psutil.AccessDenied: # won't show all the processes
                        pass
                    except psutil.NoSuchProcess:
                        pass
            else:
                        continue
        self.conn.send("    Enter the threshold...(in MB)(Press # at the end) ")
        d=""
        t=self.conn.recv(65536).decode('utf-8')
        while(t!='#'):
                    d=d+str(t)
                    t=self.conn.recv(65536).decode('utf-8')
                names=[]      # LIST OF PROCESSES TO BE KILLED...#A RECORD IS KEPT SO THAT THEY CAN BE RESTARTED......
                for p in psutil.pids():
                    try:
                        p1=psutil.Process(p)
                        if(r in p1.name()):
                            if((p2[0]/(1024*1024))>=int(d)):
                                fp.write(str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
                                fp.write("|")
                                fp.write(str(self.addr))
                                m=p1.name()
                                m=m.encode('ascii','ignore') # converting unicode object into string object
                                for l in o:
                                    f=0
                                    for root, dirs, files in os.walk(l): # walks through the entire file system of the Windows OS
                                        for name in files:
                                            if name==m:
                                                p1.kill()
                                                self.conn.send("     Finally the process is killed...... ")
                                                f=1
                                                self.conn.send(str(os.path.abspath(os.path.join(root,name))))
                                                fp.write("|")
                                                fp.write(str(os.path.abspath(os.path.join(root,name)))+'\n')
                                                names.append(os.path.abspath(os.path.join(root,name)))
                                                break
                                        if(f==1):
                                            break

                                    if(f==1):
                                        break

                    except psutil.AccessDenied:
                        pass
                    except psutil.NoSuchProcess:
                        pass
                    else:
                        continue
                self.conn.send("    Now the processes will be restarted after the connection is terminated......" )
                fp.close()
                self.conn.close()  # closes the connection...
                if(names):
                    for l in names:
                        subprocess.Popen(str(l))

class serverThread(threading.Thread): # Thread class for Server(FOR LISTENING)
    def __init__(self, threadID, name,):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
    def run(self):
        threadLock.acquire()
        host=raw_input("Enter the hostname.....")
        HOST=socket.gethostbyname(host)   
        PORT=input("Enter the port no......") # specific port available
        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print 'Socket created' 
        #Bind socket to local host and port
        try:
            s.bind((HOST, PORT))
        except socket.error as msg:
            print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
            sys.exit()
        print 'Socket bind complete'
        s.listen(10) # no of connections @ one time
        print self.name+' now listening'
        threadLock.release()
        while 1:
            conn, addr = s.accept() # connection gets established here..
            print 'Connected with ' + addr[0] + ':' + str(addr[1])
            conn.send('Thank you for connecting   ')
            procs=procThread(conn,addr)
            procs.start()
            #start_new_thread(proc_info,(conn,addr))
        s.close() 
print("WELCOME")
print(" server-client")
threadLock=threading.Lock()
thread1 =serverThread(1,"Server 1")
thread1.start() # starting the server thread...
1

There are 1 answers

0
Anand S Kumar On BEST ANSWER

The issue could be that some exception is getting thrown before the process can close the connection. You should try the below suggestion and check if that is the case.

You should make it a habit to write the code to close connections in a finally block, something like -

try:
    <code you want to execute , may even include the creation of connection, etc>
finally:
    conn.close()

This would make sure that even if there are some errors/exceptions thrown from the code before conn.close() is called, it is still executed. Also, a good practice to keep these kinds of sanitary code (cleanup code) at the end of the function/script , so that they are executed at end , though that may not always be applicable based on the requirement.