Imported Upstream version 2.4.5
[debian/amanda] / common-src / alloc.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: alloc.c,v 1.17.2.1.4.3.2.3.2.1 2004/08/31 12:46:06 martinea Exp $
28  *
29  * Memory allocators with error handling.  If the allocation fails,
30  * errordump() is called, relieving the caller from checking the return
31  * code
32  */
33 #include "amanda.h"
34 #include "arglist.h"
35
36 /*
37  *=====================================================================
38  * debug_caller_loc -- keep track of all allocation callers
39  *
40  * char *debug_caller_loc(char *file, int line)
41  *
42  * entry:       file = source file
43  *              line = source line
44  * exit:        a string like "genversion.c@999"
45  *
46  * The debug malloc library has a concept of a call stack that can be used
47  * to fine tune what was running when a particular allocation was done.
48  * We use it to tell who called our various allocation wrappers since
49  * it wouldn't do much good to tell us a problem happened because of
50  * the malloc call in alloc (they are all from there at some point).
51  *
52  * But the library expects the string passed to malloc_enter/malloc_leave
53  * to be static, so we build a linked list of each one we get (there are
54  * not really that many during a given execution).  When we get a repeat
55  * we return the previously allocated string.  For a bit of performance,
56  * we keep the list in least recently used order, which helps because
57  * the calls to us come in pairs (one for malloc_enter and one right
58  * after for malloc_leave).
59  *=====================================================================
60  */
61
62 char *
63 debug_caller_loc(file, line)
64     char *file;
65     int line;
66 {
67     struct loc_str {
68         char *str;
69         struct loc_str *next;
70     };
71     static struct loc_str *root = NULL;
72     struct loc_str *ls, *ls_last;
73     int len;
74     int size;
75     char *p;
76     static char *loc = NULL;
77     static int loc_size = 0;
78
79     if ((p = strrchr(file, '/')) == NULL) {
80         p = file;                               /* keep the whole name */
81     } else {
82         p++;                                    /* just the last path element */
83     }
84
85     len = strlen (p);
86     size = len + 1 + NUM_STR_SIZE + 1;
87     if (size > loc_size) {
88         size = ((size + 64 - 1) / 64) * 64;     /* might as well get a bunch */
89         /*
90          * We should free the previous loc area, but we have marked it
91          * as a non-leak and the library considers it an error to free
92          * such an area, so we just ignore it.  We probably grabbed
93          * enough the first time that this will not even happen.
94          */
95         loc = malloc (size);
96         if (loc == NULL) {
97             return "??";                        /* not much better than abort */
98         }
99         malloc_mark (loc);
100         loc_size = size;
101     }
102
103     strcpy (loc, p);
104     ap_snprintf(loc + len, 1 + NUM_STR_SIZE, "@%d", line);
105
106     for (ls_last = NULL, ls = root; ls != NULL; ls_last = ls, ls = ls->next) {
107         if (strcmp (loc, ls->str) == 0) {
108             break;
109         }
110     }
111
112     if (ls == NULL) {
113         /*
114          * This is a new entry.  Put it at the head of the list.
115          */
116         ls = malloc (sizeof (*ls));
117         if (ls == NULL) {
118             return "??";                        /* not much better than abort */
119         }
120         malloc_mark (ls);
121         size = strlen (loc) + 1;
122         ls->str = malloc (size);
123         if (ls->str == NULL) {
124             free (ls);
125             return "??";                        /* not much better than abort */
126         }
127         malloc_mark (ls->str);
128         strcpy (ls->str, loc);
129         ls->next = root;
130         root = ls;
131     } else if (ls_last != NULL) {
132         /*
133          * This is a repeat and was not at the head of the list.
134          * Unlink it and move it to the front.
135          */
136         ls_last->next = ls->next;
137         ls->next = root;
138         root = ls;
139     } else {
140         /*
141          * This is a repeat but was already at the head of the list,
142          * so nothing else needs to be done.
143          */
144     }
145     return ls->str;
146 }
147
148 /*
149  *=====================================================================
150  * Save the current source line for vstralloc/newvstralloc.
151  *
152  * int debug_alloc_push (char *s, int l)
153  *
154  * entry:       s = source file
155  *              l = source line
156  * exit:        always zero
157  * 
158  * See the comments in amanda.h about what this is used for.
159  *=====================================================================
160  */
161
162 #define DEBUG_ALLOC_SAVE_MAX    10
163
164 static struct {
165         char            *file;
166         int             line;
167 } debug_alloc_loc_info[DEBUG_ALLOC_SAVE_MAX];
168 static int debug_alloc_ptr = 0;
169
170 static char             *saved_file;
171 static int              saved_line;
172
173 int
174 debug_alloc_push (s, l)
175     char *s;
176     int l;
177 {
178     debug_alloc_loc_info[debug_alloc_ptr].file = s;
179     debug_alloc_loc_info[debug_alloc_ptr].line = l;
180     debug_alloc_ptr = (debug_alloc_ptr + 1) % DEBUG_ALLOC_SAVE_MAX;
181     return 0;
182 }
183
184 /*
185  *=====================================================================
186  * Pop the current source line information for vstralloc/newvstralloc.
187  *
188  * int debug_alloc_pop (void)
189  *
190  * entry:       none
191  * exit:        none
192  * 
193  * See the comments in amanda.h about what this is used for.
194  *=====================================================================
195  */
196
197 void
198 debug_alloc_pop ()
199 {
200     debug_alloc_ptr =
201       (debug_alloc_ptr + DEBUG_ALLOC_SAVE_MAX - 1) % DEBUG_ALLOC_SAVE_MAX;
202     saved_file = debug_alloc_loc_info[debug_alloc_ptr].file;
203     saved_line = debug_alloc_loc_info[debug_alloc_ptr].line;
204 }
205
206 /*
207  * alloc - a wrapper for malloc.
208  */
209 void *
210 debug_alloc(s, l, size)
211     char *s;
212     int l;
213     size_t size;
214 {
215     void *addr;
216
217     malloc_enter(debug_caller_loc(s, l));
218     addr = (void *)malloc(max(size, 1));
219     if(addr == NULL) {
220         errordump("%s@%d: memory allocation failed (%u bytes requested)",
221                   s ? s : "(unknown)",
222                   s ? l : -1,
223                   size);
224     }
225     malloc_leave(debug_caller_loc(s, l));
226     return addr;
227 }
228
229
230 /*
231  * newalloc - free existing buffer and then alloc a new one.
232  */
233 void *
234 debug_newalloc(s, l, old, size)
235     char *s;
236     int l;
237     void *old;
238     size_t size;
239 {
240     char *addr;
241
242     malloc_enter(debug_caller_loc(s, l));
243     addr = debug_alloc(s, l, size);
244     amfree(old);
245     malloc_leave(debug_caller_loc(s, l));
246     return addr;
247 }
248
249
250 /*
251  * stralloc - copies the given string into newly allocated memory.
252  *            Just like strdup()!
253  */
254 char *
255 debug_stralloc(s, l, str)
256     char *s;
257     int l;
258     const char *str;
259 {
260     char *addr;
261
262     malloc_enter(debug_caller_loc(s, l));
263     addr = debug_alloc(s, l, strlen(str) + 1);
264     strcpy(addr, str);
265     malloc_leave(debug_caller_loc(s, l));
266     return addr;
267 }
268
269
270 /*
271  * internal_vstralloc - copies up to MAX_STR_ARGS strings into newly
272  * allocated memory.
273  *
274  * The MAX_STR_ARGS limit is purely an efficiency issue so we do not have
275  * to scan the strings more than necessary.
276  */
277
278 #define MAX_VSTRALLOC_ARGS      32
279
280 static char *
281 internal_vstralloc(str, argp)
282     const char *str;
283     va_list argp;
284 {
285     char *next;
286     char *result;
287     int a;
288     size_t total_len;
289     const char *arg[MAX_VSTRALLOC_ARGS+1];
290     size_t len[MAX_VSTRALLOC_ARGS+1];
291     size_t l;
292     const char *s;
293
294     if (str == NULL) {
295         return NULL;                            /* probably will not happen */
296     }
297
298     a = 0;
299     arg[a] = str;
300     l = strlen(str);
301     total_len = len[a] = l;
302     a++;
303
304     while ((next = arglist_val(argp, char *)) != NULL) {
305         if ((l = strlen(next)) == 0) {
306             continue;                           /* minor optimisation */
307         }
308         if (a >= MAX_VSTRALLOC_ARGS) {
309             errordump("%s@%d: more than %d arg%s to vstralloc",
310                       saved_file ? saved_file : "(unknown)",
311                       saved_file ? saved_line : -1,
312                       MAX_VSTRALLOC_ARGS,
313                       (MAX_VSTRALLOC_ARGS == 1) ? "" : "s");
314         }
315         arg[a] = next;
316         len[a] = l;
317         total_len += l;
318         a++;
319     }
320     arg[a] = NULL;
321     len[a] = 0;
322
323     next = result = debug_alloc(saved_file, saved_line, total_len+1);
324     for (a = 0; (s = arg[a]) != NULL; a++) {
325         memcpy(next, s, len[a]);
326         next += len[a];
327     }
328     *next = '\0';
329
330     return result;
331 }
332
333
334 /*
335  * vstralloc - copies multiple strings into newly allocated memory.
336  */
337 arglist_function(char *debug_vstralloc, const char *, str)
338 {
339     va_list argp;
340     char *result;
341
342     debug_alloc_pop();
343     malloc_enter(debug_caller_loc(saved_file, saved_line));
344     arglist_start(argp, str);
345     result = internal_vstralloc(str, argp);
346     arglist_end(argp);
347     malloc_leave(debug_caller_loc(saved_file, saved_line));
348     return result;
349 }
350
351
352 /*
353  * newstralloc - free existing string and then stralloc a new one.
354  */
355 char *
356 debug_newstralloc(s, l, oldstr, newstr)
357     char *s;
358     int l;
359     char *oldstr;
360     const char *newstr;
361 {
362     char *addr;
363
364     malloc_enter(debug_caller_loc(s, l));
365     addr = debug_stralloc(s, l, newstr);
366     amfree(oldstr);
367     malloc_leave(debug_caller_loc(s, l));
368     return addr;
369 }
370
371
372 /*
373  * newvstralloc - free existing string and then vstralloc a new one.
374  */
375 arglist_function1(char *debug_newvstralloc,
376                   char *,
377                   oldstr,
378                   const char *,
379                   newstr)
380 {
381     va_list argp;
382     char *result;
383
384     debug_alloc_pop();
385     malloc_enter(debug_caller_loc(saved_file, saved_line));
386     arglist_start(argp, newstr);
387     result = internal_vstralloc(newstr, argp);
388     arglist_end(argp);
389     amfree(oldstr);
390     malloc_leave(debug_caller_loc(saved_file, saved_line));
391     return result;
392 }
393
394
395 /*
396  * sbuf_man - static buffer manager.
397  *
398  * Manage a bunch of static buffer pointers.
399  */
400 void *sbuf_man(e_bufs, ptr)
401     void *e_bufs; /* XXX - I dont think this is right */
402     void *ptr;
403 {
404         SBUF2_DEF(1) *bufs;
405         int slot;
406
407         bufs = e_bufs;
408
409         /* try and trap bugs */
410         assert(bufs->magic == SBUF_MAGIC);
411         assert(bufs->max > 0);
412
413         /* initialise first time through */
414         if(bufs->cur == -1)
415                 for(slot=0; slot < bufs->max; slot++) {
416                         bufs->bufp[slot] = (void *)0;
417                 } 
418
419         /* calculate the next slot */
420         slot = bufs->cur + 1;
421         if (slot >= bufs->max) slot = 0;
422
423         /* free the previous inhabitant */
424         if(bufs->bufp[slot] != (void *)0) free(bufs->bufp[slot]);
425
426         /* store the new one */
427         bufs->bufp[slot] = ptr;
428         bufs->cur = slot;
429
430         return ptr;
431 }
432
433
434 /*
435  * safe_env - build a "safe" environment list.
436  */
437 char **
438 safe_env()
439 {
440     static char *safe_env_list[] = {
441         "TZ",
442 #ifdef __CYGWIN__
443         "SYSTEMROOT",
444 #endif
445 #ifdef NEED_PATH_ENV
446         "PATH",
447 #endif
448         NULL
449     };
450
451     /*
452      * If the initial environment pointer malloc fails, set up to
453      * pass back a pointer to the NULL string pointer at the end of
454      * safe_env_list so our result is always a valid, although possibly
455      * empty, environment list.
456      */
457 #define SAFE_ENV_CNT    (sizeof(safe_env_list) / sizeof(*safe_env_list))
458     char **envp = safe_env_list + SAFE_ENV_CNT - 1;
459
460     char **p;
461     char **q;
462     char *s;
463     char *v;
464     size_t l1, l2;
465
466     if ((q = (char **)malloc(sizeof(safe_env_list))) != NULL) {
467         envp = q;
468         for (p = safe_env_list; *p != NULL; p++) {
469             if ((v = getenv(*p)) == NULL) {
470                 continue;                       /* no variable to dup */
471             }
472             l1 = strlen(*p);                    /* variable name w/o null */
473             l2 = strlen(v) + 1;                 /* include null byte here */
474             if ((s = (char *)malloc(l1 + 1 + l2)) == NULL) {
475                 break;                          /* out of memory */
476             }
477             *q++ = s;                           /* save the new pointer */
478             memcpy(s, *p, l1);                  /* left hand side */
479             s += l1;
480             *s++ = '=';
481             memcpy(s, v, l2);                   /* right hand side and null */
482         }
483         *q = NULL;                              /* terminate the list */
484     }
485     return envp;
486 }
487
488 /*
489  * amtable_alloc -- (re)allocate enough space for some number of elements.
490  *
491  * input:       table -- pointer to pointer to table
492  *              current -- pointer to current number of elements
493  *              elsize -- size of a table element
494  *              count -- desired number of elements
495  *              bump -- round up factor
496  *              init_func -- optional element initialization function
497  * output:      table -- possibly adjusted to point to new table area
498  *              current -- possibly adjusted to new number of elements
499  */
500
501 int
502 debug_amtable_alloc(s, l, table, current, elsize, count, bump, init_func)
503     char *s;
504     int l;
505     void **table;
506     int *current;
507     size_t elsize;
508     int count;
509     int bump;
510     void (*init_func)(void *);
511 {
512     void *table_new;
513     int table_count_new;
514     int i;
515
516     if (count >= *current) {
517         table_count_new = ((count + bump) / bump) * bump;
518         table_new = debug_alloc(s, l, table_count_new * elsize);
519         if (0 != *table) {
520             memcpy(table_new, *table, *current * elsize);
521             free(*table);
522         }
523         *table = table_new;
524         memset(((char *)*table) + *current * elsize,
525                0,
526                (table_count_new - *current) * elsize);
527         if (init_func != NULL) {
528             for (i = *current; i < table_count_new; i++) {
529                 (*init_func)(((char *)*table) + i * elsize);
530             }
531         }
532         *current = table_count_new;
533     }
534     return 0;
535 }
536
537 /*
538  * amtable_free -- release a table.
539  *
540  * input:       table -- pointer to pointer to table
541  *              current -- pointer to current number of elements
542  * output:      table -- possibly adjusted to point to new table area
543  *              current -- possibly adjusted to new number of elements
544  */
545
546 void
547 amtable_free(table, current)
548     void **table;
549     int *current;
550 {
551     amfree(*table);
552     *current = 0;
553 }