first batch of license fixes (boring)
[oweals/gnunet.git] / src / arm / test_exponential_backoff.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15 /**
16  * @file arm/test_exponential_backoff.c
17  * @brief testcase for gnunet-service-arm.c
18  * @author Christian Grothoff
19  */
20 #include "platform.h"
21 #include "gnunet_arm_service.h"
22 #include "gnunet_util_lib.h"
23 #include "gnunet_protocols.h"
24
25 #define LOG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
26
27 #define LOG_BACKOFF GNUNET_NO
28
29 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
30
31 #define SERVICE_TEST_TIMEOUT GNUNET_TIME_UNIT_FOREVER_REL
32
33 #define FIVE_MILLISECONDS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5)
34
35 #define SERVICE "do-nothing"
36
37 #define BINARY "mockup-service"
38
39 #define CFGFILENAME "test_arm_api_data2.conf"
40
41
42 static const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44 static struct GNUNET_ARM_Handle *arm;
45
46 static struct GNUNET_ARM_MonitorHandle *mon;
47
48 static struct GNUNET_SCHEDULER_Task *kt;
49
50 static int ok = 1;
51
52 static int phase = 0;
53
54 static int trialCount;
55
56 static struct GNUNET_TIME_Absolute startedWaitingAt;
57
58 struct GNUNET_TIME_Relative waitedFor;
59
60 struct GNUNET_TIME_Relative waitedFor_prev;
61
62 #if LOG_BACKOFF
63 static FILE *killLogFilePtr;
64
65 static char *killLogFileName;
66 #endif
67
68
69 /**
70  * Context for handling the shutdown of a service.
71  */
72 struct ShutdownContext
73 {
74   /**
75    * Connection to the service that is being shutdown.
76    */
77   struct GNUNET_MQ_Handle *mq;
78
79   /**
80    * Task set up to cancel the shutdown request on timeout.
81    */
82   struct GNUNET_SCHEDULER_Task *cancel_task;
83
84 };
85
86
87 static void
88 kill_task (void *cbData);
89
90
91 /**
92  * Shutting down took too long, cancel receive and return error.
93  *
94  * @param cls closure
95  */
96 static void
97 service_shutdown_timeout (void *cls)
98 {
99   GNUNET_assert (0);
100 }
101
102
103 /**
104  * Generic error handler, called with the appropriate error code and
105  * the same closure specified at the creation of the message queue.
106  * Not every message queue implementation supports an error handler.
107  *
108  * @param cls closure with the `struct ShutdownContext *`
109  * @param error error code
110  */
111 static void
112 mq_error_handler (void *cls,
113                   enum GNUNET_MQ_Error error)
114 {
115   struct ShutdownContext *shutdown_ctx = cls;
116
117   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
118               "Service shutdown complete (MQ error).\n");
119   GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
120   GNUNET_MQ_destroy (shutdown_ctx->mq);
121   GNUNET_free (shutdown_ctx);
122 }
123
124
125 static void
126 kill_task (void *cbData)
127 {
128   struct ShutdownContext *shutdown_ctx
129     = GNUNET_new (struct ShutdownContext);
130   struct GNUNET_MQ_Envelope *env;
131   struct GNUNET_MessageHeader *msg;
132   struct GNUNET_MQ_MessageHandler handlers[] = {
133     GNUNET_MQ_handler_end ()
134   };
135
136   kt = NULL;
137   if (trialCount == 13)
138   {
139     LOG ("Saw enough kills, asking ARM to stop mock service for good\n");
140     GNUNET_ARM_request_service_stop (arm,
141                                      SERVICE,
142                                      NULL,
143                                      NULL);
144     ok = 0;
145     trialCount++;
146     GNUNET_free (shutdown_ctx);
147     return;
148   }
149   shutdown_ctx->mq = GNUNET_CLIENT_connect (cfg,
150                                             SERVICE,
151                                             handlers,
152                                             &mq_error_handler,
153                                             shutdown_ctx);
154   GNUNET_assert (NULL != shutdown_ctx->mq);
155   trialCount++;
156   LOG ("Sending a shutdown request to the mock service\n");
157   env = GNUNET_MQ_msg (msg,
158                        GNUNET_MESSAGE_TYPE_ARM_STOP); /* FIXME: abuse of message type */
159   GNUNET_MQ_send (shutdown_ctx->mq,
160                   env);
161   shutdown_ctx->cancel_task
162     = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
163                                     &service_shutdown_timeout,
164                                     shutdown_ctx);
165 }
166
167
168 static void
169 trigger_disconnect (void *cls)
170 {
171   GNUNET_ARM_disconnect (arm);
172   GNUNET_ARM_monitor_stop (mon);
173   if (NULL != kt)
174   {
175     GNUNET_SCHEDULER_cancel (kt);
176     kt = NULL;
177   }
178 }
179
180
181 static void
182 arm_stop_cb (void *cls,
183              enum GNUNET_ARM_RequestStatus status,
184              enum GNUNET_ARM_Result result)
185 {
186   GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
187   GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
188   LOG ("ARM service stopped\n");
189   GNUNET_SCHEDULER_shutdown ();
190 }
191
192
193 static void
194 srv_status (void *cls,
195             const char *service,
196             enum GNUNET_ARM_ServiceStatus status)
197 {
198   if (status == GNUNET_ARM_SERVICE_MONITORING_STARTED)
199   {
200     LOG ("ARM monitor started, starting mock service\n");
201     phase++;
202     GNUNET_ARM_request_service_start (arm,
203                                       SERVICE,
204                                       GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
205                                       NULL,
206                                       NULL);
207     return;
208   }
209   if (0 != strcasecmp (service, SERVICE))
210     return; /* not what we care about */
211   if (phase == 1)
212   {
213     GNUNET_break (status == GNUNET_ARM_SERVICE_STARTING);
214     GNUNET_break (phase == 1);
215     LOG ("do-nothing is starting\n");
216     phase++;
217     ok = 1;
218     GNUNET_assert (NULL == kt);
219     startedWaitingAt = GNUNET_TIME_absolute_get ();
220     kt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
221                                        &kill_task,
222                                        NULL);
223   }
224   else if (phase == 2)
225   {
226     /* We passively monitor ARM for status updates. ARM should tell us
227      * when do-nothing dies (no need to run a service upness test ourselves).
228      */
229     if (status == GNUNET_ARM_SERVICE_STARTING)
230     {
231       waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
232       LOG ("Waited for: %s\n",
233            GNUNET_STRINGS_relative_time_to_string (waitedFor,
234                                                    GNUNET_YES));
235
236       LOG ("do-nothing is starting, killing it...\n");
237       GNUNET_assert (NULL == kt);
238       kt = GNUNET_SCHEDULER_add_now (&kill_task, &ok);
239     }
240     else if ((status == GNUNET_ARM_SERVICE_STOPPED) && (trialCount == 14))
241     {
242       phase++;
243       LOG ("do-nothing stopped working %u times, we are done here\n",
244            (unsigned int) trialCount);
245       GNUNET_ARM_request_service_stop (arm,
246                                        "arm",
247                                        &arm_stop_cb,
248                                        NULL);
249     }
250   }
251 }
252
253
254 static void
255 arm_start_cb (void *cls,
256               enum GNUNET_ARM_RequestStatus status,
257               enum GNUNET_ARM_Result result)
258 {
259   GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
260   GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
261   GNUNET_break (phase == 0);
262   LOG ("Sent 'START' request for arm to ARM %s\n",
263        (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
264 }
265
266
267 static void
268 task (void *cls,
269       char *const *args,
270       const char *cfgfile,
271       const struct GNUNET_CONFIGURATION_Handle *c)
272 {
273   cfg = c;
274   arm = GNUNET_ARM_connect (cfg, NULL, NULL);
275   if (NULL == arm)
276   {
277     GNUNET_break (0);
278     return;
279   }
280   mon = GNUNET_ARM_monitor_start (cfg,
281                                   &srv_status,
282                                   NULL);
283   if (NULL == mon)
284   {
285     GNUNET_break (0);
286     GNUNET_ARM_disconnect (arm);
287     arm = NULL;
288     return;
289   }
290   GNUNET_ARM_request_service_start (arm,
291                                     "arm",
292                                     GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
293                                     &arm_start_cb,
294                                     NULL);
295   GNUNET_SCHEDULER_add_shutdown (&trigger_disconnect,
296                                  NULL);
297 }
298
299
300 static int
301 check ()
302 {
303   char *const argv[] = {
304     "test-exponential-backoff",
305     "-c", CFGFILENAME,
306     NULL
307   };
308   struct GNUNET_GETOPT_CommandLineOption options[] = {
309     GNUNET_GETOPT_OPTION_END
310   };
311
312   /* Running ARM  and running the do_nothing task */
313   GNUNET_assert (GNUNET_OK ==
314                  GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
315                                      argv,
316                                      "test-exponential-backoff",
317                                      "nohelp",
318                                      options,
319                                      &task,
320                                      NULL));
321   return ok;
322 }
323
324
325 #ifndef PATH_MAX
326 /**
327  * Assumed maximum path length (for the log file name).
328  */
329 #define PATH_MAX 4096
330 #endif
331
332
333 static int
334 init ()
335 {
336   struct GNUNET_CONFIGURATION_Handle *cfg;
337   char pwd[PATH_MAX];
338   char *binary;
339
340   cfg = GNUNET_CONFIGURATION_create ();
341   if (GNUNET_OK != GNUNET_CONFIGURATION_parse (cfg,
342                                                "test_arm_api_data.conf"))
343     return GNUNET_SYSERR;
344   if (NULL == getcwd (pwd, PATH_MAX))
345     return GNUNET_SYSERR;
346   GNUNET_assert (0 < GNUNET_asprintf (&binary,
347                                       "%s/%s",
348                                       pwd,
349                                       BINARY));
350   GNUNET_CONFIGURATION_set_value_string (cfg,
351                                          SERVICE,
352                                          "BINARY",
353                                          binary);
354   GNUNET_free (binary);
355   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg,
356                                                CFGFILENAME))
357   {
358     GNUNET_CONFIGURATION_destroy (cfg);
359     return GNUNET_SYSERR;
360   }
361   GNUNET_CONFIGURATION_destroy (cfg);
362
363 #if LOG_BACKOFF
364   killLogFileName = GNUNET_DISK_mktemp ("exponential-backoff-waiting.log");
365   if (NULL == (killLogFilePtr = FOPEN (killLogFileName,
366                                        "w")))
367     {
368       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
369                                 "fopen",
370                                 killLogFileName);
371       GNUNET_free (killLogFileName);
372       return GNUNET_SYSERR;
373     }
374 #endif
375   return GNUNET_OK;
376 }
377
378
379 static void
380 houseKeep ()
381 {
382 #if LOG_BACKOFF
383   GNUNET_assert (0 == fclose (killLogFilePtr));
384   GNUNET_free (killLogFileName);
385 #endif
386   (void) unlink (CFGFILENAME);
387 }
388
389
390 int
391 main (int argc, char *argv[])
392 {
393   int ret;
394
395   GNUNET_log_setup ("test-exponential-backoff",
396                     "WARNING",
397                     NULL);
398
399   if (GNUNET_OK != init ())
400     return 1;
401   ret = check ();
402   houseKeep ();
403   return ret;
404 }
405
406 /* end of test_exponential_backoff.c */