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