multicast: replay
[oweals/gnunet.git] / src / util / client_manager.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file util/client_manager.c
23  * @brief Client manager; higher level client API with transmission queue
24  * and message handler registration.
25  * @author Gabor X Toth
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "util-client-mgr", __VA_ARGS__)
34
35
36 struct OperationListItem
37 {
38   struct OperationListItem *prev;
39   struct OperationListItem *next;
40
41   /**
42    * Operation ID.
43    */
44   uint64_t op_id;
45
46   /**
47    * Continuation to invoke with the result of an operation.
48    */
49   GNUNET_ResultCallback result_cb;
50
51   /**
52    * Closure for @a result_cb.
53    */
54   void *cls;
55 };
56
57
58 /**
59  * List of arrays of message handlers.
60  */
61 struct HandlersListItem
62 {
63   struct HandlersListItem *prev;
64   struct HandlersListItem *next;
65
66   /**
67    * NULL-terminated array of handlers.
68    */
69   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
70 };
71
72
73 struct MessageQueueItem
74 {
75   struct MessageQueueItem *prev;
76   struct MessageQueueItem *next;
77   struct GNUNET_MessageHeader *msg;
78 };
79
80
81 struct GNUNET_CLIENT_MANAGER_Connection
82 {
83   /**
84    * Configuration to use.
85    */
86   const struct GNUNET_CONFIGURATION_Handle *cfg;
87
88   /**
89    * Client connection to service.
90    */
91   struct GNUNET_CLIENT_Connection *client;
92
93   /**
94    * Currently pending transmission request, or NULL for none.
95    */
96   struct GNUNET_CLIENT_TransmitHandle *client_tmit;
97
98   /**
99    * Service name to connect to.
100    */
101   const char *service_name;
102
103   /**
104    * Head of messages to transmit to the service.
105    */
106   struct MessageQueueItem *tmit_head;
107
108   /**
109    * Tail of messages to transmit to the service.
110    */
111   struct MessageQueueItem *tmit_tail;
112
113   /**
114    * Message handlers.
115    */
116   const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
117
118   /**
119    * First operation in the linked list.
120    */
121   struct OperationListItem *op_head;
122
123   /**
124    * Last operation in the linked list.
125    */
126   struct OperationListItem *op_tail;
127
128   /**
129    * Last operation ID used.
130    */
131   uint64_t last_op_id;
132
133   /**
134    * Disconnect callback.
135    */
136   void (*disconnect_cb)(void *);
137
138   /**
139    * Disconnect closure.
140    */
141   void *disconnect_cls;
142
143   /**
144    * User context value.
145    * @see GNUNET_CLIENT_MANAGER_set_user_context()
146    * @see GNUNET_CLIENT_MANAGER_get_user_context()
147    */
148   void *user_ctx;
149
150   /**
151    * Last size given when user context was initialized.
152    * Used for sanity check.
153    */
154   size_t user_ctx_size;
155
156   /**
157    * Task doing exponential back-off trying to reconnect.
158    */
159   struct GNUNET_SCHEDULER_Task * reconnect_task;
160
161   /**
162    * Time for next connect retry.
163    */
164   struct GNUNET_TIME_Relative reconnect_delay;
165
166   /**
167    * Are we currently polling for incoming messages?
168    */
169   uint8_t in_receive;
170
171   /**
172    * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
173    * and we're transmitting the last messages from the queue.
174    */
175   uint8_t is_disconnecting;
176 };
177
178
179 /**
180  * Handle received messages from the service.
181  */
182 static void
183 recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
184 {
185   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
186   uint16_t type = 0, size = 0;
187
188   if (NULL != msg)
189   {
190     type = ntohs (msg->type);
191     size = ntohs (msg->size);
192     /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
193   }
194   else /* disconnected */
195   {
196     mgr->client_tmit = NULL;
197   }
198
199   size_t i = 0;
200   while (NULL != mgr->handlers[i].callback)
201   {
202     const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
203     if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
204     {
205       if (0 != mh->expected_size
206           && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
207               || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
208       {
209         LOG (GNUNET_ERROR_TYPE_ERROR,
210              "Expected %u bytes for message of type %u, got %u.\n",
211              mh->expected_size, type, size);
212         GNUNET_break_op (0);
213         GNUNET_CLIENT_disconnect (mgr->client);
214         mgr->client = NULL;
215         recv_message (mgr, NULL);
216         break;
217       }
218       mh->callback (mh->callback_cls, mgr, msg);
219     }
220     i++;
221   }
222   if (NULL != mgr->client)
223   {
224     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
225                            GNUNET_TIME_UNIT_FOREVER_REL);
226   }
227 }
228
229
230 /**
231  * Schedule transmission of the next message from our queue.
232  *
233  * @param mgr  Client manager connection.
234  */
235 static void
236 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
237
238
239 static void
240 schedule_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
241 {
242   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
243   GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
244                                     mgr->disconnect_cb, mgr->disconnect_cls);
245 }
246
247
248 /**
249  * Transmit next message to service.
250  *
251  * @param cls
252  *        struct GNUNET_CLIENT_MANAGER_Connection
253  * @param buf_size
254  *        Number of bytes available in @a buf.
255  * @param buf
256  *        Where to copy the message.
257  *
258  * @return Number of bytes copied to @a buf.
259  */
260 static size_t
261 send_next_message (void *cls, size_t buf_size, void *buf)
262 {
263   LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
264   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
265
266   if (NULL == buf)
267   {
268     /* disconnected */
269     recv_message (mgr, NULL);
270     return 0;
271   }
272
273   struct MessageQueueItem *mqi = mgr->tmit_head;
274   if (NULL == mqi)
275     return 0;
276
277   uint16_t size = ntohs (mqi->msg->size);
278   mgr->client_tmit = NULL;
279   GNUNET_assert (size <= buf_size);
280   memcpy (buf, mqi->msg, size);
281
282   GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
283   GNUNET_free (mqi->msg);
284   GNUNET_free (mqi);
285
286   if (NULL != mgr->tmit_head)
287   {
288     transmit_next (mgr);
289   }
290   else if (GNUNET_YES == mgr->is_disconnecting)
291   {
292     GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
293     return size;
294   }
295
296   if (GNUNET_NO == mgr->in_receive)
297   {
298     mgr->in_receive = GNUNET_YES;
299     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
300                            GNUNET_TIME_UNIT_FOREVER_REL);
301   }
302   return size;
303 }
304
305
306 /**
307  * Schedule transmission of the next message from our queue.
308  *
309  * @param mgr  Client manager connection.
310  */
311 static void
312 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
313 {
314   LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
315   if (NULL != mgr->client_tmit || NULL == mgr->client)
316     return;
317
318   if (NULL == mgr->tmit_head)
319   {
320     if (GNUNET_YES == mgr->is_disconnecting)
321       GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
322                                         mgr->disconnect_cb, mgr->disconnect_cls);
323     return;
324   }
325
326   mgr->client_tmit
327     = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
328                                            ntohs (mgr->tmit_head->msg->size),
329                                            GNUNET_TIME_UNIT_FOREVER_REL,
330                                            GNUNET_NO,
331                                            &send_next_message,
332                                            mgr);
333 }
334
335
336 /**
337  * Try again to connect to the service.
338  *
339  * @param cls
340  *        Channel handle.
341  * @param tc
342  *        Scheduler context.
343  */
344 static void
345 schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
346 {
347   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
348   mgr->reconnect_task = NULL;
349
350   LOG (GNUNET_ERROR_TYPE_DEBUG,
351        "Connecting to %s service.\n", mgr->service_name);
352   GNUNET_assert (NULL == mgr->client);
353   mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
354   GNUNET_assert (NULL != mgr->client);
355
356   transmit_next (mgr);
357 }
358
359
360 /**
361  * Connect to service.
362  *
363  * @param cfg
364  *        Configuration to use.
365  * @param service_name
366  *        Service name to connect to.
367  * @param handlers
368  *        Message handlers.
369  *
370  * @return Client manager connection handle.
371  */
372 struct GNUNET_CLIENT_MANAGER_Connection *
373 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
374                                const char *service_name,
375                                const struct
376                                GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
377 {
378   struct GNUNET_CLIENT_MANAGER_Connection *
379     mgr = GNUNET_malloc (sizeof (*mgr));
380   mgr->cfg = cfg;
381   mgr->service_name = service_name;
382   mgr->handlers = handlers;
383   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
384   mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
385   return mgr;
386 }
387
388
389 /**
390  * Disconnect from the service.
391  *
392  * @param mgr
393  *        Client manager connection.
394  * @param transmit_queue
395  *        Transmit pending messages in queue before disconnecting.
396  * @param disconnect_cb
397  *        Function called after disconnected from the service.
398  * @param cls
399  *        Closure for @a disconnect_cb.
400  */
401 void
402 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
403                                   int transmit_queue,
404                                   GNUNET_ContinuationCallback disconnect_cb,
405                                   void *cls)
406 {
407   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
408   mgr->disconnect_cb = disconnect_cb;
409   mgr->disconnect_cls = cls;
410   if (NULL != mgr->tmit_head)
411   {
412     if (GNUNET_YES == transmit_queue)
413     {
414       mgr->is_disconnecting = GNUNET_YES;
415       transmit_next (mgr);
416       return;
417     }
418     else
419     {
420       LOG (GNUNET_ERROR_TYPE_DEBUG,
421            "Disconnecting while there are still messages "
422            "in the transmission queue.\n");
423       GNUNET_CLIENT_MANAGER_drop_queue (mgr);
424     }
425   }
426   if (mgr->reconnect_task != NULL)
427   {
428     GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
429     mgr->reconnect_task = NULL;
430   }
431   if (NULL != mgr->client_tmit)
432   {
433     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
434     mgr->client_tmit = NULL;
435   }
436   if (NULL != mgr->client)
437   {
438     GNUNET_CLIENT_disconnect (mgr->client);
439     mgr->client = NULL;
440   }
441   if (NULL != mgr->disconnect_cb)
442     mgr->disconnect_cb (mgr->disconnect_cls);
443   GNUNET_free (mgr);
444   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
445 }
446
447
448 /**
449  * Reschedule connect to the service using exponential back-off.
450  *
451  * @param mgr
452  *        Client manager connection.
453  */
454 void
455 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
456 {
457   if (NULL != mgr->reconnect_task)
458     return;
459
460   if (NULL != mgr->client_tmit)
461   {
462     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
463     mgr->client_tmit = NULL;
464   }
465   if (NULL != mgr->client)
466   {
467     GNUNET_CLIENT_disconnect (mgr->client);
468     mgr->client = NULL;
469   }
470   mgr->in_receive = GNUNET_NO;
471   LOG (GNUNET_ERROR_TYPE_DEBUG,
472        "Scheduling task to reconnect to service in %s.\n",
473        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
474   mgr->reconnect_task =
475     GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
476   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
477 }
478
479
480 /**
481  * Add a message to the end of the transmission queue.
482  *
483  * @param mgr
484  *        Client manager connection.
485  * @param msg
486  *        Message to transmit, should be allocated with GNUNET_malloc() or
487  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
488  */
489 void
490 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
491                                 struct GNUNET_MessageHeader *msg)
492 {
493   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
494   mqi->msg = GNUNET_copy_message (msg);
495   GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
496   transmit_next (mgr);
497 }
498
499
500 /**
501  * Add a message to the beginning of the transmission queue.
502  *
503  * @param mgr
504  *        Client manager connection.
505  * @param msg
506  *        Message to transmit, should be allocated with GNUNET_malloc() or
507  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
508  */
509 void
510 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
511                                     struct GNUNET_MessageHeader *msg)
512 {
513   struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
514   mqi->msg = GNUNET_copy_message (msg);
515   GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
516   transmit_next (mgr);
517 }
518
519
520 /**
521  * Drop all queued messages.
522  *
523  * @param mgr
524  *        Client manager connection.
525  */
526 void
527 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
528 {
529   struct MessageQueueItem *cur, *next = mgr->tmit_head;
530   while (NULL != next)
531   {
532     cur = next;
533     next = cur->next;
534     GNUNET_free (cur->msg);
535     GNUNET_free (cur);
536   }
537 }
538
539
540 /**
541  * Obtain client connection handle.
542  *
543  * @param mgr
544  *        Client manager connection.
545  *
546  * @return Client connection handle.
547  */
548 struct GNUNET_CLIENT_Connection *
549 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
550 {
551   return mgr->client;
552 }
553
554
555 /**
556  * Return user context associated with the given client.
557  * Note: you should probably use the macro (call without the underscore).
558  *
559  * @param mgr
560  *        Client manager connection.
561  * @param size
562  *        Number of bytes in user context struct (for verification only).
563  *
564  * @return User context.
565  */
566 void *
567 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
568                                          size_t size)
569 {
570   if ((0 == mgr->user_ctx_size) &&
571       (NULL == mgr->user_ctx))
572     return NULL; /* never set */
573   GNUNET_assert (size == mgr->user_ctx_size);
574   return mgr->user_ctx;
575 }
576
577
578 /**
579  * Set user context to be associated with the given client.
580  * Note: you should probably use the macro (call without the underscore).
581  *
582  * @param mgr
583  *        Client manager connection.
584  * @param ctx
585  *        User context.
586  * @param size
587  *        Number of bytes in user context struct (for verification only).
588  */
589 void
590 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
591                                          void *ctx,
592                                          size_t size)
593 {
594   if (NULL == ctx)
595   {
596     mgr->user_ctx_size = 0;
597     mgr->user_ctx = ctx;
598     return;
599   }
600   mgr->user_ctx_size = size;
601   mgr->user_ctx = ctx;
602 }
603
604
605 /**
606  * Get a unique operation ID to distinguish between asynchronous requests.
607  *
608  * @param mgr
609  *        Client manager connection.
610  *
611  * @return Operation ID to use.
612  */
613 uint64_t
614 GNUNET_CLIENT_MANAGER_op_get_next_id (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
615 {
616   return ++mgr->last_op_id;
617 }
618
619
620 /**
621  * Find operation by ID.
622  *
623  * @param mgr
624  *        Client manager connection.
625  * @param op_id
626  *        Operation ID to look up.
627  *
628  * @return Operation, or NULL if not found.
629  */
630 static struct OperationListItem *
631 op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr, uint64_t op_id)
632 {
633   struct OperationListItem *op = mgr->op_head;
634   while (NULL != op)
635   {
636     if (op->op_id == op_id)
637       return op;
638     op = op->next;
639   }
640   return NULL;
641 }
642
643
644 /**
645  * Find operation by ID.
646  *
647  * @param mgr
648  *        Client manager connection.
649  * @param op_id
650  *        Operation ID to look up.
651  * @param[out] result_cb
652  *        If an operation was found, its result callback is returned here.
653  * @param[out] cls
654  *        If an operation was found, its closure is returned here.
655  *
656  * @return #GNUNET_YES if an operation was found,
657  *         #GNUNET_NO  if not found.
658  */
659 int
660 GNUNET_CLIENT_MANAGER_op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
661                                uint64_t op_id,
662                                GNUNET_ResultCallback *result_cb,
663                                void **cls)
664 {
665   struct OperationListItem *op = op_find (mgr, op_id);
666   if (NULL != op)
667   {
668     *result_cb = op->result_cb;
669     *cls = op->cls;
670     return GNUNET_YES;
671   }
672   return GNUNET_NO;
673 }
674
675
676 /**
677  * Add a new operation.
678  *
679  * @param mgr
680  *        Client manager connection.
681  * @param result_cb
682  *        Function to call with the result of the operation.
683  * @param cls
684  *        Closure for @a result_cb.
685  *
686  * @return ID of the new operation.
687  */
688 uint64_t
689 GNUNET_CLIENT_MANAGER_op_add (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
690                               GNUNET_ResultCallback result_cb,
691                               void *cls)
692 {
693   if (NULL == result_cb)
694     return 0;
695
696   struct OperationListItem *op = GNUNET_malloc (sizeof (*op));
697   op->op_id = GNUNET_CLIENT_MANAGER_op_get_next_id (mgr);
698   op->result_cb = result_cb;
699   op->cls = cls;
700   GNUNET_CONTAINER_DLL_insert_tail (mgr->op_head, mgr->op_tail, op);
701
702   LOG (GNUNET_ERROR_TYPE_DEBUG,
703        "%p Added operation #%" PRIu64 "\n", mgr, op->op_id);
704   return op->op_id;
705 }
706
707
708 /**
709  * Remove an operation, and call its result callback (unless it was cancelled).
710  *
711  *
712  * @param mgr
713  *        Client manager connection.
714  * @param op_id
715  *        Operation ID.
716  * @param result_code
717  *        Result of the operation.
718  * @param data
719  *        Data result of the operation.
720  * @param data_size
721  *        Size of @a data.
722  * @param cancel
723  *        Is the operation cancelled?
724  *        #GNUNET_NO  Not cancelled, result callback is called.
725  *        #GNUNET_YES Cancelled, result callback is not called.
726  *
727  * @return #GNUNET_YES if the operation was found and removed,
728  *         #GNUNET_NO  if the operation was not found.
729  */
730 static int
731 op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
732            uint64_t op_id, int64_t result_code,
733            const void *data, uint16_t data_size, uint8_t cancel)
734 {
735   if (0 == op_id)
736     return GNUNET_NO;
737
738   struct OperationListItem *op = op_find (mgr, op_id);
739   if (NULL == op)
740   {
741     LOG (GNUNET_ERROR_TYPE_WARNING,
742          "Could not find operation #%" PRIu64 "\n", op_id);
743     return GNUNET_NO;
744   }
745
746   GNUNET_CONTAINER_DLL_remove (mgr->op_head, mgr->op_tail, op);
747
748   if (GNUNET_YES != cancel && NULL != op->result_cb)
749     op->result_cb (op->cls, result_code, data, data_size);
750
751   GNUNET_free (op);
752   return GNUNET_YES;
753 }
754
755
756 /**
757  * Call the result callback of an operation and remove it.
758  *
759  * @param mgr
760  *        Client manager connection.
761  * @param op_id
762  *        Operation ID.
763  * @param result_code
764  *        Result of the operation.
765  * @param data
766  *        Data result of the operation.
767  * @param data_size
768  *        Size of @a data.
769  *
770  * @return #GNUNET_YES if the operation was found and removed,
771  *         #GNUNET_NO  if the operation was not found.
772  */
773 int
774 GNUNET_CLIENT_MANAGER_op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
775                                  uint64_t op_id, int64_t result_code,
776                                  const void *data, uint16_t data_size)
777 {
778   LOG (GNUNET_ERROR_TYPE_DEBUG,
779        "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
780        mgr, op_id, result_code, data_size);
781   return op_result (mgr, op_id, result_code, data, data_size, GNUNET_NO);
782 }
783
784
785 /**
786  * Cancel an operation.
787  *
788  * @param mgr
789  *        Client manager connection.
790  * @param op_id
791  *        Operation ID.
792  *
793  * @return #GNUNET_YES if the operation was found and removed,
794  *         #GNUNET_NO  if the operation was not found.
795  */
796 int
797 GNUNET_CLIENT_MANAGER_op_cancel (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
798                                  uint64_t op_id)
799 {
800   LOG (GNUNET_ERROR_TYPE_DEBUG,
801        "%p Cancelling operation #%" PRIu64  "\n", mgr, op_id);
802   return op_result (mgr, op_id, 0, NULL, 0, GNUNET_YES);
803 }