]>
Commit | Line | Data |
---|---|---|
fe000966 DM |
1 | /* |
2 | Copyright (C) 2010 Proxmox Server Solutions GmbH | |
3 | ||
4 | This program is free software: you can redistribute it and/or modify | |
5 | it under the terms of the GNU Affero General Public License as published by | |
6 | the Free Software Foundation, either version 3 of the License, or | |
7 | (at your option) any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU Affero General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Affero General Public License | |
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | Author: Dietmar Maurer <dietmar@proxmox.com> | |
18 | ||
19 | */ | |
20 | ||
21 | #define G_LOG_DOMAIN "loop" | |
22 | ||
23 | #ifdef HAVE_CONFIG_H | |
24 | #include <config.h> | |
25 | #endif /* HAVE_CONFIG_H */ | |
26 | ||
27 | #include <stdlib.h> | |
28 | #include <unistd.h> | |
29 | #include <errno.h> | |
30 | #include <stdio.h> | |
31 | #include <string.h> | |
32 | #include <sys/types.h> | |
33 | #include <utime.h> | |
34 | #include <sys/stat.h> | |
35 | #include <glib.h> | |
36 | #include <syslog.h> | |
37 | #include <poll.h> | |
38 | ||
39 | #include "cfs-utils.h" | |
40 | #include "loop.h" | |
41 | ||
42 | struct cfs_service { | |
43 | qb_loop_t *qbloop; | |
44 | const char *log_domain; | |
45 | cfs_service_callbacks_t *callbacks; | |
46 | gboolean restartable; | |
47 | gpointer context; | |
48 | gboolean initialized; | |
49 | int errcount; | |
50 | time_t last_init; | |
51 | enum qb_loop_priority priority; | |
52 | time_t period; | |
53 | time_t last_timeout; | |
54 | }; | |
55 | ||
56 | struct cfs_loop { | |
57 | GThread *worker; | |
58 | gboolean stop_worker_flag; | |
59 | qb_loop_t *qbloop; | |
60 | struct fuse *fuse; | |
61 | GList *services; | |
62 | }; | |
63 | ||
64 | gboolean | |
65 | cfs_service_set_timer( | |
66 | cfs_service_t *service, | |
67 | time_t period) | |
68 | { | |
69 | g_return_val_if_fail(service != NULL, FALSE); | |
70 | ||
71 | service->period = period; | |
72 | ||
73 | return TRUE; | |
74 | } | |
75 | ||
76 | gpointer | |
77 | cfs_service_get_context(cfs_service_t *service) | |
78 | { | |
79 | g_return_val_if_fail(service != NULL, NULL); | |
80 | ||
81 | return service->context; | |
82 | } | |
83 | ||
84 | void | |
85 | cfs_service_set_restartable( | |
86 | cfs_service_t *service, | |
87 | gboolean restartable) | |
88 | { | |
89 | g_return_if_fail(service != NULL); | |
90 | ||
91 | service->restartable = restartable; | |
92 | } | |
93 | ||
94 | cfs_service_t * | |
95 | cfs_service_new( | |
96 | cfs_service_callbacks_t *callbacks, | |
97 | const char *log_domain, | |
98 | gpointer context) | |
99 | { | |
100 | g_return_val_if_fail(callbacks != NULL, NULL); | |
101 | g_return_val_if_fail(callbacks->cfs_service_initialize_fn != NULL, NULL); | |
102 | g_return_val_if_fail(callbacks->cfs_service_finalize_fn != NULL, NULL); | |
103 | g_return_val_if_fail(callbacks->cfs_service_dispatch_fn != NULL, NULL); | |
104 | ||
105 | cfs_service_t *service = g_new0(cfs_service_t, 1); | |
106 | if(!service) | |
107 | return NULL; | |
108 | ||
109 | if (log_domain) | |
110 | service->log_domain = log_domain; | |
111 | else | |
112 | service->log_domain = G_LOG_DOMAIN; | |
113 | ||
114 | service->callbacks = callbacks; | |
115 | ||
116 | service->restartable = TRUE; | |
117 | ||
118 | service->context = context; | |
119 | ||
120 | return service; | |
121 | } | |
122 | ||
123 | cfs_loop_t * | |
124 | cfs_loop_new(struct fuse *fuse) | |
125 | { | |
126 | cfs_loop_t *loop = g_new0(cfs_loop_t, 1); | |
127 | ||
128 | if (!(loop->qbloop = qb_loop_create())) { | |
129 | cfs_critical("cant create event loop"); | |
130 | g_free(loop); | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | loop->fuse = fuse; | |
135 | ||
136 | return loop; | |
137 | } | |
138 | ||
139 | void | |
140 | cfs_loop_destroy(cfs_loop_t *loop) | |
141 | { | |
142 | g_return_if_fail(loop != NULL); | |
143 | ||
144 | if (loop->qbloop) | |
145 | qb_loop_destroy(loop->qbloop); | |
146 | ||
147 | if(loop->services) | |
148 | g_list_free(loop->services); | |
149 | ||
150 | g_free(loop); | |
151 | } | |
152 | ||
153 | gboolean | |
154 | cfs_loop_add_service( | |
155 | cfs_loop_t *loop, | |
156 | cfs_service_t *service, | |
157 | enum qb_loop_priority priority) | |
158 | { | |
159 | g_return_val_if_fail(loop != NULL, FALSE); | |
160 | g_return_val_if_fail(service != NULL, FALSE); | |
161 | g_return_val_if_fail(service->log_domain != NULL, FALSE); | |
162 | ||
163 | service->priority = priority; | |
164 | service->qbloop = loop->qbloop; | |
165 | ||
166 | loop->services = g_list_append(loop->services, service); | |
167 | ||
168 | return TRUE; | |
169 | } | |
170 | ||
171 | static int32_t | |
172 | poll_dispatch_fn( | |
173 | int32_t fd, | |
174 | int32_t revents, | |
175 | void *data) | |
176 | { | |
177 | cfs_service_t *service = (cfs_service_t *)data; | |
178 | ||
179 | if (!service->callbacks->cfs_service_dispatch_fn(service, service->context)) { | |
180 | qb_loop_poll_del(service->qbloop, fd); | |
181 | service->initialized = FALSE; | |
182 | service->errcount = 0; | |
183 | ||
184 | if (!service->restartable) | |
185 | service->callbacks->cfs_service_finalize_fn(service, service->context); | |
186 | ||
187 | return -1; | |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void | |
194 | service_timer_job(void *data) | |
195 | { | |
196 | g_return_if_fail(data != NULL); | |
197 | ||
198 | cfs_loop_t *loop = (cfs_loop_t *)data; | |
199 | qb_loop_t *qbloop = loop->qbloop; | |
200 | ||
201 | if (loop->stop_worker_flag) { | |
202 | qb_loop_stop(qbloop); | |
203 | return; | |
204 | } | |
205 | ||
206 | GList *l = loop->services; | |
207 | while (l) { | |
208 | cfs_service_t *service = (cfs_service_t *)l->data; | |
209 | l = g_list_next(l); | |
210 | ||
211 | if (!service->initialized) | |
212 | continue; | |
213 | ||
214 | time_t ctime = time(NULL); | |
215 | if (service->period && service->callbacks->cfs_service_timer_fn && | |
216 | ((ctime - service->last_timeout) >= service->period)) { | |
217 | service->last_timeout = ctime; | |
218 | service->callbacks->cfs_service_timer_fn(service, service->context); | |
219 | } | |
220 | } | |
221 | ||
222 | qb_loop_timer_handle th; | |
223 | qb_loop_timer_add(qbloop, QB_LOOP_LOW, 1000000000, data, service_timer_job, &th); | |
224 | } | |
225 | ||
226 | static void | |
227 | service_start_job(void *data) | |
228 | { | |
229 | g_return_if_fail(data != NULL); | |
230 | ||
231 | cfs_loop_t *loop = (cfs_loop_t *)data; | |
232 | qb_loop_t *qbloop = loop->qbloop; | |
233 | ||
234 | if (loop->stop_worker_flag) { | |
235 | qb_loop_stop(qbloop); | |
236 | return; | |
237 | } | |
238 | ||
239 | GList *l = loop->services; | |
240 | time_t ctime = time(NULL); | |
241 | ||
242 | while (l) { | |
243 | cfs_service_t *service = (cfs_service_t *)l->data; | |
244 | l = g_list_next(l); | |
245 | ||
246 | if (service->restartable && !service->initialized && | |
247 | ((ctime - service->last_init) > 5)) { | |
248 | int fd = service->callbacks->cfs_service_initialize_fn(service, service->context); | |
249 | service->last_init = ctime; | |
250 | ||
251 | if (fd >= 0) { | |
252 | service->initialized = TRUE; | |
253 | service->errcount = 0; | |
254 | ||
255 | int res; | |
256 | if ((res = qb_loop_poll_add(qbloop, service->priority, fd, POLLIN, | |
257 | service, poll_dispatch_fn)) != 0) { | |
258 | cfs_critical("qb_loop_poll_add failed: %s - disabling service", | |
259 | g_strerror(-res)); | |
260 | service->initialized = FALSE; | |
261 | service->restartable = FALSE; | |
262 | service->callbacks->cfs_service_finalize_fn(service, service->context); | |
263 | } | |
264 | } else { | |
265 | if (!service->errcount) | |
266 | cfs_dom_critical(service->log_domain, "can't initialize service"); | |
267 | service->errcount++; | |
268 | } | |
269 | } | |
270 | } | |
271 | ||
272 | qb_loop_timer_handle th; | |
273 | qb_loop_timer_add(qbloop, QB_LOOP_LOW, 1000000000, data, service_start_job, &th); | |
274 | } | |
275 | ||
276 | static gpointer | |
277 | cfs_loop_worker_thread(gpointer data) | |
278 | { | |
279 | g_return_val_if_fail(data != NULL, NULL); | |
280 | ||
281 | cfs_loop_t *loop = (cfs_loop_t *)data; | |
282 | qb_loop_t *qbloop = loop->qbloop; | |
283 | ||
284 | GList *l; | |
285 | time_t ctime = time(NULL); | |
286 | l = loop->services; | |
287 | while (l) { | |
288 | cfs_service_t *service = (cfs_service_t *)l->data; | |
289 | l = g_list_next(l); | |
290 | service->last_timeout = ctime; | |
291 | } | |
292 | ||
293 | qb_loop_timer_handle th; | |
294 | qb_loop_timer_add(qbloop, QB_LOOP_LOW, 1000, loop, service_start_job, &th); | |
295 | ||
296 | qb_loop_timer_add(qbloop, QB_LOOP_LOW, 1000000000, loop, service_timer_job, &th); | |
297 | ||
298 | cfs_debug("start loop"); | |
299 | ||
300 | qb_loop_run(qbloop); | |
301 | ||
302 | cfs_debug("end loop"); | |
303 | ||
304 | l = loop->services; | |
305 | while (l) { | |
306 | cfs_service_t *service = (cfs_service_t *)l->data; | |
307 | l = g_list_next(l); | |
308 | service->callbacks->cfs_service_finalize_fn(service, service->context); | |
309 | } | |
310 | ||
311 | ||
312 | return NULL; | |
313 | } | |
314 | ||
315 | gboolean | |
316 | cfs_loop_start_worker(cfs_loop_t *loop) | |
317 | { | |
318 | g_return_val_if_fail(loop != NULL, FALSE); | |
319 | ||
320 | loop->worker = g_thread_create (cfs_loop_worker_thread, loop, TRUE, NULL); | |
321 | ||
322 | return TRUE; | |
323 | } | |
324 | ||
325 | gpointer | |
326 | cfs_loop_stop_worker(cfs_loop_t *loop) | |
327 | { | |
328 | g_return_val_if_fail(loop != NULL, NULL); | |
329 | ||
330 | cfs_debug("cfs_loop_stop_worker"); | |
331 | ||
332 | loop->stop_worker_flag = TRUE; | |
333 | ||
334 | return g_thread_join(loop->worker); | |
335 | } |