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