2016-07-16 17:42:19 +02:00
|
|
|
#!/usr/bin/python2.7
|
2016-05-01 08:34:04 +02:00
|
|
|
# Copyright (c) 2012-2016 Jonathan Warren
|
2017-09-27 17:25:14 +02:00
|
|
|
# Copyright (c) 2012-2018 The Bitmessage developers
|
2012-11-19 20:45:05 +01:00
|
|
|
# Distributed under the MIT/X11 software license. See the accompanying
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2013-06-13 20:00:56 +02:00
|
|
|
# Right now, PyBitmessage only support connecting to stream 1. It doesn't
|
|
|
|
# yet contain logic to expand into further streams.
|
2012-11-19 20:45:05 +01:00
|
|
|
|
2013-06-13 20:00:56 +02:00
|
|
|
# The software version variable is now held in shared.py
|
2013-02-18 21:22:48 +01:00
|
|
|
|
2017-02-28 14:51:49 +01:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
app_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
os.chdir(app_dir)
|
|
|
|
sys.path.insert(0, app_dir)
|
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2014-08-06 08:40:41 +02:00
|
|
|
import depends
|
|
|
|
depends.check_dependencies()
|
2014-07-29 08:51:59 +02:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
|
|
|
|
import signal
|
2013-06-13 20:00:56 +02:00
|
|
|
# The next 3 are used for the API
|
2017-01-10 21:15:35 +01:00
|
|
|
from singleinstance import singleinstance
|
2017-05-27 19:00:19 +02:00
|
|
|
import errno
|
2014-02-16 17:21:20 +01:00
|
|
|
import socket
|
|
|
|
import ctypes
|
|
|
|
from struct import pack
|
2015-03-19 23:09:04 +01:00
|
|
|
from subprocess import call
|
2017-07-30 09:36:20 +02:00
|
|
|
from time import sleep
|
2017-08-09 17:29:23 +02:00
|
|
|
from random import randint
|
2017-09-23 23:42:15 +02:00
|
|
|
import getopt
|
2013-05-01 22:06:55 +02:00
|
|
|
|
2015-11-24 01:55:17 +01:00
|
|
|
from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
|
2017-09-27 17:25:14 +02:00
|
|
|
from helper_startup import (
|
|
|
|
isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections
|
|
|
|
)
|
2013-12-30 01:53:44 +01:00
|
|
|
|
2017-02-08 20:37:42 +01:00
|
|
|
import defaults
|
2013-12-30 01:53:44 +01:00
|
|
|
import shared
|
2017-02-08 20:48:22 +01:00
|
|
|
import knownnodes
|
2017-01-11 17:00:00 +01:00
|
|
|
import state
|
2017-02-08 13:41:56 +01:00
|
|
|
import shutdown
|
2013-12-30 01:53:44 +01:00
|
|
|
import threading
|
|
|
|
|
2013-06-20 23:23:03 +02:00
|
|
|
# Classes
|
2013-12-30 01:53:44 +01:00
|
|
|
from class_sqlThread import sqlThread
|
|
|
|
from class_singleCleaner import singleCleaner
|
|
|
|
from class_objectProcessor import objectProcessor
|
|
|
|
from class_singleWorker import singleWorker
|
|
|
|
from class_addressGenerator import addressGenerator
|
2017-02-22 09:34:54 +01:00
|
|
|
from bmconfigparser import BMConfigParser
|
2013-06-20 23:23:03 +02:00
|
|
|
|
2017-05-27 19:09:21 +02:00
|
|
|
from inventory import Inventory
|
|
|
|
|
2017-05-24 16:51:49 +02:00
|
|
|
from network.connectionpool import BMConnectionPool
|
2017-10-20 01:21:49 +02:00
|
|
|
from network.dandelion import Dandelion
|
2017-05-24 16:51:49 +02:00
|
|
|
from network.networkthread import BMNetworkThread
|
2017-05-25 23:04:33 +02:00
|
|
|
from network.receivequeuethread import ReceiveQueueThread
|
2017-05-27 19:09:21 +02:00
|
|
|
from network.announcethread import AnnounceThread
|
2017-05-29 00:24:07 +02:00
|
|
|
from network.invthread import InvThread
|
2017-07-05 08:57:44 +02:00
|
|
|
from network.addrthread import AddrThread
|
2017-06-21 12:16:33 +02:00
|
|
|
from network.downloadthread import DownloadThread
|
2017-05-24 16:51:49 +02:00
|
|
|
|
2013-06-21 00:55:04 +02:00
|
|
|
# Helper Functions
|
|
|
|
import helper_bootstrap
|
2013-12-30 01:53:44 +01:00
|
|
|
import helper_generic
|
2017-07-06 19:35:40 +02:00
|
|
|
import helper_threading
|
2015-11-15 15:08:48 +01:00
|
|
|
|
2013-05-01 22:06:55 +02:00
|
|
|
def connectToStream(streamNumber):
|
2017-02-06 17:47:05 +01:00
|
|
|
state.streamsInWhichIAmParticipating.append(streamNumber)
|
2013-05-01 22:06:55 +02:00
|
|
|
selfInitiatedConnections[streamNumber] = {}
|
2013-09-07 00:55:12 +02:00
|
|
|
|
2014-01-20 21:25:02 +01:00
|
|
|
if isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
|
2017-09-27 17:25:14 +02:00
|
|
|
# Some XP and Vista systems can only have 10 outgoing connections
|
|
|
|
# at a time.
|
2017-05-25 23:04:33 +02:00
|
|
|
state.maximumNumberOfHalfOpenConnections = 9
|
2013-05-08 23:11:16 +02:00
|
|
|
else:
|
2017-05-25 23:04:33 +02:00
|
|
|
state.maximumNumberOfHalfOpenConnections = 64
|
2016-03-22 14:47:18 +01:00
|
|
|
try:
|
|
|
|
# don't overload Tor
|
2017-09-27 17:25:14 +02:00
|
|
|
if BMConfigParser().get(
|
|
|
|
'bitmessagesettings', 'socksproxytype') != 'none':
|
2017-05-25 23:04:33 +02:00
|
|
|
state.maximumNumberOfHalfOpenConnections = 4
|
2016-03-22 14:47:18 +01:00
|
|
|
except:
|
|
|
|
pass
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2017-02-08 20:48:22 +01:00
|
|
|
with knownnodes.knownNodesLock:
|
|
|
|
if streamNumber not in knownnodes.knownNodes:
|
|
|
|
knownnodes.knownNodes[streamNumber] = {}
|
2017-02-08 20:52:18 +01:00
|
|
|
if streamNumber*2 not in knownnodes.knownNodes:
|
|
|
|
knownnodes.knownNodes[streamNumber*2] = {}
|
|
|
|
if streamNumber*2+1 not in knownnodes.knownNodes:
|
|
|
|
knownnodes.knownNodes[streamNumber*2+1] = {}
|
2017-02-08 20:48:22 +01:00
|
|
|
|
2017-08-09 17:36:52 +02:00
|
|
|
BMConnectionPool().connectToStream(streamNumber)
|
2013-05-01 22:06:55 +02:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2017-05-27 19:00:19 +02:00
|
|
|
def _fixSocket():
|
|
|
|
if sys.platform.startswith('linux'):
|
|
|
|
socket.SO_BINDTODEVICE = 25
|
|
|
|
|
|
|
|
if not sys.platform.startswith('win'):
|
2014-02-16 17:21:20 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
# Python 2 on Windows doesn't define a wrapper for
|
|
|
|
# socket.inet_ntop but we can make one ourselves using ctypes
|
|
|
|
if not hasattr(socket, 'inet_ntop'):
|
|
|
|
addressToString = ctypes.windll.ws2_32.WSAAddressToStringA
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2014-02-16 17:21:20 +01:00
|
|
|
def inet_ntop(family, host):
|
|
|
|
if family == socket.AF_INET:
|
|
|
|
if len(host) != 4:
|
|
|
|
raise ValueError("invalid IPv4 host")
|
|
|
|
host = pack("hH4s8s", socket.AF_INET, 0, host, "\0" * 8)
|
|
|
|
elif family == socket.AF_INET6:
|
|
|
|
if len(host) != 16:
|
|
|
|
raise ValueError("invalid IPv6 host")
|
|
|
|
host = pack("hHL16sL", socket.AF_INET6, 0, 0, host, 0)
|
|
|
|
else:
|
|
|
|
raise ValueError("invalid address family")
|
|
|
|
buf = "\0" * 64
|
|
|
|
lengthBuf = pack("I", len(buf))
|
|
|
|
addressToString(host, len(host), None, buf, lengthBuf)
|
|
|
|
return buf[0:buf.index("\0")]
|
|
|
|
socket.inet_ntop = inet_ntop
|
|
|
|
|
|
|
|
# Same for inet_pton
|
|
|
|
if not hasattr(socket, 'inet_pton'):
|
|
|
|
stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2014-02-16 17:21:20 +01:00
|
|
|
def inet_pton(family, host):
|
|
|
|
buf = "\0" * 28
|
|
|
|
lengthBuf = pack("I", len(buf))
|
|
|
|
if stringToAddress(str(host),
|
|
|
|
int(family),
|
|
|
|
None,
|
|
|
|
buf,
|
|
|
|
lengthBuf) != 0:
|
|
|
|
raise socket.error("illegal IP address passed to inet_pton")
|
|
|
|
if family == socket.AF_INET:
|
|
|
|
return buf[4:8]
|
|
|
|
elif family == socket.AF_INET6:
|
|
|
|
return buf[8:24]
|
|
|
|
else:
|
|
|
|
raise ValueError("invalid address family")
|
|
|
|
socket.inet_pton = inet_pton
|
|
|
|
|
|
|
|
# These sockopts are needed on for IPv6 support
|
|
|
|
if not hasattr(socket, 'IPPROTO_IPV6'):
|
|
|
|
socket.IPPROTO_IPV6 = 41
|
|
|
|
if not hasattr(socket, 'IPV6_V6ONLY'):
|
|
|
|
socket.IPV6_V6ONLY = 27
|
2013-04-26 19:20:30 +02:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2013-06-13 20:00:56 +02:00
|
|
|
# This thread, of which there is only one, runs the API.
|
2017-07-06 19:35:40 +02:00
|
|
|
class singleAPI(threading.Thread, helper_threading.StoppableThread):
|
2013-05-01 22:06:55 +02:00
|
|
|
def __init__(self):
|
2015-11-24 01:55:17 +01:00
|
|
|
threading.Thread.__init__(self, name="singleAPI")
|
|
|
|
self.initStop()
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2015-11-24 01:55:17 +01:00
|
|
|
def stopThread(self):
|
|
|
|
super(singleAPI, self).stopThread()
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
try:
|
2017-09-27 17:25:14 +02:00
|
|
|
s.connect((
|
|
|
|
BMConfigParser().get('bitmessagesettings', 'apiinterface'),
|
|
|
|
BMConfigParser().getint('bitmessagesettings', 'apiport')
|
|
|
|
))
|
2015-11-24 01:55:17 +01:00
|
|
|
s.shutdown(socket.SHUT_RDWR)
|
|
|
|
s.close()
|
|
|
|
except:
|
|
|
|
pass
|
2013-03-19 18:32:37 +01:00
|
|
|
|
|
|
|
def run(self):
|
2017-08-09 17:29:23 +02:00
|
|
|
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
|
|
|
|
try:
|
|
|
|
from errno import WSAEADDRINUSE
|
|
|
|
except (ImportError, AttributeError):
|
|
|
|
errno.WSAEADDRINUSE = errno.EADDRINUSE
|
|
|
|
for attempt in range(50):
|
|
|
|
try:
|
|
|
|
if attempt > 0:
|
|
|
|
port = randint(32767, 65535)
|
2017-09-27 17:25:14 +02:00
|
|
|
se = StoppableXMLRPCServer(
|
|
|
|
(BMConfigParser().get(
|
|
|
|
'bitmessagesettings', 'apiinterface'),
|
|
|
|
port),
|
2017-08-09 17:29:23 +02:00
|
|
|
MySimpleXMLRPCRequestHandler, True, True)
|
|
|
|
except socket.error as e:
|
|
|
|
if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE):
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
if attempt > 0:
|
2017-09-27 17:25:14 +02:00
|
|
|
BMConfigParser().set(
|
|
|
|
"bitmessagesettings", "apiport", str(port))
|
2017-08-09 17:29:23 +02:00
|
|
|
BMConfigParser().save()
|
|
|
|
break
|
2013-03-19 18:32:37 +01:00
|
|
|
se.register_introspection_functions()
|
|
|
|
se.serve_forever()
|
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2013-08-06 19:19:26 +02:00
|
|
|
# This is a list of current connections (the thread pointers at least)
|
2013-06-24 21:51:01 +02:00
|
|
|
selfInitiatedConnections = {}
|
|
|
|
|
|
|
|
if shared.useVeryEasyProofOfWorkForTesting:
|
2017-02-08 20:37:42 +01:00
|
|
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int(
|
|
|
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte / 100)
|
|
|
|
defaults.networkDefaultPayloadLengthExtraBytes = int(
|
|
|
|
defaults.networkDefaultPayloadLengthExtraBytes / 100)
|
2013-01-18 23:38:09 +01:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2013-08-06 13:23:56 +02:00
|
|
|
class Main:
|
2017-09-26 16:36:02 +02:00
|
|
|
def start(self):
|
2017-05-27 19:00:19 +02:00
|
|
|
_fixSocket()
|
2014-02-16 17:21:20 +01:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
daemon = BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'daemon')
|
2017-09-26 16:36:02 +02:00
|
|
|
|
2017-09-23 23:42:15 +02:00
|
|
|
try:
|
2018-03-23 15:56:01 +01:00
|
|
|
opts, args = getopt.getopt(
|
|
|
|
sys.argv[1:], "hcdt",
|
|
|
|
["help", "curses", "daemon", "test"])
|
2017-09-23 23:42:15 +02:00
|
|
|
|
|
|
|
except getopt.GetoptError:
|
|
|
|
self.usage()
|
|
|
|
sys.exit(2)
|
|
|
|
|
|
|
|
for opt, arg in opts:
|
|
|
|
if opt in ("-h", "--help"):
|
|
|
|
self.usage()
|
|
|
|
sys.exit()
|
|
|
|
elif opt in ("-d", "--daemon"):
|
|
|
|
daemon = True
|
2017-09-27 17:25:14 +02:00
|
|
|
state.enableGUI = False # run without a UI
|
2017-09-23 23:42:15 +02:00
|
|
|
elif opt in ("-c", "--curses"):
|
|
|
|
state.curses = True
|
2018-04-05 11:50:34 +02:00
|
|
|
elif opt in ("-t", "--test"):
|
|
|
|
state.testmode = daemon = True
|
2017-09-27 17:25:14 +02:00
|
|
|
state.enableGUI = False # run without a UI
|
2013-08-06 13:23:56 +02:00
|
|
|
|
2016-06-30 12:30:05 +02:00
|
|
|
# is the application already running? If yes then exit.
|
2018-05-22 13:17:47 +02:00
|
|
|
if state.enableGUI and not state.curses and not depends.check_pyqt():
|
|
|
|
sys.exit(
|
|
|
|
'PyBitmessage requires PyQt unless you want'
|
|
|
|
' to run it as a daemon and interact with it'
|
|
|
|
' using the API. You can download PyQt from '
|
|
|
|
'http://www.riverbankcomputing.com/software/pyqt/download'
|
|
|
|
' or by searching Google for \'PyQt Download\'.'
|
|
|
|
' If you want to run in daemon mode, see '
|
|
|
|
'https://bitmessage.org/wiki/Daemon\n'
|
|
|
|
'You can also run PyBitmessage with'
|
|
|
|
' the new curses interface by providing'
|
|
|
|
' \'-c\' as a commandline argument.'
|
|
|
|
)
|
2017-01-10 21:15:35 +01:00
|
|
|
shared.thisapp = singleinstance("", daemon)
|
2016-06-30 12:30:05 +02:00
|
|
|
|
2018-04-05 11:50:34 +02:00
|
|
|
if daemon and not state.testmode:
|
2016-06-30 12:30:05 +02:00
|
|
|
with shared.printLock:
|
|
|
|
print('Running as a daemon. Send TERM signal to end.')
|
|
|
|
self.daemonize()
|
|
|
|
|
|
|
|
self.setSignalHandler()
|
2013-08-06 13:23:56 +02:00
|
|
|
|
2017-09-21 17:51:34 +02:00
|
|
|
helper_threading.set_thread_name("PyBitmessage")
|
2017-07-06 19:35:40 +02:00
|
|
|
|
2018-02-03 11:46:39 +01:00
|
|
|
state.dandelion = BMConfigParser().safeGetInt('network', 'dandelion')
|
2017-09-27 17:25:14 +02:00
|
|
|
# dandelion requires outbound connections, without them,
|
|
|
|
# stem objects will get stuck forever
|
|
|
|
if state.dandelion and not BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'sendoutgoingconnections'):
|
2018-02-03 11:46:39 +01:00
|
|
|
state.dandelion = 0
|
|
|
|
|
2018-05-03 12:49:43 +02:00
|
|
|
knownnodes.readKnownNodes()
|
2013-08-06 13:23:56 +02:00
|
|
|
|
2018-04-09 06:38:48 +02:00
|
|
|
# Not needed if objproc is disabled
|
|
|
|
if state.enableObjProc:
|
|
|
|
|
|
|
|
# Start the address generation thread
|
|
|
|
addressGeneratorThread = addressGenerator()
|
2017-09-27 17:25:14 +02:00
|
|
|
# close the main program even if there are threads left
|
|
|
|
addressGeneratorThread.daemon = True
|
2018-04-09 06:38:48 +02:00
|
|
|
addressGeneratorThread.start()
|
|
|
|
|
|
|
|
# Start the thread that calculates POWs
|
|
|
|
singleWorkerThread = singleWorker()
|
2017-09-27 17:25:14 +02:00
|
|
|
# close the main program even if there are threads left
|
|
|
|
singleWorkerThread.daemon = True
|
2018-04-09 06:38:48 +02:00
|
|
|
singleWorkerThread.start()
|
2013-08-06 13:23:56 +02:00
|
|
|
|
|
|
|
# Start the SQL thread
|
|
|
|
sqlLookup = sqlThread()
|
2017-09-27 17:25:14 +02:00
|
|
|
# DON'T close the main program even if there are threads left.
|
|
|
|
# The closeEvent should command this thread to exit gracefully.
|
|
|
|
sqlLookup.daemon = False
|
2013-08-06 13:23:56 +02:00
|
|
|
sqlLookup.start()
|
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
Inventory() # init
|
|
|
|
# init, needs to be early because other thread may access it early
|
|
|
|
Dandelion()
|
2017-05-27 19:09:21 +02:00
|
|
|
|
2018-04-09 06:38:48 +02:00
|
|
|
# Enable object processor and SMTP only if objproc enabled
|
|
|
|
if state.enableObjProc:
|
|
|
|
|
|
|
|
# SMTP delivery thread
|
2017-09-27 17:25:14 +02:00
|
|
|
if daemon and BMConfigParser().safeGet(
|
|
|
|
"bitmessagesettings", "smtpdeliver", '') != '':
|
2018-06-21 12:03:50 +02:00
|
|
|
from class_smtpDeliver import smtpDeliver
|
2018-04-09 06:38:48 +02:00
|
|
|
smtpDeliveryThread = smtpDeliver()
|
|
|
|
smtpDeliveryThread.start()
|
2016-06-30 12:30:05 +02:00
|
|
|
|
2018-04-09 06:38:48 +02:00
|
|
|
# SMTP daemon thread
|
2017-09-27 17:25:14 +02:00
|
|
|
if daemon and BMConfigParser().safeGetBoolean(
|
|
|
|
"bitmessagesettings", "smtpd"):
|
2018-06-21 12:03:50 +02:00
|
|
|
from class_smtpServer import smtpServer
|
2018-04-09 06:38:48 +02:00
|
|
|
smtpServerThread = smtpServer()
|
|
|
|
smtpServerThread.start()
|
2016-07-19 13:57:54 +02:00
|
|
|
|
2018-04-09 06:38:48 +02:00
|
|
|
# Start the thread that calculates POWs
|
|
|
|
objectProcessorThread = objectProcessor()
|
2017-09-27 17:25:14 +02:00
|
|
|
# DON'T close the main program even the thread remains.
|
|
|
|
# This thread checks the shutdown variable after processing
|
|
|
|
# each object.
|
|
|
|
objectProcessorThread.daemon = False
|
2018-04-09 06:38:48 +02:00
|
|
|
objectProcessorThread.start()
|
2013-12-02 07:35:34 +01:00
|
|
|
|
2013-08-06 13:23:56 +02:00
|
|
|
# Start the cleanerThread
|
|
|
|
singleCleanerThread = singleCleaner()
|
2017-09-27 17:25:14 +02:00
|
|
|
# close the main program even if there are threads left
|
|
|
|
singleCleanerThread.daemon = True
|
2013-08-06 13:23:56 +02:00
|
|
|
singleCleanerThread.start()
|
|
|
|
|
2018-04-09 06:38:48 +02:00
|
|
|
# Not needed if objproc disabled
|
|
|
|
if state.enableObjProc:
|
|
|
|
shared.reloadMyAddressHashes()
|
|
|
|
shared.reloadBroadcastSendersForWhichImWatching()
|
|
|
|
|
|
|
|
# API is also objproc dependent
|
2017-09-27 17:25:14 +02:00
|
|
|
if BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'apienabled'):
|
2018-04-09 06:38:48 +02:00
|
|
|
try:
|
|
|
|
apiNotifyPath = BMConfigParser().get(
|
|
|
|
'bitmessagesettings', 'apinotifypath')
|
|
|
|
except:
|
|
|
|
apiNotifyPath = ''
|
|
|
|
if apiNotifyPath != '':
|
|
|
|
with shared.printLock:
|
|
|
|
print('Trying to call', apiNotifyPath)
|
|
|
|
|
|
|
|
call([apiNotifyPath, "startingUp"])
|
|
|
|
singleAPIThread = singleAPI()
|
2017-09-27 17:25:14 +02:00
|
|
|
# close the main program even if there are threads left
|
|
|
|
singleAPIThread.daemon = True
|
2018-04-09 06:38:48 +02:00
|
|
|
singleAPIThread.start()
|
|
|
|
|
|
|
|
# start network components if networking is enabled
|
|
|
|
if state.enableNetwork:
|
|
|
|
BMConnectionPool()
|
|
|
|
asyncoreThread = BMNetworkThread()
|
|
|
|
asyncoreThread.daemon = True
|
|
|
|
asyncoreThread.start()
|
|
|
|
for i in range(BMConfigParser().getint("threads", "receive")):
|
|
|
|
receiveQueueThread = ReceiveQueueThread(i)
|
|
|
|
receiveQueueThread.daemon = True
|
|
|
|
receiveQueueThread.start()
|
|
|
|
announceThread = AnnounceThread()
|
|
|
|
announceThread.daemon = True
|
|
|
|
announceThread.start()
|
|
|
|
state.invThread = InvThread()
|
|
|
|
state.invThread.daemon = True
|
|
|
|
state.invThread.start()
|
|
|
|
state.addrThread = AddrThread()
|
|
|
|
state.addrThread.daemon = True
|
|
|
|
state.addrThread.start()
|
|
|
|
state.downloadThread = DownloadThread()
|
|
|
|
state.downloadThread.daemon = True
|
|
|
|
state.downloadThread.start()
|
|
|
|
|
|
|
|
connectToStream(1)
|
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
if BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'upnp'):
|
2018-04-09 06:38:48 +02:00
|
|
|
import upnp
|
|
|
|
upnpThread = upnp.uPnPThread()
|
|
|
|
upnpThread.start()
|
|
|
|
else:
|
|
|
|
# Populate with hardcoded value (same as connectToStream above)
|
|
|
|
state.streamsInWhichIAmParticipating.append(1)
|
|
|
|
|
2018-07-09 14:06:28 +02:00
|
|
|
if not daemon and state.enableGUI:
|
2017-09-27 17:25:14 +02:00
|
|
|
if state.curses:
|
2018-05-22 13:17:47 +02:00
|
|
|
if not depends.check_curses():
|
|
|
|
sys.exit()
|
2017-09-27 17:25:14 +02:00
|
|
|
print('Running with curses')
|
|
|
|
import bitmessagecurses
|
|
|
|
bitmessagecurses.runwrapper()
|
2018-07-05 15:37:16 +02:00
|
|
|
elif state.kivy:
|
|
|
|
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
|
2018-07-09 14:06:28 +02:00
|
|
|
from bitmessagekivy.mpybit import MainApp
|
2018-07-05 15:37:16 +02:00
|
|
|
MainApp().run()
|
2018-05-22 13:17:47 +02:00
|
|
|
else:
|
2014-04-19 20:45:37 +02:00
|
|
|
import bitmessageqt
|
|
|
|
bitmessageqt.run()
|
2018-07-05 15:37:16 +02:00
|
|
|
else:
|
|
|
|
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
|
2013-05-02 22:55:13 +02:00
|
|
|
|
2017-07-30 09:36:20 +02:00
|
|
|
if daemon:
|
2018-04-05 11:50:34 +02:00
|
|
|
if state.testmode:
|
|
|
|
sleep(30)
|
2018-03-23 15:56:01 +01:00
|
|
|
# make testing
|
|
|
|
self.stop()
|
2017-07-30 09:36:20 +02:00
|
|
|
while state.shutdown == 0:
|
|
|
|
sleep(1)
|
|
|
|
|
2016-06-30 12:30:05 +02:00
|
|
|
def daemonize(self):
|
2018-01-01 13:08:12 +01:00
|
|
|
grandfatherPid = os.getpid()
|
|
|
|
parentPid = None
|
2017-07-28 08:54:34 +02:00
|
|
|
try:
|
|
|
|
if os.fork():
|
2018-01-01 13:08:12 +01:00
|
|
|
# unlock
|
|
|
|
shared.thisapp.cleanup()
|
|
|
|
# wait until grandchild ready
|
|
|
|
while True:
|
|
|
|
sleep(1)
|
2017-08-15 12:22:24 +02:00
|
|
|
os._exit(0)
|
2017-07-28 08:54:34 +02:00
|
|
|
except AttributeError:
|
|
|
|
# fork not implemented
|
|
|
|
pass
|
|
|
|
else:
|
2018-01-01 13:08:12 +01:00
|
|
|
parentPid = os.getpid()
|
2017-09-27 17:25:14 +02:00
|
|
|
shared.thisapp.lock() # relock
|
|
|
|
|
2016-06-30 12:30:05 +02:00
|
|
|
os.umask(0)
|
2017-07-28 09:19:53 +02:00
|
|
|
try:
|
|
|
|
os.setsid()
|
|
|
|
except AttributeError:
|
|
|
|
# setsid not implemented
|
|
|
|
pass
|
2017-07-28 08:54:34 +02:00
|
|
|
try:
|
|
|
|
if os.fork():
|
2018-01-01 13:08:12 +01:00
|
|
|
# unlock
|
|
|
|
shared.thisapp.cleanup()
|
|
|
|
# wait until child ready
|
|
|
|
while True:
|
|
|
|
sleep(1)
|
2017-08-15 12:22:24 +02:00
|
|
|
os._exit(0)
|
2017-07-28 08:54:34 +02:00
|
|
|
except AttributeError:
|
|
|
|
# fork not implemented
|
|
|
|
pass
|
|
|
|
else:
|
2017-09-27 17:25:14 +02:00
|
|
|
shared.thisapp.lock() # relock
|
|
|
|
shared.thisapp.lockPid = None # indicate we're the final child
|
2016-06-30 12:30:05 +02:00
|
|
|
sys.stdout.flush()
|
|
|
|
sys.stderr.flush()
|
2017-09-23 18:25:41 +02:00
|
|
|
if not sys.platform.startswith('win'):
|
|
|
|
si = file(os.devnull, 'r')
|
|
|
|
so = file(os.devnull, 'a+')
|
|
|
|
se = file(os.devnull, 'a+', 0)
|
|
|
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
|
|
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
|
|
|
os.dup2(se.fileno(), sys.stderr.fileno())
|
2018-01-01 13:08:12 +01:00
|
|
|
if parentPid:
|
|
|
|
# signal ready
|
|
|
|
os.kill(parentPid, signal.SIGTERM)
|
|
|
|
os.kill(grandfatherPid, signal.SIGTERM)
|
2016-06-30 12:30:05 +02:00
|
|
|
|
|
|
|
def setSignalHandler(self):
|
|
|
|
signal.signal(signal.SIGINT, helper_generic.signal_handler)
|
|
|
|
signal.signal(signal.SIGTERM, helper_generic.signal_handler)
|
|
|
|
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
2013-09-28 14:09:15 +02:00
|
|
|
|
2017-09-23 23:42:15 +02:00
|
|
|
def usage(self):
|
|
|
|
print 'Usage: ' + sys.argv[0] + ' [OPTIONS]'
|
|
|
|
print '''
|
|
|
|
Options:
|
|
|
|
-h, --help show this help message and exit
|
|
|
|
-c, --curses use curses (text mode) interface
|
|
|
|
-d, --daemon run in daemon (background) mode
|
2018-03-23 15:56:01 +01:00
|
|
|
-t, --test dryrun, make testing
|
2017-09-23 23:42:15 +02:00
|
|
|
|
|
|
|
All parameters are optional.
|
|
|
|
'''
|
|
|
|
|
2013-08-06 13:23:56 +02:00
|
|
|
def stop(self):
|
2013-06-29 19:29:35 +02:00
|
|
|
with shared.printLock:
|
2014-07-29 08:51:59 +02:00
|
|
|
print('Stopping Bitmessage Deamon.')
|
2017-02-08 13:41:56 +01:00
|
|
|
shutdown.doCleanShutdown()
|
2013-09-28 14:09:15 +02:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
# TODO: nice function but no one is using this
|
2013-08-06 13:23:56 +02:00
|
|
|
def getApiAddress(self):
|
2017-09-27 17:25:14 +02:00
|
|
|
if not BMConfigParser().safeGetBoolean(
|
|
|
|
'bitmessagesettings', 'apienabled'):
|
2013-08-06 13:23:56 +02:00
|
|
|
return None
|
2017-01-11 14:27:19 +01:00
|
|
|
address = BMConfigParser().get('bitmessagesettings', 'apiinterface')
|
|
|
|
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
|
2017-09-27 17:25:14 +02:00
|
|
|
return {'address': address, 'port': port}
|
2013-09-28 14:09:15 +02:00
|
|
|
|
2017-02-28 14:51:49 +01:00
|
|
|
|
|
|
|
def main():
|
2013-08-06 13:23:56 +02:00
|
|
|
mainprogram = Main()
|
2017-09-26 16:36:02 +02:00
|
|
|
mainprogram.start()
|
2017-02-28 14:51:49 +01:00
|
|
|
|
2017-09-27 17:25:14 +02:00
|
|
|
|
2017-02-28 14:51:49 +01:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|
2013-02-18 21:22:48 +01:00
|
|
|
|
2013-09-28 14:09:15 +02:00
|
|
|
|
2013-06-17 22:42:30 +02:00
|
|
|
# So far, the creation of and management of the Bitmessage protocol and this
|
|
|
|
# client is a one-man operation. Bitcoin tips are quite appreciated.
|
2012-11-29 11:39:39 +01:00
|
|
|
# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u
|