]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/mem_pool.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / lib / mem_pool.c
index 5d123b426ee6dc8f3d93f3ae74e9e11e6816ea5a..a29e68ef38bf2fb302e0176f373097141dcb67b8 100644 (file)
@@ -1,10 +1,28 @@
 /*
- *  Bacula memory pool routines. 
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is 
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+/*
+ *  Bacula memory pool routines.
  *
  *  The idea behind these routines is that there will be
  *  pools of memory that are pre-allocated for quick
  *  access. The pools will have a fixed memory size on allocation
- *  but if need be, the size can be increased. This is 
+ *  but if need be, the size can be increased. This is
  *  particularly useful for filename
  *  buffers where 256 bytes should be sufficient in 99.99%
  *  of the cases, but when it isn't we want to be able to
  *  there is enough memory, simply call the check_pool_memory_size()
  *  with the desired size and it will adjust only if necessary.
  *
- *          Kern E. Sibbald
+ *           Kern E. Sibbald
  *
- *   Version $Id$
- */
-
-/*
-   Copyright (C) 2000-2003 Kern Sibbald and John Walker
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public
-   License along with this program; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
-
  */
 
 #include "bacula.h"
+#define dbglvl DT_MEMORY|800
+
+#ifdef HAVE_MALLOC_TRIM
+extern "C" int malloc_trim (size_t pad);
+#endif
 
 struct s_pool_ctl {
-   int32_t size;                     /* default size */
-   int32_t max_size;                 /* max allocated */
-   int32_t max_used;                 /* max buffers used */
-   int32_t in_use;                   /* number in use */
-   struct abufhead *free_buf;        /* pointer to free buffers */
+   int32_t size;                      /* default size */
+   int32_t max_allocated;             /* max allocated */
+   int32_t max_used;                  /* max buffers used */
+   int32_t in_use;                    /* number in use */
+   struct abufhead *free_buf;         /* pointer to free buffers */
 };
 
 /* Bacula Name length plus extra */
