Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / list.c
1 /*
2  * IRC - Internet Relay Chat, ircd/list.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Finland
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id$
21  */
22 #include "config.h"
23
24 #include "list.h"
25 #include "client.h"
26 #include "ircd.h"
27 #include "ircd_alloc.h"
28 #include "ircd_reply.h"
29 #include "ircd_string.h"
30 #include "listener.h"
31 #include "match.h"
32 #include "numeric.h"
33 #include "res.h"
34 #include "s_auth.h"
35 #include "s_bsd.h"
36 #include "s_conf.h"
37 #include "s_debug.h"
38 #include "s_misc.h"
39 #include "s_user.h"
40 #include "send.h"
41 #include "struct.h"
42 #include "support.h"
43 #include "whowas.h"
44
45 #include <assert.h>
46 #include <stddef.h>  /* offsetof */
47 #include <unistd.h>  /* close */
48 #include <string.h>
49
50 #ifdef DEBUGMODE
51 static struct liststats {
52   int inuse;
53 } clients, connections, users, servs, links;
54 #endif
55
56 static unsigned int clientAllocCount;
57 static struct Client* clientFreeList;
58
59 static unsigned int connectionAllocCount;
60 static struct Connection* connectionFreeList;
61
62 static unsigned int slinkAllocCount;
63 static struct SLink* slinkFreeList;
64
65 void init_list(void)
66 {
67   struct Client* cptr;
68   struct Connection* con;
69   int i;
70   /*
71    * pre-allocate MAXCONNECTIONS clients and connections
72    */
73   for (i = 0; i < MAXCONNECTIONS; ++i) {
74     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
75     cli_next(cptr) = clientFreeList;
76     clientFreeList = cptr;
77     ++clientAllocCount;
78
79     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
80     con_next(con) = connectionFreeList;
81     connectionFreeList = con;
82     ++connectionAllocCount;
83   }
84
85 #ifdef DEBUGMODE
86   memset(&clients, 0, sizeof(clients));
87   memset(&connections, 0, sizeof(connections));
88   memset(&users, 0, sizeof(users));
89   memset(&servs, 0, sizeof(servs));
90   memset(&links, 0, sizeof(links));
91 #endif
92 }
93
94 static struct Client* alloc_client(void)
95 {
96   struct Client* cptr = clientFreeList;
97
98   if (!cptr) {
99     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
100     ++clientAllocCount;
101   } else
102     clientFreeList = cli_next(cptr);
103
104 #ifdef DEBUGMODE
105   clients.inuse++;
106 #endif
107
108   memset(cptr, 0, sizeof(struct Client));
109
110   return cptr;
111 }
112
113 static void dealloc_client(struct Client* cptr)
114 {
115   assert(cli_verify(cptr));
116   assert(0 == cli_connect(cptr));
117
118 #ifdef DEBUGMODE
119   --clients.inuse;
120 #endif
121
122   cli_next(cptr) = clientFreeList;
123   clientFreeList = cptr;
124
125   cli_magic(cptr) = 0;
126 }
127
128 static struct Connection* alloc_connection(void)
129 {
130   struct Connection* con = connectionFreeList;
131
132   if (!con) {
133     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
134     ++connectionAllocCount;
135   } else
136     connectionFreeList = con_next(con);
137
138 #ifdef DEBUGMODE
139   connections.inuse++;
140 #endif
141
142   memset(con, 0, sizeof(struct Connection));
143
144   return con;
145 }
146
147 static void dealloc_connection(struct Connection* con)
148 {
149   assert(con_verify(con));
150
151   Debug((DEBUG_LIST, "Deallocating connection %p", con));
152
153   if (con_dns_reply(con))
154     --(con_dns_reply(con)->ref_count);
155   if (-1 < con_fd(con))
156     close(con_fd(con));
157   MsgQClear(&(con_sendQ(con)));
158   client_drop_sendq(con);
159   DBufClear(&(con_recvQ(con)));
160   if (con_listener(con))
161     release_listener(con_listener(con));
162
163 #ifdef DEBUGMODE
164   --connections.inuse;
165 #endif
166
167   con_next(con) = connectionFreeList;
168   connectionFreeList = con;
169
170   con_magic(con) = 0;
171 }
172
173 /*
174  * Create a new struct Client structure and set it to initial state.
175  *
176  *   from == NULL,   create local client (a client connected to a socket).
177  *
178  *   from != NULL,   create remote client (behind a socket associated with
179  *                   the client defined by 'from').
180  *                   ('from' is a local client!!).
181  */
182 struct Client* make_client(struct Client *from, int status)
183 {
184   struct Client* cptr = 0;
185   struct Connection* con = 0;
186
187   assert(!from || cli_verify(from));
188
189   cptr = alloc_client();
190
191   assert(0 != cptr);
192   assert(!cli_magic(cptr));
193   assert(0 == from || 0 != cli_connect(from));
194
195   if (!from) { /* local client, allocate a struct Connection */
196     con = alloc_connection();
197
198     assert(0 != con);
199     assert(!con_magic(con));
200
201     con_magic(con) = CONNECTION_MAGIC;
202     con_fd(con) = -1; /* initialize struct Connection */
203     con_freeflag(con) = 0;
204     con_nextnick(con) = CurrentTime - NICK_DELAY;
205     con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
206     con_handler(con) = UNREGISTERED_HANDLER;
207     con_client(con) = cptr;
208
209     cli_local(cptr) = 1; /* Set certain fields of the struct Client */
210     cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
211     cli_lastnick(cptr) = TStime();
212   } else
213     con = cli_connect(from); /* use 'from's connection */
214
215   assert(0 != con);
216   assert(con_verify(con));
217
218   cli_magic(cptr) = CLIENT_MAGIC;
219   cli_connect(cptr) = con; /* set the connection and other fields */
220   cli_status(cptr) = status;
221   cli_hnext(cptr) = cptr;
222   strcpy(cli_username(cptr), "unknown");
223
224   return cptr;
225 }
226
227 void free_connection(struct Connection* con)
228 {
229   if (!con)
230     return;
231
232   assert(con_verify(con));
233   assert(0 == con_client(con));
234
235   dealloc_connection(con); /* deallocate the connection */
236 }
237
238 void free_client(struct Client* cptr)
239 {
240   if (!cptr)
241     return;
242   /*
243    * forget to remove the client from the hash table?
244    */
245   assert(cli_verify(cptr));
246   assert(cli_hnext(cptr) == cptr);
247
248   Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr),
249          cptr, cli_connect(cptr)));
250
251   if (cli_auth(cptr))
252     destroy_auth_request(cli_auth(cptr), 0);
253
254   if (cli_from(cptr) == cptr) { /* in other words, we're local */
255     cli_from(cptr) = 0;
256     /* timer must be marked as not active */
257     if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr))))
258       dealloc_connection(cli_connect(cptr)); /* connection not open anymore */
259     else {
260       if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET)
261         socket_del(&(cli_socket(cptr))); /* queue a socket delete */
262       if (cli_freeflag(cptr) & FREEFLAG_TIMER)
263         timer_del(&(cli_proc(cptr))); /* queue a timer delete */
264     }
265   }
266
267   cli_connect(cptr) = 0;
268
269   dealloc_client(cptr); /* actually destroy the client */
270 }
271
272 struct Server *make_server(struct Client *cptr)
273 {
274   struct Server *serv = cli_serv(cptr);
275
276   assert(cli_verify(cptr));
277
278   if (!serv)
279   {
280     serv = (struct Server*) MyMalloc(sizeof(struct Server));
281     assert(0 != serv);
282     memset(serv, 0, sizeof(struct Server)); /* All variables are 0 by default */
283 #ifdef  DEBUGMODE
284     servs.inuse++;
285 #endif
286     cli_serv(cptr) = serv;
287     cli_serv(cptr)->lag = 60000;
288     *serv->by = '\0';
289     DupString(serv->last_error_msg, "<>");      /* String must be non-empty */
290   }
291   return cli_serv(cptr);
292 }
293
294 /*
295  * Taken the code from ExitOneClient() for this and placed it here.
296  * - avalon
297  */
298 void remove_client_from_list(struct Client *cptr)
299 {
300   assert(cli_verify(cptr));
301   assert(con_verify(cli_connect(cptr)));
302
303   if (cli_prev(cptr))
304     cli_next(cli_prev(cptr)) = cli_next(cptr);
305   else {
306     GlobalClientList = cli_next(cptr);
307     if (GlobalClientList)
308       cli_prev(GlobalClientList) = 0;
309   }
310   if (cli_next(cptr))
311     cli_prev(cli_next(cptr)) = cli_prev(cptr);
312
313   cli_next(cptr) = cli_prev(cptr) = 0;
314
315   if (IsUser(cptr) && cli_user(cptr)) {
316     add_history(cptr, 0);
317     off_history(cptr);
318   }
319   if (cli_user(cptr)) {
320     free_user(cli_user(cptr));
321     cli_user(cptr) = 0;
322   }
323
324   if (cli_serv(cptr)) {
325     if (cli_serv(cptr)->user) {
326       free_user(cli_serv(cptr)->user);
327       cli_serv(cptr)->user = 0;
328     }
329     if (cli_serv(cptr)->client_list)
330       MyFree(cli_serv(cptr)->client_list);
331     MyFree(cli_serv(cptr)->last_error_msg);
332     MyFree(cli_serv(cptr));
333 #ifdef  DEBUGMODE
334     --servs.inuse;
335 #endif
336   }
337   free_client(cptr);
338 }
339
340 /*
341  * Although only a small routine, it appears in a number of places
342  * as a collection of a few lines...functions like this *should* be
343  * in this file, shouldnt they ?  after all, this is list.c, isn't it ?
344  * -avalon
345  */
346 void add_client_to_list(struct Client *cptr)
347 {
348   assert(cli_verify(cptr));
349   /*
350    * Since we always insert new clients to the top of the list,
351    * this should mean the "me" is the bottom most item in the list.
352    * XXX - don't always count on the above, things change
353    */
354   cli_prev(cptr) = 0;
355   cli_next(cptr) = GlobalClientList;
356   GlobalClientList = cptr;
357   if (cli_next(cptr))
358     cli_prev(cli_next(cptr)) = cptr;
359 }
360
361 /*
362  * Look for ptr in the linked listed pointed to by link.
363  */
364 struct SLink *find_user_link(struct SLink *lp, struct Client *ptr)
365 {
366   if (ptr) {
367     while (lp) {
368       if (lp->value.cptr == ptr)
369         return (lp);
370       lp = lp->next;
371     }
372   }
373   return NULL;
374 }
375
376 struct SLink* make_link(void)
377 {
378   struct SLink* lp = slinkFreeList;
379   if (lp)
380     slinkFreeList = lp->next;
381   else {
382     lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
383     ++slinkAllocCount;
384   }
385   assert(0 != lp);
386 #ifdef  DEBUGMODE
387   links.inuse++;
388 #endif
389   return lp;
390 }
391
392 void free_link(struct SLink* lp)
393 {
394   if (lp) {
395     lp->next = slinkFreeList;
396     slinkFreeList = lp;
397   }
398 #ifdef  DEBUGMODE
399   links.inuse--;
400 #endif
401 }
402
403 struct DLink *add_dlink(struct DLink **lpp, struct Client *cp)
404 {
405   struct DLink* lp = (struct DLink*) MyMalloc(sizeof(struct DLink));
406   assert(0 != lp);
407   lp->value.cptr = cp;
408   lp->prev = 0;
409   if ((lp->next = *lpp))
410     lp->next->prev = lp;
411   *lpp = lp;
412   return lp;
413 }
414
415 void remove_dlink(struct DLink **lpp, struct DLink *lp)
416 {
417   assert(0 != lpp);
418   assert(0 != lp);
419
420   if (lp->prev) {
421     if ((lp->prev->next = lp->next))
422       lp->next->prev = lp->prev;
423   }
424   else if ((*lpp = lp->next))
425     lp->next->prev = NULL;
426   MyFree(lp);
427 }
428
429 #ifdef  DEBUGMODE
430 void send_listinfo(struct Client *cptr, char *name)
431 {
432   int inuse = 0, mem = 0, tmp = 0;
433
434   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients: inuse: %d(%d)",
435              clients.inuse, tmp = clients.inuse * sizeof(struct Client));
436   mem += tmp;
437   inuse += clients.inuse;
438   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, "Connections: inuse: %d(%d)",
439              connections.inuse,
440              tmp = connections.inuse * sizeof(struct Connection));
441   mem += tmp;
442   inuse += connections.inuse;
443   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Users: inuse: %d(%d)",
444              users.inuse, tmp = users.inuse * sizeof(struct User));
445   mem += tmp;
446   inuse += users.inuse;
447   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Servs: inuse: %d(%d)",
448              servs.inuse, tmp = servs.inuse * sizeof(struct Server));
449   mem += tmp;
450   inuse += servs.inuse;
451   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Links: inuse: %d(%d)",
452              links.inuse, tmp = links.inuse * sizeof(struct SLink));
453   mem += tmp;
454   inuse += links.inuse;
455   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Confs: inuse: %d(%d)",
456              GlobalConfCount, tmp = GlobalConfCount * sizeof(struct ConfItem));
457   mem += tmp;
458   inuse += GlobalConfCount;
459   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Totals: inuse %d %d",
460              inuse, mem);
461 }
462
463 #endif