aboutsummaryrefslogtreecommitdiff
path: root/src/arm
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-12-07 14:53:57 +0000
committerChristian Grothoff <christian@grothoff.org>2011-12-07 14:53:57 +0000
commit360a11e64931f3c0430f4b3dc46a8d3745bb0a10 (patch)
treec99d6d2092ee180eb6e755569e9329e3925031a9 /src/arm
parenta07507e69721a1ee257ed758ce1561a4391660c7 (diff)
downloadgnunet-360a11e64931f3c0430f4b3dc46a8d3745bb0a10.tar.gz
gnunet-360a11e64931f3c0430f4b3dc46a8d3745bb0a10.zip
-moving interceptor.c code into main arm file
Diffstat (limited to 'src/arm')
-rw-r--r--src/arm/Makefile.am3
-rw-r--r--src/arm/gnunet-service-arm.c621
-rw-r--r--src/arm/gnunet-service-arm.h55
-rw-r--r--src/arm/gnunet-service-arm_interceptor.c408
4 files changed, 492 insertions, 595 deletions
diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am
index 9ed8bbe15..7d96ba594 100644
--- a/src/arm/Makefile.am
+++ b/src/arm/Makefile.am
@@ -41,8 +41,7 @@ gnunet_arm_DEPENDENCIES = \
41 libgnunetarm.la 41 libgnunetarm.la
42 42
43gnunet_service_arm_SOURCES = \ 43gnunet_service_arm_SOURCES = \
44 gnunet-service-arm.c gnunet-service-arm.h \ 44 gnunet-service-arm.c
45 gnunet-service-arm_interceptor.c
46gnunet_service_arm_LDADD = \ 45gnunet_service_arm_LDADD = \
47 $(top_builddir)/src/util/libgnunetutil.la \ 46 $(top_builddir)/src/util/libgnunetutil.la \
48 $(GN_LIBINTL) 47 $(GN_LIBINTL)
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c
index d99020d84..74bbd76c2 100644
--- a/src/arm/gnunet-service-arm.c
+++ b/src/arm/gnunet-service-arm.c
@@ -35,7 +35,6 @@
35#include "platform.h" 35#include "platform.h"
36#include "gnunet_util_lib.h" 36#include "gnunet_util_lib.h"
37#include "gnunet_protocols.h" 37#include "gnunet_protocols.h"
38#include "gnunet-service-arm.h"
39#include "arm.h" 38#include "arm.h"
40 39
41 40
@@ -150,6 +149,67 @@ static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
150 */ 149 */
151static GNUNET_SCHEDULER_TaskIdentifier child_restart_task; 150static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
152 151
152/**
153 *
154 */
155struct ServiceListeningInfo
156{
157 /**
158 * This is a linked list.
159 */
160 struct ServiceListeningInfo *next;
161
162 /**
163 * This is a linked list.
164 */
165 struct ServiceListeningInfo *prev;
166
167 /**
168 * Name of the service being forwarded.
169 */
170 char *serviceName;
171
172 /**
173 *
174 */
175 struct sockaddr *service_addr;
176
177 /**
178 *
179 */
180 socklen_t service_addr_len;
181
182 /**
183 * Our listening socket.
184 */
185 struct GNUNET_NETWORK_Handle *listeningSocket;
186
187 /**
188 * Task doing the accepting.
189 */
190 GNUNET_SCHEDULER_TaskIdentifier acceptTask;
191};
192
193
194/**
195 * Array with the names of the services started by default.
196 */
197static char **defaultServicesList;
198
199/**
200 * Size of the defaultServicesList array.
201 */
202static unsigned int numDefaultServices;
203
204/**
205 *
206 */
207static struct ServiceListeningInfo *serviceListeningInfoList_head;
208
209/**
210 *
211 */
212static struct ServiceListeningInfo *serviceListeningInfoList_tail;
153 213
154 214
155/** 215/**
@@ -181,36 +241,181 @@ static int in_shutdown;
181static struct GNUNET_SERVER_Handle *server; 241static struct GNUNET_SERVER_Handle *server;
182 242
183 243
244#include "do_start_process.c"
245
246
184/** 247/**
185 * If the configuration file changes, restart tasks that depended on that 248 * Actually start the process for the given service.
186 * option.
187 * 249 *
188 * @param cls closure, NULL if we need to self-restart 250 * @param sl identifies service to start
189 * @param tc context 251 * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
190 */ 252 */
191static void 253static void
192config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 254start_process (struct ServiceList *sl, const SOCKTYPE *lsocks)
193{ 255{
194 struct ServiceList *pos; 256 char *loprefix;
195 struct stat sbuf; 257 char *options;
258 char *optpos;
259 char *optend;
260 const char *next;
261 int use_debug;
262 char b;
263 char *val;
196 264
197 pos = running_head; 265 /* start service */
198 while (pos != NULL) 266 if (GNUNET_OK !=
267 GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
268 &loprefix))
269 loprefix = GNUNET_strdup (prefix_command);
270 if (GNUNET_OK !=
271 GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
272 &options))
199 { 273 {
200 /* FIXME: this test for config change may be a bit too coarse grained */ 274 options = GNUNET_strdup (final_option);
201 if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) && 275 if (NULL == strstr (options, "%"))
202 (pos->proc != NULL))
203 { 276 {
204 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 277 /* replace '{}' with service name */
205 _ 278 while (NULL != (optpos = strstr (options, "{}")))
206 ("Restarting service `%s' due to configuration file change.\n")); 279 {
207 if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) 280 optpos[0] = '%';
208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); 281 optpos[1] = 's';
209 else 282 GNUNET_asprintf (&optpos, options, sl->name);
210 pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS; 283 GNUNET_free (options);
284 options = optpos;
285 }
286 /* replace '$PATH' with value associated with "PATH" */
287 while (NULL != (optpos = strstr (options, "$")))
288 {
289 optend = optpos + 1;
290 while (isupper ((unsigned char) *optend))
291 optend++;
292 b = *optend;
293 if ('\0' == b)
294 next = "";
295 else
296 next = optend + 1;
297 *optend = '\0';
298 if (GNUNET_OK !=
299 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1,
300 &val))
301 val = GNUNET_strdup ("");
302 *optpos = '\0';
303 GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
304 GNUNET_free (options);
305 GNUNET_free (val);
306 options = optpos;
307 }
211 } 308 }
212 pos = pos->next;
213 } 309 }
310 use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
311
312#if DEBUG_ARM
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Starting service `%s' using binary `%s' and configuration `%s'\n",
315 sl->name, sl->binary, sl->config);
316#endif
317 if (GNUNET_YES == use_debug)
318 sl->proc =
319 do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
320 "DEBUG", options, NULL);
321 else
322 sl->proc =
323 do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
324 options, NULL);
325 if (sl->proc == NULL)
326 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
327 sl->name);
328 else
329 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
330 GNUNET_free (loprefix);
331 GNUNET_free (options);
332}
333
334
335/**
336 * Put the default services represented by a space separated string into an array of strings
337 *
338 * @param services space separated string of default services
339 */
340static void
341addDefaultServicesToList (const char *services)
342{
343 unsigned int i;
344 const char *token;
345 char *s;
346
347 if (strlen (services) == 0)
348 return;
349 s = GNUNET_strdup (services);
350 token = strtok (s, " ");
351 while (NULL != token)
352 {
353 numDefaultServices++;
354 token = strtok (NULL, " ");
355 }
356 GNUNET_free (s);
357
358 defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
359 i = 0;
360 s = GNUNET_strdup (services);
361 token = strtok (s, " ");
362 while (NULL != token)
363 {
364 defaultServicesList[i++] = GNUNET_strdup (token);
365 token = strtok (NULL, " ");
366 }
367 GNUNET_free (s);
368 GNUNET_assert (i == numDefaultServices);
369}
370
371
372/**
373 * Checks whether the serviceName is in the list of default services
374 *
375 * @param serviceName string to check its existance in the list
376 * @return GNUNET_YES if the service is started by default
377 */
378static int
379isInDefaultList (const char *serviceName)
380{
381 unsigned int i;
382
383 for (i = 0; i < numDefaultServices; i++)
384 if (strcmp (serviceName, defaultServicesList[i]) == 0)
385 return GNUNET_YES;
386 return GNUNET_NO;
387}
388
389
390/**
391 *
392 */
393static int
394stop_listening (const char *serviceName)
395{
396 struct ServiceListeningInfo *pos;
397 struct ServiceListeningInfo *next;
398 int ret;
399
400 ret = GNUNET_NO;
401 next = serviceListeningInfoList_head;
402 while (NULL != (pos = next))
403 {
404 next = pos->next;
405 if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
406 continue;
407 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
408 GNUNET_SCHEDULER_cancel (pos->acceptTask);
409 GNUNET_break (GNUNET_OK ==
410 GNUNET_NETWORK_socket_close (pos->listeningSocket));
411 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
412 serviceListeningInfoList_tail, pos);
413 GNUNET_free (pos->serviceName);
414 GNUNET_free (pos->service_addr);
415 GNUNET_free (pos);
416 ret = GNUNET_OK;
417 }
418 return ret;
214} 419}
215 420
216 421
@@ -306,113 +511,6 @@ find_service (const char *name)
306 511
307 512
308/** 513/**
309 * Remove and free an entry in the service list.
310 *
311 * @param pos entry to free
312 */
313static void
314free_service (struct ServiceList *pos)
315{
316 GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos);
317 GNUNET_free_non_null (pos->config);
318 GNUNET_free_non_null (pos->binary);
319 GNUNET_free (pos->name);
320 GNUNET_free (pos);
321}
322
323
324#include "do_start_process.c"
325
326
327/**
328 * Actually start the process for the given service.
329 *
330 * @param sl identifies service to start
331 * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
332 */
333static void
334start_process (struct ServiceList *sl, const SOCKTYPE *lsocks)
335{
336 char *loprefix;
337 char *options;
338 char *optpos;
339 char *optend;
340 const char *next;
341 int use_debug;
342 char b;
343 char *val;
344
345 /* start service */
346 if (GNUNET_OK !=
347 GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
348 &loprefix))
349 loprefix = GNUNET_strdup (prefix_command);
350 if (GNUNET_OK !=
351 GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
352 &options))
353 {
354 options = GNUNET_strdup (final_option);
355 if (NULL == strstr (options, "%"))
356 {
357 /* replace '{}' with service name */
358 while (NULL != (optpos = strstr (options, "{}")))
359 {
360 optpos[0] = '%';
361 optpos[1] = 's';
362 GNUNET_asprintf (&optpos, options, sl->name);
363 GNUNET_free (options);
364 options = optpos;
365 }
366 /* replace '$PATH' with value associated with "PATH" */
367 while (NULL != (optpos = strstr (options, "$")))
368 {
369 optend = optpos + 1;
370 while (isupper ((unsigned char) *optend))
371 optend++;
372 b = *optend;
373 if ('\0' == b)
374 next = "";
375 else
376 next = optend + 1;
377 *optend = '\0';
378 if (GNUNET_OK !=
379 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1,
380 &val))
381 val = GNUNET_strdup ("");
382 *optpos = '\0';
383 GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
384 GNUNET_free (options);
385 GNUNET_free (val);
386 options = optpos;
387 }
388 }
389 }
390 use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
391
392#if DEBUG_ARM
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Starting service `%s' using binary `%s' and configuration `%s'\n",
395 sl->name, sl->binary, sl->config);
396#endif
397 if (GNUNET_YES == use_debug)
398 sl->proc =
399 do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
400 "DEBUG", options, NULL);
401 else
402 sl->proc =
403 do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
404 options, NULL);
405 if (sl->proc == NULL)
406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
407 sl->name);
408 else
409 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
410 GNUNET_free (loprefix);
411 GNUNET_free (options);
412}
413
414
415/**
416 * Start the specified service. 514 * Start the specified service.
417 * 515 *
418 * @param client who is asking for this 516 * @param client who is asking for this
@@ -420,7 +518,7 @@ start_process (struct ServiceList *sl, const SOCKTYPE *lsocks)
420 * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL 518 * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
421 * @return GNUNET_OK on success, GNUNET_SYSERR on error 519 * @return GNUNET_OK on success, GNUNET_SYSERR on error
422 */ 520 */
423int 521static int
424start_service (struct GNUNET_SERVER_Client *client, const char *servicename, 522start_service (struct GNUNET_SERVER_Client *client, const char *servicename,
425 const SOCKTYPE *lsocks) 523 const SOCKTYPE *lsocks)
426{ 524{
@@ -485,6 +583,269 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename,
485 583
486 584
487/** 585/**
586 * First connection has come to the listening socket associated with the service,
587 * create the service in order to relay the incoming connection to it
588 *
589 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
590 * @param tc context
591 */
592static void
593acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
594{
595 struct ServiceListeningInfo *sli = cls;
596 struct ServiceListeningInfo *pos;
597 struct ServiceListeningInfo *next;
598 SOCKTYPE *lsocks;
599 unsigned int ls;
600
601 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
602 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
603 return;
604 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
605 serviceListeningInfoList_tail, sli);
606 lsocks = NULL;
607 ls = 0;
608 next = serviceListeningInfoList_head;
609 while (NULL != (pos = next))
610 {
611 next = pos->next;
612 if (0 == strcmp (pos->serviceName, sli->serviceName))
613 {
614 GNUNET_array_append (lsocks, ls,
615 GNUNET_NETWORK_get_fd (pos->listeningSocket));
616 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
617 GNUNET_free (pos->service_addr);
618 GNUNET_free (pos->serviceName);
619 GNUNET_SCHEDULER_cancel (pos->acceptTask);
620 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
621 serviceListeningInfoList_tail, pos);
622 GNUNET_free (pos);
623 }
624 }
625 GNUNET_array_append (lsocks, ls,
626 GNUNET_NETWORK_get_fd (sli->listeningSocket));
627 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
628 GNUNET_free (sli->service_addr);
629#if WINDOWS
630 GNUNET_array_append (lsocks, ls, INVALID_SOCKET);
631#else
632 GNUNET_array_append (lsocks, ls, -1);
633#endif
634 start_service (NULL, sli->serviceName, lsocks);
635 ls = 0;
636 while (lsocks[ls] != -1)
637#if WINDOWS
638 GNUNET_break (0 == closesocket (lsocks[ls++]));
639#else
640 GNUNET_break (0 == close (lsocks[ls++]));
641#endif
642 GNUNET_array_grow (lsocks, ls, 0);
643 GNUNET_free (sli->serviceName);
644 GNUNET_free (sli);
645}
646
647
648/**
649 * Creating a listening socket for each of the service's addresses and
650 * wait for the first incoming connection to it
651 *
652 * @param sa address associated with the service
653 * @param addr_len length of sa
654 * @param serviceName the name of the service in question
655 */
656static void
657createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
658 const char *serviceName)
659{
660 const static int on = 1;
661 struct GNUNET_NETWORK_Handle *sock;
662 struct ServiceListeningInfo *serviceListeningInfo;
663
664 switch (sa->sa_family)
665 {
666 case AF_INET:
667 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
668 break;
669 case AF_INET6:
670 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
671 break;
672 case AF_UNIX:
673 if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
674 return;
675 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
676 break;
677 default:
678 GNUNET_break (0);
679 sock = NULL;
680 errno = EAFNOSUPPORT;
681 break;
682 }
683 if (NULL == sock)
684 {
685 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
686 _("Unable to create socket for service `%s': %s\n"),
687 serviceName, STRERROR (errno));
688 GNUNET_free (sa);
689 return;
690 }
691 if (GNUNET_NETWORK_socket_setsockopt
692 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
693 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
694 "setsockopt");
695#ifdef IPV6_V6ONLY
696 if ((sa->sa_family == AF_INET6) &&
697 (GNUNET_NETWORK_socket_setsockopt
698 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
699 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
700 "setsockopt");
701#endif
702
703 if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
704 != GNUNET_OK)
705 {
706 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
707 _
708 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
709 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
710 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
711 GNUNET_free (sa);
712 return;
713 }
714 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
715 {
716 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
717 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
718 GNUNET_free (sa);
719 return;
720 }
721 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
722 _("ARM now monitors connections to service `%s' at `%s'\n"),
723 serviceName, GNUNET_a2s (sa, addr_len));
724 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
725 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
726 serviceListeningInfo->service_addr = sa;
727 serviceListeningInfo->service_addr_len = addr_len;
728 serviceListeningInfo->listeningSocket = sock;
729 serviceListeningInfo->acceptTask =
730 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
731 &acceptConnection, serviceListeningInfo);
732 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
733 serviceListeningInfoList_tail,
734 serviceListeningInfo);
735}
736
737
738/**
739 * Callback function, checks whether the current tokens are representing a service,
740 * gets its addresses and create listening socket for it.
741 *
742 * @param cls callback data, not used
743 * @param section configuration section
744 * @param option configuration option
745 * @param value the option's value
746 */
747static void
748checkPortNumberCB (void *cls, const char *section, const char *option,
749 const char *value)
750{
751 struct sockaddr **addrs;
752 socklen_t *addr_lens;
753 int ret;
754 unsigned int i;
755
756 if ((strcasecmp (section, "arm") == 0) ||
757 (strcasecmp (option, "AUTOSTART") != 0) ||
758 (strcasecmp (value, "YES") != 0) ||
759 (isInDefaultList (section) == GNUNET_YES))
760 return;
761 if (0 >=
762 (ret =
763 GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
764 return;
765 /* this will free (or capture) addrs[i] */
766 for (i = 0; i < ret; i++)
767 createListeningSocket (addrs[i], addr_lens[i], section);
768 GNUNET_free (addrs);
769 GNUNET_free (addr_lens);
770}
771
772
773/**
774 * Entry point to the Service Manager
775 *
776 * @param configurationHandle configuration to use to get services
777 */
778static void
779prepare_services (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
780{
781 char *defaultServicesString;
782
783 cfg = configurationHandle;
784 /* Split the default services into a list */
785 if (GNUNET_OK ==
786 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
787 &defaultServicesString))
788 {
789 addDefaultServicesToList (defaultServicesString);
790 GNUNET_free (defaultServicesString);
791 }
792 /* Spot the services from the configuration and create a listening
793 * socket for each */
794 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
795}
796
797
798
799/**
800 * If the configuration file changes, restart tasks that depended on that
801 * option.
802 *
803 * @param cls closure, NULL if we need to self-restart
804 * @param tc context
805 */
806static void
807config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
808{
809 struct ServiceList *pos;
810 struct stat sbuf;
811
812 pos = running_head;
813 while (pos != NULL)
814 {
815 /* FIXME: this test for config change may be a bit too coarse grained */
816 if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) &&
817 (pos->proc != NULL))
818 {
819 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820 _
821 ("Restarting service `%s' due to configuration file change.\n"));
822 if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
823 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
824 else
825 pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
826 }
827 pos = pos->next;
828 }
829}
830
831
832/**
833 * Remove and free an entry in the service list.
834 *
835 * @param pos entry to free
836 */
837static void
838free_service (struct ServiceList *pos)
839{
840 GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos);
841 GNUNET_free_non_null (pos->config);
842 GNUNET_free_non_null (pos->binary);
843 GNUNET_free (pos->name);
844 GNUNET_free (pos);
845}
846
847
848/**
488 * Stop the specified service. 849 * Stop the specified service.
489 * 850 *
490 * @param client who is asking for this 851 * @param client who is asking for this
@@ -992,7 +1353,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
992 } 1353 }
993 1354
994 /* create listening sockets for future services */ 1355 /* create listening sockets for future services */
995 prepareServices (cfg); 1356 prepare_services (cfg);
996 1357
997 /* process client requests */ 1358 /* process client requests */
998 GNUNET_SERVER_add_handlers (server, handlers); 1359 GNUNET_SERVER_add_handlers (server, handlers);
diff --git a/src/arm/gnunet-service-arm.h b/src/arm/gnunet-service-arm.h
deleted file mode 100644
index 8b17ec816..000000000
--- a/src/arm/gnunet-service-arm.h
+++ /dev/null
@@ -1,55 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 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 3, 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/**
22 * @file arm/gnunet-service-arm.h
23 * @brief function prototypes for gnunet_service_arm.c, and gnunet_service_manager.c
24 * @author Safey Abdel Halim
25 */
26
27#ifndef GNUNET_SERVICE_ARM__H
28#define GNUNET_SERVICE_ARM__H
29
30/**
31 * Start the specified service.
32 *
33 * @param client who is asking for this
34 * @param servicename name of the service to start
35 * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
36 * @return GNUNET_OK on success
37 */
38int
39start_service (struct GNUNET_SERVER_Client *client, const char *servicename,
40 const SOCKTYPE *lsocks);
41
42/**
43 * Stop listening for connections to a service.
44 *
45 * @param serviceName name of service to stop listening for
46 * @return GNUNET_OK if we stopped to listen, GNUNET_NO if we were
47 * not listening
48 */
49int
50stop_listening (const char *serviceName);
51
52void
53prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle);
54
55#endif
diff --git a/src/arm/gnunet-service-arm_interceptor.c b/src/arm/gnunet-service-arm_interceptor.c
deleted file mode 100644
index 8d312e28c..000000000
--- a/src/arm/gnunet-service-arm_interceptor.c
+++ /dev/null
@@ -1,408 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010, 2011 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 3, 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-arm_interceptor.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 *
27 * @author Safey Abdel Halim
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_service_lib.h"
33#include "gnunet_configuration_lib.h"
34#include "gnunet_constants.h"
35#include "gnunet_client_lib.h"
36#include "gnunet_container_lib.h"
37#include "gnunet-service-arm.h"
38
39
40#define DEBUG_SERVICE_MANAGER GNUNET_EXTRA_LOGGING
41
42/**
43 *
44 */
45struct ServiceListeningInfo
46{
47 /**
48 * This is a linked list.
49 */
50 struct ServiceListeningInfo *next;
51
52 /**
53 * This is a linked list.
54 */
55 struct ServiceListeningInfo *prev;
56
57 /**
58 * Name of the service being forwarded.
59 */
60 char *serviceName;
61
62 /**
63 *
64 */
65 struct sockaddr *service_addr;
66
67 /**
68 *
69 */
70 socklen_t service_addr_len;
71
72 /**
73 * Our listening socket.
74 */
75 struct GNUNET_NETWORK_Handle *listeningSocket;
76
77 /**
78 * Task doing the accepting.
79 */
80 GNUNET_SCHEDULER_TaskIdentifier acceptTask;
81};
82
83
84/**
85 * Array with the names of the services started by default.
86 */
87static char **defaultServicesList;
88
89/**
90 * Size of the defaultServicesList array.
91 */
92static unsigned int numDefaultServices;
93
94/**
95 *
96 */
97static const struct GNUNET_CONFIGURATION_Handle *cfg;
98
99/**
100 *
101 */
102static struct ServiceListeningInfo *serviceListeningInfoList_head;
103
104/**
105 *
106 */
107static struct ServiceListeningInfo *serviceListeningInfoList_tail;
108
109
110/**
111 * Put the default services represented by a space separated string into an array of strings
112 *
113 * @param services space separated string of default services
114 */
115static void
116addDefaultServicesToList (const char *services)
117{
118 unsigned int i;
119 const char *token;
120 char *s;
121
122 if (strlen (services) == 0)
123 return;
124 s = GNUNET_strdup (services);
125 token = strtok (s, " ");
126 while (NULL != token)
127 {
128 numDefaultServices++;
129 token = strtok (NULL, " ");
130 }
131 GNUNET_free (s);
132
133 defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
134 i = 0;
135 s = GNUNET_strdup (services);
136 token = strtok (s, " ");
137 while (NULL != token)
138 {
139 defaultServicesList[i++] = GNUNET_strdup (token);
140 token = strtok (NULL, " ");
141 }
142 GNUNET_free (s);
143 GNUNET_assert (i == numDefaultServices);
144}
145
146/**
147 * Checks whether the serviceName is in the list of default services
148 *
149 * @param serviceName string to check its existance in the list
150 * @return GNUNET_YES if the service is started by default
151 */
152static int
153isInDefaultList (const char *serviceName)
154{
155 unsigned int i;
156
157 for (i = 0; i < numDefaultServices; i++)
158 if (strcmp (serviceName, defaultServicesList[i]) == 0)
159 return GNUNET_YES;
160 return GNUNET_NO;
161}
162
163
164/**
165 *
166 */
167int
168stop_listening (const char *serviceName)
169{
170 struct ServiceListeningInfo *pos;
171 struct ServiceListeningInfo *next;
172 int ret;
173
174 ret = GNUNET_NO;
175 next = serviceListeningInfoList_head;
176 while (NULL != (pos = next))
177 {
178 next = pos->next;
179 if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
180 continue;
181 if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
182 GNUNET_SCHEDULER_cancel (pos->acceptTask);
183 GNUNET_break (GNUNET_OK ==
184 GNUNET_NETWORK_socket_close (pos->listeningSocket));
185 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
186 serviceListeningInfoList_tail, pos);
187 GNUNET_free (pos->serviceName);
188 GNUNET_free (pos->service_addr);
189 GNUNET_free (pos);
190 ret = GNUNET_OK;
191 }
192 return ret;
193}
194
195
196/**
197 * First connection has come to the listening socket associated with the service,
198 * create the service in order to relay the incoming connection to it
199 *
200 * @param cls callback data, struct ServiceListeningInfo describing a listen socket
201 * @param tc context
202 */
203static void
204acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
205{
206 struct ServiceListeningInfo *sli = cls;
207 struct ServiceListeningInfo *pos;
208 struct ServiceListeningInfo *next;
209 SOCKTYPE *lsocks;
210 unsigned int ls;
211
212 sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
213 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
214 return;
215 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
216 serviceListeningInfoList_tail, sli);
217 lsocks = NULL;
218 ls = 0;
219 next = serviceListeningInfoList_head;
220 while (NULL != (pos = next))
221 {
222 next = pos->next;
223 if (0 == strcmp (pos->serviceName, sli->serviceName))
224 {
225 GNUNET_array_append (lsocks, ls,
226 GNUNET_NETWORK_get_fd (pos->listeningSocket));
227 GNUNET_free (pos->listeningSocket); /* deliberately no closing! */
228 GNUNET_free (pos->service_addr);
229 GNUNET_free (pos->serviceName);
230 GNUNET_SCHEDULER_cancel (pos->acceptTask);
231 GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
232 serviceListeningInfoList_tail, pos);
233 GNUNET_free (pos);
234 }
235 }
236 GNUNET_array_append (lsocks, ls,
237 GNUNET_NETWORK_get_fd (sli->listeningSocket));
238 GNUNET_free (sli->listeningSocket); /* deliberately no closing! */
239 GNUNET_free (sli->service_addr);
240#if WINDOWS
241 GNUNET_array_append (lsocks, ls, INVALID_SOCKET);
242#else
243 GNUNET_array_append (lsocks, ls, -1);
244#endif
245 start_service (NULL, sli->serviceName, lsocks);
246 ls = 0;
247 while (lsocks[ls] != -1)
248#if WINDOWS
249 GNUNET_break (0 == closesocket (lsocks[ls++]));
250#else
251 GNUNET_break (0 == close (lsocks[ls++]));
252#endif
253 GNUNET_array_grow (lsocks, ls, 0);
254 GNUNET_free (sli->serviceName);
255 GNUNET_free (sli);
256}
257
258
259/**
260 * Creating a listening socket for each of the service's addresses and
261 * wait for the first incoming connection to it
262 *
263 * @param sa address associated with the service
264 * @param addr_len length of sa
265 * @param serviceName the name of the service in question
266 */
267static void
268createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
269 const char *serviceName)
270{
271 const static int on = 1;
272 struct GNUNET_NETWORK_Handle *sock;
273 struct ServiceListeningInfo *serviceListeningInfo;
274
275 switch (sa->sa_family)
276 {
277 case AF_INET:
278 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
279 break;
280 case AF_INET6:
281 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
282 break;
283 case AF_UNIX:
284 if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
285 return;
286 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
287 break;
288 default:
289 GNUNET_break (0);
290 sock = NULL;
291 errno = EAFNOSUPPORT;
292 break;
293 }
294 if (NULL == sock)
295 {
296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297 _("Unable to create socket for service `%s': %s\n"),
298 serviceName, STRERROR (errno));
299 GNUNET_free (sa);
300 return;
301 }
302 if (GNUNET_NETWORK_socket_setsockopt
303 (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
304 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
305 "setsockopt");
306#ifdef IPV6_V6ONLY
307 if ((sa->sa_family == AF_INET6) &&
308 (GNUNET_NETWORK_socket_setsockopt
309 (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
310 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
311 "setsockopt");
312#endif
313
314 if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
315 != GNUNET_OK)
316 {
317 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
318 _
319 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
320 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
321 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
322 GNUNET_free (sa);
323 return;
324 }
325 if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
326 {
327 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
328 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
329 GNUNET_free (sa);
330 return;
331 }
332 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
333 _("ARM now monitors connections to service `%s' at `%s'\n"),
334 serviceName, GNUNET_a2s (sa, addr_len));
335 serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
336 serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
337 serviceListeningInfo->service_addr = sa;
338 serviceListeningInfo->service_addr_len = addr_len;
339 serviceListeningInfo->listeningSocket = sock;
340 serviceListeningInfo->acceptTask =
341 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
342 &acceptConnection, serviceListeningInfo);
343 GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
344 serviceListeningInfoList_tail,
345 serviceListeningInfo);
346}
347
348
349/**
350 * Callback function, checks whether the current tokens are representing a service,
351 * gets its addresses and create listening socket for it.
352 *
353 * @param cls callback data, not used
354 * @param section configuration section
355 * @param option configuration option
356 * @param value the option's value
357 */
358static void
359checkPortNumberCB (void *cls, const char *section, const char *option,
360 const char *value)
361{
362 struct sockaddr **addrs;
363 socklen_t *addr_lens;
364 int ret;
365 unsigned int i;
366
367 if ((strcasecmp (section, "arm") == 0) ||
368 (strcasecmp (option, "AUTOSTART") != 0) ||
369 (strcasecmp (value, "YES") != 0) ||
370 (isInDefaultList (section) == GNUNET_YES))
371 return;
372 if (0 >=
373 (ret =
374 GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
375 return;
376 /* this will free (or capture) addrs[i] */
377 for (i = 0; i < ret; i++)
378 createListeningSocket (addrs[i], addr_lens[i], section);
379 GNUNET_free (addrs);
380 GNUNET_free (addr_lens);
381}
382
383
384/**
385 * Entry point to the Service Manager
386 *
387 * @param configurationHandle configuration to use to get services
388 */
389void
390prepareServices (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
391{
392 char *defaultServicesString;
393
394 cfg = configurationHandle;
395 /* Split the default services into a list */
396 if (GNUNET_OK ==
397 GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
398 &defaultServicesString))
399 {
400 addDefaultServicesToList (defaultServicesString);
401 GNUNET_free (defaultServicesString);
402 }
403 /* Spot the services from the configuration and create a listening
404 * socket for each */
405 GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
406}
407
408/* end of gnunet-service-arm_interceptor.c */