dead
[oweals/gnunet.git] / src / arm / arm_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 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 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 /**
22  * @file arm/arm_api.c
23  * @brief API for accessing the ARM service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_server_lib.h"
33 #include "arm.h"
34
35
36 /**
37  * Handle for interacting with ARM.
38  */
39 struct GNUNET_ARM_Handle
40 {
41
42   /**
43    * Our connection to the ARM service.
44    */
45   struct GNUNET_CLIENT_Connection *client;
46
47   /**
48    * The configuration that we are using.
49    */
50   struct GNUNET_CONFIGURATION_Handle *cfg;
51
52   /**
53    * Scheduler to use.
54    */
55   struct GNUNET_SCHEDULER_Handle *sched;
56
57 };
58
59
60 /**
61  * Context for handling the shutdown of a service.
62  */
63 struct ShutdownContext
64 {
65   /**
66    * Scheduler to be used to call continuation
67    */
68   struct GNUNET_SCHEDULER_Handle *sched;
69   /**
70    * Connection to the service that is being shutdown.
71    */
72   struct GNUNET_CLIENT_Connection *sock;
73
74   /**
75    * Time allowed for shutdown to happen.
76    */
77   struct GNUNET_TIME_Absolute timeout;
78
79   /**
80    * Task set up to cancel the shutdown request on timeout.
81    */
82   GNUNET_SCHEDULER_TaskIdentifier cancel_task;
83
84   /**
85    * Task to call once shutdown complete
86    */
87   GNUNET_CLIENT_ShutdownTask cont;
88
89   /**
90    * Closure for shutdown continuation
91    */
92   void *cont_cls;
93
94   /**
95    * We received a confirmation that the service will shut down.
96    */
97   int confirmed;
98
99 };
100
101 /**
102  * Handler receiving response to service shutdown requests.
103  * First call with NULL: service misbehaving, or something.
104  * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
105  *   - service will shutdown
106  * Second call with NULL:
107  *   - service has now really shut down.
108  *
109  * @param cls closure
110  * @param msg NULL, indicating socket closure.
111  */
112 static void
113 service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
114 {
115   struct ShutdownContext *shutdown_ctx = cls;
116
117   if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))
118     {
119       /* Means the other side closed the connection and never confirmed a shutdown */
120       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
121                   "Service handle shutdown before ACK!\n");
122       if (shutdown_ctx->cont != NULL)
123         shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
124       GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
125       GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
126       GNUNET_free(shutdown_ctx);
127     }
128   else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
129     {
130       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
131                  "Service shutdown complete.\n");
132       if (shutdown_ctx->cont != NULL)
133         shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO);
134
135       GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
136       GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
137       GNUNET_free(shutdown_ctx);
138     }
139   else
140     {
141       GNUNET_assert(ntohs(msg->size) == sizeof(struct GNUNET_MessageHeader));
142       switch (ntohs(msg->type))
143         {
144         case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
145           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
146                      "Received confirmation for service shutdown.\n");
147           shutdown_ctx->confirmed = GNUNET_YES;
148           GNUNET_CLIENT_receive (shutdown_ctx->sock,
149                                  &service_shutdown_handler,
150                                  shutdown_ctx,
151                                  GNUNET_TIME_UNIT_FOREVER_REL);
152           break;
153         default: /* Fall through */
154           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
155                      "Service shutdown refused!\n");
156           if (shutdown_ctx->cont != NULL)
157             shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_YES);
158
159           GNUNET_SCHEDULER_cancel(shutdown_ctx->sched, shutdown_ctx->cancel_task);
160           GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
161           GNUNET_free(shutdown_ctx);
162           break;
163         }
164     }
165 }
166
167 /**
168  * Shutting down took too long, cancel receive and return error.
169  *
170  * @param cls closure
171  * @param tc context information (why was this task triggered now)
172  */
173 void service_shutdown_cancel (void *cls,
174                               const struct GNUNET_SCHEDULER_TaskContext * tc)
175 {
176   struct ShutdownContext *shutdown_ctx = cls;
177   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "service_shutdown_cancel called!\n");
178   shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
179   GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
180   GNUNET_free(shutdown_ctx);
181 }
182
183
184 /**
185  * If possible, write a shutdown message to the target
186  * buffer and destroy the client connection.
187  *
188  * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
189  * @param size number of bytes available in buf
190  * @param buf NULL on error, otherwise target buffer
191  * @return number of bytes written to buf
192  */
193 static size_t
194 write_shutdown (void *cls, size_t size, void *buf)
195 {
196   struct GNUNET_MessageHeader *msg;
197   struct ShutdownContext *shutdown_ctx = cls;
198
199   if (size < sizeof (struct GNUNET_MessageHeader))
200     {
201       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
202                   _("Failed to transmit shutdown request to client.\n"));
203       shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
204       GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
205       GNUNET_free(shutdown_ctx);
206       return 0;                 /* client disconnected */
207     }
208
209   GNUNET_CLIENT_receive (shutdown_ctx->sock,
210                          &service_shutdown_handler, shutdown_ctx,
211                          GNUNET_TIME_UNIT_FOREVER_REL);
212   shutdown_ctx->cancel_task = GNUNET_SCHEDULER_add_delayed (shutdown_ctx->sched,
213                                                             GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout),
214                                                             &service_shutdown_cancel,
215                                                             shutdown_ctx);
216   msg = (struct GNUNET_MessageHeader *) buf;
217   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
218   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
219   return sizeof (struct GNUNET_MessageHeader);
220 }
221
222
223 /**
224  * Request that the service should shutdown.
225  * Afterwards, the connection will automatically be
226  * disconnected.  Hence the "sock" should not
227  * be used by the caller after this call
228  * (calling this function frees "sock" after a while).
229  *
230  * @param sched the scheduler to use for calling shutdown continuation
231  * @param sock the socket connected to the service
232  * @param timeout how long to wait before giving up on transmission
233  * @param cont continuation to call once the service is really down
234  * @param cont_cls closure for continuation
235  *
236  */
237 static void
238 arm_service_shutdown (struct GNUNET_SCHEDULER_Handle *sched,
239                       struct GNUNET_CLIENT_Connection *sock,
240                       struct GNUNET_TIME_Relative timeout,
241                       GNUNET_CLIENT_ShutdownTask cont,
242                       void *cont_cls)
243 {
244   struct ShutdownContext *shutdown_ctx;
245   shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
246   shutdown_ctx->sched = sched;
247   shutdown_ctx->cont = cont;
248   shutdown_ctx->cont_cls = cont_cls;
249   shutdown_ctx->sock = sock;
250   shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout);
251   GNUNET_CLIENT_notify_transmit_ready (sock,
252                                        sizeof (struct
253                                                GNUNET_MessageHeader),
254                                        timeout,
255                                        GNUNET_NO,
256                                        &write_shutdown, shutdown_ctx);
257 }
258
259
260 /**
261  * Setup a context for communicating with ARM.  Note that this
262  * can be done even if the ARM service is not yet running.
263  *
264  * @param cfg configuration to use (needed to contact ARM;
265  *        the ARM service may internally use a different
266  *        configuration to determine how to start the service).
267  * @param sched scheduler to use
268  * @param service service that *this* process is implementing/providing, can be NULL
269  * @return context to use for further ARM operations, NULL on error
270  */
271 struct GNUNET_ARM_Handle *
272 GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
273                     struct GNUNET_SCHEDULER_Handle *sched,
274                     const char *service)
275 {
276   struct GNUNET_ARM_Handle *ret;
277
278   ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
279   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
280   ret->sched = sched;
281   return ret;
282 }
283
284
285 /**
286  * Disconnect from the ARM service.
287  *
288  * @param h the handle that was being used
289  */
290 void
291 GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
292 {
293   if (h->client != NULL)
294     GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
295   GNUNET_CONFIGURATION_destroy (h->cfg);
296   GNUNET_free (h);
297 }
298
299 struct ARM_ShutdownContext
300 {
301   /**
302    * Callback to call once shutdown complete.
303    */
304   GNUNET_ARM_Callback cb;
305
306   /**
307    * Closure for callback.
308    */
309   void *cb_cls;
310 };
311
312
313
314 /**
315  * Internal state for a request with ARM.
316  */
317 struct RequestContext
318 {
319
320   /**
321    * Pointer to our handle with ARM.
322    */
323   struct GNUNET_ARM_Handle *h;
324
325   /**
326    * Function to call with a status code for the requested operation.
327    */
328   GNUNET_ARM_Callback callback;
329
330   /**
331    * Closure for "callback".
332    */
333   void *cls;
334
335   /**
336    * Timeout for the operation.
337    */
338   struct GNUNET_TIME_Absolute timeout;
339
340   /**
341    * Type of the request expressed as a message type (start or stop).
342    */
343   uint16_t type;
344
345 };
346
347 #include "do_start_process.c"
348
349
350 /**
351  * A client specifically requested starting of ARM itself.
352  * This function is called with information about whether
353  * or not ARM is running; if it is, report success.  If
354  * it is not, start the ARM process.
355  *
356  * @param cls the context for the request that we will report on (struct RequestContext*)
357  * @param tc why were we called (reason says if ARM is running)
358  */
359 static void
360 arm_service_report (void *cls,
361                     const struct GNUNET_SCHEDULER_TaskContext *tc)
362 {
363   struct RequestContext *pos = cls;
364   pid_t pid;
365   char *binary;
366   char *config;
367   char *loprefix;
368   char *lopostfix;
369
370   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
371     {
372 #if DEBUG_ARM
373       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                   "Looks like `%s' is already running.\n",
375                   "gnunet-service-arm");
376 #endif
377       /* arm is running! */
378       if (pos->callback != NULL)
379         pos->callback (pos->cls, GNUNET_YES);
380       GNUNET_free (pos);
381       return;
382     }
383 #if DEBUG_ARM
384   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385               "Looks like `%s' is not running, will start it.\n",
386               "gnunet-service-arm");
387 #endif
388   /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
389
390  if (GNUNET_OK !=
391       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
392                                              "arm", "PREFIX", &loprefix))
393     loprefix = GNUNET_strdup ("");
394   if (GNUNET_OK !=
395       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
396                                              "arm", "OPTIONS", &lopostfix))
397     lopostfix = GNUNET_strdup ("");
398   if (GNUNET_OK !=
399       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
400                                              "arm",
401                                              "BINARY",
402                                              &binary))
403     {
404       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405                   _("Configuration failes to specify option `%s' in section `%s'!\n"),
406                   "BINARY",
407                   "arm");
408       if (pos->callback != NULL)
409         pos->callback (pos->cls, GNUNET_SYSERR);
410       GNUNET_free (pos);
411       GNUNET_free (loprefix);
412       GNUNET_free (lopostfix);
413       return;
414     }
415   if (GNUNET_OK !=
416       GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
417                                                "arm", "CONFIG", &config))
418     {
419       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
420                   _("Configuration fails to specify option `%s' in section `%s'!\n"),
421                   "CONFIG",
422                   "arm");
423       if (pos->callback != NULL)
424         pos->callback (pos->cls, GNUNET_SYSERR);
425       GNUNET_free (binary);
426       GNUNET_free (pos);
427       GNUNET_free (loprefix);
428       GNUNET_free (lopostfix);
429       return;
430     }
431   pid = do_start_process (loprefix,
432                           binary,
433                           "-c", config,
434 #if DEBUG_ARM
435                           "-L", "DEBUG",
436 #endif
437                           "-d",
438                           lopostfix,
439                           NULL);
440   GNUNET_free (binary);
441   GNUNET_free (config);
442   GNUNET_free (loprefix);
443   GNUNET_free (lopostfix);
444   if (pid == -1)
445     {
446       if (pos->callback != NULL)
447         pos->callback (pos->cls, GNUNET_SYSERR);
448       GNUNET_free (pos);
449       return;
450     }
451   if (pos->callback != NULL)
452     pos->callback (pos->cls, GNUNET_YES);
453   GNUNET_free (pos);
454 }
455
456
457 /**
458  * Process a response from ARM to a request for a change in service
459  * status.
460  *
461  * @param cls the request context
462  * @param msg the response
463  */
464 static void
465 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
466 {
467   struct RequestContext *sc = cls;
468   int ret;
469
470   if (msg == NULL)
471     {
472       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
473                   _("Error receiving response to `%s' request from ARM for service `%s'\n"),
474                   (sc->type == GNUNET_MESSAGE_TYPE_ARM_START)
475                   ? "START"
476                   : "STOP",
477                   (const char*) &sc[1]);
478       GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
479       sc->h->client = GNUNET_CLIENT_connect (sc->h->sched,
480                                              "arm",
481                                              sc->h->cfg);
482       GNUNET_assert (NULL != sc->h->client);
483       GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
484       if (sc->callback != NULL)
485         sc->callback (sc->cls, GNUNET_SYSERR);
486       GNUNET_free (sc);
487       return;
488     }
489 #if DEBUG_ARM
490   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491               "Received response from ARM for service `%s': %u\n",
492               (const char*) &sc[1],
493               ntohs(msg->type));
494 #endif
495   switch (ntohs (msg->type))
496     {
497     case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
498       ret = GNUNET_YES;
499       break;
500     case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
501       ret = GNUNET_NO;
502       break;
503     case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
504       ret = GNUNET_SYSERR;
505       break;
506     default:
507       GNUNET_break (0);
508       ret = GNUNET_SYSERR;
509     }
510   if (sc->callback != NULL)
511     sc->callback (sc->cls, ret);
512   GNUNET_free (sc);
513 }
514
515
516 /**
517  * Start or stop a service.
518  *
519  * @param h handle to ARM
520  * @param service_name name of the service
521  * @param timeout how long to wait before failing for good
522  * @param cb callback to invoke when service is ready
523  * @param cb_cls closure for callback
524  * @param type type of the request
525  */
526 static void
527 change_service (struct GNUNET_ARM_Handle *h,
528                 const char *service_name,
529                 struct GNUNET_TIME_Relative timeout,
530                 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
531 {
532   struct RequestContext *sctx;
533   size_t slen;
534   struct GNUNET_MessageHeader *msg;
535
536   slen = strlen (service_name) + 1;
537   if (slen + sizeof (struct GNUNET_MessageHeader) >
538       GNUNET_SERVER_MAX_MESSAGE_SIZE)
539     {
540       GNUNET_break (0);
541       if (cb != NULL)
542         cb (cb_cls, GNUNET_NO);
543       return;
544     }
545 #if DEBUG_ARM
546   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547               (type == GNUNET_MESSAGE_TYPE_ARM_START)
548               ? _("Requesting start of service `%s'.\n")
549               : _("Requesting termination of service `%s'.\n"),
550               service_name);
551 #endif
552   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
553   sctx->h = h;
554   sctx->callback = cb;
555   sctx->cls = cb_cls;
556   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
557   sctx->type = type;
558   memcpy (&sctx[1], service_name, slen);
559   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
560   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
561   msg->type = htons (sctx->type);
562   memcpy (&msg[1], service_name, slen);
563   if (GNUNET_OK !=
564       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
565                                                msg,
566                                                GNUNET_TIME_absolute_get_remaining (sctx->timeout),
567                                                GNUNET_YES,
568                                                &handle_response,
569                                                sctx))
570     {
571       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
572                   (type == GNUNET_MESSAGE_TYPE_ARM_START)
573                   ? _("Error while trying to transmit request to start `%s' to ARM\n")
574                   : _("Error while trying to transmit request to stop `%s' to ARM\n"),
575                   (const char*) &service_name);
576       if (cb != NULL)
577         cb (cb_cls, GNUNET_SYSERR);
578       GNUNET_free (sctx);
579       GNUNET_free (msg);
580       return;
581     }
582   GNUNET_free (msg);
583 }
584
585
586 /**
587  * Start a service.
588  *
589  * @param h handle to ARM
590  * @param service_name name of the service
591  * @param timeout how long to wait before failing for good
592  * @param cb callback to invoke when service is ready
593  * @param cb_cls closure for callback
594  */
595 void
596 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
597                           const char *service_name,
598                           struct GNUNET_TIME_Relative timeout,
599                           GNUNET_ARM_Callback cb, void *cb_cls)
600 {
601   struct RequestContext *sctx;
602   struct GNUNET_CLIENT_Connection *client;
603   size_t slen;
604 #if DEBUG_ARM
605   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606               _("Asked to start service `%s' within %llu ms\n"), service_name,
607               (unsigned long long) timeout.value);
608 #endif
609   if (0 == strcasecmp ("arm", service_name))
610     {
611       slen = strlen ("arm") + 1;
612       sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
613       sctx->h = h;
614       sctx->callback = cb;
615       sctx->cls = cb_cls;
616       sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
617       memcpy (&sctx[1], service_name, slen);
618       GNUNET_CLIENT_service_test (h->sched,
619                                   "arm",
620                                   h->cfg, timeout, &arm_service_report, sctx);
621       return;
622     }
623   if (h->client == NULL)
624     {
625       client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
626       if (client == NULL)
627         {
628           cb (cb_cls, GNUNET_SYSERR);
629           return;
630         }
631       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
632       h->client = client;
633     }
634   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
635 }
636
637 /**
638  * Callback from the arm stop service call, indicates that the arm service
639  * is well and truly dead, won't die, or an error occurred.
640  *
641  * @param cls closure for the callback
642  * @param reason reason for callback, GNUNET_NO if arm is shutdown
643  *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
644  */
645 void arm_shutdown_callback (void *cls,
646                             int reason)
647 {
648   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
649
650   if (arm_shutdown_ctx->cb != NULL)
651     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
652
653   GNUNET_free(arm_shutdown_ctx);
654 }
655
656
657 /**
658  * Stop a service.
659  *
660  * @param h handle to ARM
661  * @param service_name name of the service
662  * @param timeout how long to wait before failing for good
663  * @param cb callback to invoke when service is ready
664  * @param cb_cls closure for callback
665  */
666 void
667 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
668                          const char *service_name,
669                          struct GNUNET_TIME_Relative timeout,
670                          GNUNET_ARM_Callback cb, void *cb_cls)
671 {
672   struct ARM_ShutdownContext *arm_shutdown_ctx;
673   struct GNUNET_CLIENT_Connection *client;
674
675   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
676               _("Stopping service `%s' within %llu ms\n"), service_name,
677               (unsigned long long) timeout.value);
678   if (h->client == NULL)
679     {
680       client = GNUNET_CLIENT_connect (h->sched, "arm", h->cfg);
681       if (client == NULL)
682         {
683           cb (cb_cls, GNUNET_SYSERR);
684           return;
685         }
686       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
687       h->client = client;
688     }
689   if (0 == strcasecmp ("arm", service_name))
690     {
691       arm_shutdown_ctx = GNUNET_malloc(sizeof(struct ARM_ShutdownContext));
692       arm_shutdown_ctx->cb = cb;
693       arm_shutdown_ctx->cb_cls = cb_cls;
694       arm_service_shutdown (h->sched, h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx);
695       h->client = NULL;
696       return;
697     }
698   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
699 }
700
701
702 /* end of arm_api.c */