#include #include #include #include #include #include #include #include #include #include "disp.h" void /* Internals of strbuild. */ __strbuild(char *buf, int size, const char *file, int line, const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (len < 0) { fprintf(stderr, "%s:%d: ", file, line); perror(""); exit(EXIT_FAILURE); } if (len >= size) { fprintf(stderr, "%s:%d: error: not enough space, " "have %d, need at least %d\n", file, line, size, len + 1); exit(EXIT_FAILURE); } va_start(ap, fmt); len = vsnprintf(buf, size, fmt, ap); va_end(ap); assert(len >= 0); } /* Safely create a formatted string and write it to BUF. BUF shall be a buffer * of size at least SIZE. BUF can be stack-allocated. If the formatting cannot * be performed, exit(3) is called. This function is not meant to be called * directly: the macro strbuild should be used. The code is adapted from the * make_message function of the vsnprintf(3) manual page, section "examples". */ #define strbuild(buf, fmt, ...) \ __strbuild(buf, sizeof(buf), __FILE__, __LINE__, fmt, __VA_ARGS__) #define strbuild_with_size(buf, size, fmt, ...) \ __strbuild(buf, size, __FILE__, __LINE__, fmt, __VA_ARGS__) int file_exists(const char *path) { struct stat statbuf __attribute__((unused)); int rc = stat(path, &statbuf); if (rc == 0) return 1; else if ((rc == -1) && (errno == ENOENT)) return 0; fprintf(stderr, "stat: \"%s\": ", path); perror(""); exit(EXIT_FAILURE); } static void * load_generic_symbol(struct disp *disp, char *base_name) { char full_name[64] = {0}; void *ptr; strcpy(full_name, disp->name); strcat(full_name, "_"); strcat(full_name, base_name); ptr = dlsym(disp->dl_handle, full_name); if (!ptr) { fprintf(stderr, "calculer: %s\n", dlerror()); destroy_disp(disp); exit(1); } return ptr; } static void load_symbols(struct disp *disp) { void *handle = dlopen(disp->so_path, RTLD_LAZY); if (!handle) { fprintf(stderr, "calculer: %s\n", dlerror()); destroy_disp(disp); exit(1); } disp->dl_handle = handle; disp->init = load_generic_symbol(disp, "init"); disp->display_calc = load_generic_symbol(disp, "display_calc"); disp->read_input = load_generic_symbol(disp, "read_input"); disp->display_res = load_generic_symbol(disp, "display_res"); disp->destroy = load_generic_symbol(disp, "destroy"); disp->pre_reload = load_generic_symbol(disp, "pre_reload"); disp->post_reload = load_generic_symbol(disp, "post_reload"); printf("INFO: successfully loaded symbols from \"%s\"\n", disp->so_path); } struct disp * get_disp(char *disp_name) { char so_path[128]; /* local lookup, useful while developing */ strbuild(so_path, "./lib%s.so", disp_name); if (!file_exists(so_path)) strbuild(so_path, "%s/lib/calculer/lib%s.so", CALCULER_PREFIX, disp_name); struct disp *disp = malloc(sizeof(*disp)); disp->so_path = strdup(so_path); disp->name = disp_name; load_symbols(disp); return disp; } void destroy_disp(struct disp *disp) { if (disp->destroy) disp->destroy(); if (disp->dl_handle) dlclose(disp->dl_handle); free(disp->so_path); free(disp); } void disp_reload(struct disp *disp) { printf("INFO: saving state\n"); void *state = disp->pre_reload(); printf("INFO: closing shared library\n"); dlclose(disp->dl_handle); printf("INFO: reloading symbols\n"); load_symbols(disp); printf("INFO: loading state\n"); disp->post_reload(state); printf("INFO: reloading done\n"); }