@@ -59,45 +61,48 @@ struct s_pool_ctl {
  * Define default Pool buffer sizes
  */
 static struct s_pool_ctl pool_ctl[] = {
-   {  256,  256, 0, 0, NULL },       /* PM_NOPOOL no pooling */
-   {  NLEN, NLEN,0, 0, NULL },       /* PM_NAME Bacula name */
-   {  256,  256, 0, 0, NULL },       /* PM_FNAME filename buffers */
-   {  512,  512, 0, 0, NULL },       /* PM_MESSAGE message buffer */
-   { 1024, 1024, 0, 0, NULL }        /* PM_EMSG error message buffer */
+   {  256,  256, 0, 0, NULL },        /* PM_NOPOOL no pooling */
+   {  NLEN, NLEN,0, 0, NULL },        /* PM_NAME Bacula name */
+   {  256,  256, 0, 0, NULL },        /* PM_FNAME filename buffers */
+   {  512,  512, 0, 0, NULL },        /* PM_MESSAGE message buffer */
+   { 1024, 1024, 0, 0, NULL },        /* PM_EMSG error message buffer */
+  {  4096, 4096, 0, 0, NULL }         /* PM_BSOCK message buffer */
 };
 #else
 
 /* This is used ONLY when stress testing the code */
 static struct s_pool_ctl pool_ctl[] = {
-   {   20,   20, 0, 0, NULL },       /* PM_NOPOOL no pooling */
-   {  NLEN, NLEN,0, 0, NULL },       /* PM_NAME Bacula name */
-   {   20,   20, 0, 0, NULL },       /* PM_FNAME filename buffers */
-   {   20,   20, 0, 0, NULL },       /* PM_MESSAGE message buffer */
-   {   20,   20, 0, 0, NULL }        /* PM_EMSG error message buffer */
+   {   20,   20, 0, 0, NULL },        /* PM_NOPOOL no pooling */
+   {  NLEN, NLEN,0, 0, NULL },        /* PM_NAME Bacula name */
+   {   20,   20, 0, 0, NULL },        /* PM_FNAME filename buffers */
+   {   20,   20, 0, 0, NULL },        /* PM_MESSAGE message buffer */
+   {   20,   20, 0, 0, NULL },        /* PM_EMSG error message buffer */
+   {   20,   20, 0, 0, NULL }         /* PM_BSOCK message buffer */
 };
 #endif
 
 
 /*  Memory allocation control structures and storage.  */
 struct abufhead {
-   int32_t ablen;                    /* Buffer length in bytes */
-   int32_t pool;                     /* pool */
-   struct abufhead *next;            /* pointer to next free buffer */
+   int32_t ablen;                     /* Buffer length in bytes */
+   int32_t pool;                      /* pool */
+   struct abufhead *next;             /* pointer to next free buffer */
+   int32_t bnet_size;                 /* dummy for bnet_send() */
+   int32_t bnet_extension;            /* dummy for bnet extension */
 };
 
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
+#define HEAD_SIZE BALIGN(sizeof(struct abufhead))
 
 #ifdef SMARTALLOC
 
-#define HEAD_SIZE BALIGN(sizeof(struct abufhead))
-
-POOLMEM *sm_get_pool_memory(char *fname, int lineno, int pool)
+POOLMEM *sm_get_pool_memory(const char *fname, int lineno, int pool)
 {
    struct abufhead *buf;
 
    if (pool > PM_MAX) {
-      Emsg2(M_ABORT, 0, "MemPool index %d larger than max %d\n", pool, PM_MAX);
+      Emsg2(M_ABORT, 0, _("MemPool index %d larger than max %d\n"), pool, PM_MAX);
    }
    P(mutex);
    if (pool_ctl[pool].free_buf) {
@@ -105,17 +110,17 @@ POOLMEM *sm_get_pool_memory(char *fname, int lineno, int pool)
       pool_ctl[pool].free_buf = buf->next;
       pool_ctl[pool].in_use++;
       if (pool_ctl[pool].in_use > pool_ctl[pool].max_used) {
-        pool_ctl[pool].max_used = pool_ctl[pool].in_use;
+         pool_ctl[pool].max_used = pool_ctl[pool].in_use;
       }
       V(mutex);
-      Dmsg3(150, "sm_get_pool_memory reuse %x to %s:%d\n", buf, fname, lineno);
+      Dmsg3(dbglvl, "sm_get_pool_memory reuse %p to %s:%d\n", buf, fname, lineno);
       sm_new_owner(fname, lineno, (char *)buf);
       return (POOLMEM *)((char *)buf+HEAD_SIZE);
    }
-      
-   if ((buf = (struct abufhead *) sm_malloc(fname, lineno, pool_ctl[pool].size+HEAD_SIZE)) == NULL) {
+
+   if ((buf = (struct abufhead *)sm_malloc(fname, lineno, pool_ctl[pool].size+HEAD_SIZE)) == NULL) {
       V(mutex);
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", pool_ctl[pool].size);
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), pool_ctl[pool].size);
    }
    buf->ablen = pool_ctl[pool].size;
    buf->pool = pool;
@@ -124,18 +129,18 @@ POOLMEM *sm_get_pool_memory(char *fname, int lineno, int pool)
       pool_ctl[pool].max_used = pool_ctl[pool].in_use;
    }
    V(mutex);
-   Dmsg3(150, "sm_get_pool_memory give %x to %s:%d\n", buf, fname, lineno);
+   Dmsg3(dbglvl, "sm_get_pool_memory give %p to %s:%d\n", buf, fname, lineno);
    return (POOLMEM *)((char *)buf+HEAD_SIZE);
 }
 
 /* Get nonpool memory of size requested */
-POOLMEM *sm_get_memory(char *fname, int lineno, int32_t size)
+POOLMEM *sm_get_memory(const char *fname, int lineno, int32_t size)
 {
    struct abufhead *buf;
    int pool = 0;
 
-   if ((buf = (struct abufhead *) sm_malloc(fname, lineno, size+HEAD_SIZE)) == NULL) {
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", size);
+   if ((buf = (struct abufhead *)sm_malloc(fname, lineno, size+HEAD_SIZE)) == NULL) {
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), size);
    }
    buf->ablen = size;
    buf->pool = pool;
@@ -146,19 +151,20 @@ POOLMEM *sm_get_memory(char *fname, int lineno, int32_t size)
    return (POOLMEM *)(((char *)buf)+HEAD_SIZE);
 }
 
-
 /* Return the size of a memory buffer */
