aboutsummaryrefslogtreecommitdiff
path: root/src/arm
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm')
-rw-r--r--src/arm/.gitignore7
-rw-r--r--src/arm/Makefile.am102
-rw-r--r--src/arm/arm.conf.in60
-rw-r--r--src/arm/arm.h164
-rw-r--r--src/arm/arm_api.c1102
-rw-r--r--src/arm/arm_monitor_api.c274
-rw-r--r--src/arm/gnunet-arm.c1065
-rw-r--r--src/arm/gnunet-service-arm.c2227
-rw-r--r--src/arm/mockup-service.c123
-rwxr-xr-xsrc/arm/mockup_service130
-rw-r--r--src/arm/test_arm_api.c258
-rw-r--r--src/arm/test_arm_api_data.conf38
-rw-r--r--src/arm/test_exponential_backoff.c416
-rw-r--r--src/arm/test_gnunet_arm.py.in129
-rw-r--r--src/arm/test_gnunet_service_arm.c267
15 files changed, 0 insertions, 6362 deletions
diff --git a/src/arm/.gitignore b/src/arm/.gitignore
deleted file mode 100644
index 859c6e393..000000000
--- a/src/arm/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
1mockup-service
2gnunet-arm
3gnunet-service-arm
4test_arm_api
5test_exponential_backoff
6test_gnunet_arm.py
7test_gnunet_service_arm
diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am
deleted file mode 100644
index 8a738c5a9..000000000
--- a/src/arm/Makefile.am
+++ /dev/null
@@ -1,102 +0,0 @@
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 = libgnunetarm.la
17
18libgnunetarm_la_SOURCES = \
19 arm_api.c arm_monitor_api.c arm.h
20libgnunetarm_la_LIBADD = \
21 $(top_builddir)/src/util/libgnunetutil.la \
22 $(GN_LIBINTL) $(XLIB)
23libgnunetarm_la_LDFLAGS = \
24 $(GN_LIB_LDFLAGS) \
25 -version-info 2:0:0
26
27
28bin_PROGRAMS = \
29 gnunet-arm
30
31noinst_PROGRAMS = \
32 mockup-service
33
34libexec_PROGRAMS = \
35 gnunet-service-arm
36
37gnunet_arm_SOURCES = \
38 gnunet-arm.c
39gnunet_arm_LDADD = \
40 libgnunetarm.la \
41 $(top_builddir)/src/util/libgnunetutil.la \
42 $(GN_LIBINTL)
43
44gnunet_service_arm_SOURCES = \
45 gnunet-service-arm.c
46gnunet_service_arm_LDADD = \
47 $(top_builddir)/src/util/libgnunetutil.la \
48 $(GN_LIBINTL)
49
50mockup_service_SOURCES = \
51 mockup-service.c
52 mockup_service_LDADD = \
53 $(top_builddir)/src/util/libgnunetutil.la
54mockup_service_LDFLAGS = \
55 $(GN_LIBINTL)
56
57
58check_PROGRAMS = \
59 test_arm_api \
60 test_exponential_backoff \
61 test_gnunet_service_arm
62
63if HAVE_PYTHON
64check_SCRIPTS = \
65 test_gnunet_arm.py
66endif
67
68if ENABLE_TEST_RUN
69AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
70TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
71endif
72
73test_arm_api_SOURCES = \
74 test_arm_api.c
75test_arm_api_LDADD = \
76 libgnunetarm.la \
77 $(top_builddir)/src/util/libgnunetutil.la
78
79test_exponential_backoff_SOURCES = \
80 test_exponential_backoff.c
81test_exponential_backoff_LDADD = \
82 libgnunetarm.la \
83 $(top_builddir)/src/util/libgnunetutil.la
84
85test_gnunet_service_arm_SOURCES = \
86 test_gnunet_service_arm.c
87 test_gnunet_service_arm_LDADD = \
88 libgnunetarm.la \
89 $(top_builddir)/src/util/libgnunetutil.la
90
91SUFFIXES = .py.in .py
92.py.in.py:
93 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@
94 chmod +x $@
95
96test_gnunet_arm.py: test_gnunet_arm.py.in Makefile
97 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_arm.py.in > test_gnunet_arm.py
98 chmod +x test_gnunet_arm.py
99
100EXTRA_DIST = \
101 test_arm_api_data.conf \
102 test_gnunet_arm.py.in
diff --git a/src/arm/arm.conf.in b/src/arm/arm.conf.in
deleted file mode 100644
index 8015e0e15..000000000
--- a/src/arm/arm.conf.in
+++ /dev/null
@@ -1,60 +0,0 @@
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/arm/arm.h b/src/arm/arm.h
deleted file mode 100644
index 8888e0105..000000000
--- a/src/arm/arm.h
+++ /dev/null
@@ -1,164 +0,0 @@
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/arm/arm_api.c b/src/arm/arm_api.c
deleted file mode 100644
index 1360ecf14..000000000
--- a/src/arm/arm_api.c
+++ /dev/null
@@ -1,1102 +0,0 @@
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 int
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 int
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 (const struct GNUNET_CONFIGURATION_Handle *cfg,
578 GNUNET_ARM_ConnectionStatusCallback conn_status,
579 void *conn_status_cls)
580{
581 struct GNUNET_ARM_Handle *h;
582
583 h = GNUNET_new (struct GNUNET_ARM_Handle);
584 h->cfg = cfg;
585 h->conn_status = conn_status;
586 h->conn_status_cls = conn_status_cls;
587 if (GNUNET_OK != reconnect_arm (h))
588 {
589 GNUNET_free (h);
590 return NULL;
591 }
592 return h;
593}
594
595
596/**
597 * Disconnect from the ARM service (if connected) and destroy the context.
598 *
599 * @param h the handle that was being used
600 */
601void
602GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
603{
604 struct GNUNET_ARM_Operation *op;
605
606 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
607 while (NULL != (op = h->operation_pending_head))
608 {
609 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
610 h->operation_pending_tail,
611 op);
612 if (NULL != op->result_cont)
613 op->result_cont (op->cont_cls,
614 GNUNET_ARM_REQUEST_DISCONNECTED,
615 0);
616 if (NULL != op->list_cont)
617 op->list_cont (op->cont_cls,
618 GNUNET_ARM_REQUEST_DISCONNECTED,
619 0,
620 NULL);
621 if (NULL != op->async)
622 {
623 GNUNET_SCHEDULER_cancel (op->async);
624 op->async = NULL;
625 }
626 GNUNET_free (op);
627 }
628 if (NULL != h->mq)
629 {
630 GNUNET_MQ_destroy (h->mq);
631 h->mq = NULL;
632 }
633 if (NULL != h->reconnect_task)
634 {
635 GNUNET_SCHEDULER_cancel (h->reconnect_task);
636 h->reconnect_task = NULL;
637 }
638 GNUNET_free (h);
639}
640
641
642/**
643 * A client specifically requested starting of ARM itself.
644 * Starts the ARM service.
645 *
646 * @param h the handle with configuration details
647 * @param std_inheritance inheritance of std streams
648 * @param sigfd socket to pass to ARM for signalling
649 * @return operation status code
650 */
651static enum GNUNET_ARM_Result
652start_arm_service (struct GNUNET_ARM_Handle *h,
653 enum GNUNET_OS_InheritStdioFlags std_inheritance,
654 struct GNUNET_DISK_FileHandle *sigfd)
655{
656 struct GNUNET_OS_Process *proc;
657 char *cbinary;
658 char *binary;
659 char *quotedbinary;
660 char *config;
661 char *loprefix;
662 char *lopostfix;
663 int ld[2];
664 int *lsocks;
665
666 if (NULL == sigfd)
667 {
668 lsocks = NULL;
669 }
670 else
671 {
672 ld[0] = sigfd->fd;
673 ld[1] = -1;
674 lsocks = ld;
675 }
676 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
677 "arm",
678 "PREFIX",
679 &loprefix))
680 loprefix = GNUNET_strdup ("");
681 else
682 loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
683 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
684 "arm",
685 "OPTIONS",
686 &lopostfix))
687 lopostfix = GNUNET_strdup ("");
688 else
689 lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
690 lopostfix);
691 if (GNUNET_OK !=
692 GNUNET_CONFIGURATION_get_value_string (h->cfg,
693 "arm",
694 "BINARY",
695 &cbinary))
696 {
697 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
698 "arm",
699 "BINARY");
700 GNUNET_free (loprefix);
701 GNUNET_free (lopostfix);
702 return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
703 }
704 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
705 "arm",
706 "CONFIG",
707 &config))
708 config = NULL;
709 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
710 GNUNET_asprintf (&quotedbinary,
711 "\"%s\"",
712 binary);
713 GNUNET_free (cbinary);
714 if ( (GNUNET_YES ==
715 GNUNET_CONFIGURATION_have_value (h->cfg,
716 "TESTING",
717 "WEAKRANDOM")) &&
718 (GNUNET_YES ==
719 GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
720 "TESTING",
721 "WEAKRANDOM")) &&
722 (GNUNET_NO ==
723 GNUNET_CONFIGURATION_have_value (h->cfg,
724 "TESTING",
725 "HOSTFILE")) )
726 {
727 /* Means we are ONLY running locally */
728 /* we're clearly running a test, don't daemonize */
729 if (NULL == config)
730 proc = GNUNET_OS_start_process_s (std_inheritance,
731 lsocks,
732 loprefix,
733 quotedbinary,
734 /* no daemonization! */
735 lopostfix,
736 NULL);
737 else
738 proc = GNUNET_OS_start_process_s (std_inheritance,
739 lsocks,
740 loprefix,
741 quotedbinary,
742 "-c",
743 config,
744 /* no daemonization! */
745 lopostfix,
746 NULL);
747 }
748 else
749 {
750 if (NULL == config)
751 proc = GNUNET_OS_start_process_s (std_inheritance,
752 lsocks,
753 loprefix,
754 quotedbinary,
755 "-d", /* do daemonize */
756 lopostfix,
757 NULL);
758 else
759 proc = GNUNET_OS_start_process_s (std_inheritance,
760 lsocks,
761 loprefix,
762 quotedbinary,
763 "-c",
764 config,
765 "-d", /* do daemonize */
766 lopostfix,
767 NULL);
768 }
769 GNUNET_free (binary);
770 GNUNET_free (quotedbinary);
771 if (NULL != config)
772 GNUNET_free (config);
773 GNUNET_free (loprefix);
774 GNUNET_free (lopostfix);
775 if (NULL == proc)
776 return GNUNET_ARM_RESULT_START_FAILED;
777 GNUNET_OS_process_destroy (proc);
778 return GNUNET_ARM_RESULT_STARTING;
779}
780
781
782/**
783 * Abort an operation. Only prevents the callback from being
784 * called, the operation may still complete.
785 *
786 * @param op operation to cancel
787 */
788void
789GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
790{
791 struct GNUNET_ARM_Handle *h = op->h;
792
793 if (NULL != op->async)
794 {
795 GNUNET_SCHEDULER_cancel (op->async);
796 op->async = NULL;
797 }
798 if (NULL != op->rfd)
799 {
800 GNUNET_DISK_file_close (op->rfd);
801 op->rfd = NULL;
802 }
803 if (h->thm == op)
804 {
805 op->result_cont = NULL;
806 return;
807 }
808 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
809 h->operation_pending_tail,
810 op);
811 GNUNET_free (op);
812}
813
814
815/**
816 * Start or stop a service.
817 *
818 * @param h handle to ARM
819 * @param service_name name of the service
820 * @param cb callback to invoke when service is ready
821 * @param cb_cls closure for @a cb
822 * @param type type of the request
823 * @return handle to queue, NULL on error
824 */
825static struct GNUNET_ARM_Operation *
826change_service (struct GNUNET_ARM_Handle *h,
827 const char *service_name,
828 GNUNET_ARM_ResultCallback cb,
829 void *cb_cls,
830 uint16_t type)
831{
832 struct GNUNET_ARM_Operation *op;
833 size_t slen;
834 struct GNUNET_MQ_Envelope *env;
835 struct GNUNET_ARM_Message *msg;
836
837 slen = strlen (service_name) + 1;
838 if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
839 {
840 GNUNET_break (0);
841 return NULL;
842 }
843 if (0 == h->request_id_counter)
844 h->request_id_counter++;
845 op = GNUNET_new (struct GNUNET_ARM_Operation);
846 op->h = h;
847 op->result_cont = cb;
848 op->cont_cls = cb_cls;
849 op->id = h->request_id_counter++;
850 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
851 h->operation_pending_tail,
852 op);
853 env = GNUNET_MQ_msg_extra (msg, slen, type);
854 msg->reserved = htonl (0);
855 msg->request_id = GNUNET_htonll (op->id);
856 GNUNET_memcpy (&msg[1], service_name, slen);
857 GNUNET_MQ_send (h->mq, env);
858 return op;
859}
860
861
862/**
863 * Task run to notify application that ARM is already up.
864 *
865 * @param cls the operation that asked ARM to be started
866 */
867static void
868notify_running (void *cls)
869{
870 struct GNUNET_ARM_Operation *op = cls;
871 struct GNUNET_ARM_Handle *h = op->h;
872
873 op->async = NULL;
874 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
875 h->operation_pending_tail,
876 op);
877 if (NULL != op->result_cont)
878 op->result_cont (op->cont_cls,
879 GNUNET_ARM_REQUEST_SENT_OK,
880 GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
881 if ( (GNUNET_YES == h->currently_up) &&
882 (NULL != h->conn_status) )
883 h->conn_status (h->conn_status_cls,
884 GNUNET_YES);
885 GNUNET_free (op);
886}
887
888
889/**
890 * Task run to notify application that ARM is being started.
891 *
892 * @param cls the operation that asked ARM to be started
893 */
894static void
895notify_starting (void *cls)
896{
897 struct GNUNET_ARM_Operation *op = cls;
898 struct GNUNET_ARM_Handle *h = op->h;
899
900 op->async = NULL;
901 LOG (GNUNET_ERROR_TYPE_DEBUG,
902 "Notifying client that we started the ARM service\n");
903 GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
904 h->operation_pending_tail,
905 op);
906 if (NULL != op->result_cont)
907 op->result_cont (op->cont_cls,
908 GNUNET_ARM_REQUEST_SENT_OK,
909 op->starting_ret);
910 GNUNET_DISK_file_close(op->rfd);
911 GNUNET_free (op);
912}
913
914
915/**
916 * Request for a service to be started.
917 *
918 * @param h handle to ARM
919 * @param service_name name of the service
920 * @param std_inheritance inheritance of std streams
921 * @param cont callback to invoke after request is sent or not sent
922 * @param cont_cls closure for @a cont
923 * @return handle for the operation, NULL on error
924 */
925struct GNUNET_ARM_Operation *
926GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
927 const char *service_name,
928 enum GNUNET_OS_InheritStdioFlags
929 std_inheritance,
930 GNUNET_ARM_ResultCallback cont,
931 void *cont_cls)
932{
933 struct GNUNET_ARM_Operation *op;
934 enum GNUNET_ARM_Result ret;
935 struct GNUNET_DISK_PipeHandle *sig;
936 struct GNUNET_DISK_FileHandle *wsig;
937
938 LOG (GNUNET_ERROR_TYPE_DEBUG,
939 "Starting service `%s'\n",
940 service_name);
941 if (0 != strcasecmp ("arm",
942 service_name))
943 return change_service (h,
944 service_name,
945 cont,
946 cont_cls,
947 GNUNET_MESSAGE_TYPE_ARM_START);
948
949 /* Possible cases:
950 * 1) We're connected to ARM already. Invoke the callback immediately.
951 * 2) We're not connected to ARM.
952 * Cancel any reconnection attempts temporarily, then perform
953 * a service test.
954 */
955 if (GNUNET_YES == h->currently_up)
956 {
957 LOG (GNUNET_ERROR_TYPE_DEBUG,
958 "ARM is already running\n");
959 op = GNUNET_new (struct GNUNET_ARM_Operation);
960 op->h = h;
961 op->result_cont = cont;
962 op->cont_cls = cont_cls;
963 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
964 h->operation_pending_tail,
965 op);
966 op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
967 return op;
968 }
969 /* This is an inherently uncertain choice, as it is of course
970 theoretically possible that ARM is up and we just did not
971 yet complete the MQ handshake. However, given that users
972 are unlikely to hammer 'gnunet-arm -s' on a busy system,
973 the above check should catch 99.99% of the cases where ARM
974 is already running. */
975 LOG (GNUNET_ERROR_TYPE_DEBUG,
976 "Starting ARM service\n");
977 if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
978 {
979 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
980 "pipe");
981 ret = GNUNET_ARM_RESULT_START_FAILED;
982 }
983 else
984 {
985 wsig = GNUNET_DISK_pipe_detach_end (sig,
986 GNUNET_DISK_PIPE_END_WRITE);
987 ret = start_arm_service (h,
988 std_inheritance,
989 wsig);
990 GNUNET_DISK_file_close (wsig);
991 if (GNUNET_ARM_RESULT_STARTING == ret)
992 reconnect_arm (h);
993 }
994 op = GNUNET_new (struct GNUNET_ARM_Operation);
995 op->h = h;
996 op->result_cont = cont;
997 op->cont_cls = cont_cls;
998 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
999 h->operation_pending_tail,
1000 op);
1001 op->starting_ret = ret;
1002 if (NULL != sig)
1003 {
1004 op->rfd = GNUNET_DISK_pipe_detach_end (sig,
1005 GNUNET_DISK_PIPE_END_READ);
1006 /* Wait at most a minute for gnunet-service-arm to be up, as beyond
1007 that something clearly just went wrong */
1008 op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES,
1009 op->rfd,
1010 &notify_starting,
1011 op);
1012 GNUNET_DISK_pipe_close (sig);
1013 }
1014 else
1015 {
1016 op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
1017 op);
1018 }
1019 return op;
1020}
1021
1022
1023/**
1024 * Request a service to be stopped. Stopping arm itself will not
1025 * invalidate its handle, and ARM API will try to restore connection
1026 * to the ARM service, even if ARM connection was lost because you
1027 * asked for ARM to be stopped. Call
1028 * #GNUNET_ARM_disconnect() to free the handle and prevent
1029 * further connection attempts.
1030 *
1031 * @param h handle to ARM
1032 * @param service_name name of the service
1033 * @param cont callback to invoke after request is sent or is not sent
1034 * @param cont_cls closure for @a cont
1035 * @return handle for the operation, NULL on error
1036 */
1037struct GNUNET_ARM_Operation *
1038GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
1039 const char *service_name,
1040 GNUNET_ARM_ResultCallback cont,
1041 void *cont_cls)
1042{
1043 struct GNUNET_ARM_Operation *op;
1044
1045 LOG (GNUNET_ERROR_TYPE_DEBUG,
1046 "Stopping service `%s'\n",
1047 service_name);
1048 op = change_service (h,
1049 service_name,
1050 cont,
1051 cont_cls,
1052 GNUNET_MESSAGE_TYPE_ARM_STOP);
1053 if (NULL == op)
1054 return NULL;
1055 /* If the service is ARM, set a flag as we will use MQ errors
1056 to detect that the process is really gone. */
1057 if (0 == strcasecmp (service_name,
1058 "arm"))
1059 op->is_arm_stop = GNUNET_YES;
1060 return op;
1061}
1062
1063
1064/**
1065 * Request a list of running services.
1066 *
1067 * @param h handle to ARM
1068 * @param cont callback to invoke after request is sent or is not sent
1069 * @param cont_cls closure for @a cont
1070 * @return handle for the operation, NULL on error
1071 */
1072struct GNUNET_ARM_Operation *
1073GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
1074 GNUNET_ARM_ServiceListCallback cont,
1075 void *cont_cls)
1076{
1077 struct GNUNET_ARM_Operation *op;
1078 struct GNUNET_MQ_Envelope *env;
1079 struct GNUNET_ARM_Message *msg;
1080
1081 LOG (GNUNET_ERROR_TYPE_DEBUG,
1082 "Requesting LIST from ARM service\n");
1083 if (0 == h->request_id_counter)
1084 h->request_id_counter++;
1085 op = GNUNET_new (struct GNUNET_ARM_Operation);
1086 op->h = h;
1087 op->list_cont = cont;
1088 op->cont_cls = cont_cls;
1089 op->id = h->request_id_counter++;
1090 GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
1091 h->operation_pending_tail,
1092 op);
1093 env = GNUNET_MQ_msg (msg,
1094 GNUNET_MESSAGE_TYPE_ARM_LIST);
1095 msg->reserved = htonl (0);
1096 msg->request_id = GNUNET_htonll (op->id);
1097 GNUNET_MQ_send (h->mq, env);
1098 return op;
1099}
1100
1101
1102/* end of arm_api.c */
diff --git a/src/arm/arm_monitor_api.c b/src/arm/arm_monitor_api.c
deleted file mode 100644
index 2c418d0ce..000000000
--- a/src/arm/arm_monitor_api.c
+++ /dev/null
@@ -1,274 +0,0 @@
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/arm/gnunet-arm.c b/src/arm/gnunet-arm.c
deleted file mode 100644
index ea3a012ab..000000000
--- a/src/arm/gnunet-arm.c
+++ /dev/null
@@ -1,1065 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2012, 2013 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-arm.c
23 * @brief arm for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_constants.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * Set if we are to shutdown all services (including ARM).
33 */
34static int end;
35
36/**
37 * Set if we are to start default services (including ARM).
38 */
39static int start;
40
41/**
42 * Set if we are to stop/start default services (including ARM).
43 */
44static int restart;
45
46/**
47 * Set if we should delete configuration and temp directory on exit.
48 */
49static int delete;
50
51/**
52 * Set if we should not print status messages.
53 */
54static int quiet;
55
56/**
57 * Set if we should print all services, including stopped ones.
58 */
59static int show_all;
60
61/**
62 * Monitor ARM activity.
63 */
64static int monitor;
65
66/**
67 * Set if we should print a list of currently running services.
68 */
69static int list;
70
71/**
72 * Set to the name of a service to start.
73 */
74static char *init;
75
76/**
77 * Set to the name of a service to kill.
78 */
79static char *term;
80
81/**
82 * Set to the name of the config file used.
83 */
84static char *config_file;
85
86/**
87 * Set to the directory where runtime files are stored.
88 */
89static char *dir;
90
91/**
92 * Final status code.
93 */
94static int ret;
95
96/**
97 * Connection with ARM.
98 */
99static struct GNUNET_ARM_Handle *h;
100
101/**
102 * Monitor connection with ARM.
103 */
104static struct GNUNET_ARM_MonitorHandle *m;
105
106/**
107 * Our configuration.
108 */
109static struct GNUNET_CONFIGURATION_Handle *cfg;
110
111/**
112 * Processing stage that we are in. Simple counter.
113 */
114static unsigned int phase;
115
116/**
117 * User defined timestamp for completing operations.
118 */
119static struct GNUNET_TIME_Relative timeout;
120
121/**
122 * Task to be run on timeout.
123 */
124static struct GNUNET_SCHEDULER_Task *timeout_task;
125
126/**
127 * Do we want to give our stdout to gnunet-service-arm?
128 */
129static int no_stdout;
130
131/**
132 * Do we want to give our stderr to gnunet-service-arm?
133 */
134static int no_stderr;
135
136/**
137 * Handle for the task running the #action_loop().
138 */
139static struct GNUNET_SCHEDULER_Task *al_task;
140
141/**
142 * Current operation.
143 */
144static struct GNUNET_ARM_Operation *op;
145
146/**
147 * Attempts to delete configuration file and GNUNET_HOME
148 * on ARM shutdown provided the end and delete options
149 * were specified when gnunet-arm was run.
150 */
151static void
152delete_files ()
153{
154 GNUNET_log (
155 GNUNET_ERROR_TYPE_DEBUG,
156 "Will attempt to remove configuration file %s and service directory %s\n",
157 config_file,
158 dir);
159 if (0 != unlink (config_file))
160 {
161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
162 _ ("Failed to remove configuration file %s\n"),
163 config_file);
164 }
165 if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
166 {
167 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
168 _ ("Failed to remove servicehome directory %s\n"),
169 dir);
170 }
171}
172
173
174/**
175 * Main continuation-passing-style loop. Runs the various
176 * jobs that we've been asked to do in order.
177 *
178 * @param cls closure, unused
179 */
180static void
181shutdown_task (void *cls)
182{
183 (void) cls;
184 if (NULL != al_task)
185 {
186 GNUNET_SCHEDULER_cancel (al_task);
187 al_task = NULL;
188 }
189 if (NULL != op)
190 {
191 GNUNET_ARM_operation_cancel (op);
192 op = NULL;
193 }
194 if (NULL != h)
195 {
196 GNUNET_ARM_disconnect (h);
197 h = NULL;
198 }
199 if (NULL != m)
200 {
201 GNUNET_ARM_monitor_stop (m);
202 m = NULL;
203 }
204 if (NULL != timeout_task)
205 {
206 GNUNET_SCHEDULER_cancel (timeout_task);
207 timeout_task = NULL;
208 }
209 if ( (GNUNET_YES == end) &&
210 (GNUNET_YES == delete) )
211 delete_files ();
212 GNUNET_CONFIGURATION_destroy (cfg);
213 cfg = NULL;
214}
215
216
217/**
218 * Returns a string interpretation of @a rs
219 *
220 * @param rs the request status from ARM
221 * @return a string interpretation of the request status
222 */
223static const char *
224req_string (enum GNUNET_ARM_RequestStatus rs)
225{
226 switch (rs)
227 {
228 case GNUNET_ARM_REQUEST_SENT_OK:
229 return _ ("Message was sent successfully");
230
231 case GNUNET_ARM_REQUEST_DISCONNECTED:
232 return _ ("We disconnected from ARM before we could send a request");
233 }
234 return _ ("Unknown request status");
235}
236
237
238/**
239 * Returns a string interpretation of the @a result
240 *
241 * @param result the arm result
242 * @return a string interpretation
243 */
244static const char *
245ret_string (enum GNUNET_ARM_Result result)
246{
247 switch (result)
248 {
249 case GNUNET_ARM_RESULT_STOPPED:
250 return _ ("is stopped");
251
252 case GNUNET_ARM_RESULT_STARTING:
253 return _ ("is starting");
254
255 case GNUNET_ARM_RESULT_STOPPING:
256 return _ ("is stopping");
257
258 case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
259 return _ ("is starting already");
260
261 case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
262 return _ ("is stopping already");
263
264 case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
265 return _ ("is started already");
266
267 case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
268 return _ ("is stopped already");
269
270 case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
271 return _ ("service is not known to ARM");
272
273 case GNUNET_ARM_RESULT_START_FAILED:
274 return _ ("service failed to start");
275
276 case GNUNET_ARM_RESULT_IN_SHUTDOWN:
277 return _ ("service cannot be manipulated because ARM is shutting down");
278 }
279 return _ ("Unknown result code.");
280}
281
282
283/**
284 * Main task that runs our various operations in order.
285 *
286 * @param cls closure
287 */
288static void
289action_loop (void *cls);
290
291
292/**
293 * Function called whenever we connect to or disconnect from ARM.
294 * Termiantes the process if we fail to connect to the service on
295 * our first attempt.
296 *
297 * @param cls closure
298 * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
299 * #GNUNET_SYSERR on error.
300 */
301static void
302conn_status (void *cls,
303 int connected)
304{
305 static int once;
306
307 (void) cls;
308 if ( (GNUNET_SYSERR == connected) &&
309 (0 == once) )
310 {
311 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312 _ ("Fatal error initializing ARM API.\n"));
313 GNUNET_SCHEDULER_shutdown ();
314 return;
315 }
316 once = 1;
317}
318
319
320/**
321 * We have requested ARM to be started, this function
322 * is called with the result of the operation. Informs the
323 * use of the result; on success, we continue with the event
324 * loop, on failure we terminate the process.
325 *
326 * @param cls closure unused
327 * @param rs what happened to our request
328 * @param result if the request was processed, this is the result
329 * according to ARM
330 */
331static void
332start_callback (void *cls,
333 enum GNUNET_ARM_RequestStatus rs,
334 enum GNUNET_ARM_Result result)
335{
336 (void) cls;
337 op = NULL;
338 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
339 {
340 fprintf (stdout,
341 _ ("Failed to start the ARM service: %s\n"),
342 req_string (rs));
343 GNUNET_SCHEDULER_shutdown ();
344 return;
345 }
346 if ((GNUNET_ARM_RESULT_STARTING != result) &&
347 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
348 {
349 fprintf (stdout,
350 _ ("Failed to start the ARM service: %s\n"),
351 ret_string (result));
352 GNUNET_SCHEDULER_shutdown ();
353 return;
354 }
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 "ARM service [re]start successful\n");
357 start = 0;
358 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
359 NULL);
360}
361
362
363/**
364 * We have requested ARM to be stopped, this function
365 * is called with the result of the operation. Informs the
366 * use of the result; on success, we continue with the event
367 * loop, on failure we terminate the process.
368 *
369 * @param cls closure unused
370 * @param rs what happened to our request
371 * @param result if the request was processed, this is the result
372 * according to ARM
373 */
374static void
375stop_callback (void *cls,
376 enum GNUNET_ARM_RequestStatus rs,
377 enum GNUNET_ARM_Result result)
378{
379 char *msg;
380
381 (void) cls;
382 op = NULL;
383 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
384 {
385 GNUNET_asprintf (&msg,
386 "%s",
387 _ (
388 "Failed to send a stop request to the ARM service: %s\n"));
389 fprintf (stdout, msg, req_string (rs));
390 GNUNET_free (msg);
391 GNUNET_SCHEDULER_shutdown ();
392 return;
393 }
394 if ( (GNUNET_ARM_RESULT_STOPPING != result) &&
395 (GNUNET_ARM_RESULT_STOPPED != result) &&
396 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) )
397 {
398 fprintf (stdout,
399 _ ("Failed to stop the ARM service: %s\n"),
400 ret_string (result));
401 GNUNET_SCHEDULER_shutdown ();
402 return;
403 }
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "ARM service shutdown successful\n");
406 end = 0;
407 if (restart)
408 {
409 restart = 0;
410 start = 1;
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "Initiating an ARM restart\n");
413 }
414 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
415 NULL);
416}
417
418
419/**
420 * We have requested a service to be started, this function
421 * is called with the result of the operation. Informs the
422 * use of the result; on success, we continue with the event
423 * loop, on failure we terminate the process.
424 *
425 * @param cls closure unused
426 * @param rs what happened to our request
427 * @param result if the request was processed, this is the result
428 * according to ARM
429 */
430static void
431init_callback (void *cls,
432 enum GNUNET_ARM_RequestStatus rs,
433 enum GNUNET_ARM_Result result)
434{
435 (void) cls;
436 op = NULL;
437 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
438 {
439 fprintf (stdout,
440 _ ("Failed to send a request to start the `%s' service: %s\n"),
441 init,
442 req_string (rs));
443 GNUNET_SCHEDULER_shutdown ();
444 return;
445 }
446 if ((GNUNET_ARM_RESULT_STARTING != result) &&
447 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
448 {
449 fprintf (stdout,
450 _ ("Failed to start the `%s' service: %s\n"),
451 init,
452 ret_string (result));
453 GNUNET_SCHEDULER_shutdown ();
454 return;
455 }
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "Service %s [re]started successfully\n",
458 init);
459 GNUNET_free (init);
460 init = NULL;
461 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
462 NULL);
463}
464
465
466/**
467 * We have requested a service to be stopped, this function
468 * is called with the result of the operation. Informs the
469 * use of the result; on success, we continue with the event
470 * loop, on failure we terminate the process.
471 *
472 * @param cls closure unused
473 * @param rs what happened to our request
474 * @param result if the request was processed, this is the result
475 * according to ARM
476 */
477static void
478term_callback (void *cls,
479 enum GNUNET_ARM_RequestStatus rs,
480 enum GNUNET_ARM_Result result)
481{
482 char *msg;
483
484 (void) cls;
485 op = NULL;
486 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
487 {
488 GNUNET_asprintf (&msg,
489 _ (
490 "Failed to send a request to kill the `%s' service: %%s\n"),
491 term);
492 fprintf (stdout,
493 msg,
494 req_string (rs));
495 GNUNET_free (msg);
496 GNUNET_SCHEDULER_shutdown ();
497 return;
498 }
499 if ( (GNUNET_ARM_RESULT_STOPPED != result) &&
500 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) )
501 {
502 fprintf (stdout,
503 _ ("Failed to kill the `%s' service: %s\n"),
504 term,
505 ret_string (result));
506 GNUNET_SCHEDULER_shutdown ();
507 return;
508 }
509
510 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511 "Service %s stopped successfully\n",
512 term);
513 GNUNET_free (term);
514 term = NULL;
515 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
516 NULL);
517}
518
519
520/**
521 * Function called with the list of running services. Prints
522 * the list to stdout, then starts the event loop again.
523 * Prints an error message and terminates the process on errors.
524 *
525 * @param cls closure (unused)
526 * @param rs request status (success, failure, etc.)
527 * @param count number of services in the list
528 * @param list list of services managed by arm
529 */
530static void
531list_callback (void *cls,
532 enum GNUNET_ARM_RequestStatus rs,
533 unsigned int count,
534 const struct GNUNET_ARM_ServiceInfo *list)
535{
536 unsigned int num_stopped = 0;
537 unsigned int num_started = 0;
538 unsigned int num_stopping = 0;
539 unsigned int num_failed = 0;
540 unsigned int num_finished = 0;
541 (void) cls;
542 op = NULL;
543 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
544 {
545 char *msg;
546
547 GNUNET_asprintf (&msg,
548 "%s",
549 _ ("Failed to request a list of services: %s\n"));
550 fprintf (stdout,
551 msg,
552 req_string (rs));
553 GNUNET_free (msg);
554 ret = 3;
555 GNUNET_SCHEDULER_shutdown ();
556 }
557 if (NULL == list)
558 {
559 fprintf (stderr,
560 "%s",
561 _ ("Error communicating with ARM. ARM not running?\n"));
562 GNUNET_SCHEDULER_shutdown ();
563 ret = 3;
564 return;
565 }
566 for (unsigned int i = 0; i < count; i++)
567 {
568 switch (list[i].status)
569 {
570 case GNUNET_ARM_SERVICE_STATUS_STOPPED:
571 num_stopped++;
572 break;
573 case GNUNET_ARM_SERVICE_STATUS_FAILED:
574 num_failed++;
575 break;
576 case GNUNET_ARM_SERVICE_STATUS_FINISHED:
577 num_finished++;
578 break;
579 case GNUNET_ARM_SERVICE_STATUS_STARTED:
580 num_started++;
581 break;
582 case GNUNET_ARM_SERVICE_STATUS_STOPPING:
583 num_stopping++;
584 fprintf (stdout,
585 "%s (binary='%s', status=stopping)\n",
586 list[i].name,
587 list[i].binary);
588 break;
589 default:
590 GNUNET_break_op (0);
591 fprintf (stdout,
592 "%s (binary='%s', status=unknown)\n",
593 list[i].name,
594 list[i].binary);
595 break;
596 }
597 }
598 if (! quiet)
599 {
600 if (show_all)
601 fprintf (stdout,
602 "%s",
603 _ ("All services:\n"));
604 else
605 fprintf (stdout,
606 "%s",
607 _ ("Services (excluding stopped services):\n"));
608 if (num_stopped || num_failed || num_finished || num_stopping ||
609 num_started)
610 {
611 int sep = 0;
612 fprintf (stdout, "(");
613 if (0 != num_started)
614 {
615 if (sep)
616 fprintf (stdout, " / ");
617 fprintf (stdout,
618 "started: %u",
619 num_started);
620 sep = 1;
621 }
622 if (0 != num_failed)
623 {
624 if (sep)
625 fprintf (stdout, " / ");
626 fprintf (stdout,
627 "failed: %u",
628 num_failed);
629 sep = 1;
630 }
631 if (0 != num_stopping)
632 {
633 if (sep)
634 fprintf (stdout, " / ");
635 fprintf (stdout,
636 "stopping: %u",
637 num_stopping);
638 sep = 1;
639 }
640 if (0 != num_stopped)
641 {
642 if (sep)
643 fprintf (stdout, " / ");
644 fprintf (stdout,
645 "stopped: %u",
646 num_stopped);
647 sep = 1;
648 }
649 if (0 != num_finished)
650 {
651 if (sep)
652 fprintf (stdout, " / ");
653 fprintf (stdout,
654 "finished: %u",
655 num_finished);
656 sep = 1;
657 }
658 fprintf (stdout, ")\n");
659 }
660 else
661 {
662 fprintf (stdout,
663 "%s",
664 _ ("(No services configured.)\n"));
665 }
666 }
667 for (unsigned int i = 0; i < count; i++)
668 {
669 struct GNUNET_TIME_Relative restart_in;
670 switch (list[i].status)
671 {
672 case GNUNET_ARM_SERVICE_STATUS_STOPPED:
673 if (show_all)
674 fprintf (stdout,
675 "%s (binary='%s', status=stopped)\n",
676 list[i].name,
677 list[i].binary);
678 break;
679 case GNUNET_ARM_SERVICE_STATUS_FAILED:
680 restart_in = GNUNET_TIME_absolute_get_remaining (list[i].restart_at);
681 fprintf (stdout,
682 "%s (binary='%s', status=failed, exit_status=%d, restart_delay='%s')\n",
683 list[i].name,
684 list[i].binary,
685 list[i].last_exit_status,
686 GNUNET_STRINGS_relative_time_to_string (restart_in,
687 GNUNET_YES));
688 break;
689 case GNUNET_ARM_SERVICE_STATUS_FINISHED:
690 fprintf (stdout,
691 "%s (binary='%s', status=finished)\n",
692 list[i].name,
693 list[i].binary);
694 break;
695 case GNUNET_ARM_SERVICE_STATUS_STARTED:
696 fprintf (stdout,
697 "%s (binary='%s', status=started)\n",
698 list[i].name,
699 list[i].binary);
700 break;
701 case GNUNET_ARM_SERVICE_STATUS_STOPPING:
702 fprintf (stdout,
703 "%s (binary='%s', status=stopping)\n",
704 list[i].name,
705 list[i].binary);
706 break;
707 default:
708 GNUNET_break_op (0);
709 fprintf (stdout,
710 "%s (binary='%s', status=unknown)\n",
711 list[i].name,
712 list[i].binary);
713 break;
714 }
715 }
716 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
717 NULL);
718}
719
720
721/**
722 * Main action loop. Runs the various jobs that we've been asked to
723 * do, in order.
724 *
725 * @param cls closure, unused
726 */
727static void
728action_loop (void *cls)
729{
730 (void) cls;
731 al_task = NULL;
732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733 "Running requested actions\n");
734 while (1)
735 {
736 switch (phase++)
737 {
738 case 0:
739 if (NULL != term)
740 {
741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742 "Termination action\n");
743 op = GNUNET_ARM_request_service_stop (h,
744 term,
745 &term_callback,
746 NULL);
747 return;
748 }
749 break;
750
751 case 1:
752 if (end || restart)
753 {
754 if (GNUNET_YES !=
755 GNUNET_CLIENT_test (cfg,
756 "arm"))
757 {
758 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
759 "GNUnet not running, cannot stop the peer\n");
760 }
761 else
762 {
763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764 "End action\n");
765 op = GNUNET_ARM_request_service_stop (h,
766 "arm",
767 &stop_callback,
768 NULL);
769 return;
770 }
771 }
772 break;
773
774 case 2:
775 if (start)
776 {
777 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778 "Start action\n");
779 op =
780 GNUNET_ARM_request_service_start (h,
781 "arm",
782 (no_stdout
783 ? 0
784 : GNUNET_OS_INHERIT_STD_OUT)
785 | (no_stderr
786 ? 0
787 : GNUNET_OS_INHERIT_STD_ERR),
788 &start_callback,
789 NULL);
790 return;
791 }
792 break;
793
794 case 3:
795 if (NULL != init)
796 {
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Initialization action\n");
799 op = GNUNET_ARM_request_service_start (h,
800 init,
801 GNUNET_OS_INHERIT_STD_NONE,
802 &init_callback,
803 NULL);
804 return;
805 }
806 break;
807
808 case 4:
809 if (list)
810 {
811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812 "Going to list all running services controlled by ARM.\n");
813 op = GNUNET_ARM_request_service_list (h,
814 &list_callback,
815 &list);
816 return;
817 }
818 break;
819
820 case 5:
821 if (monitor)
822 {
823 if (! quiet)
824 fprintf (stderr,
825 _ ("Now only monitoring, press CTRL-C to stop.\n"));
826 quiet =
827 0; /* does not make sense to stay quiet in monitor mode at this time */
828 return; /* done with tasks, just monitor */
829 }
830 break;
831
832 default: /* last phase */
833 GNUNET_SCHEDULER_shutdown ();
834 return;
835 }
836 }
837}
838
839
840/**
841 * Function called when a service starts or stops.
842 *
843 * @param cls closure
844 * @param service service name
845 * @param status status of the service
846 */
847static void
848srv_status (void *cls,
849 const char *service,
850 enum GNUNET_ARM_ServiceMonitorStatus status)
851{
852 const char *msg;
853
854 (void) cls;
855 switch (status)
856 {
857 case GNUNET_ARM_SERVICE_MONITORING_STARTED:
858 return; /* this should be done silently */
859
860 case GNUNET_ARM_SERVICE_STOPPED:
861 msg = _ ("Stopped %s.\n");
862 break;
863
864 case GNUNET_ARM_SERVICE_STARTING:
865 msg = _ ("Starting %s...\n");
866 break;
867
868 case GNUNET_ARM_SERVICE_STOPPING:
869 msg = _ ("Stopping %s...\n");
870 break;
871
872 default:
873 msg = NULL;
874 break;
875 }
876 if (! quiet)
877 {
878 if (NULL != msg)
879 fprintf (stderr,
880 msg,
881 service);
882 else
883 fprintf (stderr,
884 _ ("Unknown status %u for service %s.\n"),
885 status,
886 service);
887 }
888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889 "Got service %s status %d\n",
890 service,
891 (int) status);
892}
893
894
895/**
896 * Task run on timeout (if -T is given).
897 */
898static void
899timeout_task_cb (void *cls)
900{
901 (void) cls;
902 timeout_task = NULL;
903 ret = 2;
904 GNUNET_SCHEDULER_shutdown ();
905}
906
907
908/**
909 * Main function that will be run by the scheduler.
910 *
911 * @param cls closure
912 * @param args remaining command-line arguments
913 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
914 * @param c configuration
915 */
916static void
917run (void *cls,
918 char *const *args,
919 const char *cfgfile,
920 const struct GNUNET_CONFIGURATION_Handle *c)
921{
922 (void) cls;
923 (void) args;
924 (void) cfgfile;
925 cfg = GNUNET_CONFIGURATION_dup (c);
926 if (GNUNET_OK !=
927 GNUNET_CONFIGURATION_get_value_string (cfg,
928 "PATHS",
929 "GNUNET_HOME",
930 &dir))
931 {
932 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
933 "PATHS",
934 "GNUNET_HOME");
935 return;
936 }
937 (void) GNUNET_CONFIGURATION_get_value_filename (cfg,
938 "arm",
939 "CONFIG",
940 &config_file);
941 if (NULL == (h = GNUNET_ARM_connect (cfg,
942 &conn_status,
943 NULL)))
944 return;
945 if (monitor)
946 m = GNUNET_ARM_monitor_start (cfg,
947 &srv_status,
948 NULL);
949 al_task = GNUNET_SCHEDULER_add_now (&action_loop,
950 NULL);
951 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
952 NULL);
953 if (0 != timeout.rel_value_us)
954 timeout_task =
955 GNUNET_SCHEDULER_add_delayed (timeout,
956 &timeout_task_cb,
957 NULL);
958}
959
960
961/**
962 * The main function to obtain arm from gnunetd.
963 *
964 * @param argc number of arguments from the command line
965 * @param argv command line arguments
966 * @return 0 ok, 1 on error, 2 on timeout
967 */
968int
969main (int argc, char *const *argv)
970{
971 struct GNUNET_GETOPT_CommandLineOption options[] = {
972 GNUNET_GETOPT_option_flag ('e',
973 "end",
974 gettext_noop ("stop all GNUnet services"),
975 &end),
976 GNUNET_GETOPT_option_string ('i',
977 "init",
978 "SERVICE",
979 gettext_noop ("start a particular service"),
980 &init),
981 GNUNET_GETOPT_option_string ('k',
982 "kill",
983 "SERVICE",
984 gettext_noop ("stop a particular service"),
985 &term),
986 GNUNET_GETOPT_option_flag ('a',
987 "all",
988 gettext_noop (
989 "also show stopped services (used with -I)"),
990 &show_all),
991 GNUNET_GETOPT_option_flag ('s',
992 "start",
993 gettext_noop (
994 "start all GNUnet default services"),
995 &start),
996 GNUNET_GETOPT_option_flag ('r',
997 "restart",
998 gettext_noop (
999 "stop and start all GNUnet default services"),
1000 &restart),
1001 GNUNET_GETOPT_option_flag ('d',
1002 "delete",
1003 gettext_noop (
1004 "delete config file and directory on exit"),
1005 &delete),
1006 GNUNET_GETOPT_option_flag ('m',
1007 "monitor",
1008 gettext_noop ("monitor ARM activities"),
1009 &monitor),
1010 GNUNET_GETOPT_option_flag ('q',
1011 "quiet",
1012 gettext_noop ("don't print status messages"),
1013 &quiet),
1014 GNUNET_GETOPT_option_relative_time (
1015 'T',
1016 "timeout",
1017 "DELAY",
1018 gettext_noop (
1019 "exit with error status if operation does not finish after DELAY"),
1020 &timeout),
1021 GNUNET_GETOPT_option_flag ('I',
1022 "info",
1023 gettext_noop (
1024 "list currently running services"),
1025 &list),
1026 GNUNET_GETOPT_option_flag (
1027 'O',
1028 "no-stdout",
1029 gettext_noop ("don't let gnunet-service-arm inherit standard output"),
1030 &no_stdout),
1031 GNUNET_GETOPT_option_flag (
1032 'E',
1033 "no-stderr",
1034 gettext_noop ("don't let gnunet-service-arm inherit standard error"),
1035 &no_stderr),
1036 GNUNET_GETOPT_OPTION_END
1037 };
1038 int lret;
1039
1040 if (GNUNET_OK !=
1041 GNUNET_STRINGS_get_utf8_args (argc,
1042 argv,
1043 &argc,
1044 &argv))
1045 return 2;
1046 if (GNUNET_OK ==
1047 (lret = GNUNET_PROGRAM_run (
1048 argc,
1049 argv,
1050 "gnunet-arm",
1051 gettext_noop (
1052 "Control services and the Automated Restart Manager (ARM)"),
1053 options,
1054 &run,
1055 NULL)))
1056 {
1057 GNUNET_free_nz ((void *) argv);
1058 return ret;
1059 }
1060 GNUNET_free_nz ((void *) argv);
1061 return lret;
1062}
1063
1064
1065/* end of gnunet-arm.c */
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c
deleted file mode 100644
index b412094d3..000000000
--- a/src/arm/gnunet-service-arm.c
+++ /dev/null
@@ -1,2227 +0,0 @@
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_INFO,
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 message 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/arm/mockup-service.c b/src/arm/mockup-service.c
deleted file mode 100644
index 43dd3c92b..000000000
--- a/src/arm/mockup-service.c
+++ /dev/null
@@ -1,123 +0,0 @@
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 <stdlib.h>
22#include "platform.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/arm/mockup_service b/src/arm/mockup_service
deleted file mode 100755
index 32f4ccc14..000000000
--- a/src/arm/mockup_service
+++ /dev/null
@@ -1,130 +0,0 @@
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/arm/test_arm_api.c b/src/arm/test_arm_api.c
deleted file mode 100644
index 56a0abbd2..000000000
--- a/src/arm/test_arm_api.c
+++ /dev/null
@@ -1,258 +0,0 @@
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/arm/test_arm_api_data.conf b/src/arm/test_arm_api_data.conf
deleted file mode 100644
index fef6cfb40..000000000
--- a/src/arm/test_arm_api_data.conf
+++ /dev/null
@@ -1,38 +0,0 @@
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/arm/test_exponential_backoff.c b/src/arm/test_exponential_backoff.c
deleted file mode 100644
index e3eed8568..000000000
--- a/src/arm/test_exponential_backoff.c
+++ /dev/null
@@ -1,416 +0,0 @@
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/arm/test_gnunet_arm.py.in b/src/arm/test_gnunet_arm.py.in
deleted file mode 100644
index 2b30b6b97..000000000
--- a/src/arm/test_gnunet_arm.py.in
+++ /dev/null
@@ -1,129 +0,0 @@
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/arm/test_gnunet_service_arm.c b/src/arm/test_gnunet_service_arm.c
deleted file mode 100644
index 90fb8bfa0..000000000
--- a/src/arm/test_gnunet_service_arm.c
+++ /dev/null
@@ -1,267 +0,0 @@
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_os_lib.h"
30#include "gnunet_program_lib.h"
31
32/**
33 * Timeout for starting services, very short because of the strange way start works
34 * (by checking if running before starting, so really this time is always waited on
35 * startup (annoying)).
36 */
37#define START_TIMEOUT GNUNET_TIME_relative_multiply ( \
38 GNUNET_TIME_UNIT_MILLISECONDS, 50)
39
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
41
42
43static int ret = 1;
44
45static int resolved_ok;
46
47static int asked_for_a_list;
48
49static struct GNUNET_ARM_Handle *arm;
50
51static const char hostname[] = "www.gnu.org"; /* any domain should do */
52
53
54static void
55trigger_disconnect (void *cls)
56{
57 GNUNET_ARM_disconnect (arm);
58 arm = NULL;
59}
60
61
62static void
63arm_stop_cb (void *cls,
64 enum GNUNET_ARM_RequestStatus status,
65 enum GNUNET_ARM_Result result)
66{
67 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
68 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
69 if (result != GNUNET_ARM_RESULT_STOPPED)
70 {
71 GNUNET_break (0);
72 ret = 4;
73 }
74 GNUNET_SCHEDULER_add_now (&trigger_disconnect, NULL);
75}
76
77
78static void
79service_list (void *cls,
80 enum GNUNET_ARM_RequestStatus rs,
81 unsigned int count,
82 const struct GNUNET_ARM_ServiceInfo *list)
83{
84 unsigned int i;
85
86 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
87 "%u services are are currently running\n",
88 count);
89 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
90 goto stop_arm;
91 for (i = 0; i < count; i++)
92 {
93 if ((0 == strcasecmp (list[i].name, "resolver")) &&
94 (0 == strcasecmp (list[i].binary, "gnunet-service-resolver")))
95 {
96 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97 "Got service list, now stopping arm\n");
98 ret = 0;
99 }
100 }
101
102stop_arm:
103 GNUNET_ARM_request_service_stop (arm,
104 "arm",
105 &arm_stop_cb,
106 NULL);
107}
108
109
110static void
111hostname_resolve_cb (void *cls,
112 const struct sockaddr *addr,
113 socklen_t addrlen)
114{
115 if ((0 == ret) || (4 == ret) || (1 == resolved_ok))
116 return;
117 if (NULL == addr)
118 {
119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
120 "Failed to resolve hostname!\n");
121 GNUNET_break (0);
122 ret = 3;
123 GNUNET_ARM_request_service_stop (arm,
124 "arm",
125 &arm_stop_cb,
126 NULL);
127 return;
128 }
129 if (0 == asked_for_a_list)
130 {
131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132 "Resolved hostname, now checking the service list\n");
133 GNUNET_ARM_request_service_list (arm,
134 &service_list,
135 NULL);
136 asked_for_a_list = 1;
137 resolved_ok = 1;
138 }
139}
140
141
142static void
143arm_start_cb (void *cls,
144 enum GNUNET_ARM_RequestStatus status,
145 enum GNUNET_ARM_Result result)
146{
147 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
148 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150 "Trying to resolve a hostname via the resolver service!\n");
151 /* connect to the resolver service */
152 if (NULL ==
153 GNUNET_RESOLVER_ip_get (hostname,
154 AF_UNSPEC,
155 TIMEOUT,
156 &hostname_resolve_cb,
157 NULL))
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160 "Unable initiate connection to resolver service\n");
161 GNUNET_break (0);
162 ret = 2;
163 GNUNET_ARM_request_service_stop (arm,
164 "arm",
165 &arm_stop_cb,
166 NULL);
167 }
168}
169
170
171static void
172run (void *cls,
173 char *const *args,
174 const char *cfgfile,
175 const struct GNUNET_CONFIGURATION_Handle *c)
176{
177 arm = GNUNET_ARM_connect (c,
178 NULL,
179 NULL);
180 GNUNET_ARM_request_service_start (arm,
181 "arm",
182 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
183 &arm_start_cb,
184 NULL);
185}
186
187
188int
189main (int argc, char *av[])
190{
191 static char *const argv[] = {
192 "test-gnunet-service-arm",
193 "-c",
194 "test_arm_api_data.conf",
195 NULL
196 };
197 static struct GNUNET_GETOPT_CommandLineOption options[] = {
198 GNUNET_GETOPT_OPTION_END
199 };
200
201 /* trigger DNS lookup */
202#if HAVE_GETADDRINFO
203 {
204 struct addrinfo *ai;
205 int ret;
206
207 if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
208 {
209 fprintf (stderr,
210 "Failed to resolve `%s', testcase not run.\n",
211 hostname);
212 return 77;
213 }
214 freeaddrinfo (ai);
215 }
216#elif HAVE_GETHOSTBYNAME2
217 {
218 struct hostent *host;
219
220 host = gethostbyname2 (hostname, AF_INET);
221 if (NULL == host)
222 host = gethostbyname2 (hostname, AF_INET6);
223 if (NULL == host)
224 {
225 fprintf (stderr,
226 "Failed to resolve `%s', testcase not run.\n",
227 hostname);
228 return 77;
229 }
230 }
231#elif HAVE_GETHOSTBYNAME
232 {
233 struct hostent *host;
234
235 host = gethostbyname (hostname);
236 if (NULL == host)
237 {
238 fprintf (stderr,
239 "Failed to resolve `%s', testcase not run.\n",
240 hostname);
241 return 77;
242 }
243 }
244#else
245 fprintf (stderr,
246 "libc fails to have resolver function, testcase not run.\n");
247 return 77;
248#endif
249 GNUNET_log_setup ("test-gnunet-service-arm",
250 "WARNING",
251 NULL);
252 GNUNET_break (GNUNET_OK ==
253 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
254 argv, "test-gnunet-service-arm",
255 "nohelp", options,
256 &run, NULL));
257 if (0 != ret)
258 {
259 fprintf (stderr,
260 "Test failed with error code %d\n",
261 ret);
262 }
263 return ret;
264}
265
266
267/* end of test_gnunet_service_arm.c */