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