-fixing 2352
[oweals/gnunet.git] / src / fs / gnunet-service-fs_put.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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 fs/gnunet-service-fs_put.c
23  * @brief API to PUT zero-anonymity index data from our datastore into the DHT
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-fs.h"
28 #include "gnunet-service-fs_put.h"
29
30
31 /**
32  * How often do we at most PUT content into the DHT?
33  */
34 #define MAX_DHT_PUT_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
35
36 /**
37  * How many replicas do we try to create per PUT?
38  */
39 #define DEFAULT_PUT_REPLICATION 5
40
41
42 /**
43  * Context for each zero-anonymity iterator.
44  */
45 struct PutOperator
46 {
47
48   /**
49    * Request to datastore for DHT PUTs (or NULL).
50    */
51   struct GNUNET_DATASTORE_QueueEntry *dht_qe;
52
53   /**
54    * Type we request from the datastore.
55    */
56   enum GNUNET_BLOCK_Type dht_put_type;
57
58   /**
59    * Handle to PUT operation.
60    */
61   struct GNUNET_DHT_PutHandle *dht_put;
62
63   /**
64    * ID of task that collects blocks for DHT PUTs.
65    */
66   GNUNET_SCHEDULER_TaskIdentifier dht_task;
67
68   /**
69    * How many entires with zero anonymity of our type do we currently
70    * estimate to have in the database?
71    */
72   uint64_t zero_anonymity_count_estimate;
73
74   /**
75    * Current offset when iterating the database.
76    */
77   uint64_t current_offset;
78 };
79
80
81 /**
82  * ANY-terminated list of our operators (one per type
83  * of block that we're putting into the DHT).
84  */
85 static struct PutOperator operators[] = {
86   {NULL, GNUNET_BLOCK_TYPE_FS_KBLOCK, 0, 0, 0},
87   {NULL, GNUNET_BLOCK_TYPE_FS_SBLOCK, 0, 0, 0},
88   {NULL, GNUNET_BLOCK_TYPE_FS_NBLOCK, 0, 0, 0},
89   {NULL, GNUNET_BLOCK_TYPE_ANY, 0, 0, 0}
90 };
91
92
93 /**
94  * Task that is run periodically to obtain blocks for DHT PUTs.
95  *
96  * @param cls type of blocks to gather
97  * @param tc scheduler context (unused)
98  */
99 static void
100 gather_dht_put_blocks (void *cls,
101                        const struct GNUNET_SCHEDULER_TaskContext *tc);
102
103
104 /**
105  * Calculate when to run the next PUT operation and schedule it.
106  *
107  * @param po put operator to schedule
108  */
109 static void
110 schedule_next_put (struct PutOperator *po)
111 {
112   struct GNUNET_TIME_Relative delay;
113
114   if (po->zero_anonymity_count_estimate > 0)
115   {
116     delay =
117         GNUNET_TIME_relative_divide (GNUNET_DHT_DEFAULT_REPUBLISH_FREQUENCY,
118                                      po->zero_anonymity_count_estimate);
119     delay = GNUNET_TIME_relative_min (delay, MAX_DHT_PUT_FREQ);
120   }
121   else
122   {
123     /* if we have NO zero-anonymity content yet, wait 5 minutes for some to
124      * (hopefully) appear */
125     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5);
126   }
127   po->dht_task =
128       GNUNET_SCHEDULER_add_delayed (delay, &gather_dht_put_blocks, po);
129 }
130
131
132 /**
133  * Continuation called after DHT PUT operation has finished.
134  *
135  * @param cls type of blocks to gather
136  * @param success GNUNET_OK if the PUT was transmitted,
137  *                GNUNET_NO on timeout,
138  *                GNUNET_SYSERR on disconnect from service
139  *                after the PUT message was transmitted
140  *                (so we don't know if it was received or not)
141  */
142 static void
143 delay_dht_put_blocks (void *cls, int success)
144 {
145   struct PutOperator *po = cls;
146
147   po->dht_put = NULL;
148   schedule_next_put (po);
149 }
150
151
152 /**
153  * Task that is run periodically to obtain blocks for DHT PUTs.
154  *
155  * @param cls type of blocks to gather
156  * @param tc scheduler context
157  */
158 static void
159 delay_dht_put_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160 {
161   struct PutOperator *po = cls;
162
163   po->dht_task = GNUNET_SCHEDULER_NO_TASK;
164   schedule_next_put (po);
165 }
166
167
168 /**
169  * Store content in DHT.
170  *
171  * @param cls closure
172  * @param key key for the content
173  * @param size number of bytes in data
174  * @param data content stored
175  * @param type type of the content
176  * @param priority priority of the content
177  * @param anonymity anonymity-level for the content
178  * @param expiration expiration time for the content
179  * @param uid unique identifier for the datum;
180  *        maybe 0 if no unique identifier is available
181  */
182 static void
183 process_dht_put_content (void *cls, const GNUNET_HashCode * key, size_t size,
184                          const void *data, enum GNUNET_BLOCK_Type type,
185                          uint32_t priority, uint32_t anonymity,
186                          struct GNUNET_TIME_Absolute expiration, uint64_t uid)
187 {
188   struct PutOperator *po = cls;
189
190   po->dht_qe = NULL;
191   if (key == NULL)
192   {
193     po->zero_anonymity_count_estimate = po->current_offset - 1;
194     po->current_offset = 0;
195     po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po);
196     return;
197   }
198   po->zero_anonymity_count_estimate =
199       GNUNET_MAX (po->current_offset, po->zero_anonymity_count_estimate);
200   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201               "Retrieved block `%s' of type %u for DHT PUT\n", GNUNET_h2s (key),
202               type);
203   po->dht_put = GNUNET_DHT_put (GSF_dht, key, DEFAULT_PUT_REPLICATION,
204                                 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, type, size, data,
205                                 expiration, GNUNET_TIME_UNIT_FOREVER_REL,
206                                 &delay_dht_put_blocks, po);
207 }
208
209
210 /**
211  * Task that is run periodically to obtain blocks for DHT PUTs.
212  *
213  * @param cls type of blocks to gather
214  * @param tc scheduler context (unused)
215  */
216 static void
217 gather_dht_put_blocks (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
218 {
219   struct PutOperator *po = cls;
220
221   po->dht_task = GNUNET_SCHEDULER_NO_TASK;
222   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
223     return;
224   po->dht_qe =
225       GNUNET_DATASTORE_get_zero_anonymity (GSF_dsh, po->current_offset++, 0,
226                                            UINT_MAX,
227                                            GNUNET_TIME_UNIT_FOREVER_REL,
228                                            po->dht_put_type,
229                                            &process_dht_put_content, po);
230   if (NULL == po->dht_qe)
231     po->dht_task = GNUNET_SCHEDULER_add_now (&delay_dht_put_task, po);
232 }
233
234
235 /**
236  * Setup the module.
237  */
238 void
239 GSF_put_init_ ()
240 {
241   unsigned int i;
242
243   i = 0;
244   while (operators[i].dht_put_type != GNUNET_BLOCK_TYPE_ANY)
245   {
246     operators[i].dht_task =
247         GNUNET_SCHEDULER_add_now (&gather_dht_put_blocks, &operators[i]);
248     i++;
249   }
250 }
251
252
253 /**
254  * Shutdown the module.
255  */
256 void
257 GSF_put_done_ ()
258 {
259   struct PutOperator *po;
260   unsigned int i;
261
262   i = 0;
263   while ((po = &operators[i])->dht_put_type != GNUNET_BLOCK_TYPE_ANY)
264   {
265     if (GNUNET_SCHEDULER_NO_TASK != po->dht_task)
266     {
267       GNUNET_SCHEDULER_cancel (po->dht_task);
268       po->dht_task = GNUNET_SCHEDULER_NO_TASK;
269     }
270     if (NULL != po->dht_put)
271     {
272       GNUNET_DHT_put_cancel (po->dht_put);
273       po->dht_put = NULL;
274     }
275     if (NULL != po->dht_qe)
276     {
277       GNUNET_DATASTORE_cancel (po->dht_qe);
278       po->dht_qe = NULL;
279     }
280     i++;
281   }
282 }
283
284 /* end of gnunet-service-fs_put.c */