diff options
author | Safey A.Halim <safey.allah@gmail.com> | 2010-05-10 11:33:03 +0000 |
---|---|---|
committer | Safey A.Halim <safey.allah@gmail.com> | 2010-05-10 11:33:03 +0000 |
commit | baecca71c8fca88ec3f9db55140861d3dddff9e9 (patch) | |
tree | 7bacdcd87f7bcd0692862e58769ce8473a7e375c /src/arm/gnunet-service-manager.c | |
parent | 788afbc98ba0bbb1282582a8bd127e609f80d43e (diff) | |
download | gnunet-baecca71c8fca88ec3f9db55140861d3dddff9e9.tar.gz gnunet-baecca71c8fca88ec3f9db55140861d3dddff9e9.zip |
gnunet service manager creates listening sockets for services having port fields in their configuration entries and runs those services whenever clients try to connect to them
Diffstat (limited to 'src/arm/gnunet-service-manager.c')
-rw-r--r-- | src/arm/gnunet-service-manager.c | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/src/arm/gnunet-service-manager.c b/src/arm/gnunet-service-manager.c new file mode 100644 index 000000000..01ede9f33 --- /dev/null +++ b/src/arm/gnunet-service-manager.c | |||
@@ -0,0 +1,787 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file arm/gnunet-service-manager.c | ||
22 | * @brief listen to incoming connections from clients to services, | ||
23 | * start services for which incoming an incoming connection occur, | ||
24 | * and relay communication between the client and the service for | ||
25 | * that first incoming connection. | ||
26 | * @author Safey Abdel Halim | ||
27 | */ | ||
28 | |||
29 | #include "platform.h" | ||
30 | #include "gnunet_service_lib.h" | ||
31 | #include "gnunet_configuration_lib.h" | ||
32 | #include "gnunet_client_lib.h" | ||
33 | #include "gnunet_container_lib.h" | ||
34 | #include "gnunet_service_arm_.h" | ||
35 | |||
36 | |||
37 | #define DEBUG_SERVICE_MANAGER GNUNET_NO | ||
38 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) | ||
39 | #define BUFFER_SIZE (63 * 1024) | ||
40 | #define REASON_CLIENT 1 | ||
41 | #define REASON_SERVICE 2 | ||
42 | |||
43 | static char **defaultServicesList; | ||
44 | static int numDefaultServices = 0; | ||
45 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
46 | static struct GNUNET_SCHEDULER_Handle *scheduler; | ||
47 | |||
48 | struct StartedService | ||
49 | { | ||
50 | const char *serviceName; | ||
51 | struct StartedService *next; | ||
52 | }; | ||
53 | |||
54 | static struct StartedService *startedServices = NULL; | ||
55 | |||
56 | |||
57 | /* Functions prototypes */ | ||
58 | static void | ||
59 | receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
60 | static void | ||
61 | receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
62 | |||
63 | |||
64 | struct ServiceListeningInfo | ||
65 | { | ||
66 | struct ServiceListeningInfo *next; | ||
67 | struct ServiceListeningInfo *prev; | ||
68 | const char *serviceName; | ||
69 | struct sockaddr *service_addr; | ||
70 | socklen_t service_addr_len; | ||
71 | struct sockaddr client_addr; | ||
72 | socklen_t client_addr_len; | ||
73 | struct GNUNET_NETWORK_Handle *listeningSocket; | ||
74 | GNUNET_SCHEDULER_TaskIdentifier acceptTask; | ||
75 | }; | ||
76 | |||
77 | static struct ServiceListeningInfo *serviceListeningInfoList_head; | ||
78 | static struct ServiceListeningInfo *serviceListeningInfoList_tail; | ||
79 | |||
80 | /** | ||
81 | * Information of the connection: client-arm-service | ||
82 | */ | ||
83 | struct ServiceInfo | ||
84 | { | ||
85 | const char *serviceName; | ||
86 | struct GNUNET_NETWORK_Handle *armClientSocket; | ||
87 | struct GNUNET_NETWORK_Handle *armServiceSocket; | ||
88 | struct sockaddr *service_addr; | ||
89 | socklen_t service_addr_len; | ||
90 | char clientBuffer[BUFFER_SIZE]; | ||
91 | ssize_t clientBufferDataLength; | ||
92 | char *clientBufferPos; | ||
93 | char serviceBuffer[BUFFER_SIZE]; | ||
94 | ssize_t serviceBufferDataLength; | ||
95 | char *serviceBufferPos; | ||
96 | GNUNET_SCHEDULER_TaskIdentifier clientReceivingTask; | ||
97 | GNUNET_SCHEDULER_TaskIdentifier serviceReceivingTask; | ||
98 | GNUNET_SCHEDULER_TaskIdentifier acceptTask; | ||
99 | }; | ||
100 | |||
101 | |||
102 | static struct ServiceInfo * | ||
103 | newServiceInfo (const char *serviceName, struct sockaddr *service_addr, | ||
104 | socklen_t service_addr_len) | ||
105 | { | ||
106 | struct ServiceInfo *serviceInfo = | ||
107 | GNUNET_malloc (sizeof (struct ServiceInfo)); | ||
108 | serviceInfo->serviceName = serviceName; | ||
109 | serviceInfo->service_addr = service_addr; | ||
110 | serviceInfo->service_addr_len = service_addr_len; | ||
111 | serviceInfo->serviceBufferPos = serviceInfo->serviceBuffer; | ||
112 | serviceInfo->clientBufferPos = serviceInfo->clientBuffer; | ||
113 | return serviceInfo; | ||
114 | } | ||
115 | |||
116 | |||
117 | static struct ServiceListeningInfo * | ||
118 | newServiceListeningInfo (const char *serviceName, | ||
119 | struct sockaddr *sa, socklen_t service_addr_len, | ||
120 | struct GNUNET_NETWORK_Handle *listeningSocket) | ||
121 | { | ||
122 | struct ServiceListeningInfo *serviceListeningInfo = | ||
123 | GNUNET_malloc (sizeof (struct ServiceListeningInfo)); | ||
124 | |||
125 | serviceListeningInfo->client_addr_len = | ||
126 | sizeof (serviceListeningInfo->client_addr); | ||
127 | serviceListeningInfo->serviceName = serviceName; | ||
128 | serviceListeningInfo->service_addr = sa; | ||
129 | serviceListeningInfo->service_addr_len = service_addr_len; | ||
130 | serviceListeningInfo->listeningSocket = listeningSocket; | ||
131 | GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head, | ||
132 | serviceListeningInfoList_tail, | ||
133 | serviceListeningInfo); | ||
134 | return serviceListeningInfo; | ||
135 | } | ||
136 | |||
137 | |||
138 | #if DEBUG_SERVICE_MANAGER | ||
139 | static void | ||
140 | printDefaultServicesList () | ||
141 | { | ||
142 | int i; | ||
143 | for (i = 0; i < numDefaultServices; i++) | ||
144 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Service: %s\n", | ||
145 | defaultServicesList[i]); | ||
146 | } | ||
147 | #endif | ||
148 | |||
149 | |||
150 | /** | ||
151 | * Put the default services represented by a space separated string into an array of strings | ||
152 | * | ||
153 | * @param services space separated string of default services | ||
154 | */ | ||
155 | static void | ||
156 | addDefaultServicesToList (char *services) | ||
157 | { | ||
158 | int i = 0; | ||
159 | char *token; | ||
160 | |||
161 | /* How many services are there */ | ||
162 | while (services[i] != '\0') | ||
163 | { | ||
164 | if (services[i] == ' ') | ||
165 | { | ||
166 | numDefaultServices++; | ||
167 | } | ||
168 | i++; | ||
169 | } | ||
170 | numDefaultServices++; | ||
171 | defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *)); | ||
172 | token = strtok ((char *) services, " "); | ||
173 | |||
174 | i = 0; | ||
175 | while (NULL != token) | ||
176 | { | ||
177 | defaultServicesList[i++] = token; | ||
178 | token = strtok (NULL, " "); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * Checks whether the serviceName is in the list of default services | ||
184 | * | ||
185 | * @param serviceName string to check its existance in the list | ||
186 | */ | ||
187 | static int | ||
188 | isInDefaultList (const char *serviceName) | ||
189 | { | ||
190 | int i; | ||
191 | for (i = 0; i < numDefaultServices; i++) | ||
192 | { | ||
193 | if (strcmp (serviceName, defaultServicesList[i]) == 0) | ||
194 | return GNUNET_YES; | ||
195 | } | ||
196 | return GNUNET_NO; | ||
197 | } | ||
198 | |||
199 | |||
200 | static int | ||
201 | isServiceAlreadyStarted (const char *serviceName) | ||
202 | { | ||
203 | struct StartedService *service; | ||
204 | service = startedServices; | ||
205 | while (NULL != service) | ||
206 | { | ||
207 | if (strcmp (service->serviceName, serviceName) == 0) | ||
208 | return GNUNET_OK; | ||
209 | service = service->next; | ||
210 | } | ||
211 | return GNUNET_NO; | ||
212 | } | ||
213 | |||
214 | |||
215 | static void | ||
216 | setStartedService (const char *serviceName) | ||
217 | { | ||
218 | if (startedServices == NULL) | ||
219 | { | ||
220 | startedServices = GNUNET_malloc (sizeof (struct StartedService)); | ||
221 | startedServices->serviceName = GNUNET_strdup (serviceName); | ||
222 | startedServices->next = NULL; | ||
223 | } | ||
224 | else | ||
225 | { | ||
226 | struct StartedService *service = | ||
227 | GNUNET_malloc (sizeof (struct StartedService)); | ||
228 | service->serviceName = GNUNET_strdup (serviceName); | ||
229 | service->next = startedServices; | ||
230 | startedServices = service; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | |||
235 | static void | ||
236 | closeClientAndServiceSockets (struct ServiceInfo *serviceInfo, int reason) | ||
237 | { | ||
238 | if (NULL != serviceInfo->armClientSocket) | ||
239 | { | ||
240 | if (0 != (REASON_SERVICE & reason)) | ||
241 | GNUNET_SCHEDULER_cancel (scheduler, serviceInfo->clientReceivingTask); | ||
242 | if (GNUNET_SYSERR == | ||
243 | GNUNET_NETWORK_socket_close (serviceInfo->armClientSocket)) | ||
244 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close"); | ||
245 | serviceInfo->armClientSocket = NULL; | ||
246 | } | ||
247 | if (NULL != serviceInfo->armServiceSocket) | ||
248 | { | ||
249 | if (0 != (REASON_CLIENT & reason)) | ||
250 | GNUNET_SCHEDULER_cancel (scheduler, | ||
251 | serviceInfo->serviceReceivingTask); | ||
252 | |||
253 | if (GNUNET_SYSERR == | ||
254 | GNUNET_NETWORK_socket_close (serviceInfo->armServiceSocket)) | ||
255 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "close"); | ||
256 | serviceInfo->armServiceSocket = NULL; | ||
257 | } | ||
258 | |||
259 | GNUNET_free (serviceInfo); | ||
260 | } | ||
261 | |||
262 | |||
263 | /** | ||
264 | * Forward messages sent from service to client | ||
265 | * | ||
266 | * @param cls callback data, for the communication between client and service | ||
267 | * @param tc context | ||
268 | */ | ||
269 | static void | ||
270 | forwardToClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
271 | { | ||
272 | struct ServiceInfo *serviceInfo = cls; | ||
273 | ssize_t numberOfBytesSent; | ||
274 | |||
275 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
276 | { | ||
277 | /* Ignore shutdown signal, reschedule yourself */ | ||
278 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
279 | serviceInfo->armClientSocket, | ||
280 | &forwardToClient, serviceInfo); | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | /* Forwarding service response to client */ | ||
285 | numberOfBytesSent = | ||
286 | GNUNET_NETWORK_socket_send (serviceInfo->armClientSocket, | ||
287 | serviceInfo->serviceBufferPos, | ||
288 | serviceInfo->serviceBufferDataLength); | ||
289 | if ((numberOfBytesSent == GNUNET_SYSERR) || (numberOfBytesSent == 0)) | ||
290 | { | ||
291 | /* Error occured or connection closed by client */ | ||
292 | closeClientAndServiceSockets (serviceInfo, | ||
293 | (REASON_CLIENT & REASON_SERVICE)); | ||
294 | return; | ||
295 | } | ||
296 | else if (numberOfBytesSent < serviceInfo->serviceBufferDataLength) | ||
297 | { | ||
298 | /* Not all service data were sent to client */ | ||
299 | serviceInfo->serviceBufferPos += numberOfBytesSent; | ||
300 | serviceInfo->serviceBufferDataLength = | ||
301 | serviceInfo->serviceBufferDataLength - numberOfBytesSent; | ||
302 | |||
303 | /* Scheduling writing again for completing the remaining data to be sent */ | ||
304 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
305 | serviceInfo->armClientSocket, | ||
306 | &forwardToClient, serviceInfo); | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | /* Data completely sent */ | ||
311 | serviceInfo->serviceBufferPos = serviceInfo->serviceBuffer; | ||
312 | } | ||
313 | |||
314 | /* Now we are ready to receive more data, rescheduling receiving from Service */ | ||
315 | serviceInfo->serviceReceivingTask = | ||
316 | GNUNET_SCHEDULER_add_read_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
317 | serviceInfo->armServiceSocket, | ||
318 | &receiveFromService, serviceInfo); | ||
319 | } | ||
320 | |||
321 | |||
322 | /** | ||
323 | * Receive service messages sent by the service and forward it to client | ||
324 | * | ||
325 | * @param cls callback data, serviceInfo struct for the communication between client and service | ||
326 | * @param tc context | ||
327 | */ | ||
328 | static void | ||
329 | receiveFromService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
330 | { | ||
331 | |||
332 | struct ServiceInfo *serviceInfo = cls; | ||
333 | |||
334 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
335 | { | ||
336 | /* Neglect shutdown signal, reschedule yourself */ | ||
337 | serviceInfo->serviceReceivingTask = | ||
338 | GNUNET_SCHEDULER_add_read_net (scheduler, | ||
339 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
340 | serviceInfo->armServiceSocket, | ||
341 | &receiveFromService, serviceInfo); | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | serviceInfo->serviceBufferDataLength = | ||
346 | GNUNET_NETWORK_socket_recv (serviceInfo->armServiceSocket, | ||
347 | serviceInfo->serviceBuffer, BUFFER_SIZE); | ||
348 | |||
349 | if (serviceInfo->serviceBufferDataLength <= 0) | ||
350 | { | ||
351 | /* The service has closed the connection or an error occured */ | ||
352 | if (serviceInfo->serviceBufferDataLength == 0) | ||
353 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
354 | _("Service `%s' closed connection! \n"), | ||
355 | serviceInfo->serviceName); | ||
356 | else | ||
357 | { | ||
358 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
359 | _("Error receiving from service:%d\n"), errno); | ||
360 | } | ||
361 | closeClientAndServiceSockets (serviceInfo, REASON_SERVICE); | ||
362 | return; | ||
363 | } | ||
364 | |||
365 | /* Forwarding Service data to Client */ | ||
366 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
367 | serviceInfo->armClientSocket, | ||
368 | &forwardToClient, serviceInfo); | ||
369 | } | ||
370 | |||
371 | |||
372 | /** | ||
373 | * Forward client message to service | ||
374 | * | ||
375 | * @param cls callback data, serviceInfo struct for the communication between client and service | ||
376 | * @param tc context | ||
377 | */ | ||
378 | static void | ||
379 | forwardToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
380 | { | ||
381 | struct ServiceInfo *serviceInfo = cls; | ||
382 | ssize_t numberOfBytesSent; | ||
383 | |||
384 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
385 | { | ||
386 | /* Neglect shutdown signal, reschedule yourself */ | ||
387 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
388 | serviceInfo->armServiceSocket, | ||
389 | &forwardToService, serviceInfo); | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | |||
394 | /* Forwarding client's message to service */ | ||
395 | numberOfBytesSent = | ||
396 | GNUNET_NETWORK_socket_send (serviceInfo->armServiceSocket, | ||
397 | serviceInfo->clientBufferPos, | ||
398 | serviceInfo->clientBufferDataLength); | ||
399 | if ((numberOfBytesSent == GNUNET_SYSERR) || (numberOfBytesSent == 0)) | ||
400 | { | ||
401 | /* Error occured or connection closed by service */ | ||
402 | closeClientAndServiceSockets (serviceInfo, | ||
403 | (REASON_CLIENT & REASON_SERVICE)); | ||
404 | return; | ||
405 | } | ||
406 | else if (numberOfBytesSent < serviceInfo->clientBufferDataLength) | ||
407 | { | ||
408 | /* Not all client data were sent to the service */ | ||
409 | serviceInfo->clientBufferPos += numberOfBytesSent; | ||
410 | serviceInfo->clientBufferDataLength = | ||
411 | serviceInfo->clientBufferDataLength - numberOfBytesSent; | ||
412 | |||
413 | /* Scheduling writing again for completing the remaining data to be sent */ | ||
414 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
415 | serviceInfo->armServiceSocket, | ||
416 | &forwardToService, serviceInfo); | ||
417 | } | ||
418 | else | ||
419 | { | ||
420 | /* Data completely sent */ | ||
421 | serviceInfo->clientBufferPos = serviceInfo->clientBuffer; | ||
422 | } | ||
423 | |||
424 | /* Now, we are ready to receive more data. Rescheduling the receiving from client */ | ||
425 | serviceInfo->clientReceivingTask = | ||
426 | GNUNET_SCHEDULER_add_read_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
427 | serviceInfo->armClientSocket, | ||
428 | &receiveFromClient, serviceInfo); | ||
429 | } | ||
430 | |||
431 | |||
432 | |||
433 | /** | ||
434 | * Message sent from client to service (faked by ARM, since it's the first connection), | ||
435 | * ARM will receive the message and forward it to the running service | ||
436 | * | ||
437 | * @param cls callback data, serviceInfo struct for the communication between client and service | ||
438 | * @param tc context | ||
439 | */ | ||
440 | static void | ||
441 | receiveFromClient (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
442 | { | ||
443 | struct ServiceInfo *serviceInfo = cls; | ||
444 | |||
445 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
446 | { | ||
447 | /* Neglect the shutdown signal, schedule yourself */ | ||
448 | serviceInfo->clientReceivingTask = | ||
449 | GNUNET_SCHEDULER_add_read_net (scheduler, | ||
450 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
451 | serviceInfo->armClientSocket, | ||
452 | &receiveFromClient, serviceInfo); | ||
453 | return; | ||
454 | } | ||
455 | |||
456 | /* Receive client's message */ | ||
457 | serviceInfo->clientBufferDataLength = | ||
458 | GNUNET_NETWORK_socket_recv (serviceInfo->armClientSocket, | ||
459 | serviceInfo->clientBuffer, BUFFER_SIZE); | ||
460 | |||
461 | if (serviceInfo->clientBufferDataLength <= 0) | ||
462 | { | ||
463 | /* The client has closed the connection or and error occured */ | ||
464 | if (serviceInfo->clientBufferDataLength == 0) | ||
465 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
466 | _("Client closed connection with service:`%s'\n"), | ||
467 | serviceInfo->serviceName); | ||
468 | else | ||
469 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
470 | _("Error receiving from client!:%d \n"), errno); | ||
471 | closeClientAndServiceSockets (serviceInfo, REASON_CLIENT); | ||
472 | return; | ||
473 | } | ||
474 | |||
475 | /* Forwarding request to service */ | ||
476 | GNUNET_SCHEDULER_add_write_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
477 | serviceInfo->armServiceSocket, | ||
478 | &forwardToService, serviceInfo); | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * ARM connects to the just created service, | ||
484 | * starts the processes for relaying messages between the client and the service | ||
485 | * | ||
486 | * @param cls callback data, serviceInfo struct for the communication between client and service | ||
487 | * @param tc context | ||
488 | */ | ||
489 | static void | ||
490 | connectToService (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
491 | { | ||
492 | struct ServiceInfo *serviceInfo = cls; | ||
493 | |||
494 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) | ||
495 | { | ||
496 | /* Service is not up. Unable to proceed */ | ||
497 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
498 | _("Unable to start service `%s': timeout\n"), | ||
499 | serviceInfo->serviceName); | ||
500 | closeClientAndServiceSockets (serviceInfo, | ||
501 | (REASON_CLIENT & REASON_SERVICE)); | ||
502 | return; | ||
503 | } | ||
504 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
505 | { | ||
506 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
507 | _("Unable to start service `%s': shutdown\n"), | ||
508 | serviceInfo->serviceName); | ||
509 | closeClientAndServiceSockets (serviceInfo, | ||
510 | (REASON_CLIENT & REASON_SERVICE)); | ||
511 | return; | ||
512 | } | ||
513 | GNUNET_break (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)); | ||
514 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' started\n"), | ||
515 | serviceInfo->serviceName); | ||
516 | |||
517 | /* Now service is up and running, connect to it */ | ||
518 | serviceInfo->armServiceSocket = | ||
519 | GNUNET_NETWORK_socket_create (serviceInfo->service_addr->sa_family, | ||
520 | SOCK_STREAM, 0); | ||
521 | if (NULL == serviceInfo->armServiceSocket) | ||
522 | { | ||
523 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
524 | _ | ||
525 | ("Unable to start service `%s': failed to create socket\n"), | ||
526 | serviceInfo->serviceName); | ||
527 | closeClientAndServiceSockets (serviceInfo, | ||
528 | (REASON_CLIENT & REASON_SERVICE)); | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if ((GNUNET_SYSERR == | ||
533 | GNUNET_NETWORK_socket_connect (serviceInfo->armServiceSocket, | ||
534 | serviceInfo->service_addr, | ||
535 | serviceInfo->service_addr_len)) | ||
536 | && (EINPROGRESS != errno)) | ||
537 | { | ||
538 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
539 | _("Unable to start service `%s': failed to connect\n"), | ||
540 | serviceInfo->serviceName); | ||
541 | closeClientAndServiceSockets (serviceInfo, | ||
542 | (REASON_CLIENT & REASON_SERVICE)); | ||
543 | return; | ||
544 | } | ||
545 | /* Handling requests from client to service */ | ||
546 | serviceInfo->clientReceivingTask = | ||
547 | GNUNET_SCHEDULER_add_read_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
548 | serviceInfo->armClientSocket, | ||
549 | &receiveFromClient, serviceInfo); | ||
550 | |||
551 | /* Handling service responses to client */ | ||
552 | serviceInfo->serviceReceivingTask = | ||
553 | GNUNET_SCHEDULER_add_read_net (scheduler, GNUNET_TIME_UNIT_FOREVER_REL, | ||
554 | serviceInfo->armServiceSocket, | ||
555 | &receiveFromService, serviceInfo); | ||
556 | } | ||
557 | |||
558 | |||
559 | static void | ||
560 | stopServiceListeningSockets (struct ServiceListeningInfo | ||
561 | *serviceListeningInfo) | ||
562 | { | ||
563 | struct ServiceListeningInfo *pos = serviceListeningInfoList_head; | ||
564 | struct ServiceListeningInfo *tmp; | ||
565 | |||
566 | while (NULL != pos) | ||
567 | { | ||
568 | if ((strcmp (pos->serviceName, serviceListeningInfo->serviceName) == 0) | ||
569 | && (pos != serviceListeningInfo)) | ||
570 | { | ||
571 | GNUNET_SCHEDULER_cancel (scheduler, pos->acceptTask); | ||
572 | GNUNET_NETWORK_socket_close (pos->listeningSocket); | ||
573 | tmp = pos; | ||
574 | pos = pos->next; | ||
575 | GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, | ||
576 | serviceListeningInfoList_tail, tmp); | ||
577 | GNUNET_free (tmp->service_addr); | ||
578 | GNUNET_free (tmp); | ||
579 | continue; | ||
580 | } | ||
581 | pos = pos->next; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | |||
586 | /** | ||
587 | * First connection has come to the listening socket associated with the service, | ||
588 | * create the service in order to relay the incoming connection to it | ||
589 | * | ||
590 | * @param cls callback data, serviceInfo struct for the communication between client and service | ||
591 | * @param tc context | ||
592 | */ | ||
593 | static void | ||
594 | acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
595 | { | ||
596 | struct ServiceListeningInfo *serviceListeningInfo = cls; | ||
597 | struct ServiceInfo *serviceInfo; | ||
598 | |||
599 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | ||
600 | return; | ||
601 | |||
602 | if ((NULL == startedServices) | ||
603 | || (GNUNET_NO == | ||
604 | isServiceAlreadyStarted (serviceListeningInfo->serviceName))) | ||
605 | { | ||
606 | /* First request to receive at all, or first request to connect to that service */ | ||
607 | /* Accept client's connection */ | ||
608 | serviceInfo = | ||
609 | newServiceInfo (serviceListeningInfo->serviceName, | ||
610 | serviceListeningInfo->service_addr, | ||
611 | serviceListeningInfo->service_addr_len); | ||
612 | serviceInfo->armClientSocket = | ||
613 | GNUNET_NETWORK_socket_accept (serviceListeningInfo->listeningSocket, | ||
614 | &(serviceListeningInfo->client_addr), | ||
615 | &(serviceListeningInfo-> | ||
616 | client_addr_len)); | ||
617 | if (NULL == serviceInfo->armClientSocket) | ||
618 | { | ||
619 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
620 | _ | ||
621 | ("Unable to accept connection for service `%s': Invalid socket\n"), | ||
622 | serviceListeningInfo->serviceName); | ||
623 | return; | ||
624 | } | ||
625 | |||
626 | |||
627 | /* | ||
628 | * Close listening socket, start service, | ||
629 | * and stop all listening sockets associated with that service | ||
630 | * and free their correspondent ServiceInfo objects | ||
631 | */ | ||
632 | GNUNET_NETWORK_socket_close (serviceListeningInfo->listeningSocket); | ||
633 | start_service (NULL, serviceListeningInfo->serviceName); | ||
634 | setStartedService (serviceListeningInfo->serviceName); | ||
635 | stopServiceListeningSockets (serviceListeningInfo); | ||
636 | |||
637 | /* Notify me when the service is up and running */ | ||
638 | GNUNET_CLIENT_service_test (scheduler, | ||
639 | serviceListeningInfo->serviceName, cfg, | ||
640 | TIMEOUT, &connectToService, serviceInfo); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | |||
645 | /** | ||
646 | * Creating a listening socket for each of the service's addresses and wait for the first incoming connection to it | ||
647 | * | ||
648 | * @param addrs list of addresses associated with the service | ||
649 | * @param addr_lens list containing length for each of the addresses in addrs | ||
650 | * @param numOfAddresses length of the addr_lens array | ||
651 | * @param serviceName the name of the service in question | ||
652 | */ | ||
653 | static void | ||
654 | createListeningSocket (struct sockaddr **addrs, socklen_t * addr_lens, | ||
655 | int numOfAddresses, char *serviceName) | ||
656 | { | ||
657 | int i; | ||
658 | struct GNUNET_NETWORK_Handle *socket; | ||
659 | struct sockaddr *sa; | ||
660 | socklen_t addr_len; | ||
661 | struct ServiceListeningInfo *serviceListeningInfo; | ||
662 | |||
663 | for (i = 0; i < numOfAddresses; i++) | ||
664 | { | ||
665 | sa = addrs[i]; | ||
666 | addr_len = addr_lens[i]; | ||
667 | switch (sa->sa_family) | ||
668 | { | ||
669 | case AF_INET: | ||
670 | socket = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); | ||
671 | break; | ||
672 | case AF_INET6: | ||
673 | socket = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); | ||
674 | break; | ||
675 | default: | ||
676 | socket = NULL; | ||
677 | break; | ||
678 | } | ||
679 | if (NULL == socket) | ||
680 | { | ||
681 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
682 | "Unable to create socket for service %s", serviceName); | ||
683 | continue; | ||
684 | } | ||
685 | |||
686 | /* Bind */ | ||
687 | if (GNUNET_NETWORK_socket_bind | ||
688 | (socket, (const struct sockaddr *) sa, addr_len) != GNUNET_OK) | ||
689 | { | ||
690 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
691 | _("Unable to bind listening socket for service `%s'\n"), | ||
692 | serviceName); | ||
693 | continue; | ||
694 | } | ||
695 | |||
696 | /* Listen */ | ||
697 | if (GNUNET_NETWORK_socket_listen (socket, 5) != GNUNET_OK) | ||
698 | { | ||
699 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
700 | _("Error listening socket for service `%s'\n"), | ||
701 | serviceName); | ||
702 | } | ||
703 | |||
704 | serviceListeningInfo = | ||
705 | newServiceListeningInfo (serviceName, sa, addr_len, socket); | ||
706 | serviceListeningInfo->listeningSocket = socket; | ||
707 | serviceListeningInfo->serviceName = serviceName; | ||
708 | serviceListeningInfo->service_addr = sa; | ||
709 | serviceListeningInfo->service_addr_len = addr_len; | ||
710 | |||
711 | /* Wait for the first incoming connection */ | ||
712 | serviceListeningInfo->acceptTask = | ||
713 | GNUNET_SCHEDULER_add_read_net (scheduler, | ||
714 | GNUNET_TIME_UNIT_FOREVER_REL, socket, | ||
715 | &acceptConnection, | ||
716 | serviceListeningInfo); | ||
717 | } | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * Callback function, checks whether the current tokens are representing a service, | ||
722 | * gets its addresses and create listening socket for it. | ||
723 | * | ||
724 | * @param cls callback data, not used | ||
725 | * @param section configuration section | ||
726 | * @param option configuration option | ||
727 | * @param the option's value | ||
728 | */ | ||
729 | static void | ||
730 | checkPortNumberCB (void *cls, | ||
731 | const char *section, const char *option, const char *value) | ||
732 | { | ||
733 | /* The service shouldn't be a default service */ | ||
734 | if ((strcmp (section, "arm") != 0) | ||
735 | && (strcmp (option, "PORT") == 0) | ||
736 | && (isInDefaultList (section) == GNUNET_NO)) | ||
737 | { | ||
738 | /* then the section is representing a service */ | ||
739 | struct sockaddr **addrs; | ||
740 | socklen_t *addr_lens; | ||
741 | int ret; | ||
742 | |||
743 | ret = | ||
744 | GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, | ||
745 | &addr_lens); | ||
746 | if (ret == GNUNET_SYSERR) | ||
747 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
748 | _("Unable to resolve host name for service `%s'\n"), | ||
749 | section); | ||
750 | else if (ret != GNUNET_NO) | ||
751 | { | ||
752 | /* Addresses found for service */ | ||
753 | createListeningSocket (addrs, addr_lens, ret, (char *) section); | ||
754 | } | ||
755 | else | ||
756 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
757 | _("No addresses for service `%s' in configuration\n"), | ||
758 | section); | ||
759 | } | ||
760 | } | ||
761 | |||
762 | /** | ||
763 | * Entry point to the Service Manager | ||
764 | * | ||
765 | * @param configurationHandle configuration to use to get services | ||
766 | * @param sched scheduler to handle clients and services communications | ||
767 | */ | ||
768 | void | ||
769 | prepareServices (const struct GNUNET_CONFIGURATION_Handle | ||
770 | *configurationHandle, struct GNUNET_SCHEDULER_Handle *sched) | ||
771 | { | ||
772 | char *defaultServicesString; | ||
773 | scheduler = sched; | ||
774 | cfg = configurationHandle; | ||
775 | |||
776 | /* Split the default services into a list */ | ||
777 | GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES", | ||
778 | &defaultServicesString); | ||
779 | addDefaultServicesToList (defaultServicesString); | ||
780 | |||
781 | #if DEBUG_SERVICE_MANAGER | ||
782 | printDefaultServicesList (); | ||
783 | #endif | ||
784 | |||
785 | /* Spot the services from the configuration and create a listening socket for each */ | ||
786 | GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL); | ||
787 | } | ||