-int32_t sm_sizeof_pool_memory(char *fname, int lineno, POOLMEM *obuf)
+int32_t sm_sizeof_pool_memory(const char *fname, int lineno, POOLMEM *obuf)
 {
    char *cp = (char *)obuf;
 
-   ASSERT(obuf);
+   if (obuf == NULL) {
+      Emsg0(M_ABORT, 0, _("obuf is NULL\n"));
+   }
    cp -= HEAD_SIZE;
    return ((struct abufhead *)cp)->ablen;
 }
 
 /* Realloc pool memory buffer */
-POOLMEM *sm_realloc_pool_memory(char *fname, int lineno, POOLMEM *obuf, int32_t size)
+POOLMEM *sm_realloc_pool_memory(const char *fname, int lineno, POOLMEM *obuf, int32_t size)
 {
    char *cp = (char *)obuf;
    void *buf;
@@ -170,18 +176,18 @@ POOLMEM *sm_realloc_pool_memory(char *fname, int lineno, POOLMEM *obuf, int32_t
    buf = sm_realloc(fname, lineno, cp, size+HEAD_SIZE);
    if (buf == NULL) {
       V(mutex);
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", size);
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), size);
    }
    ((struct abufhead *)buf)->ablen = size;
    pool = ((struct abufhead *)buf)->pool;
-   if (size > pool_ctl[pool].max_size) {
-      pool_ctl[pool].max_size = size;
+   if (size > pool_ctl[pool].max_allocated) {
+      pool_ctl[pool].max_allocated = size;
    }
    V(mutex);
    return (POOLMEM *)(((char *)buf)+HEAD_SIZE);
 }
 
-POOLMEM *sm_check_pool_memory_size(char *fname, int lineno, POOLMEM *obuf, int32_t size)
+POOLMEM *sm_check_pool_memory_size(const char *fname, int lineno, POOLMEM *obuf, int32_t size)
 {
    ASSERT(obuf);
    if (size <= sizeof_pool_memory(obuf)) {
@@ -191,7 +197,7 @@ POOLMEM *sm_check_pool_memory_size(char *fname, int lineno, POOLMEM *obuf, int32
 }
 
 /* Free a memory buffer */
-void sm_free_pool_memory(char *fname, int lineno, POOLMEM *obuf)
+void sm_free_pool_memory(const char *fname, int lineno, POOLMEM *obuf)
 {
    struct abufhead *buf;
    int pool;
@@ -202,26 +208,32 @@ void sm_free_pool_memory(char *fname, int lineno, POOLMEM *obuf)
    pool = buf->pool;
    pool_ctl[pool].in_use--;
    if (pool == 0) {
-      free((char *)buf);             /* free nonpooled memory */
-   } else {                          /* otherwise link it to the free pool chain */
-#ifdef DEBUG
+      free((char *)buf);              /* free nonpooled memory */
+   } else {                           /* otherwise link it to the free pool chain */
+
+   /* Disabled because it hangs in #5507 */
+#ifdef xDEBUG
       struct abufhead *next;
       /* Don't let him free the same buffer twice */
       for (next=pool_ctl[pool].free_buf; next; next=next->next) {
-        ASSERT(next != buf);  /* attempt to free twice */
+         if (next == buf) {
+            Dmsg4(dbglvl, "free_pool_memory %p pool=%d from %s:%d\n", buf, pool, fname, lineno);
+            Dmsg4(dbglvl, "bad free_pool_memory %p pool=%d from %s:%d\n", buf, pool, fname, lineno);
+            V(mutex);                 /* unblock the pool */
+            ASSERT(next != buf);      /* attempt to free twice */
+         }
       }
 #endif
       buf->next = pool_ctl[pool].free_buf;
       pool_ctl[pool].free_buf = buf;
    }
-   Dmsg2(150, "free_pool_memory %x pool=%d\n", buf, pool);
+   Dmsg4(dbglvl, "free_pool_memory %p pool=%d from %s:%d\n", buf, pool, fname, lineno);
    V(mutex);
 }
 
-
 #else
 
-/* =================================================================== */
+/* =========  NO SMARTALLOC  =========================================  */
 
 POOLMEM *get_pool_memory(int pool)
 {
@@ -234,10 +246,10 @@ POOLMEM *get_pool_memory(int pool)
       V(mutex);
       return (POOLMEM *)((char *)buf+HEAD_SIZE);
    }
-      
-   if ((buf=malloc(pool_ctl[pool].size+HEAD_SIZE)) == NULL) {
+
+   if ((buf=(struct abufhead*)malloc(pool_ctl[pool].size+HEAD_SIZE)) == NULL) {
       V(mutex);
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", pool_ctl[pool].size);
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), pool_ctl[pool].size);
    }
    buf->ablen = pool_ctl[pool].size;
    buf->pool = pool;
@@ -256,8 +268,8 @@ POOLMEM *get_memory(int32_t size)
    struct abufhead *buf;
    int pool = 0;
 
-   if ((buf=malloc(size+HEAD_SIZE)) == NULL) {
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", size);
+   if ((buf=(struct abufhead *)malloc(size+HEAD_SIZE)) == NULL) {
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), size);
    }
    buf->ablen = size;
    buf->pool = pool;
