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