source: branches/ithildin-1.1/source/module.c @ 623

Revision 623, 28.1 KB checked in by wd, 6 years ago (diff)

Don't unload modules when debugging.

  • Property svn:keywords set to Id Rev
Line 
1/*
2 * module.c: module system support functions
3 *
4 * Copyright 2002 the Ithildin Project.
5 * See the COPYING file for more information on licensing and use.
6 */
7
8#include <ithildin/stand.h>
9
10IDSTRING(rcsid, "$Id$");
11
12#ifdef HAVE_DLFCN_H
13# include <dlfcn.h>
14#else
15# ifdef HAVE_DL_H
16#  include <dl.h>
17# endif
18#endif
19
20/* create a module structure. */
21static module_t *create_module(char *name);
22static void destroy_module(module_t *);
23
24static LIST_HEAD(, msymbol) msym_list;
25static msymbol_t *find_msymbol(char *);
26
27/* this is called both at start-up and at any time when the conf is reloaded
28 * to parse the 'modules' configuration section */
29void build_module_list(void) {
30    conf_list_t *list = conf_find_list("modules", me.confhead, 1);
31    conf_list_t *clp = NULL;
32    conf_entry_t *cep = NULL, *oldcep;
33    module_t *m = NULL;
34    module_t *last = NULL; /* track our last addition */
35    char *s = NULL;
36
37    if (list == NULL) {
38        log_warn("there is no config entity for modules, this probably "
39                "won't be a very functional daemon.");
40        return;
41    }
42
43    /* new configuration stuff will be read in here if it is created */
44
45    /* use the conf_find_next bits to find what we want here */
46    oldcep = cep = conf_find("module", NULL, 0, list, 1);
47    /* now pull in each load statement and parse it */
48    while (cep != NULL) {
49        do { /* use a do/while loop so we can 'break' from it without making
50                life miserable or using silly gotos */
51            int new = 0;
52            if (cep->type == CONF_TYPE_LIST) {
53                /* check the 'string' value first, before checking the file
54                 * value. */
55                clp = cep->list;
56                if (cep->string != NULL)
57                    s = cep->string;
58                else {
59                    s = conf_find_entry("file", clp, 1);
60                    if (s == NULL)
61                        break;
62                }
63            } else
64                s = cep->string;
65
66            m = find_module(s);
67            if (m == NULL) {
68                m = create_module(s);
69                new++;
70            }
71            if (m == NULL)
72                break;
73
74            if (cep->type == CONF_TYPE_LIST) {
75                m->confdata = conf_find_list("data", clp, 1);
76                if ((s = conf_find_entry("load", clp, 1)) != NULL &&
77                        str_conv_bool(s, false))
78                    m->flags |= MODULE_FL_AUTOLOAD;
79                else
80                    m->flags |= MODULE_FL_AUTOLOAD; /* default is to load. */
81                if (str_conv_bool(conf_find_entry("export", clp, 1), 0))
82                    m->flags |= MODULE_FL_DEPENDLOAD;
83                if (cep->string != NULL) {
84                    free(m->name);
85                    m->name = strdup(cep->string);
86                }
87            } else
88                m->confdata = NULL;
89
90            if (new) {
91                if (last == NULL)
92                    LIST_INSERT_HEAD(&me.modules, m, lp);
93                else
94                    LIST_INSERT_AFTER(last, m, lp);
95
96                last = m;
97            }
98        } while (0); /* just a once-through, heh */
99        oldcep = cep = conf_find_next("module", NULL, 0, oldcep, list, 1);
100    }
101
102    /* now load modules which requested autoloading.  we do this here instead
103     * of above so we can read in all the module data before we load
104     * anything.  very handy, incidentally, modules are loaded in reverse
105     * order from the file.  hopefully that won't matter to anyone :) */
106    LIST_FOREACH(m, &me.modules, lp) {
107        if (m->flags & MODULE_FL_AUTOLOAD && !(m->flags & MODULE_FL_LOADED))
108            load_module(m->name, m->flags);
109    }
110}
111
112/* this function unloads all modules rather indiscriminately.  it just starts
113 * at the top of the load and starts dropping them. */
114void unload_all_modules(void) {
115    module_t *m;
116
117    while ((m = LIST_FIRST(&me.modules)) != NULL) {
118        unload_module(m->name);
119        destroy_module(m);
120    }
121}
122
123/* this function handles all the necessary work to load a module.  it first
124 * checks for a .deps file in the same place as the module, and if one exists,
125 * opens it and loads all dependencies listed therein.  then it opens the dll,
126 * checks version headers, and calls the module's loader function. */
127int load_module(char *name, int flags) {
128    module_t *m = NULL;
129    /* initially, we don't resolve symbols, we will run another dlopen call
130     * when our dependancies are loaded with RTLD_NOW to ensure that things
131     * work as they should */
132    int rtld_flags = RTLD_NOW;
133    char fn[33]; /* used for making function names */
134    char *sn; /* short name */
135    FILE *fp;
136    struct module_savedata *msdp;
137    struct module_dependency *mdep;
138    msymbol_t *msym;
139
140    if ((m = find_module(name)) == NULL) {
141        if (!(flags & MODULE_FL_CREATE)) {
142            log_warn("attempt to load module %s not in modules list.", name);
143            return 0;
144        } else {
145            module_t *last;
146            m = create_module(name);
147            if (m == NULL)
148                return 0;
149            last = LIST_FIRST(&me.modules);
150            while (last != NULL) {
151                if (LIST_NEXT(last, lp) == NULL)
152                    break;
153                last = LIST_NEXT(last, lp);
154            }
155
156            LIST_INSERT_AFTER(last, m, lp);
157        }
158    }
159
160    if (!(m->flags & MODULE_FL_LOADED) && !(flags & MODULE_FL_QUIET))
161        log_notice("loading module %s...", m->name);
162
163    /* load dependcies.  before we do, attest to the open-ness of this module.
164     * this isn't, technically, true, since we haven't initialized it.
165     * however, that can't be helped in the case of circular dependencies.
166     * this is simply a tough area... since we don't even do the dlopen()
167     * before we do the load_modules call, if you've got a circular dependency
168     * that won't survive on a lazy load, you're up the creek.  sorry. */
169    if (m->depfile != NULL && !(m->flags & MODULE_FL_LOADED)) {
170        char sname[PATH_MAX];
171
172        m->flags |= MODULE_FL_LOADED;
173        fp = fopen(m->depfile, "r");
174        if (fp != NULL) {
175            while ((sn = sfgets(sname, PATH_MAX, fp)) != NULL) {
176                module_t *m2 = find_module(sn);
177
178                /* okay, depending on what's going on here, we do different
179                 * things.  if the module is reloading and a dependency isn't
180                 * loaded yet, we just return quietly because we assume we'll
181                 * get called later (see down towards the bottom of this
182                 * function), otherwise if we can't load that dependency,
183                 * return an error. */
184                if ((m2 == NULL || !(m2->flags & MODULE_FL_DEPENDLOAD))) {
185                    if (m2 == NULL)
186                        log_debug("loading dependant module %s for %s",
187                                sn, name);
188#if 0
189                    if (m->flags & MODULE_FL_RELOADING) {
190                        /* aaaaactually.  we only return if the module already
191                         * exists and is *also* reloading, this way a reload
192                         * with a module that has new dependencies will still
193                         * work! :) */
194                        if (m2 != NULL && m2->flags & MODULE_FL_RELOADING) {
195                            m->flags &= ~MODULE_FL_LOADED;
196                            fclose(fp);
197                            return 0; /* waiting.. */
198                        }
199                    }
200#endif
201                    if (!load_module(sn, flags | MODULE_FL_DEPENDLOAD |
202                            MODULE_FL_CREATE | MODULE_FL_QUIET)) {
203                        log_error(
204                                "loading dependant failed, giving up for %s!",
205                                sn);
206                        fclose(fp);
207                        return 0;
208                    }
209                }
210
211                /* loaded?  add our current module to this new module's
212                 * dependency listing. */
213                m2 = find_module(sn);
214                mdep = malloc(sizeof(struct module_dependency));
215                mdep->mod = m;
216                LIST_INSERT_HEAD(&m2->deps, mdep, lp);
217
218            }
219            fclose(fp);
220        }
221        m->flags &= ~MODULE_FL_LOADED; /* undo this trickery. */
222    }
223
224    /* chceck dependencies here */
225    if (flags & MODULE_FL_DEPENDLOAD) {
226        if (m->flags & MODULE_FL_DEPENDLOAD && m->flags & MODULE_FL_LOADED)
227            return 1; /* already depend loaded */
228        rtld_flags |= RTLD_GLOBAL;
229        m->flags |= MODULE_FL_DEPENDLOAD;
230    }
231
232    m->handle = dlopen(m->fullpath, rtld_flags);
233    m->opencalls++;
234    if (m->handle == NULL) {
235        log_error("in %s: %s", m->name, dlerror());
236        return 0;
237    }
238
239    if (m->flags & MODULE_FL_LOADED)
240        return 1;
241       
242    m->header = (struct module_header *)dlsym(m->handle, "mheader");
243    if (m->header == NULL) {
244        log_error("in %s: %s", m->name, dlerror());
245        dlclose(m->handle);
246        m->opencalls = 0;
247        return 0;
248    }
249
250    sn = strrchr(m->name, '/');
251    if (sn == NULL)
252        sn = m->name;
253    else
254        sn++;
255
256    snprintf(fn, 32, "%s_loader", sn);
257    m->load_function = (module_load_function)dlsym(m->handle, fn);
258    snprintf(fn, 32, "%s_unloader", sn);
259    m->unload_function = (module_unload_function)dlsym(m->handle, fn);
260
261    /* check the actual mheader here for problems, warn user if it is
262     * significantly older, and refuse to load if it is significantly newer.
263     * (significantly newer means a major release, or two minor releases
264     * ahead (i.e 2.2 -> 2.4 is a significant change!), significantly older
265     * means two minor releases older, or a major release older) */
266    if (m->header->baseversion.major > MAJOR_VER) {
267        log_error("module %s was created for a newer %s (%i.%i(%i)), "
268                "and will not be loaded.", name, BASENAME_VER,
269                m->header->baseversion.major, m->header->baseversion.minor,
270                m->header->baseversion.patch);
271        dlclose(m->handle);
272        m->opencalls = 0;
273        return 0;
274    } else if (m->header->baseversion.major < MAJOR_VER)
275        log_warn("module %s was created for an older %s (%i.%i(%i)), "
276                "and may not work", name, BASENAME_VER,
277                m->header->baseversion.major, m->header->baseversion.minor,
278                m->header->baseversion.patch);
279    else {
280        if (m->header->baseversion.minor - MINOR_VER >= 2) {
281            log_error("module %s was created for a newer %s (%i.%i(%i)), "
282                    "and will not be loaded.", name, BASENAME_VER,
283                    m->header->baseversion.major, m->header->baseversion.minor,
284                    m->header->baseversion.patch);
285            dlclose(m->handle);
286            m->opencalls = 0;
287            return 0;
288        }
289        else if (m->header->baseversion.minor - MINOR_VER <= -2)
290            log_warn("module %s was created for an older %s (%i.%i(%i)), "
291                    "and may not work", name, BASENAME_VER,
292                    m->header->baseversion.major, m->header->baseversion.minor,
293                    m->header->baseversion.patch);
294    } /* ugh, okay, it's an alright version <G> */
295
296    /* go ahead and say it's loaded now, so that if it tries to load other
297     * modules it will be okay. */
298    m->flags |= MODULE_FL_LOADED | flags;
299
300    /* okay, hopefully this will stay here.  I've decided to hook the
301     * 'load_module' event prior to load function being called so that other
302     * subsystems can do any data setup necessary that the load function might
303     * wish to rely on.  hope this works. ;) */
304    hook_event(me.events.load_module, m->name);
305
306    if (m->load_function != NULL) {
307        if (!m->load_function((m->flags & MODULE_FL_RELOADING ? 1 : 0),
308                    &m->savedata, &m->confdata, m)) {
309            /* okay, it broke...clean up and go home */
310            dlclose(m->handle);
311            m->opencalls = 0;
312            m->flags &= ~MODULE_FL_LOADED;
313            log_error("failed to load module %s", m->name);
314            return 0;
315        }
316    }
317
318    /* go ahead and take care of destroying the saved data for them */
319    while (!SLIST_EMPTY(&m->savedata)) {
320        msdp = SLIST_FIRST(&m->savedata);
321        SLIST_REMOVE_HEAD(&m->savedata, lp);
322        free(msdp->data);
323        free(msdp->name);
324        free(msdp);
325    }
326
327    /* fix symbol exports */
328    LIST_FOREACH(msym, &msym_list, lp) {
329        if (msym->module == m) {
330            msym->val = dlsym(m->handle, msym->name);
331            if (msym->val == NULL)
332                log_error("yipes!  invalidated module symbol %s(%s)",
333                        msym->name, m->name);
334        }
335    }
336
337    /* if we are reloading, walk the dependants list and load all those, as
338     * well. */
339    if (m->flags & MODULE_FL_RELOADING) {
340        log_debug("reloaded module %s", m->name);
341        m->flags &= ~MODULE_FL_RELOADING; /* reloaded.  (keep this above) */
342        LIST_FOREACH(mdep, &m->deps, lp) {
343            load_module(mdep->mod->name,
344                    mdep->mod->flags | MODULE_FL_DEPENDLOAD | MODULE_FL_QUIET);
345        }
346    }
347
348    return 1;
349}
350
351module_t *find_module(char *name) {
352    module_t *m;
353
354    LIST_FOREACH(m, &me.modules, lp) {
355        if (!strcmp(m->name, name))
356            return m;
357    }
358
359    return NULL;
360}
361
362void *lookup_module_symbol(char *name, char *sym) {
363    module_t *mod;
364    if (*name == '\0')
365        return module_symbol(NULL, sym);
366    else {
367        mod = find_module(name);
368        if (mod != NULL)
369            return module_symbol(mod, sym);
370    }
371
372    return NULL;
373}
374
375void *module_symbol(module_t *mod, const char *sym) {
376    return dlsym(mod->handle, (char *) sym);
377}
378
379int unload_module(char *name) {
380    module_t *m = find_module(name);
381    module_t *m2;
382    struct module_dependency *mdep, *mdep2;
383    msymbol_t *msym;
384
385    if (m == NULL) {
386        log_warn("attempt to unloaded nonexistant module %s", name);
387        return 0;
388    }
389
390    /* sure, we unloaded it!  since this may be done automatically to clean
391     * up stale dependencies, and may happen more than once (possible,
392     * shrug), we just silently ignore this */
393    if (!(m->flags & MODULE_FL_LOADED))
394        return 1; 
395
396    /* tag the module as unloaded so we don't try and unload it again.  at this
397     * point everything below ought to be successful, soooo. */
398    m->flags &= ~(MODULE_FL_LOADED|MODULE_FL_DEPENDLOAD);
399
400    /* go through their list of dependencies and unload them.  if we are
401     * actually reloading, set the reload flag on all of them prior to the
402     * unload call. */
403    mdep = LIST_FIRST(&m->deps);
404    while (mdep != NULL) {
405        /* we have different behaviors depending on what we're doing.  if this
406         * is a reload, we know our entries won't disappear, if it isn't, we
407         * know they will! */
408        if (m->flags & MODULE_FL_RELOADING) {
409            mdep2 = LIST_NEXT(mdep, lp);
410            if (mdep->mod->flags & MODULE_FL_LOADED) {
411                mdep->mod->flags |= MODULE_FL_RELOADING;
412                unload_module(mdep->mod->name);
413            }
414            mdep = mdep2;
415        } else {
416            if (mdep->mod->flags & MODULE_FL_LOADED)
417                unload_module(mdep->mod->name);
418            else {
419                log_warn("unload_module(%s): unloaded dependency for %s",
420                        m->name, mdep->mod->name);
421                LIST_REMOVE(mdep, lp);
422                free(mdep); /* um..? */
423            }
424            mdep = LIST_FIRST(&m->deps);
425        }
426    }
427
428    /* similar to the way it works in load_module(), hook here, before we call
429     * the unload function, so that everything is cleaned up prior to the
430     * module really disappearing. */
431    hook_event(me.events.unload_module, m->name);
432
433    /* clear sailing from here, close the handle and clear out the data */
434    if (m->unload_function != NULL)
435        m->unload_function((m->flags & MODULE_FL_RELOADING ? 1 : 0),
436                &m->savedata);
437       
438    while (m->opencalls) {
439#ifdef DEBUG
440        /* dmalloc doesn't like it when we close libraries while it's shutting
441         * down.  okay, well, if me.shutdown is true, don't dlclose, but do
442         * everything else.  Valgrind dislikes this too, let's just disable
443         * it in the debug case. */
444        if (!me.shutdown)
445#endif
446        if (dlclose(m->handle))
447            log_error("dlclose(%s): %s", m->name, dlerror());
448        m->opencalls--;
449    }
450
451    /* nullify symbols */
452    LIST_FOREACH(msym, &msym_list, lp) {
453        if (msym->module == m)
454            msym->val = NULL;
455    }
456
457    /* now for each of our modules, if they have a dependency for this module,
458     * remove it from their listing. */
459    if (!(m->flags & MODULE_FL_RELOADING)) {
460        LIST_FOREACH(m2, &me.modules, lp) {
461            mdep = LIST_FIRST(&m2->deps);
462            while (mdep != NULL) {
463                mdep2 = LIST_NEXT(mdep, lp);
464                if (mdep->mod == m) {
465                    LIST_REMOVE(mdep, lp);
466                    free(mdep);
467                }
468                mdep = mdep2;
469            }
470        }
471    }
472
473    log_debug("unloaded module %s", m->name);
474
475    return 1;
476}
477
478int reload_module(char *name) {
479    module_t *m = find_module(name);
480
481    if (m == NULL)
482        return 0;
483    m->flags |= MODULE_FL_RELOADING;
484    me.reloads++;
485
486    return 1; /* the module will get reloaded later. */
487}
488
489void do_module_reloads(void) {
490    module_t *m;
491
492    LIST_FOREACH(m, &me.modules, lp) {
493        if (!(m->flags & MODULE_FL_RELOADING))
494            continue; /* skip */
495
496        if (m->flags & MODULE_FL_LOADED)
497            if (unload_module(m->name) == 0)
498                return;
499
500        load_module(m->name, m->flags);
501    }
502}
503
504int module_loaded(char *name) {
505    module_t *m = find_module(name);
506
507    if (m != NULL && m->flags & MODULE_FL_LOADED)
508        return 1;
509
510    return 0;
511}
512
513/* this function adds data of the given size with the given name into the given
514 * list.  it is useful to call this when a module is being unloaded to save its
515 * data. */
516void add_module_savedata(struct module_savedata_list *list, const char *name,
517        size_t size, const void *data) {
518    struct module_savedata *amsdp = malloc(sizeof(struct module_savedata));
519
520    amsdp->name = strdup(name);
521    amsdp->size = size;
522    amsdp->data = malloc(amsdp->size);
523    memcpy(amsdp->data, data, amsdp->size);
524    SLIST_INSERT_HEAD(list, amsdp, lp);               
525}
526
527/* this function attempts to copy the data from a given module savedata list
528 * with the given name into the given address.  If it finds the data, it copies
529 * it in and returns the number of bytes copied, otherwise it returns 0. */
530size_t get_module_savedata(struct module_savedata_list *list, const char *name,
531        void *data) {
532    struct module_savedata *gmsdp;
533    SLIST_FOREACH(gmsdp, list, lp) {
534        if (!strcmp(gmsdp->name, name)) {
535            memcpy(data, gmsdp->data, gmsdp->size);
536            return gmsdp->size;
537        }
538    }                       
539
540    return 0;
541}
542
543static module_t *create_module(char *name) {
544    module_t *m;
545    char *s;
546
547    m = calloc(1, sizeof(module_t));
548    m->fullpath = malloc(PATH_MAX);
549    m->name = strdup(name);
550    /* strip off the extension */
551    s = strrchr(m->name, '.');
552    if (s != NULL) {
553        if (!strcmp(s, ".so"))
554            *s = '\0';
555    }
556    /* check to see if there's a dependncies file */
557    sprintf(m->fullpath, "%s/%s.deps", me.lib_path, m->name);
558    if (access(m->fullpath, R_OK) != -1)
559        m->depfile = strdup(m->fullpath);
560    else
561        m->depfile = NULL;
562    sprintf(m->fullpath, "%s/%s.so", me.lib_path, m->name);
563    if (access(m->fullpath, R_OK) == -1) {
564        log_error("unable to open module %s from %s: %s", m->name,
565                m->fullpath, strerror(errno));
566        if (m->depfile != NULL)
567            free(m->depfile);
568        free(m->name);
569        free(m);
570        return NULL;
571    }
572    return m;
573}
574
575static void destroy_module(module_t *mod) {
576
577    LIST_REMOVE(mod, lp);
578    free(mod->name);
579    free(mod->fullpath);
580    if (mod->depfile != NULL)
581        free(mod->depfile);
582    free(mod);
583}
584
585/* module symbol stuffs. */
586static msymbol_t *find_msymbol(char *name) {
587    msymbol_t *msp;
588
589    LIST_FOREACH(msp, &msym_list, lp) {
590        if (!strcmp(msp->name, name))
591            return msp;
592    }
593
594    return NULL;
595}
596
597/* this function 'exports' a symbol (basically, creates an msymbol structure
598 * for it, and finds it, etc.  it can be called by import_msymbol, and is
599 * basically useful as a 'pre-declare' call. */
600msymbol_t *export_symbol(char *name, module_t *mod) {
601    msymbol_t *msp;
602    void *obj = NULL;
603
604    if (mod != NULL && !(mod->flags & MODULE_FL_LOADED)) {
605        log_error("export_symbol(%s, %s) called with unloaded module.",
606                name, mod->name);
607        return NULL; /* blech. */
608    }
609   
610    msp = find_msymbol(name);
611    if (msp != NULL && msp->module != mod) {
612        log_warn("attempt to overwrite msymbol %s(%s)", msp->name,
613                msp->module->name);
614        return msp; /* hm..? */
615    }
616   
617    if (msp == NULL) {
618        msp = malloc(sizeof(msymbol_t));
619        LIST_INSERT_HEAD(&msym_list, msp, lp);
620    }
621
622    strlcpy(msp->name, name, sizeof(msp->name));
623    if (mod != NULL) {
624        /* they told us where to get it from.. */
625        if ((obj = dlsym(mod->handle, msp->name)) == NULL) {
626            log_warn("export_symbol(%s, %s): could not find symbol", msp->name,
627                    mod->name);
628            LIST_REMOVE(msp, lp);
629            free(msp);
630            return NULL;
631        }
632        msp->module = mod;
633        msp->val = obj;
634        return msp;
635    } else {
636        /* try the modules we know of first */
637        mod = LIST_FIRST(&me.modules);
638        while (obj == NULL && mod != NULL) {
639            if (mod->flags & MODULE_FL_LOADED) {
640                if ((obj = dlsym(mod->handle, msp->name)) != NULL)
641                    break;
642            }
643            mod = LIST_NEXT(mod, lp);
644        }
645        if (obj == NULL) {
646            /* no luck?  try RTLD_DEFAULT..  Actually, try NULL (the executable
647             * itself), then RTLD_NEXT (all the shared modules loaded by the
648             * executable .. which is actually more than we want, but will get
649             * us to compile-time linked libraries when nothing else will) */
650            mod = NULL;
651            if ((obj = dlsym(NULL, msp->name)) == NULL)
652                obj = dlsym(RTLD_NEXT, msp->name);
653        }
654    }
655    if (obj == NULL) {
656        log_warn("export_symbol(%s): could not find symbol", msp->name);
657        LIST_REMOVE(msp, lp);
658        free(msp);
659        return NULL;
660    }
661    msp->module = mod;
662    msp->val = obj;
663
664    return msp;
665}
666
667/* this function is considerably simpler.  do a symbol lookup to see if we
668 * already know of this symbol, if we do then pass it back to the caller,
669 * otherwise try to get it from an export, then return the final value. */
670msymbol_t *import_symbol(char *name) {
671    msymbol_t *msp;
672
673    msp = find_msymbol(name);
674    if (msp == NULL)
675        msp = export_symbol(name, NULL);
676
677    return msp;
678}
679
680/* below here live the declarations for 'module data extensions' (mdext).  the
681 * gist of this is that you create an mdext header for your structure type, and
682 * then in each structure place a char pointer called 'mdext'.  now when a
683 * module wants to register extra data in another module's structure, it can
684 * ask for an mdext offset, with a given size, and can then use that mdext
685 * offset to access into the structure.  modules are responsible for properly
686 * allocating mdext space, and should provide hooks for expanding and shrinking
687 * mdext data. */
688
689#define mdext_realloc(header, data) do {                                \
690    if (header->size != 0)                                                \
691        data = realloc(data, header->size);                                \
692    else {                                                                \
693        if (data != NULL)                                                \
694            free(data);                                                        \
695        data = NULL;                                                        \
696    }                                                                        \
697} while (0)
698
699/* this creates an mdext header.  you should have one header per structure
700 * type, not per structure.  this is used as the head of the tracking data for
701 * that structure. */
702struct mdext_header *create_mdext_header(const char *iter) {
703    struct mdext_header *mdhp = NULL;
704
705    mdhp = calloc(1, sizeof(struct mdext_header));
706    mdhp->create = create_event(EVENT_FL_NORETURN);
707    mdhp->destroy = create_event(EVENT_FL_NORETURN);
708    mdhp->iter = import_symbol((char *) iter);
709
710    return mdhp;
711}
712
713/* this destroys an mdext header and all associated data. */
714void destroy_mdext_header(struct mdext_header *header) {
715    struct mdext_item *mip, *mip2;
716
717    /* wipe out any remaining items. */
718    mip = SLIST_FIRST(&header->items);
719    while (mip != NULL) {
720        mip2 = SLIST_NEXT(mip, lp);
721        destroy_mdext_item(header, mip);
722        mip = mip2;
723    }
724    destroy_event(header->create);
725    destroy_event(header->destroy);
726    free(header);
727}
728
729/* this creates a new mdext item, and allocates new space for it in all the
730 * structures handled with the iteration function in the header. */
731struct mdext_item *create_mdext_item(struct mdext_header *header,
732        size_t size) {
733    struct mdext_item *mip;
734    char **data;
735    char *rhold = NULL; /* data held for iter */
736    mdext_iter_function iter = (mdext_iter_function)getsym(header->iter);
737
738    mip = calloc(1, sizeof(struct mdext_item));
739   
740    mip->size = size;
741    mip->offset = header->size;
742    header->size += mip->size;
743
744    /* go through and resize everything.  also, zero-fill our new section. */
745    while ((data = iter(&rhold)) != NULL) {
746        mdext_realloc(header, *data);
747        memset(*data + mip->offset, 0, mip->size);
748    }
749
750    SLIST_INSERT_HEAD(&header->items, mip, lp);
751    return mip;
752}
753
754/* this function destroys an mdext_item, and also handles resizing and
755 * re-aligning the offsets in each structure and item. */
756void destroy_mdext_item(struct mdext_header *header, struct mdext_item *item) {
757    struct mdext_item *mip;
758    struct mdext_item *old;
759    char **data;
760    char *rhold = NULL; /* data held for iter */
761    mdext_iter_function iter = (mdext_iter_function)getsym(header->iter);
762
763    SLIST_FOREACH(mip, &header->items, lp) {
764        if (mip == item)
765            break;
766    }
767    if (mip == NULL) {
768        log_warn("tried to free an mdext_item from an mdext_header it didn't "
769                "belong to!");
770        return;
771    }
772
773    /* this is where it gets tricky.  we have to shift memory down, then
774     * reallocate to get rid of the extra space no longer being occupied.  the
775     * way we do this is by doing a memmove at the offset for the rest of the
776     * data, then calling realloc.  of course, we also have to slide down the
777     * offsets for everything with an offset greater than the offset of the
778     * item we're deleting, too. */
779
780    /* if we need to move (that is, if offset+size isn't equivalent to the
781     * total size (making this the last item)) then do so. */
782    if (mip->offset + mip->size != header->size) {
783        while ((data = iter(&rhold)) != NULL) 
784            memmove(*data + mip->offset, *data + (mip->offset + mip->size),
785                    (header->size - mip->offset - mip->size));
786    }
787    header->size -= mip->size;
788    rhold = NULL;
789    while ((data = iter(&rhold)) != NULL)
790        mdext_realloc(header, *data);
791
792    old = mip;
793    SLIST_REMOVE(&header->items, old, mdext_item, lp);
794    SLIST_FOREACH(mip, &header->items, lp) {
795        if (mip->offset > old->offset)
796            mip->offset -= old->size; /* move down. */
797    }
798    free(old);
799}
800
801/* this function allocates the data for an object with the given mdext header.
802 * it also calls the "create" hook, so don't call mdext_alloc more than once
803 * unless you want trouble. ;) */
804char *mdext_alloc(struct mdext_header *header) {
805    char *data = NULL;
806
807    if (header->size) { 
808        data = calloc(1, header->size);
809        hook_event(header->create, data);
810    }
811
812    return data;;
813}
814
815/* this function frees the data for ab object with the given mdext header.  it
816 * also calls the "destroy" hook. */
817void mdext_free(struct mdext_header *header, char *data) {
818
819    if (data != NULL) {
820        hook_event(header->destroy, data);
821        free(data);
822    }
823}
824/* vi:set ts=8 sts=4 sw=4 tw=76 et: */
Note: See TracBrowser for help on using the repository browser.