move GNUNET_TRANSPORT_ATS_ to GNUNET_ATS_
[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 #define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
36
37 /**
38  * Handle for interacting with ARM.
39  */
40 struct GNUNET_ARM_Handle
41 {
42
43   /**
44    * Our connection to the ARM service.
45    */
46   struct GNUNET_CLIENT_Connection *client;
47
48   /**
49    * The configuration that we are using.
50    */
51   struct GNUNET_CONFIGURATION_Handle *cfg;
52
53 };
54
55
56 /**
57  * Context for handling the shutdown of a service.
58  */
59 struct ShutdownContext
60 {
61   /**
62    * Connection to the service that is being shutdown.
63    */
64   struct GNUNET_CLIENT_Connection *sock;
65
66   /**
67    * Time allowed for shutdown to happen.
68    */
69   struct GNUNET_TIME_Absolute timeout;
70
71   /**
72    * Task set up to cancel the shutdown request on timeout.
73    */
74   GNUNET_SCHEDULER_TaskIdentifier cancel_task;
75
76   /**
77    * Task to call once shutdown complete
78    */
79   GNUNET_CLIENT_ShutdownTask cont;
80
81   /**
82    * Closure for shutdown continuation
83    */
84   void *cont_cls;
85
86   /**
87    * We received a confirmation that the service will shut down.
88    */
89   int confirmed;
90
91 };
92
93 /**
94  * Handler receiving response to service shutdown requests.
95  * First call with NULL: service misbehaving, or something.
96  * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
97  *   - service will shutdown
98  * Second call with NULL:
99  *   - service has now really shut down.
100  *
101  * @param cls closure
102  * @param msg NULL, indicating socket closure.
103  */
104 static void
105 service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
106 {
107   struct ShutdownContext *shutdown_ctx = cls;
108
109   if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))
110   {
111 #if DEBUG_ARM
112     /* Means the other side closed the connection and never confirmed a shutdown */
113     LOG (GNUNET_ERROR_TYPE_DEBUG, "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     LOG (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
125 #endif
126     if (shutdown_ctx->cont != NULL)
127       shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_NO);
128
129     GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
130     GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
131     GNUNET_free (shutdown_ctx);
132   }
133   else
134   {
135     GNUNET_assert (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader));
136     switch (ntohs (msg->type))
137     {
138     case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
139 #if DEBUG_ARM
140       LOG (GNUNET_ERROR_TYPE_DEBUG,
141            "Received confirmation for service shutdown.\n");
142 #endif
143       shutdown_ctx->confirmed = GNUNET_YES;
144       GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
145                              shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
146       break;
147     default:                   /* Fall through */
148 #if DEBUG_ARM
149       LOG (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown refused!\n");
150 #endif
151       if (shutdown_ctx->cont != NULL)
152         shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES);
153
154       GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
155       GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
156       GNUNET_free (shutdown_ctx);
157       break;
158     }
159   }
160 }
161
162 /**
163  * Shutting down took too long, cancel receive and return error.
164  *
165  * @param cls closure
166  * @param tc context information (why was this task triggered now)
167  */
168 void
169 service_shutdown_cancel (void *cls,
170                          const struct GNUNET_SCHEDULER_TaskContext *tc)
171 {
172   struct ShutdownContext *shutdown_ctx = cls;
173
174 #if DEBUG_ARM
175   LOG (GNUNET_ERROR_TYPE_DEBUG, "service_shutdown_cancel called!\n");
176 #endif
177   shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
178   GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
179   GNUNET_free (shutdown_ctx);
180 }
181
182
183 /**
184  * If possible, write a shutdown message to the target
185  * buffer and destroy the client connection.
186  *
187  * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
188  * @param size number of bytes available in buf
189  * @param buf NULL on error, otherwise target buffer
190  * @return number of bytes written to buf
191  */
192 static size_t
193 write_shutdown (void *cls, size_t size, void *buf)
194 {
195   struct GNUNET_MessageHeader *msg;
196   struct ShutdownContext *shutdown_ctx = cls;
197
198   if (size < sizeof (struct GNUNET_MessageHeader))
199   {
200     LOG (GNUNET_ERROR_TYPE_WARNING,
201          _("Failed to transmit shutdown request to client.\n"));
202     shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
203     GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
204     GNUNET_free (shutdown_ctx);
205     return 0;                   /* client disconnected */
206   }
207
208   GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
209                          shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
210   shutdown_ctx->cancel_task =
211       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
212                                     (shutdown_ctx->timeout),
213                                     &service_shutdown_cancel, shutdown_ctx);
214   msg = (struct GNUNET_MessageHeader *) buf;
215   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
216   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
217   return sizeof (struct GNUNET_MessageHeader);
218 }
219
220
221 /**
222  * Request that the service should shutdown.
223  * Afterwards, the connection will automatically be
224  * disconnected.  Hence the "sock" should not
225  * be used by the caller after this call
226  * (calling this function frees "sock" after a while).
227  *
228  * @param sock the socket connected to the service
229  * @param timeout how long to wait before giving up on transmission
230  * @param cont continuation to call once the service is really down
231  * @param cont_cls closure for continuation
232  *
233  */
234 static void
235 arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
236                       struct GNUNET_TIME_Relative timeout,
237                       GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
238 {
239   struct ShutdownContext *shutdown_ctx;
240
241   shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
242   shutdown_ctx->cont = cont;
243   shutdown_ctx->cont_cls = cont_cls;
244   shutdown_ctx->sock = sock;
245   shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
246   GNUNET_CLIENT_notify_transmit_ready (sock,
247                                        sizeof (struct GNUNET_MessageHeader),
248                                        timeout, GNUNET_YES, &write_shutdown,
249                                        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, const struct GNUNET_SCHEDULER_TaskContext *tc)
351 {
352   struct RequestContext *pos = cls;
353   struct GNUNET_OS_Process *proc;
354   char *binary;
355   char *config;
356   char *loprefix;
357   char *lopostfix;
358
359   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
360   {
361 #if DEBUG_ARM
362     LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n",
363          "gnunet-service-arm");
364 #endif
365     /* arm is running! */
366     if (pos->callback != NULL)
367       pos->callback (pos->cls, GNUNET_YES);
368     GNUNET_free (pos);
369     return;
370   }
371 #if DEBUG_ARM
372   LOG (GNUNET_ERROR_TYPE_DEBUG,
373        "Looks like `%s' is not running, will start it.\n",
374        "gnunet-service-arm");
375 #endif
376   if (GNUNET_OK !=
377       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX",
378                                              &loprefix))
379     loprefix = GNUNET_strdup ("");
380   if (GNUNET_OK !=
381       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS",
382                                              &lopostfix))
383     lopostfix = GNUNET_strdup ("");
384   if (GNUNET_OK !=
385       GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY",
386                                              &binary))
387   {
388     LOG (GNUNET_ERROR_TYPE_WARNING,
389          _("Configuration failes to specify option `%s' in section `%s'!\n"),
390          "BINARY", "arm");
391     if (pos->callback != NULL)
392       pos->callback (pos->cls, GNUNET_SYSERR);
393     GNUNET_free (pos);
394     GNUNET_free (loprefix);
395     GNUNET_free (lopostfix);
396     return;
397   }
398   if (GNUNET_OK !=
399       GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG",
400                                                &config))
401   {
402     LOG (GNUNET_ERROR_TYPE_WARNING,
403          _("Configuration fails to specify option `%s' in section `%s'!\n"),
404          "CONFIG", "arm");
405     if (pos->callback != NULL)
406       pos->callback (pos->cls, GNUNET_SYSERR);
407     GNUNET_free (binary);
408     GNUNET_free (pos);
409     GNUNET_free (loprefix);
410     GNUNET_free (lopostfix);
411     return;
412   }
413   if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", "WEAKRANDOM")) && (GNUNET_NO == GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "HOSTFILE"))      /* Means we are ONLY running locally */
414       )
415   {
416     /* we're clearly running a test, don't daemonize */
417     proc = do_start_process (NULL, loprefix, binary, "-c", config,
418 #if DEBUG_ARM
419                              "-L", "DEBUG",
420 #endif
421                              /* no daemonization! */
422                              lopostfix, NULL);
423   }
424   else
425   {
426     proc = do_start_process (NULL, loprefix, binary, "-c", config,
427 #if DEBUG_ARM
428                              "-L", "DEBUG",
429 #endif
430                              "-d", lopostfix, NULL);
431   }
432   GNUNET_free (binary);
433   GNUNET_free (config);
434   GNUNET_free (loprefix);
435   GNUNET_free (lopostfix);
436   if (proc == NULL)
437   {
438     if (pos->callback != NULL)
439       pos->callback (pos->cls, GNUNET_SYSERR);
440     GNUNET_free (pos);
441     return;
442   }
443   if (pos->callback != NULL)
444     pos->callback (pos->cls, GNUNET_YES);
445   GNUNET_free (proc);
446   GNUNET_free (pos);
447 }
448
449
450 /**
451  * Process a response from ARM to a request for a change in service
452  * status.
453  *
454  * @param cls the request context
455  * @param msg the response
456  */
457 static void
458 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
459 {
460   struct RequestContext *sc = cls;
461   int ret;
462
463   if (msg == NULL)
464   {
465     LOG (GNUNET_ERROR_TYPE_WARNING,
466          _
467          ("Error receiving response to `%s' request from ARM for service `%s'\n"),
468          (sc->type == GNUNET_MESSAGE_TYPE_ARM_START) ? "START" : "STOP",
469          (const char *) &sc[1]);
470     GNUNET_CLIENT_disconnect (sc->h->client, GNUNET_NO);
471     sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
472     GNUNET_assert (NULL != sc->h->client);
473     GNUNET_CLIENT_ignore_shutdown (sc->h->client, GNUNET_YES);
474     if (sc->callback != NULL)
475       sc->callback (sc->cls, GNUNET_SYSERR);
476     GNUNET_free (sc);
477     return;
478   }
479 #if DEBUG_ARM
480   LOG (GNUNET_ERROR_TYPE_DEBUG,
481        "Received response from ARM for service `%s': %u\n",
482        (const char *) &sc[1], ntohs (msg->type));
483 #endif
484   switch (ntohs (msg->type))
485   {
486   case GNUNET_MESSAGE_TYPE_ARM_IS_UP:
487     ret = GNUNET_YES;
488     break;
489   case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN:
490     ret = GNUNET_NO;
491     break;
492   case GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN:
493     ret = GNUNET_SYSERR;
494     break;
495   default:
496     GNUNET_break (0);
497     ret = GNUNET_SYSERR;
498   }
499   if (sc->callback != NULL)
500     sc->callback (sc->cls, ret);
501   GNUNET_free (sc);
502 }
503
504
505 /**
506  * Start or stop a service.
507  *
508  * @param h handle to ARM
509  * @param service_name name of the service
510  * @param timeout how long to wait before failing for good
511  * @param cb callback to invoke when service is ready
512  * @param cb_cls closure for callback
513  * @param type type of the request
514  */
515 static void
516 change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
517                 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb,
518                 void *cb_cls, uint16_t type)
519 {
520   struct RequestContext *sctx;
521   size_t slen;
522   struct GNUNET_MessageHeader *msg;
523
524   slen = strlen (service_name) + 1;
525   if (slen + sizeof (struct GNUNET_MessageHeader) >=
526       GNUNET_SERVER_MAX_MESSAGE_SIZE)
527   {
528     GNUNET_break (0);
529     if (cb != NULL)
530       cb (cb_cls, GNUNET_NO);
531     return;
532   }
533 #if DEBUG_ARM
534   LOG (GNUNET_ERROR_TYPE_DEBUG,
535        (type ==
536         GNUNET_MESSAGE_TYPE_ARM_START) ?
537        _("Requesting start of service `%s'.\n") :
538        _("Requesting termination of service `%s'.\n"), service_name);
539 #endif
540   sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
541   sctx->h = h;
542   sctx->callback = cb;
543   sctx->cls = cb_cls;
544   sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
545   sctx->type = type;
546   memcpy (&sctx[1], service_name, slen);
547   msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
548   msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
549   msg->type = htons (sctx->type);
550   memcpy (&msg[1], service_name, slen);
551   if (GNUNET_OK !=
552       GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg,
553                                                GNUNET_TIME_absolute_get_remaining
554                                                (sctx->timeout), GNUNET_YES,
555                                                &handle_response, sctx))
556   {
557     LOG (GNUNET_ERROR_TYPE_WARNING,
558          (type ==
559           GNUNET_MESSAGE_TYPE_ARM_START) ?
560          _("Error while trying to transmit request to start `%s' to ARM\n") :
561          _("Error while trying to transmit request to stop `%s' to ARM\n"),
562          (const char *) &service_name);
563     if (cb != NULL)
564       cb (cb_cls, GNUNET_SYSERR);
565     GNUNET_free (sctx);
566     GNUNET_free (msg);
567     return;
568   }
569   GNUNET_free (msg);
570 }
571
572
573 /**
574  * Start a service.
575  *
576  * @param h handle to ARM
577  * @param service_name name of the service
578  * @param timeout how long to wait before failing for good
579  * @param cb callback to invoke when service is ready
580  * @param cb_cls closure for callback
581  */
582 void
583 GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, const char *service_name,
584                           struct GNUNET_TIME_Relative timeout,
585                           GNUNET_ARM_Callback cb, void *cb_cls)
586 {
587   struct RequestContext *sctx;
588   struct GNUNET_CLIENT_Connection *client;
589   size_t slen;
590
591 #if DEBUG_ARM
592   LOG (GNUNET_ERROR_TYPE_DEBUG,
593        _("Asked to start service `%s' within %llu ms\n"), service_name,
594        (unsigned long long) timeout.rel_value);
595 #endif
596   if (0 == strcasecmp ("arm", service_name))
597   {
598     slen = strlen ("arm") + 1;
599     sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen);
600     sctx->h = h;
601     sctx->callback = cb;
602     sctx->cls = cb_cls;
603     sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
604     memcpy (&sctx[1], service_name, slen);
605     GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
606                                 sctx);
607     return;
608   }
609   if (h->client == NULL)
610   {
611     client = GNUNET_CLIENT_connect ("arm", h->cfg);
612     if (client == NULL)
613     {
614       LOG (GNUNET_ERROR_TYPE_DEBUG,
615            "arm_api, GNUNET_CLIENT_connect returned NULL\n");
616       cb (cb_cls, GNUNET_SYSERR);
617       return;
618     }
619     LOG (GNUNET_ERROR_TYPE_DEBUG,
620          "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
621     GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
622     h->client = client;
623   }
624   LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n");
625   change_service (h, service_name, timeout, cb, cb_cls,
626                   GNUNET_MESSAGE_TYPE_ARM_START);
627 }
628
629 /**
630  * Callback from the arm stop service call, indicates that the arm service
631  * is well and truly dead, won't die, or an error occurred.
632  *
633  * @param cls closure for the callback
634  * @param reason reason for callback, GNUNET_NO if arm is shutdown
635  *        GNUNET_YES if arm remains running, and GNUNET_SYSERR on error
636  */
637 void
638 arm_shutdown_callback (void *cls, int reason)
639 {
640   struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
641
642   if (arm_shutdown_ctx->cb != NULL)
643     arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
644
645   GNUNET_free (arm_shutdown_ctx);
646 }
647
648
649 /**
650  * Stop a service.
651  *
652  * @param h handle to ARM
653  * @param service_name name of the service
654  * @param timeout how long to wait before failing for good
655  * @param cb callback to invoke when service is ready
656  * @param cb_cls closure for callback
657  */
658 void
659 GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, const char *service_name,
660                          struct GNUNET_TIME_Relative timeout,
661                          GNUNET_ARM_Callback cb, void *cb_cls)
662 {
663   struct ARM_ShutdownContext *arm_shutdown_ctx;
664   struct GNUNET_CLIENT_Connection *client;
665
666   LOG (GNUNET_ERROR_TYPE_INFO, _("Stopping service `%s' within %llu ms\n"),
667        service_name, (unsigned long long) timeout.rel_value);
668   if (h->client == NULL)
669   {
670     client = GNUNET_CLIENT_connect ("arm", h->cfg);
671     if (client == NULL)
672     {
673       cb (cb_cls, GNUNET_SYSERR);
674       return;
675     }
676     GNUNET_CLIENT_ignore_shutdown (client, GNUNET_YES);
677     h->client = client;
678   }
679   if (0 == strcasecmp ("arm", service_name))
680   {
681     arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
682     arm_shutdown_ctx->cb = cb;
683     arm_shutdown_ctx->cb_cls = cb_cls;
684     arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
685                           arm_shutdown_ctx);
686     h->client = NULL;
687     return;
688   }
689   change_service (h, service_name, timeout, cb, cb_cls,
690                   GNUNET_MESSAGE_TYPE_ARM_STOP);
691 }
692
693
694 /* end of arm_api.c */