/* Routines to maintain a list of online servers. * by Andrew Kempe (TheShadow) * E-mail: * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * Parts written by Andrew Kempe and others. * This program is free but copyrighted software; see the file COPYING for * details. */ #include "services.h" #include "modules.h" #include "hash.h" /*************************************************************************/ void add_server(Server *server); /* to avoid "no prototype" warning */ void del_server(Server *server); /* same */ DEFINE_HASH(server, Server, name) static Server *root_server; /* Entry for root server (Services) */ static int16 servercnt = 0; /* Number of online servers */ /* Callback IDs: */ static int cb_create = -1; static int cb_delete = -1; /*************************************************************************/ /**************************** Internal Functions *************************/ /*************************************************************************/ /* Allocate a new Server structure, fill in basic values, link it to the * overall list, and return it. Always successful. */ static Server *new_server(const char *servername) { Server *server; servercnt++; server = scalloc(sizeof(Server), 1); server->name = sstrdup(servername); add_server(server); return server; } /* Remove and free a Server structure. */ static void delete_server(Server *server) { del_server(server); servercnt--; free(server->name); free(server); } /*************************************************************************/ /* Remove the given server. This takes care of recursively removing child * servers, handling NOQUIT, calling the server delete callback, and * actually deleting the server data. */ static void recursive_squit(Server *parent, const char *reason); static void squit_server(Server *server, const char *reason) { User *user, *nextuser; recursive_squit(server, reason); if (protocol_features & PF_NOQUIT) { #define next snext #define prev sprev LIST_FOREACH_SAFE (user, server->userlist, nextuser) quit_user(user, reason, 0); #undef next #undef prev } if (!server->fake) call_callback_2(NULL, cb_delete, server, reason); delete_server(server); } /* "SQUIT" all servers who are linked to us via the specified server by * deleting them from the server list. The parent server is not deleted, * so this must be done by the calling function. */ static void recursive_squit(Server *parent, const char *reason) { Server *server, *nextserver; server = parent->child; if (debug >= 2) log("debug: recursive_squit, parent: %s", parent->name); while (server) { nextserver = server->sibling; if (debug >= 2) log("debug: recursive_squit, child: %s", server->name); squit_server(server, reason); server = nextserver; } } /*************************************************************************/ /**************************** External Functions *************************/ /*************************************************************************/ /* Handle a server SERVER command. * source = server's hub; !*source indicates this is our hub. * av[0] = server's name * If ac < 0, the server in av[0] is assumed to be a "dummy" server that * should not be passed to the callback (e.g. for JUPE). * * When called internally to add a server (from OperServ JUPE, etc.), * callers may assume that the contents of the argument strings will not be * modified. */ void do_server(const char *source, int ac, char **av) { Server *server, *tmpserver; server = new_server(av[0]); server->t_join = time(NULL); server->child = NULL; server->sibling = NULL; if (*source) { server->hub = get_server(source); if (!server->hub) { /* PARANOIA: This should NEVER EVER happen, but we check anyway. * * I've heard that on older ircds it is possible for "source" * not to be the new server's hub. This will cause problems. * -TheShadow */ wallops(ServerName, "WARNING: Could not find server \2%s\2 which is supposed " "to be the hub for \2%s\2", source, av[0]); log("server: could not find hub %s for %s", source, av[0]); } } else { server->hub = root_server; } if (!server->hub->child) { server->hub->child = server; } else { tmpserver = server->hub->child; while (tmpserver->sibling) tmpserver = tmpserver->sibling; tmpserver->sibling = server; } if (ac > 0) call_callback_1(NULL, cb_create, server); else server->fake = 1; return; } /*************************************************************************/ /* Handle a server SQUIT command. * av[0] = server's name * av[1] = quit message */ void do_squit(const char *source, int ac, char **av) { Server *server; server = get_server(av[0]); if (server) { if (server->hub) { if (server->hub->child == server) { server->hub->child = server->sibling; } else { Server *tmpserver; for (tmpserver = server->hub->child; tmpserver->sibling; tmpserver = tmpserver->sibling) { if (tmpserver->sibling == server) { tmpserver->sibling = server->sibling; break; } } } } squit_server(server, av[1]); } else { wallops(ServerName, "WARNING: Tried to quit non-existent server: \2%s", av[0]); log("server: Tried to quit non-existent server: %s", av[0]); log("server: Input buffer: %s", inbuf); return; } } /*************************************************************************/ /*************************************************************************/ int server_init(int ac, char **av) { Server *server; cb_create = register_callback(NULL, "server create"); cb_delete = register_callback(NULL, "server delete"); if (cb_create < 0 || cb_delete < 0) { log("server_init: register_callback() failed\n"); return 0; } server = new_server(""); server->fake = 1; server->t_join = time(NULL); server->hub = server->child = server->sibling = NULL; root_server = server; return 1; } /*************************************************************************/ /* Remove all servers; this recursively takes out all users and channels, * as well. */ void server_cleanup(void) { uint32 pf = protocol_features; protocol_features |= PF_NOQUIT; squit_server(root_server, "server_cleanup"); protocol_features = pf; unregister_callback(NULL, cb_delete); unregister_callback(NULL, cb_create); } /*************************************************************************/ /* Return information on memory use. Assumes pointers are valid. */ void get_server_stats(long *nservers, long *memuse) { Server *server; long mem; mem = sizeof(Server) * servercnt; for (server = first_server(); server; server = next_server()) mem += strlen(server->name)+1; *nservers = servercnt; *memuse = mem; } /*************************************************************************/