Exemplo n.º 1
0
def run():
    realnets = dict((name, net) for name, net in networks.nets.iteritems() if '_testnet' not in name)
    
    parser = fixargparse.FixedArgumentParser(description='p2pool (version %s)' % (p2pool.__version__,), fromfile_prefix_chars='@')
    parser.add_argument('--version', action='version', version=p2pool.__version__)
    parser.add_argument('--net',
        help='use specified network (default: bitbar)',
        action='store', choices=sorted(realnets), default='bitbar', dest='net_name')
    parser.add_argument('--testnet',
        help='''use the network's testnet''',
        action='store_const', const=True, default=False, dest='testnet')
    parser.add_argument('--debug',
        help='enable debugging mode',
        action='store_const', const=True, default=False, dest='debug')
    parser.add_argument('--datadir',
        help='store data in this directory (default: <directory run_p2pool.py is in>/data)',
        type=str, action='store', default=None, dest='datadir')
    parser.add_argument('--logfile',
        help='''log to this file (default: data/<NET>/log)''',
        type=str, action='store', default=None, dest='logfile')
    parser.add_argument('--merged',
        help='call getauxblock on this url to get work for merged mining (example: http://ncuser:[email protected]:10332/)',
        type=str, action='append', default=[], dest='merged_urls')
    parser.add_argument('--give-author', metavar='DONATION_PERCENTAGE',
        help='donate this percentage of work towards the development of p2pool (default: 0.0)',
        type=float, action='store', default=0.0, dest='donation_percentage')
    parser.add_argument('--iocp',
        help='use Windows IOCP API in order to avoid errors due to large number of sockets being open',
        action='store_true', default=False, dest='iocp')
    parser.add_argument('--irc-announce',
        help='announce any blocks found on irc://irc.freenode.net/#p2pool',
        action='store_true', default=False, dest='irc_announce')
    parser.add_argument('--no-bugreport',
        help='disable submitting caught exceptions to the author',
        action='store_true', default=False, dest='no_bugreport')
    
    p2pool_group = parser.add_argument_group('p2pool interface')
    p2pool_group.add_argument('--p2pool-port', metavar='PORT',
        help='use port PORT to listen for connections (forward this port from your router!) (default: %s)' % ', '.join('%s:%i' % (name, net.P2P_PORT) for name, net in sorted(realnets.items())),
        type=int, action='store', default=None, dest='p2pool_port')
    p2pool_group.add_argument('-n', '--p2pool-node', metavar='ADDR[:PORT]',
        help='connect to existing p2pool node at ADDR listening on port PORT (defaults to default p2pool P2P port) in addition to builtin addresses',
        type=str, action='append', default=[], dest='p2pool_nodes')
    parser.add_argument('--disable-upnp',
        help='''don't attempt to use UPnP to forward p2pool's P2P port from the Internet to this computer''',
        action='store_false', default=True, dest='upnp')
    p2pool_group.add_argument('--max-conns', metavar='CONNS',
        help='maximum incoming connections (default: 40)',
        type=int, action='store', default=40, dest='p2pool_conns')
    p2pool_group.add_argument('--outgoing-conns', metavar='CONNS',
        help='outgoing connections (default: 6)',
        type=int, action='store', default=6, dest='p2pool_outgoing_conns')
    
    worker_group = parser.add_argument_group('worker interface')
    worker_group.add_argument('-w', '--worker-port', metavar='PORT or ADDR:PORT',
        help='listen on PORT on interface with ADDR for RPC connections from miners (default: all interfaces, %s)' % ', '.join('%s:%i' % (name, net.WORKER_PORT) for name, net in sorted(realnets.items())),
        type=str, action='store', default=None, dest='worker_endpoint')
    worker_group.add_argument('-f', '--fee', metavar='FEE_PERCENTAGE',
        help='''charge workers mining to their own bitcoin address (by setting their miner's username to a bitcoin address) this percentage fee to mine on your p2pool instance. Amount displayed at http://127.0.0.1:WORKER_PORT/fee (default: 0)''',
        type=float, action='store', default=0, dest='worker_fee')
    
    bitcoind_group = parser.add_argument_group('bitcoind interface')
    bitcoind_group.add_argument('--bitcoind-address', metavar='BITCOIND_ADDRESS',
        help='connect to this address (default: 127.0.0.1)',
        type=str, action='store', default='127.0.0.1', dest='bitcoind_address')
    bitcoind_group.add_argument('--bitcoind-rpc-port', metavar='BITCOIND_RPC_PORT',
        help='''connect to JSON-RPC interface at this port (default: %s <read from bitcoin.conf if password not provided>)''' % ', '.join('%s:%i' % (name, net.PARENT.RPC_PORT) for name, net in sorted(realnets.items())),
        type=int, action='store', default=None, dest='bitcoind_rpc_port')
    bitcoind_group.add_argument('--bitcoind-rpc-ssl',
        help='connect to JSON-RPC interface using SSL',
        action='store_true', default=False, dest='bitcoind_rpc_ssl')
    bitcoind_group.add_argument('--bitcoind-p2p-port', metavar='BITCOIND_P2P_PORT',
        help='''connect to P2P interface at this port (default: %s <read from bitcoin.conf if password not provided>)''' % ', '.join('%s:%i' % (name, net.PARENT.P2P_PORT) for name, net in sorted(realnets.items())),
        type=int, action='store', default=None, dest='bitcoind_p2p_port')
    
    bitcoind_group.add_argument(metavar='BITCOIND_RPCUSERPASS',
        help='bitcoind RPC interface username, then password, space-separated (only one being provided will cause the username to default to being empty, and none will cause P2Pool to read them from bitcoin.conf)',
        type=str, action='store', default=[], nargs='*', dest='bitcoind_rpc_userpass')
    
    args = parser.parse_args()
    
    if args.debug:
        p2pool.DEBUG = True
        defer.setDebugging(True)
    else:
        p2pool.DEBUG = False
    
    net_name = args.net_name + ('_testnet' if args.testnet else '')
    net = networks.nets[net_name]
    
    datadir_path = os.path.join((os.path.join(os.path.dirname(sys.argv[0]), 'data') if args.datadir is None else args.datadir), net_name)
    if not os.path.exists(datadir_path):
        os.makedirs(datadir_path)
    
    if len(args.bitcoind_rpc_userpass) > 2:
        parser.error('a maximum of two arguments are allowed')
    args.bitcoind_rpc_username, args.bitcoind_rpc_password = ([None, None] + args.bitcoind_rpc_userpass)[-2:]
    
    if args.bitcoind_rpc_password is None:
        conf_path = net.PARENT.CONF_FILE_FUNC()
        if not os.path.exists(conf_path):
            parser.error('''Bitcoin configuration file not found. Manually enter your RPC password.\r\n'''
                '''If you actually haven't created a configuration file, you should create one at %s with the text:\r\n'''
                '''\r\n'''
                '''server=1\r\n'''
                '''rpcpassword=%x\r\n'''
                '''\r\n'''
                '''Keep that password secret! After creating the file, restart Bitcoin.''' % (conf_path, random.randrange(2**128)))
        conf = open(conf_path, 'rb').read()
        contents = {}
        for line in conf.splitlines(True):
            if '#' in line:
                line = line[:line.index('#')]
            if '=' not in line:
                continue
            k, v = line.split('=', 1)
            contents[k.strip()] = v.strip()
        for conf_name, var_name, var_type in [
            ('rpcuser', 'bitcoind_rpc_username', str),
            ('rpcpassword', 'bitcoind_rpc_password', str),
            ('rpcport', 'bitcoind_rpc_port', int),
            ('port', 'bitcoind_p2p_port', int),
        ]:
            if getattr(args, var_name) is None and conf_name in contents:
                setattr(args, var_name, var_type(contents[conf_name]))
        if args.bitcoind_rpc_password is None:
            parser.error('''Bitcoin configuration file didn't contain an rpcpassword= line! Add one!''')
    
    if args.bitcoind_rpc_username is None:
        args.bitcoind_rpc_username = ''
    
    if args.bitcoind_rpc_port is None:
        args.bitcoind_rpc_port = net.PARENT.RPC_PORT
    
    if args.bitcoind_p2p_port is None:
        args.bitcoind_p2p_port = net.PARENT.P2P_PORT
    
    if args.p2pool_port is None:
        args.p2pool_port = net.P2P_PORT
    
    if args.p2pool_outgoing_conns > 10:
        parser.error('''--outgoing-conns can't be more than 10''')
    
    if args.worker_endpoint is None:
        worker_endpoint = '', net.WORKER_PORT
    elif ':' not in args.worker_endpoint:
        worker_endpoint = '', int(args.worker_endpoint)
    else:
        addr, port = args.worker_endpoint.rsplit(':', 1)
        worker_endpoint = addr, int(port)

    def separate_url(url):
        s = urlparse.urlsplit(url)
        if '@' not in s.netloc:
            parser.error('merged url netloc must contain an "@"')
        userpass, new_netloc = s.netloc.rsplit('@', 1)
        return urlparse.urlunsplit(s._replace(netloc=new_netloc)), userpass
    merged_urls = map(separate_url, args.merged_urls)
    
    if args.logfile is None:
        args.logfile = os.path.join(datadir_path, 'log')
    
    logfile = logging.LogFile(args.logfile)
    pipe = logging.TimestampingPipe(logging.TeePipe([logging.EncodeReplacerPipe(sys.stderr), logfile]))
    sys.stdout = logging.AbortPipe(pipe)
    sys.stderr = log.DefaultObserver.stderr = logging.AbortPipe(logging.PrefixPipe(pipe, '> '))
    if hasattr(signal, "SIGUSR1"):
        def sigusr1(signum, frame):
            print 'Caught SIGUSR1, closing %r...' % (args.logfile,)
            logfile.reopen()
            print '...and reopened %r after catching SIGUSR1.' % (args.logfile,)
        signal.signal(signal.SIGUSR1, sigusr1)
    task.LoopingCall(logfile.reopen).start(5)
    
    class ErrorReporter(object):
        def __init__(self):
            self.last_sent = None
        
        def emit(self, eventDict):
            if not eventDict["isError"]:
                return
            
            if self.last_sent is not None and time.time() < self.last_sent + 5:
                return
            self.last_sent = time.time()
            
            if 'failure' in eventDict:
                text = ((eventDict.get('why') or 'Unhandled Error')
                    + '\n' + eventDict['failure'].getTraceback())
            else:
                text = " ".join([str(m) for m in eventDict["message"]]) + "\n"
            
            from twisted.web import client
            client.getPage(
                url='http://u.forre.st/p2pool_error.cgi',
                method='POST',
                postdata=p2pool.__version__ + ' ' + net.NAME + '\n' + text,
                timeout=15,
            ).addBoth(lambda x: None)
    if not args.no_bugreport:
        log.addObserver(ErrorReporter().emit)
    
    reactor.callWhenRunning(main, args, net, datadir_path, merged_urls, worker_endpoint)
    reactor.run()
