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