Attempt to fix SF bug #2874316 by invalidating a file descriptor that the system...
[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 /** @file
21  * @brief Singly and doubly linked list manipulation implementation.
22  * @version $Id$
23  */
24 #include "config.h"
25
26 #include "list.h"
27 #include "client.h"
28 #include "ircd.h"
29 #include "ircd_alloc.h"
30 #include "ircd_events.h"
31 #include "ircd_log.h"
32 #include "ircd_reply.h"
33 #include "ircd_string.h"
34 #include "listener.h"
35 #include "match.h"
36 #include "numeric.h"
37 #include "res.h"
38 #include "s_auth.h"
39 #include "s_bsd.h"
40 #include "s_conf.h"
41 #include "s_debug.h"
42 #include "s_misc.h"
43 #include "s_user.h"
44 #include "send.h"
45 #include "struct.h"
46 #include "whowas.h"
47
48 /* #include <assert.h> -- Now using assert in ircd_log.h */
49 #include <stddef.h>  /* offsetof */
50 #include <unistd.h>  /* close */
51 #include <string.h>
52
53 /** Stores linked list statistics for various types of lists. */
54 static struct liststats {
55   size_t alloc; /**< Number of structures ever allocated. */
56   size_t inuse; /**< Number of structures currently in use. */
57   size_t mem;   /**< Memory used by in-use structures. */
58 } clients, connections, servs, links;
59
60 /** Linked list of currently unused Client structures. */
61 static struct Client* clientFreeList;
62
63 /** Linked list of currently unused Connection structures. */
64 static struct Connection* connectionFreeList;
65
66 /** Linked list of currently unused SLink structures. */
67 static struct SLink* slinkFreeList;
68
69 /** Initialize the list manipulation support system.
70  * Pre-allocate MAXCONNECTIONS Client and Connection structures.
71  */
72 void init_list(void)
73 {
74   struct Client* cptr;
75   struct Connection* con;
76   int i;
77   /*
78    * pre-allocate MAXCONNECTIONS clients and connections
79    */
80   for (i = 0; i < MAXCONNECTIONS; ++i) {
81     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
82     cli_next(cptr) = clientFreeList;
83     clientFreeList = cptr;
84     clients.alloc++;
85
86     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
87     con_next(con) = connectionFreeList;
88     connectionFreeList = con;
89     connections.alloc++;
90   }
91 }
92
93 /** Allocate a new Client structure.
94  * If #clientFreeList != NULL, use the head of that list.
95  * Otherwise, allocate a new structure.
96  * @return Newly allocated Client.
97  */
98 static struct Client* alloc_client(void)
99 {
100   struct Client* cptr = clientFreeList;
101
102   if (!cptr) {
103     cptr = (struct Client*) MyMalloc(sizeof(struct Client));
104     clients.alloc++;
105   } else
106     clientFreeList = cli_next(cptr);
107
108   clients.inuse++;
109
110   memset(cptr, 0, sizeof(struct Client));
111
112   return cptr;
113 }
114
115 /** Release a Client structure by prepending it to #clientFreeList.
116  * @param[in] cptr Client that is no longer being used.
117  */
118 static void dealloc_client(struct Client* cptr)
119 {
120   assert(cli_verify(cptr));
121   assert(0 == cli_connect(cptr));
122
123   --clients.inuse;
124
125   cli_next(cptr) = clientFreeList;
126   clientFreeList = cptr;
127
128   cli_magic(cptr) = 0;
129 }
130
131 /** Allocate a new Connection structure.
132  * If #connectionFreeList != NULL, use the head of that list.
133  * Otherwise, allocate a new structure.
134  * @return Newly allocated Connection.
135  */
136 static struct Connection* alloc_connection(void)
137 {
138   struct Connection* con = connectionFreeList;
139
140   if (!con) {
141     con = (struct Connection*) MyMalloc(sizeof(struct Connection));
142     connections.alloc++;
143   } else
144     connectionFreeList = con_next(con);
145
146   connections.inuse++;
147
148   memset(con, 0, sizeof(struct Connection));
149   timer_init(&(con_proc(con)));
150
151   return con;
152 }
153
154 /** Release a Connection and all memory associated with it.
155  * The connection's DNS reply field is freed, its file descriptor is
156  * closed, its msgq and sendq are cleared, and its associated Listener
157  * is dereferenced.  Then it is prepended to #connectionFreeList.
158  * @param[in] con Connection to free.
159  */
160 static void dealloc_connection(struct Connection* con)
161 {
162   assert(con_verify(con));
163   assert(!t_active(&(con_proc(con))));
164   assert(!t_onqueue(&(con_proc(con))));
165
166   Debug((DEBUG_LIST, "Deallocating connection %p", con));
167
168   if (-1 < con_fd(con))
169     close(con_fd(con));
170   MsgQClear(&(con_sendQ(con)));
171   client_drop_sendq(con);
172   DBufClear(&(con_recvQ(con)));
173   if (con_listener(con))
174     release_listener(con_listener(con));
175
176   --connections.inuse;
177
178   con_next(con) = connectionFreeList;
179   connectionFreeList = con;
180
181   con_magic(con) = 0;
182 }
183
184 /** Allocate a new client and initialize it.
185  * If \a from == NULL, initialize the fields for a local client,
186  * including allocating a Connection for him; otherwise initialize the
187  * fields for a remote client..
188  * @param[in] from Server connection that introduced the client (or
189  * NULL).
190  * @param[in] status Initial Client::cli_status value.
191  * @return Newly allocated and initialized Client.
192  */
193 struct Client* make_client(struct Client *from, int status)
194 {
195   struct Client* cptr = 0;
196
197   assert(!from || cli_verify(from));
198
199   cptr = alloc_client();
200
201   assert(0 != cptr);
202   assert(!cli_magic(cptr));
203   assert(0 == from || 0 != cli_connect(from));
204
205   if (!from) { /* local client, allocate a struct Connection */
206     struct Connection *con = alloc_connection();
207
208     assert(0 != con);
209     assert(!con_magic(con));
210
211     con_magic(con) = CONNECTION_MAGIC;
212     con_fd(con) = -1; /* initialize struct Connection */
213     con_freeflag(con) = 0;
214     con_nextnick(con) = CurrentTime - NICK_DELAY;
215     con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
216     con_handler(con) = UNREGISTERED_HANDLER;
217     con_client(con) = cptr;
218
219     cli_connect(cptr) = con; /* set the connection and other fields */
220     cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
221     cli_lastnick(cptr) = TStime();
222   } else
223     cli_connect(cptr) = cli_connect(from); /* use 'from's connection */
224
225   assert(con_verify(cli_connect(cptr)));
226
227   cli_magic(cptr) = CLIENT_MAGIC;
228   cli_status(cptr) = status;
229   cli_hnext(cptr) = cptr;
230   strcpy(cli_username(cptr), "unknown");
231
232   return cptr;
233 }
234
235 /** Release a Connection.
236  * @param[in] con Connection to free.
237  */
238 void free_connection(struct Connection* con)
239 {
240   if (!con)
241     return;
242
243   assert(con_verify(con));
244   assert(0 == con_client(con));
245
246   dealloc_connection(con); /* deallocate the connection */
247 }
248
249 /** Release a Client.
250  * In addition to the cleanup done by dealloc_client(), this will free
251  * any pending auth request, free the connection for local clients,
252  * and delete the processing timer for the client.
253  * @param[in] cptr Client to free.
254  */
255 void free_client(struct Client* cptr)
256 {
257   if (!cptr)
258     return;
259   /*
260    * forget to remove the client from the hash table?
261    */
262   assert(cli_verify(cptr));
263   assert(cli_hnext(cptr) == cptr);
264   /* or from linked list? */
265   assert(cli_next(cptr) == 0);
266   assert(cli_prev(cptr) == 0);
267
268   Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr),
269          cptr, cli_connect(cptr)));
270
271   if (cli_auth(cptr))
272     destroy_auth_request(cli_auth(cptr));
273
274   /* Make sure we didn't magically get re-added to the list */
275   assert(cli_next(cptr) == 0);
276   assert(cli_prev(cptr) == 0);
277
278   if (cli_from(cptr) == cptr) { /* in other words, we're local */
279     cli_from(cptr) = 0;
280     /* timer must be marked as not active */
281     if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr))))
282       dealloc_connection(cli_connect(cptr)); /* connection not open anymore */
283     else {
284       if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET)
285         socket_del(&(cli_socket(cptr))); /* queue a socket delete */
286       if (cli_freeflag(cptr) & FREEFLAG_TIMER)
287         timer_del(&(cli_proc(cptr))); /* queue a timer delete */
288     }
289   }
290
291   cli_connect(cptr) = 0;
292
293   dealloc_client(cptr); /* actually destroy the client */
294 }
295
296 /** Allocate a new Server object for a client.
297  * If Client::cli_serv == NULL, allocate a Server structure for it and
298  * initialize it.
299  * @param[in] cptr %Client to make into a server.
300  * @return The value of cli_serv(\a cptr).
301  */
302 struct Server *make_server(struct Client *cptr)
303 {
304   struct Server *serv = cli_serv(cptr);
305
306   assert(cli_verify(cptr));
307
308   if (!serv)
309   {
310     serv = (struct Server*) MyMalloc(sizeof(struct Server));
311     assert(0 != serv);
312     memset(serv, 0, sizeof(struct Server)); /* All variables are 0 by default */
313     servs.inuse++;
314     servs.alloc++;
315     cli_serv(cptr) = serv;
316     cli_serv(cptr)->lag = 60000;
317     *serv->by = '\0';
318     DupString(serv->last_error_msg, "<>");      /* String must be non-empty */
319   }
320   return cli_serv(cptr);
321 }
322
323 /** Remove \a cptr from lists that it is a member of.
324  * Specifically, this delinks \a cptr from #GlobalClientList, updates
325  * the whowas history list, frees its Client::cli_user and
326  * Client::cli_serv fields, and finally calls free_client() on it.
327  * @param[in] cptr Client to remove from lists and free.
328  */
329 void remove_client_from_list(struct Client *cptr)
330 {
331   assert(cli_verify(cptr));
332   assert(con_verify(cli_connect(cptr)));
333   assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr)));
334   assert(!cli_next(cptr) || cli_verify(cli_next(cptr)));
335   assert(!IsMe(cptr));
336
337   /* Only try remove cptr from the list if it IS in the list.
338    * cli_next(cptr) cannot be NULL here, as &me is always the end
339    * the list, and we never remove &me.    -GW 
340    */
341   if(cli_next(cptr))
342   {
343     if (cli_prev(cptr))
344       cli_next(cli_prev(cptr)) = cli_next(cptr);
345     else {
346       GlobalClientList = cli_next(cptr);
347       cli_prev(GlobalClientList) = 0;
348     }
349     cli_prev(cli_next(cptr)) = cli_prev(cptr);
350   }
351   cli_next(cptr) = cli_prev(cptr) = 0;
352
353   if (IsUser(cptr) && cli_user(cptr)) {
354     add_history(cptr, 0);
355     off_history(cptr);
356   }
357   if (cli_user(cptr)) {
358     free_user(cli_user(cptr));
359     cli_user(cptr) = 0;
360   }
361
362   if (cli_serv(cptr)) {
363     if (cli_serv(cptr)->user) {
364       free_user(cli_serv(cptr)->user);
365       cli_serv(cptr)->user = 0;
366     }
367     if (cli_serv(cptr)->client_list)
368       MyFree(cli_serv(cptr)->client_list);
369     MyFree(cli_serv(cptr)->last_error_msg);
370     MyFree(cli_serv(cptr));
371     --servs.inuse;
372     --servs.alloc;
373   }
374   free_client(cptr);
375 }
376
377 /** Link \a cptr into #GlobalClientList.
378  * @param[in] cptr Client to link into the global list.
379  */
380 void add_client_to_list(struct Client *cptr)
381 {
382   assert(cli_verify(cptr));
383   assert(cli_next(cptr) == 0);
384   assert(cli_prev(cptr) == 0);
385
386   /*
387    * Since we always insert new clients to the top of the list,
388    * this should mean the "me" is the bottom most item in the list.
389    * XXX - don't always count on the above, things change
390    */
391   cli_prev(cptr) = 0;
392   cli_next(cptr) = GlobalClientList;
393   GlobalClientList = cptr;
394   if (cli_next(cptr))
395     cli_prev(cli_next(cptr)) = cptr;
396 }
397
398 #if 0
399 /** Perform a very CPU-intensive verification of %GlobalClientList.
400  * This checks the Client::cli_magic and Client::cli_prev field for
401  * each element in the list, and also checks that there are no loops.
402  * Any detected error will lead to an assertion failure.
403  */
404 void verify_client_list(void)
405 {
406   struct Client *client, *prev = 0;
407   unsigned int visited = 0;
408
409   for (client = GlobalClientList; client; client = cli_next(client), ++visited) {
410     /* Verify that this is a valid client, not a free'd one */
411     assert(cli_verify(client));
412     /* Verify that the list hasn't suddenly jumped around */
413     assert(cli_prev(client) == prev);
414     /* Verify that the list hasn't become circular */
415     assert(cli_next(client) != GlobalClientList);
416     assert(visited <= clients.alloc);
417     /* Remember what should precede us */
418     prev = client;
419   }
420 }
421 #endif /* DEBUGMODE */
422
423 /** Allocate a new SLink element.
424  * Pulls from #slinkFreeList if it contains anything, else it
425  * allocates a new one from the heap.
426  * @return Newly allocated list element.
427  */
428 struct SLink* make_link(void)
429 {
430 #if 1
431   struct SLink* lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
432 #else
433   struct SLink* lp = slinkFreeList;
434   if (lp)
435     slinkFreeList = lp->next;
436   else {
437     lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
438     links.alloc++;
439   }
440 #endif
441   assert(0 != lp);
442   links.inuse++;
443   memset(lp, 0, sizeof(*lp));
444   return lp;
445 }
446
447 /** Release a singly linked list element.
448  * @param[in] lp List element to mark as unused.
449  */
450 void free_link(struct SLink* lp)
451 {
452   if (lp) {
453 #if 1
454     MyFree(lp);
455 #else
456     lp->next = slinkFreeList;
457     slinkFreeList = lp;
458 #endif
459     links.inuse--;
460   }
461 }
462
463 /** Add an element to a doubly linked list.
464  * If \a lpp points to a non-NULL pointer, its DLink::prev field is
465  * updated to point to the newly allocated element.  Regardless,
466  * \a lpp is overwritten with the pointer to the new link.
467  * @param[in,out] lpp Pointer to insertion location.
468  * @param[in] cp %Client to put in newly allocated element.
469  * @return Allocated link structure (same as \a lpp on output).
470  */
471 struct DLink *add_dlink(struct DLink **lpp, struct Client *cp)
472 {
473   struct DLink* lp = (struct DLink*) MyMalloc(sizeof(struct DLink));
474   assert(0 != lp);
475   lp->value.cptr = cp;
476   lp->prev = 0;
477   if ((lp->next = *lpp))
478     lp->next->prev = lp;
479   *lpp = lp;
480   return lp;
481 }
482
483 /** Remove a node from a doubly linked list.
484  * @param[out] lpp Pointer to next list element.
485  * @param[in] lp List node to unlink.
486  */
487 void remove_dlink(struct DLink **lpp, struct DLink *lp)
488 {
489   assert(0 != lpp);
490   assert(0 != lp);
491
492   if (lp->prev) {
493     if ((lp->prev->next = lp->next))
494       lp->next->prev = lp->prev;
495   }
496   else if ((*lpp = lp->next))
497     lp->next->prev = NULL;
498   MyFree(lp);
499 }
500
501 /** Report memory usage of a list to \a cptr.
502  * @param[in] cptr Client requesting information.
503  * @param[in] lstats List statistics descriptor.
504  * @param[in] itemname Plural name of item type.
505  * @param[in,out] totals If non-null, accumulates item counts and memory usage.
506  */
507 void send_liststats(struct Client *cptr, const struct liststats *lstats,
508                     const char *itemname, struct liststats *totals)
509 {
510   send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s: inuse %zu(%zu) alloc %zu",
511              itemname, lstats->inuse, lstats->mem, lstats->alloc);
512   if (totals)
513   {
514     totals->inuse += lstats->inuse;
515     totals->alloc += lstats->alloc;
516     totals->mem += lstats->mem;
517   }
518 }
519
520 /** Report memory usage of list elements to \a cptr.
521  * @param[in] cptr Client requesting information.
522  * @param[in] name Unused pointer.
523  */
524 void send_listinfo(struct Client *cptr, char *name)
525 {
526   struct liststats total;
527   struct liststats confs;
528   struct ConfItem *conf;
529
530   memset(&total, 0, sizeof(total));
531
532   clients.mem = clients.inuse * sizeof(struct Client);
533   send_liststats(cptr, &clients, "Clients", &total);
534
535   connections.mem = connections.inuse * sizeof(struct Connection);
536   send_liststats(cptr, &connections, "Connections", &total);
537
538   servs.mem = servs.inuse * sizeof(struct Server);
539   send_liststats(cptr, &servs, "Servers", &total);
540
541   links.mem = links.inuse * sizeof(struct SLink);
542   send_liststats(cptr, &links, "Links", &total);
543
544   confs.alloc = GlobalConfCount;
545   confs.mem = confs.alloc * sizeof(GlobalConfCount);
546   for (confs.inuse = 0, conf = GlobalConfList; conf; conf = conf->next)
547     confs.inuse++;
548   send_liststats(cptr, &confs, "Confs", &total);
549
550   send_liststats(cptr, &total, "Totals", NULL);
551 }