1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-01 23:06:38 +02:00

- Fixed "quickly-rehashing + autoconnect linkblocks = crash"-bug. This involved fixing

multiple reference count bugs, one related to sptr->serv->conf, and another one related
  to sptr->serv->class. Both caused problems when someone did a /rehash when a server
  was in the process of connecting (so it might also happen when connfreq was hit and you
  did a /rehash). Original bug was reported by sh0 (#0001872).
This commit is contained in:
Bram Matthys
2004-06-25 23:50:08 +00:00
parent af3c66dea5
commit 5877a32b3b
9 changed files with 98 additions and 29 deletions
+5
View File
@@ -3313,3 +3313,8 @@ This is the 3.2 fixes branch.
the correct path (#0001898) reported by AngryWolf
- Updated HOOKTYPE_TKL_ADD/HOOKTYPE_TKL_DEL to cptr, sptr, tk, parc, parv, else it was
impossible to tell *who* removed a *line. Again, parc/parv are 0/NULL for expires.
- Fixed "quickly-rehashing + autoconnect linkblocks = crash"-bug. This involved fixing
multiple reference count bugs, one related to sptr->serv->conf, and another one related
to sptr->serv->class. Both caused problems when someone did a /rehash when a server
was in the process of connecting (so it might also happen when connfreq was hit and you
did a /rehash). Original bug was reported by sh0 (#0001872).
+5 -1
View File
@@ -241,7 +241,11 @@
* the maintainer.
*/
/* #undef DEBUGMODE define DEBUGMODE to enable debugging mode.*/
/* DEBUGMODE: This should only be used when tracing a problem. It creates
* an insane amount of log output which can be very useful for debugging.
* You should *NEVER* enable this setting on production servers.
*/
/* #undef DEBUGMODE */
/*
* Full pathnames and defaults of irc system's support files. Please note that
+2
View File
@@ -701,3 +701,5 @@ extern int register_user(aClient *cptr, aClient *sptr, char *nick, char *usernam
extern int on_dccallow_list(aClient *to, aClient *from);
extern int add_dccallow(aClient *sptr, aClient *optr);
extern int del_dccallow(aClient *sptr, aClient *optr);
extern void delete_linkblock(ConfigItem_link *link_ptr);
extern void delete_classblock(ConfigItem_class *class_ptr);
+3
View File
@@ -1060,6 +1060,9 @@ struct _configitem_class {
ConfigFlag flag;
char *name;
int pingfreq, connfreq, maxclients, sendq, recvq, clients;
int xrefcount; /* EXTRA reference count, 'clients' also acts as a reference count but
* link blocks also refer to classes so a 2nd ref. count was needed.
*/
};
struct _configflag_allow {
+8 -2
View File
@@ -593,6 +593,7 @@ int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *aconf)
aClient *acptr;
int i;
char buf[BUFSIZE];
int incoming = IsUnknown(cptr) ? 1 : 0;
ircd_log(LOG_SERVER, "SERVER %s", cptr->name);
@@ -601,7 +602,7 @@ int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *aconf)
MyFree(cptr->passwd);
cptr->passwd = NULL;
}
if (IsUnknown(cptr))
if (incoming)
{
/* If this is an incomming connection, then we have just received
* their stuff and now send our stuff back.
@@ -684,7 +685,12 @@ int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *aconf)
cptr->srvptr = &me;
cptr->serv->numeric = numeric;
cptr->serv->conf = aconf;
cptr->serv->conf->refcount++;
if (incoming)
{
cptr->serv->conf->refcount++;
Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d",
cptr->name, cptr->serv->conf->servername, cptr->serv->conf->refcount));
}
cptr->serv->conf->class->clients++;
cptr->class = cptr->serv->conf->class;
add_server_to_table(cptr);
+10 -2
View File
@@ -502,7 +502,7 @@ int stats_links(aClient *sptr, char *para)
ConfigItem_link *link_p;
for (link_p = conf_link; link_p; link_p = (ConfigItem_link *) link_p->next)
{
sendto_one(sptr, ":%s 213 %s C %s@%s * %s %i %s %s%s%s%s%s",
sendto_one(sptr, ":%s 213 %s C %s@%s * %s %i %s %s%s%s%s%s%s",
me.name, sptr->name, IsOper(sptr) ? link_p->username : "*",
IsOper(sptr) ? link_p->hostname : "*", link_p->servername,
link_p->port,
@@ -511,7 +511,11 @@ int stats_links(aClient *sptr, char *para)
(link_p->options & CONNECT_SSL) ? "S" : "",
(link_p->options & CONNECT_ZIP) ? "z" : "",
(link_p->options & CONNECT_NODNSCACHE) ? "d" : "",
(link_p->options & CONNECT_NOHOSTCHECK) ? "h" : "");
(link_p->options & CONNECT_NOHOSTCHECK) ? "h" : "",
(link_p->flag.temporary == 1) ? "T" : "");
#ifdef DEBUGMODE
sendnotice(sptr, "%s has refcount %d", link_p->servername, link_p->refcount);
#endif
if (link_p->hubmask)
sendto_one(sptr, ":%s 244 %s H %s * %s",
me.name, sptr->name, link_p->hubmask,
@@ -1420,6 +1424,10 @@ int stats_class(aClient *sptr, char *para)
sendto_one(sptr, rpl_str(RPL_STATSYLINE),
me.name, sptr->name, classes->name, classes->pingfreq, classes->connfreq,
classes->maxclients, classes->sendq, classes->recvq ? classes->recvq : CLIENT_FLOOD);
#ifdef DEBUGMODE
sendnotice(sptr, "class '%s' has clients=%d, xrefcount=%d",
classes->name, classes->clients, classes->xrefcount);
#endif
}
return 0;
}
+3
View File
@@ -2445,6 +2445,9 @@ int connect_server(ConfigItem_link *aconf, aClient *by, struct hostent *hp)
*/
(void)make_server(cptr);
cptr->serv->conf = aconf;
cptr->serv->conf->refcount++;
Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d",
cptr->name, cptr->serv->conf->servername, cptr->serv->conf->refcount));
if (by && IsPerson(by))
{
(void)strlcpy(cptr->serv->by, by->name, sizeof cptr->serv->by);
+46 -18
View File
@@ -1702,6 +1702,21 @@ void config_rehash()
DelListItem(oper_ptr, conf_oper);
MyFree(oper_ptr);
}
for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next)
{
next = (ListStruct *)link_ptr->next;
if (link_ptr->refcount == 0)
{
Debug((DEBUG_ERROR, "s_conf: deleting block %s (refcount 0)", link_ptr->servername));
delete_linkblock(link_ptr);
}
else
{
Debug((DEBUG_ERROR, "s_conf: marking block %s (refcount %d) as temporary",
link_ptr->servername, link_ptr->refcount));
link_ptr->flag.temporary = 1;
}
}
for (class_ptr = conf_class; class_ptr; class_ptr = (ConfigItem_class *) next)
{
next = (ListStruct *)class_ptr->next;
@@ -1709,11 +1724,9 @@ void config_rehash()
continue;
class_ptr->flag.temporary = 1;
/* We'll wipe it out when it has no clients */
if (!class_ptr->clients)
if (!class_ptr->clients && !class_ptr->xrefcount)
{
ircfree(class_ptr->name);
DelListItem(class_ptr, conf_class);
MyFree(class_ptr);
delete_classblock(class_ptr);
}
}
for (uline_ptr = conf_ulines; uline_ptr; uline_ptr = (ConfigItem_ulines *) next)
@@ -1757,20 +1770,6 @@ void config_rehash()
MyFree(ban_ptr);
}
}
for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next)
{
next = (ListStruct *)link_ptr->next;
if (link_ptr->refcount == 0)
{
link_cleanup(link_ptr);
DelListItem(link_ptr, conf_link);
MyFree(link_ptr);
}
else
{
link_ptr->flag.temporary = 1;
}
}
for (listen_ptr = conf_listen; listen_ptr; listen_ptr = (ConfigItem_listen *)listen_ptr->next)
{
listen_ptr->flag.temporary = 1;
@@ -5298,6 +5297,7 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce)
cep->ce_vardata);
link->class = default_class;
}
link->class->xrefcount++;
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{
if (!strcmp(cep->ce_varname, "options"))
@@ -7692,6 +7692,34 @@ void link_cleanup(ConfigItem_link *link_ptr)
link_ptr->recvauth = NULL;
}
void delete_linkblock(ConfigItem_link *link_ptr)
{
Debug((DEBUG_ERROR, "delete_linkblock: deleting %s, refcount=%d",
link_ptr->servername, link_ptr->refcount));
if (link_ptr->class)
{
link_ptr->class->xrefcount--;
/* Perhaps the class is temporary too and we need to free it... */
if (link_ptr->class->flag.temporary &&
!link_ptr->class->clients && !link_ptr->class->xrefcount)
{
delete_classblock(link_ptr->class);
link_ptr->class = NULL;
}
}
link_cleanup(link_ptr);
DelListItem(link_ptr, conf_link);
MyFree(link_ptr);
}
void delete_classblock(ConfigItem_class *class_ptr)
{
Debug((DEBUG_ERROR, "delete_classblock: deleting %s, clients=%d, xrefcount=%d",
class_ptr->name, class_ptr->clients, class_ptr->xrefcount));
ircfree(class_ptr->name);
DelListItem(class_ptr, conf_class);
MyFree(class_ptr);
}
void listen_cleanup()
{
+16 -6
View File
@@ -457,21 +457,31 @@ int exit_client(aClient *cptr, aClient *sptr, aClient *from, char *comment)
delfrom_fdlist(sptr->slot, &serv_fdlist);
#endif
if (sptr->class)
{
sptr->class->clients--;
if ((sptr->class->flag.temporary) && !sptr->class->clients && !sptr->class->xrefcount)
{
delete_classblock(sptr->class);
sptr->class = NULL;
}
}
if (IsClient(sptr))
IRCstats.me_clients--;
if (IsServer(sptr))
if (sptr->serv && sptr->serv->conf)
{
IRCstats.me_servers--;
sptr->serv->conf->refcount--;
Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d",
sptr->name, sptr->serv->conf->servername, sptr->serv->conf->refcount));
if (!sptr->serv->conf->refcount
&& sptr->serv->conf->flag.temporary)
{
/* Due for deletion */
DelListItem(sptr->serv->conf, conf_link);
link_cleanup(sptr->serv->conf);
MyFree(sptr->serv->conf);
Debug((DEBUG_ERROR, "deleting temporary block %s", sptr->serv->conf->servername));
delete_linkblock(sptr->serv->conf);
}
}
if (IsServer(sptr))
{
IRCstats.me_servers--;
ircd_log(LOG_SERVER, "SQUIT %s (%s)", sptr->name, comment);
}