#include #include #include #include #include #include #include #include #include #include "ccdl.h" static unsigned int tmp_i = 0; static const char *mktemp() { const char *tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; static char buf[256]; if (snprintf(buf, sizeof(buf), "%s/ccdl-%d-%i.so", tmpdir, getpid(), tmp_i++) < 0) return NULL; return buf; } void lib_init(struct lib *lib, const char *src_fname, const char *sym_name) { lib->src_fname = src_fname; lib->lib_fname = NULL; lib->sym_name = sym_name; lib->handle = NULL; lib->sym = NULL; } static int compile(const char *src, const char *lib) { if (!src || !lib) return -1; int child = fork(); if (child < 0) return -1; if (child == 0) { char *const argv[] = { "cc", (char *)src, "-o", (char *)lib, "-fPIC", "-shared", "-lm", "-std=c99", NULL}; return execvp(argv[0], argv); } else { int wstatus; if (waitpid(child, &wstatus, 0) < 0) { return -1; } else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { return 0; } else { return -1; } } } int lib_build(struct lib *lib) { void *handle = lib->handle; if (!handle) { const char *lib_fname = lib->lib_fname; if (!lib_fname) { const char *src_fname = lib->src_fname; if (!src_fname) return -1; lib_fname = mktemp(); if (!lib_fname) return -1; lib->lib_fname = lib_fname; if (compile(src_fname, lib_fname)) return -1; } handle = dlopen(lib_fname, RTLD_NOW); lib->handle = handle; } const char *sym_name = lib->sym_name; if (!sym_name) return -1; void *sym = dlsym(handle, sym_name); if (!sym) return -1; lib->sym = sym; return 0; } void *lib_get(struct lib *lib) { if (!lib->sym) lib_build(lib); return lib->sym; } void lib_deinit(struct lib *lib) { if (lib->handle) { dlclose(lib->handle); lib->handle = NULL; lib->sym = NULL; } if (lib->lib_fname) { unlink(lib->lib_fname); lib->lib_fname = NULL; } } void ccdl_init(struct ccdl *ccdl, const char *src_fname, const char *sym_name) { lib_init(&ccdl->libs[0], src_fname, sym_name); lib_init(&ccdl->libs[1], src_fname, sym_name); ccdl->lib = &ccdl->libs[0]; } static struct lib *ccdl_get_old_lib(struct ccdl *ccdl) { return ccdl->lib == &ccdl->libs[1] ? &ccdl->libs[0] : &ccdl->libs[1]; } void *ccdl_get(struct ccdl *ccdl) { struct lib *old_lib = ccdl_get_old_lib(ccdl); if (old_lib) { lib_deinit(old_lib); } return lib_get(ccdl->lib); } static int ccdl_watch_run(struct ccdl *ccdl) { int ifd = inotify_init(); if (ifd < 0) { warn("inotify_init"); return -1; } if (!ccdl || !ccdl->lib || !ccdl->lib->src_fname) { warn("missing lib filename"); return -1; } if (inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY) < 0) { warn("watch"); return -1; } while (1) { struct inotify_event ev; ssize_t len = read(ifd, &ev, sizeof ev); //printf("readed %zd %u %u %u\n", len, sizeof ev, ev.len, ev.mask); if (len < 0) { warn("read"); return -1; } if (len < sizeof ev) { warn("small read"); continue; } if (ev.len > 0) { warn("unexpected name"); return -1; } if (!(ev.mask & IN_MODIFY)) { printf("unknown mask: %u\n", ev.mask); continue; } (void)ccdl_rebuild(ccdl); } return 0; } static void *ccdl_watch_run_thread(void *ccdl) { return (void *)ccdl_watch_run(ccdl); } int ccdl_watch(struct ccdl *ccdl) { pthread_t *id = &ccdl->watcher_thread; if (pthread_create(id, NULL, ccdl_watch_run_thread, ccdl)) return -1; return 0; } int ccdl_rebuild(struct ccdl *ccdl) { struct lib *old_lib, *new_lib; old_lib = ccdl->lib; new_lib = ccdl_get_old_lib(ccdl); lib_deinit(new_lib); if (lib_build(new_lib) < 0) return -1; ccdl->lib = new_lib; return 0; } int ccdl_deinit(struct ccdl *ccdl) { pthread_t thread = ccdl->watcher_thread; if (thread) { void *retval; pthread_join(thread, &retval); return (int)retval; } return 0; }