| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678 |
- #!/usr/bin/env python
- ################################################################################
- # $Id: tftornado.py 2459 2007-01-31 16:54:50Z b4rt $
- # $Revision: 2459 $
- # $Date: 2007-01-31 10:54:50 -0600 (Wed, 31 Jan 2007) $
- ################################################################################
- #
- # Written by Bram Cohen
- # see LICENSE.txt for license information
- #
- ################################################################################
- #
- # tftornado.py - use BitTornado with torrentflux-b4rt
- # http://tf-b4rt.berlios.de/
- #
- ################################################################################
- from BitTornado import PSYCO
- if PSYCO.psyco:
- try:
- import psyco
- assert psyco.__version__ >= 0x010100f0
- psyco.full()
- except:
- pass
- from BitTornado.download_bt1 import BT1Download, defaults, parse_params, get_usage, get_response
- from BitTornado.RawServer import RawServer, UPnP_ERROR
- from random import seed
- from socket import error as socketerror
- from BitTornado.bencode import bencode
- from BitTornado.natpunch import UPnP_test
- from threading import Event
- from os.path import abspath, isfile
- from os import getpid, remove
- from sys import argv, stdout
- import sys
- from sha import sha
- from time import strftime
- from BitTornado.clock import clock
- from BitTornado import createPeerID, version
- assert sys.version >= '2', "Install Python 2.0 or greater"
- try:
- True
- except:
- True = 1
- False = 0
- PROFILER = False
- if __debug__: LOGFILE=open(argv[3]+"."+str(getpid()),"w")
- def traceMsg(msg):
- try:
- if __debug__:
- LOGFILE.write(msg + "\n")
- LOGFILE.flush()
- except:
- return
- #------------------------------------------------------------------------------#
- # tfb static methods #
- #------------------------------------------------------------------------------#
- def fmttime(n):
- """ fmttime """
- # short format :
- return fmttimeshort(n)
- # long format :
- # return fmttimelong(n)
- def fmttimeshort(n):
- """ fmttimeshort """
- if n == 0:
- return 'complete!'
- try:
- n = int(n)
- assert n >= 0 and n < 5184000 # 60 days
- except:
- return '<unknown>'
- m, s = divmod(n, 60)
- h, m = divmod(m, 60)
- d, h = divmod(h, 24)
- if d >= 7:
- return '-'
- elif d > 0:
- return '%dd %02d:%02d:%02d' % (d, h, m, s)
- else:
- return '%02d:%02d:%02d' % (h, m, s)
- def fmttimelong(n):
- """ fmttimelong """
- if n == 0:
- return 'complete!'
- try:
- n = int(n)
- assert n >= 0 and n < 5184000 # 60 days
- except:
- return '<unknown>'
- m, s = divmod(n, 60)
- h, m = divmod(m, 60)
- d, h = divmod(h, 24)
- y, d = divmod(d, 365)
- dec, y = divmod(y, 10)
- cent, dec = divmod(dec, 10)
- if cent > 0:
- return '%dcent %ddec %dy %dd %02d:%02d:%02d' % (cent, dec, y, d, h, m, s)
- elif dec > 0:
- return '%ddec %dy %dd %02d:%02d:%02d' % (dec, y, d, h, m, s)
- elif y > 0:
- return '%dy %dd %02d:%02d:%02d' % (y, d, h, m, s)
- elif d > 0:
- return '%dd %02d:%02d:%02d' % (d, h, m, s)
- else:
- return '%02d:%02d:%02d' % (h, m, s)
- def transferLog(message, ts):
- """ transferLog """
- try:
- FILE = open(transferLogFile,"a+")
- if not ts:
- FILE.write(message)
- else:
- FILE.write(strftime('[%Y/%m/%d - %H:%M:%S]') + " " + message)
- FILE.flush()
- FILE.close()
- except Exception, e:
- sys.stderr.write("Failed to write log-file : " + transferLogFile + "\n")
- #------------------------------------------------------------------------------#
- # HeadlessDisplayer #
- #------------------------------------------------------------------------------#
- class HeadlessDisplayer:
- def __init__(self):
- self.done = False
- self.file = ''
- self.running = '1'
- self.percentDone = ''
- self.timeEst = 'Connecting to Peers'
- self.downloadTo = ''
- self.downRate = ''
- self.upRate = ''
- self.shareRating = ''
- self.percentShare = ''
- self.upTotal = 0
- self.downTotal = 0
- self.seedStatus = ''
- self.peerStatus = ''
- self.seeds = ''
- self.peers = ''
- self.errors = []
- self.last_update_time = -1
- self.autoShutdown = 'False'
- self.user = 'unknown'
- self.size = 0
- self.shareKill = '100'
- self.distcopy = ''
- self.stoppedAt = ''
- self.dow = None
- self.displayCounter = 0
- def finished(self):
- if __debug__: traceMsg('finished - begin')
- self.done = True
- self.percentDone = '100'
- self.timeEst = 'Download Succeeded!'
- self.downRate = ''
- self.display()
- if self.autoShutdown == 'True':
- self.upRate = ''
- if self.stoppedAt == '':
- self.writeStatus()
- if __debug__: traceMsg('finished - end - raising ki')
- raise KeyboardInterrupt
- if __debug__: traceMsg('finished - end')
- def failed(self):
- if __debug__: traceMsg('failed - begin')
- self.done = True
- self.percentDone = '0'
- self.timeEst = 'Download Failed!'
- self.downRate = ''
- self.display()
- if self.autoShutdown == 'True':
- self.upRate = ''
- if self.stoppedAt == '':
- self.writeStatus()
- if __debug__:traceMsg('failed - end - raising ki')
- raise KeyboardInterrupt
- if __debug__: traceMsg('failed - end')
- def error(self, errormsg):
- self.errors.append(errormsg)
- # log error
- transferLog("error: " + errormsg + "\n", True)
- def chooseFile(self, default, size, saveas, dir):
- self.file = '%s (%.1f MB)' % (default, float(size) / (1 << 20))
- self.size = size
- if saveas != '':
- default = saveas
- self.downloadTo = abspath(default)
- return default
- def newpath(self, path):
- self.downloadTo = path
- def scrub_errs(self):
- new_errors = []
- try:
- if self.errors:
- last_errMsg = ''
- errCount = 0
- for err in self.errors:
- try:
- if last_errMsg == '':
- last_errMsg = err
- elif last_errMsg == err:
- errCount += 1
- elif last_errMsg != err:
- if errCount > 0:
- new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')
- else:
- new_errors.append(last_errMsg)
- errCount = 0
- last_errMsg = err
- except:
- if __debug__: traceMsg('scrub_errs - Failed scrub')
- pass
- try:
- if len(new_errors) > 0:
- if last_errMsg != new_errors[len(new_errors)-1]:
- if errCount > 0:
- new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')
- else:
- new_errors.append(last_errMsg)
- else:
- if errCount > 0:
- new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')
- else:
- new_errors.append(last_errMsg)
- except:
- if __debug__: traceMsg('scrub_errs - Failed during scrub last Msg ')
- pass
- if len(self.errors) > 100:
- while len(self.errors) > 100 :
- del self.errors[0:99]
- self.errors = new_errors
- except:
- if __debug__: traceMsg('scrub_errs - Failed during scrub Errors')
- pass
- return new_errors
- def display(self, dpflag = Event(), fractionDone = None, timeEst = None,
- downRate = None, upRate = None, activity = None,
- statistics = None, **kws):
- if self.last_update_time + 0.1 > clock() and fractionDone not in (0.0, 1.0) and activity is not None:
- return
- self.last_update_time = clock()
- if fractionDone is not None:
- self.percentDone = str(float(int(fractionDone * 1000)) / 10)
- if timeEst is not None:
- self.timeEst = fmttime(timeEst)
- if activity is not None and not self.done:
- self.timeEst = activity
- if downRate is not None:
- self.downRate = '%.1f kB/s' % (float(downRate) / (1 << 10))
- if upRate is not None:
- self.upRate = '%.1f kB/s' % (float(upRate) / (1 << 10))
- if statistics is not None:
- if (statistics.shareRating < 0) or (statistics.shareRating > 100):
- self.shareRating = 'oo (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
- self.downTotal = statistics.downTotal
- self.upTotal = statistics.upTotal
- else:
- self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
- self.downTotal = statistics.downTotal
- self.upTotal = statistics.upTotal
- if not self.done:
- self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies))
- self.seeds = (str(statistics.numSeeds))
- else:
- self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies))
- self.seeds = (str(statistics.numOldSeeds))
- self.peers = '%d' % (statistics.numPeers)
- self.distcopy = '%.3f' % (0.001*int(1000*statistics.numCopies))
- self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10))
- dpflag.set()
- # process command-stack
- die = self.processCommandStack()
- # shutdown if requested
- if die:
- self.execShutdown()
- return;
- # ratio- + limit- checks / shutdown / stat
- if self.stoppedAt == '':
- die = False
- downRate = self.downTotal
- die = False
- if downRate == 0 and self.upTotal > 0:
- downRate = self.size
- if self.done:
- self.percentDone = '100'
- downRate = self.size
- if self.autoShutdown == 'True':
- transferLog("die-when-done set, setting shutdown-flag...\n", True)
- die = True
- if self.upTotal > 0:
- self.percentShare = '%.1f' % ((float(self.upTotal)/float(downRate))*100)
- else:
- self.percentShare = '0.0'
- if self.done and self.percentShare is not '' and self.autoShutdown == 'False':
- if (float(self.percentShare) >= float(self.shareKill)) and (self.shareKill != '0'):
- transferLog("seed-limit "+str(self.shareKill)+" reached, setting shutdown-flag...\n", True)
- die = True
- self.upRate = ''
- elif (not self.done) and (self.timeEst == 'complete!') and (self.percentDone == '100.0'):
- if (float(self.percentShare) >= float(self.shareKill)) and (self.shareKill != '0'):
- transferLog("seed-limit "+str(self.shareKill)+" reached, setting shutdown-flag...\n", True)
- die = True
- self.upRate = ''
- # shutdown / write stat-file
- if die:
- self.execShutdown()
- else:
- # write every 5 secs
- if self.displayCounter < 5:
- self.displayCounter += 1
- else:
- self.displayCounter = 0
- # write stat-file
- self.writeStatus()
- def processCommandStack(self):
- """ processCommandStack """
- if isfile(transferCommandFile):
- # process file
- transferLog("Processing command-file " + transferCommandFile + "...\n", True)
- try:
- # read file to mem
- f = open(transferCommandFile, 'r')
- commands = f.readlines()
- f.close
- # remove file
- try:
- remove(transferCommandFile)
- except:
- transferLog("Failed to remove command-file : " + transferCommandFile + "\n", True)
- pass
- # exec commands
- if len(commands) > 0:
- for command in commands:
- command = command.replace("\n", "")
- if len(command) > 0:
- # exec, early out when reading a quit-command
- if self.execCommand(command):
- return True
- else:
- transferLog("No commands found.\n", True)
- except:
- transferLog("Failed to read command-file : " + transferCommandFile + "\n", True)
- pass
- return False
- def execCommand(self, command):
- """ execCommand """
- opCode = command[0]
- # q
- if opCode == 'q':
- transferLog("command: stop-request, setting shutdown-flag...\n", True)
- return True
- # u
- elif opCode == 'u':
- if len(command) < 2:
- transferLog("invalid rate.\n", True)
- return False
- rateNew = command[1:]
- transferLog("command: setting upload-rate to " + rateNew + "...\n", True)
- self.dow.setUploadRate(int(rateNew))
- return False
- # d
- elif opCode == 'd':
- if len(command) < 2:
- transferLog("invalid rate.\n", True)
- return False
- rateNew = command[1:]
- transferLog("command: setting download-rate to " + rateNew + "...\n", True)
- self.dow.setDownloadRate(int(rateNew))
- return False
- # r
- elif opCode == 'r':
- if len(command) < 2:
- transferLog("invalid runtime-code.\n", True)
- return False
- runtimeNew = command[1]
- rt = ''
- if runtimeNew == '0':
- rt = 'False'
- elif runtimeNew == '1':
- rt = 'True'
- else:
- transferLog("runtime-code unknown: " + runtimeNew + "\n", True)
- return False
- transferLog("command: setting die-when-done to " + rt + "...\n", True)
- self.autoShutdown = rt
- return False
- # s
- elif opCode == 's':
- if len(command) < 2:
- transferLog("invalid sharekill.\n", True)
- return False
- sharekillNew = command[1:]
- transferLog("command: setting sharekill to " + sharekillNew + "...\n", True)
- self.shareKill = sharekillNew
- return False
- # default
- else:
- transferLog("op-code unknown: " + opCode + "\n", True)
- return False
- def execShutdown(self):
- """ execShutdown """
- transferLog("initializing shutdown...\n", True)
- # write stat
- if self.stoppedAt == '':
- if self.percentDone == '100':
- self.stoppedAt = '100'
- else:
- self.stoppedAt = str((float(self.percentDone)+100)*-1)
- self.timeEst = 'Torrent Stopped'
- self.running = '0'
- self.upRate = ''
- self.downRate = ''
- self.percentDone = self.stoppedAt
- self.writeStatus()
- # quit
- transferLog("tornado shutting down...\n", True)
- raise KeyboardInterrupt
- def writeStatus(self):
- """ writeStatus """
- lcount = 0
- while 1:
- lcount += 1
- try:
- f = open(transferStatFile, 'w')
- f.write(self.running + '\n')
- f.write(self.percentDone + '\n')
- f.write(self.timeEst + '\n')
- f.write(self.downRate + '\n')
- f.write(self.upRate + '\n')
- f.write(self.user + '\n')
- f.write(self.seeds + '+' + self.distcopy + '\n')
- f.write(self.peers + '\n')
- f.write(self.percentShare + '\n')
- f.write(self.shareKill + '\n')
- f.write(str(self.upTotal) + '\n')
- f.write(str(self.downTotal) + '\n')
- f.write(str(self.size))
- f.flush()
- f.close()
- break
- except:
- transferLog("Failed to open stat-file for writing : " + transferStatFile + "\n", True)
- if lcount > 30:
- break
- pass
- #------------------------------------------------------------------------------#
- # run #
- #------------------------------------------------------------------------------#
- def run(autoDie, shareKill, userName, params):
- try:
- h = HeadlessDisplayer()
- h.autoShutdown = autoDie
- h.shareKill = shareKill
- h.user = userName
- while 1:
- try:
- config = parse_params(params)
- except ValueError, e:
- print 'error: ' + str(e) + '\nrun with no args for parameter explanations'
- break
- if not config:
- print get_usage()
- break
- # log what we are starting up
- transferLog("tornado starting up :\n", True)
- transferLog(" - torrentfile : " + config['responsefile'] + "\n", True)
- transferLog(" - userName : " + userName + "\n", True)
- transferLog(" - transferStatFile : " + transferStatFile + "\n", True)
- transferLog(" - transferCommandFile : " + transferCommandFile + "\n", True)
- transferLog(" - transferLogFile : " + transferLogFile + "\n", True)
- transferLog(" - transferPidFile : " + transferPidFile + "\n", True)
- transferLog(" - autoDie : " + autoDie + "\n", True)
- transferLog(" - shareKill : " + shareKill + "\n", True)
- transferLog(" - minport : " + str(config['minport']) + "\n", True)
- transferLog(" - maxport : " + str(config['maxport']) + "\n", True)
- transferLog(" - max_upload_rate : " + str(config['max_upload_rate']) + "\n", True)
- transferLog(" - max_download_rate : " + str(config['max_download_rate']) + "\n", True)
- transferLog(" - min_uploads : " + str(config['min_uploads']) + "\n", True)
- transferLog(" - max_uploads : " + str(config['max_uploads']) + "\n", True)
- transferLog(" - min_peers : " + str(config['min_peers']) + "\n", True)
- transferLog(" - max_initiate : " + str(config['max_initiate']) + "\n", True)
- transferLog(" - max_connections : " + str(config['max_connections']) + "\n", True)
- transferLog(" - super_seeder : " + str(config['super_seeder']) + "\n", True)
- transferLog(" - security : " + str(config['security']) + "\n", True)
- transferLog(" - auto_kick : " + str(config['auto_kick']) + "\n", True)
- if 'crypto_allowed' in config:
- transferLog(" - crypto_allowed : " + str(config['crypto_allowed']) + "\n", True)
- if 'crypto_only' in config:
- transferLog(" - crypto_only : " + str(config['crypto_only']) + "\n", True)
- if 'crypto_stealth' in config:
- transferLog(" - crypto_stealth : " + str(config['crypto_stealth']) + "\n", True)
- transferLog(" - priority : " + str(config['priority']) + "\n", True)
- transferLog(" - alloc_type : " + str(config['alloc_type']) + "\n", True)
- transferLog(" - alloc_rate : " + str(config['alloc_rate']) + "\n", True)
- transferLog(" - buffer_reads : " + str(config['buffer_reads']) + "\n", True)
- transferLog(" - write_buffer_size : " + str(config['write_buffer_size']) + "\n", True)
- transferLog(" - check_hashes : " + str(config['check_hashes']) + "\n", True)
- transferLog(" - max_files_open : " + str(config['max_files_open']) + "\n", True)
- transferLog(" - upnp_nat_access : " + str(config['upnp_nat_access']) + "\n", True)
- # remove command-file if exists
- if isfile(transferCommandFile):
- try:
- transferLog("removing command-file " + transferCommandFile + "...\n", True)
- remove(transferCommandFile)
- except:
- pass
- # write pid-file
- currentPid = (str(getpid())).strip()
- transferLog("writing pid-file : " + transferPidFile + " (" + currentPid + ")\n", True)
- try:
- pidFile = open(transferPidFile, 'w')
- pidFile.write(currentPid + "\n")
- pidFile.flush()
- pidFile.close()
- except Exception, e:
- transferLog("Failed to write pid-file, shutting down : " + transferPidFile + " (" + currentPid + ")" + "\n", True)
- break
- myid = createPeerID()
- seed(myid)
- doneflag = Event()
- def disp_exception(text):
- print text
- rawserver = RawServer(doneflag, config['timeout_check_interval'],
- config['timeout'], ipv6_enable = config['ipv6_enabled'],
- failfunc = h.failed, errorfunc = disp_exception)
- upnp_type = UPnP_test(config['upnp_nat_access'])
- while True:
- try:
- listen_port = rawserver.find_and_bind(config['minport'], config['maxport'],
- config['bind'], ipv6_socket_style = config['ipv6_binds_v4'],
- upnp = upnp_type, randomizer = config['random_port'])
- break
- except socketerror, e:
- if upnp_type and e == UPnP_ERROR:
- print 'WARNING: COULD NOT FORWARD VIA UPnP'
- upnp_type = 0
- continue
- print "error: Couldn't listen - " + str(e)
- h.failed()
- return
- response = get_response(config['responsefile'], config['url'], h.error)
- if not response:
- break
- infohash = sha(bencode(response['info'])).digest()
- h.dow = BT1Download(h.display, h.finished, h.error, disp_exception, doneflag,
- config, response, infohash, myid, rawserver, listen_port)
- if not h.dow.saveAs(h.chooseFile, h.newpath):
- break
- if not h.dow.initFiles(old_style = True):
- break
- if not h.dow.startEngine():
- h.dow.shutdown()
- break
- h.dow.startRerequester()
- h.dow.autoStats()
- if not h.dow.am_I_finished():
- h.display(activity = 'connecting to peers')
- # log that we are done with startup
- transferLog("tornado up and running.\n", True)
- # listen forever
- rawserver.listen_forever(h.dow.getPortHandler())
- # shutdown
- h.display(activity = 'shutting down')
- h.dow.shutdown()
- break
- try:
- rawserver.shutdown()
- except:
- pass
- if not h.done:
- h.failed()
- finally:
- transferLog("removing pid-file : " + transferPidFile + "\n", True)
- try:
- remove(transferPidFile)
- except:
- transferLog("Failed to remove pid-file : " + transferPidFile + "\n", True)
- pass
- #------------------------------------------------------------------------------#
- # __main__ #
- #------------------------------------------------------------------------------#
- if __name__ == '__main__':
- if argv[1:] == ['--version']:
- print version
- sys.exit(0)
- # check argv-length
- if len(argv) < 5:
- print "Error : missing arguments, exiting. \n"
- sys.exit(0)
- # get/set stat-file
- transferStatFile = argv[4] + ".stat"
- # get/set cmd-file
- transferCommandFile = argv[4] + ".cmd"
- # get/set log-file
- transferLogFile = argv[4] + ".log"
- # get/set pid-file
- transferPidFile = argv[4] + ".pid"
- if PROFILER:
- import profile, pstats
- p = profile.Profile()
- p.runcall(run, argv[1],argv[2],argv[3],argv[5:])
- log = open('profile_data.'+strftime('%y%m%d%H%M%S')+'.txt','a')
- normalstdout = sys.stdout
- sys.stdout = log
- # pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()
- pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()
- sys.stdout = normalstdout
- else:
- run(argv[1],argv[2],argv[3],argv[5:])
- # log exit
- transferLog("tornado exit.\n", True)
|