codesonar fixes
[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 (pos);
471 }
472
473
474 /**
475  * Process a response from ARM to a request for a change in service
476  * status.
477  *
478  * @param cls the request context
479  * @param msg the response
480  */
481 static void
482 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
483 {
484   struct RequestContext *sc = cls;
485   int ret;
486
487   if (msg == NULL)
488     {
489       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
490                   _("Error receiving response to `%s' request from ARM for service `%s'\n"),
491                   (sc->type == GNUNET_MESSAGE_TYPE_ARM_START)
492                   ? "START"
493                   : "STOP",
494                   (const char*) &sc[1]);
495       GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
496       sc->h->client = GNUNET_CLIENT_connect ("arm",
497                                              sc->h->cfg);
498       GNUNET_assert (NULL != sc->h->client);
499       GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
500       if (sc->callback != NULL)
501         sc->callback (sc->cls, GNUNET_SYSERR);
502       GNUNET_free (sc);
503       return;
504     }
505 #if DEBUG_ARM
506   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
507               "Received response from ARM for service `%s': %u\n",
508               (const char*) &sc[1],
509               ntohs(msg->type));
510 #endif
511   switch (ntohs (msg->type))
512     {
513     case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
514       ret = GNUNET_YES;
515       break;
516     case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
517       ret = GNUNET_NO;
518       break;
519     case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
520       ret = GNUNET_SYSERR;
521       break;
522     default:
523       GNUNET_break (0);
524       ret = GNUNET_SYSERR;
525     }
526   if (sc->callback != NULL)
527     sc->callback (sc->cls, ret);
528   GNUNET_free (sc);
529 }
530
531
532 /**
533  * Start or stop a service.
534  *
535  * @param h handle to ARM
536  * @param service_name name of the service
537  * @param timeout how long to wait before failing for good
538  * @param cb callback to invoke when service is ready
539  * @param cb_cls closure for callback
540  * @param type type of the request
541  */
542 static void
543 change_service (struct GNUNET_ARM_Handle *h,
544                 const char *service_name,
545                 struct GNUNET_TIME_Relative timeout,
546                 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
547 {
548   struct RequestContext *sctx;
549   size_t slen;
550   struct GNUNET_MessageHeader *msg;
551
552   slen = strlen (service_name) + 1;
553   if (slen + sizeof (struct GNUNET_MessageHeader) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
554     {
555       GNUNET_break (0);
556       if (cb != NULL)
557         cb (cb_cls, GNUNET_NO);
558       return;
559     }
560 #if DEBUG_ARM
561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562               (type == GNUNET_MESSAGE_TYPE_ARM_START)
563               ? _("Requesting start of service `%s'.\n")
564               : _("Requesting termination of service `%s'.\n"),
565               service_name);
566 #endif
567   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
568   sctx->h = h;
569   sctx->callback = cb;
570   sctx->cls = cb_cls;
571   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
572   sctx->type = type;
573   memcpy (&sctx[1], service_name, slen);
574   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
575   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
576   msg->type = htons (sctx->type);
577   memcpy (&msg[1], service_name, slen);
578   if (GNUNET_OK !=
579       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
580                                                msg,
581                                                GNUNET_TIME_absolute_get_remaining (sctx->timeout),
582                                                GNUNET_YES,
583                                                &handle_response,
584                                                sctx))
585     {
586       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
587                   (type == GNUNET_MESSAGE_TYPE_ARM_START)
588                   ? _("Error while trying to transmit request to start `%s' to ARM\n")
589                   : _("Error while trying to transmit request to stop `%s' to ARM\n"),
590                   (const char*) &service_name);
591       if (cb != NULL)
592         cb (cb_cls, GNUNET_SYSERR);
593       GNUNET_free (sctx);
594       GNUNET_free (msg);
595       return;
596     }
597   GNUNET_free (msg);
598 }
599
600
601 /**
602  * Start a service.
603  *
604  * @param h handle to ARM
605  * @param service_name name of the service
606  * @param timeout how long to wait before failing for good
607  * @param cb callback to invoke when service is ready
608  * @param cb_cls closure for callback
609  */
610 void
611 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
612                           const char *service_name,
613                           struct GNUNET_TIME_Relative timeout,
614                           GNUNET_ARM_Callback cb, void *cb_cls)
615 {
616   struct RequestContext *sctx;
617   struct GNUNET_CLIENT_Connection *client;
618   size_t slen;
619 #if DEBUG_ARM
620   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621               _("Asked to start service `%s' within %llu ms\n"), service_name,
622               (unsigned long long) timeout.rel_value);
623 #endif
624   if (0 == strcasecmp ("arm", service_name))
625     {
626       slen = strlen ("arm") + 1;
627       sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
628       sctx->h = h;
629       sctx->callback = cb;
630       sctx->cls = cb_cls;
631       sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
632       memcpy (&sctx[1], service_name, slen);
633       GNUNET_CLIENT_service_test ("arm",
634                                   h->cfg, timeout, &arm_service_report, sctx);
635       return;
636     }
637   if (h->client == NULL)
638     {
639       client = GNUNET_CLIENT_connect ("arm", h->cfg);
640       if (client == NULL)
641         {
642           cb (cb_cls, GNUNET_SYSERR);
643           return;
644         }
645       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
646       h->client = client;
647     }
648   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
649 }
650
651 /**
652  * Callback from the arm stop service call, indicates that the arm service
653  * is well and truly dead, won't die, or an error occurred.
654  *
655  * @param cls closure for the callback
656  * @param reason reason for callback, GNUNET_NO if arm is shutdown
657  *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
658  */
659 void arm_shutdown_callback (void *cls,
660                             int reason)
661 {
662   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
663
664   if (arm_shutdown_ctx->cb != NULL)
665     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
666
667   GNUNET_free(arm_shutdown_ctx);
668 }
669
670
671 /**
672  * Stop a service.
673  *
674  * @param h handle to ARM
675  * @param service_name name of the service
676  * @param timeout how long to wait before failing for good
677  * @param cb callback to invoke when service is ready
678  * @param cb_cls closure for callback
679  */
680 void
681 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
682                          const char *service_name,
683                          struct GNUNET_TIME_Relative timeout,
684                          GNUNET_ARM_Callback cb, void *cb_cls)
685 {
686   struct ARM_ShutdownContext *arm_shutdown_ctx;
687   struct GNUNET_CLIENT_Connection *client;
688
689   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
690               _("Stopping service `%s' within %llu ms\n"), service_name,
691               (unsigned long long) timeout.rel_value);
692   if (h->client == NULL)
693     {
694       client = GNUNET_CLIENT_connect ("arm", h->cfg);
695       if (client == NULL)
696         {
697           cb (cb_cls, GNUNET_SYSERR);
698           return;
699         }
700       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
701       h->client = client;
702     }
703   if (0 == strcasecmp ("arm", service_name))
704     {
705       arm_shutdown_ctx = GNUNET_malloc(sizeof(struct ARM_ShutdownContext));
706       arm_shutdown_ctx->cb = cb;
707       arm_shutdown_ctx->cb_cls = cb_cls;
708       arm_service_shutdown (h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx);
709       h->client = NULL;
710       return;
711     }
712   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
713 }
714
715
716 /* end of arm_api.c */