wip
[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   /* FIXME: should we check that HOSTNAME for 'arm' is localhost? */
383
384  if (GNUNET_OK !=
385       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
386                                              "arm", "PREFIX", &loprefix))
387     loprefix = GNUNET_strdup ("");
388   if (GNUNET_OK !=
389       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
390                                              "arm", "OPTIONS", &lopostfix))
391     lopostfix = GNUNET_strdup ("");
392   if (GNUNET_OK !=
393       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg,
394                                              "arm",
395                                              "BINARY",
396                                              &binary))
397     {
398       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
399                   _("Configuration failes to specify option `%s' in section `%s'!\n"),
400                   "BINARY",
401                   "arm");
402       if (pos->callback != NULL)
403         pos->callback (pos->cls, GNUNET_SYSERR);
404       GNUNET_free (pos);
405       GNUNET_free (loprefix);
406       GNUNET_free (lopostfix);
407       return;
408     }
409   if (GNUNET_OK !=
410       GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg,
411                                                "arm", "CONFIG", &config))
412     {
413       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
414                   _("Configuration fails to specify option `%s' in section `%s'!\n"),
415                   "CONFIG",
416                   "arm");
417       if (pos->callback != NULL)
418         pos->callback (pos->cls, GNUNET_SYSERR);
419       GNUNET_free (binary);
420       GNUNET_free (pos);
421       GNUNET_free (loprefix);
422       GNUNET_free (lopostfix);
423       return;
424     }
425   if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (pos->h->cfg,
426                                                       "TESTING",
427                                                       "WEAKRANDOM")) &&
428       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg,
429                                                            "TESTING",
430                                                            "WEAKRANDOM")) &&
431       (GNUNET_NO == GNUNET_CONFIGURATION_have_value (pos->h->cfg,
432                                                      "TESTING",
433                                                      "HOSTFILE")) /* Means we are ONLY running locally */
434                                                            )
435     {
436       /* we're clearly running a test, don't daemonize */
437       proc = do_start_process (NULL,
438                               loprefix,
439                               binary,
440                               "-c", config,
441 #if DEBUG_ARM
442                               "-L", "DEBUG",
443 #endif
444                               /* no daemonization! */
445                               lopostfix,
446                               NULL);
447     }
448   else
449     {
450       proc = do_start_process (NULL,
451                               loprefix,
452                               binary,
453                               "-c", config,
454 #if DEBUG_ARM
455                               "-L", "DEBUG",
456 #endif
457                               "-d",
458                               lopostfix,
459                               NULL);
460     }
461   GNUNET_free (binary);
462   GNUNET_free (config);
463   GNUNET_free (loprefix);
464   GNUNET_free (lopostfix);
465   if (proc == NULL)
466     {
467       if (pos->callback != NULL)
468         pos->callback (pos->cls, GNUNET_SYSERR);
469       GNUNET_free (pos);
470       return;
471     }
472   if (pos->callback != NULL)
473     pos->callback (pos->cls, GNUNET_YES);
474   GNUNET_free (proc);
475   GNUNET_free (pos);
476 }
477
478
479 /**
480  * Process a response from ARM to a request for a change in service
481  * status.
482  *
483  * @param cls the request context
484  * @param msg the response
485  */
486 static void
487 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
488 {
489   struct RequestContext *sc = cls;
490   int ret;
491
492   if (msg == NULL)
493     {
494       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
495                   _("Error receiving response to `%s' request from ARM for service `%s'\n"),
496                   (sc->type == GNUNET_MESSAGE_TYPE_ARM_START)
497                   ? "START"
498                   : "STOP",
499                   (const char*) &sc[1]);
500       GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
501       sc->h->client = GNUNET_CLIENT_connect ("arm",
502                                              sc->h->cfg);
503       GNUNET_assert (NULL != sc->h->client);
504       GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
505       if (sc->callback != NULL)
506         sc->callback (sc->cls, GNUNET_SYSERR);
507       GNUNET_free (sc);
508       return;
509     }
510 #if DEBUG_ARM
511   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512               "Received response from ARM for service `%s': %u\n",
513               (const char*) &sc[1],
514               ntohs(msg->type));
515 #endif
516   switch (ntohs (msg->type))
517     {
518     case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
519       ret = GNUNET_YES;
520       break;
521     case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
522       ret = GNUNET_NO;
523       break;
524     case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
525       ret = GNUNET_SYSERR;
526       break;
527     default:
528       GNUNET_break (0);
529       ret = GNUNET_SYSERR;
530     }
531   if (sc->callback != NULL)
532     sc->callback (sc->cls, ret);
533   GNUNET_free (sc);
534 }
535
536
537 /**
538  * Start or stop a service.
539  *
540  * @param h handle to ARM
541  * @param service_name name of the service
542  * @param timeout how long to wait before failing for good
543  * @param cb callback to invoke when service is ready
544  * @param cb_cls closure for callback
545  * @param type type of the request
546  */
547 static void
548 change_service (struct GNUNET_ARM_Handle *h,
549                 const char *service_name,
550                 struct GNUNET_TIME_Relative timeout,
551                 GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type)
552 {
553   struct RequestContext *sctx;
554   size_t slen;
555   struct GNUNET_MessageHeader *msg;
556
557   slen = strlen (service_name) + 1;
558   if (slen + sizeof (struct GNUNET_MessageHeader) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
559     {
560       GNUNET_break (0);
561       if (cb != NULL)
562         cb (cb_cls, GNUNET_NO);
563       return;
564     }
565 #if DEBUG_ARM
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567               (type == GNUNET_MESSAGE_TYPE_ARM_START)
568               ? _("Requesting start of service `%s'.\n")
569               : _("Requesting termination of service `%s'.\n"),
570               service_name);
571 #endif
572   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
573   sctx->h = h;
574   sctx->callback = cb;
575   sctx->cls = cb_cls;
576   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
577   sctx->type = type;
578   memcpy (&sctx[1], service_name, slen);
579   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
580   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
581   msg->type = htons (sctx->type);
582   memcpy (&msg[1], service_name, slen);
583   if (GNUNET_OK !=
584       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client,
585                                                msg,
586                                                GNUNET_TIME_absolute_get_remaining (sctx->timeout),
587                                                GNUNET_YES,
588                                                &handle_response,
589                                                sctx))
590     {
591       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
592                   (type == GNUNET_MESSAGE_TYPE_ARM_START)
593                   ? _("Error while trying to transmit request to start `%s' to ARM\n")
594                   : _("Error while trying to transmit request to stop `%s' to ARM\n"),
595                   (const char*) &service_name);
596       if (cb != NULL)
597         cb (cb_cls, GNUNET_SYSERR);
598       GNUNET_free (sctx);
599       GNUNET_free (msg);
600       return;
601     }
602   GNUNET_free (msg);
603 }
604
605
606 /**
607  * Start a service.
608  *
609  * @param h handle to ARM
610  * @param service_name name of the service
611  * @param timeout how long to wait before failing for good
612  * @param cb callback to invoke when service is ready
613  * @param cb_cls closure for callback
614  */
615 void
616 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
617                           const char *service_name,
618                           struct GNUNET_TIME_Relative timeout,
619                           GNUNET_ARM_Callback cb, void *cb_cls)
620 {
621   struct RequestContext *sctx;
622   struct GNUNET_CLIENT_Connection *client;
623   size_t slen;
624 #if DEBUG_ARM
625   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626               _("Asked to start service `%s' within %llu ms\n"), service_name,
627               (unsigned long long) timeout.rel_value);
628 #endif
629   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
630               _("Asked to start service `%s' within %llu ms\n"), service_name,
631               (unsigned long long) timeout.rel_value);
632   if (0 == strcasecmp ("arm", service_name))
633     {
634       slen = strlen ("arm") + 1;
635       sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
636       sctx->h = h;
637       sctx->callback = cb;
638       sctx->cls = cb_cls;
639       sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
640       memcpy (&sctx[1], service_name, slen);
641       GNUNET_CLIENT_service_test ("arm",
642                                   h->cfg, timeout, &arm_service_report, sctx);
643       return;
644     }
645   if (h->client == NULL)
646     {
647       client = GNUNET_CLIENT_connect ("arm", h->cfg);
648       if (client == NULL)
649         {
650           GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "arm_api, GNUNET_CLIENT_connect returned NULL\n");
651           cb (cb_cls, GNUNET_SYSERR);
652           return;
653         }
654       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
655       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
656       h->client = client;
657     }
658   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
659   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START);
660 }
661
662 /**
663  * Callback from the arm stop service call, indicates that the arm service
664  * is well and truly dead, won't die, or an error occurred.
665  *
666  * @param cls closure for the callback
667  * @param reason reason for callback, GNUNET_NO if arm is shutdown
668  *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
669  */
670 void arm_shutdown_callback (void *cls,
671                             int reason)
672 {
673   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
674
675   if (arm_shutdown_ctx->cb != NULL)
676     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
677
678   GNUNET_free(arm_shutdown_ctx);
679 }
680
681
682 /**
683  * Stop a service.
684  *
685  * @param h handle to ARM
686  * @param service_name name of the service
687  * @param timeout how long to wait before failing for good
688  * @param cb callback to invoke when service is ready
689  * @param cb_cls closure for callback
690  */
691 void
692 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h,
693                          const char *service_name,
694                          struct GNUNET_TIME_Relative timeout,
695                          GNUNET_ARM_Callback cb, void *cb_cls)
696 {
697   struct ARM_ShutdownContext *arm_shutdown_ctx;
698   struct GNUNET_CLIENT_Connection *client;
699
700   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
701               _("Stopping service `%s' within %llu ms\n"), service_name,
702               (unsigned long long) timeout.rel_value);
703   if (h->client == NULL)
704     {
705       client = GNUNET_CLIENT_connect ("arm", h->cfg);
706       if (client == NULL)
707         {
708           cb (cb_cls, GNUNET_SYSERR);
709           return;
710         }
711       GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
712       h->client = client;
713     }
714   if (0 == strcasecmp ("arm", service_name))
715     {
716       arm_shutdown_ctx = GNUNET_malloc(sizeof(struct ARM_ShutdownContext));
717       arm_shutdown_ctx->cb = cb;
718       arm_shutdown_ctx->cb_cls = cb_cls;
719       arm_service_shutdown (h->client, timeout, &arm_shutdown_callback, arm_shutdown_ctx);
720       h->client = NULL;
721       return;
722     }
723   change_service (h, service_name, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP);
724 }
725
726
727 /* end of arm_api.c */