Imported Upstream version 2.5.1
[debian/amanda] / common-src / alloc.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 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.37 2006/07/05 10:41:32 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 #include "queue.h"
36
37 static char *internal_vstralloc(const char *, va_list);
38
39 /*
40  *=====================================================================
41  * debug_caller_loc -- keep track of all allocation callers
42  *
43  * const char *debug_caller_loc(const char *file, int line)
44  *
45  * entry:       file = source file
46  *              line = source line
47  * exit:        a string like "genversion.c@999"
48  *
49  * The debug malloc library has a concept of a call stack that can be used
50  * to fine tune what was running when a particular allocation was done.
51  * We use it to tell who called our various allocation wrappers since
52  * it wouldn't do much good to tell us a problem happened because of
53  * the malloc call in alloc (they are all from there at some point).
54  *
55  * But the library expects the string passed to malloc_enter/malloc_leave
56  * to be static, so we build a linked list of each one we get (there are
57  * not really that many during a given execution).  When we get a repeat
58  * we return the previously allocated string.  For a bit of performance,
59  * we keep the list in least recently used order, which helps because
60  * the calls to us come in pairs (one for malloc_enter and one right
61  * after for malloc_leave).
62  *=====================================================================
63  */
64
65 const char *
66 debug_caller_loc(
67     const char *file,
68     int line)
69 {
70     /*@keep@*/
71     struct loc_str {
72         char *str;
73         LIST_ENTRY(loc_str) le;
74     } *ls;
75     static LIST_HEAD(, loc_str) root = LIST_HEAD_INITIALIZER(root);
76     static char loc[256];       /* big enough for filename@lineno */
77     const char *p;
78
79     if ((p = strrchr(file, '/')) != NULL)
80         file = p + 1;                           /* just the last path element */
81
82     snprintf(loc, SIZEOF(loc), "%s@%d", file, line);
83
84     for (ls = LIST_FIRST(&root); ls != NULL; ls = LIST_NEXT(ls, le)) {
85         if (strcmp(loc, ls->str) == 0) {
86             if (ls != LIST_FIRST(&root)) {
87                 /*
88                  * This is a repeat and was not at the head of the list.
89                  * Unlink it and move it to the front.
90                  */
91                 LIST_REMOVE(ls, le);
92                 LIST_INSERT_HEAD(&root, ls, le);
93             }
94             return (ls->str);
95         }
96     }
97
98     /*
99      * This is a new entry.  Put it at the head of the list.
100      */
101     ls = malloc(SIZEOF(*ls));
102     if (ls == NULL)
103         return ("??");                  /* not much better than abort */
104     ls->str = malloc(strlen(loc) + 1);
105     if (ls->str == NULL) {
106         free(ls);
107         return ("??");                  /* not much better than abort */
108     }
109     strcpy(ls->str, loc);
110     malloc_mark(ls);
111     malloc_mark(ls->str);
112     LIST_INSERT_HEAD(&root, ls, le);
113     return (ls->str);
114 }
115
116 /*
117  *=====================================================================
118  * Save the current source line for vstralloc/newvstralloc.
119  *
120  * int debug_alloc_push (char *s, int l)
121  *
122  * entry:       s = source file
123  *              l = source line
124  * exit:        always zero
125  * 
126  * See the comments in amanda.h about what this is used for.
127  *=====================================================================
128  */
129
130 #define DEBUG_ALLOC_SAVE_MAX    10
131
132 static struct {
133         char            *file;
134         int             line;
135 } debug_alloc_loc_info[DEBUG_ALLOC_SAVE_MAX];
136 static int debug_alloc_ptr = 0;
137
138 static char             *saved_file;
139 static int              saved_line;
140
141 int
142 debug_alloc_push(
143     char *s,
144     int l)
145 {
146     debug_alloc_loc_info[debug_alloc_ptr].file = s;
147     debug_alloc_loc_info[debug_alloc_ptr].line = l;
148     debug_alloc_ptr = (debug_alloc_ptr + 1) % DEBUG_ALLOC_SAVE_MAX;
149     return 0;
150 }
151
152 /*
153  *=====================================================================
154  * Pop the current source line information for vstralloc/newvstralloc.
155  *
156  * int debug_alloc_pop (void)
157  *
158  * entry:       none
159  * exit:        none
160  * 
161  * See the comments in amanda.h about what this is used for.
162  *=====================================================================
163  */
164
165 void
166 debug_alloc_pop(void)
167 {
168     debug_alloc_ptr =
169       (debug_alloc_ptr + DEBUG_ALLOC_SAVE_MAX - 1) % DEBUG_ALLOC_SAVE_MAX;
170     saved_file = debug_alloc_loc_info[debug_alloc_ptr].file;
171     saved_line = debug_alloc_loc_info[debug_alloc_ptr].line;
172 }
173
174 /*
175  * alloc - a wrapper for malloc.
176  */
177 void *
178 debug_alloc(
179     const char *s,
180     int l,
181     size_t size)
182 {
183     void *addr;
184
185     malloc_enter(debug_caller_loc(s, l));
186     addr = (void *)malloc(max(size, 1));
187     if (addr == NULL) {
188         errordump("%s@%d: memory allocation failed (" SIZE_T_FMT " bytes requested)",
189                   s ? s : "(unknown)",
190                   s ? l : -1,
191                   (SIZE_T_FMT_TYPE)size);
192         /*NOTREACHED*/
193     }
194     malloc_leave(debug_caller_loc(s, l));
195     return addr;
196 }
197
198
199 /*
200  * newalloc - free existing buffer and then alloc a new one.
201  */
202 void *
203 debug_newalloc(
204     const char *s,
205     int l,
206     void *old,
207     size_t size)
208 {
209     char *addr;
210
211     malloc_enter(debug_caller_loc(s, l));
212     addr = debug_alloc(s, l, size);
213     amfree(old);
214     malloc_leave(debug_caller_loc(s, l));
215     return addr;
216 }
217
218
219 /*
220  * stralloc - copies the given string into newly allocated memory.
221  *            Just like strdup()!
222  */
223 char *
224 debug_stralloc(
225     const char *s,
226     int l,
227     const char *str)
228 {
229     char *addr;
230
231     malloc_enter(debug_caller_loc(s, l));
232     addr = debug_alloc(s, l, strlen(str) + 1);
233     strcpy(addr, str);
234     malloc_leave(debug_caller_loc(s, l));
235     return (addr);
236 }
237
238 /* vstrextend -- Extends the existing string by appending the other 
239  * arguments. */
240 /*@ignore@*/
241 arglist_function(
242     char *vstrextend,
243     char **, oldstr)
244 {
245         char *keep = *oldstr;
246         va_list ap;
247
248         arglist_start(ap, oldstr);
249
250         if (*oldstr == NULL)
251                 *oldstr = "";
252         *oldstr = internal_vstralloc(*oldstr, ap);
253         amfree(keep);
254
255         arglist_end(ap);
256         return *oldstr;
257 }
258 /*@end@*/
259
260 /*
261  * internal_vstralloc - copies up to MAX_STR_ARGS strings into newly
262  * allocated memory.
263  *
264  * The MAX_STR_ARGS limit is purely an efficiency issue so we do not have
265  * to scan the strings more than necessary.
266  */
267
268 #define MAX_VSTRALLOC_ARGS      32
269
270 static char *
271 internal_vstralloc(
272     const char *str,
273     va_list argp)
274 {
275     char *next;
276     char *result;
277     int a, b;
278     size_t total_len;
279     const char *arg[MAX_VSTRALLOC_ARGS+1];
280     size_t len[MAX_VSTRALLOC_ARGS+1];
281     size_t l;
282
283     if (str == NULL) {
284         errordump("internal_vstralloc: str is NULL");
285         /*NOTREACHED*/
286     }
287
288     a = 0;
289     arg[a] = str;
290     l = strlen(str);
291     total_len = len[a] = l;
292     a++;
293
294     while ((next = arglist_val(argp, char *)) != NULL) {
295         if ((l = strlen(next)) == 0) {
296             continue;                           /* minor optimisation */
297         }
298         if (a >= MAX_VSTRALLOC_ARGS) {
299             errordump("%s@%d: more than %d args to vstralloc",
300                       saved_file ? saved_file : "(unknown)",
301                       saved_file ? saved_line : -1,
302                       MAX_VSTRALLOC_ARGS);
303             /*NOTREACHED*/
304         }
305         arg[a] = next;
306         len[a] = l;
307         total_len += l;
308         a++;
309     }
310
311     result = debug_alloc(saved_file, saved_line, total_len+1);
312
313     next = result;
314     for (b = 0; b < a; b++) {
315         memcpy(next, arg[b], len[b]);
316         next += len[b];
317     }
318     *next = '\0';
319
320     return result;
321 }
322
323
324 /*
325  * vstralloc - copies multiple strings into newly allocated memory.
326  */
327 arglist_function(
328     char *debug_vstralloc,
329     const char *, str)
330 {
331     va_list argp;
332     char *result;
333
334     debug_alloc_pop();
335     malloc_enter(debug_caller_loc(saved_file, saved_line));
336     arglist_start(argp, str);
337     result = internal_vstralloc(str, argp);
338     arglist_end(argp);
339     malloc_leave(debug_caller_loc(saved_file, saved_line));
340     return result;
341 }
342
343
344 /*
345  * newstralloc - free existing string and then stralloc a new one.
346  */
347 char *
348 debug_newstralloc(
349     const char *s,
350     int l,
351     char *oldstr,
352     const char *newstr)
353 {
354     char *addr;
355
356     malloc_enter(debug_caller_loc(s, l));
357     addr = debug_stralloc(s, l, newstr);
358     amfree(oldstr);
359     malloc_leave(debug_caller_loc(s, l));
360     return (addr);
361 }
362
363
364 /*
365  * newvstralloc - free existing string and then vstralloc a new one.
366  */
367 arglist_function1(
368     char *debug_newvstralloc,
369     char *, oldstr,
370     const char *, newstr)
371 {
372     va_list argp;
373     char *result;
374
375     debug_alloc_pop();
376     malloc_enter(debug_caller_loc(saved_file, saved_line));
377     arglist_start(argp, newstr);
378     result = internal_vstralloc(newstr, argp);
379     arglist_end(argp);
380     amfree(oldstr);
381     malloc_leave(debug_caller_loc(saved_file, saved_line));
382     return result;
383 }
384
385
386 /*
387  * safe_env - build a "safe" environment list.
388  */
389 char **
390 safe_env(void)
391 {
392     static char *safe_env_list[] = {
393         "TZ",
394 #ifdef __CYGWIN__
395         "SYSTEMROOT",
396 #endif
397 #ifdef NEED_PATH_ENV
398         "PATH",
399 #endif
400         "DISPLAY",
401         NULL
402     };
403
404     /*
405      * If the initial environment pointer malloc fails, set up to
406      * pass back a pointer to the NULL string pointer at the end of
407      * safe_env_list so our result is always a valid, although possibly
408      * empty, environment list.
409      */
410 #define SAFE_ENV_CNT    (size_t)(sizeof(safe_env_list) / sizeof(*safe_env_list))
411     char **envp = safe_env_list + SAFE_ENV_CNT - 1;
412
413     char **p;
414     char **q;
415     char *s;
416     char *v;
417     size_t l1, l2;
418
419     if ((q = (char **)malloc(SIZEOF(safe_env_list))) != NULL) {
420         envp = q;
421         for (p = safe_env_list; *p != NULL; p++) {
422             if ((v = getenv(*p)) == NULL) {
423                 continue;                       /* no variable to dup */
424             }
425             l1 = strlen(*p);                    /* variable name w/o null */
426             l2 = strlen(v) + 1;                 /* include null byte here */
427             if ((s = (char *)malloc(l1 + 1 + l2)) == NULL) {
428                 break;                          /* out of memory */
429             }
430             *q++ = s;                           /* save the new pointer */
431             memcpy(s, *p, l1);                  /* left hand side */
432             s += l1;
433             *s++ = '=';
434             memcpy(s, v, l2);                   /* right hand side and null */
435         }
436         *q = NULL;                              /* terminate the list */
437     }
438     return envp;
439 }
440
441 /*
442  * amtable_alloc -- (re)allocate enough space for some number of elements.
443  *
444  * input:       table -- pointer to pointer to table
445  *              current -- pointer to current number of elements
446  *              elsize -- size of a table element
447  *              count -- desired number of elements
448  *              bump -- round up factor
449  *              init_func -- optional element initialization function
450  * output:      table -- possibly adjusted to point to new table area
451  *              current -- possibly adjusted to new number of elements
452  */
453
454 int
455 debug_amtable_alloc(
456     const char *s,
457     int l,
458     void **table,
459     size_t *current,
460     size_t elsize,
461     size_t count,
462     int bump,
463     void (*init_func)(void *))
464 {
465     void *table_new;
466     size_t table_count_new;
467     size_t i;
468
469     if (count >= *current) {
470         table_count_new = ((count + bump) / bump) * bump;
471         table_new = debug_alloc(s, l, table_count_new * elsize);
472         if (0 != *table) {
473             memcpy(table_new, *table, *current * elsize);
474             free(*table);
475         }
476         *table = table_new;
477         memset(((char *)*table) + *current * elsize,
478                0,
479                (table_count_new - *current) * elsize);
480         if (init_func != NULL) {
481             for (i = *current; i < table_count_new; i++) {
482                 (*init_func)(((char *)*table) + i * elsize);
483             }
484         }
485         *current = table_count_new;
486     }
487     return 0;
488 }
489
490 /*
491  * amtable_free -- release a table.
492  *
493  * input:       table -- pointer to pointer to table
494  *              current -- pointer to current number of elements
495  * output:      table -- possibly adjusted to point to new table area
496  *              current -- possibly adjusted to new number of elements
497  */
498
499 void
500 amtable_free(
501     void **table,
502     size_t *current)
503 {
504     amfree(*table);
505     *current = 0;
506 }