Reputation: 4538
The code below is supposed to authenticate to an FTP server and check if write access is allowed. It runs fine whenever a single host is used.
import ftplib
import queue
import threading
def check_ftp(options):
host = options.get('host')
user = options.get('user')
passwd = options.get('passwd')
port = options.get('port')
# Attempt to connect and authenticate
ftp = ftplib.FTP()
try:
ftp.connect(host, port, timeout=5)
ftp.login(user, passwd)
print('{}:{} - Login successful.'.format(host, port))
except(ftplib.error_perm, OSError) as e:
print('{}:{} - Unable to connect/auth: {}'.format(host, port, e))
return
# Attempt to write & get dir listing
is_writable = False
contents = []
try:
test_folder = 'test_folder'
ftp.mkd(test_folder)
print('{}:{} - FTP root is writable'.format(host, port))
is_writable = True
ftp.retrlines('LIST', contents.append)
ftp.rmd(test_folder)
except ftplib.error_perm as e:
ftp.retrlines('LIST', contents.append)
print('{}:{} - Not writable: {}'.format(host, port, e))
ftp.quit()
class ModuleRunner(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# gets the options from the queue
options = self.queue.get()
# run the module
check_ftp(options)
# Let queue know the job is one
self.queue.task_done()
def run(thread_count):
options =[
{'host': 'ftp.uconn.edu',
'user': 'anonymous',
'passwd': '[email protected]',
'port': 21},
{'host': 'speedtest.tele2.net',
'user': 'anonymous',
'passwd': '[email protected]',
'port': 21},
{'host': 'test.talia.net',
'user': 'anonymous',
'passwd': '[email protected]',
'port': 21},
]
# Fill queue
q = queue.Queue()
for opt in options:
q.put(opt)
# Create a thread pool
threads = thread_count
for i in range(threads):
t = ModuleRunner(q)
t.setDaemon(True)
t.start()
q.join()
if __name__ == '__main__':
run(1)
However, if 03 or more hosts are added (see options variable), the script crashes and hangs every time, even when one thread is used. Errors:
ftp.uconn.edu:21 - Login successful.
ftp.uconn.edu:21 - Not writable: 550 test_folder: Permission denied
speedtest.tele2.net:21 - Login successful.
speedtest.tele2.net:21 - Not writable: 550 Permission denied.
test.talia.net:21 - Login successful.
Exception in thread Thread-1:
Traceback (most recent call last):
File "ftp.py", line 25, in check_ftp
ftp.mkd(test_folder)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 641, in mkd
resp = self.voidcmd('MKD ' + dirname)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 276, in voidcmd
return self.voidresp()
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 249, in voidresp
resp = self.getresp()
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 244, in getresp
raise error_perm(resp)
ftplib.error_perm: 550 test_folder: Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "ftp.py", line 47, in run
check_ftp(options)
File "ftp.py", line 31, in check_ftp
ftp.retrlines('LIST', contents.append)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 466, in retrlines
with self.transfercmd(cmd) as conn, \
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 397, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ftplib.py", line 359, in ntransfercmd
source_address=self.source_address)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 722, in create_connection
raise err
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 713, in create_connection
sock.connect(sa)
I don't understand why the socket times out, even when the checks run under one thread. Also, how can a thread be terminated if it hangs for too long. Thank you in advance.
Upvotes: 0
Views: 194
Reputation: 4820
First thing is does this work:
def run():
check_ftp(options[0])
check_ftp(options[1])
check_ftp(options[2])
I'm guessing no. It appears that the FTP object is not being cleaned up correctly. Using the with
operation should make it easier to close FTP. See this example
from ftplib import FTP
with FTP("ftp1.at.proftpd.org") as ftp:
ftp.login()
ftp.dir()
Also, threads cannot be cancelled, but processes can. Switching to the multiprocessing
library will allow them to be killed.
Upvotes: 1