fix #4545: create directory for log file if it does not exist
[oweals/gnunet.git] / src / util / client_manager.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 GNUnet e.V.
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   if (GNUNET_YES == mgr->is_disconnecting)
200     return;
201
202   size_t i = 0;
203   while (NULL != mgr->handlers[i].callback)
204   {
205     const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
206     if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
207     {
208       if (0 != mh->expected_size
209           && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
210               || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
211       {
212         LOG (GNUNET_ERROR_TYPE_ERROR,
213              "Expected %u bytes for message of type %u, got %u.\n",
214              mh->expected_size, type, size);
215         GNUNET_break_op (0);
216         GNUNET_CLIENT_disconnect (mgr->client);
217         mgr->client = NULL;
218         recv_message (mgr, NULL);
219         break;
220       }
221       mh->callback (mh->callback_cls, mgr, msg);
222     }
223     i++;
224   }
225   if (NULL != mgr->client)
226   {
227     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
228                            GNUNET_TIME_UNIT_FOREVER_REL);
229   }
230 }
231
232
233 /**
234  * Schedule transmission of the next message from our queue.
235  *
236  * @param mgr  Client manager connection.
237  */
238 static void
239 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
240
241
242 static void
243 schedule_disconnect (void *cls)
244 {
245   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
246
247   GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
248                                     mgr->disconnect_cb,
249                                     mgr->disconnect_cls);
250 }
251
252
253 /**
254  * Transmit next message to service.
255  *
256  * @param cls
257  *        struct GNUNET_CLIENT_MANAGER_Connection
258  * @param buf_size
259  *        Number of bytes available in @a buf.
260  * @param buf
261  *        Where to copy the message.
262  *
263  * @return Number of bytes copied to @a buf.
264  */
265 static size_t
266 send_next_message (void *cls, size_t buf_size, void *buf)
267 {
268   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
269
270   LOG (GNUNET_ERROR_TYPE_DEBUG,
271        "send_next_message()\n");
272   if (NULL == buf)
273   {
274     /* disconnected */
275     recv_message (mgr, NULL);
276     return 0;
277   }
278
279   struct MessageQueueItem *mqi = mgr->tmit_head;
280   if (NULL == mqi)
281     return 0;
282
283   uint16_t size = ntohs (mqi->msg->size);
284   mgr->client_tmit = NULL;
285   GNUNET_assert (size <= buf_size);
286   memcpy (buf, mqi->msg, size);
287
288   GNUNET_CONTAINER_DLL_remove (mgr->tmit_head,
289                                mgr->tmit_tail,
290                                mqi);
291   GNUNET_free (mqi->msg);
292   GNUNET_free (mqi);
293
294   if (NULL != mgr->tmit_head)
295   {
296     transmit_next (mgr);
297   }
298   else if (GNUNET_YES == mgr->is_disconnecting)
299   {
300     (void) GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
301     return size;
302   }
303
304   if (GNUNET_NO == mgr->in_receive)
305   {
306     mgr->in_receive = GNUNET_YES;
307     GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
308                            GNUNET_TIME_UNIT_FOREVER_REL);
309   }
310   return size;
311 }
312
313
314 /**
315  * Schedule transmission of the next message from our queue.
316  *
317  * @param mgr  Client manager connection.
318  */
319 static void
320 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
321 {
322   LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
323   if (NULL != mgr->client_tmit || NULL == mgr->client)
324     return;
325
326   if (NULL == mgr->tmit_head)
327   {
328     if (GNUNET_YES == mgr->is_disconnecting)
329       GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
330                                         mgr->disconnect_cb,
331                                         mgr->disconnect_cls);
332     return;
333   }
334
335   mgr->client_tmit
336     = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
337                                            GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
338                                            GNUNET_TIME_UNIT_FOREVER_REL,
339                                            GNUNET_NO,
340                                            &send_next_message,
341                                            mgr);
342 }
343
344
345 /**
346  * Try again to connect to the service.
347  *
348  * @param cls
349  *        Channel handle.
350  */
351 static void
352 schedule_reconnect (void *cls)
353 {
354   struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
355
356   mgr->reconnect_task = NULL;
357   LOG (GNUNET_ERROR_TYPE_DEBUG,
358        "Connecting to %s service.\n",
359        mgr->service_name);
360   GNUNET_assert (NULL == mgr->client);
361   mgr->client = GNUNET_CLIENT_connect (mgr->service_name,
362                                        mgr->cfg);
363   GNUNET_assert (NULL != mgr->client);
364   transmit_next (mgr);
365 }
366
367
368 /**
369  * Connect to service.
370  *
371  * @param cfg
372  *        Configuration to use.
373  * @param service_name
374  *        Service name to connect to.
375  * @param handlers
376  *        Message handlers.
377  *
378  * @return Client manager connection handle.
379  */
380 struct GNUNET_CLIENT_MANAGER_Connection *
381 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
382                                const char *service_name,
383                                const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
384 {
385   struct GNUNET_CLIENT_MANAGER_Connection *mgr;
386   
387   mgr = GNUNET_new (struct GNUNET_CLIENT_MANAGER_Connection);
388   mgr->cfg = cfg;
389   mgr->service_name = service_name;
390   mgr->handlers = handlers;
391   mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
392   mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect,
393                                                   mgr);
394   return mgr;
395 }
396
397
398 /**
399  * Disconnect from the service.
400  *
401  * @param mgr
402  *        Client manager connection.
403  * @param transmit_queue
404  *        Transmit pending messages in queue before disconnecting.
405  * @param disconnect_cb
406  *        Function called after disconnected from the service.
407  * @param cls
408  *        Closure for @a disconnect_cb.
409  */
410 void
411 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
412                                   int transmit_queue,
413                                   GNUNET_ContinuationCallback disconnect_cb,
414                                   void *cls)
415 {
416   LOG (GNUNET_ERROR_TYPE_DEBUG,
417        "Disconnecting (%d)\n",
418        transmit_queue);
419   mgr->disconnect_cb = disconnect_cb;
420   mgr->disconnect_cls = cls;
421   if (NULL != mgr->tmit_head)
422   {
423     if (GNUNET_YES == transmit_queue)
424     {
425       mgr->is_disconnecting = GNUNET_YES;
426       transmit_next (mgr);
427       return;
428     }
429     else
430     {
431       LOG (GNUNET_ERROR_TYPE_DEBUG,
432            "Disconnecting while there are still messages "
433            "in the transmission queue.\n");
434       GNUNET_CLIENT_MANAGER_drop_queue (mgr);
435     }
436   }
437   if (NULL != mgr->reconnect_task)
438   {
439     GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
440     mgr->reconnect_task = NULL;
441   }
442   if (NULL != mgr->client_tmit)
443   {
444     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
445     mgr->client_tmit = NULL;
446   }
447   if (NULL != mgr->client)
448   {
449     GNUNET_CLIENT_disconnect (mgr->client);
450     mgr->client = NULL;
451   }
452   if (NULL != mgr->disconnect_cb)
453     mgr->disconnect_cb (mgr->disconnect_cls);
454   GNUNET_free (mgr);
455   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
456 }
457
458
459 /**
460  * Reschedule connect to the service using exponential back-off.
461  *
462  * @param mgr
463  *        Client manager connection.
464  */
465 void
466 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
467 {
468   if (NULL != mgr->reconnect_task)
469     return;
470
471   if (NULL != mgr->client_tmit)
472   {
473     GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
474     mgr->client_tmit = NULL;
475   }
476   if (NULL != mgr->client)
477   {
478     GNUNET_CLIENT_disconnect (mgr->client);
479     mgr->client = NULL;
480   }
481   mgr->in_receive = GNUNET_NO;
482   LOG (GNUNET_ERROR_TYPE_DEBUG,
483        "Scheduling task to reconnect to service in %s.\n",
484        GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
485   mgr->reconnect_task =
486     GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay,
487                                   &schedule_reconnect,
488                                   mgr);
489   mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
490 }
491
492
493 /**
494  * Add a message to the end of the transmission queue.
495  *
496  * @param mgr
497  *        Client manager connection.
498  * @param msg
499  *        Message to transmit, should be allocated with GNUNET_malloc() or
500  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
501  */
502 void
503 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
504                                 struct GNUNET_MessageHeader *msg)
505 {
506   struct MessageQueueItem *mqi;
507
508   mqi = GNUNET_new (struct MessageQueueItem);
509   mqi->msg = GNUNET_copy_message (msg);
510   GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head,
511                                     mgr->tmit_tail,
512                                     mqi);
513   transmit_next (mgr);
514 }
515
516
517 /**
518  * Add a message to the beginning of the transmission queue.
519  *
520  * @param mgr
521  *        Client manager connection.
522  * @param msg
523  *        Message to transmit, should be allocated with GNUNET_malloc() or
524  *        GNUNET_new(), as it is freed with GNUNET_free() after transmission.
525  */
526 void
527 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
528                                     struct GNUNET_MessageHeader *msg)
529 {
530   struct MessageQueueItem *mqi;
531
532   mqi = GNUNET_new (struct MessageQueueItem);
533   mqi->msg = GNUNET_copy_message (msg);
534   GNUNET_CONTAINER_DLL_insert (mgr->tmit_head,
535                                mgr->tmit_tail,
536                                mqi);
537   transmit_next (mgr);
538 }
539
540
541 /**
542  * Drop all queued messages.
543  *
544  * @param mgr
545  *        Client manager connection.
546  */
547 void
548 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
549 {
550   struct MessageQueueItem *cur;
551   struct MessageQueueItem *next;
552
553   next = mgr->tmit_head;
554   while (NULL != next)
555   {
556     cur = next;
557     next = cur->next;
558     GNUNET_free (cur->msg);
559     GNUNET_free (cur);
560   }
561 }
562
563
564 /**
565  * Obtain client connection handle.
566  *
567  * @param mgr
568  *        Client manager connection.
569  *
570  * @return Client connection handle.
571  */
572 struct GNUNET_CLIENT_Connection *
573 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
574 {
575   return mgr->client;
576 }
577
578
579 /**
580  * Return user context associated with the given client.
581  * Note: you should probably use the macro (call without the underscore).
582  *
583  * @param mgr
584  *        Client manager connection.
585  * @param size
586  *        Number of bytes in user context struct (for verification only).
587  *
588  * @return User context.
589  */
590 void *
591 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
592                                          size_t size)
593 {
594   if ((0 == mgr->user_ctx_size) &&
595       (NULL == mgr->user_ctx))
596     return NULL; /* never set */
597   GNUNET_assert (size == mgr->user_ctx_size);
598   return mgr->user_ctx;
599 }
600
601
602 /**
603  * Set user context to be associated with the given client.
604  * Note: you should probably use the macro (call without the underscore).
605  *
606  * @param mgr
607  *        Client manager connection.
608  * @param ctx
609  *        User context.
610  * @param size
611  *        Number of bytes in user context struct (for verification only).
612  */
613 void
614 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
615                                          void *ctx,
616                                          size_t size)
617 {
618   if (NULL == ctx)
619   {
620     mgr->user_ctx_size = 0;
621     mgr->user_ctx = ctx;
622     return;
623   }
624   mgr->user_ctx_size = size;
625   mgr->user_ctx = ctx;
626 }
627
628
629 /**
630  * Get a unique operation ID to distinguish between asynchronous requests.
631  *
632  * @param mgr
633  *        Client manager connection.
634  *
635  * @return Operation ID to use.
636  */
637 uint64_t
638 GNUNET_CLIENT_MANAGER_op_get_next_id (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
639 {
640   return ++mgr->last_op_id;
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  *
652  * @return Operation, or NULL if not found.
653  */
654 static struct OperationListItem *
655 op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
656          uint64_t op_id)
657 {
658   struct OperationListItem *op;
659
660   for (op = mgr->op_head; NULL != op; op = op->next)
661     if (op->op_id == op_id)
662       return op;
663   return NULL;
664 }
665
666
667 /**
668  * Find operation by ID.
669  *
670  * @param mgr
671  *        Client manager connection.
672  * @param op_id
673  *        Operation ID to look up.
674  * @param[out] result_cb
675  *        If an operation was found, its result callback is returned here.
676  * @param[out] cls
677  *        If an operation was found, its closure is returned here.
678  *
679  * @return #GNUNET_YES if an operation was found,
680  *         #GNUNET_NO  if not found.
681  */
682 int
683 GNUNET_CLIENT_MANAGER_op_find (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
684                                uint64_t op_id,
685                                GNUNET_ResultCallback *result_cb,
686                                void **cls)
687 {
688   struct OperationListItem *op = op_find (mgr, op_id);
689   if (NULL != op)
690   {
691     *result_cb = op->result_cb;
692     *cls = op->cls;
693     return GNUNET_YES;
694   }
695   return GNUNET_NO;
696 }
697
698
699 /**
700  * Add a new operation.
701  *
702  * @param mgr
703  *        Client manager connection.
704  * @param result_cb
705  *        Function to call with the result of the operation.
706  * @param cls
707  *        Closure for @a result_cb.
708  *
709  * @return ID of the new operation.
710  */
711 uint64_t
712 GNUNET_CLIENT_MANAGER_op_add (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
713                               GNUNET_ResultCallback result_cb,
714                               void *cls)
715 {
716   struct OperationListItem *op;
717
718   if (NULL == result_cb)
719     return 0;
720   op = GNUNET_new (struct OperationListItem);
721   op->op_id = GNUNET_CLIENT_MANAGER_op_get_next_id (mgr);
722   op->result_cb = result_cb;
723   op->cls = cls;
724   GNUNET_CONTAINER_DLL_insert_tail (mgr->op_head,
725                                     mgr->op_tail,
726                                     op);
727   LOG (GNUNET_ERROR_TYPE_DEBUG,
728        "%p Added operation #%" PRIu64 "\n",
729        mgr,
730        op->op_id);
731   return op->op_id;
732 }
733
734
735 /**
736  * Remove an operation, and call its result callback (unless it was cancelled).
737  *
738  *
739  * @param mgr
740  *        Client manager connection.
741  * @param op_id
742  *        Operation ID.
743  * @param result_code
744  *        Result of the operation.
745  * @param data
746  *        Data result of the operation.
747  * @param data_size
748  *        Size of @a data.
749  * @param cancel
750  *        Is the operation cancelled?
751  *        #GNUNET_NO  Not cancelled, result callback is called.
752  *        #GNUNET_YES Cancelled, result callback is not called.
753  *
754  * @return #GNUNET_YES if the operation was found and removed,
755  *         #GNUNET_NO  if the operation was not found.
756  */
757 static int
758 op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
759            uint64_t op_id,
760            int64_t result_code,
761            const void *data,
762            uint16_t data_size,
763            uint8_t cancel)
764 {
765   if (0 == op_id)
766     return GNUNET_NO;
767
768   struct OperationListItem *op = op_find (mgr, op_id);
769   if (NULL == op)
770   {
771     LOG (GNUNET_ERROR_TYPE_WARNING,
772          "Could not find operation #%" PRIu64 "\n", op_id);
773     return GNUNET_NO;
774   }
775
776   GNUNET_CONTAINER_DLL_remove (mgr->op_head,
777                                mgr->op_tail,
778                                op);
779
780   if ( (GNUNET_YES != cancel) &&
781        (NULL != op->result_cb) )
782     op->result_cb (op->cls,
783                    result_code, data,
784                    data_size);
785   GNUNET_free (op);
786   return GNUNET_YES;
787 }
788
789
790 /**
791  * Call the result callback of an operation and remove it.
792  *
793  * @param mgr
794  *        Client manager connection.
795  * @param op_id
796  *        Operation ID.
797  * @param result_code
798  *        Result of the operation.
799  * @param data
800  *        Data result of the operation.
801  * @param data_size
802  *        Size of @a data.
803  *
804  * @return #GNUNET_YES if the operation was found and removed,
805  *         #GNUNET_NO  if the operation was not found.
806  */
807 int
808 GNUNET_CLIENT_MANAGER_op_result (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
809                                  uint64_t op_id,
810                                  int64_t result_code,
811                                  const void *data,
812                                  uint16_t data_size)
813 {
814   LOG (GNUNET_ERROR_TYPE_DEBUG,
815        "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
816        mgr, op_id, result_code, data_size);
817   return op_result (mgr, op_id, result_code, data, data_size, GNUNET_NO);
818 }
819
820
821 /**
822  * Cancel an operation.
823  *
824  * @param mgr
825  *        Client manager connection.
826  * @param op_id
827  *        Operation ID.
828  *
829  * @return #GNUNET_YES if the operation was found and removed,
830  *         #GNUNET_NO  if the operation was not found.
831  */
832 int
833 GNUNET_CLIENT_MANAGER_op_cancel (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
834                                  uint64_t op_id)
835 {
836   LOG (GNUNET_ERROR_TYPE_DEBUG,
837        "%p Cancelling operation #%" PRIu64  "\n",
838        mgr,
839        op_id);
840   return op_result (mgr, op_id, 0, NULL, 0, GNUNET_YES);
841 }