Files: 7603bddceeba3b2835ae0944abac262064c76801 / ccdl.c
4643 bytesRaw
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | static void lib_init(struct lib *, const char *src_fname, const char *sym); |
14 | static int lib_build(struct lib *); |
15 | static void *lib_get(struct lib *); |
16 | static void lib_deinit(struct lib *); |
17 | |
18 | static int ccdl_rebuild(struct ccdl *); |
19 | static struct lib *ccdl_get_old_lib(struct ccdl *); |
20 | static int ccdl_watch_run(struct ccdl *); |
21 | |
22 | static const char *my_mktemp() |
23 | { |
24 | const char *tmpdir = getenv("TMPDIR"); |
25 | if (!tmpdir) tmpdir = "/tmp"; |
26 | static char buf[256]; |
27 | ssize_t len = snprintf(buf, sizeof(buf), "%s/ccdl-XXXXXX", tmpdir); |
28 | if (len < 0) return NULL; |
29 | if (mkstemp(buf) < 0) return NULL; |
30 | return buf; |
31 | } |
32 | |
33 | static void lib_init(struct lib *lib, const char *src_fname, const char *sym_name) |
34 | { |
35 | lib->src_fname = src_fname; |
36 | lib->sym_name = sym_name; |
37 | lib->handle = NULL; |
38 | lib->sym = NULL; |
39 | } |
40 | |
41 | static int compile(const char *src, const char *lib) |
42 | { |
43 | if (!src || !lib) return -1; |
44 | int child = fork(); |
45 | if (child < 0) return -1; |
46 | if (child == 0) { |
47 | char *cc = getenv("CC"); |
48 | if (!cc) cc = "cc"; |
49 | char *const argv[] = { |
50 | cc, (char *)src, "-o", (char *)lib, |
51 | "-fPIC", "-shared", "-lm", |
52 | NULL}; |
53 | if (execvp(argv[0], argv)) err(1, "execvp"); |
54 | exit(0); |
55 | } else { |
56 | int wstatus; |
57 | if (waitpid(child, &wstatus, 0) < 0) return -1; |
58 | if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) return -1; |
59 | return 0; |
60 | } |
61 | } |
62 | |
63 | static int lib_build(struct lib *lib) |
64 | { |
65 | void *handle = lib->handle; |
66 | if (!handle) { |
67 | const char *src_fname = lib->src_fname; |
68 | if (!src_fname) return -1; |
69 | const char *lib_fname = my_mktemp(); |
70 | if (!lib_fname) { |
71 | warn("mktemp"); |
72 | return -1; |
73 | } |
74 | if (compile(src_fname, lib_fname)) return -1; |
75 | dlerror(); |
76 | handle = dlopen(lib_fname, RTLD_NOW); |
77 | char *err = dlerror(); |
78 | if (unlink(lib_fname) < 0) warn("unlink"); |
79 | if (err) { |
80 | warnx("dlopen: %s", err); |
81 | return -1; |
82 | } |
83 | lib->handle = handle; |
84 | } |
85 | const char *sym_name = lib->sym_name; |
86 | if (!sym_name) return -1; |
87 | void *sym = dlsym(handle, sym_name); |
88 | if (!sym) return -1; |
89 | lib->sym = sym; |
90 | return 0; |
91 | } |
92 | |
93 | static void *lib_get(struct lib *lib) |
94 | { |
95 | if (!lib->sym) lib_build(lib); |
96 | return lib->sym; |
97 | } |
98 | |
99 | static void lib_deinit(struct lib *lib) |
100 | { |
101 | if (lib->handle) { |
102 | dlclose(lib->handle); |
103 | lib->handle = NULL; |
104 | lib->sym = NULL; |
105 | } |
106 | } |
107 | |
108 | void ccdl_init(struct ccdl *ccdl, const char *src_fname, const char *sym_name) |
109 | { |
110 | lib_init(&ccdl->libs[0], src_fname, sym_name); |
111 | lib_init(&ccdl->libs[1], src_fname, sym_name); |
112 | ccdl->lib = &ccdl->libs[0]; |
113 | } |
114 | |
115 | static struct lib *ccdl_get_old_lib(struct ccdl *ccdl) |
116 | { |
117 | return ccdl->lib == &ccdl->libs[1] ? &ccdl->libs[0] : &ccdl->libs[1]; |
118 | } |
119 | |
120 | void *ccdl_get(struct ccdl *ccdl) |
121 | { |
122 | struct lib *old_lib = ccdl_get_old_lib(ccdl); |
123 | if (old_lib) { |
124 | lib_deinit(old_lib); |
125 | } |
126 | return lib_get(ccdl->lib); |
127 | } |
128 | |
129 | static int ccdl_watch_run(struct ccdl *ccdl) |
130 | { |
131 | int ifd = inotify_init(); |
132 | int wd = 0; |
133 | if (ifd < 0) { |
134 | warn("inotify_init"); |
135 | return -1; |
136 | } |
137 | if (!ccdl || !ccdl->lib || !ccdl->lib->src_fname) { |
138 | warn("missing lib filename"); |
139 | return -1; |
140 | } |
141 | wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); |
142 | if (wd < 0) { |
143 | warn("watch"); |
144 | return -1; |
145 | } |
146 | |
147 | while (1) { |
148 | struct inotify_event ev; |
149 | ssize_t len = read(ifd, &ev, sizeof ev); |
150 | //printf("readed %zd %u %u %u\n", len, sizeof ev, ev.len, ev.mask); |
151 | if (len < 0) { |
152 | warn("read"); |
153 | return -1; |
154 | } |
155 | if ((size_t)len < sizeof ev) { |
156 | warn("small read"); |
157 | continue; |
158 | } |
159 | if (ev.len > 0) { |
160 | warn("unexpected name"); |
161 | return -1; |
162 | } |
163 | if (ev.mask & IN_MODIFY) { |
164 | } else if (ev.mask & IN_DELETE_SELF) { |
165 | printf("deleted\n"); |
166 | } else if (ev.mask & IN_IGNORED) { |
167 | // file is being replaced. replace the watch |
168 | wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); |
169 | if (wd < 0) { |
170 | warn("inotify_add_watch"); |
171 | return -1; |
172 | } |
173 | } else { |
174 | printf("unknown mask: %u\n", ev.mask); |
175 | } |
176 | (void)ccdl_rebuild(ccdl); |
177 | } |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static void *ccdl_watch_run_thread(void *ccdl) |
183 | { |
184 | return (void *)(intptr_t)ccdl_watch_run(ccdl); |
185 | } |
186 | |
187 | int ccdl_watch(struct ccdl *ccdl) |
188 | { |
189 | pthread_t *id = &ccdl->watcher_thread; |
190 | if (pthread_create(id, NULL, ccdl_watch_run_thread, ccdl)) return -1; |
191 | return 0; |
192 | } |
193 | |
194 | static int ccdl_rebuild(struct ccdl *ccdl) |
195 | { |
196 | struct lib *new_lib = ccdl_get_old_lib(ccdl); |
197 | lib_deinit(new_lib); |
198 | if (lib_build(new_lib) < 0) return -1; |
199 | ccdl->lib = new_lib; |
200 | return 0; |
201 | } |
202 | |
203 | int ccdl_deinit(struct ccdl *ccdl) |
204 | { |
205 | pthread_t thread = ccdl->watcher_thread; |
206 | if (thread) { |
207 | void *retval; |
208 | pthread_join(thread, &retval); |
209 | return (intptr_t)retval; |
210 | } |
211 | return 0; |
212 | } |
213 | |
214 |
Built with git-ssb-web