fix possible race condition in collect_gargage
[electrum-server.git] / server.py
1 #!/usr/bin/env python
2 # Copyright(C) 2012 thomasv@gitorious
3
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public
15 # License along with this program.  If not, see
16 # <http://www.gnu.org/licenses/agpl.html>.
17
18 import ConfigParser
19 import logging
20 import socket
21 import sys
22 import time
23 import threading
24 import traceback
25
26 import json
27
28 logging.basicConfig()
29
30 if sys.maxsize <= 2**32:
31     print "Warning: it looks like you are using a 32bit system. You may experience crashes caused by mmap"
32
33
34 def attempt_read_config(config, filename):
35     try:
36         with open(filename, 'r') as f:
37             config.readfp(f)
38     except IOError:
39         pass
40
41
42 def create_config():
43     config = ConfigParser.ConfigParser()
44     # set some defaults, which will be overwritten by the config file
45     config.add_section('server')
46     config.set('server', 'banner', 'Welcome to Electrum!')
47     config.set('server', 'host', 'localhost')
48     config.set('server', 'report_host', '')
49     config.set('server', 'stratum_tcp_port', '50001')
50     config.set('server', 'stratum_http_port', '8081')
51     config.set('server', 'stratum_tcp_ssl_port', '')
52     config.set('server', 'stratum_http_ssl_port', '')
53     config.set('server', 'report_stratum_tcp_port', '')
54     config.set('server', 'report_stratum_http_port', '')
55     config.set('server', 'report_stratum_tcp_ssl_port', '')
56     config.set('server', 'report_stratum_http_ssl_port', '')
57     config.set('server', 'ssl_certfile', '')
58     config.set('server', 'ssl_keyfile', '')
59     config.set('server', 'password', '')
60     config.set('server', 'irc', 'yes')
61     config.set('server', 'irc_nick', '')
62     config.set('server', 'coin', '')
63     config.set('server', 'datadir', '')
64
65     # use leveldb as default
66     config.set('server', 'backend', 'leveldb')
67     config.add_section('leveldb')
68     config.set('leveldb', 'path', '/dev/shm/electrum_db')
69     config.set('leveldb', 'pruning_limit', '100')
70
71     for path in ('/etc/', ''):
72         filename = path + 'electrum.conf'
73         attempt_read_config(config, filename)
74
75     try:
76         with open('/etc/electrum.banner', 'r') as f:
77             config.set('server', 'banner', f.read())
78     except IOError:
79         pass
80
81     return config
82
83
84 def run_rpc_command(command, stratum_tcp_port):
85     try:
86         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
87         s.connect((host, int(stratum_tcp_port)))
88     except:
89         print "cannot connect to server."
90         return
91
92     method = 'server.' + command
93     request = json.dumps({'id': 0, 'method': method, 'params': [password]})
94     s.send(request + '\n')
95     msg = ''
96     while True:
97         o = s.recv(1024)
98         msg += o
99         if msg.find('\n') != -1:
100             break
101     s.close()
102     r = json.loads(msg).get('result')
103
104     if command == 'info':
105         now = time.time()
106         print 'type           address   sub  version  time'
107         for item in r:
108             print '%4s   %15s   %3s  %7s  %.2f' % (item.get('name'),
109                                                    item.get('address'),
110                                                    item.get('subscriptions'),
111                                                    item.get('version'),
112                                                    (now - item.get('time')),
113                                                    )
114     else:
115         print r
116
117
118 if __name__ == '__main__':
119     config = create_config()
120     password = config.get('server', 'password')
121     host = config.get('server', 'host')
122     stratum_tcp_port = config.get('server', 'stratum_tcp_port')
123     stratum_http_port = config.get('server', 'stratum_http_port')
124     stratum_tcp_ssl_port = config.get('server', 'stratum_tcp_ssl_port')
125     stratum_http_ssl_port = config.get('server', 'stratum_http_ssl_port')
126     ssl_certfile = config.get('server', 'ssl_certfile')
127     ssl_keyfile = config.get('server', 'ssl_keyfile')
128
129     if stratum_tcp_ssl_port or stratum_http_ssl_port:
130         assert ssl_certfile and ssl_keyfile
131
132     if len(sys.argv) > 1:
133         run_rpc_command(sys.argv[1], stratum_tcp_port)
134         sys.exit(0)
135
136     from processor import Dispatcher, print_log
137     from backends.irc import ServerProcessor
138
139     backend_name = config.get('server', 'backend')
140     if backend_name == 'abe':
141         from backends.abe import BlockchainProcessor
142     elif backend_name == 'libbitcoin':
143         from backends.libbitcoin import BlockchainProcessor
144     elif backend_name == 'leveldb':
145         from backends.bitcoind import BlockchainProcessor
146     else:
147         print "Unknown backend '%s' specified\n" % backend_name
148         sys.exit(1)
149
150     for i in xrange(5):
151         print ""
152     print_log("Starting Electrum server on", host)
153
154     # Create hub
155     dispatcher = Dispatcher(config)
156     shared = dispatcher.shared
157
158     # Create and register processors
159     chain_proc = BlockchainProcessor(config, shared)
160     dispatcher.register('blockchain', chain_proc)
161
162     server_proc = ServerProcessor(config)
163     dispatcher.register('server', server_proc)
164
165     transports = []
166     # Create various transports we need
167     if stratum_tcp_port:
168         from transports.stratum_tcp import TcpServer
169         tcp_server = TcpServer(dispatcher, host, int(stratum_tcp_port), False, None, None)
170         transports.append(tcp_server)
171
172     if stratum_tcp_ssl_port:
173         from transports.stratum_tcp import TcpServer
174         tcp_server = TcpServer(dispatcher, host, int(stratum_tcp_ssl_port), True, ssl_certfile, ssl_keyfile)
175         transports.append(tcp_server)
176
177     if stratum_http_port:
178         from transports.stratum_http import HttpServer
179         http_server = HttpServer(dispatcher, host, int(stratum_http_port), False, None, None)
180         transports.append(http_server)
181
182     if stratum_http_ssl_port:
183         from transports.stratum_http import HttpServer
184         http_server = HttpServer(dispatcher, host, int(stratum_http_ssl_port), True, ssl_certfile, ssl_keyfile)
185         transports.append(http_server)
186
187     for server in transports:
188         server.start()
189
190     while not shared.stopped():
191         try:
192             time.sleep(1)
193         except:
194             shared.stop()
195
196     print_log("Electrum Server stopped")