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