]> git.proxmox.com Git - pve-cluster.git/blame - data/src/loop.c
add ability to update cfs locks, bump version to 3.0-17
[pve-cluster.git] / data / src / loop.c
CommitLineData
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
42struct 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
56struct cfs_loop {
57 GThread *worker;
58 gboolean stop_worker_flag;
59 qb_loop_t *qbloop;
60 struct fuse *fuse;
61 GList *services;
62};
63
64gboolean
65cfs_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
76gpointer
77cfs_service_get_context(cfs_service_t *service)
78{
79 g_return_val_if_fail(service != NULL, NULL);
80
81 return service->context;
82}
83
84void
85cfs_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
94cfs_service_t *
95cfs_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
123cfs_loop_t *
124cfs_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
139void
140cfs_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
153gboolean
154cfs_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
171static int32_t
172poll_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
193static void
194service_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
226static void
227service_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
276static gpointer
277cfs_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
315gboolean
316cfs_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
325gpointer
326cfs_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}