1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * BAREOS memory pool routines.
24  *
25  * The idea behind these routines is that there will be pools of memory that
26  * are pre-allocated for quick access. The pools will have a fixed memory size
27  * on allocation but if need be, the size can be increased. This is particularly
28  * useful for filename buffers where 256 bytes should be sufficient in 99.99%
29  * of the cases, but when it isn't we want to be able to increase the size.
30  *
31  * A major advantage of the pool memory aside from the speed is that the buffer
32  * carries around its size, so to ensure that there is enough memory, simply
33  * call the CheckPoolMemorySize() with the desired size and it will adjust only
34  * if necessary.
35  *
36  * Kern E. Sibbald
37  */
38 
39 #include "include/bareos.h"
40 #include "lib/util.h"
41 
42 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
43 
44 struct s_pool_ctl {
45   int32_t size;              /* default size */
46   int32_t max_allocated;     /* max allocated */
47   int32_t max_used;          /* max buffers used */
48   int32_t in_use;            /* number in use */
49   struct abufhead* free_buf; /* pointer to free buffers */
50 };
51 
52 /*
53  * Bareos Name length plus extra
54  */
55 #define NLEN (MAX_NAME_LENGTH + 2)
56 
57 /*
58  * Bareos Record length
59  */
60 #define RLEN 128
61 
62 /* #define STRESS_TEST_POOL */
63 
64 /*
65  * Define default Pool buffer sizes
66  */
67 #ifndef STRESS_TEST_POOL
68 static struct s_pool_ctl pool_ctl[] = {
69     {256, 256, 0, 0, NULL},   /* PM_NOPOOL no pooling */
70     {NLEN, NLEN, 0, 0, NULL}, /* PM_NAME Bareos name */
71     {256, 256, 0, 0, NULL},   /* PM_FNAME filename buffers */
72     {512, 512, 0, 0, NULL},   /* PM_MESSAGE message buffer */
73     {1024, 1024, 0, 0, NULL}, /* PM_EMSG error message buffer */
74     {4096, 4096, 0, 0, NULL}, /* PM_BSOCK message buffer */
75     {RLEN, RLEN, 0, 0, NULL}  /* PM_RECORD message buffer */
76 };
77 #else
78 /*
79  * This is used ONLY when stress testing the code
80  */
81 static struct s_pool_ctl pool_ctl[] = {
82     {20, 20, 0, 0, NULL},     /* PM_NOPOOL no pooling */
83     {NLEN, NLEN, 0, 0, NULL}, /* PM_NAME Bareos name */
84     {20, 20, 0, 0, NULL},     /* PM_FNAME filename buffers */
85     {20, 20, 0, 0, NULL},     /* PM_MESSAGE message buffer */
86     {20, 20, 0, 0, NULL},     /* PM_EMSG error message buffer */
87     {20, 20, 0, 0, NULL}      /* PM_BSOCK message buffer */
88     {RLEN, RLEN, 0, 0, NULL}  /* PM_RECORD message buffer */
89 };
90 #endif
91 
92 /*
93  * Memory allocation control structures and storage.
94  */
95 struct abufhead {
96   int32_t ablen;         /* Buffer length in bytes */
97   int32_t pool;          /* pool */
98   struct abufhead* next; /* pointer to next free buffer */
99   int32_t bnet_size;     /* dummy for BnetSend() */
100 };
101 
102 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
103 
104 
105 /*
106  * Special version of error reporting using a static buffer so we don't use
107  * the normal error reporting which uses dynamic memory e.g. recursivly calls
108  * these routines again leading to deadlocks.
109  */
MemPoolErrorMessage(const char * file,int line,const char * fmt,...)110 [[noreturn]] static void MemPoolErrorMessage(const char* file,
111                                              int line,
112                                              const char* fmt,
113                                              ...)
114 {
115   char buf[256];
116   va_list arg_ptr;
117   int len;
118 
119   len = Bsnprintf(buf, sizeof(buf), _("%s: ABORTING due to ERROR in %s:%d\n"),
120                   my_name, get_basename(file), line);
121 
122   va_start(arg_ptr, fmt);
123   Bvsnprintf(buf + len, sizeof(buf) - len, (char*)fmt, arg_ptr);
124   va_end(arg_ptr);
125 
126   DispatchMessage(NULL, M_ABORT, 0, buf);
127   abort();
128 }
129 
GetPoolMemory(int pool)130 POOLMEM* GetPoolMemory(int pool)
131 {
132   struct abufhead* buf;
133 
134   P(mutex);
135   if (pool_ctl[pool].free_buf) {
136     buf = pool_ctl[pool].free_buf;
137     pool_ctl[pool].free_buf = buf->next;
138     V(mutex);
139     return (POOLMEM*)((char*)buf + HEAD_SIZE);
140   }
141 
142   if ((buf = (struct abufhead*)malloc(pool_ctl[pool].size + HEAD_SIZE))
143       == NULL) {
144     V(mutex);
145     MemPoolErrorMessage(__FILE__, __LINE__,
146                         _("Out of memory requesting %d bytes\n"),
147                         pool_ctl[pool].size);
148     return NULL;
149   }
150 
151   buf->ablen = pool_ctl[pool].size;
152   buf->pool = pool;
153   buf->next = NULL;
154   pool_ctl[pool].in_use++;
155   pool_ctl[pool].max_used
156       = std::max(pool_ctl[pool].in_use, pool_ctl[pool].max_used);
157   V(mutex);
158   return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
159 }
160 
161 /* Get nonpool memory of size requested */
GetMemory(int32_t size)162 POOLMEM* GetMemory(int32_t size)
163 {
164   struct abufhead* buf;
165 
166   if ((buf = (struct abufhead*)malloc(size + HEAD_SIZE)) == NULL) {
167     MemPoolErrorMessage(__FILE__, __LINE__,
168                         _("Out of memory requesting %d bytes\n"), size);
169     return NULL;
170   }
171 
172   buf->ablen = size;
173   buf->pool = 0;
174   buf->next = NULL;
175   P(mutex);
176   pool_ctl[0].in_use++;
177   pool_ctl[0].max_used = std::max(pool_ctl[0].in_use, pool_ctl[0].max_used);
178   V(mutex);
179   return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
180 }
181 
182 /* Return the size of a memory buffer */
SizeofPoolMemory(POOLMEM * obuf)183 int32_t SizeofPoolMemory(POOLMEM* obuf)
184 {
185   char* cp = (char*)obuf;
186 
187   ASSERT(obuf);
188   cp -= HEAD_SIZE;
189   return ((struct abufhead*)cp)->ablen;
190 }
191 
192 /* Realloc pool memory buffer */
ReallocPoolMemory(POOLMEM * obuf,int32_t size)193 POOLMEM* ReallocPoolMemory(POOLMEM* obuf, int32_t size)
194 {
195   ASSERT(obuf);
196   void* buf = realloc((char*)obuf - HEAD_SIZE, size + HEAD_SIZE);
197   if (buf == NULL) {
198     MemPoolErrorMessage(__FILE__, __LINE__,
199                         _("Out of memory requesting %d bytes\n"), size);
200     return NULL;
201   }
202 
203   ((struct abufhead*)buf)->ablen = size;
204   const int pool = ((struct abufhead*)buf)->pool;
205   P(mutex);
206   if (size > pool_ctl[pool].max_allocated) {
207     pool_ctl[pool].max_allocated = size;
208   }
209   V(mutex);
210   return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
211 }
212 
CheckPoolMemorySize(POOLMEM * obuf,int32_t size)213 POOLMEM* CheckPoolMemorySize(POOLMEM* obuf, int32_t size)
214 {
215   ASSERT(obuf);
216   if (size <= SizeofPoolMemory(obuf)) { return obuf; }
217   return ReallocPoolMemory(obuf, size);
218 }
219 
220 /* Free a memory buffer */
FreePoolMemory(POOLMEM * obuf)221 void FreePoolMemory(POOLMEM* obuf)
222 {
223   ASSERT(obuf);
224   struct abufhead* buf = (struct abufhead*)((char*)obuf - HEAD_SIZE);
225 
226   const int pool = buf->pool;
227 
228   if (pool == 0) {
229     free((char*)buf); /* free nonpooled memory */
230     P(mutex);
231     pool_ctl[0].in_use--;
232     V(mutex);
233     return;
234   }
235   P(mutex);
236   struct abufhead* next;
237   for (next = pool_ctl[pool].free_buf; next; next = next->next) {
238     if (next == buf) {  // attempt to free twice
239       V(mutex);
240       ASSERT(next != buf);
241     }
242   }
243   // otherwise link it to the free pool chain
244   pool_ctl[pool].in_use--;
245   buf->next = pool_ctl[pool].free_buf;
246   pool_ctl[pool].free_buf = buf;
247   V(mutex);
248 }
249 
250 /*
251  * Clean up memory pool periodically
252  *
253  */
254 static time_t last_garbage_collection = 0;
255 const int garbage_interval = 24 * 60 * 60; /* garbage collect every 24 hours */
256 
GarbageCollectMemoryPool()257 void GarbageCollectMemoryPool()
258 {
259   time_t now;
260 
261   P(mutex);
262   if (last_garbage_collection == 0) {
263     last_garbage_collection = time(NULL);
264     V(mutex);
265     return;
266   }
267   now = time(NULL);
268   if (now >= last_garbage_collection + garbage_interval) {
269     last_garbage_collection = now;
270     V(mutex);
271     GarbageCollectMemory();
272   } else {
273     V(mutex);
274   }
275 }
276 
277 /* Release all freed pooled memory */
CloseMemoryPool()278 void CloseMemoryPool()
279 {
280   P(mutex);
281   for (int i = 1; i <= PM_MAX; i++) {
282     abufhead* buf = pool_ctl[i].free_buf;
283     while (buf) {
284       abufhead* next = buf->next;
285       free(buf);
286       buf = next;
287     }
288     pool_ctl[i].free_buf = NULL;
289   }
290   V(mutex);
291 
292   if (debug_level >= 1) { PrintMemoryPoolStats(); }
293 }
294 
295 /*
296  * Garbage collect and trim memory if possible
297  * This should be called after all big memory usages if possible.
298  */
GarbageCollectMemory()299 void GarbageCollectMemory() { CloseMemoryPool(); /* release free chain */ }
300 
pool_name(int pool)301 static const char* pool_name(int pool)
302 {
303   static char buf[30];
304   static const char* name[] = {"NoPool", "NAME  ",        "FNAME ", "MSG   ",
305                                "EMSG  ", "BareosSocket ", "RECORD"};
306 
307   if (pool >= 0 && pool <= PM_MAX) { return name[pool]; }
308   sprintf(buf, "%-6d", pool);
309 
310   return buf;
311 }
312 
313 /*
314  * Print staticstics on memory pool usage
315  */
PrintMemoryPoolStats()316 void PrintMemoryPoolStats()
317 {
318   Pmsg0(-1, "Pool   Maxsize  Maxused  Inuse\n");
319   for (int i = 0; i <= PM_MAX; i++) {
320     Pmsg4(-1, "%5s  %7d  %7d  %5d\n", pool_name(i), pool_ctl[i].max_allocated,
321           pool_ctl[i].max_used, pool_ctl[i].in_use);
322   }
323 
324   Pmsg0(-1, "\n");
325 }
326 
327 /*
328  * Concatenate a string (str) onto a pool memory buffer pm
329  * Returns: length of concatenated string
330  */
PmStrcat(POOLMEM * & pm,const char * str)331 int PmStrcat(POOLMEM*& pm, const char* str)
332 {
333   int pmlen = strlen(pm);
334   int len;
335 
336   if (!str) str = "";
337 
338   len = strlen(str) + 1;
339   pm = CheckPoolMemorySize(pm, pmlen + len);
340   memcpy(pm + pmlen, str, len);
341   return pmlen + len - 1;
342 }
343 
PmStrcat(POOLMEM * & pm,PoolMem & str)344 int PmStrcat(POOLMEM*& pm, PoolMem& str)
345 {
346   int pmlen = strlen(pm);
347   int len = strlen(str.c_str()) + 1;
348 
349   pm = CheckPoolMemorySize(pm, pmlen + len);
350   memcpy(pm + pmlen, str.c_str(), len);
351   return pmlen + len - 1;
352 }
353 
PmStrcat(PoolMem & pm,const char * str)354 int PmStrcat(PoolMem& pm, const char* str)
355 {
356   int pmlen = strlen(pm.c_str());
357   int len;
358 
359   if (!str) str = "";
360 
361   len = strlen(str) + 1;
362   pm.check_size(pmlen + len);
363   memcpy(pm.c_str() + pmlen, str, len);
364   return pmlen + len - 1;
365 }
366 
PmStrcat(PoolMem * & pm,const char * str)367 int PmStrcat(PoolMem*& pm, const char* str)
368 {
369   int pmlen = strlen(pm->c_str());
370   int len;
371 
372   if (!str) str = "";
373 
374   len = strlen(str) + 1;
375   pm->check_size(pmlen + len);
376   memcpy(pm->c_str() + pmlen, str, len);
377   return pmlen + len - 1;
378 }
379 
380 /*
381  * Copy a string (str) into a pool memory buffer pm
382  * Returns: length of string copied
383  */
PmStrcpy(POOLMEM * & pm,const char * str)384 int PmStrcpy(POOLMEM*& pm, const char* str)
385 {
386   int len;
387 
388   if (!str) str = "";
389 
390   len = strlen(str) + 1;
391   pm = CheckPoolMemorySize(pm, len);
392   memcpy(pm, str, len);
393   return len - 1;
394 }
395 
PmStrcpy(POOLMEM * & pm,PoolMem & str)396 int PmStrcpy(POOLMEM*& pm, PoolMem& str)
397 {
398   int len = strlen(str.c_str()) + 1;
399 
400   pm = CheckPoolMemorySize(pm, len);
401   memcpy(pm, str.c_str(), len);
402   return len - 1;
403 }
404 
PmStrcpy(PoolMem & pm,const char * str)405 int PmStrcpy(PoolMem& pm, const char* str)
406 {
407   int len;
408 
409   if (!str) str = "";
410 
411   len = strlen(str) + 1;
412   pm.check_size(len);
413   memcpy(pm.c_str(), str, len);
414   return len - 1;
415 }
416 
PmStrcpy(PoolMem * & pm,const char * str)417 int PmStrcpy(PoolMem*& pm, const char* str)
418 {
419   int len;
420 
421   if (!str) str = "";
422 
423   len = strlen(str) + 1;
424   pm->check_size(len);
425   memcpy(pm->c_str(), str, len);
426   return len - 1;
427 }
428 
429 /*
430  * Copy data into a pool memory buffer pm
431  * Returns: length of data copied
432  */
PmMemcpy(POOLMEM * & pm,const char * data,int32_t n)433 int PmMemcpy(POOLMEM*& pm, const char* data, int32_t n)
434 {
435   pm = CheckPoolMemorySize(pm, n);
436   memcpy(pm, data, n);
437   return n;
438 }
439 
PmMemcpy(POOLMEM * & pm,PoolMem & data,int32_t n)440 int PmMemcpy(POOLMEM*& pm, PoolMem& data, int32_t n)
441 {
442   pm = CheckPoolMemorySize(pm, n);
443   memcpy(pm, data.c_str(), n);
444   return n;
445 }
446 
PmMemcpy(PoolMem & pm,const char * data,int32_t n)447 int PmMemcpy(PoolMem& pm, const char* data, int32_t n)
448 {
449   pm.check_size(n);
450   memcpy(pm.c_str(), data, n);
451   return n;
452 }
453 
PmMemcpy(PoolMem * & pm,const char * data,int32_t n)454 int PmMemcpy(PoolMem*& pm, const char* data, int32_t n)
455 {
456   pm->check_size(n);
457   memcpy(pm->c_str(), data, n);
458   return n;
459 }
460 
461 /* ==============  CLASS PoolMem   ============== */
462 
463 /*
464  * Return the size of a memory buffer
465  */
MaxSize()466 int32_t PoolMem::MaxSize()
467 {
468   int32_t size;
469   char* cp = mem;
470 
471   cp -= HEAD_SIZE;
472   size = ((struct abufhead*)cp)->ablen;
473 
474   return size;
475 }
476 
ReallocPm(int32_t size)477 void PoolMem::ReallocPm(int32_t size)
478 {
479   char* cp = mem;
480   char* buf;
481   int pool;
482 
483   P(mutex);
484   cp -= HEAD_SIZE;
485   buf = (char*)realloc(cp, size + HEAD_SIZE);
486   if (buf == NULL) {
487     V(mutex);
488     MemPoolErrorMessage(__FILE__, __LINE__,
489                         _("Out of memory requesting %d bytes\n"), size);
490     return;
491   }
492 
493   ((struct abufhead*)buf)->ablen = size;
494   pool = ((struct abufhead*)buf)->pool;
495   if (size > pool_ctl[pool].max_allocated) {
496     pool_ctl[pool].max_allocated = size;
497   }
498   mem = buf + HEAD_SIZE;
499   V(mutex);
500 }
501 
strcat(PoolMem & str)502 int PoolMem::strcat(PoolMem& str) { return strcat(str.c_str()); }
503 
strcat(const char * str)504 int PoolMem::strcat(const char* str)
505 {
506   int pmlen = strlen();
507   int len;
508 
509   if (!str) str = "";
510 
511   len = ::strlen(str) + 1;
512   check_size(pmlen + len);
513   memcpy(mem + pmlen, str, len);
514   return pmlen + len - 1;
515 }
516 
strcpy(PoolMem & str)517 int PoolMem::strcpy(PoolMem& str) { return strcpy(str.c_str()); }
518 
strcpy(const char * str)519 int PoolMem::strcpy(const char* str)
520 {
521   int len;
522 
523   if (!str) str = "";
524 
525   len = ::strlen(str) + 1;
526   check_size(len);
527   memcpy(mem, str, len);
528   return len - 1;
529 }
530 
toLower()531 void PoolMem::toLower() { lcase(mem); }
532 
bsprintf(const char * fmt,...)533 int PoolMem::bsprintf(const char* fmt, ...)
534 {
535   int len;
536   va_list arg_ptr;
537   va_start(arg_ptr, fmt);
538   len = Bvsprintf(fmt, arg_ptr);
539   va_end(arg_ptr);
540   return len;
541 }
542 
Bvsprintf(const char * fmt,va_list arg_ptr)543 int PoolMem::Bvsprintf(const char* fmt, va_list arg_ptr)
544 {
545   int maxlen, len;
546   va_list ap;
547 
548 again:
549   maxlen = MaxSize() - 1;
550   va_copy(ap, arg_ptr);
551   len = ::Bvsnprintf(mem, maxlen, fmt, ap);
552   va_end(ap);
553   if (len < 0 || len >= maxlen) {
554     ReallocPm(maxlen + maxlen / 2);
555     goto again;
556   }
557   return len;
558 }
559