Exemplo n.º 2
0
         parser.error('error parsing address: ' + repr(e))
 else:
     args.pubkey_hash = None
 
 def separate_url(url):
     s = urlparse.urlsplit(url)
     if '@' not in s.netloc:
         parser.error('merged url netloc must contain an "@"')
     userpass, new_netloc = s.netloc.rsplit('@', 1)
     return urlparse.urlunsplit(s._replace(netloc=new_netloc)), userpass
 merged_urls = map(separate_url, args.merged_urls)
 
 if args.logfile is None:
     args.logfile = os.path.join(datadir_path, 'log')
 
 logfile = logging.LogFile(args.logfile)
 pipe = logging.TimestampingPipe(logging.TeePipe([logging.EncodeReplacerPipe(sys.stderr), logfile]))
 sys.stdout = logging.AbortPipe(pipe)
 sys.stderr = log.DefaultObserver.stderr = logging.AbortPipe(logging.PrefixPipe(pipe, '> '))
 if hasattr(signal, "SIGUSR1"):
     def sigusr1(signum, frame):
         print 'Caught SIGUSR1, closing %r...' % (args.logfile,)
         logfile.reopen()
         print '...and reopened %r after catching SIGUSR1.' % (args.logfile,)
     signal.signal(signal.SIGUSR1, sigusr1)
 deferral.RobustLoopingCall(logfile.reopen).start(5)
 
 class ErrorReporter(object):
     def __init__(self):
         self.last_sent = None
     
