5ce5523f87dcdf20b771b94ee36e3c35b8458b30
[strongswan.git] / src / manager / lib / dispatcher.c
1 /**
2 * @file dispatcher.c
3 *
4 * @brief Implementation of dispatcher_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "dispatcher.h"
24
25 #include "request.h"
26 #include "session.h"
27
28 #include <fcgiapp.h>
29 #include <pthread.h>
30 #include <signal.h>
31 #include <unistd.h>
32
33 #include <utils/linked_list.h>
34
35 typedef struct private_dispatcher_t private_dispatcher_t;
36
37 /**
38 * private data of the task manager
39 */
40 struct private_dispatcher_t {
41
42 /**
43 * public functions
44 */
45 dispatcher_t public;
46
47 /**
48 * fcgi socket fd
49 */
50 int fd;
51
52 /**
53 * thread list
54 */
55 pthread_t *threads;
56
57 /**
58 * number of threads in "threads"
59 */
60 int thread_count;
61
62 /**
63 * session locking mutex
64 */
65 pthread_mutex_t mutex;
66
67 /**
68 * List of sessions
69 */
70 linked_list_t *sessions;
71
72 /**
73 * session timeout
74 */
75 time_t timeout;
76
77 /**
78 * List of controllers controller_constructor_t
79 */
80 linked_list_t *controllers;
81
82 /**
83 * constructor function to create session context (in constructor_entry_t)
84 */
85 context_constructor_t context_constructor;
86
87 /**
88 * user param to context constructor
89 */
90 void *param;
91 };
92
93 typedef struct {
94 /** constructor function */
95 controller_constructor_t constructor;
96 /** parameter to constructor */
97 void *param;
98 } constructor_entry_t;
99
100 typedef struct {
101 /** session instance */
102 session_t *session;
103 /** condvar to wait for session */
104 pthread_cond_t cond;
105 /** number of threads waiting for session */
106 int waiting;
107 /** last use of the session */
108 time_t used;
109 } session_entry_t;
110
111 /**
112 * create a session and instanciate controllers
113 */
114 static session_t* load_session(private_dispatcher_t *this)
115 {
116 iterator_t *iterator;
117 constructor_entry_t *entry;
118 session_t *session;
119 context_t *context = NULL;
120 controller_t *controller;
121
122 if (this->context_constructor)
123 {
124 context = this->context_constructor(this->param);
125 }
126 session = session_create(context);
127
128 iterator = this->controllers->create_iterator(this->controllers, TRUE);
129 while (iterator->iterate(iterator, (void**)&entry))
130 {
131 controller = entry->constructor(context, entry->param);
132 session->add_controller(session, controller);
133 }
134 iterator->destroy(iterator);
135
136 return session;
137 }
138
139 /**
140 * create a new session entry
141 */
142 static session_entry_t *session_entry_create(private_dispatcher_t *this)
143 {
144 session_entry_t *entry;
145
146 entry = malloc_thing(session_entry_t);
147 entry->waiting = 1;
148 pthread_cond_init(&entry->cond, NULL);
149 entry->session = load_session(this);
150 entry->used = time(NULL);
151
152 return entry;
153 }
154
155 static void session_entry_destroy(session_entry_t *entry)
156 {
157 entry->session->destroy(entry->session);
158 free(entry);
159 }
160
161 /**
162 * Implementation of dispatcher_t.add_controller.
163 */
164 static void add_controller(private_dispatcher_t *this,
165 controller_constructor_t constructor, void *param)
166 {
167 constructor_entry_t *entry = malloc_thing(constructor_entry_t);
168
169 entry->constructor = constructor;
170 entry->param = param;
171 this->controllers->insert_last(this->controllers, entry);
172 }
173
174 /**
175 * Dispatch
176 */
177 static void dispatch(private_dispatcher_t *this)
178 {
179 FCGX_Request fcgi_req;
180
181 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
182 if (FCGX_InitRequest(&fcgi_req, this->fd, 0) == 0)
183 {
184 while (TRUE)
185 {
186 request_t *request;
187 session_entry_t *current, *found = NULL;
188 iterator_t *iterator;
189 time_t now;
190 char *sid;
191 int accepted;
192
193 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
194 accepted = FCGX_Accept_r(&fcgi_req);
195 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
196
197 if (accepted != 0)
198 {
199 break;
200 }
201
202 /* prepare */
203 request = request_create(&fcgi_req, TRUE);
204 if (request == NULL)
205 {
206 continue;
207 }
208 sid = request->get_cookie(request, "SID");
209 now = time(NULL);
210
211 /* find session */
212 iterator = this->sessions->create_iterator_locked(this->sessions, &this->mutex);
213 while (iterator->iterate(iterator, (void**)&current))
214 {
215 /* check all sessions for timeout */
216 if (current->waiting == 0 &&
217 current->used < now - this->timeout)
218 {
219 iterator->remove(iterator);
220 session_entry_destroy(current);
221 continue;
222 }
223 if (!found && sid &&
224 streq(current->session->get_sid(current->session), sid))
225 {
226 found = current;
227 found->waiting++;
228 }
229 }
230 iterator->destroy(iterator);
231
232 if (found)
233 { /* wait until session is unused */
234 pthread_mutex_lock(&this->mutex);
235 while (found->waiting > 1)
236 {
237 pthread_cond_wait(&found->cond, &this->mutex);
238 }
239 pthread_mutex_unlock(&this->mutex);
240 }
241 else
242 { /* create a new session if not found */
243 found = session_entry_create(this);
244 pthread_mutex_lock(&this->mutex);
245 this->sessions->insert_first(this->sessions, found);
246 pthread_mutex_unlock(&this->mutex);
247 }
248
249 /* start processing */
250 found->session->process(found->session, request);
251 found->used = time(NULL);
252
253 /* release session */
254 pthread_mutex_lock(&this->mutex);
255 found->waiting--;
256 pthread_cond_signal(&found->cond);
257 pthread_mutex_unlock(&this->mutex);
258
259 /* cleanup */
260 request->destroy(request);
261
262 /*
263 FCGX_FPrintF(fcgi_req.out, "<ul>");
264 char **env = fcgi_req.envp;
265 while (*env)
266 {
267 FCGX_FPrintF(fcgi_req.out, "<li>%s</li>", *env);
268 env++;
269 }
270 FCGX_FPrintF(fcgi_req.out, "</ul>");
271 */
272 }
273 }
274 }
275
276 /**
277 * Implementation of dispatcher_t.run.
278 */
279 static void run(private_dispatcher_t *this, int threads)
280 {
281 this->thread_count = threads;
282 this->threads = malloc(sizeof(pthread_t) * threads);
283 while (threads)
284 {
285 if (pthread_create(&this->threads[threads - 1],
286 NULL, (void*)dispatch, this) == 0)
287 {
288 threads--;
289 }
290 }
291 }
292
293 /**
294 * Implementation of dispatcher_t.waitsignal.
295 */
296 static void waitsignal(private_dispatcher_t *this)
297 {
298 sigset_t set;
299 int sig;
300
301 sigemptyset(&set);
302 sigaddset(&set, SIGINT);
303 sigaddset(&set, SIGTERM);
304 sigaddset(&set, SIGHUP);
305 sigprocmask(SIG_BLOCK, &set, NULL);
306 sigwait(&set, &sig);
307 }
308
309 /**
310 * Implementation of dispatcher_t.destroy
311 */
312 static void destroy(private_dispatcher_t *this)
313 {
314 FCGX_ShutdownPending();
315 while (this->thread_count--)
316 {
317 pthread_cancel(this->threads[this->thread_count]);
318 pthread_join(this->threads[this->thread_count], NULL);
319 }
320 this->sessions->destroy_function(this->sessions, (void*)session_entry_destroy);
321 this->controllers->destroy_function(this->controllers, free);
322 free(this);
323 }
324
325 /*
326 * see header file
327 */
328 dispatcher_t *dispatcher_create(char *socket, int timeout,
329 context_constructor_t constructor, void *param)
330 {
331 private_dispatcher_t *this = malloc_thing(private_dispatcher_t);
332
333 this->public.add_controller = (void(*)(dispatcher_t*, controller_constructor_t, void*))add_controller;
334 this->public.run = (void(*)(dispatcher_t*, int threads))run;
335 this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal;
336 this->public.destroy = (void(*)(dispatcher_t*))destroy;
337
338 this->sessions = linked_list_create();
339 this->controllers = linked_list_create();
340 this->context_constructor = constructor;
341 pthread_mutex_init(&this->mutex, NULL);
342 this->param = param;
343 this->fd = 0;
344 this->timeout = timeout;
345
346 FCGX_Init();
347
348 if (socket)
349 {
350 unlink(socket);
351 this->fd = FCGX_OpenSocket(socket, 10);
352 }
353 return &this->public;
354 }
355