Files: 033d2aa1890af2ff80fde819e83b034484c47cbc / ccdl.c
4663 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 | printf("built\n"); |
91 | return 0; |
92 | } |
93 | |
94 | static void *lib_get(struct lib *lib) |
95 | { |
96 | if (!lib->sym) lib_build(lib); |
97 | return lib->sym; |
98 | } |
99 | |
100 | static void lib_deinit(struct lib *lib) |
101 | { |
102 | if (lib->handle) { |
103 | dlclose(lib->handle); |
104 | lib->handle = NULL; |
105 | lib->sym = NULL; |
106 | } |
107 | } |
108 | |
109 | void ccdl_init(struct ccdl *ccdl, const char *src_fname, const char *sym_name) |
110 | { |
111 | lib_init(&ccdl->libs[0], src_fname, sym_name); |
112 | lib_init(&ccdl->libs[1], src_fname, sym_name); |
113 | ccdl->lib = &ccdl->libs[0]; |
114 | } |
115 | |
116 | static struct lib *ccdl_get_old_lib(struct ccdl *ccdl) |
117 | { |
118 | return ccdl->lib == &ccdl->libs[1] ? &ccdl->libs[0] : &ccdl->libs[1]; |
119 | } |
120 | |
121 | void *ccdl_get(struct ccdl *ccdl) |
122 | { |
123 | struct lib *old_lib = ccdl_get_old_lib(ccdl); |
124 | if (old_lib) { |
125 | lib_deinit(old_lib); |
126 | } |
127 | return lib_get(ccdl->lib); |
128 | } |
129 | |
130 | static int ccdl_watch_run(struct ccdl *ccdl) |
131 | { |
132 | int ifd = inotify_init(); |
133 | int wd = 0; |
134 | if (ifd < 0) { |
135 | warn("inotify_init"); |
136 | return -1; |
137 | } |
138 | if (!ccdl || !ccdl->lib || !ccdl->lib->src_fname) { |
139 | warn("missing lib filename"); |
140 | return -1; |
141 | } |
142 | wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); |
143 | if (wd < 0) { |
144 | warn("watch"); |
145 | return -1; |
146 | } |
147 | |
148 | while (1) { |
149 | struct inotify_event ev; |
150 | ssize_t len = read(ifd, &ev, sizeof ev); |
151 | //printf("readed %zd %u %u %u\n", len, sizeof ev, ev.len, ev.mask); |
152 | if (len < 0) { |
153 | warn("read"); |
154 | return -1; |
155 | } |
156 | if ((size_t)len < sizeof ev) { |
157 | warn("small read"); |
158 | continue; |
159 | } |
160 | if (ev.len > 0) { |
161 | warn("unexpected name"); |
162 | return -1; |
163 | } |
164 | if (ev.mask & IN_MODIFY) { |
165 | } else if (ev.mask & IN_DELETE_SELF) { |
166 | printf("deleted\n"); |
167 | } else if (ev.mask & IN_IGNORED) { |
168 | // file is being replaced. replace the watch |
169 | wd = inotify_add_watch(ifd, ccdl->lib->src_fname, IN_MODIFY); |
170 | if (wd < 0) { |
171 | warn("inotify_add_watch"); |
172 | return -1; |
173 | } |
174 | } else { |
175 | printf("unknown mask: %u\n", ev.mask); |
176 | } |
177 | (void)ccdl_rebuild(ccdl); |
178 | } |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static void *ccdl_watch_run_thread(void *ccdl) |
184 | { |
185 | return (void *)(intptr_t)ccdl_watch_run(ccdl); |
186 | } |
187 | |
188 | int ccdl_watch(struct ccdl *ccdl) |
189 | { |
190 | pthread_t *id = &ccdl->watcher_thread; |
191 | if (pthread_create(id, NULL, ccdl_watch_run_thread, ccdl)) return -1; |
192 | return 0; |
193 | } |
194 | |
195 | static int ccdl_rebuild(struct ccdl *ccdl) |
196 | { |
197 | struct lib *new_lib = ccdl_get_old_lib(ccdl); |
198 | lib_deinit(new_lib); |
199 | if (lib_build(new_lib) < 0) return -1; |
200 | ccdl->lib = new_lib; |
201 | return 0; |
202 | } |
203 | |
204 | int ccdl_deinit(struct ccdl *ccdl) |
205 | { |
206 | pthread_t thread = ccdl->watcher_thread; |
207 | if (thread) { |
208 | void *retval; |
209 | pthread_join(thread, &retval); |
210 | return (intptr_t)retval; |
211 | } |
212 | return 0; |
213 | } |
214 | |
215 |
Built with git-ssb-web