Exemplo n.º 3
0
def run():
    if not hasattr(tcp.Client, 'abortConnection'):
        print "Twisted doesn't have abortConnection! Upgrade to a newer version of Twisted to avoid memory leaks!"
        print 'Pausing for 3 seconds...'
        time.sleep(3)

    realnets = dict((name, net) for name, net in networks.nets.iteritems()
                    if '_testnet' not in name)

    parser = fixargparse.FixedArgumentParser(
        description='p2pool (version %s)' % (p2pool.__version__, ),
        fromfile_prefix_chars='@')
    parser.add_argument('--version',
                        action='version',
                        version=p2pool.__version__)
    parser.add_argument('--net',
                        help='use specified network (default: dash)',
                        action='store',
                        choices=sorted(realnets),
                        default='dash',
                        dest='net_name')
    parser.add_argument('--testnet',
                        help='''use the network's testnet''',
                        action='store_const',
                        const=True,
                        default=False,
                        dest='testnet')
    parser.add_argument('--debug',
                        help='enable debugging mode',
                        action='store_const',
                        const=True,
                        default=False,
                        dest='debug')
    parser.add_argument('--rconsole',
                        help='enable rconsole debugging mode (requires rfoo)',
                        action='store_const',
                        const=True,
                        default=False,
                        dest='rconsole')

    parser.add_argument(
        '-a',
        '--address',
        help=
        'generate payouts to this address (default: <address requested from dashd>), or (dynamic)',
        type=str,
        action='store',
        default=None,
        dest='address')
    parser.add_argument(
        '-i',
        '--numaddresses',
        help=
        'number of dash auto-generated addresses to maintain for getwork dynamic address allocation',
        type=int,
        action='store',
        default=2,
        dest='numaddresses')
    parser.add_argument(
        '-t',
        '--timeaddresses',
        help=
        'seconds between acquisition of new address and removal of single old (default: 2 days or 172800s)',
        type=int,
        action='store',
        default=172800,
        dest='timeaddresses')
    parser.add_argument(
        '--datadir',
        help=
        'store data in this directory (default: <directory run_p2pool.py is in>/data)',
        type=str,
        action='store',
        default=None,
        dest='datadir')
    parser.add_argument('--logfile',
                        help='''log to this file (default: data/<NET>/log)''',
                        type=str,
                        action='store',
                        default=None,
                        dest='logfile')
    parser.add_argument(
        '--web-static',
        help=
        'use an alternative web frontend in this directory (otherwise use the built-in frontend)',
        type=str,
        action='store',
        default=None,
        dest='web_static')
    parser.add_argument(
        '--merged',
        help=
        'call getauxblock on this url to get work for merged mining (example: http://ncuser:[email protected]:10332/)',
        type=str,
        action='append',
        default=[],
        dest='merged_urls')
    parser.add_argument('--coinbtext',
                        help='append this text to the coinbase',
                        type=str,
                        action='append',
                        default=[],
                        dest='coinb_texts')
    parser.add_argument(
        '--give-author',
        metavar='DONATION_PERCENTAGE',
        help=
        'donate this percentage of work towards the development of p2pool (default: 1.0)',
        type=float,
        action='store',
        default=1.0,
        dest='donation_percentage')
    parser.add_argument(
        '--iocp',
        help=
        'use Windows IOCP API in order to avoid errors due to large number of sockets being open',
        action='store_true',
        default=False,
        dest='iocp')
    parser.add_argument(
        '--irc-announce',
        help='announce any blocks found on irc://irc.freenode.net/#p2pool',
        action='store_true',
        default=False,
        dest='irc_announce')
    parser.add_argument(
        '--no-bugreport',
        help='disable submitting caught exceptions to the author',
        action='store_true',
        default=False,
        dest='no_bugreport')

    p2pool_group = parser.add_argument_group('p2pool interface')
    p2pool_group.add_argument(
        '--p2pool-port',
        metavar='PORT',
        help=
        'use port PORT to listen for connections (forward this port from your router!) (default: %s)'
        % ', '.join('%s:%i' % (name, net.P2P_PORT)
                    for name, net in sorted(realnets.items())),
        type=int,
        action='store',
        default=None,
        dest='p2pool_port')
    p2pool_group.add_argument(
        '-n',
        '--p2pool-node',
        metavar='ADDR[:PORT]',
        help=
        'connect to existing p2pool node at ADDR listening on port PORT (defaults to default p2pool P2P port) in addition to builtin addresses',
        type=str,
        action='append',
        default=[],
        dest='p2pool_nodes')
    parser.add_argument(
        '--disable-upnp',
        help=
        '''don't attempt to use UPnP to forward p2pool's P2P port from the Internet to this computer''',
        action='store_false',
        default=True,
        dest='upnp')
    p2pool_group.add_argument(
        '--max-conns',
        metavar='CONNS',
        help='maximum incoming connections (default: 40)',
        type=int,
        action='store',
        default=40,
        dest='p2pool_conns')
    p2pool_group.add_argument('--outgoing-conns',
                              metavar='CONNS',
                              help='outgoing connections (default: 6)',
                              type=int,
                              action='store',
                              default=6,
                              dest='p2pool_outgoing_conns')
    p2pool_group.add_argument(
        '--external-ip',
        metavar='ADDR[:PORT]',
        help=
        'specify your own public IP address instead of asking peers to discover it, useful for running dual WAN or asymmetric routing',
        type=str,
        action='store',
        default=None,
        dest='p2pool_external_ip')
    parser.add_argument(
        '--disable-advertise',
        help=
        '''don't advertise local IP address as being available for incoming connections. useful for running a dark node, along with multiple -n ADDR's and --outgoing-conns 0''',
        action='store_false',
        default=True,
        dest='advertise_ip')

    worker_group = parser.add_argument_group('worker interface')
    worker_group.add_argument(
        '-w',
        '--worker-port',
        metavar='PORT or ADDR:PORT',
        help=
        'listen on PORT on interface with ADDR for RPC connections from miners (default: all interfaces, %s)'
        % ', '.join('%s:%i' % (name, net.WORKER_PORT)
                    for name, net in sorted(realnets.items())),
        type=str,
        action='store',
        default=None,
        dest='worker_endpoint')
    worker_group.add_argument(
        '-f',
        '--fee',
        metavar='FEE_PERCENTAGE',
        help=
        '''charge workers mining to their own dash address (by setting their miner's username to a dash address) this percentage fee to mine on your p2pool instance. Amount displayed at http://127.0.0.1:WORKER_PORT/fee (default: 0)''',
        type=float,
        action='store',
        default=0,
        dest='worker_fee')
    worker_group.add_argument(
        '--miner-share-rate',
        metavar='SHARES_PER_MINUTE',
        help='number of psuedoshares per minute for each miner',
        type=float,
        action='store',
        default=None,
        dest='miner_share_rate')
    worker_group.add_argument(
        '--address-share-rate',
        metavar='SHARES_PER_MINUTE',
        help='number of psuedoshares per minute for each address',
        type=float,
        action='store',
        default=None,
        dest='address_share_rate')
    worker_group.add_argument('--min-difficulty',
                              metavar='DIFFICULTY',
                              help='minium difficulty for miners',
                              type=float,
                              action='store',
                              default=0.01,
                              dest='min_difficulty')

    dashd_group = parser.add_argument_group('dashd interface')
    dashd_group.add_argument(
        '--dashd-config-path',
        metavar='DASHD_CONFIG_PATH',
        help='custom configuration file path (when dashd -conf option used)',
        type=str,
        action='store',
        default=None,
        dest='dashd_config_path')
    dashd_group.add_argument(
        '--dashd-address',
        metavar='DASHD_ADDRESS',
        help='connect to this address (default: 127.0.0.1)',
        type=str,
        action='store',
        default='127.0.0.1',
        dest='dashd_address')
    dashd_group.add_argument(
        '--dashd-rpc-port',
        metavar='DASHD_RPC_PORT',
        help=
        '''connect to JSON-RPC interface at this port (default: %s <read from dash.conf if password not provided>)'''
        % ', '.join('%s:%i' % (name, net.PARENT.RPC_PORT)
                    for name, net in sorted(realnets.items())),
        type=int,
        action='store',
        default=None,
        dest='dashd_rpc_port')
    dashd_group.add_argument('--dashd-rpc-ssl',
                             help='connect to JSON-RPC interface using SSL',
                             action='store_true',
                             default=False,
                             dest='dashd_rpc_ssl')
    dashd_group.add_argument(
        '--dashd-p2p-port',
        metavar='DASHD_P2P_PORT',
        help=
        '''connect to P2P interface at this port (default: %s <read from dash.conf if password not provided>)'''
        % ', '.join('%s:%i' % (name, net.PARENT.P2P_PORT)
                    for name, net in sorted(realnets.items())),
        type=int,
        action='store',
        default=None,
        dest='dashd_p2p_port')

    dashd_group.add_argument(
        metavar='DASHD_RPCUSERPASS',
        help=
        'dashd RPC interface username, then password, space-separated (only one being provided will cause the username to default to being empty, and none will cause P2Pool to read them from dash.conf)',
        type=str,
        action='store',
        default=[],
        nargs='*',
        dest='dashd_rpc_userpass')

    args = parser.parse_args()

    if args.debug:
        p2pool.DEBUG = True
        defer.setDebugging(True)
    else:
        p2pool.DEBUG = False

    net_name = args.net_name + ('_testnet' if args.testnet else '')
    net = networks.nets[net_name]

    datadir_path = os.path.join(
        (os.path.join(os.path.dirname(sys.argv[0]), 'data')
         if args.datadir is None else args.datadir), net_name)
    if not os.path.exists(datadir_path):
        os.makedirs(datadir_path)

    if len(args.dashd_rpc_userpass) > 2:
        parser.error('a maximum of two arguments are allowed')
    args.dashd_rpc_username, args.dashd_rpc_password = (
        [None, None] + args.dashd_rpc_userpass)[-2:]

    if args.dashd_rpc_password is None:
        conf_path = args.dashd_config_path or net.PARENT.CONF_FILE_FUNC()
        if not os.path.exists(conf_path):
            parser.error(
                '''dash configuration file not found. Manually enter your RPC password.\r\n'''
                '''If you actually haven't created a configuration file, you should create one at %s with the text:\r\n'''
                '''\r\n'''
                '''server=1\r\n'''
                '''rpcpassword=%x\r\n'''
                '''\r\n'''
                '''Keep that password secret! After creating the file, restart dash.'''
                % (conf_path, random.randrange(2**128)))
        conf = open(conf_path, 'rb').read()
        contents = {}
        for line in conf.splitlines(True):
            if '#' in line:
                line = line[:line.index('#')]
            if '=' not in line:
                continue
            k, v = line.split('=', 1)
            contents[k.strip()] = v.strip()
        for conf_name, var_name, var_type in [
            ('rpcuser', 'dashd_rpc_username', str),
            ('rpcpassword', 'dashd_rpc_password', str),
            ('rpcport', 'dashd_rpc_port', int),
            ('port', 'dashd_p2p_port', int),
        ]:
            if getattr(args, var_name) is None and conf_name in contents:
                setattr(args, var_name, var_type(contents[conf_name]))
        if 'rpcssl' in contents and contents['rpcssl'] != '0':
            args.dashd_rpc_ssl = True
        if args.dashd_rpc_password is None:
            parser.error(
                '''dash configuration file didn't contain an rpcpassword= line! Add one!'''
            )

    if args.dashd_rpc_username is None:
        args.dashd_rpc_username = ''

    if args.dashd_rpc_port is None:
        args.dashd_rpc_port = net.PARENT.RPC_PORT

    if args.dashd_p2p_port is None:
        args.dashd_p2p_port = net.PARENT.P2P_PORT

    if args.p2pool_port is None:
        args.p2pool_port = net.P2P_PORT

    if args.worker_endpoint is None:
        worker_endpoint = '', net.WORKER_PORT
    elif ':' not in args.worker_endpoint:
        worker_endpoint = '', int(args.worker_endpoint)
    else:
        addr, port = args.worker_endpoint.rsplit(':', 1)
        worker_endpoint = addr, int(port)

    if args.address is not None and args.address != 'dynamic':
        try:
            args.pubkey_hash = dash_data.address_to_pubkey_hash(
                args.address, net.PARENT)
        except Exception as e:
            parser.error('error parsing address: ' + repr(e))
    else:
        args.pubkey_hash = None

    def separate_url(url):
        s = urlparse.urlsplit(url)
        if '@' not in s.netloc:
            parser.error('merged url netloc must contain an "@"')
        userpass, new_netloc = s.netloc.rsplit('@', 1)
        return urlparse.urlunsplit(s._replace(netloc=new_netloc)), userpass

    merged_urls = map(separate_url, args.merged_urls)

    if args.logfile is None:
        args.logfile = os.path.join(datadir_path, 'log')

    logfile = logging.LogFile(args.logfile)
    pipe = logging.TimestampingPipe(
        logging.TeePipe([logging.EncodeReplacerPipe(sys.stderr), logfile]))
    sys.stdout = logging.AbortPipe(pipe)
    sys.stderr = log.DefaultObserver.stderr = logging.AbortPipe(
        logging.PrefixPipe(pipe, '> '))
    if hasattr(signal, "SIGUSR1"):

        def sigusr1(signum, frame):
            print 'Caught SIGUSR1, closing %r...' % (args.logfile, )
            logfile.reopen()
            print '...and reopened %r after catching SIGUSR1.' % (
                args.logfile, )

        signal.signal(signal.SIGUSR1, sigusr1)
    deferral.RobustLoopingCall(logfile.reopen).start(5)

    class ErrorReporter(object):
        def __init__(self):
            self.last_sent = None

        def emit(self, eventDict):
            if not eventDict["isError"]:
                return

            if self.last_sent is not None and time.time() < self.last_sent + 5:
                return
            self.last_sent = time.time()

            if 'failure' in eventDict:
                text = ((eventDict.get('why') or 'Unhandled Error') + '\n' +
                        eventDict['failure'].getTraceback())
            else:
                text = " ".join([str(m) for m in eventDict["message"]]) + "\n"

            from twisted.web import client
            client.getPage(
                url='http://u.forre.st/p2pool_error.cgi',
                method='POST',
                postdata=p2pool.__version__ + ' ' + net.NAME + '\n' + text,
                timeout=15,
            ).addBoth(lambda x: None)

    if not args.no_bugreport:
        log.addObserver(ErrorReporter().emit)
    if args.rconsole:
        from rfoo.utils import rconsole
        rconsole.spawn_server()

    reactor.callWhenRunning(main, args, net, datadir_path, merged_urls,
                            worker_endpoint)
    reactor.run()