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