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