3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-27 21:19:31 +01:00

relay: stop execution if spawn lock acquire fails

Also, make the lock timeout a consistent, global variable.

(partial merge of commit e24bc54bbcefc40bf73a197de2dc24f7cd42cf79)
This commit is contained in:
James Lu 2017-05-04 21:16:25 -07:00
parent fd15600d80
commit 5c7752a203

View File

@ -8,6 +8,9 @@ from pylinkirc import utils, world, conf, structures
from pylinkirc.log import log from pylinkirc.log import log
from pylinkirc.coremods import permissions from pylinkirc.coremods import permissions
# Sets the timeout to wait for as individual servers / the PyLink daemon to start up.
TCONDITION_TIMEOUT = 2
### GLOBAL (statekeeping) VARIABLES ### GLOBAL (statekeeping) VARIABLES
relayusers = defaultdict(dict) relayusers = defaultdict(dict)
relayservers = defaultdict(dict) relayservers = defaultdict(dict)
@ -31,16 +34,16 @@ def initialize_all(irc):
# Wait for all IRC objects to be created first. This prevents # Wait for all IRC objects to be created first. This prevents
# relay servers from being spawned too early (before server authentication), # relay servers from being spawned too early (before server authentication),
# which would break connections. # which would break connections.
world.started.wait(2) if world.started.wait(TCONDITION_TIMEOUT):
for chanpair, entrydata in db.items():
# Iterate over all the channels stored in our relay links DB.
network, channel = chanpair
for chanpair, entrydata in db.items(): # Initialize each relay channel on their home network, and on every linked one too.
# Iterate over all the channels stored in our relay links DB.
network, channel = chanpair
# Initialize each relay channel on their home network, and on every linked one too.
initialize_channel(irc, channel)
for link in entrydata['links']:
network, channel = link
initialize_channel(irc, channel) initialize_channel(irc, channel)
for link in entrydata['links']:
network, channel = link
initialize_channel(irc, channel)
def main(irc=None): def main(irc=None):
"""Main function, called during plugin loading at start.""" """Main function, called during plugin loading at start."""
@ -209,37 +212,40 @@ def get_prefix_modes(irc, remoteirc, channel, user, mlist=None):
return modes return modes
def spawn_relay_server(irc, remoteirc): def spawn_relay_server(irc, remoteirc):
irc.connected.wait(5) if irc.connected.wait(TCONDITION_TIMEOUT):
try: try:
# ENDBURST is delayed by 3 secs on supported IRCds to prevent # ENDBURST is delayed by 3 secs on supported IRCds to prevent
# triggering join-flood protection and the like. # triggering join-flood protection and the like.
suffix = conf.conf.get('relay', {}).get('server_suffix', 'relay') suffix = conf.conf.get('relay', {}).get('server_suffix', 'relay')
# Strip any leading or trailing .'s # Strip any leading or trailing .'s
suffix = suffix.strip('.') suffix = suffix.strip('.')
sid = irc.proto.spawnServer('%s.%s' % (remoteirc.name, suffix), sid = irc.proto.spawnServer('%s.%s' % (remoteirc.name, suffix),
desc="PyLink Relay network - %s" % desc="PyLink Relay network - %s" %
(remoteirc.getFullNetworkName()), endburst_delay=3) (remoteirc.getFullNetworkName()), endburst_delay=3)
except (RuntimeError, ValueError): # Network not initialized yet, or a server name conflict. except (RuntimeError, ValueError): # Network not initialized yet, or a server name conflict.
log.exception('(%s) Failed to spawn server for %r (possible jupe?):', log.exception('(%s) Failed to spawn server for %r (possible jupe?):',
irc.name, remoteirc.name) irc.name, remoteirc.name)
# We will just bail here. Disconnect the bad network. # We will just bail here. Disconnect the bad network.
irc.disconnect() irc.disconnect()
return return
# Mark the server as a relay server # Mark the server as a relay server
irc.servers[sid].remote = remoteirc.name irc.servers[sid].remote = remoteirc.name
# Assign the newly spawned server as our relay server for the target net. # Assign the newly spawned server as our relay server for the target net.
relayservers[irc.name][remoteirc.name] = sid relayservers[irc.name][remoteirc.name] = sid
return sid return sid
else:
log.debug('(%s) skipping spawn_relay_server(%s, %s); the remote is not ready yet',
irc.name, irc.name, remoteirc.name)
def get_remote_sid(irc, remoteirc, spawn_if_missing=True): def get_remote_sid(irc, remoteirc, spawn_if_missing=True):
"""Gets the remote server SID representing remoteirc on irc, spawning """Gets the remote server SID representing remoteirc on irc, spawning
it if it doesn't exist (and spawn_if_missing is enabled).""" it if it doesn't exist (and spawn_if_missing is enabled)."""
log.debug('(%s) Grabbing spawnlocks_servers[%s]', irc.name, irc.name) log.debug('(%s) Grabbing spawnlocks_servers[%s]', irc.name, irc.name)
if spawnlocks_servers[irc.name].acquire(5): if spawnlocks_servers[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
try: try:
sid = relayservers[irc.name][remoteirc.name] sid = relayservers[irc.name][remoteirc.name]
except KeyError: except KeyError:
@ -343,36 +349,38 @@ def get_remote_user(irc, remoteirc, user, spawn_if_missing=True, times_tagged=0)
spawning one if it doesn't exist and spawn_if_missing is True.""" spawning one if it doesn't exist and spawn_if_missing is True."""
# Wait until the network is working before trying to spawn anything. # Wait until the network is working before trying to spawn anything.
irc.connected.wait(5) if irc.connected.wait(TCONDITION_TIMEOUT):
# Don't spawn clones for registered service bots.
sbot = irc.getServiceBot(user)
if sbot:
return sbot.uids.get(remoteirc.name)
# Don't spawn clones for registered service bots. log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name)
sbot = irc.getServiceBot(user) if spawnlocks[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
if sbot: # Be sort-of thread safe: lock the user spawns for the current net first.
return sbot.uids.get(remoteirc.name) u = None
try:
# Look up the existing user, stored here as dict entries in the format:
# {('ournet', 'UID'): {'remotenet1': 'UID1', 'remotenet2': 'UID2'}}
u = relayusers[(irc.name, user)][remoteirc.name]
except KeyError:
# User doesn't exist. Spawn a new one if requested.
if spawn_if_missing:
u = spawn_relay_user(irc, remoteirc, user, times_tagged=times_tagged)
log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name) # This is a sanity check to make sure netsplits and other state resets
if spawnlocks[irc.name].acquire(5): # don't break the relayer. If it turns out there was a client in our relayusers
# Be sort-of thread safe: lock the user spawns for the current net first. # cache for the requested UID, but it doesn't match the request,
u = None # assume it was a leftover from the last split and replace it with a new one.
try: if u and ((u not in remoteirc.users) or remoteirc.users[u].remote != (irc.name, user)):
# Look up the existing user, stored here as dict entries in the format:
# {('ournet', 'UID'): {'remotenet1': 'UID1', 'remotenet2': 'UID2'}}
u = relayusers[(irc.name, user)][remoteirc.name]
except KeyError:
# User doesn't exist. Spawn a new one if requested.
if spawn_if_missing:
u = spawn_relay_user(irc, remoteirc, user, times_tagged=times_tagged) u = spawn_relay_user(irc, remoteirc, user, times_tagged=times_tagged)
# This is a sanity check to make sure netsplits and other state resets spawnlocks[irc.name].release()
# don't break the relayer. If it turns out there was a client in our relayusers
# cache for the requested UID, but it doesn't match the request,
# assume it was a leftover from the last split and replace it with a new one.
if u and ((u not in remoteirc.users) or remoteirc.users[u].remote != (irc.name, user)):
u = spawn_relay_user(irc, remoteirc, user, times_tagged=times_tagged)
spawnlocks[irc.name].release() return u
else:
return u log.debug('(%s) skipping spawn_relay_user(%s, %s, %s, ...); the remote is not ready yet',
irc.name, irc.name, remoteirc.name, user)
def get_orig_user(irc, user, targetirc=None): def get_orig_user(irc, user, targetirc=None):
""" """
@ -937,7 +945,7 @@ def handle_quit(irc, numeric, command, args):
# Lock the user spawning mechanism before proceeding, since we're going to be # Lock the user spawning mechanism before proceeding, since we're going to be
# deleting client from the relayusers cache. # deleting client from the relayusers cache.
log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name) log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name)
if spawnlocks[irc.name].acquire(5): if spawnlocks[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
for netname, user in relayusers[(irc.name, numeric)].copy().items(): for netname, user in relayusers[(irc.name, numeric)].copy().items():
remoteirc = world.networkobjects[netname] remoteirc = world.networkobjects[netname]
try: # Try to quit the client. If this fails because they're missing, bail. try: # Try to quit the client. If this fails because they're missing, bail.
@ -1468,7 +1476,7 @@ def handle_disconnect(irc, numeric, command, args):
# Quit all of our users' representations on other nets, and remove # Quit all of our users' representations on other nets, and remove
# them from our relay clients index. # them from our relay clients index.
log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name) log.debug('(%s) Grabbing spawnlocks[%s]', irc.name, irc.name)
if spawnlocks[irc.name].acquire(5): if spawnlocks[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
for k, v in relayusers.copy().items(): for k, v in relayusers.copy().items():
if irc.name in v: if irc.name in v:
del relayusers[k][irc.name] del relayusers[k][irc.name]
@ -1479,7 +1487,7 @@ def handle_disconnect(irc, numeric, command, args):
# SQUIT all relay pseudoservers spawned for us, and remove them # SQUIT all relay pseudoservers spawned for us, and remove them
# from our relay subservers index. # from our relay subservers index.
log.debug('(%s) Grabbing spawnlocks_servers[%s]', irc.name, irc.name) log.debug('(%s) Grabbing spawnlocks_servers[%s]', irc.name, irc.name)
if spawnlocks_servers[irc.name].acquire(5): if spawnlocks_servers[irc.name].acquire(timeout=TCONDITION_TIMEOUT):
for name, ircobj in world.networkobjects.copy().items(): for name, ircobj in world.networkobjects.copy().items():
if name != irc.name: if name != irc.name:
try: try:
@ -1855,7 +1863,6 @@ def linked(irc, source, args):
# Sort the list of shared channels when displaying # Sort the list of shared channels when displaying
for k, v in sorted(db.items()): for k, v in sorted(db.items()):
# Skip if we're filtering by network and the network given isn't relayed # Skip if we're filtering by network and the network given isn't relayed
# to the channel. # to the channel.
if net and not (net == k[0] or net in [link[0] for link in v['links']]): if net and not (net == k[0] or net in [link[0] for link in v['links']]):