@@ -269,7 +281,6 @@ POOLMEM *get_memory(int32_t size)
    return (POOLMEM *)(((char *)buf)+HEAD_SIZE);
 }
 
-
 /* Return the size of a memory buffer */
 int32_t sizeof_pool_memory(POOLMEM *obuf)
 {
@@ -293,12 +304,12 @@ POOLMEM *realloc_pool_memory(POOLMEM *obuf, int32_t size)
    buf = realloc(cp, size+HEAD_SIZE);
    if (buf == NULL) {
       V(mutex);
-      Emsg1(M_ABORT, 0, "Out of memory requesting %d bytes\n", size);
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), size);
    }
    ((struct abufhead *)buf)->ablen = size;
    pool = ((struct abufhead *)buf)->pool;
-   if (size > pool_ctl[pool].max_size) {
-      pool_ctl[pool].max_size = size;
+   if (size > pool_ctl[pool].max_allocated) {
+      pool_ctl[pool].max_allocated = size;
    }
    V(mutex);
    return (POOLMEM *)(((char *)buf)+HEAD_SIZE);
@@ -325,54 +336,103 @@ void free_pool_memory(POOLMEM *obuf)
    pool = buf->pool;
    pool_ctl[pool].in_use--;
    if (pool == 0) {
-      free((char *)buf);             /* free nonpooled memory */
-   } else {                          /* otherwise link it to the free pool chain */
+      free((char *)buf);              /* free nonpooled memory */
+   } else {                           /* otherwise link it to the free pool chain */
 #ifdef DEBUG
       struct abufhead *next;
       /* Don't let him free the same buffer twice */
       for (next=pool_ctl[pool].free_buf; next; next=next->next) {
-        ASSERT(next != buf);  /* attempt to free twice */
+         if (next == buf) {
+            V(mutex);
+            ASSERT(next != buf);  /* attempt to free twice */
+         }
       }
 #endif
       buf->next = pool_ctl[pool].free_buf;
       pool_ctl[pool].free_buf = buf;
    }
-   Dmsg2(150, "free_pool_memory %x pool=%d\n", buf, pool);
+   Dmsg2(dbglvl, "free_pool_memory %p pool=%d\n", buf, pool);
    V(mutex);
 }
-
 #endif /* SMARTALLOC */
 
+/*
+ * Clean up memory pool periodically
+ *
+ */
+static time_t last_garbage_collection = 0;
+const int garbage_interval = 24 * 60 * 60;  /* garbage collect every 24 hours */
 
+void garbage_collect_memory_pool()
+{
+   time_t now;
 
+   Dmsg0(200, "garbage collect memory pool\n");
+   P(mutex);
+   if (last_garbage_collection == 0) {
+      last_garbage_collection = time(NULL);
+      V(mutex);
+      return;
+   }
+   now = time(NULL);
+   if (now >= last_garbage_collection + garbage_interval ||
+       sm_bytes > 500000) {
+      last_garbage_collection = now;
+      V(mutex);
+      garbage_collect_memory();
+   } else {
+      V(mutex);
+   }
+}
 
