aboutsummaryrefslogtreecommitdiff
path: root/src/service/arm
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/arm')
-rw-r--r--src/service/arm/.gitignore7
-rw-r--r--src/service/arm/Makefile.am96
-rw-r--r--src/service/arm/arm.conf.in60
-rw-r--r--src/service/arm/arm.h164
-rw-r--r--src/service/arm/arm_api.c1113
-rw-r--r--src/service/arm/arm_monitor_api.c274
-rw-r--r--src/service/arm/gnunet-service-arm.c2227
-rw-r--r--src/service/arm/meson.build74
-rw-r--r--src/service/arm/mockup-service.c123
-rwxr-xr-xsrc/service/arm/mockup_service130
-rw-r--r--src/service/arm/test_arm_api.c258
-rw-r--r--src/service/arm/test_arm_api_data.conf38
-rw-r--r--src/service/arm/test_exponential_backoff.c416
-rw-r--r--src/service/arm/test_gnunet_arm.py.in129
-rw-r--r--src/service/arm/test_gnunet_service_arm.c266
-rw-r--r--src/service/arm/testing_arm_cmd_start_peer.c293
-rw-r--r--src/service/arm/testing_arm_cmd_stop_peer.c174
-rw-r--r--src/service/arm/testing_arm_traits.c33
18 files changed, 5875 insertions, 0 deletions
diff --git a/src/service/arm/.gitignore b/src/service/arm/.gitignore
new file mode 100644
index 000000000..b1d66852b
--- /dev/null
+++ b/src/service/arm/.gitignore
@@ -0,0 +1,7 @@
1arm.conf
2mockup-service
3gnunet-service-arm
4test_arm_api
5test_exponential_backoff
6test_gnunet_arm.py
7test_gnunet_service_arm
diff --git a/src/service/arm/Makefile.am b/src/service/arm/Makefile.am
new file mode 100644
index 000000000..26c940688
--- /dev/null
+++ b/src/service/arm/Makefile.am
@@ -0,0 +1,96 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 arm.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = --coverage -O0
13 XLIB = -lgcov
14endif
15
16lib_LTLIBRARIES = \
17 libgnunetarm.la \
18 libgnunettestingarm.la
19
20libgnunetarm_la_SOURCES = \
21 arm_api.c \
22 arm_monitor_api.c \
23 arm.h
24libgnunetarm_la_LIBADD = \
25 $(top_builddir)/src/lib/util/libgnunetutil.la \
26 $(GN_LIBINTL) $(XLIB)
27libgnunetarm_la_LDFLAGS = \
28 $(GN_LIB_LDFLAGS) \
29 -version-info 2:0:0
30
31libgnunettestingarm_la_SOURCES = \
32 testing_arm_cmd_start_peer.c \
33 testing_arm_cmd_stop_peer.c \
34 testing_arm_traits.c
35libgnunettestingarm_la_LIBADD = \
36 libgnunetarm.la \
37 $(top_builddir)/src/service/testbed/libgnunettestingtestbed.la \
38 $(top_builddir)/src/service/testbed/libgnunettestbed.la \
39 $(top_builddir)/src/lib/testing/libgnunettesting.la \
40 $(top_builddir)/src/lib/util/libgnunetutil.la \
41 $(GN_LIBINTL) $(XLIB)
42libgnunettestingarm_la_LDFLAGS = \
43 $(GN_LIB_LDFLAGS) \
44 -version-info 0:0:0
45
46
47noinst_PROGRAMS = \
48 mockup-service
49
50libexec_PROGRAMS = \
51 gnunet-service-arm
52
53gnunet_service_arm_SOURCES = \
54 gnunet-service-arm.c
55gnunet_service_arm_LDADD = \
56 $(top_builddir)/src/lib/util/libgnunetutil.la \
57 $(GN_LIBINTL)
58
59mockup_service_SOURCES = \
60 mockup-service.c
61 mockup_service_LDADD = \
62 $(top_builddir)/src/lib/util/libgnunetutil.la
63mockup_service_LDFLAGS = \
64 $(GN_LIBINTL)
65
66
67check_PROGRAMS = \
68 test_arm_api \
69 test_exponential_backoff \
70 test_gnunet_service_arm
71
72if ENABLE_TEST_RUN
73AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
74TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
75endif
76
77test_arm_api_SOURCES = \
78 test_arm_api.c
79test_arm_api_LDADD = \
80 libgnunetarm.la \
81 $(top_builddir)/src/lib/util/libgnunetutil.la
82
83test_exponential_backoff_SOURCES = \
84 test_exponential_backoff.c
85test_exponential_backoff_LDADD = \
86 libgnunetarm.la \
87 $(top_builddir)/src/lib/util/libgnunetutil.la
88
89test_gnunet_service_arm_SOURCES = \
90 test_gnunet_service_arm.c
91 test_gnunet_service_arm_LDADD = \
92 libgnunetarm.la \
93 $(top_builddir)/src/lib/util/libgnunetutil.la
94
95EXTRA_DIST = \
96 test_arm_api_data.conf
diff --git a/src/service/arm/arm.conf.in b/src/service/arm/arm.conf.in
new file mode 100644
index 000000000..8015e0e15
--- /dev/null
+++ b/src/service/arm/arm.conf.in
@@ -0,0 +1,60 @@
1[arm]
2@UNIXONLY@ PORT = 2087
3HOSTNAME = localhost
4BINARY = gnunet-service-arm
5ACCEPT_FROM = 127.0.0.1;
6ACCEPT_FROM6 = ::1;
7
8# Special case, uses user runtime dir even for per-system service.
9UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-arm.sock
10UNIX_MATCH_UID = YES
11UNIX_MATCH_GID = YES
12
13# In the "-l" option, format characters from 'strftime' are allowed;
14# In the GLOBAL_POSTFIX, "{}" stands for the name of the respective
15# service. Thus the following option would introduce per-service
16# logging with a new log file each day. Note that only the last 3
17# log files are preserved.
18# GLOBAL_POSTFIX = -l $GNUNET_CACHE_HOME/{}-%Y-%m-%d.log
19GLOBAL_PREFIX =
20
21# By default, when launching GNUnet via gnunet-arm, log
22# everything to a simple flat log file.
23OPTIONS = -l $GNUNET_CACHE_HOME/gnunet-%Y-%m-%d.log
24
25# If set to YES, ARM will only start services that are marked as
26# system-level services (and we'll expect a second ARM to be
27# run per-user to run user-level services). Note that in this
28# case you must have manually created a different configuration
29# file with the user where at least this and the START_USER_SERVICES
30# options differ.
31# START_SYSTEM_SERVICES = YES
32
33# If set to YES, ARM will only start services that are marked as
34# per-user services (and we'll expect a system user to run ARM to
35# provide system-level services). Per-user services enable
36# better personalization and privilege separation and in particular
37# ensures that personal data is stored under $HOME, which might
38# be important in a multi-user system (or if $HOME is encrypted
39# and /var/ is not).
40#
41# Note that if you have different ARM services for SYSTEM and USER,
42# and you are not on UNIX, you need to change the PORT option for the
43# USER ARM instances to some free port (counting down from 2085 should
44# be sane).
45#
46# START_USER_SERVICES = YES
47
48# File where we should log per-service resource consumption on exit.
49# RESOURCE_DIAGNOSTICS = resource.log
50
51
52# Name of the user that will be used to provide the service
53# USERNAME =
54# MAXBUF =
55# TIMEOUT =
56# DISABLEV6 =
57# BINDTO =
58# REJECT_FROM =
59# REJECT_FROM6 =
60# PREFIX =
diff --git a/src/service/arm/arm.h b/src/service/arm/arm.h
new file mode 100644
index 000000000..8888e0105
--- /dev/null
+++ b/src/service/arm/arm.h
@@ -0,0 +1,164 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 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/**
22 * @author Christian Grothoff
23 * @file arm/arm.h
24 */
25#ifndef ARM_H
26#define ARM_H
27
28#include "gnunet_common.h"
29
30/**
31 * This option will turn on the DEBUG loglevel for
32 * all processes controlled by this ARM!
33 */
34#define DEBUG_ARM GNUNET_EXTRA_LOGGING
35
36GNUNET_NETWORK_STRUCT_BEGIN
37
38/**
39 * Status update from ARM to client.
40 */
41struct GNUNET_ARM_StatusMessage
42{
43 /**
44 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_STATUS.
45 */
46 struct GNUNET_MessageHeader header;
47
48 /**
49 * Status from the 'enum GNUNET_ARM_ServiceStatus'
50 */
51 uint32_t status;
52
53 /* followed by a 0-terminated service name */
54};
55
56struct GNUNET_ARM_Message
57{
58 /**
59 * Reply to client, type is #GNUNET_MESSAGE_TYPE_ARM_RESULT or
60 * #GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT.
61 * OR
62 * Request from client, type is #GNUNET_MESSAGE_TYPE_ARM_START or
63 * #GNUNET_MESSAGE_TYPE_ARM_STOP.
64 */
65 struct GNUNET_MessageHeader header;
66
67 /**
68 * For alignment.
69 */
70 uint32_t reserved;
71
72 /**
73 * ID of a request that is being replied to.
74 * OR
75 * ID of a request that is being sent.
76 */
77 uint64_t request_id;
78
79 /* For requests - followed by a 0-terminated service name */
80};
81
82
83/**
84 * Reply from ARM to client.
85 */
86struct GNUNET_ARM_ResultMessage
87{
88 /**
89 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_RESULT, with an ID.
90 */
91 struct GNUNET_ARM_Message arm_msg;
92
93 /**
94 * Result from the `enum GNUNET_ARM_Result`
95 */
96 uint32_t result;
97};
98
99struct GNUNET_ARM_ServiceInfoMessage
100{
101 /**
102 * String pool index for the service's name.
103 */
104 uint16_t name_index;
105
106 /**
107 * String pool index for the service's binary.
108 */
109 uint16_t binary_index;
110
111 /**
112 * Last process exit status.
113 */
114 int16_t last_exit_status;
115
116 /**
117 * Padding.
118 */
119 uint16_t padding;
120
121 /**
122 * Status from the 'enum GNUNET_ARM_ServiceStatus'
123 */
124 uint32_t status;
125
126 /**
127 * Time when the service will be restarted, if applicable
128 * to the current status.
129 */
130 struct GNUNET_TIME_AbsoluteNBO restart_at;
131
132 /**
133 * Time when the service was first started, if applicable.
134 */
135 struct GNUNET_TIME_AbsoluteNBO last_started_at;
136};
137
138/**
139 * Reply from ARM to client for the
140 * #GNUNET_MESSAGE_TYPE_ARM_LIST request followed by count
141 * '\0' terminated strings. header->size contains the
142 * total size (including all strings).
143 */
144struct GNUNET_ARM_ListResultMessage
145{
146 /**
147 * Reply to client, of type is #GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
148 * with an ID.
149 */
150 struct GNUNET_ARM_Message arm_msg;
151
152 /**
153 * Number of 'struct GNUNET_ARM_ServiceInfoMessage' that
154 * are at the end of this message.
155 */
156 uint16_t count;
157
158 /* struct GNUNET_ARM_ServiceInfoMessage[count]; */
159 /* pool of 0-terminated strings */
160};
161
162GNUNET_NETWORK_STRUCT_END
163
164#endif
diff --git a/src/service/arm/arm_api.c b/src/service/arm/arm_api.c
new file mode 100644
index 000000000..bc5efbd3f
--- /dev/null
+++ b/src/service/arm/arm_api.c
@@ -0,0 +1,1113 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013, 2016, 2019 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/**
22 * @file arm/arm_api.c
23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff
25 * @author LRN
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_arm_service.h"
30#include "gnunet_protocols.h"
31#include "arm.h"
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "arm-api", __VA_ARGS__)
34
35
36/**
37 * Entry in a doubly-linked list of operations awaiting for replies
38 * (in-order) from the ARM service.
39 */
40struct GNUNET_ARM_Operation
41{
42 /**
43 * This is a doubly-linked list.
44 */
45 struct GNUNET_ARM_Operation *next;
46
47 /**
48 * This is a doubly-linked list.
49 */
50 struct GNUNET_ARM_Operation *prev;
51
52 /**
53 * ARM handle.
54 */
55 struct GNUNET_ARM_Handle *h;
56
57 /**
58 * Callback for service state change requests.
59 */
60 GNUNET_ARM_ResultCallback result_cont;
61
62 /**
63 * Callback for service list requests.
64 */
65 GNUNET_ARM_ServiceListCallback list_cont;
66
67 /**
68 * Closure for @e result_cont or @e list_cont.
69 */
70 void *cont_cls;
71
72 /**
73 * Task for async completion.
74 */
75 struct GNUNET_SCHEDULER_Task *async;
76
77 /**
78 * Unique ID for the request.
79 */
80 uint64_t id;
81
82 /**
83 * Result of this operation for #notify_starting().
84 */
85 enum GNUNET_ARM_Result starting_ret;
86
87 /**
88 * File descriptor to close on operation stop, if not NULL.
89 */
90 struct GNUNET_DISK_FileHandle *rfd;
91
92 /**
93 * Is this an operation to stop the ARM service?
94 */
95 int is_arm_stop;
96};
97
98
99/**
100 * Handle for interacting with ARM.
101 */
102struct GNUNET_ARM_Handle
103{
104 /**
105 * Our connection to the ARM service.
106 */
107 struct GNUNET_MQ_Handle *mq;
108
109 /**
110 * The configuration that we are using.
111 */
112 const struct GNUNET_CONFIGURATION_Handle *cfg;
113
114 /**
115 * Head of doubly-linked list of pending operations.
116 */
117 struct GNUNET_ARM_Operation *operation_pending_head;
118
119 /**
120 * Tail of doubly-linked list of pending operations.
121 */
122 struct GNUNET_ARM_Operation *operation_pending_tail;
123
124 /**
125 * Callback to invoke on connection/disconnection.
126 */
127 GNUNET_ARM_ConnectionStatusCallback conn_status;
128
129 /**
130 * Closure for @e conn_status.
131 */
132 void *conn_status_cls;
133
134 /**
135 * ARM operation where the goal is to wait for ARM shutdown to
136 * complete. This operation is special in that it waits for an
137 * error on the @e mq. So we complete it by calling the
138 * continuation in the #mq_error_handler(). Note that the operation
139 * is no longer in the @e operation_pending_head DLL once it is
140 * referenced from this field.
141 */
142 struct GNUNET_ARM_Operation *thm;
143
144 /**
145 * ID of the reconnect task (if any).
146 */
147 struct GNUNET_SCHEDULER_Task *reconnect_task;
148
149 /**
150 * Current delay we use for re-trying to connect to core.
151 */
152 struct GNUNET_TIME_Relative retry_backoff;
153
154 /**
155 * Counter for request identifiers. They are used to match replies
156 * from ARM to operations in the @e operation_pending_head DLL.
157 */
158 uint64_t request_id_counter;
159
160 /**
161 * Have we detected that ARM is up?
162 */
163 int currently_up;
164};
165
166
167/**
168 * Connect to arm.
169 *
170 * @param h arm handle
171 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
172 */
173static int
174reconnect_arm (struct GNUNET_ARM_Handle *h);
175
176
177/**
178 * Task scheduled to try to re-connect to arm.
179 *
180 * @param cls the `struct GNUNET_ARM_Handle`
181 */
182static void
183reconnect_arm_task (void *cls)
184{
185 struct GNUNET_ARM_Handle *h = cls;
186
187 h->reconnect_task = NULL;
188 reconnect_arm (h);
189}
190
191
192/**
193 * Close down any existing connection to the ARM service and
194 * try re-establishing it later.
195 *
196 * @param h our handle
197 */
198static void
199reconnect_arm_later (struct GNUNET_ARM_Handle *h)
200{
201 struct GNUNET_ARM_Operation *op;
202
203 if (NULL != h->mq)
204 {
205 GNUNET_MQ_destroy (h->mq);
206 h->mq = NULL;
207 }
208 h->currently_up = GNUNET_NO;
209 GNUNET_assert (NULL == h->reconnect_task);
210 h->reconnect_task =
211 GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
212 &reconnect_arm_task,
213 h);
214 while (NULL != (op = h->operation_pending_head))
215 {
216 if (NULL != op->result_cont)
217 op->result_cont (op->cont_cls,
218 GNUNET_ARM_REQUEST_DISCONNECTED,
219 0);
220 if (NULL != op->list_cont)
221 op->list_cont (op->cont_cls,
222 GNUNET_ARM_REQUEST_DISCONNECTED,
223 0,
224 NULL);
225 GNUNET_ARM_operation_cancel (op);
226 }
227 GNUNET_assert (NULL == h->operation_pending_head);
228 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
229 if (NULL != h->conn_status)
230 h->conn_status (h->conn_status_cls,
231 GNUNET_NO);
232}
233
234
235/**
236 * Find a control message by its unique ID.
237 *
238 * @param h ARM handle
239 * @param id unique message ID to use for the lookup
240 * @return NULL if not found
241 */
242static struct GNUNET_ARM_Operation *
243find_op_by_id (struct GNUNET_ARM_Handle *h,
244 uint64_t id)
245{
246 for (struct GNUNET_ARM_Operation *result = h->operation_pending_head;
247 NULL != result;
248 result = result->next)
249 if (id == result->id)
250 return result;
251 return NULL;
252}
253
254
255/**
256 * Handler for ARM replies.
257 *
258 * @param cls our `struct GNUNET_ARM_Handle`
259 * @param res the message received from the arm service
260 */
261static void
262handle_arm_result (void *cls,
263 const struct GNUNET_ARM_ResultMessage *res)
264{
265 struct GNUNET_ARM_Handle *h = cls;
266 struct GNUNET_ARM_Operation *op;
267 uint64_t id;
268 enum GNUNET_ARM_Result result;
269 GNUNET_ARM_ResultCallback result_cont;
270 void *result_cont_cls;
271
272 id = GNUNET_ntohll (res->arm_msg.request_id);
273 op = find_op_by_id (h,
274 id);
275 if (NULL == op)
276 {
277 LOG (GNUNET_ERROR_TYPE_DEBUG,
278 "Message with unknown id %llu\n",
279 (unsigned long long) id);
280 return;
281 }
282
283 result = (enum GNUNET_ARM_Result) ntohl (res->result);
284 if ( (GNUNET_YES == op->is_arm_stop) &&
285 (GNUNET_ARM_RESULT_STOPPING == result) )
286 {
287 /* special case: if we are stopping 'gnunet-service-arm', we do not just
288 wait for the result message, but also wait for the service to close
289 the connection (and then we have to close our client handle as well);
290 this is done by installing a different receive handler, waiting for
291 the connection to go down */if (NULL != h->thm)
292 {
293 GNUNET_break (0);
294 op->result_cont (h->thm->cont_cls,
295 GNUNET_ARM_REQUEST_SENT_OK,
296 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
297 GNUNET_free (h->thm);
298 }
299 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
300 h->operation_pending_tail,
301 op);
302 h->thm = op;
303 return;
304 }
305 result_cont = op->result_cont;
306 result_cont_cls = op->cont_cls;
307 GNUNET_ARM_operation_cancel (op);
308 if (NULL != result_cont)
309 result_cont (result_cont_cls,
310 GNUNET_ARM_REQUEST_SENT_OK,
311 result);
312}
313
314
315/**
316 * Read from a string pool.
317 *
318 * @param pool_start start of the string pool
319 * @param pool_size size of the string pool
320 * @param str_index index into the string pool
321 * @returns an index into the string pool, or
322 * NULL if the index is out of bounds
323 */
324static const char *
325pool_get (const char *pool_start,
326 size_t pool_size,
327 size_t str_index)
328{
329 const char *str_start;
330 const char *end;
331
332 if (str_index >= pool_size)
333 return NULL;
334 str_start = pool_start + str_index;
335 end = memchr (str_start, 0, pool_size - str_index);
336 if (NULL == end)
337 return NULL;
338 return str_start;
339}
340
341
342/**
343 * Check that list result message is well-formed.
344 *
345 * @param cls our `struct GNUNET_ARM_Handle`
346 * @param lres the message received from the arm service
347 * @return #GNUNET_OK if message is well-formed
348 */
349static enum GNUNET_GenericReturnValue
350check_arm_list_result (void *cls,
351 const struct GNUNET_ARM_ListResultMessage *lres)
352{
353 uint16_t rcount = ntohs (lres->count);
354 uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
355 struct GNUNET_ARM_ServiceInfoMessage *ssm;
356 size_t pool_size;
357 char *pool_start;
358
359 (void) cls;
360 if ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) > msize))
361 {
362 GNUNET_break_op (0);
363 return GNUNET_NO;
364 }
365 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
366 pool_start = (char *) (ssm + rcount);
367 pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
368 for (unsigned int i = 0; i < rcount; i++)
369 {
370 uint16_t name_index = ntohs (ssm->name_index);
371 uint16_t binary_index = ntohs (ssm->binary_index);
372 if (NULL == pool_get (pool_start,
373 pool_size,
374 name_index))
375 {
376 GNUNET_break_op (0);
377 return GNUNET_NO;
378 }
379 if (NULL == pool_get (pool_start,
380 pool_size,
381 binary_index))
382 {
383 GNUNET_break_op (0);
384 return GNUNET_NO;
385 }
386 ssm++;
387 }
388 return GNUNET_OK;
389}
390
391
392/**
393 * Handler for ARM list replies.
394 *
395 * @param cls our `struct GNUNET_ARM_Handle`
396 * @param lres the message received from the arm service
397 */
398static void
399handle_arm_list_result (void *cls,
400 const struct GNUNET_ARM_ListResultMessage *lres)
401{
402 struct GNUNET_ARM_Handle *h = cls;
403 uint16_t rcount = ntohs (lres->count);
404 uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
405 struct GNUNET_ARM_ServiceInfo list[rcount];
406 struct GNUNET_ARM_ServiceInfoMessage *ssm;
407 struct GNUNET_ARM_Operation *op;
408 uint64_t id;
409 size_t pool_size;
410 char *pool_start;
411
412 id = GNUNET_ntohll (lres->arm_msg.request_id);
413 op = find_op_by_id (h, id);
414 if (NULL == op)
415 {
416 LOG (GNUNET_ERROR_TYPE_DEBUG,
417 "Message with unknown id %llu\n",
418 (unsigned long long) id);
419 return;
420 }
421
422 GNUNET_assert ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) <=
423 msize));
424
425 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
426 pool_start = (char *) (ssm + rcount);
427 pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
428
429 for (unsigned int i = 0; i < rcount; i++)
430 {
431 uint16_t name_index = ntohs (ssm->name_index);
432 uint16_t binary_index = ntohs (ssm->binary_index);
433 const char *name;
434 const char *binary;
435
436 name = pool_get (pool_start, pool_size, name_index);
437 binary = pool_get (pool_start, pool_size, binary_index);
438 GNUNET_assert (NULL != name);
439 GNUNET_assert (NULL != binary);
440 list[i] = (struct GNUNET_ARM_ServiceInfo) {
441 .name = name,
442 .binary = binary,
443 .status = ntohl (ssm->status),
444 .last_started_at = GNUNET_TIME_absolute_ntoh (ssm->last_started_at),
445 .restart_at = GNUNET_TIME_absolute_ntoh (ssm->restart_at),
446 .last_exit_status = ntohs (ssm->last_exit_status),
447 };
448 ssm++;
449 }
450 if (NULL != op->list_cont)
451 op->list_cont (op->cont_cls,
452 GNUNET_ARM_REQUEST_SENT_OK,
453 rcount,
454 list);
455 GNUNET_ARM_operation_cancel (op);
456}
457
458
459/**
460 * Receive confirmation from test, ARM service is up.
461 *
462 * @param cls closure with the `struct GNUNET_ARM_Handle`
463 * @param msg message received
464 */
465static void
466handle_confirm (void *cls,
467 const struct GNUNET_MessageHeader *msg)
468{
469 struct GNUNET_ARM_Handle *h = cls;
470
471 (void) msg;
472 LOG (GNUNET_ERROR_TYPE_DEBUG,
473 "Got confirmation from ARM that we are up!\n");
474 if (GNUNET_NO == h->currently_up)
475 {
476 h->currently_up = GNUNET_YES;
477 if (NULL != h->conn_status)
478 h->conn_status (h->conn_status_cls, GNUNET_YES);
479 }
480}
481
482
483/**
484 * Generic error handler, called with the appropriate error code and
485 * the same closure specified at the creation of the message queue.
486 * Not every message queue implementation supports an error handler.
487 *
488 * @param cls closure with the `struct GNUNET_ARM_Handle *`
489 * @param error error code
490 */
491static void
492mq_error_handler (void *cls,
493 enum GNUNET_MQ_Error error)
494{
495 struct GNUNET_ARM_Handle *h = cls;
496 struct GNUNET_ARM_Operation *op;
497
498 (void) error;
499 h->currently_up = GNUNET_NO;
500 if (NULL != (op = h->thm))
501 {
502 h->thm = NULL;
503 op->result_cont (op->cont_cls,
504 GNUNET_ARM_REQUEST_SENT_OK,
505 GNUNET_ARM_RESULT_STOPPED);
506 GNUNET_free (op);
507 }
508 reconnect_arm_later (h);
509}
510
511
512/**
513 * Connect to arm.
514 *
515 * @param h arm handle
516 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
517 */
518static enum GNUNET_GenericReturnValue
519reconnect_arm (struct GNUNET_ARM_Handle *h)
520{
521 struct GNUNET_MQ_MessageHandler handlers[] = {
522 GNUNET_MQ_hd_fixed_size (arm_result,
523 GNUNET_MESSAGE_TYPE_ARM_RESULT,
524 struct GNUNET_ARM_ResultMessage,
525 h),
526 GNUNET_MQ_hd_var_size (arm_list_result,
527 GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
528 struct GNUNET_ARM_ListResultMessage,
529 h),
530 GNUNET_MQ_hd_fixed_size (confirm,
531 GNUNET_MESSAGE_TYPE_ARM_TEST,
532 struct GNUNET_MessageHeader,
533 h),
534 GNUNET_MQ_handler_end ()
535 };
536 struct GNUNET_MessageHeader *test;
537 struct GNUNET_MQ_Envelope *env;
538
539 if (NULL != h->mq)
540 return GNUNET_OK;
541 GNUNET_assert (GNUNET_NO == h->currently_up);
542 h->mq = GNUNET_CLIENT_connect (h->cfg,
543 "arm",
544 handlers,
545 &mq_error_handler,
546 h);
547 if (NULL == h->mq)
548 {
549 LOG (GNUNET_ERROR_TYPE_DEBUG,
550 "GNUNET_CLIENT_connect returned NULL\n");
551 if (NULL != h->conn_status)
552 h->conn_status (h->conn_status_cls,
553 GNUNET_SYSERR);
554 return GNUNET_SYSERR;
555 }
556 LOG (GNUNET_ERROR_TYPE_DEBUG,
557 "Sending TEST message to ARM\n");
558 env = GNUNET_MQ_msg (test,
559 GNUNET_MESSAGE_TYPE_ARM_TEST);
560 GNUNET_MQ_send (h->mq, env);
561 return GNUNET_OK;
562}
563
564
565/**
566 * Set up a context for communicating with ARM, then
567 * start connecting to the ARM service using that context.
568 *
569 * @param cfg configuration to use (needed to contact ARM;
570 * the ARM service may internally use a different
571 * configuration to determine how to start the service).
572 * @param conn_status will be called when connecting/disconnecting
573 * @param conn_status_cls closure for @a conn_status
574 * @return context to use for further ARM operations, NULL on error.
575 */
576struct GNUNET_ARM_Handle *
577GNUNET_ARM_connect (
578 const struct GNUNET_CONFIGURATION_Handle *cfg,
579 GNUNET_ARM_ConnectionStatusCallback conn_status,
580 void *conn_status_cls)
581{
582 struct GNUNET_ARM_Handle *h;
583
584 h = GNUNET_new (struct GNUNET_ARM_Handle);
585 h->cfg = cfg;
586 h->conn_status = conn_status;
587 h->conn_status_cls = conn_status_cls;
588 if (GNUNET_OK != reconnect_arm (h))
589 {
590 GNUNET_free (h);
591 return NULL;
592 }
593 return h;
594}
595
596
597/**
598 * Disconnect from the ARM service (if connected) and destroy the context.
599 *
600 * @param h the handle that was being used
601 */
602void
603GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
604{
605 struct GNUNET_ARM_Operation *op;
606
607 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
608 while (NULL != (op = h->operation_pending_head))
609 {
610 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
611 h->operation_pending_tail,
612 op);
613 if (NULL != op->result_cont)
614 op->result_cont (op->cont_cls,
615 GNUNET_ARM_REQUEST_DISCONNECTED,
616 0);
617 if (NULL != op->list_cont)
618 op->list_cont (op->cont_cls,
619 GNUNET_ARM_REQUEST_DISCONNECTED,
620 0,
621 NULL);
622 if (NULL != op->async)
623 {
624 GNUNET_SCHEDULER_cancel (op->async);
625 op->async = NULL;
626 }
627 GNUNET_free (op);
628 }
629 if (NULL != h->mq)
630 {
631 GNUNET_MQ_destroy (h->mq);
632 h->mq = NULL;
633 }
634 if (NULL != h->reconnect_task)
635 {
636 GNUNET_SCHEDULER_cancel (h->reconnect_task);
637 h->reconnect_task = NULL;
638 }
639 GNUNET_free (h);
640}
641
642
643/**
644 * A client specifically requested starting of ARM itself.
645 * Starts the ARM service.
646 *
647 * @param h the handle with configuration details
648 * @param std_inheritance inheritance of std streams
649 * @param sigfd socket to pass to ARM for signalling
650 * @return operation status code
651 */
652static enum GNUNET_ARM_Result
653start_arm_service (struct GNUNET_ARM_Handle *h,
654 enum GNUNET_OS_InheritStdioFlags std_inheritance,
655 struct GNUNET_DISK_FileHandle *sigfd)
656{
657 struct GNUNET_OS_Process *proc;
658 char *cbinary;
659 char *binary;
660 char *quotedbinary;
661 char *config;
662 char *loprefix;
663 char *lopostfix;
664 int ld[2];
665 int *lsocks;
666
667 if (NULL == sigfd)
668 {
669 lsocks = NULL;
670 }
671 else
672 {
673 ld[0] = sigfd->fd;
674 ld[1] = -1;
675 lsocks = ld;
676 }
677 if (GNUNET_OK !=
678 GNUNET_CONFIGURATION_get_value_string (h->cfg,
679 "arm",
680 "PREFIX",
681 &loprefix))
682 loprefix = GNUNET_strdup ("");
683 else
684 loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
685 if (GNUNET_OK !=
686 GNUNET_CONFIGURATION_get_value_string (h->cfg,
687 "arm",
688 "OPTIONS",
689 &lopostfix))
690 lopostfix = GNUNET_strdup ("");
691 else
692 lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
693 lopostfix);
694 if (GNUNET_OK !=
695 GNUNET_CONFIGURATION_get_value_string (h->cfg,
696 "arm",
697 "BINARY",
698 &cbinary))
699 {
700 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
701 "arm",
702 "BINARY");
703 GNUNET_free (loprefix);
704 GNUNET_free (lopostfix);
705 return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
706 }
707 if (GNUNET_OK !=
708 GNUNET_CONFIGURATION_get_value_filename (h->cfg,
709 "arm",
710 "CONFIG",
711 &config))
712 config = NULL;
713 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
714 GNUNET_asprintf (&quotedbinary,
715 "\"%s\"",
716 binary);
717 GNUNET_free (cbinary);
718 if ( (GNUNET_YES ==
719 GNUNET_CONFIGURATION_have_value (h->cfg,
720 "TESTING",
721 "WEAKRANDOM")) &&
722 (GNUNET_YES ==
723 GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
724 "TESTING",
725 "WEAKRANDOM")) &&
726 (GNUNET_NO ==
727 GNUNET_CONFIGURATION_have_value (h->cfg,
728 "TESTING",
729 "HOSTFILE")) )
730 {
731 /* Means we are ONLY running locally */
732 /* we're clearly running a test, don't daemonize */
733 if (NULL == config)
734 proc = GNUNET_OS_start_process_s (std_inheritance,
735 lsocks,
736 loprefix,
737 quotedbinary,
738 /* no daemonization! */
739 lopostfix,
740 NULL);
741 else
742 proc = GNUNET_OS_start_process_s (std_inheritance,
743 lsocks,
744 loprefix,
745 quotedbinary,
746 "-c",
747 config,
748 /* no daemonization! */
749 lopostfix,
750 NULL);
751 }
752 else
753 {
754 if (NULL == config)
755 proc = GNUNET_OS_start_process_s (std_inheritance,
756 lsocks,
757 loprefix,
758 quotedbinary,
759 "-d", /* do daemonize */
760 lopostfix,
761 NULL);
762 else
763 proc = GNUNET_OS_start_process_s (std_inheritance,
764 lsocks,
765 loprefix,
766 quotedbinary,
767 "-c",
768 config,
769 "-d", /* do daemonize */
770 lopostfix,
771 NULL);
772 }
773 GNUNET_free (binary);
774 GNUNET_free (quotedbinary);
775 if (NULL != config)
776 GNUNET_free (config);
777 GNUNET_free (loprefix);
778 GNUNET_free (lopostfix);
779 if (NULL == proc)
780 return GNUNET_ARM_RESULT_START_FAILED;
781 GNUNET_OS_process_destroy (proc);
782 return GNUNET_ARM_RESULT_STARTING;
783}
784
785
786/**
787 * Abort an operation. Only prevents the callback from being
788 * called, the operation may still complete.
789 *
790 * @param op operation to cancel
791 */
792void
793GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
794{
795 struct GNUNET_ARM_Handle *h = op->h;
796
797 if (NULL != op->async)
798 {
799 GNUNET_SCHEDULER_cancel (op->async);
800 op->async = NULL;
801 }
802 if (NULL != op->rfd)
803 {
804 GNUNET_DISK_file_close (op->rfd);
805 op->rfd = NULL;
806 }
807 if (h->thm == op)
808 {
809 op->result_cont = NULL;
810 return;
811 }
812 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
813 h->operation_pending_tail,
814 op);
815 GNUNET_free (op);
816}
817
818
819/**
820 * Start or stop a service.
821 *
822 * @param h handle to ARM
823 * @param service_name name of the service
824 * @param cb callback to invoke when service is ready
825 * @param cb_cls closure for @a cb
826 * @param type type of the request
827 * @return handle to queue, NULL on error
828 */
829static struct GNUNET_ARM_Operation *
830change_service (struct GNUNET_ARM_Handle *h,
831 const char *service_name,
832 GNUNET_ARM_ResultCallback cb,
833 void *cb_cls,
834 uint16_t type)
835{
836 struct GNUNET_ARM_Operation *op;
837 size_t slen;
838 struct GNUNET_MQ_Envelope *env;
839 struct GNUNET_ARM_Message *msg;
840
841 slen = strlen (service_name) + 1;
842 if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
843 {
844 GNUNET_break (0);
845 return NULL;
846 }
847 if (0 == h->request_id_counter)
848 h->request_id_counter++;
849 op = GNUNET_new (struct GNUNET_ARM_Operation);
850 op->h = h;
851 op->result_cont = cb;
852 op->cont_cls = cb_cls;
853 op->id = h->request_id_counter++;
854 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
855 h->operation_pending_tail,
856 op);
857 env = GNUNET_MQ_msg_extra (msg,
858 slen,
859 type);
860 msg->reserved = htonl (0);
861 msg->request_id = GNUNET_htonll (op->id);
862 GNUNET_memcpy (&msg[1],
863 service_name,
864 slen);
865 GNUNET_MQ_send (h->mq, env);
866 return op;
867}
868
869
870/**
871 * Task run to notify application that ARM is already up.
872 *
873 * @param cls the operation that asked ARM to be started
874 */
875static void
876notify_running (void *cls)
877{
878 struct GNUNET_ARM_Operation *op = cls;
879 struct GNUNET_ARM_Handle *h = op->h;
880
881 op->async = NULL;
882 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
883 h->operation_pending_tail,
884 op);
885 if (NULL != op->result_cont)
886 op->result_cont (op->cont_cls,
887 GNUNET_ARM_REQUEST_SENT_OK,
888 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
889 if ( (GNUNET_YES == h->currently_up) &&
890 (NULL != h->conn_status) )
891 h->conn_status (h->conn_status_cls,
892 GNUNET_YES);
893 GNUNET_free (op);
894}
895
896
897/**
898 * Task run to notify application that ARM is being started.
899 *
900 * @param cls the operation that asked ARM to be started
901 */
902static void
903notify_starting (void *cls)
904{
905 struct GNUNET_ARM_Operation *op = cls;
906 struct GNUNET_ARM_Handle *h = op->h;
907
908 op->async = NULL;
909 LOG (GNUNET_ERROR_TYPE_DEBUG,
910 "Notifying client that we started the ARM service\n");
911 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
912 h->operation_pending_tail,
913 op);
914 if (NULL != op->result_cont)
915 op->result_cont (op->cont_cls,
916 GNUNET_ARM_REQUEST_SENT_OK,
917 op->starting_ret);
918 GNUNET_DISK_file_close (op->rfd);
919 GNUNET_free (op);
920}
921
922
923/**
924 * Request for a service to be started.
925 *
926 * @param h handle to ARM
927 * @param service_name name of the service
928 * @param std_inheritance inheritance of std streams
929 * @param cont callback to invoke after request is sent or not sent
930 * @param cont_cls closure for @a cont
931 * @return handle for the operation, NULL on error
932 */
933struct GNUNET_ARM_Operation *
934GNUNET_ARM_request_service_start (
935 struct GNUNET_ARM_Handle *h,
936 const char *service_name,
937 enum GNUNET_OS_InheritStdioFlags
938 std_inheritance,
939 GNUNET_ARM_ResultCallback cont,
940 void *cont_cls)
941{
942 struct GNUNET_ARM_Operation *op;
943 enum GNUNET_ARM_Result ret;
944 struct GNUNET_DISK_PipeHandle *sig;
945 struct GNUNET_DISK_FileHandle *wsig;
946
947 LOG (GNUNET_ERROR_TYPE_DEBUG,
948 "Starting service `%s'\n",
949 service_name);
950 if (0 != strcasecmp ("arm",
951 service_name))
952 return change_service (h,
953 service_name,
954 cont,
955 cont_cls,
956 GNUNET_MESSAGE_TYPE_ARM_START);
957
958 /* Possible cases:
959 * 1) We're connected to ARM already. Invoke the callback immediately.
960 * 2) We're not connected to ARM.
961 * Cancel any reconnection attempts temporarily, then perform
962 * a service test.
963 */
964 if (GNUNET_YES == h->currently_up)
965 {
966 LOG (GNUNET_ERROR_TYPE_DEBUG,
967 "ARM is already running\n");
968 op = GNUNET_new (struct GNUNET_ARM_Operation);
969 op->h = h;
970 op->result_cont = cont;
971 op->cont_cls = cont_cls;
972 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
973 h->operation_pending_tail,
974 op);
975 op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
976 return op;
977 }
978 /* This is an inherently uncertain choice, as it is of course
979 theoretically possible that ARM is up and we just did not
980 yet complete the MQ handshake. However, given that users
981 are unlikely to hammer 'gnunet-arm -s' on a busy system,
982 the above check should catch 99.99% of the cases where ARM
983 is already running. */
984 LOG (GNUNET_ERROR_TYPE_DEBUG,
985 "Starting ARM service\n");
986 if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
987 {
988 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
989 "pipe");
990 ret = GNUNET_ARM_RESULT_START_FAILED;
991 }
992 else
993 {
994 wsig = GNUNET_DISK_pipe_detach_end (sig,
995 GNUNET_DISK_PIPE_END_WRITE);
996 ret = start_arm_service (h,
997 std_inheritance,
998 wsig);
999 GNUNET_DISK_file_close (wsig);
1000 if (GNUNET_ARM_RESULT_STARTING == ret)
1001 reconnect_arm (h);
1002 }
1003 op = GNUNET_new (struct GNUNET_ARM_Operation);
1004 op->h = h;
1005 op->result_cont = cont;
1006 op->cont_cls = cont_cls;
1007 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1008 h->operation_pending_tail,
1009 op);
1010 op->starting_ret = ret;
1011 if (NULL != sig)
1012 {
1013 op->rfd = GNUNET_DISK_pipe_detach_end (sig,
1014 GNUNET_DISK_PIPE_END_READ);
1015 /* Wait at most a minute for gnunet-service-arm to be up, as beyond
1016 that something clearly just went wrong */
1017 op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES,
1018 op->rfd,
1019 &notify_starting,
1020 op);
1021 GNUNET_DISK_pipe_close (sig);
1022 }
1023 else
1024 {
1025 op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
1026 op);
1027 }
1028 return op;
1029}
1030
1031
1032/**
1033 * Request a service to be stopped. Stopping arm itself will not
1034 * invalidate its handle, and ARM API will try to restore connection
1035 * to the ARM service, even if ARM connection was lost because you
1036 * asked for ARM to be stopped. Call
1037 * #GNUNET_ARM_disconnect() to free the handle and prevent
1038 * further connection attempts.
1039 *
1040 * @param h handle to ARM
1041 * @param service_name name of the service
1042 * @param cont callback to invoke after request is sent or is not sent
1043 * @param cont_cls closure for @a cont
1044 * @return handle for the operation, NULL on error
1045 */
1046struct GNUNET_ARM_Operation *
1047GNUNET_ARM_request_service_stop (
1048 struct GNUNET_ARM_Handle *h,
1049 const char *service_name,
1050 GNUNET_ARM_ResultCallback cont,
1051 void *cont_cls)
1052{
1053 struct GNUNET_ARM_Operation *op;
1054
1055 LOG (GNUNET_ERROR_TYPE_DEBUG,
1056 "Stopping service `%s'\n",
1057 service_name);
1058 op = change_service (h,
1059 service_name,
1060 cont,
1061 cont_cls,
1062 GNUNET_MESSAGE_TYPE_ARM_STOP);
1063 if (NULL == op)
1064 return NULL;
1065 /* If the service is ARM, set a flag as we will use MQ errors
1066 to detect that the process is really gone. */
1067 if (0 == strcasecmp (service_name,
1068 "arm"))
1069 op->is_arm_stop = GNUNET_YES;
1070 return op;
1071}
1072
1073
1074/**
1075 * Request a list of running services.
1076 *
1077 * @param h handle to ARM
1078 * @param cont callback to invoke after request is sent or is not sent
1079 * @param cont_cls closure for @a cont
1080 * @return handle for the operation, NULL on error
1081 */
1082struct GNUNET_ARM_Operation *
1083GNUNET_ARM_request_service_list (
1084 struct GNUNET_ARM_Handle *h,
1085 GNUNET_ARM_ServiceListCallback cont,
1086 void *cont_cls)
1087{
1088 struct GNUNET_ARM_Operation *op;
1089 struct GNUNET_MQ_Envelope *env;
1090 struct GNUNET_ARM_Message *msg;
1091
1092 LOG (GNUNET_ERROR_TYPE_DEBUG,
1093 "Requesting LIST from ARM service\n");
1094 if (0 == h->request_id_counter)
1095 h->request_id_counter++;
1096 op = GNUNET_new (struct GNUNET_ARM_Operation);
1097 op->h = h;
1098 op->list_cont = cont;
1099 op->cont_cls = cont_cls;
1100 op->id = h->request_id_counter++;
1101 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1102 h->operation_pending_tail,
1103 op);
1104 env = GNUNET_MQ_msg (msg,
1105 GNUNET_MESSAGE_TYPE_ARM_LIST);
1106 msg->reserved = htonl (0);
1107 msg->request_id = GNUNET_htonll (op->id);
1108 GNUNET_MQ_send (h->mq, env);
1109 return op;
1110}
1111
1112
1113/* end of arm_api.c */
diff --git a/src/service/arm/arm_monitor_api.c b/src/service/arm/arm_monitor_api.c
new file mode 100644
index 000000000..2c418d0ce
--- /dev/null
+++ b/src/service/arm/arm_monitor_api.c
@@ -0,0 +1,274 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2012, 2013, 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/**
22 * @file arm/arm_monitor_api.c
23 * @brief API for monitoring the ARM service
24 * @author Christian Grothoff
25 * @author LRN
26 */
27#include "platform.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_protocols.h"
31#include "arm.h"
32
33#define INIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "arm-monitor-api", __VA_ARGS__)
36
37/**
38 * Handle for interacting with ARM.
39 */
40struct GNUNET_ARM_MonitorHandle
41{
42 /**
43 * Our control connection to the ARM service.
44 */
45 struct GNUNET_MQ_Handle *mq;
46
47 /**
48 * The configuration that we are using.
49 */
50 const struct GNUNET_CONFIGURATION_Handle *cfg;
51
52 /**
53 * ID of the reconnect task (if any).
54 */
55 struct GNUNET_SCHEDULER_Task *reconnect_task;
56
57 /**
58 * Current delay we use for re-trying to connect to core.
59 */
60 struct GNUNET_TIME_Relative retry_backoff;
61
62 /**
63 * Callback to invoke on status updates.
64 */
65 GNUNET_ARM_ServiceMonitorCallback service_status;
66
67 /**
68 * Closure for @e service_status.
69 */
70 void *service_status_cls;
71};
72
73
74/**
75 * Connect to the ARM service for monitoring.
76 *
77 * @param h handle to connect
78 * @return #GNUNET_OK on success
79 */
80static int
81reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h);
82
83
84/**
85 * Task scheduled to try to re-connect to arm.
86 *
87 * @param cls the `struct GNUNET_ARM_MonitorHandle`
88 */
89static void
90reconnect_arm_monitor_task (void *cls)
91{
92 struct GNUNET_ARM_MonitorHandle *h = cls;
93
94 h->reconnect_task = NULL;
95 LOG (GNUNET_ERROR_TYPE_DEBUG,
96 "Connecting to ARM service for monitoring after delay\n");
97 GNUNET_break (GNUNET_OK == reconnect_arm_monitor (h));
98}
99
100
101/**
102 * Close down any existing connection to the ARM service and
103 * try re-establishing it later.
104 *
105 * @param h our handle
106 */
107static void
108reconnect_arm_monitor_later (struct GNUNET_ARM_MonitorHandle *h)
109{
110 if (NULL != h->mq)
111 {
112 GNUNET_MQ_destroy (h->mq);
113 h->mq = NULL;
114 }
115 GNUNET_assert (NULL == h->reconnect_task);
116 h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
117 &reconnect_arm_monitor_task,
118 h);
119 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
120}
121
122
123/**
124 * Check notification messages received from ARM is well-formed.
125 *
126 * @param cls our `struct GNUNET_ARM_MonitorHandle`
127 * @param msg the message received from the arm service
128 * @return #GNUNET_OK if the message is well-formed
129 */
130static int
131check_monitor_notify (void *cls, const struct GNUNET_ARM_StatusMessage *msg)
132{
133 size_t sl =
134 ntohs (msg->header.size) - sizeof(struct GNUNET_ARM_StatusMessage);
135 const char *name = (const char *) &msg[1];
136
137 (void) cls;
138 if ((0 == sl) || ('\0' != name[sl - 1]))
139 {
140 GNUNET_break (0);
141 return GNUNET_SYSERR;
142 }
143 return GNUNET_OK;
144}
145
146
147/**
148 * Handler for notification messages received from ARM.
149 *
150 * @param cls our `struct GNUNET_ARM_MonitorHandle`
151 * @param res the message received from the arm service
152 */
153static void
154handle_monitor_notify (void *cls, const struct GNUNET_ARM_StatusMessage *res)
155{
156 struct GNUNET_ARM_MonitorHandle *h = cls;
157 enum GNUNET_ARM_ServiceMonitorStatus status;
158
159 status = (enum GNUNET_ARM_ServiceMonitorStatus) ntohl (res->status);
160 LOG (GNUNET_ERROR_TYPE_DEBUG,
161 "Received notification from ARM for service `%s' with status %d\n",
162 (const char *) &res[1],
163 (int) status);
164 if (NULL != h->service_status)
165 h->service_status (h->service_status_cls, (const char *) &res[1], status);
166}
167
168
169/**
170 * Generic error handler, called with the appropriate error code and
171 * the same closure specified at the creation of the message queue.
172 * Not every message queue implementation supports an error handler.
173 *
174 * @param cls closure with the `struct GNUNET_ARM_MonitorHandle *`
175 * @param error error code
176 */
177static void
178mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
179{
180 struct GNUNET_ARM_MonitorHandle *h = cls;
181
182 (void) error;
183 reconnect_arm_monitor_later (h);
184}
185
186
187/**
188 * Connect to the ARM service for monitoring.
189 *
190 * @param h handle to connect
191 * @return #GNUNET_OK on success
192 */
193static int
194reconnect_arm_monitor (struct GNUNET_ARM_MonitorHandle *h)
195{
196 struct GNUNET_MQ_MessageHandler handlers[] =
197 { GNUNET_MQ_hd_var_size (monitor_notify,
198 GNUNET_MESSAGE_TYPE_ARM_STATUS,
199 struct GNUNET_ARM_StatusMessage,
200 h),
201 GNUNET_MQ_handler_end () };
202 struct GNUNET_MessageHeader *msg;
203 struct GNUNET_MQ_Envelope *env;
204
205 GNUNET_assert (NULL == h->mq);
206 h->mq = GNUNET_CLIENT_connect (h->cfg, "arm", handlers, &mq_error_handler, h);
207 if (NULL == h->mq)
208 {
209 if (NULL != h->service_status)
210 h->service_status (h->service_status_cls,
211 NULL,
212 GNUNET_ARM_SERVICE_STOPPED);
213 return GNUNET_SYSERR;
214 }
215 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_MONITOR);
216 GNUNET_MQ_send (h->mq, env);
217 return GNUNET_OK;
218}
219
220
221/**
222 * Setup a context for monitoring ARM, then
223 * start connecting to the ARM service for monitoring using that context.
224 *
225 * @param cfg configuration to use (needed to contact ARM;
226 * the ARM service may internally use a different
227 * configuration to determine how to start the service).
228 * @param cont callback to invoke on status updates
229 * @param cont_cls closure for @a cont
230 * @return context to use for further ARM monitor operations, NULL on error.
231 */
232struct GNUNET_ARM_MonitorHandle *
233GNUNET_ARM_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
234 GNUNET_ARM_ServiceMonitorCallback cont,
235 void *cont_cls)
236{
237 struct GNUNET_ARM_MonitorHandle *h;
238
239 h = GNUNET_new (struct GNUNET_ARM_MonitorHandle);
240 h->cfg = cfg;
241 h->service_status = cont;
242 h->service_status_cls = cont_cls;
243 if (GNUNET_OK != reconnect_arm_monitor (h))
244 {
245 GNUNET_free (h);
246 return NULL;
247 }
248 return h;
249}
250
251
252/**
253 * Disconnect from the ARM service (if connected) and destroy the context.
254 *
255 * @param h the handle that was being used
256 */
257void
258GNUNET_ARM_monitor_stop (struct GNUNET_ARM_MonitorHandle *h)
259{
260 if (NULL != h->mq)
261 {
262 GNUNET_MQ_destroy (h->mq);
263 h->mq = NULL;
264 }
265 if (NULL != h->reconnect_task)
266 {
267 GNUNET_SCHEDULER_cancel (h->reconnect_task);
268 h->reconnect_task = NULL;
269 }
270 GNUNET_free (h);
271}
272
273
274/* end of arm_api.c */
diff --git a/src/service/arm/gnunet-service-arm.c b/src/service/arm/gnunet-service-arm.c
new file mode 100644
index 000000000..e4126efdf
--- /dev/null
+++ b/src/service/arm/gnunet-service-arm.c
@@ -0,0 +1,2227 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2011, 2015, 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/**
22 * @file arm/gnunet-service-arm.c
23 * @brief the automated restart manager service
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_protocols.h"
30#include "arm.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util", __VA_ARGS__)
33
34#define LOG_STRERROR(kind, syscall) \
35 GNUNET_log_from_strerror (kind, "util", syscall)
36
37
38#if HAVE_WAIT4
39/**
40 * Name of the file for writing resource utilization summaries to.
41 */
42static char *wait_filename;
43
44/**
45 * Handle for the file for writing resource summaries.
46 */
47static FILE *wait_file;
48#endif
49
50
51/**
52 * How many messages do we queue up at most for optional
53 * notifications to a client? (this can cause notifications
54 * about outgoing messages to be dropped).
55 */
56#define MAX_NOTIFY_QUEUE 1024
57
58
59/**
60 * List of our services.
61 */
62struct ServiceList;
63
64
65/**
66 * Record with information about a listen socket we have open.
67 */
68struct ServiceListeningInfo
69{
70 /**
71 * This is a linked list.
72 */
73 struct ServiceListeningInfo *next;
74
75 /**
76 * This is a linked list.
77 */
78 struct ServiceListeningInfo *prev;
79
80 /**
81 * Address this socket is listening on.
82 */
83 struct sockaddr *service_addr;
84
85 /**
86 * Service this listen socket is for.
87 */
88 struct ServiceList *sl;
89
90 /**
91 * Number of bytes in @e service_addr
92 */
93 socklen_t service_addr_len;
94
95 /**
96 * Our listening socket.
97 */
98 struct GNUNET_NETWORK_Handle *listen_socket;
99
100 /**
101 * Task doing the accepting.
102 */
103 struct GNUNET_SCHEDULER_Task *accept_task;
104};
105
106
107/**
108 * List of our services.
109 */
110struct ServiceList
111{
112 /**
113 * This is a doubly-linked list.
114 */
115 struct ServiceList *next;
116
117 /**
118 * This is a doubly-linked list.
119 */
120 struct ServiceList *prev;
121
122 /**
123 * Linked list of listen sockets associated with this service.
124 */
125 struct ServiceListeningInfo *listen_head;
126
127 /**
128 * Linked list of listen sockets associated with this service.
129 */
130 struct ServiceListeningInfo *listen_tail;
131
132 /**
133 * Name of the service.
134 */
135 char *name;
136
137 /**
138 * Name of the binary used.
139 */
140 char *binary;
141
142 /**
143 * Name of the configuration file used.
144 */
145 char *config;
146
147 /**
148 * Client to notify upon kill completion (waitpid), NULL
149 * if we should simply restart the process.
150 */
151 struct GNUNET_SERVICE_Client *killing_client;
152
153 /**
154 * ID of the request that killed the service (for reporting back).
155 */
156 uint64_t killing_client_request_id;
157
158 /**
159 * Process structure pointer of the child.
160 */
161 struct GNUNET_OS_Process *proc;
162
163 /**
164 * Process exponential backoff time
165 */
166 struct GNUNET_TIME_Relative backoff;
167
168 /**
169 * Absolute time at which the process was (re-)started last.
170 */
171 struct GNUNET_TIME_Absolute last_started_at;
172
173 /**
174 * Absolute time at which the process is scheduled to restart in case of death
175 */
176 struct GNUNET_TIME_Absolute restart_at;
177
178 /**
179 * Time we asked the service to shut down (used to calculate time it took
180 * the service to terminate).
181 */
182 struct GNUNET_TIME_Absolute killed_at;
183
184 /**
185 * Is this service to be started by default (or did a client tell us explicitly
186 * to start it)? #GNUNET_NO if the service is started only upon 'accept' on a
187 * listen socket or possibly explicitly by a client changing the value.
188 */
189 int force_start;
190
191 /**
192 * Should we use pipes to signal this process? (YES for Java binaries and if we
193 * are on Windoze).
194 */
195 int pipe_control;
196
197 /**
198 * Last exit status of the process.
199 */
200 int last_exit_status;
201};
202
203/**
204 * List of running services.
205 */
206static struct ServiceList *running_head;
207
208/**
209 * List of running services.
210 */
211static struct ServiceList *running_tail;
212
213/**
214 * Our configuration
215 */
216static const struct GNUNET_CONFIGURATION_Handle *cfg;
217
218/**
219 * Command to prepend to each actual command.
220 */
221static char *prefix_command;
222
223/**
224 * Option to append to each actual command.
225 */
226static char *final_option;
227
228/**
229 * ID of task called whenever we get a SIGCHILD.
230 */
231static struct GNUNET_SCHEDULER_Task *child_death_task;
232
233/**
234 * ID of task called whenever the timeout for restarting a child
235 * expires.
236 */
237static struct GNUNET_SCHEDULER_Task *child_restart_task;
238
239/**
240 * Pipe used to communicate shutdown via signal.
241 */
242static struct GNUNET_DISK_PipeHandle *sigpipe;
243
244/**
245 * Are we in shutdown mode?
246 */
247static int in_shutdown;
248
249/**
250 * Return value from main
251 */
252static int global_ret;
253
254/**
255 * Are we starting user services?
256 */
257static int start_user = GNUNET_YES;
258
259/**
260 * Are we starting system services?
261 */
262static int start_system = GNUNET_YES;
263
264/**
265 * Handle to our service instance. Our service is a bit special in that
266 * its service is not immediately stopped once we get a shutdown
267 * request (since we need to continue service until all of our child
268 * processes are dead). This handle is used to shut down the service
269 * (and thus trigger process termination) once all child processes are
270 * also dead. A special option in the ARM configuration modifies the
271 * behaviour of the service implementation to not do the shutdown
272 * immediately.
273 */
274static struct GNUNET_SERVICE_Handle *service;
275
276/**
277 * Context for notifications we need to send to our clients.
278 */
279static struct GNUNET_NotificationContext *notifier;
280
281
282/**
283 * Add the given UNIX domain path as an address to the
284 * list (as the first entry).
285 *
286 * @param saddrs array to update
287 * @param saddrlens where to store the address length
288 * @param unixpath path to add
289 * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This
290 * parameter is ignore on systems other than LINUX
291 */
292static void
293add_unixpath (struct sockaddr **saddrs,
294 socklen_t *saddrlens,
295 const char *unixpath,
296 int abstract)
297{
298#ifdef AF_UNIX
299 struct sockaddr_un *un;
300
301 un = GNUNET_new (struct sockaddr_un);
302 un->sun_family = AF_UNIX;
303 GNUNET_strlcpy (un->sun_path, unixpath, sizeof(un->sun_path));
304#ifdef __linux__
305 if (GNUNET_YES == abstract)
306 un->sun_path[0] = '\0';
307#endif
308#if HAVE_SOCKADDR_UN_SUN_LEN
309 un->sun_len = (u_char) sizeof(struct sockaddr_un);
310#endif
311 *saddrs = (struct sockaddr *) un;
312 *saddrlens = sizeof(struct sockaddr_un);
313#else
314 /* this function should never be called
315 * unless AF_UNIX is defined! */
316 GNUNET_assert (0);
317#endif
318}
319
320
321/**
322 * Get the list of addresses that a server for the given service
323 * should bind to.
324 *
325 * @param service_name name of the service
326 * @param cfg configuration (which specifies the addresses)
327 * @param addrs set (call by reference) to an array of pointers to the
328 * addresses the server should bind to and listen on; the
329 * array will be NULL-terminated (on success)
330 * @param addr_lens set (call by reference) to an array of the lengths
331 * of the respective `struct sockaddr` struct in the @a addrs
332 * array (on success)
333 * @return number of addresses found on success,
334 * #GNUNET_SYSERR if the configuration
335 * did not specify reasonable finding information or
336 * if it specified a hostname that could not be resolved;
337 * #GNUNET_NO if the number of addresses configured is
338 * zero (in this case, `*addrs` and `*addr_lens` will be
339 * set to NULL).
340 */
341static int
342get_server_addresses (const char *service_name,
343 const struct GNUNET_CONFIGURATION_Handle *cfg,
344 struct sockaddr ***addrs,
345 socklen_t **addr_lens)
346{
347 int disablev6;
348 struct GNUNET_NETWORK_Handle *desc;
349 unsigned long long port;
350 char *unixpath;
351 struct addrinfo hints;
352 struct addrinfo *res;
353 struct addrinfo *pos;
354 struct addrinfo *next;
355 unsigned int i;
356 int resi;
357 int ret;
358 int abstract;
359 struct sockaddr **saddrs;
360 socklen_t *saddrlens;
361 char *hostname;
362
363 *addrs = NULL;
364 *addr_lens = NULL;
365 desc = NULL;
366 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6"))
367 {
368 if (GNUNET_SYSERR ==
369 (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
370 service_name,
371 "DISABLEV6")))
372 return GNUNET_SYSERR;
373 }
374 else
375 disablev6 = GNUNET_NO;
376
377 if (! disablev6)
378 {
379 /* probe IPv6 support */
380 desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
381 if (NULL == desc)
382 {
383 if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
384 (EACCES == errno))
385 {
386 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
387 return GNUNET_SYSERR;
388 }
389 LOG (GNUNET_ERROR_TYPE_INFO,
390 _ (
391 "Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
392 service_name,
393 strerror (errno));
394 disablev6 = GNUNET_YES;
395 }
396 else
397 {
398 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
399 desc = NULL;
400 }
401 }
402
403 port = 0;
404 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT"))
405 {
406 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
407 service_name,
408 "PORT",
409 &port))
410 {
411 LOG (GNUNET_ERROR_TYPE_ERROR,
412 _ ("Require valid port number for service `%s' in configuration!\n"),
413 service_name);
414 }
415 if (port > 65535)
416 {
417 LOG (GNUNET_ERROR_TYPE_ERROR,
418 _ ("Require valid port number for service `%s' in configuration!\n"),
419 service_name);
420 return GNUNET_SYSERR;
421 }
422 }
423
424 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO"))
425 {
426 GNUNET_break (GNUNET_OK ==
427 GNUNET_CONFIGURATION_get_value_string (cfg,
428 service_name,
429 "BINDTO",
430 &hostname));
431 }
432 else
433 hostname = NULL;
434
435 unixpath = NULL;
436 abstract = GNUNET_NO;
437#ifdef AF_UNIX
438 if ((GNUNET_YES ==
439 GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) &&
440 (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
441 service_name,
442 "UNIXPATH",
443 &unixpath)) &&
444 (0 < strlen (unixpath)))
445 {
446 /* probe UNIX support */
447 struct sockaddr_un s_un;
448
449 if (strlen (unixpath) >= sizeof(s_un.sun_path))
450 {
451 LOG (GNUNET_ERROR_TYPE_WARNING,
452 _ ("UNIXPATH `%s' too long, maximum length is %llu\n"),
453 unixpath,
454 (unsigned long long) sizeof(s_un.sun_path));
455 unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
456 LOG (GNUNET_ERROR_TYPE_INFO, _ ("Using `%s' instead\n"), unixpath);
457 }
458#ifdef __linux__
459 abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
460 "TESTING",
461 "USE_ABSTRACT_SOCKETS");
462 if (GNUNET_SYSERR == abstract)
463 abstract = GNUNET_NO;
464#endif
465 if ((GNUNET_YES != abstract) &&
466 (GNUNET_OK != GNUNET_DISK_directory_create_for_file (unixpath)))
467 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", unixpath);
468 }
469 if (NULL != unixpath)
470 {
471 desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
472 if (NULL == desc)
473 {
474 if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
475 (EACCES == errno))
476 {
477 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
478 GNUNET_free (hostname);
479 GNUNET_free (unixpath);
480 return GNUNET_SYSERR;
481 }
482 LOG (GNUNET_ERROR_TYPE_INFO,
483 _ (
484 "Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"),
485 service_name,
486 strerror (errno));
487 GNUNET_free (unixpath);
488 unixpath = NULL;
489 }
490 else
491 {
492 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
493 desc = NULL;
494 }
495 }
496#endif
497
498 if ((0 == port) && (NULL == unixpath))
499 {
500 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg,
501 service_name,
502 "START_ON_DEMAND"))
503 LOG (GNUNET_ERROR_TYPE_ERROR,
504 _ (
505 "Have neither PORT nor UNIXPATH for service `%s', but one is required\n"),
506 service_name);
507 GNUNET_free (hostname);
508 return GNUNET_SYSERR;
509 }
510 if (0 == port)
511 {
512 saddrs = GNUNET_new_array (2, struct sockaddr *);
513 saddrlens = GNUNET_new_array (2, socklen_t);
514 add_unixpath (saddrs, saddrlens, unixpath, abstract);
515 GNUNET_free (unixpath);
516 GNUNET_free (hostname);
517 *addrs = saddrs;
518 *addr_lens = saddrlens;
519 return 1;
520 }
521
522 if (NULL != hostname)
523 {
524 LOG (GNUNET_ERROR_TYPE_DEBUG,
525 "Resolving `%s' since that is where `%s' will bind to.\n",
526 hostname,
527 service_name);
528 memset (&hints, 0, sizeof(struct addrinfo));
529 if (disablev6)
530 hints.ai_family = AF_INET;
531 hints.ai_protocol = IPPROTO_TCP;
532 if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
533 (NULL == res))
534 {
535 LOG (GNUNET_ERROR_TYPE_ERROR,
536 _ ("Failed to resolve `%s': %s\n"),
537 hostname,
538 gai_strerror (ret));
539 GNUNET_free (hostname);
540 GNUNET_free (unixpath);
541 return GNUNET_SYSERR;
542 }
543 next = res;
544 i = 0;
545 while (NULL != (pos = next))
546 {
547 next = pos->ai_next;
548 if ((disablev6) && (pos->ai_family == AF_INET6))
549 continue;
550 i++;
551 }
552 if (0 == i)
553 {
554 LOG (GNUNET_ERROR_TYPE_ERROR,
555 _ ("Failed to find %saddress for `%s'.\n"),
556 disablev6 ? "IPv4 " : "",
557 hostname);
558 freeaddrinfo (res);
559 GNUNET_free (hostname);
560 GNUNET_free (unixpath);
561 return GNUNET_SYSERR;
562 }
563 resi = i;
564 if (NULL != unixpath)
565 resi++;
566 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
567 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
568 i = 0;
569 if (NULL != unixpath)
570 {
571 add_unixpath (saddrs, saddrlens, unixpath, abstract);
572 i++;
573 }
574 next = res;
575 while (NULL != (pos = next))
576 {
577 next = pos->ai_next;
578 if ((disablev6) && (AF_INET6 == pos->ai_family))
579 continue;
580 if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol))
581 continue; /* not TCP */
582 if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype))
583 continue; /* huh? */
584 LOG (GNUNET_ERROR_TYPE_DEBUG,
585 "Service `%s' will bind to `%s'\n",
586 service_name,
587 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen));
588 if (AF_INET == pos->ai_family)
589 {
590 GNUNET_assert (sizeof(struct sockaddr_in) == pos->ai_addrlen);
591 saddrlens[i] = pos->ai_addrlen;
592 saddrs[i] = GNUNET_malloc (saddrlens[i]);
593 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
594 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
595 }
596 else
597 {
598 GNUNET_assert (AF_INET6 == pos->ai_family);
599 GNUNET_assert (sizeof(struct sockaddr_in6) == pos->ai_addrlen);
600 saddrlens[i] = pos->ai_addrlen;
601 saddrs[i] = GNUNET_malloc (saddrlens[i]);
602 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
603 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
604 }
605 i++;
606 }
607 GNUNET_free (hostname);
608 freeaddrinfo (res);
609 resi = i;
610 }
611 else
612 {
613 /* will bind against everything, just set port */
614 if (disablev6)
615 {
616 /* V4-only */
617 resi = 1;
618 if (NULL != unixpath)
619 resi++;
620 i = 0;
621 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
622 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
623 if (NULL != unixpath)
624 {
625 add_unixpath (saddrs, saddrlens, unixpath, abstract);
626 i++;
627 }
628 saddrlens[i] = sizeof(struct sockaddr_in);
629 saddrs[i] = GNUNET_malloc (saddrlens[i]);
630#if HAVE_SOCKADDR_IN_SIN_LEN
631 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
632#endif
633 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
634 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
635 }
636 else
637 {
638 /* dual stack */
639 resi = 2;
640 if (NULL != unixpath)
641 resi++;
642 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
643 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
644 i = 0;
645 if (NULL != unixpath)
646 {
647 add_unixpath (saddrs, saddrlens, unixpath, abstract);
648 i++;
649 }
650 saddrlens[i] = sizeof(struct sockaddr_in6);
651 saddrs[i] = GNUNET_malloc (saddrlens[i]);
652#if HAVE_SOCKADDR_IN_SIN_LEN
653 ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
654#endif
655 ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
656 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
657 i++;
658 saddrlens[i] = sizeof(struct sockaddr_in);
659 saddrs[i] = GNUNET_malloc (saddrlens[i]);
660#if HAVE_SOCKADDR_IN_SIN_LEN
661 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
662#endif
663 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
664 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
665 }
666 }
667 GNUNET_free (unixpath);
668 *addrs = saddrs;
669 *addr_lens = saddrlens;
670 return resi;
671}
672
673
674/**
675 * Signal our client that we will start or stop the
676 * service.
677 *
678 * @param client who is being signalled
679 * @param name name of the service
680 * @param request_id id of the request that is being responded to.
681 * @param result message type to send
682 * @return NULL if it was not found
683 */
684static void
685signal_result (struct GNUNET_SERVICE_Client *client,
686 const char *name,
687 uint64_t request_id,
688 enum GNUNET_ARM_Result result)
689{
690 struct GNUNET_MQ_Envelope *env;
691 struct GNUNET_ARM_ResultMessage *msg;
692
693 (void) name;
694 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_RESULT);
695 msg->result = htonl (result);
696 msg->arm_msg.request_id = GNUNET_htonll (request_id);
697 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
698}
699
700
701/**
702 * Tell all clients about status change of a service.
703 *
704 * @param name name of the service
705 * @param status message type to send
706 * @param unicast if not NULL, send to this client only.
707 * otherwise, send to all clients in the notifier
708 */
709static void
710broadcast_status (const char *name,
711 enum GNUNET_ARM_ServiceMonitorStatus status,
712 struct GNUNET_SERVICE_Client *unicast)
713{
714 struct GNUNET_MQ_Envelope *env;
715 struct GNUNET_ARM_StatusMessage *msg;
716 size_t namelen;
717
718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719 "Sending status %u of service `%s' to client\n",
720 (unsigned int) status,
721 name);
722 namelen = strlen (name) + 1;
723 env = GNUNET_MQ_msg_extra (msg, namelen, GNUNET_MESSAGE_TYPE_ARM_STATUS);
724 msg->status = htonl ((uint32_t) (status));
725 GNUNET_memcpy ((char *) &msg[1], name, namelen);
726 if (NULL == unicast)
727 {
728 if (NULL != notifier)
729 GNUNET_notification_context_broadcast (notifier,
730 &msg->header,
731 GNUNET_YES);
732 GNUNET_MQ_discard (env);
733 }
734 else
735 {
736 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (unicast), env);
737 }
738}
739
740
741/**
742 * Actually start the process for the given service.
743 *
744 * @param sl identifies service to start
745 * @param client that asked to start the service (may be NULL)
746 * @param request_id id of the request in response to which the process is
747 * being started. 0 if starting was not requested.
748 */
749static void
750start_process (struct ServiceList *sl,
751 struct GNUNET_SERVICE_Client *client,
752 uint64_t request_id)
753{
754 char *loprefix;
755 char *options;
756 int use_debug;
757 int is_simple_service;
758 struct ServiceListeningInfo *sli;
759 int *lsocks;
760 unsigned int ls;
761 char *binary;
762 char *quotedbinary;
763
764 /* calculate listen socket list */
765 lsocks = NULL;
766 ls = 0;
767 for (sli = sl->listen_head; NULL != sli; sli = sli->next)
768 {
769 GNUNET_array_append (lsocks,
770 ls,
771 GNUNET_NETWORK_get_fd (sli->listen_socket));
772 if (NULL != sli->accept_task)
773 {
774 GNUNET_SCHEDULER_cancel (sli->accept_task);
775 sli->accept_task = NULL;
776 }
777 }
778
779 GNUNET_array_append (lsocks, ls, -1);
780
781 /* obtain configuration */
782 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
783 sl->name,
784 "PREFIX",
785 &loprefix))
786 loprefix = GNUNET_strdup (prefix_command);
787 else
788 loprefix = GNUNET_CONFIGURATION_expand_dollar (cfg, loprefix);
789 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
790 sl->name,
791 "OPTIONS",
792 &options))
793 options = NULL;
794 else
795 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
796 {
797 char *new_options;
798 char *optpos;
799 char *fin_options;
800
801 fin_options = GNUNET_strdup (final_option);
802 /* replace '{}' with service name */
803 while (NULL != (optpos = strstr (fin_options, "{}")))
804 {
805 /* terminate string at opening parenthesis */
806 *optpos = 0;
807 GNUNET_asprintf (&new_options,
808 "%s%s%s",
809 fin_options,
810 sl->name,
811 optpos + 2);
812 GNUNET_free (fin_options);
813 fin_options = new_options;
814 }
815 if (NULL != options)
816 {
817 /* combine "fin_options" with "options" */
818 optpos = options;
819 GNUNET_asprintf (&options, "%s %s", fin_options, optpos);
820 GNUNET_free (fin_options);
821 GNUNET_free (optpos);
822 }
823 else
824 {
825 /* only have "fin_options", use that */
826 options = fin_options;
827 }
828 }
829 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
830 use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
831 {
832 const char *service_type = NULL;
833 const char *choices[] = { "GNUNET", "SIMPLE", NULL };
834
835 is_simple_service = GNUNET_NO;
836 if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_choice (cfg,
837 sl->name,
838 "TYPE",
839 choices,
840 &service_type)) &&
841 (0 == strcasecmp (service_type, "SIMPLE")))
842 is_simple_service = GNUNET_YES;
843 }
844
845 GNUNET_assert (NULL == sl->proc);
846 if (GNUNET_YES == is_simple_service)
847 {
848 /* A simple service will receive no GNUnet specific
849 command line options. */
850 binary = GNUNET_strdup (sl->binary);
851 binary = GNUNET_CONFIGURATION_expand_dollar (cfg, binary);
852 GNUNET_asprintf (&quotedbinary, "\"%s\"", sl->binary);
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Starting simple service `%s' using binary `%s'\n",
855 sl->name,
856 sl->binary);
857 /* FIXME: dollar expansion should only be done outside
858 * of ''-quoted strings, escaping should be considered. */
859 if (NULL != options)
860 options = GNUNET_CONFIGURATION_expand_dollar (cfg, options);
861 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
862 ? GNUNET_OS_INHERIT_STD_OUT_AND_ERR
863 | GNUNET_OS_USE_PIPE_CONTROL
864 : GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
865 lsocks,
866 loprefix,
867 quotedbinary,
868 options,
869 NULL);
870 }
871 else
872 {
873 /* actually start process */
874 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875 "Starting service `%s' using binary `%s' and configuration `%s'\n",
876 sl->name,
877 sl->binary,
878 sl->config);
879 binary = GNUNET_OS_get_libexec_binary_path (sl->binary);
880 GNUNET_asprintf (&quotedbinary, "\"%s\"", binary);
881
882 if (GNUNET_YES == use_debug)
883 {
884 if (NULL == sl->config)
885 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
886 ?
887 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
888 | GNUNET_OS_USE_PIPE_CONTROL
889 :
890 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
891 lsocks,
892 loprefix,
893 quotedbinary,
894 "-L",
895 "DEBUG",
896 options,
897 NULL);
898 else
899 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
900 ?
901 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
902 | GNUNET_OS_USE_PIPE_CONTROL
903 :
904 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
905 lsocks,
906 loprefix,
907 quotedbinary,
908 "-c",
909 sl->config,
910 "-L",
911 "DEBUG",
912 options,
913 NULL);
914 }
915 else
916 {
917 if (NULL == sl->config)
918 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
919 ?
920 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
921 | GNUNET_OS_USE_PIPE_CONTROL
922 :
923 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
924 lsocks,
925 loprefix,
926 quotedbinary,
927 options,
928 NULL);
929 else
930 sl->proc = GNUNET_OS_start_process_s (sl->pipe_control
931 ?
932 GNUNET_OS_INHERIT_STD_OUT_AND_ERR
933 | GNUNET_OS_USE_PIPE_CONTROL
934 :
935 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
936 lsocks,
937 loprefix,
938 quotedbinary,
939 "-c",
940 sl->config,
941 options,
942 NULL);
943 }
944 }
945 GNUNET_free (binary);
946 GNUNET_free (quotedbinary);
947 sl->last_started_at = GNUNET_TIME_absolute_get ();
948 if (NULL == sl->proc)
949 {
950 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951 _ ("Failed to start service `%s'\n"),
952 sl->name);
953 if (client)
954 signal_result (client,
955 sl->name,
956 request_id,
957 GNUNET_ARM_RESULT_START_FAILED);
958 }
959 else
960 {
961 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
962 _ ("Starting service `%s'\n"),
963 sl->name);
964 broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL);
965 if (client)
966 signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING);
967 }
968 /* clean up */
969 GNUNET_free (loprefix);
970 GNUNET_free (options);
971 GNUNET_array_grow (lsocks, ls, 0);
972}
973
974
975/**
976 * Find the process with the given service
977 * name in the given list and return it.
978 *
979 * @param name which service entry to look up
980 * @return NULL if it was not found
981 */
982static struct ServiceList *
983find_service (const char *name)
984{
985 struct ServiceList *sl;
986
987 sl = running_head;
988 while (sl != NULL)
989 {
990 if (0 == strcasecmp (sl->name, name))
991 return sl;
992 sl = sl->next;
993 }
994 return NULL;
995}
996
997
998/**
999 * First connection has come to the listening socket associated with the service,
1000 * create the service in order to relay the incoming connection to it
1001 *
1002 * @param cls callback data, `struct ServiceListeningInfo` describing a listen socket
1003 */
1004static void
1005accept_connection (void *cls)
1006{
1007 struct ServiceListeningInfo *sli = cls;
1008 struct ServiceList *sl = sli->sl;
1009
1010 sli->accept_task = NULL;
1011 GNUNET_assert (GNUNET_NO == in_shutdown);
1012 start_process (sl, NULL, 0);
1013}
1014
1015
1016/**
1017 * Creating a listening socket for each of the service's addresses and
1018 * wait for the first incoming connection to it
1019 *
1020 * @param sa address associated with the service
1021 * @param addr_len length of @a sa
1022 * @param sl service entry for the service in question
1023 */
1024static void
1025create_listen_socket (struct sockaddr *sa,
1026 socklen_t addr_len,
1027 struct ServiceList *sl)
1028{
1029 static int on = 1;
1030 struct GNUNET_NETWORK_Handle *sock;
1031 struct ServiceListeningInfo *sli;
1032
1033 int match_uid;
1034 int match_gid;
1035
1036 switch (sa->sa_family)
1037 {
1038 case AF_INET:
1039 sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
1040 break;
1041
1042 case AF_INET6:
1043 sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1044 break;
1045
1046 case AF_UNIX:
1047 if (0 == strcmp (GNUNET_a2s (sa, addr_len),
1048 "@")) /* Do not bind to blank UNIX path! */
1049 return;
1050 sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
1051 break;
1052
1053 default:
1054 GNUNET_break (0);
1055 sock = NULL;
1056 errno = EAFNOSUPPORT;
1057 break;
1058 }
1059 if (NULL == sock)
1060 {
1061 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1062 _ ("Unable to create socket for service `%s': %s\n"),
1063 sl->name,
1064 strerror (errno));
1065 GNUNET_free (sa);
1066 return;
1067 }
1068 if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (sock,
1069 SOL_SOCKET,
1070 SO_REUSEADDR,
1071 &on,
1072 sizeof(on)))
1073 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1074 "setsockopt");
1075#ifdef IPV6_V6ONLY
1076 if ((sa->sa_family == AF_INET6) &&
1077 (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (sock,
1078 IPPROTO_IPV6,
1079 IPV6_V6ONLY,
1080 &on,
1081 sizeof(on))))
1082 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1083 "setsockopt");
1084#endif
1085 if (AF_UNIX == sa->sa_family)
1086 GNUNET_NETWORK_unix_precheck ((struct sockaddr_un *) sa);
1087 if (GNUNET_OK !=
1088 GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len))
1089 {
1090 GNUNET_log (
1091 GNUNET_ERROR_TYPE_WARNING,
1092 _ (
1093 "Unable to bind listening socket for service `%s' to address `%s': %s\n"),
1094 sl->name,
1095 GNUNET_a2s (sa, addr_len),
1096 strerror (errno));
1097 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1098 GNUNET_free (sa);
1099 return;
1100 }
1101 if ((AF_UNIX == sa->sa_family)
1102#ifdef __linux__
1103 /* Permission settings are not required when abstract sockets are used */
1104 && ('\0' != ((const struct sockaddr_un *) sa)->sun_path[0])
1105#endif
1106 )
1107 {
1108 match_uid =
1109 GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "UNIX_MATCH_UID");
1110 match_gid =
1111 GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "UNIX_MATCH_GID");
1112 GNUNET_DISK_fix_permissions (((const struct sockaddr_un *) sa)->sun_path,
1113 match_uid,
1114 match_gid);
1115 }
1116 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
1117 {
1118 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
1119 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1120 GNUNET_free (sa);
1121 return;
1122 }
1123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1124 _ ("ARM now monitors connections to service `%s' at `%s'\n"),
1125 sl->name,
1126 GNUNET_a2s (sa, addr_len));
1127 sli = GNUNET_new (struct ServiceListeningInfo);
1128 sli->service_addr = sa;
1129 sli->service_addr_len = addr_len;
1130 sli->listen_socket = sock;
1131 sli->sl = sl;
1132 sli->accept_task =
1133 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1134 sock,
1135 &accept_connection,
1136 sli);
1137 GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli);
1138}
1139
1140
1141/**
1142 * Remove and free an entry in the service list. Listen sockets
1143 * must have already been cleaned up. Only to be called during shutdown.
1144 *
1145 * @param sl entry to free
1146 */
1147static void
1148free_service (struct ServiceList *sl)
1149{
1150 GNUNET_assert (GNUNET_YES == in_shutdown);
1151 GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl);
1152 GNUNET_assert (NULL == sl->listen_head);
1153 GNUNET_free (sl->config);
1154 GNUNET_free (sl->binary);
1155 GNUNET_free (sl->name);
1156 GNUNET_free (sl);
1157}
1158
1159
1160/**
1161 * Check START-message.
1162 *
1163 * @param cls identification of the client
1164 * @param amsg the actual message
1165 * @return #GNUNET_OK to keep the connection open,
1166 * #GNUNET_SYSERR to close it (signal serious error)
1167 */
1168static int
1169check_start (void *cls, const struct GNUNET_ARM_Message *amsg)
1170{
1171 (void) cls;
1172 GNUNET_MQ_check_zero_termination (amsg);
1173 return GNUNET_OK;
1174}
1175
1176
1177/**
1178 * Handle START-message.
1179 *
1180 * @param cls identification of the client
1181 * @param amsg the actual message
1182 */
1183static void
1184handle_start (void *cls, const struct GNUNET_ARM_Message *amsg)
1185{
1186 struct GNUNET_SERVICE_Client *client = cls;
1187 const char *servicename;
1188 struct ServiceList *sl;
1189 uint64_t request_id;
1190
1191 request_id = GNUNET_ntohll (amsg->request_id);
1192 servicename = (const char *) &amsg[1];
1193 GNUNET_SERVICE_client_continue (client);
1194 if (GNUNET_YES == in_shutdown)
1195 {
1196 signal_result (client,
1197 servicename,
1198 request_id,
1199 GNUNET_ARM_RESULT_IN_SHUTDOWN);
1200 return;
1201 }
1202 sl = find_service (servicename);
1203 if (NULL == sl)
1204 {
1205 signal_result (client,
1206 servicename,
1207 request_id,
1208 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
1209 return;
1210 }
1211 sl->force_start = GNUNET_YES;
1212 if (NULL != sl->proc)
1213 {
1214 signal_result (client,
1215 servicename,
1216 request_id,
1217 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
1218 return;
1219 }
1220 start_process (sl, client, request_id);
1221}
1222
1223
1224/**
1225 * Start a shutdown sequence.
1226 *
1227 * @param cls closure (refers to service)
1228 */
1229static void
1230trigger_shutdown (void *cls)
1231{
1232 (void) cls;
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n");
1234 GNUNET_SCHEDULER_shutdown ();
1235}
1236
1237
1238/**
1239 * Check STOP-message.
1240 *
1241 * @param cls identification of the client
1242 * @param amsg the actual message
1243 * @return #GNUNET_OK to keep the connection open,
1244 * #GNUNET_SYSERR to close it (signal serious error)
1245 */
1246static int
1247check_stop (void *cls, const struct GNUNET_ARM_Message *amsg)
1248{
1249 (void) cls;
1250 GNUNET_MQ_check_zero_termination (amsg);
1251 return GNUNET_OK;
1252}
1253
1254
1255/**
1256 * Handle STOP-message.
1257 *
1258 * @param cls identification of the client
1259 * @param amsg the actual message
1260 */
1261static void
1262handle_stop (void *cls, const struct GNUNET_ARM_Message *amsg)
1263{
1264 struct GNUNET_SERVICE_Client *client = cls;
1265 struct ServiceList *sl;
1266 const char *servicename;
1267 uint64_t request_id;
1268
1269 request_id = GNUNET_ntohll (amsg->request_id);
1270 servicename = (const char *) &amsg[1];
1271 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1272 _ ("Preparing to stop `%s'\n"),
1273 servicename);
1274 GNUNET_SERVICE_client_continue (client);
1275 if (0 == strcasecmp (servicename, "arm"))
1276 {
1277 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
1278 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING);
1279 GNUNET_SERVICE_client_persist (client);
1280 GNUNET_SCHEDULER_add_now (&trigger_shutdown, NULL);
1281 return;
1282 }
1283 sl = find_service (servicename);
1284 if (NULL == sl)
1285 {
1286 signal_result (client,
1287 servicename,
1288 request_id,
1289 GNUNET_ARM_RESULT_IS_NOT_KNOWN);
1290 return;
1291 }
1292 sl->force_start = GNUNET_NO;
1293 if (GNUNET_YES == in_shutdown)
1294 {
1295 /* shutdown in progress */
1296 signal_result (client,
1297 servicename,
1298 request_id,
1299 GNUNET_ARM_RESULT_IN_SHUTDOWN);
1300 return;
1301 }
1302 if (NULL != sl->killing_client)
1303 {
1304 /* killing already in progress */
1305 signal_result (client,
1306 servicename,
1307 request_id,
1308 GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
1309 return;
1310 }
1311 if (NULL == sl->proc)
1312 {
1313 /* process is down */
1314 signal_result (client,
1315 servicename,
1316 request_id,
1317 GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
1318 return;
1319 }
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1321 "Sending kill signal to service `%s', waiting for process to die.\n",
1322 servicename);
1323 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
1324 /* no signal_start - only when it's STOPPED */
1325 sl->killed_at = GNUNET_TIME_absolute_get ();
1326 if (0 != GNUNET_OS_process_kill (sl->proc, GNUNET_TERM_SIG))
1327 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
1328 sl->killing_client = client;
1329 sl->killing_client_request_id = request_id;
1330}
1331
1332
1333/**
1334 * Write a string to a string pool.
1335 *
1336 * @param pool_start pointer to the start of the string pool
1337 * @param pool_size size of the string pool
1338 * @param[in,out] pool_pos current position index in the string pool,
1339 * will be updated
1340 * @param str string to write to the string pool
1341 * @returns GNUNET_OK if the string fits into the pool,
1342 * GNUNET_SYSERR otherwise
1343 */
1344static int
1345pool_write (char *pool_start, size_t pool_size, size_t *pool_pos, char *str)
1346{
1347 size_t next_pos = (*pool_pos) + strlen (str) + 1;
1348
1349 if (next_pos > pool_size)
1350 return GNUNET_SYSERR;
1351 memcpy (pool_start + *pool_pos, str, strlen (str) + 1);
1352 *pool_pos = next_pos;
1353 return GNUNET_OK;
1354}
1355
1356
1357/**
1358 * Handle LIST-message.
1359 *
1360 * @param cls identification of the client
1361 * @param request the actual message
1362 */
1363static void
1364handle_list (void *cls, const struct GNUNET_ARM_Message *request)
1365{
1366 struct GNUNET_SERVICE_Client *client = cls;
1367 struct GNUNET_MQ_Envelope *env;
1368 struct GNUNET_ARM_ListResultMessage *msg;
1369 size_t extra_size;
1370 struct ServiceList *sl;
1371 uint16_t count;
1372 size_t pool_size;
1373 size_t pool_pos;
1374 char *pool_start;
1375 struct GNUNET_ARM_ServiceInfoMessage *ssm;
1376
1377 GNUNET_break_op (0 == ntohl (request->reserved));
1378 count = 0;
1379 pool_size = 0;
1380
1381 /* Do one pass over the list to compute the number of services
1382 * and the string pool size */
1383 for (sl = running_head; NULL != sl; sl = sl->next)
1384 {
1385 pool_size += strlen (sl->name) + 1;
1386 pool_size += strlen (sl->binary) + 1;
1387 count++;
1388 }
1389
1390 extra_size = pool_size + (count * sizeof (struct
1391 GNUNET_ARM_ServiceInfoMessage));
1392 env = GNUNET_MQ_msg_extra (msg,
1393 extra_size,
1394 GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT);
1395 msg->arm_msg.request_id = request->request_id;
1396 msg->count = htons (count);
1397
1398 ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &msg[1];
1399 pool_start = (char *) (ssm + count);
1400 pool_pos = 0;
1401
1402 for (sl = running_head; NULL != sl; sl = sl->next)
1403 {
1404 ssm->name_index = htons ((uint16_t) pool_pos);
1405 GNUNET_assert (GNUNET_OK == pool_write (pool_start, pool_size, &pool_pos,
1406 sl->name));
1407 ssm->binary_index = htons ((uint16_t) pool_pos);
1408 GNUNET_assert (GNUNET_OK == pool_write (pool_start, pool_size, &pool_pos,
1409 sl->binary));
1410 if (NULL == sl->proc)
1411 {
1412 if (0 == sl->last_started_at.abs_value_us)
1413 {
1414 /* Process never started */
1415 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STOPPED);
1416 }
1417 else if (0 == sl->last_exit_status)
1418 {
1419 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_FINISHED);
1420 }
1421 else
1422 {
1423 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_FAILED);
1424 ssm->last_exit_status = htons (sl->last_exit_status);
1425 }
1426 }
1427 else if ((NULL != sl->killing_client) || (GNUNET_YES == in_shutdown))
1428 {
1429 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STOPPING);
1430 }
1431 else
1432 {
1433 ssm->status = htonl (GNUNET_ARM_SERVICE_STATUS_STARTED);
1434 }
1435 ssm->last_started_at = GNUNET_TIME_absolute_hton (sl->last_started_at);
1436 ssm->restart_at = GNUNET_TIME_absolute_hton (sl->restart_at);
1437 ssm++;
1438 }
1439 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1440 GNUNET_SERVICE_client_continue (client);
1441}
1442
1443
1444/**
1445 * Handle TEST-message by sending back TEST.
1446 *
1447 * @param cls identification of the client
1448 * @param message the actual message
1449 */
1450static void
1451handle_test (void *cls, const struct GNUNET_MessageHeader *message)
1452{
1453 struct GNUNET_SERVICE_Client *client = cls;
1454 struct GNUNET_MQ_Envelope *env;
1455 struct GNUNET_MessageHeader *msg;
1456
1457 (void) message;
1458 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_TEST);
1459 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1460 GNUNET_SERVICE_client_continue (client);
1461}
1462
1463
1464/**
1465 * We are done with everything. Stop remaining
1466 * tasks, signal handler and the server.
1467 */
1468static void
1469do_shutdown ()
1470{
1471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n");
1472 if (NULL != notifier)
1473 {
1474 GNUNET_notification_context_destroy (notifier);
1475 notifier = NULL;
1476 }
1477 if (NULL != service)
1478 {
1479 GNUNET_SERVICE_shutdown (service);
1480 service = NULL;
1481 }
1482 if (NULL != child_death_task)
1483 {
1484 GNUNET_SCHEDULER_cancel (child_death_task);
1485 child_death_task = NULL;
1486 }
1487}
1488
1489
1490/**
1491 * Count how many services are still active.
1492 *
1493 * @param running_head list of services
1494 * @return number of active services found
1495 */
1496static unsigned int
1497list_count (struct ServiceList *running_head)
1498{
1499 struct ServiceList *i;
1500 unsigned int res;
1501
1502 for (res = 0, i = running_head; NULL != i; i = i->next, res++)
1503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name);
1504 return res;
1505}
1506
1507
1508/**
1509 * Task run for shutdown.
1510 *
1511 * @param cls closure, NULL if we need to self-restart
1512 */
1513static void
1514shutdown_task (void *cls)
1515{
1516 struct ServiceList *pos;
1517 struct ServiceList *nxt;
1518 struct ServiceListeningInfo *sli;
1519
1520 (void) cls;
1521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n");
1522 if (NULL != child_restart_task)
1523 {
1524 GNUNET_SCHEDULER_cancel (child_restart_task);
1525 child_restart_task = NULL;
1526 }
1527 in_shutdown = GNUNET_YES;
1528 /* first, stop listening */
1529 for (pos = running_head; NULL != pos; pos = pos->next)
1530 {
1531 while (NULL != (sli = pos->listen_head))
1532 {
1533 GNUNET_CONTAINER_DLL_remove (pos->listen_head, pos->listen_tail, sli);
1534 if (NULL != sli->accept_task)
1535 {
1536 GNUNET_SCHEDULER_cancel (sli->accept_task);
1537 sli->accept_task = NULL;
1538 }
1539 GNUNET_break (GNUNET_OK ==
1540 GNUNET_NETWORK_socket_close (sli->listen_socket));
1541 GNUNET_free (sli->service_addr);
1542 GNUNET_free (sli);
1543 }
1544 }
1545 /* then, shutdown all existing service processes */
1546 nxt = running_head;
1547 while (NULL != (pos = nxt))
1548 {
1549 nxt = pos->next;
1550 if (NULL != pos->proc)
1551 {
1552 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name);
1553 pos->killed_at = GNUNET_TIME_absolute_get ();
1554 if (0 != GNUNET_OS_process_kill (pos->proc, GNUNET_TERM_SIG))
1555 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
1556 }
1557 else
1558 {
1559 free_service (pos);
1560 }
1561 }
1562 /* finally, should all service processes be already gone, terminate for real */
1563 if (NULL == running_head)
1564 do_shutdown ();
1565 else
1566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1567 "Delaying shutdown, have %u children still running\n",
1568 list_count (running_head));
1569}
1570
1571
1572/**
1573 * Task run whenever it is time to restart a child that died.
1574 *
1575 * @param cls closure, always NULL
1576 */
1577static void
1578delayed_restart_task (void *cls)
1579
1580{
1581 struct ServiceList *sl;
1582 struct GNUNET_TIME_Relative lowestRestartDelay;
1583 struct ServiceListeningInfo *sli;
1584
1585 (void) cls;
1586 child_restart_task = NULL;
1587 GNUNET_assert (GNUNET_NO == in_shutdown);
1588 lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
1589
1590 /* check for services that need to be restarted due to
1591 * configuration changes or because the last restart failed */
1592 for (sl = running_head; NULL != sl; sl = sl->next)
1593 {
1594 if (NULL != sl->proc)
1595 continue;
1596 /* service is currently not running */
1597 if (0 == GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value_us)
1598 {
1599 /* restart is now allowed */
1600 if (sl->force_start)
1601 {
1602 /* process should run by default, start immediately */
1603 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1604 _ ("Restarting service `%s'.\n"),
1605 sl->name);
1606 start_process (sl, NULL, 0);
1607 }
1608 else
1609 {
1610 /* process is run on-demand, ensure it is re-started if there is demand */
1611 for (sli = sl->listen_head; NULL != sli; sli = sli->next)
1612 if (NULL == sli->accept_task)
1613 {
1614 /* accept was actually paused, so start it again */
1615 sli->accept_task =
1616 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1617 sli->listen_socket,
1618 &accept_connection,
1619 sli);
1620 }
1621 }
1622 }
1623 else
1624 {
1625 /* update calculation for earliest time to reactivate a service */
1626 lowestRestartDelay =
1627 GNUNET_TIME_relative_min (lowestRestartDelay,
1628 GNUNET_TIME_absolute_get_remaining (
1629 sl->restart_at));
1630 }
1631 }
1632 if (lowestRestartDelay.rel_value_us !=
1633 GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
1634 {
1635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1636 "Will restart process in %s\n",
1637 GNUNET_STRINGS_relative_time_to_string (lowestRestartDelay,
1638 GNUNET_YES));
1639 child_restart_task =
1640 GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
1641 GNUNET_SCHEDULER_PRIORITY_IDLE,
1642 &delayed_restart_task,
1643 NULL);
1644 }
1645}
1646
1647
1648/**
1649 * Task triggered whenever we receive a SIGCHLD (child
1650 * process died).
1651 *
1652 * @param cls closure, NULL
1653 */
1654static void
1655maint_child_death (void *cls)
1656{
1657 struct ServiceList *pos;
1658 struct ServiceList *next;
1659 struct ServiceListeningInfo *sli;
1660 const char *statstr;
1661 int statcode;
1662 int ret;
1663 char c[16];
1664 enum GNUNET_OS_ProcessStatusType statusType;
1665 unsigned long statusCode;
1666 const struct GNUNET_DISK_FileHandle *pr;
1667
1668 (void) cls;
1669 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1670 child_death_task = NULL;
1671 /* consume the signal */
1672 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
1673
1674 /* check for services that died (WAITPID) */
1675 next = running_head;
1676 while (NULL != (pos = next))
1677 {
1678 next = pos->next;
1679
1680 if (NULL == pos->proc)
1681 {
1682 if (GNUNET_YES == in_shutdown)
1683 free_service (pos);
1684 continue;
1685 }
1686#if HAVE_WAIT4
1687 if (NULL != wait_file)
1688 {
1689 /* need to use 'wait4()' to obtain and log performance data */
1690 struct rusage ru;
1691 int status;
1692 pid_t pid;
1693
1694 pid = GNUNET_OS_process_get_pid (pos->proc);
1695 ret = wait4 (pid, &status, WNOHANG, &ru);
1696 if (ret <= 0)
1697 continue; /* no process done */
1698 if (WIFEXITED (status))
1699 {
1700 statusType = GNUNET_OS_PROCESS_EXITED;
1701 statusCode = WEXITSTATUS (status);
1702 }
1703 else if (WIFSIGNALED (status))
1704 {
1705 statusType = GNUNET_OS_PROCESS_SIGNALED;
1706 statusCode = WTERMSIG (status);
1707 }
1708 else if (WIFSTOPPED (status))
1709 {
1710 statusType = GNUNET_OS_PROCESS_SIGNALED;
1711 statusCode = WSTOPSIG (status);
1712 }
1713#ifdef WIFCONTINUED
1714 else if (WIFCONTINUED (status))
1715 {
1716 statusType = GNUNET_OS_PROCESS_RUNNING;
1717 statusCode = 0;
1718 }
1719#endif
1720 else
1721 {
1722 statusType = GNUNET_OS_PROCESS_UNKNOWN;
1723 statusCode = 0;
1724 }
1725 if ((GNUNET_OS_PROCESS_EXITED == statusType) ||
1726 (GNUNET_OS_PROCESS_SIGNALED == statusType))
1727 {
1728 double utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 10e6);
1729 double stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 10e6);
1730 fprintf (wait_file,
1731 "%s(%u) %.3f %.3f %llu %llu %llu %llu %llu\n",
1732 pos->binary,
1733 (unsigned int) pid,
1734 utime,
1735 stime,
1736 (unsigned long long) ru.ru_maxrss,
1737 (unsigned long long) ru.ru_inblock,
1738 (unsigned long long) ru.ru_oublock,
1739 (unsigned long long) ru.ru_nvcsw,
1740 (unsigned long long) ru.ru_nivcsw);
1741 }
1742 }
1743 else /* continue with JUST this "if" as "else" (intentionally no brackets!) */
1744#endif
1745 if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->proc,
1746 &statusType,
1747 &statusCode))) ||
1748 (ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) ||
1749 (statusType == GNUNET_OS_PROCESS_UNKNOWN) ||
1750 (statusType == GNUNET_OS_PROCESS_RUNNING))
1751 continue;
1752
1753 if (statusType == GNUNET_OS_PROCESS_EXITED)
1754 {
1755 statstr = _ (/* process termination method */ "exit");
1756 statcode = statusCode;
1757 }
1758 else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
1759 {
1760 statstr = _ (/* process termination method */ "signal");
1761 statcode = statusCode;
1762 }
1763 else
1764 {
1765 statstr = _ (/* process termination method */ "unknown");
1766 statcode = 0;
1767 }
1768 if (0 != pos->killed_at.abs_value_us)
1769 {
1770 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1771 _ ("Service `%s' took %s to terminate\n"),
1772 pos->name,
1773 GNUNET_STRINGS_relative_time_to_string (
1774 GNUNET_TIME_absolute_get_duration (pos->killed_at),
1775 GNUNET_YES));
1776 }
1777 GNUNET_OS_process_destroy (pos->proc);
1778 pos->proc = NULL;
1779 broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL);
1780 if (NULL != pos->killing_client)
1781 {
1782 signal_result (pos->killing_client,
1783 pos->name,
1784 pos->killing_client_request_id,
1785 GNUNET_ARM_RESULT_STOPPED);
1786 pos->killing_client = NULL;
1787 pos->killing_client_request_id = 0;
1788 }
1789 if (GNUNET_YES != in_shutdown)
1790 {
1791 pos->last_exit_status = statcode;
1792 if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
1793 {
1794 /* process terminated normally, allow restart at any time */
1795 pos->restart_at.abs_value_us = 0;
1796 GNUNET_log (
1797 GNUNET_ERROR_TYPE_INFO,
1798 _ ("Service `%s' terminated normally, will restart at any time\n"),
1799 pos->name);
1800 /* process can still be re-started on-demand, ensure it is re-started if there is demand */
1801 for (sli = pos->listen_head; NULL != sli; sli = sli->next)
1802 {
1803 GNUNET_break (NULL == sli->accept_task);
1804 sli->accept_task =
1805 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1806 sli->listen_socket,
1807 &accept_connection,
1808 sli);
1809 }
1810 }
1811 else
1812 {
1813 GNUNET_log (
1814 GNUNET_ERROR_TYPE_WARNING,
1815 _ ("Service `%s' terminated with status %s/%d, will restart in %s\n"),
1816 pos->name,
1817 statstr,
1818 statcode,
1819 GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES));
1820 {
1821 /* Reduce backoff based on runtime of the process,
1822 so that there is a cool-down if a process actually
1823 runs for a while. */
1824 struct GNUNET_TIME_Relative runtime;
1825 unsigned int minutes;
1826
1827 runtime = GNUNET_TIME_absolute_get_duration (pos->restart_at);
1828 minutes =
1829 runtime.rel_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us;
1830 if (minutes > 31)
1831 pos->backoff = GNUNET_TIME_UNIT_ZERO;
1832 else
1833 pos->backoff.rel_value_us >>= minutes;
1834 }
1835 /* schedule restart */
1836 pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
1837 pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
1838 if (NULL != child_restart_task)
1839 GNUNET_SCHEDULER_cancel (child_restart_task);
1840 child_restart_task =
1841 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1842 &delayed_restart_task,
1843 NULL);
1844 }
1845 }
1846 else
1847 {
1848 free_service (pos);
1849 }
1850 }
1851 child_death_task =
1852 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1853 pr,
1854 &maint_child_death,
1855 NULL);
1856 if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
1857 do_shutdown ();
1858 else if (GNUNET_YES == in_shutdown)
1859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1860 "Delaying shutdown after child's death, still have %u children\n",
1861 list_count (running_head));
1862}
1863
1864
1865/**
1866 * Signal handler called for SIGCHLD. Triggers the
1867 * respective handler by writing to the trigger pipe.
1868 */
1869static void
1870sighandler_child_death ()
1871{
1872 static char c;
1873 int old_errno = errno; /* back-up errno */
1874
1875 GNUNET_break (
1876 1 ==
1877 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
1878 GNUNET_DISK_PIPE_END_WRITE),
1879 &c,
1880 sizeof(c)));
1881 errno = old_errno; /* restore errno */
1882}
1883
1884
1885/**
1886 * Setup our service record for the given section in the configuration file
1887 * (assuming the section is for a service).
1888 *
1889 * @param cls unused
1890 * @param section a section in the configuration file
1891 * @return #GNUNET_OK (continue)
1892 */
1893static void
1894setup_service (void *cls, const char *section)
1895{
1896 struct ServiceList *sl;
1897 char *binary;
1898 char *config;
1899 struct stat sbuf;
1900 struct sockaddr **addrs;
1901 socklen_t *addr_lens;
1902 int ret;
1903
1904 (void) cls;
1905 if (0 == strcasecmp (section, "arm"))
1906 return;
1907 if (GNUNET_OK !=
1908 GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary))
1909 {
1910 /* not a service section */
1911 return;
1912 }
1913 if ((GNUNET_YES ==
1914 GNUNET_CONFIGURATION_have_value (cfg, section, "RUN_PER_USER")) &&
1915 (GNUNET_YES ==
1916 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "RUN_PER_USER")))
1917 {
1918 if (GNUNET_NO == start_user)
1919 {
1920 GNUNET_free (binary);
1921 return; /* user service, and we don't deal with those */
1922 }
1923 }
1924 else
1925 {
1926 if (GNUNET_NO == start_system)
1927 {
1928 GNUNET_free (binary);
1929 return; /* system service, and we don't deal with those */
1930 }
1931 }
1932 sl = find_service (section);
1933 if (NULL != sl)
1934 {
1935 /* got the same section twice!? */
1936 GNUNET_break (0);
1937 GNUNET_free (binary);
1938 return;
1939 }
1940 config = NULL;
1941 if (((GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1942 section,
1943 "CONFIG",
1944 &config)) &&
1945 (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1946 "PATHS",
1947 "DEFAULTCONFIG",
1948 &config))) ||
1949 (0 != stat (config, &sbuf)))
1950 {
1951 if (NULL != config)
1952 {
1953 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
1954 section,
1955 "CONFIG",
1956 strerror (errno));
1957 GNUNET_free (config);
1958 config = NULL;
1959 }
1960 }
1961 sl = GNUNET_new (struct ServiceList);
1962 sl->name = GNUNET_strdup (section);
1963 sl->binary = binary;
1964 sl->config = config;
1965 sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1966 sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS;
1967 if (GNUNET_CONFIGURATION_have_value (cfg, section, "PIPECONTROL"))
1968 sl->pipe_control =
1969 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "PIPECONTROL");
1970 GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
1971 if (GNUNET_YES ==
1972 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "IMMEDIATE_START"))
1973 {
1974 sl->force_start = GNUNET_YES;
1975 if (GNUNET_YES ==
1976 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "NOARMBIND"))
1977 return;
1978 }
1979 else
1980 {
1981 if (GNUNET_YES !=
1982 GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "START_ON_DEMAND"))
1983 return;
1984 }
1985 if (0 >= (ret = get_server_addresses (section, cfg, &addrs, &addr_lens)))
1986 return;
1987 /* this will free (or capture) addrs[i] */
1988 for (unsigned int i = 0; i < (unsigned int) ret; i++)
1989 create_listen_socket (addrs[i], addr_lens[i], sl);
1990 GNUNET_free (addrs);
1991 GNUNET_free (addr_lens);
1992}
1993
1994
1995/**
1996 * A client connected, mark as a monitoring client.
1997 *
1998 * @param cls closure
1999 * @param client identification of the client
2000 * @param mq queue to talk to @a client
2001 * @return @a client
2002 */
2003static void *
2004client_connect_cb (void *cls,
2005 struct GNUNET_SERVICE_Client *client,
2006 struct GNUNET_MQ_Handle *mq)
2007{
2008 /* All clients are considered to be of the "monitor" kind
2009 * (that is, they don't affect ARM shutdown).
2010 */
2011 (void) cls;
2012 (void) mq;
2013 GNUNET_SERVICE_client_mark_monitor (client);
2014 return client;
2015}
2016
2017
2018/**
2019 * A client disconnected, clean up associated state.
2020 *
2021 * @param cls closure
2022 * @param client identification of the client
2023 * @param app_ctx must match @a client
2024 */
2025static void
2026client_disconnect_cb (void *cls,
2027 struct GNUNET_SERVICE_Client *client,
2028 void *app_ctx)
2029{
2030 (void) cls;
2031 GNUNET_assert (client == app_ctx);
2032 for (struct ServiceList *sl = running_head; NULL != sl; sl = sl->next)
2033 if (sl->killing_client == client)
2034 sl->killing_client = NULL;
2035}
2036
2037
2038/**
2039 * Handle MONITOR-message.
2040 *
2041 * @param cls identification of the client
2042 * @param message the actual message
2043 * @return #GNUNET_OK to keep the connection open,
2044 * #GNUNET_SYSERR to close it (signal serious error)
2045 */
2046static void
2047handle_monitor (void *cls, const struct GNUNET_MessageHeader *message)
2048{
2049 struct GNUNET_SERVICE_Client *client = cls;
2050
2051 (void) message;
2052 /* FIXME: might want to start by letting monitor know about
2053 services that are already running */
2054 /* Removal is handled by the server implementation, internally. */
2055 GNUNET_notification_context_add (notifier,
2056 GNUNET_SERVICE_client_get_mq (client));
2057 broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client);
2058 GNUNET_SERVICE_client_continue (client);
2059}
2060
2061
2062/**
2063 * Process arm requests.
2064 *
2065 * @param cls closure, NULL
2066 * @param serv the initialized service
2067 * @param c configuration to use
2068 */
2069static void
2070run (void *cls,
2071 const struct GNUNET_CONFIGURATION_Handle *c,
2072 struct GNUNET_SERVICE_Handle *serv)
2073{
2074 struct ServiceList *sl;
2075
2076 (void) cls;
2077 cfg = c;
2078 service = serv;
2079 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
2080 child_death_task = GNUNET_SCHEDULER_add_read_file (
2081 GNUNET_TIME_UNIT_FOREVER_REL,
2082 GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ),
2083 &maint_child_death,
2084 NULL);
2085#if HAVE_WAIT4
2086 if (GNUNET_OK ==
2087 GNUNET_CONFIGURATION_get_value_filename (cfg,
2088 "ARM",
2089 "RESOURCE_DIAGNOSTICS",
2090 &wait_filename))
2091 {
2092 wait_file = fopen (wait_filename, "w");
2093 if (NULL == wait_file)
2094 {
2095 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2096 "fopen",
2097 wait_filename);
2098 }
2099 }
2100#endif
2101 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2102 "ARM",
2103 "GLOBAL_PREFIX",
2104 &prefix_command))
2105 prefix_command = GNUNET_strdup ("");
2106 else
2107 prefix_command = GNUNET_CONFIGURATION_expand_dollar (cfg, prefix_command);
2108 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2109 "ARM",
2110 "GLOBAL_POSTFIX",
2111 &final_option))
2112 final_option = GNUNET_strdup ("");
2113 else
2114 final_option = GNUNET_CONFIGURATION_expand_dollar (cfg, final_option);
2115 start_user =
2116 GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "START_USER_SERVICES");
2117 start_system =
2118 GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "START_SYSTEM_SERVICES");
2119 if ((GNUNET_NO == start_user) && (GNUNET_NO == start_system))
2120 {
2121 GNUNET_log (
2122 GNUNET_ERROR_TYPE_ERROR,
2123 "Please configure either START_USER_SERVICES or START_SYSTEM_SERVICES or both.\n");
2124 GNUNET_SCHEDULER_shutdown ();
2125 global_ret = 1;
2126 return;
2127 }
2128 GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL);
2129
2130 /* start default services... */
2131 for (sl = running_head; NULL != sl; sl = sl->next)
2132 if (GNUNET_YES == sl->force_start)
2133 start_process (sl, NULL, 0);
2134 notifier = GNUNET_notification_context_create (MAX_NOTIFY_QUEUE);
2135}
2136
2137
2138/**
2139 * The main function for the arm service.
2140 *
2141 * @param argc number of arguments from the command line
2142 * @param argv command line arguments
2143 * @return 0 ok, 1 on error
2144 */
2145int
2146main (int argc, char *const *argv)
2147{
2148 struct GNUNET_SIGNAL_Context *shc_chld;
2149 struct GNUNET_MQ_MessageHandler handlers[] = {
2150 GNUNET_MQ_hd_var_size (start,
2151 GNUNET_MESSAGE_TYPE_ARM_START,
2152 struct GNUNET_ARM_Message,
2153 NULL),
2154 GNUNET_MQ_hd_var_size (stop,
2155 GNUNET_MESSAGE_TYPE_ARM_STOP,
2156 struct GNUNET_ARM_Message,
2157 NULL),
2158 GNUNET_MQ_hd_fixed_size (monitor,
2159 GNUNET_MESSAGE_TYPE_ARM_MONITOR,
2160 struct GNUNET_MessageHeader,
2161 NULL),
2162 GNUNET_MQ_hd_fixed_size (list,
2163 GNUNET_MESSAGE_TYPE_ARM_LIST,
2164 struct GNUNET_ARM_Message,
2165 NULL),
2166 GNUNET_MQ_hd_fixed_size (test,
2167 GNUNET_MESSAGE_TYPE_ARM_TEST,
2168 struct GNUNET_MessageHeader,
2169 NULL),
2170 GNUNET_MQ_handler_end ()
2171 };
2172
2173 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
2174 GNUNET_assert (NULL != sigpipe);
2175 shc_chld =
2176 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
2177 &sighandler_child_death);
2178 if (0 != GNUNET_SERVICE_run_ (argc,
2179 argv,
2180 "arm",
2181 GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN
2182 | GNUNET_SERVICE_OPTION_CLOSE_LSOCKS,
2183 &run,
2184 &client_connect_cb,
2185 &client_disconnect_cb,
2186 NULL,
2187 handlers))
2188 global_ret = 2;
2189#if HAVE_WAIT4
2190 if (NULL != wait_file)
2191 {
2192 fclose (wait_file);
2193 wait_file = NULL;
2194 }
2195 if (NULL != wait_filename)
2196 {
2197 GNUNET_free (wait_filename);
2198 wait_filename = NULL;
2199 }
2200#endif
2201 GNUNET_SIGNAL_handler_uninstall (shc_chld);
2202 shc_chld = NULL;
2203 GNUNET_DISK_pipe_close (sigpipe);
2204 sigpipe = NULL;
2205 return global_ret;
2206}
2207
2208
2209#if defined(__linux__) && defined(__GLIBC__)
2210#include <malloc.h>
2211
2212/**
2213 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
2214 */
2215void __attribute__ ((constructor))
2216GNUNET_ARM_memory_init ()
2217{
2218 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
2219 mallopt (M_TOP_PAD, 1 * 1024);
2220 malloc_trim (0);
2221}
2222
2223
2224#endif
2225
2226
2227/* end of gnunet-service-arm.c */
diff --git a/src/service/arm/meson.build b/src/service/arm/meson.build
new file mode 100644
index 000000000..56c992efe
--- /dev/null
+++ b/src/service/arm/meson.build
@@ -0,0 +1,74 @@
1libgnunetarm_src = ['arm_api.c',
2 'arm_monitor_api.c']
3
4gnunetservicearm_src = ['gnunet-service-arm.c']
5
6testarmapi_src = ['test_arm_api.c']
7testexpbo_src = ['test_exponential_backoff.c']
8testgnunetservice_src = ['test_gnunet_service_arm.c']
9
10configure_file(input : 'arm.conf.in',
11 output : 'arm.conf',
12 configuration : cdata,
13 install: true,
14 install_dir: pkgcfgdir)
15
16libgnunetarm = library('gnunetarm',
17 libgnunetarm_src,
18 dependencies: libgnunetutil_dep,
19 version: '2.0.0',
20 soversion: '2',
21 include_directories: [incdir, configuration_inc],
22 install: true,
23 install_dir: get_option('libdir'))
24pkg.generate(libgnunetarm, url: 'https://www.gnunet.org',
25 description : 'Provides API for accessing the Automated Restart Manager service')
26libgnunetarm_dep = declare_dependency(link_with : libgnunetarm)
27
28executable ('gnunet-service-arm',
29 gnunetservicearm_src,
30 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
31 include_directories: [incdir, configuration_inc],
32 install: true,
33 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
34
35mockservice = executable ('mockup-service',
36 ['mockup-service.c'],
37 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
38 include_directories: [incdir, configuration_inc],
39 install: false)
40
41testarmapi = executable ('test_arm_api',
42 testarmapi_src,
43 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
44 include_directories: [incdir, configuration_inc],
45 install: false)
46testexpbo = executable ('test_exponential_backoff',
47 testexpbo_src,
48 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
49 include_directories: [incdir, configuration_inc],
50 install: false)
51testgnunetservice = executable ('test_gnunet_service',
52 testgnunetservice_src,
53 dependencies: [libgnunetarm_dep, libgnunetutil_dep],
54 include_directories: [incdir, configuration_inc],
55 install: false)
56configure_file(copy: true,
57 input: 'test_arm_api_data.conf',
58 output: 'test_arm_api_data.conf')
59
60# FIXME: We need to convert the convoluted awk script from Makefile.am here
61#configure_file(copy: true,
62# input: 'test_gnunet_arm.py.in',
63# output: 'test_gnunet_arm.py')
64
65test('test_arm_api', testarmapi, workdir: meson.current_build_dir(),
66 is_parallel: false,
67 suite: 'arm')
68test('test_exponential_backoff', testexpbo, workdir: meson.current_build_dir(),
69 depends: mockservice,
70 is_parallel: false,
71 timeout: 60, suite: 'arm')
72test('test_gnunet_service_arm', testgnunetservice, workdir: meson.current_build_dir(),
73 is_parallel: false,
74 suite: 'arm')
diff --git a/src/service/arm/mockup-service.c b/src/service/arm/mockup-service.c
new file mode 100644
index 000000000..3d3f79d35
--- /dev/null
+++ b/src/service/arm/mockup-service.c
@@ -0,0 +1,123 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007, 2008, 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#include "platform.h"
22#include <stdlib.h>
23#include "gnunet_util_lib.h"
24#include "gnunet_protocols.h"
25
26
27static int special_ret = 0;
28
29/**
30 * Handler for STOP message.
31 *
32 * @param cls client identification of the client
33 * @param message the actual message
34 */
35static void
36handle_stop (void *cls, const struct GNUNET_MessageHeader *message)
37{
38 struct GNUNET_SERVICE_Client *client = cls;
39
40 (void) message;
41 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
42 _ ("Initiating shutdown as requested by client.\n"));
43 GNUNET_SERVICE_client_persist (client);
44 GNUNET_SCHEDULER_shutdown ();
45 /* ARM won't exponentially increase restart delay if we
46 * terminate normally. This changes the return code.
47 */
48 special_ret = 1;
49}
50
51
52/**
53 * Callback called when a client connects to the service.
54 *
55 * @param cls closure for the service
56 * @param c the new client that connected to the service
57 * @param mq the message queue used to send messages to the client
58 * @return @a c
59 */
60static void *
61client_connect_cb (void *cls,
62 struct GNUNET_SERVICE_Client *c,
63 struct GNUNET_MQ_Handle *mq)
64{
65 (void) cls;
66 (void) mq;
67 return c;
68}
69
70
71/**
72 * Callback called when a client disconnected from the service
73 *
74 * @param cls closure for the service
75 * @param c the client that disconnected
76 * @param internal_cls should be equal to @a c
77 */
78static void
79client_disconnect_cb (void *cls,
80 struct GNUNET_SERVICE_Client *c,
81 void *internal_cls)
82{
83 (void) cls;
84 GNUNET_assert (c == internal_cls);
85}
86
87
88static void
89run (void *cls,
90 const struct GNUNET_CONFIGURATION_Handle *cfg,
91 struct GNUNET_SERVICE_Handle *service)
92{
93 (void) cls;
94 (void) cfg;
95 (void) service;
96 /* nothing to do */
97}
98
99
100/**
101 * Define "main" method using service macro.
102 */
103GNUNET_SERVICE_MAIN ("do-nothing",
104 GNUNET_SERVICE_OPTION_NONE,
105 &run,
106 &client_connect_cb,
107 &client_disconnect_cb,
108 NULL,
109 GNUNET_MQ_hd_fixed_size (stop,
110 GNUNET_MESSAGE_TYPE_ARM_STOP,
111 struct GNUNET_MessageHeader,
112 NULL),
113 GNUNET_MQ_handler_end ());
114
115
116/**
117 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
118 */
119void __attribute__ ((destructor))
120GNUNET_mockup_done ()
121{
122 _exit (special_ret);
123}
diff --git a/src/service/arm/mockup_service b/src/service/arm/mockup_service
new file mode 100755
index 000000000..32f4ccc14
--- /dev/null
+++ b/src/service/arm/mockup_service
@@ -0,0 +1,130 @@
1#! /bin/sh
2
3# mockup_service - temporary wrapper script for .libs/mockup_service
4# Generated by ltmain.sh (GNU libtool) 2.2.6
5#
6# The mockup_service program cannot be directly executed until all the libtool
7# libraries that it depends on are installed.
8#
9# This wrapper script should never be moved out of the build directory.
10# If it is, it will not operate correctly.
11
12# Sed substitution that helps us do robust quoting. It backslashifies
13# metacharacters that are still active within double-quoted strings.
14Xsed='/usr/bin/sed -e 1s/^X//'
15sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
16
17# Be Bourne compatible
18if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
19 emulate sh
20 NULLCMD=:
21 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
22 # is contrary to our usage. Disable this feature.
23 alias -g '${1+"$@"}'='"$@"'
24 setopt NO_GLOB_SUBST
25else
26 case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
27fi
28BIN_SH=xpg4; export BIN_SH # for Tru64
29DUALCASE=1; export DUALCASE # for MKS sh
30
31# The HP-UX ksh and POSIX shell print the target directory to stdout
32# if CDPATH is set.
33(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
34
35relink_command=""
36
37# This environment variable determines our operation mode.
38if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
39 # install mode needs the following variables:
40 generated_by_libtool_version='2.2.6'
41 notinst_deplibs=' ../../src/util/libgnunetutil.la'
42else
43 # When we are sourced in execute mode, $file and $ECHO are already set.
44 if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
45 ECHO="echo"
46 file="$0"
47 # Make sure echo works.
48 if test "X$1" = X--no-reexec; then
49 # Discard the --no-reexec flag, and continue.
50 shift
51 elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
52 # Yippee, $ECHO works!
53 :
54 else
55 # Restart under the correct shell, and then maybe $ECHO will work.
56 exec /bin/sh "$0" --no-reexec ${1+"$@"}
57 fi
58 fi
59
60 # Find the directory that this script lives in.
61 thisdir=`$ECHO "X$file" | $Xsed -e 's%/[^/]*$%%'`
62 test "x$thisdir" = "x$file" && thisdir=.
63
64 # Follow symbolic links until we get to the real thisdir.
65 file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
66 while test -n "$file"; do
67 destdir=`$ECHO "X$file" | $Xsed -e 's%/[^/]*$%%'`
68
69 # If there was a directory component, then change thisdir.
70 if test "x$destdir" != "x$file"; then
71 case "$destdir" in
72 [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
73 *) thisdir="$thisdir/$destdir" ;;
74 esac
75 fi
76
77 file=`$ECHO "X$file" | $Xsed -e 's%^.*/%%'`
78 file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
79 done
80
81
82 # Usually 'no', except on cygwin/mingw when embedded into
83 # the cwrapper.
84 WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
85 if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
86 # special case for '.'
87 if test "$thisdir" = "."; then
88 thisdir=`pwd`
89 fi
90 # remove .libs from thisdir
91 case "$thisdir" in
92 *[\\/].libs ) thisdir=`$ECHO "X$thisdir" | $Xsed -e 's%[\\/][^\\/]*$%%'` ;;
93 .libs ) thisdir=. ;;
94 esac
95 fi
96
97 # Try to get the absolute directory name.
98 absdir=`cd "$thisdir" && pwd`
99 test -n "$absdir" && thisdir="$absdir"
100
101 program='mockup_service'
102 progdir="$thisdir/.libs"
103
104
105 if test -f "$progdir/$program"; then
106 # Add our own library path to DYLD_LIBRARY_PATH
107 DYLD_LIBRARY_PATH="/Users/soufi/Career/Munich/TUM/MasterOfScience/Research/GNUnet/EclipseWorkspaceGNUnet/gnunet/src/util/.libs:$DYLD_LIBRARY_PATH"
108
109 # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH
110 # The second colon is a workaround for a bug in BeOS R4 sed
111 DYLD_LIBRARY_PATH=`$ECHO "X$DYLD_LIBRARY_PATH" | $Xsed -e 's/::*$//'`
112
113 export DYLD_LIBRARY_PATH
114
115 if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
116 # Run the actual program with our arguments.
117
118 exec "$progdir/$program" ${1+"$@"}
119
120 $ECHO "$0: cannot exec $program $*" 1>&2
121 exit 1
122 fi
123 else
124 # The program doesn't exist.
125 $ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2
126 $ECHO "This script is just a wrapper for $program." 1>&2
127 echo "See the libtool documentation for more information." 1>&2
128 exit 1
129 fi
130fi
diff --git a/src/service/arm/test_arm_api.c b/src/service/arm/test_arm_api.c
new file mode 100644
index 000000000..56a0abbd2
--- /dev/null
+++ b/src/service/arm/test_arm_api.c
@@ -0,0 +1,258 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011, 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_arm_api.c
22 * @brief testcase for arm_api.c
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_arm_service.h"
27#include "gnunet_resolver_service.h"
28
29#define LOG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
30
31#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
32
33static const struct GNUNET_CONFIGURATION_Handle *cfg;
34
35static struct GNUNET_ARM_Handle *arm;
36
37static struct GNUNET_ARM_Operation *op;
38
39static int ok = 1;
40
41static int phase = 0;
42
43
44static void
45arm_stop_cb (void *cls,
46 enum GNUNET_ARM_RequestStatus status,
47 enum GNUNET_ARM_Result result)
48{
49 op = NULL;
50 /* (6), a stop request should be sent to ARM successfully */
51 /* ARM should report that it is stopping */
52 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
53 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
54 GNUNET_break (phase == 6);
55 phase++;
56 LOG ("Sent 'STOP' request for arm to ARM %s\n",
57 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
58 "unsuccessfully");
59 GNUNET_SCHEDULER_shutdown ();
60}
61
62
63static void
64resolver_stop_cb (void *cls,
65 enum GNUNET_ARM_RequestStatus status,
66 enum GNUNET_ARM_Result result)
67{
68 op = NULL;
69 /* (5), a stop request should be sent to ARM successfully.
70 * ARM should report that resolver is stopped.
71 */
72 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
73 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
74 GNUNET_break (phase == 5);
75 LOG ("Sent 'STOP' request for resolver to ARM %s\n",
76 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
77 "unsuccessfully");
78 phase++;
79 GNUNET_assert (NULL == op);
80 op = GNUNET_ARM_request_service_stop (arm,
81 "arm",
82 &arm_stop_cb,
83 NULL);
84}
85
86
87static void
88dns_notify (void *cls,
89 const struct sockaddr *addr,
90 socklen_t addrlen)
91{
92 if (addr == NULL)
93 {
94 /* (4), resolver should finish resolving localhost */
95 GNUNET_break (phase == 4);
96 phase++;
97 LOG ("Finished resolving localhost\n");
98 if (ok != 0)
99 ok = 2;
100 GNUNET_assert (NULL == op);
101 op = GNUNET_ARM_request_service_stop (arm,
102 "resolver",
103 &resolver_stop_cb,
104 NULL);
105 return;
106 }
107 /* (3), resolver should resolve localhost */
108 GNUNET_break (phase == 3);
109 LOG ("Resolved localhost\n");
110 phase++;
111 GNUNET_break (addr != NULL);
112 ok = 0;
113}
114
115
116static void
117resolver_start_cb (void *cls,
118 enum GNUNET_ARM_RequestStatus status,
119 enum GNUNET_ARM_Result result)
120{
121 op = NULL;
122 /* (2), the start request for resolver should be sent successfully
123 * ARM should report that resolver service is starting.
124 */
125 GNUNET_assert (status == GNUNET_ARM_REQUEST_SENT_OK);
126 GNUNET_break (phase == 2);
127 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
128 LOG ("Sent 'START' request for resolver to ARM %s\n",
129 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
130 "unsuccessfully");
131 phase++;
132 GNUNET_RESOLVER_ip_get ("localhost",
133 AF_INET,
134 TIMEOUT,
135 &dns_notify, NULL);
136}
137
138
139static void
140arm_conn (void *cls,
141 int connected)
142{
143 if (GNUNET_SYSERR == connected)
144 {
145 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 _ ("Fatal error initializing ARM API.\n"));
147 GNUNET_SCHEDULER_shutdown ();
148 GNUNET_assert (0);
149 return;
150 }
151 if (GNUNET_YES == connected)
152 {
153 /* (1), arm connection should be established */
154 LOG ("Connected to ARM\n");
155 GNUNET_break (phase == 1);
156 phase++;
157 GNUNET_assert (NULL == op);
158 op = GNUNET_ARM_request_service_start (arm,
159 "resolver",
160 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
161 &resolver_start_cb,
162 NULL);
163 }
164 else
165 {
166 /* (7), ARM should stop (we disconnect from it) */
167 LOG ("Disconnected from ARM\n");
168 GNUNET_break (phase == 7);
169 if (phase != 7)
170 ok = 3;
171 else if (ok == 1)
172 ok = 0;
173 }
174}
175
176
177static void
178arm_start_cb (void *cls,
179 enum GNUNET_ARM_RequestStatus status,
180 enum GNUNET_ARM_Result result)
181{
182 op = NULL;
183 /* (0) The request should be "sent" successfully
184 * ("sent", because it isn't going anywhere, ARM API starts ARM service
185 * by itself).
186 * ARM API should report that ARM service is starting.
187 */GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
188 GNUNET_break (phase == 0);
189 LOG ("Sent 'START' request for arm to ARM %s\n",
190 (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" :
191 "unsuccessfully");
192 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
193 phase++;
194}
195
196
197static void
198do_shutdown (void *cls)
199{
200 if (NULL != op)
201 {
202 GNUNET_ARM_operation_cancel (op);
203 op = NULL;
204 }
205 if (NULL != arm)
206 {
207 GNUNET_ARM_disconnect (arm);
208 arm = NULL;
209 }
210}
211
212
213static void
214task (void *cls,
215 char *const *args,
216 const char *cfgfile,
217 const struct GNUNET_CONFIGURATION_Handle *c)
218{
219 cfg = c;
220 arm = GNUNET_ARM_connect (cfg,
221 &arm_conn,
222 NULL);
223 if (NULL == arm)
224 return;
225 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
226 NULL);
227 op = GNUNET_ARM_request_service_start (arm,
228 "arm",
229 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
230 &arm_start_cb,
231 NULL);
232}
233
234
235int
236main (int argc, char *argvx[])
237{
238 char *const argv[] = {
239 "test-arm-api",
240 "-c", "test_arm_api_data.conf",
241 NULL
242 };
243 struct GNUNET_GETOPT_CommandLineOption options[] = {
244 GNUNET_GETOPT_OPTION_END
245 };
246
247 GNUNET_log_setup ("test-arm-api",
248 "WARNING",
249 NULL);
250 GNUNET_assert (GNUNET_OK ==
251 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
252 argv, "test-arm-api", "nohelp", options,
253 &task, NULL));
254 return ok;
255}
256
257
258/* end of test_arm_api.c */
diff --git a/src/service/arm/test_arm_api_data.conf b/src/service/arm/test_arm_api_data.conf
new file mode 100644
index 000000000..13be62b53
--- /dev/null
+++ b/src/service/arm/test_arm_api_data.conf
@@ -0,0 +1,38 @@
1@INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[PATHS]
5GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-arm/
6
7[arm]
8BINARY = gnunet-service-arm
9OPTIONS = -L ERROR
10#PREFIX = valgrind --tool=memcheck --leak-check=yes
11
12[resolver]
13START_ON_DEMAND = YES
14PORT = 23355
15# PREFIX = valgrind
16
17[do-nothing]
18START_ON_DEMAND = NO
19PORT = 48223
20HOSTNAME = localhost
21BINARY = /will/be/overwritten/by/test_exponential_backoff
22ACCEPT_FROM = 127.0.0.1;
23ACCEPT_FROM6 = ::1;
24
25[statistics]
26START_ON_DEMAND = YES
27
28[core]
29START_ON_DEMAND = NO
30
31[transport]
32START_ON_DEMAND = NO
33
34[ats]
35START_ON_DEMAND = NO
36
37[peerinfo]
38START_ON_DEMAND = NO
diff --git a/src/service/arm/test_exponential_backoff.c b/src/service/arm/test_exponential_backoff.c
new file mode 100644
index 000000000..e3eed8568
--- /dev/null
+++ b/src/service/arm/test_exponential_backoff.c
@@ -0,0 +1,416 @@
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
48static const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50static struct GNUNET_ARM_Handle *arm;
51
52static struct GNUNET_ARM_MonitorHandle *mon;
53
54static struct GNUNET_SCHEDULER_Task *kt;
55
56static int ok = 1;
57
58static int phase = 0;
59
60static int trialCount;
61
62static struct GNUNET_TIME_Absolute startedWaitingAt;
63
64struct GNUNET_TIME_Relative waitedFor;
65
66struct GNUNET_TIME_Relative waitedFor_prev;
67
68#if LOG_BACKOFF
69static FILE *killLogFilePtr;
70
71static char *killLogFileName;
72#endif
73
74
75/**
76 * Context for handling the shutdown of a service.
77 */
78struct 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
92static void
93kill_task (void *cbData);
94
95
96/**
97 * Shutting down took too long, cancel receive and return error.
98 *
99 * @param cls closure
100 */
101static void
102service_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 */
116static void
117mq_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
130static void
131kill_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
173static void
174trigger_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
186static void
187arm_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
198static void
199srv_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
259static void
260arm_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
273static void
274task (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
306static int
307check ()
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
339static int
340init ()
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
388static void
389houseKeep ()
390{
391#if LOG_BACKOFF
392 GNUNET_assert (0 == fclose (killLogFilePtr));
393 GNUNET_free (killLogFileName);
394#endif
395 (void) unlink (CFGFILENAME);
396}
397
398
399int
400main (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 */
diff --git a/src/service/arm/test_gnunet_arm.py.in b/src/service/arm/test_gnunet_arm.py.in
new file mode 100644
index 000000000..2b30b6b97
--- /dev/null
+++ b/src/service/arm/test_gnunet_arm.py.in
@@ -0,0 +1,129 @@
1#!@PYTHONEXE@
2
3import os
4import sys
5import shutil
6import re
7import subprocess
8import time
9
10# FIXME: There's too much repetition, move generally used parts into reusable modules.
11if os.name == "nt":
12 tmp = os.getenv("TEMP")
13else:
14 tmp = "/tmp"
15
16if os.name == 'nt':
17 st = 'gnunet-statistics.exe'
18 arm = './gnunet-arm.exe'
19else:
20 st = 'gnunet-statistics'
21 arm = './gnunet-arm'
22
23run_arm = [arm, '-c', 'test_arm_api_data.conf', '--no-stdout', '--no-stderr']
24debug = os.getenv('DEBUG')
25if debug:
26 run_arm += [debug.split(' ')]
27
28
29def cleanup():
30 shutil.rmtree(os.path.join(tmp, "test-gnunetd-arm"), True)
31
32
33def sub_run(args, want_stdo=True, want_stde=False, nofail=False):
34 if want_stdo:
35 stdo = subprocess.PIPE
36 else:
37 stdo = None
38 if want_stde:
39 stde = subprocess.PIPE
40 else:
41 stde = None
42 p = subprocess.Popen(args, stdout=stdo, stderr=stde)
43 stdo, stde = p.communicate()
44 if not nofail:
45 if p.returncode != 0:
46 sys.exit(p.returncode)
47 return (p.returncode, stdo, stde)
48
49
50def fail(result):
51 print(result)
52 r_arm(['-e'], want_stdo=False)
53 sys.exit(1)
54
55
56def end_arm_failure(command, rc, stdo, stde, normal):
57 if normal:
58 if rc != 0:
59 fail(
60 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
61 command, stdo, stde
62 )
63 )
64 else:
65 if rc == 0:
66 fail(
67 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
68 .format(command, stdo, stde)
69 )
70
71
72def print_only_failure(command, rc, stdo, stde, normal):
73 if normal:
74 if rc != 0:
75 print(
76 "FAIL: error running {}\nCommand output was:\n{}\n{}".format(
77 command, stdo, stde
78 )
79 )
80 sys.exit(1)
81 else:
82 if rc == 0:
83 print(
84 "FAIL: expected error while running {}\nCommand output was:\n{}\n{}"
85 .format(command, stdo, stde)
86 )
87 sys.exit(1)
88
89
90def r_something(to_run, extra_args, failure=None, normal=True, **kw):
91 rc, stdo, stde = sub_run(
92 to_run + extra_args, nofail=True, want_stde=True, **kw
93 )
94 if failure is not None:
95 failure(to_run + extra_args, rc, stdo, stde, normal)
96 return (rc, stdo, stde)
97
98
99def r_arm(extra_args, **kw):
100 return r_something(run_arm, extra_args, **kw)
101
102
103cleanup()
104
105print("TEST: Bad argument checking...", end='')
106r_arm(['-x'], normal=False, failure=print_only_failure)
107print("PASS")
108
109print("TEST: Start ARM...", end='')
110r_arm(['-s'], failure=print_only_failure)
111time.sleep(1)
112print("PASS")
113
114print("TEST: Start another service...", end='')
115r_arm(['-i', 'resolver'], failure=end_arm_failure)
116time.sleep(1)
117print("PASS")
118
119print("TEST: Stop a service...", end='')
120r_arm(['-k', 'resolver'], failure=end_arm_failure)
121time.sleep(1)
122print("PASS")
123
124print("TEST: Stop ARM...", end='')
125r_arm(['-e'], failure=print_only_failure)
126time.sleep(1)
127print("PASS")
128
129cleanup()
diff --git a/src/service/arm/test_gnunet_service_arm.c b/src/service/arm/test_gnunet_service_arm.c
new file mode 100644
index 000000000..df4ad95c2
--- /dev/null
+++ b/src/service/arm/test_gnunet_service_arm.c
@@ -0,0 +1,266 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2014 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_gnunet_service_arm.c
22 * @brief testcase for gnunet-service-arm.c; tests ARM by making it start the resolver
23 * @author Safey
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_resolver_service.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * Timeout for starting services, very short because of the strange way start works
33 * (by checking if running before starting, so really this time is always waited on
34 * startup (annoying)).
35 */
36#define START_TIMEOUT GNUNET_TIME_relative_multiply ( \
37 GNUNET_TIME_UNIT_MILLISECONDS, 50)
38
39#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
40
41
42static int ret = 1;
43
44static int resolved_ok;
45
46static int asked_for_a_list;
47
48static struct GNUNET_ARM_Handle *arm;
49
50static const char hostname[] = "www.gnu.org"; /* any domain should do */
51
52
53static void
54trigger_disconnect (void *cls)
55{
56 GNUNET_ARM_disconnect (arm);
57 arm = NULL;
58}
59
60
61static void
62arm_stop_cb (void *cls,
63 enum GNUNET_ARM_RequestStatus status,
64 enum GNUNET_ARM_Result result)
65{
66 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
67 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
68 if (result != GNUNET_ARM_RESULT_STOPPED)
69 {
70 GNUNET_break (0);
71 ret = 4;
72 }
73 GNUNET_SCHEDULER_add_now (&trigger_disconnect, NULL);
74}
75
76
77static void
78service_list (void *cls,
79 enum GNUNET_ARM_RequestStatus rs,
80 unsigned int count,
81 const struct GNUNET_ARM_ServiceInfo *list)
82{
83 unsigned int i;
84
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "%u services are are currently running\n",
87 count);
88 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
89 goto stop_arm;
90 for (i = 0; i < count; i++)
91 {
92 if ((0 == strcasecmp (list[i].name, "resolver")) &&
93 (0 == strcasecmp (list[i].binary, "gnunet-service-resolver")))
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "Got service list, now stopping arm\n");
97 ret = 0;
98 }
99 }
100
101stop_arm:
102 GNUNET_ARM_request_service_stop (arm,
103 "arm",
104 &arm_stop_cb,
105 NULL);
106}
107
108
109static void
110hostname_resolve_cb (void *cls,
111 const struct sockaddr *addr,
112 socklen_t addrlen)
113{
114 if ((0 == ret) || (4 == ret) || (1 == resolved_ok))
115 return;
116 if (NULL == addr)
117 {
118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
119 "Failed to resolve hostname!\n");
120 GNUNET_break (0);
121 ret = 3;
122 GNUNET_ARM_request_service_stop (arm,
123 "arm",
124 &arm_stop_cb,
125 NULL);
126 return;
127 }
128 if (0 == asked_for_a_list)
129 {
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Resolved hostname, now checking the service list\n");
132 GNUNET_ARM_request_service_list (arm,
133 &service_list,
134 NULL);
135 asked_for_a_list = 1;
136 resolved_ok = 1;
137 }
138}
139
140
141static void
142arm_start_cb (void *cls,
143 enum GNUNET_ARM_RequestStatus status,
144 enum GNUNET_ARM_Result result)
145{
146 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
147 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Trying to resolve a hostname via the resolver service!\n");
150 /* connect to the resolver service */
151 if (NULL ==
152 GNUNET_RESOLVER_ip_get (hostname,
153 AF_UNSPEC,
154 TIMEOUT,
155 &hostname_resolve_cb,
156 NULL))
157 {
158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 "Unable initiate connection to resolver service\n");
160 GNUNET_break (0);
161 ret = 2;
162 GNUNET_ARM_request_service_stop (arm,
163 "arm",
164 &arm_stop_cb,
165 NULL);
166 }
167}
168
169
170static void
171run (void *cls,
172 char *const *args,
173 const char *cfgfile,
174 const struct GNUNET_CONFIGURATION_Handle *c)
175{
176 arm = GNUNET_ARM_connect (c,
177 NULL,
178 NULL);
179 GNUNET_ARM_request_service_start (arm,
180 "arm",
181 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
182 &arm_start_cb,
183 NULL);
184}
185
186
187int
188main (int argc, char *av[])
189{
190 static char *const argv[] = {
191 "test-gnunet-service-arm",
192 "-c",
193 "test_arm_api_data.conf",
194 NULL
195 };
196 static struct GNUNET_GETOPT_CommandLineOption options[] = {
197 GNUNET_GETOPT_OPTION_END
198 };
199
200 /* trigger DNS lookup */
201#if HAVE_GETADDRINFO
202 {
203 struct addrinfo *ai;
204 int ret;
205
206 if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
207 {
208 fprintf (stderr,
209 "Failed to resolve `%s', testcase not run.\n",
210 hostname);
211 return 77;
212 }
213 freeaddrinfo (ai);
214 }
215#elif HAVE_GETHOSTBYNAME2
216 {
217 struct hostent *host;
218
219 host = gethostbyname2 (hostname, AF_INET);
220 if (NULL == host)
221 host = gethostbyname2 (hostname, AF_INET6);
222 if (NULL == host)
223 {
224 fprintf (stderr,
225 "Failed to resolve `%s', testcase not run.\n",
226 hostname);
227 return 77;
228 }
229 }
230#elif HAVE_GETHOSTBYNAME
231 {
232 struct hostent *host;
233
234 host = gethostbyname (hostname);
235 if (NULL == host)
236 {
237 fprintf (stderr,
238 "Failed to resolve `%s', testcase not run.\n",
239 hostname);
240 return 77;
241 }
242 }
243#else
244 fprintf (stderr,
245 "libc fails to have resolver function, testcase not run.\n");
246 return 77;
247#endif
248 GNUNET_log_setup ("test-gnunet-service-arm",
249 "WARNING",
250 NULL);
251 GNUNET_break (GNUNET_OK ==
252 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
253 argv, "test-gnunet-service-arm",
254 "nohelp", options,
255 &run, NULL));
256 if (0 != ret)
257 {
258 fprintf (stderr,
259 "Test failed with error code %d\n",
260 ret);
261 }
262 return ret;
263}
264
265
266/* end of test_gnunet_service_arm.c */
diff --git a/src/service/arm/testing_arm_cmd_start_peer.c b/src/service/arm/testing_arm_cmd_start_peer.c
new file mode 100644
index 000000000..73d7ca4e7
--- /dev/null
+++ b/src/service/arm/testing_arm_cmd_start_peer.c
@@ -0,0 +1,293 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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/**
22 * @file testing_api_cmd_start_peer.c
23 * @brief cmd to start a peer.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_testbed_lib.h"
30#include "gnunet_testing_testbed_lib.h"
31#include "gnunet_testing_arm_lib.h"
32
33
34/**
35 * Handle for a peer controlled via ARM.
36 */
37struct GNUNET_TESTING_StartPeerState
38{
39
40 const char *system_label;
41
42 const char *cfgname;
43
44 /**
45 * Our interpreter.
46 */
47 struct GNUNET_TESTING_Interpreter *is;
48
49 /**
50 * Asynchronous start context.
51 */
52 struct GNUNET_TESTING_AsyncContext ac;
53
54 /**
55 * The TESTBED system associated with this peer
56 */
57 struct GNUNET_TESTBED_System *system;
58
59 /**
60 * The handle to the peer's ARM service
61 */
62 struct GNUNET_ARM_Handle *ah;
63
64 /**
65 * Handle to the ARM process information.
66 */
67 struct GNUNET_OS_Process *arm;
68
69 /**
70 * The config of the peer
71 */
72 struct GNUNET_CONFIGURATION_Handle *cfg;
73
74};
75
76
77/**
78 * Function called whenever we connect to or disconnect from ARM.
79 *
80 * @param cls closure
81 * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
82 * #GNUNET_SYSERR if there was an error.
83 */
84static void
85conn_status (
86 void *cls,
87 enum GNUNET_GenericReturnValue connected)
88{
89 struct GNUNET_TESTING_StartPeerState *sps = cls;
90
91 if (GNUNET_OK != connected)
92 {
93 GNUNET_break (0);
94 GNUNET_TESTING_async_fail (&sps->ac);
95 return;
96 }
97 GNUNET_TESTING_async_finish (&sps->ac);
98}
99
100
101/**
102 * The run method of this cmd will start all services of a peer to test the transport service.
103 *
104 */
105static void
106start_peer_run (void *cls,
107 struct GNUNET_TESTING_Interpreter *is)
108{
109 struct GNUNET_TESTING_StartPeerState *sps = cls;
110 const struct GNUNET_TESTING_Command *system_cmd;
111
112 sps->is = is;
113 if (GNUNET_NO ==
114 GNUNET_DISK_file_test (sps->cfgname))
115 {
116 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
117 "File not found: `%s'\n",
118 sps->cfgname);
119 GNUNET_TESTING_FAIL (is);
120 }
121 system_cmd
122 = GNUNET_TESTING_interpreter_lookup_command (is,
123 sps->system_label);
124 if (NULL == system_cmd)
125 GNUNET_TESTING_FAIL (is);
126 if (GNUNET_OK !=
127 GNUNET_TESTING_TESTBED_get_trait_test_system (
128 system_cmd,
129 &sps->system))
130 GNUNET_TESTING_FAIL (is);
131 sps->cfg = GNUNET_CONFIGURATION_create ();
132 if (GNUNET_OK !=
133 GNUNET_CONFIGURATION_load (sps->cfg,
134 sps->cfgname))
135 GNUNET_TESTING_FAIL (is);
136 if (GNUNET_SYSERR ==
137 GNUNET_TESTBED_configuration_create (sps->system,
138 sps->cfg,
139 NULL,
140 NULL))
141 GNUNET_TESTING_FAIL (is);
142 {
143 char *config_filename;
144 char *libexec_binary;
145 char *main_binary;
146 char *args;
147 char *prefix;
148
149 GNUNET_assert (
150 GNUNET_OK ==
151 GNUNET_CONFIGURATION_get_value_filename (
152 sps->cfg,
153 "PATHS",
154 "DEFAULTCONFIG",
155 &config_filename));
156 if (GNUNET_OK !=
157 GNUNET_CONFIGURATION_write (sps->cfg,
158 config_filename))
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
161 "Failed to write configuration file `%s': %s\n",
162 config_filename,
163 strerror (errno));
164 GNUNET_free (config_filename);
165 GNUNET_TESTING_FAIL (is);
166 }
167
168 libexec_binary
169 = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
170
171 if (GNUNET_SYSERR ==
172 GNUNET_CONFIGURATION_get_value_string (sps->cfg,
173 "arm",
174 "PREFIX",
175 &prefix))
176 {
177 /* No prefix */
178 main_binary = libexec_binary;
179 args = GNUNET_strdup ("");
180 }
181 else
182 {
183 main_binary = prefix;
184 args = libexec_binary;
185 }
186 sps->arm
187 = GNUNET_OS_start_process_s (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
188 NULL,
189 main_binary,
190 args,
191 "-c",
192 config_filename,
193 NULL);
194 if (NULL == sps->arm)
195 {
196 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197 _ ("Failed to start `%s': %s\n"),
198 main_binary,
199 strerror (errno));
200 GNUNET_TESTING_FAIL (is);
201 }
202 GNUNET_free (config_filename);
203 GNUNET_free (main_binary);
204 GNUNET_free (args);
205 }
206
207 sps->ah = GNUNET_ARM_connect (sps->cfg,
208 &conn_status,
209 sps);
210 if (NULL == sps->ah)
211 GNUNET_TESTING_FAIL (is);
212}
213
214
215/**
216 * The cleanup function of this cmd frees resources the cmd allocated.
217 *
218 */
219static void
220start_peer_cleanup (void *cls)
221{
222 struct GNUNET_TESTING_StartPeerState *sps = cls;
223
224 if (NULL != sps->ah)
225 {
226 GNUNET_ARM_disconnect (sps->ah);
227 sps->ah = NULL;
228 }
229 if (NULL != sps->arm)
230 {
231 GNUNET_break (0 ==
232 GNUNET_OS_process_kill (sps->arm,
233 SIGTERM));
234 GNUNET_break (GNUNET_OK ==
235 GNUNET_OS_process_wait (sps->arm));
236 GNUNET_OS_process_destroy (sps->arm);
237 sps->ah = NULL;
238 }
239
240 if (NULL != sps->cfg)
241 {
242 GNUNET_CONFIGURATION_destroy (sps->cfg);
243 sps->cfg = NULL;
244 }
245 GNUNET_free (sps);
246}
247
248
249/**
250 * This function prepares an array with traits.
251 *
252 */
253static enum GNUNET_GenericReturnValue
254start_peer_traits (void *cls,
255 const void **ret,
256 const char *trait,
257 unsigned int index)
258{
259 struct GNUNET_TESTING_StartPeerState *sps = cls;
260 struct GNUNET_TESTING_Trait traits[] = {
261 GNUNET_TESTING_make_trait_process (
262 &sps->arm),
263 // FIXME: expose sps->cfg as trait...
264 GNUNET_TESTING_ARM_make_trait_arm_handle (
265 sps->ah),
266 GNUNET_TESTING_trait_end ()
267 };
268
269 return GNUNET_TESTING_get_trait (traits,
270 ret,
271 trait,
272 index);
273}
274
275
276struct GNUNET_TESTING_Command
277GNUNET_TESTING_ARM_cmd_start_peer (
278 const char *label,
279 const char *system_label,
280 const char *cfgname)
281{
282 struct GNUNET_TESTING_StartPeerState *sps;
283
284 sps = GNUNET_new (struct GNUNET_TESTING_StartPeerState);
285 sps->system_label = GNUNET_strdup (system_label);
286 sps->cfgname = cfgname;
287 return GNUNET_TESTING_command_new_ac (sps,
288 label,
289 &start_peer_run,
290 &start_peer_cleanup,
291 &start_peer_traits,
292 &sps->ac);
293}
diff --git a/src/service/arm/testing_arm_cmd_stop_peer.c b/src/service/arm/testing_arm_cmd_stop_peer.c
new file mode 100644
index 000000000..e83b2bf29
--- /dev/null
+++ b/src/service/arm/testing_arm_cmd_stop_peer.c
@@ -0,0 +1,174 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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/**
22 * @file testing_api_cmd_stop_peer.c
23 * @brief cmd to stop a peer.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_testbed_lib.h"
30#include "gnunet_testing_arm_lib.h"
31#include "gnunet_arm_service.h"
32
33
34/**
35 * Struct to hold information for callbacks.
36 */
37struct StopPeerState
38{
39 /**
40 * Label of the cmd to start the peer.
41 */
42 const char *start_label;
43
44 /**
45 * Label of the cmd.
46 */
47 const char *label;
48
49 struct GNUNET_ARM_Operation *op;
50
51 struct GNUNET_TESTING_Interpreter *is;
52
53 struct GNUNET_TESTING_AsyncContext ac;
54};
55
56
57/**
58 * Function called in response to a start/stop request.
59 * Will be called when request was not sent successfully,
60 * or when a reply comes. If the request was not sent successfully,
61 * @a rs will indicate that, and @a result will be undefined.
62 *
63 * @param cls closure
64 * @param rs status of the request
65 * @param result result of the operation
66 */
67static void
68stop_cb (
69 void *cls,
70 enum GNUNET_ARM_RequestStatus rs,
71 enum GNUNET_ARM_Result result)
72{
73 struct StopPeerState *stop_ps = cls;
74
75 stop_ps->op = NULL;
76 if (GNUNET_ARM_RESULT_STOPPED != result)
77 {
78 GNUNET_TESTING_async_fail (&stop_ps->ac);
79 return;
80 }
81 GNUNET_TESTING_async_finish (&stop_ps->ac);
82}
83
84
85/**
86 * The run method of this cmd will stop all services of a peer which were used to test the transport service.
87 *
88 */
89static void
90stop_peer_run (void *cls,
91 struct GNUNET_TESTING_Interpreter *is)
92{
93 struct StopPeerState *stop_ps = cls;
94 const struct GNUNET_TESTING_Command *start_cmd;
95 struct GNUNET_ARM_Handle *ah;
96
97 stop_ps->is = is;
98 start_cmd
99 = GNUNET_TESTING_interpreter_lookup_command (is,
100 stop_ps->start_label);
101 if (NULL == start_cmd)
102 GNUNET_TESTING_FAIL (is);
103 if (GNUNET_OK !=
104 GNUNET_TESTING_ARM_get_trait_arm_handle (start_cmd,
105 &ah))
106 GNUNET_TESTING_FAIL (is);
107 stop_ps->op = GNUNET_ARM_request_service_stop (ah,
108 "arm",
109 &stop_cb,
110 stop_ps);
111 if (NULL == stop_ps->op)
112 GNUNET_TESTING_FAIL (is);
113}
114
115
116/**
117 * The cleanup function of this cmd frees resources the cmd allocated.
118 *
119 */
120static void
121stop_peer_cleanup (void *cls)
122{
123 struct StopPeerState *sps = cls;
124
125 if (NULL != sps->op)
126 {
127 GNUNET_TESTING_command_incomplete (sps->is,
128 sps->label);
129 GNUNET_ARM_operation_cancel (sps->op);
130 sps->op = NULL;
131 }
132 GNUNET_free (sps);
133}
134
135
136/**
137 * Trait function of this cmd does nothing.
138 *
139 */
140static int
141stop_peer_traits (void *cls,
142 const void **ret,
143 const char *trait,
144 unsigned int index)
145{
146 struct GNUNET_TESTING_Trait traits[] = {
147 GNUNET_TESTING_trait_end ()
148 };
149
150 (void) cls;
151 return GNUNET_TESTING_get_trait (traits,
152 ret,
153 trait,
154 index);
155}
156
157
158struct GNUNET_TESTING_Command
159GNUNET_TESTING_cmd_stop_peer (const char *label,
160 const char *start_label)
161{
162 struct StopPeerState *sps;
163
164 sps = GNUNET_new (struct StopPeerState);
165 sps->start_label = start_label;
166 sps->label = label;
167 return GNUNET_TESTING_command_new_ac (
168 sps,
169 label,
170 &stop_peer_run,
171 &stop_peer_cleanup,
172 &stop_peer_traits,
173 &sps->ac);
174}
diff --git a/src/service/arm/testing_arm_traits.c b/src/service/arm/testing_arm_traits.c
new file mode 100644
index 000000000..d965220a2
--- /dev/null
+++ b/src/service/arm/testing_arm_traits.c
@@ -0,0 +1,33 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 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/**
22 * @file transport/test_transport_start_with_config.c
23 * @brief Generic program to start testcases in an configurable topology.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_testing_arm_lib.h"
30
31
32GNUNET_TESTING_ARM_SIMPLE_TRAITS (GNUNET_TESTING_MAKE_IMPL_SIMPLE_TRAIT,
33 GNUNET_TESTING_ARM)