mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-24 03:29:28 +01:00
relay: fix race conditions when multiple networks disconnect simultaneously
This commit is contained in:
parent
7b3271b430
commit
69cd3cfbf8
115
plugins/relay.py
115
plugins/relay.py
@ -199,22 +199,8 @@ def getPrefixModes(irc, remoteirc, channel, user, mlist=None):
|
|||||||
modes += remoteirc.cmodes[pmode]
|
modes += remoteirc.cmodes[pmode]
|
||||||
return modes
|
return modes
|
||||||
|
|
||||||
def getRemoteSid(irc, remoteirc):
|
def spawnRelayServer(irc, remoteirc):
|
||||||
"""Gets the remote server SID representing remoteirc on irc, spawning
|
irc.connected.wait()
|
||||||
it if it doesn't exist."""
|
|
||||||
# Don't spawn servers too early.
|
|
||||||
irc.connected.wait(2)
|
|
||||||
|
|
||||||
try:
|
|
||||||
spawnservers = irc.conf['relay']['spawn_servers']
|
|
||||||
except KeyError:
|
|
||||||
spawnservers = True
|
|
||||||
if not spawnservers:
|
|
||||||
return irc.sid
|
|
||||||
with spawnlocks_servers[irc.name]:
|
|
||||||
try:
|
|
||||||
sid = relayservers[irc.name][remoteirc.name]
|
|
||||||
except KeyError:
|
|
||||||
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.
|
||||||
@ -229,28 +215,49 @@ def getRemoteSid(irc, remoteirc):
|
|||||||
handle_disconnect(irc, None, 'PYLINK_DISCONNECT_RELAY_FORCED', {})
|
handle_disconnect(irc, None, 'PYLINK_DISCONNECT_RELAY_FORCED', {})
|
||||||
irc.disconnect()
|
irc.disconnect()
|
||||||
raise
|
raise
|
||||||
else:
|
|
||||||
|
# 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.
|
||||||
relayservers[irc.name][remoteirc.name] = sid
|
relayservers[irc.name][remoteirc.name] = sid
|
||||||
|
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
|
def getRemoteSid(irc, remoteirc):
|
||||||
"""Gets the UID of the relay client for the given IRC network/user pair,
|
"""Gets the remote server SID representing remoteirc on irc, spawning
|
||||||
spawning one if it doesn't exist and spawnIfMissing is True."""
|
it if it doesn't exist."""
|
||||||
# If the user (stored here as {('netname', 'UID'):
|
|
||||||
# {'network1': 'UID1', 'network2': 'UID2'}}) exists, don't spawn it
|
|
||||||
# again!
|
|
||||||
try:
|
try:
|
||||||
if user == irc.pseudoclient.uid:
|
spawnservers = irc.conf['relay']['spawn_servers']
|
||||||
return remoteirc.pseudoclient.uid
|
|
||||||
except AttributeError: # Network hasn't been initialized yet?
|
|
||||||
pass
|
|
||||||
with spawnlocks[irc.name]:
|
|
||||||
try:
|
|
||||||
u = relayusers[(irc.name, user)][remoteirc.name]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
spawnservers = True
|
||||||
|
if not spawnservers:
|
||||||
|
return irc.sid
|
||||||
|
|
||||||
|
with spawnlocks_servers[irc.name]:
|
||||||
|
try:
|
||||||
|
sid = relayservers[irc.name][remoteirc.name]
|
||||||
|
except KeyError:
|
||||||
|
log.debug('(%s) getRemoteSid: %s.relay doesn\'t have a known SID, spawning.', irc.name, remoteirc.name)
|
||||||
|
sid = spawnRelayServer(irc, remoteirc)
|
||||||
|
|
||||||
|
log.debug('(%s) getRemoteSid: got %s for %s.relay', irc.name, sid, remoteirc.name)
|
||||||
|
if sid not in irc.servers:
|
||||||
|
log.debug('(%s) getRemoteSid: SID %s for %s.relay doesn\'t exist, respawning', irc.name, sid, remoteirc.name)
|
||||||
|
# Our stored server doesn't exist anymore. This state is probably a holdover from a netsplit,
|
||||||
|
# so let's refresh it.
|
||||||
|
sid = spawnRelayServer(irc, remoteirc)
|
||||||
|
elif sid in irc.servers and irc.servers[sid].remote != remoteirc.name:
|
||||||
|
log.debug('(%s) getRemoteSid: %s.relay != %s.relay, respawning', irc.name, irc.servers[sid].remote, remoteirc.name)
|
||||||
|
sid = spawnRelayServer(irc, remoteirc)
|
||||||
|
|
||||||
|
log.debug('(%s) getRemoteSid: got %s for %s.relay (round 2)', irc.name, sid, remoteirc.name)
|
||||||
|
return sid
|
||||||
|
|
||||||
|
def spawnRelayUser(irc, remoteirc, user):
|
||||||
userobj = irc.users.get(user)
|
userobj = irc.users.get(user)
|
||||||
if userobj is None or (not spawnIfMissing) or (not remoteirc.connected.is_set()):
|
if userobj is None:
|
||||||
# The query wasn't actually a valid user, or the network hasn't
|
# The query wasn't actually a valid user, or the network hasn't
|
||||||
# been connected yet... Oh well!
|
# been connected yet... Oh well!
|
||||||
return
|
return
|
||||||
@ -310,9 +317,39 @@ def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
|
|||||||
away = userobj.away
|
away = userobj.away
|
||||||
if away:
|
if away:
|
||||||
remoteirc.proto.away(u, away)
|
remoteirc.proto.away(u, away)
|
||||||
|
|
||||||
relayusers[(irc.name, user)][remoteirc.name] = u
|
relayusers[(irc.name, user)][remoteirc.name] = u
|
||||||
return u
|
return u
|
||||||
|
|
||||||
|
def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
|
||||||
|
"""Gets the UID of the relay client for the given IRC network/user pair,
|
||||||
|
spawning one if it doesn't exist and spawnIfMissing is True."""
|
||||||
|
|
||||||
|
log.debug('(%s) getRemoteUser: waiting for irc.connected', irc.name)
|
||||||
|
irc.connected.wait()
|
||||||
|
log.debug('(%s) getRemoteUser: waiting for %s.connected', irc.name, remoteirc.name)
|
||||||
|
remoteirc.connected.wait()
|
||||||
|
|
||||||
|
# If the user (stored here as {('netname', 'UID'):
|
||||||
|
# {'network1': 'UID1', 'network2': 'UID2'}}) exists, don't spawn it
|
||||||
|
# again!
|
||||||
|
try:
|
||||||
|
if user == irc.pseudoclient.uid:
|
||||||
|
return remoteirc.pseudoclient.uid
|
||||||
|
except AttributeError: # Network hasn't been initialized yet?
|
||||||
|
pass
|
||||||
|
with spawnlocks[irc.name]:
|
||||||
|
u = None
|
||||||
|
try:
|
||||||
|
u = relayusers[(irc.name, user)][remoteirc.name]
|
||||||
|
except KeyError:
|
||||||
|
if spawnIfMissing:
|
||||||
|
u = spawnRelayUser(irc, remoteirc, user)
|
||||||
|
|
||||||
|
if u and ((u not in remoteirc.users) or remoteirc.users[u].remote != (irc.name, user)):
|
||||||
|
spawnRelayUser(irc, remoteirc, user)
|
||||||
|
return u
|
||||||
|
|
||||||
def getOrigUser(irc, user, targetirc=None):
|
def getOrigUser(irc, user, targetirc=None):
|
||||||
"""
|
"""
|
||||||
Given the UID of a relay client, returns a tuple of the home network name
|
Given the UID of a relay client, returns a tuple of the home network name
|
||||||
@ -757,10 +794,12 @@ def handle_join(irc, numeric, command, args):
|
|||||||
utils.add_hook(handle_join, 'JOIN')
|
utils.add_hook(handle_join, 'JOIN')
|
||||||
|
|
||||||
def handle_quit(irc, numeric, command, args):
|
def handle_quit(irc, numeric, command, args):
|
||||||
|
with spawnlocks[irc.name]:
|
||||||
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]
|
||||||
remoteirc.proto.quit(user, args['text'])
|
remoteirc.proto.quit(user, args['text'])
|
||||||
del relayusers[(irc.name, numeric)]
|
del relayusers[(irc.name, numeric)]
|
||||||
|
|
||||||
utils.add_hook(handle_quit, 'QUIT')
|
utils.add_hook(handle_quit, 'QUIT')
|
||||||
|
|
||||||
def handle_squit(irc, numeric, command, args):
|
def handle_squit(irc, numeric, command, args):
|
||||||
@ -1164,16 +1203,13 @@ def handle_disconnect(irc, numeric, command, args):
|
|||||||
if irc.name in v:
|
if irc.name in v:
|
||||||
del relayusers[k][irc.name]
|
del relayusers[k][irc.name]
|
||||||
if k[0] == irc.name:
|
if k[0] == irc.name:
|
||||||
try:
|
|
||||||
handle_quit(irc, k[1], 'PYLINK_DISCONNECT', {'text': 'Relay network lost connection.'})
|
handle_quit(irc, k[1], 'PYLINK_DISCONNECT', {'text': 'Relay network lost connection.'})
|
||||||
del relayusers[k]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# 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.
|
||||||
with spawnlocks_servers[irc.name]:
|
with spawnlocks_servers[irc.name]:
|
||||||
for name, ircobj in world.networkobjects.copy().items():
|
for name, ircobj in world.networkobjects.copy().items():
|
||||||
if name != irc.name and ircobj.connected.is_set():
|
if name != irc.name:
|
||||||
try:
|
try:
|
||||||
rsid = relayservers[name][irc.name]
|
rsid = relayservers[name][irc.name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -1181,15 +1217,10 @@ def handle_disconnect(irc, numeric, command, args):
|
|||||||
else:
|
else:
|
||||||
ircobj.proto.squit(ircobj.sid, rsid, text='Relay network lost connection.')
|
ircobj.proto.squit(ircobj.sid, rsid, text='Relay network lost connection.')
|
||||||
|
|
||||||
try:
|
if irc.name in relayservers[name]:
|
||||||
del relayservers[name][irc.name]
|
del relayservers[name][irc.name]
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
del relayservers[irc.name]
|
del relayservers[irc.name]
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
utils.add_hook(handle_disconnect, "PYLINK_DISCONNECT")
|
utils.add_hook(handle_disconnect, "PYLINK_DISCONNECT")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user