13933f1cb8aa8dc7fb3c3d237eb3660d32b5a84c
[ircu2.10.12-pk.git] / ircd / dbuf.c
1 /*
2  * IRC - Internet Relay Chat, common/dbuf.c
3  * Copyright (C) 1990 Markku Savela
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /** @file
20  * @brief Implementation of functions dealing with data buffers.
21  * @version $Id$
22  */
23 #include "config.h"
24
25 #include "dbuf.h"
26 #include "ircd_alloc.h"
27 #include "ircd_chattr.h"
28 #include "ircd_features.h"
29 #include "send.h"
30 #include "sys.h"       /* MIN */
31
32 #include <assert.h>
33 #include <string.h>
34
35 /*
36  * dbuf is a collection of functions which can be used to
37  * maintain a dynamic buffering of a byte stream.
38  * Functions allocate and release memory dynamically as
39  * required [Actually, there is nothing that prevents
40  * this package maintaining the buffer on disk, either]
41  */
42
43 /** Number of dbufs allocated.
44  * This should only be modified by dbuf.c.
45  */
46 int DBufAllocCount = 0;
47 /** Number of dbufs in use.
48  * This should only be modified by dbuf.c.
49  */
50 int DBufUsedCount = 0;
51
52 /** List of allocated but unused DBuf structures. */
53 static struct DBufBuffer *dbufFreeList = 0;
54
55 /** Size of data for a single DBufBuffer. */
56 #define DBUF_SIZE 2048
57
58 /** Single data buffer in a DBuf. */
59 struct DBufBuffer {
60   struct DBufBuffer *next;      /**< Next data buffer, NULL if last */
61   char *start;                  /**< data starts here */
62   char *end;                    /**< data ends here */
63   char data[DBUF_SIZE];         /**< Actual data stored here */
64 };
65
66 /** Return memory used by allocated data buffers.
67  * @param[out] allocated Receives number of bytes allocated to DBufs.
68  * @param[out] used Receives number of bytes for currently used DBufs.
69  */
70 void dbuf_count_memory(size_t *allocated, size_t *used)
71 {
72   assert(0 != allocated);
73   assert(0 != used);
74   *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
75   *used = DBufUsedCount * sizeof(struct DBufBuffer);
76 }
77
78 /** Allocate a new DBufBuffer.
79  * If #dbufFreeList != NULL, use the head of that list; otherwise,
80  * allocate a new buffer.
81  * @return Newly allocated buffer list.
82  */
83 static struct DBufBuffer *dbuf_alloc(void)
84 {
85   struct DBufBuffer* db = dbufFreeList;
86
87   if (db) {
88     dbufFreeList = db->next;
89     ++DBufUsedCount;
90   }
91   else if (DBufAllocCount * DBUF_SIZE < feature_int(FEAT_BUFFERPOOL)) {
92     db = (struct DBufBuffer*) MyMalloc(sizeof(struct DBufBuffer));
93     assert(0 != db);
94     ++DBufAllocCount;
95     ++DBufUsedCount;
96   }
97   return db;
98 }
99
100 /** Release a DBufBuffer back to the free list.
101  * @param[in] db Data buffer to release.
102  */
103 static void dbuf_free(struct DBufBuffer *db)
104 {
105   assert(0 != db);
106   --DBufUsedCount;
107   db->next = dbufFreeList;
108   dbufFreeList = db;
109 }
110
111 /** Handle a memory allocation error on a DBuf.
112  * This frees all the buffers owned by the DBuf, since we have to
113  * close the associated connection.
114  * @param[in] dyn DBuf to clean out.
115  * @return Zero.
116  */
117 static int dbuf_malloc_error(struct DBuf *dyn)
118 {
119   struct DBufBuffer *db;
120   struct DBufBuffer *next;
121
122   for (db = dyn->head; db; db = next)
123   {
124     next = db->next;
125     dbuf_free(db);
126   }
127   dyn->tail = dyn->head = 0;
128   dyn->length = 0;
129   return 0;
130 }
131
132 /** Append bytes to a data buffer.
133  * @param[in] dyn Buffer to append to.
134  * @param[in] buf Data to append.
135  * @param[in] length Number of bytes to append.
136  * @return Non-zero on success, or zero on failure.
137  */
138 int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
139 {
140   struct DBufBuffer** h;
141   struct DBufBuffer*  db;
142   unsigned int chunk;
143
144   assert(0 != dyn);
145   assert(0 != buf);
146   /*
147    * Locate the last non-empty buffer. If the last buffer is full,
148    * the loop will terminate with 'db==NULL'.
149    * This loop assumes that the 'dyn->length' field is correctly
150    * maintained, as it should--no other check really needed.
151    */
152   if (!dyn->length)
153     h = &(dyn->head);
154   else
155     h = &(dyn->tail);
156   /*
157    * Append users data to buffer, allocating buffers as needed
158    */
159   dyn->length += length;
160
161   for (; length > 0; h = &(db->next)) {
162     if (0 == (db = *h)) {
163       if (0 == (db = dbuf_alloc())) {
164         if (feature_bool(FEAT_HAS_FERGUSON_FLUSHER)) {
165           /*
166            * from "Married With Children" episode were Al bought a REAL toilet
167            * on the black market because he was tired of the wimpy water
168            * conserving toilets they make these days --Bleep
169            */
170           /*
171            * Apparently this doesn't work, the server _has_ to
172            * dump a few clients to handle the load. A fully loaded
173            * server cannot handle a net break without dumping some
174            * clients. If we flush the connections here under a full
175            * load we may end up starving the kernel for mbufs and
176            * crash the machine
177            */
178           /*
179            * attempt to recover from buffer starvation before
180            * bailing this may help servers running out of memory
181            */
182           flush_connections(0);
183           db = dbuf_alloc();
184         }
185
186         if (0 == db)
187           return dbuf_malloc_error(dyn);
188       }
189       dyn->tail = db;
190       *h = db;
191       db->next = 0;
192       db->start = db->end = db->data;
193     }
194     chunk = (db->data + DBUF_SIZE) - db->end;
195     if (chunk) {
196       if (chunk > length)
197         chunk = length;
198
199       memcpy(db->end, buf, chunk);
200
201       length -= chunk;
202       buf += chunk;
203       db->end += chunk;
204     }
205   }
206   return 1;
207 }
208
209 /** Get the first contiguous block of data from a DBuf.
210  * Generally a call to dbuf_map(dyn, &count) will be followed with a
211  * call to dbuf_delete(dyn, count).
212  * @param[in] dyn DBuf to retrieve data from.
213  * @param[out] length Receives number of bytes in block.
214  * @return Pointer to start of block (or NULL if the first block is empty).
215  */
216 const char *dbuf_map(const struct DBuf* dyn, unsigned int* length)
217 {
218   assert(0 != dyn);
219   assert(0 != length);
220
221   if (0 == dyn->length)
222   {
223     *length = 0;
224     return 0;
225   }
226   assert(0 != dyn->head);
227
228   *length = dyn->head->end - dyn->head->start;
229   return dyn->head->start;
230 }
231
232 /** Discard data from a DBuf.
233  * @param[in,out] dyn DBuf to drop data from.
234  * @param[in] length Number of bytes to discard.
235  */
236 void dbuf_delete(struct DBuf *dyn, unsigned int length)
237 {
238   struct DBufBuffer *db;
239   unsigned int chunk;
240
241   if (length > dyn->length)
242     length = dyn->length;
243
244   while (length > 0)
245   {
246     if (0 == (db = dyn->head))
247       break;
248     chunk = db->end - db->start;
249     if (chunk > length)
250       chunk = length;
251
252     length -= chunk;
253     dyn->length -= chunk;
254     db->start += chunk;
255
256     if (db->start == db->end)
257     {
258       dyn->head = db->next;
259       dbuf_free(db);
260     }
261   }
262   if (0 == dyn->head)
263   {
264     dyn->length = 0;
265     dyn->tail = 0;
266   }
267 }
268
269 /** Copy data from a buffer and remove what was copied.
270  * @param[in,out] dyn Buffer to copy from.
271  * @param[out] buf Buffer to write to.
272  * @param[in] length Maximum number of bytes to copy.
273  * @return Number of bytes actually copied.
274  */
275 unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
276 {
277   unsigned int moved = 0;
278   unsigned int chunk;
279   const char *b;
280
281   assert(0 != dyn);
282   assert(0 != buf);
283
284   while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
285   {
286     if (chunk > length)
287       chunk = length;
288
289     memcpy(buf, b, chunk);
290     dbuf_delete(dyn, chunk);
291
292     buf += chunk;
293     length -= chunk;
294     moved += chunk;
295   }
296   return moved;
297 }
298
299 /** Flush empty lines from a buffer.
300  * @param[in,out] dyn Data buffer to flush.
301  * @return Number of bytes in first available block (or zero if none).
302  */
303 static unsigned int dbuf_flush(struct DBuf *dyn)
304 {
305   struct DBufBuffer *db = dyn->head;
306
307   if (0 == db)
308     return 0;
309
310   assert(db->start < db->end);
311   /*
312    * flush extra line terms
313    */
314   while (IsEol(*db->start))
315   {
316     if (++db->start == db->end)
317     {
318       dyn->head = db->next;
319       dbuf_free(db);
320       if (0 == (db = dyn->head))
321       {
322         dyn->tail = 0;
323         dyn->length = 0;
324         break;
325       }
326     }
327     --dyn->length;
328   }
329   return dyn->length;
330 }
331
332 /** Copy a single line from a data buffer.
333  * If the output buffer cannot hold the whole line, or if there is no
334  * EOL in the buffer, return 0.
335  * @param[in,out] dyn Data buffer to copy from.
336  * @param[out] buf Buffer to copy to.
337  * @param[in] length Maximum number of bytes to copy.
338  * @return Number of bytes copied to \a buf.
339  */
340 unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
341 {
342   struct DBufBuffer *db;
343   char *start;
344   char *end;
345   unsigned int count;
346   unsigned int copied = 0;
347
348   assert(0 != dyn);
349   assert(0 != buf);
350
351   if (0 == dbuf_flush(dyn))
352     return 0;
353
354   assert(0 != dyn->head);
355
356   db = dyn->head;
357   start = db->start;
358
359   assert(start < db->end);
360
361   if (length > dyn->length)
362     length = dyn->length;
363   /*
364    * might as well copy it while we're here
365    */
366   while (length > 0)
367   {
368     end = IRCD_MIN(db->end, (start + length));
369     while (start < end && !IsEol(*start))
370       *buf++ = *start++;
371
372     count = start - db->start;
373     if (start < end)
374     {
375       *buf = '\0';
376       copied += count;
377       dbuf_delete(dyn, copied);
378       dbuf_flush(dyn);
379       return copied;
380     }
381     if (0 == (db = db->next))
382       break;
383     copied += count;
384     length -= count;
385     start = db->start;
386   }
387   return 0;
388 }