#include #include #include #include #include #include #include #include #include #include "ccdl.h" static void lib_init(struct lib *, const char *src_fname, const char *sym); static int lib_build(struct lib *); static void *lib_get(struct lib *); static void lib_deinit(struct lib *); static int ccdl_rebuild(struct ccdl *); static struct lib *ccdl_get_old_lib(struct ccdl *); static int ccdl_watch_run(struct ccdl *); static const char *my_mktemp() { const char *tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; static char buf[256]; ssize_t len = snprintf(buf, sizeof(buf), "%s/ccdl-XXXXXX", tmpdir); if (len < 0) return NULL; if (mkstemp(buf) < 0) return NULL; return buf; } static void lib_init(struct lib *lib, const char *src_fname, const char *sym_name) { lib->src_fname = src_fname; 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 *cc = getenv("CC"); if (!cc) cc = "cc"; char *const argv[] = { cc, (char *)src, "-o", (char *)lib, "-fPIC", "-shared", "-lm", NULL}; if (execvp(argv[0], argv)) err(1, "execvp"); exit(0); } else { int wstatus; if (waitpid(child, &wstatus, 0) < 0) { return -1; } else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { return 0; } else { return -1; } } } static int lib_build(struct lib *lib) { void *handle = lib->handle; if (!handle) { const char *src_fname = lib->src_fname; if (!src_fname) return -1; const char *lib_fname = my_mktemp(); if (!lib_fname) { warn("mktemp"); return -1; } if (compile(src_fname, lib_fname)) return -1; dlerror(); handle = dlopen(lib_fname, RTLD_NOW); char *err = dlerror(); if (unlink(lib_fname) < 0) warn("unlink"); if (err) { warnx("dlopen: %s", err); return -1; } 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; } static void *lib_get(struct lib *lib) { if (!lib->sym) lib_build(lib); return lib->sym; } static void lib_deinit(struct lib *lib) { if (lib->handle) { dlclose(lib->handle); lib->handle = NULL; lib->sym = 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(); int wd = 0; if (ifd < 0) { warn("inotify_init"); return -1; } if (!ccdl || !ccdl->lib || !ccdl->lib->src_fname) { warn("missing lib filename"); return -1; } wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); if (wd < 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 ((size_t)len < sizeof ev) { warn("small read"); continue; } if (ev.len > 0) { warn("unexpected name"); return -1; } if (ev.mask & IN_MODIFY) { } else if (ev.mask & IN_DELETE_SELF) { printf("deleted\n"); } else if (ev.mask & IN_IGNORED) { printf("ignored\n"); // file is being replaced. replace the watch wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); if (wd < 0) { warn("inotify_add_watch"); return -1; } } else { printf("unknown mask: %u\n", ev.mask); } (void)ccdl_rebuild(ccdl); } return 0; } static void *ccdl_watch_run_thread(void *ccdl) { return (void *)(intptr_t)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; } static int ccdl_rebuild(struct ccdl *ccdl) { struct 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; }