-
-
-/* Release all pooled memory */
+/* Release all freed pooled memory */
 void close_memory_pool()
 {
    struct abufhead *buf, *next;
-   int i;
+   int count = 0;
+   uint64_t bytes = 0;
+   char ed1[50];
 
-   sm_check(__FILE__, __LINE__, False);
+   sm_check(__FILE__, __LINE__, false);
    P(mutex);
-   for (i=1; i<=PM_MAX; i++) {
+   for (int i=1; i<=PM_MAX; i++) {
       buf = pool_ctl[i].free_buf;
       while (buf) {
-        next = buf->next;
-        free((char *)buf);
-        buf = next;
+         next = buf->next;
+         count++;
+         bytes += sizeof_pool_memory((char *)buf);
+         free((char *)buf);
+         buf = next;
       }
       pool_ctl[i].free_buf = NULL;
    }
+   Dmsg2(DT_MEMORY|001, "Freed mem_pool count=%d size=%s\n", count, edit_uint64_with_commas(bytes, ed1));
+   if (chk_dbglvl(DT_MEMORY|1)) {
+      print_memory_pool_stats();
+   }
    V(mutex);
+
 }
 
-#ifdef DEBUG
+/*
+ * Garbage collect and trim memory if possible
+ *  This should be called after all big memory usages
+ *  if possible.
+ */
+void garbage_collect_memory()
+{
+   close_memory_pool();         /* release free chain */
+#ifdef HAVE_MALLOC_TRIM
+   P(mutex);
+   malloc_trim(8192);
+   V(mutex);
+#endif
+}
 
-static char *pool_name(int pool)
+#ifdef DEBUG
+static const char *pool_name(int pool)
 {
-   static char *name[] = {"NoPool", "FNAME ", "MSG   ", "EMSG  "};
+   static const char *name[] = {"NoPool", "NAME  ", "FNAME ", "MSG   ", "EMSG  ", "BSOCK "};
    static char buf[30];
 
    if (pool >= 0 && pool <= PM_MAX) {
@@ -381,21 +441,226 @@ static char *pool_name(int pool)
    sprintf(buf, "%-6d", pool);
    return buf;
 }
-   
-/* Print staticstics on memory pool usage   
- */ 
+
+/* Print staticstics on memory pool usage
+ */
 void print_memory_pool_stats()
 {
-   int i;
-
-   Dmsg0(-1, "Pool   Maxsize  Maxused  Inuse\n");
-   for (i=0; i<=PM_MAX; i++)
-      Dmsg4(-1, "%5s  %7d  %7d  %5d\n", pool_name(i), pool_ctl[i].max_size,
-        pool_ctl[i].max_used, pool_ctl[i].in_use);
+   Pmsg0(-1, "Pool   Maxsize  Maxused  Inuse\n");
+   for (int i=0; i<=PM_MAX; i++)
+      Pmsg4(-1, "%5s  %7d  %7d  %5d\n", pool_name(i), pool_ctl[i].max_allocated,
+         pool_ctl[i].max_used, pool_ctl[i].in_use);
 
-   Dmsg0(-1, "\n");
+   Pmsg0(-1, "\n");
 }
 
 #else
-void print_memory_pool_stats() {} 
+void print_memory_pool_stats() {}
 #endif /* DEBUG */
+
+/*
+ * Concatenate a string (str) onto a pool memory buffer pm
+ *   Returns: length of concatenated string
+ */
+int pm_strcat(POOLMEM **pm, const char *str)
+{
+   int pmlen = strlen(*pm);
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   *pm = check_pool_memory_size(*pm, pmlen + len);
+   memcpy(*pm+pmlen, str, len);
+   return pmlen + len - 1;
+}
+
+int pm_strcat(POOLMEM *&pm, const char *str)
+{
+   int pmlen = strlen(pm);
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   pm = check_pool_memory_size(pm, pmlen + len);
+   memcpy(pm+pmlen, str, len);
+   return pmlen + len - 1;
+}
+
+int pm_strcat(POOLMEM *&pm, POOL_MEM &str)
+{
+   int pmlen = strlen(pm);
+   int len = strlen(str.c_str()) + 1;
+
+   pm = check_pool_memory_size(pm, pmlen + len);
+   memcpy(pm+pmlen, str.c_str(), len);
+   return pmlen + len - 1;
+}
+
+int pm_strcat(POOL_MEM &pm, const char *str)
+{
+   int pmlen = strlen(pm.c_str());
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   pm.check_size(pmlen + len);
+   memcpy(pm.c_str()+pmlen, str, len);
+   return pmlen + len - 1;
+}
+
+int pm_strcat(POOL_MEM &pm, POOL_MEM &str)
+{
+   int pmlen = strlen(pm.c_str());
+   int len;
+
+   len = strlen(str.c_str()) + 1;
+   pm.check_size(pmlen + len);
+   memcpy(pm.c_str()+pmlen, str.c_str(), len);
+   return pmlen + len - 1;
+}
+
+/*
+ * Copy a string (str) into a pool memory buffer pm
+ *   Returns: length of string copied
+ */
+int pm_strcpy(POOLMEM **pm, const char *str)
+{
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   *pm = check_pool_memory_size(*pm, len);
+   memcpy(*pm, str, len);
+   return len - 1;
+}
+
+int pm_strcpy(POOLMEM *&pm, const char *str)
+{
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   pm = check_pool_memory_size(pm, len);
+   memcpy(pm, str, len);
+   return len - 1;
+}
+
+int pm_strcpy(POOLMEM *&pm, POOL_MEM &str)
+{
+   int len = strlen(str.c_str()) + 1;
+
+   pm = check_pool_memory_size(pm, len);
+   memcpy(pm, str.c_str(), len);
+   return len - 1;
+}
+
+int pm_strcpy(POOL_MEM &pm, const char *str)
+{
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   pm.check_size(len);
+   memcpy(pm.c_str(), str, len);
+   return len - 1;
+}
+
+/*
+ * Copy data into a pool memory buffer pm
+ *   Returns: length of data copied
+ */
+int pm_memcpy(POOLMEM **pm, const char *data, int32_t n)
+{
+   *pm = check_pool_memory_size(*pm, n);
+   memcpy(*pm, data, n);
+   return n;
+}
+
+int pm_memcpy(POOLMEM *&pm, const char *data, int32_t n)
+{
+   pm = check_pool_memory_size(pm, n);
+   memcpy(pm, data, n);
+   return n;
+}
+
+int pm_memcpy(POOLMEM *&pm, POOL_MEM &data, int32_t n)
+{
+   pm = check_pool_memory_size(pm, n);
+   memcpy(pm, data.c_str(), n);
+   return n;
+}
+
+int pm_memcpy(POOL_MEM &pm, const char *data, int32_t n)
+{
+   pm.check_size(n);
+   memcpy(pm.c_str(), data, n);
+   return n;
+}
+
+/* ==============  CLASS POOL_MEM   ============== */
+
+/* Return the size of a memory buffer */
+int32_t POOL_MEM::max_size()
+{
+   int32_t size;
+   char *cp = mem;
+   cp -= HEAD_SIZE;
+   size = ((struct abufhead *)cp)->ablen;
+   Dmsg1(900, "max_size=%d\n", size);
+   return size;
+}
+
+void POOL_MEM::realloc_pm(int32_t size)
+{
+   char *cp = mem;
+   char *buf;
+   int pool;
+
+   P(mutex);
+   cp -= HEAD_SIZE;
+   buf = (char *)realloc(cp, size+HEAD_SIZE);
+   if (buf == NULL) {
+      V(mutex);
+      Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), size);
+   }
+   Dmsg2(900, "Old buf=%p new buf=%p\n", cp, buf);
+   ((struct abufhead *)buf)->ablen = size;
+   pool = ((struct abufhead *)buf)->pool;
+   if (size > pool_ctl[pool].max_allocated) {
+      pool_ctl[pool].max_allocated = size;
+   }
+   mem = buf+HEAD_SIZE;
+   V(mutex);
+   Dmsg3(900, "Old buf=%p new buf=%p mem=%p\n", cp, buf, mem);
+}
+
+int POOL_MEM::strcat(const char *str)
+{
+   int pmlen = strlen(mem);
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   check_size(pmlen + len);
+   memcpy(mem+pmlen, str, len);
+   return pmlen + len - 1;
+}
+
+int POOL_MEM::strcpy(const char *str)
+{
+   int len;
+
+   if (!str) str = "";
+
+   len = strlen(str) + 1;
+   check_size(len);
+   memcpy(mem, str, len);
+   return len - 1;
+}